diff --git a/stages/as0/as0.asm b/stages/as0/as0.asm index 4661df4..c6b4502 100644 --- a/stages/as0/as0.asm +++ b/stages/as0/as0.asm @@ -5,7 +5,9 @@ section .bss global buf buf resb 0x100 - labels resb 0x1000 + labels resq 1 + label_refs resq 1 + heap resq 1 section .data _start_lbl db "_start", 0 @@ -374,6 +376,230 @@ op_table_end: section .text +panic_abort: + mov rdi, 1 + mov rax, 60 + syscall + +;; Allocator + +;; allocates $rdi bytes worth of pages via mmap +alloc_pages: + mov rax, 9 ; syscall: mmap + mov rsi, rdi ; length: rdi + xor rdi, rdi ; addr: NULL + mov rdx, 3 ; prot: PROT_READ | PROT_WRITE + mov r10, 34 ; flags: MAP_PRIVATE | MAP_ANONYMOUS + mov r8, -1 ; fd: -1 + xor r9, r9 ; offset: 0 + syscall + cmp rax, -1 + jae panic_abort + ret + +dealloc_pages: + mov rax, 11 ; syscall: munmap + mov rdi, rsi ; addr: rsi + mov rsi, rdx ; length: rdx + syscall + cmp rax, -1 + jae panic_abort + ret + +;; reallocates memory at $rdi[..$rsi] to a new location of size $rdx. +realloc_pages: + sub rsp, 24 + mov qword [rsp], rsi + mov qword [rsp + 8], rdi + mov rdi, rdx + call alloc_pages + mov rsi, rdi + mov rdi, qword [rsp + 8] + mov rdx, qword [rsp] + mov qword [rsp + 16], rax + call memcpy + mov rdi, qword [rsp + 8] + mov rsi, qword [rsp] + call dealloc_pages + mov rax, qword [rsp + 16] + add rsp, 24 + ret + +make_slab: + push rdi + mov rdi, 0x4000 + call alloc_pages + mov qword [rax + 0x4000 - 8], 0 ; initialize the tail pointer to NULL + pop rdi + mov qword [rdi], 0 ; tail_end = 0 + mov qword [rdi + 8], 0 ; free = NULL + mov qword [rdi + 16], rax ; first_block = allocated + mov qword [rdi + 24], rax ; last_block = allocated + mov rax, rdi + ret + +init_heap: + push r14 + xor r14, r14 + mov rax, qword [rel heap] + test rax, rax + jnz .done + mov rdi, 0x1000 + call alloc_pages + mov qword [rel heap], rax +.loop: + cmp r14, 9 + jge .done + mov rdi, r14 + shl rdi, 5 ; idx * 32 + mov rax, qword [rel heap] + lea rdi, [rax + rdi] ; &heap.slabs[idx] + call make_slab + inc r14 + jmp .loop +.done: + pop r14 + ret + +;; finds the correct slab for an allocation with size $rdi and align $rsi +slab_bucket: + xor rax, rax + dec rdi ; if size is a power of two, dec so we can later inc + dec rsi ; ^^ + or rdi, rsi ; we only care about the log2, so just gather all the bits + bsr rsi, rdi ; log2((size-1) | (align-1)) + sub rsi, 2 ; +1 for the dec, -3 to collapse the first 3 slabs into one + cmovae rax, rsi ; saturating sub + ret + +slab_alloc: + push rbx + mov rax, qword [rdi + 8] ; free + test rax, rax + jz .no_free + mov rdx, qword [rax] ; next free chunk + mov qword [rdi + 8], rdx ; free = next + pop rbx + ret +.no_free: + add esi, 3 ; undo the -3 from slab_bucket to get the actual log2(size) + and esi, 63 ; clamp for safety + mov edx, 16376 ; 0x4000 - 8 + mov ecx, esi + shr rdx, cl ; 0x4000 - 8 >> log2(size) + mov rax, qword [rdi] ; tail_end + mov rbx, qword [rdi + 24] ; last_block + cmp rax, rdx + jb .alloc_from_block + push rsi + push rax + push rdi + mov rdi, 0x4000 ; allocate a new block + call alloc_pages + mov qword [rax + 0x4000 - 8], 0 ; initialize the tail pointer to NULL + pop rdi + mov rsi, qword [rdi + 24] ; last_block + mov qword [rsi + 0x4000 - 8], rax + mov qword [rdi + 24], rax ; last_block = new block + mov rbx, rax + pop rax + pop rsi +.alloc_from_block: + inc qword [rdi] ; tail_end++ + mov ecx, esi + shl rax, cl + add rax, rbx + pop rbx + ret + + ;; allocate a chunk of memory of size $rdi and alignment $rsi +heap_alloc: + mov rax, qword [rel heap] + cmp rax, 0 + je .init +.is_init: + push rdi + call slab_bucket + cmp rax, 9 + jge .mmap + mov rsi, rax + shl rax, 5 ; idx * 32 + mov rdi, qword [rel heap] + lea rdi, [rdi + rax] ; &heap.slabs[idx] + call slab_alloc + pop rdi + ret +.init: + push rdi + push rsi + call init_heap + pop rsi + pop rdi + jmp .is_init +.mmap: + pop rdi + call alloc_pages + ret + +;; deallocates a chunk of memory at $rdi of size $rsi and alignment $rdx +heap_dealloc: + push rsi + push rdi + mov rdi, rsi + mov rsi, rdx + call slab_bucket + cmp rax, 9 + jge .mmap + mov rsi, rax + shl rax, 5 ; idx * 32 + mov rdi, qword [rel heap] + lea rdi, [rdi + rax] ; &heap.slabs[idx] + mov rax, qword [rdi + 8] ; free + pop rsi + mov qword [rsi], rax + mov qword [rdi + 8], rsi + pop rsi + ret +.mmap: + pop rdi + pop rsi + call dealloc_pages + ret + +;; reallocates a chunk of memory at $rdi of size $rsi and alignment $rdx to a new location of size $rcx +heap_realloc: + push rbx + push r12 + push r13 + push r14 + + mov rbx, rdi + mov r12, rsi + mov r13, rdx + mov r14, rcx + + mov rdi, r14 + mov rsi, r13 + call heap_alloc + mov r14, rax + + mov rdi, rbx + mov rsi, rax + mov rdx, r12 + call memcpy + + mov rdi, rbx + mov rsi, r12 + mov rdx, r13 + call heap_dealloc + + mov rax, r14 + pop r14 + pop r13 + pop r12 + pop rbx + ret + ;; format dil as hex u8 into $ax format_u8: xor ecx, ecx @@ -613,6 +839,34 @@ is_digit: setb al ret +is_alphanumeric: + lea eax, [rdi - 48] + cmp eax, 10 + setb cl + and dil, 0xdf + add dil, -65 + cmp dil, 26 + setb al + or al, cl + ret + +is_id_start: + call is_alpha + cmp dil, 95 + sete cl + or al, cl + ret + +is_id_cont: + call is_alphanumeric + cmp dil, 45 + sete cl + or al, cl + cmp dil, 95 + sete cl + or al, cl + ret + ;; converts char $dil to a digit with radix $rsi, returning it in $edx. $al is set to 1 if the char is a valid digit, and 0 otherwise. to_digit: lea eax, [rsi - 2] @@ -646,6 +900,8 @@ exit: %define KIND_REG 1 %define KIND_MEM 2 %define KIND_IMM 3 +%define KIND_MEM_LABEL 4 ; a memory operand whose displacement is a label +%define KIND_IMM_LABEL 5 ; an immediate value that is a label struc operand .type resd 1 @@ -679,7 +935,7 @@ try_parse_reg: mov rbx, rdi mov dil, byte [rbx] call is_alpha - jnb .done + jnb .not_reg cmp dil, 'r' jne .not_64_or_rX @@ -707,10 +963,11 @@ try_parse_reg: mov rsi, 3 mov dl, byte [rbx] call strfind + mov r13d, 3 cmp rax, 0 + jl .done mov r13d, eax - mov edi, 3 - cmovl r13d, edi + inc rbx jmp .done .not_rX: @@ -720,6 +977,11 @@ try_parse_reg: mov dx, word [rbx] add rbx, 2 + xor eax, eax + cmp r13d, 0 + sete al + add rbx, rax ; spl, bpl, sil, dil have a 3rd character we need to account for + mov r12d, 4 ; rsp cmp dx, 'sp' je .done @@ -740,12 +1002,20 @@ try_parse_reg: cmp dx, 'ip' je .done + sub rbx, rax ; its not spl, bpl, sil, dil, so undo the inc we did earlier + lea rax, [rel abcd_reg_indices] sub dl, 'a' + cmp dl, 3 + ja .not_reg movzx edx, dl movzx r12d, byte [rax + rdx] ; rax, rcx, rdx, rbx .done: + mov dil, byte [rbx] + call is_alphanumeric + jnz .not_reg ; if the next char is alphanumeric, then this is not a valid reg + mov rax, rbx pop rdi sub rax, rdi ; number of bytes consumed @@ -760,6 +1030,15 @@ try_parse_reg: pop rbx ret +.not_reg: + xor rax, rax + xor rdx, rdx + pop rdi + pop r13 + pop r12 + pop rbx + ret + .not_64_or_rX: cmp dil, 'e' jne .not_32 @@ -791,10 +1070,9 @@ try_parse_num: xor eax, eax mov rbx, rdi - ; eat leading - and flip sign + ; eat leading - cmp byte [rbx], '-' sete al - sub r13, rax lea rbx, [rbx + rax] ; eat any leading + @@ -852,6 +1130,11 @@ try_parse_num: sub rax, rdi ; number of bytes consumed mov rdx, r13 ; acc + cmp byte [rdi], '-' + jne .positive + neg rdx +.positive: + pop r13 pop r12 pop rbx @@ -931,9 +1214,14 @@ try_parse_mem: jne .not_mem inc rbx + cmp byte [rbx], `'` + je .disp_lbl + call skip_whitespace_rbx mov rdi, rbx call try_parse_reg + test eax, eax + jz .disp add rbx, rax call skip_whitespace_rbx @@ -958,6 +1246,7 @@ try_parse_mem: ; TODO: allow for label as displacement .disp: + mov dil, byte [rbx] sub dil, `,` neg dil ; sign mov al, dil @@ -969,6 +1258,12 @@ try_parse_mem: inc rbx call skip_whitespace_rbx + + .disp_skip_sign: + cmp byte [rbx], `'` + je .disp_lbl + + mov rdi, rbx call try_parse_num test eax, eax @@ -983,19 +1278,39 @@ try_parse_mem: je .done jmp .not_mem +.disp_lbl: + inc rbx ; skip the leading `'` + mov dword [r12 + operand.type], KIND_MEM_LABEL + mov rdi, rbx + call try_parse_label + test eax, eax + jz .not_mem + + mov rdi, rbx + mov rsi, rax + add rbx, rax + call fasthash + mov dword [r12 + operand.disp], eax + + call skip_whitespace_rbx + cmp byte [rbx], ']' + je .done + jmp .not_mem + .base: mov qword [r12 + operand.reg], rdx cmp byte [rbx], ']' je .done cmp byte [rbx], '+' - jne .not_mem + jne .disp + inc rbx call skip_whitespace_rbx mov rdi, rbx call try_parse_reg test eax, eax - jz .disp + jz .disp_skip_sign add rbx, rax mov qword [r12 + operand.index], rdx @@ -1026,6 +1341,232 @@ try_parse_mem: ret +global try_parse_operand +try_parse_operand: + push rbx ; buf + push r12 ; operand ptr + + mov rbx, rdi + mov r12, rsi ; output operand ptr + + mov dword [r12 + operand.type], KIND_NONE + mov dword [r12 + operand.size], 0 + mov qword [r12 + operand.reg], -1 + mov qword [r12 + operand.index], -1 + mov dword [r12 + operand.scale], 0 + mov dword [r12 + operand.disp], 0 + + mov al, byte [rbx] + cmp al, '[' + je .mem + cmp al, `'` + je .label + + mov dword [r12 + operand.type], KIND_IMM + call try_parse_num + test eax, eax + jz .not_imm + + mov qword [r12 + operand_imm.imm], rdx + test rdx, rdx + jz .done + + mov rcx, rdx + neg rdx + cmovs rdx, rcx ; abs(imm) + + bsr rcx, rdx ; # of bits required to represent the immediate + mov edx, ecx + + cmp edx, 1 + adc edx, -1 ; saturating sub + + bsr edx, edx ; log2 of the (# of bits - 1) + 1 + inc edx + + cmp ecx, 2 + mov ecx, 1 + cmovae ecx, edx ; checked ilog2 + + mov dword [r12 + operand.size], ecx + jmp .done + +.not_imm: + mov dword [r12 + operand.type], KIND_REG + mov rdi, rbx + call try_parse_reg + test eax, eax + jz .not_reg + + mov qword [r12 + operand.reg], rdx + mov dword [r12 + operand.size], edx ; low dword of rdx is the size + jmp .done + +.not_reg: +.mem: + mov rdi, rbx + mov rsi, r12 + call try_parse_mem + test eax, eax + jz .dont_know + jmp .done + +.label: + inc rbx ; skip the leading `'` + mov dword [r12 + operand.type], KIND_IMM_LABEL + mov rdi, rbx + call try_parse_label + test eax, eax + jz .dont_know + + mov rdi, rbx + mov rsi, rax + mov rbx, rax + call fasthash + mov dword [r12 + operand_imm.imm], eax + mov rax, rbx ; number of bytes consumed + jmp .done + +.dont_know: + xor rax, rax +.done: + mov rdx, r12 ; output operand ptr + pop r12 + pop rbx + ret + + +try_parse_label: + push rbx ; buf + push r12 ; label length + + mov rbx, rdi + xor r12d, r12d + mov dil, byte [rbx] + call is_id_start + jz .done + inc r12d +.loop: + mov dil, byte [rbx + r12] + call is_id_cont + jz .done + inc r12d + jmp .loop +.done: + mov eax, r12d + pop r12 + pop rbx + ret + + + +;; Labels + +struc Label +.name_hash resd 1 +.offset resd 1 +endstruc + +struc LabelTable +.size resd 1 +.cap resd 1 +.labels resq 1 +endstruc + + +;; calculates the hash of a byte sequence in $rdi of length $rsi, returning it in $eax +global fasthash +fasthash: + mov eax, 0x811C9DC5 ; FNV-1a 32-bit offset basis +.loop: + test rsi, rsi + jz .done + movzx ecx, byte [rdi] + xor eax, ecx + imul eax, 0x01000193 ; FNV prime + inc rdi + dec rsi + jmp .loop +.done: + ret + +;; push label $rdi with length $rsi and offset $rdx into the label ref table +push_lbl_ref: + mov rcx, [rel label_refs] + jmp push_lbl_common + +;; push label $rdi with length $rsi and offset $edx into the label table +push_lbl: + mov rcx, [rel labels] +push_lbl_common: + push rbx + push r12 + push r13 + + mov rbx, rcx + mov r13d, edx ; label offset + + call fasthash + mov r12d, eax ; store the hash in r12d + + mov eax, dword [rbx + LabelTable.size] + cmp eax, dword [rbx + LabelTable.cap] + jl .has_space + + mov rdi, qword [rbx + LabelTable.labels] + mov esi, dword [rbx + LabelTable.cap] + mov edx, 8 + mov ecx, esi + shl ecx, 1 + call heap_realloc + mov qword [rbx + LabelTable.labels], rax + shl dword [rbx + LabelTable.cap], 1 + +.has_space: + mov rax, qword [rbx + LabelTable.labels] + mov edi, dword [rbx + LabelTable.size] + lea rax, [rax + rdi * 8] + mov dword [rax + Label.name_hash], r12d + mov dword [rax + Label.offset], r13d + inc dword [rbx + LabelTable.size] + + pop r13 + pop r12 + pop rbx + ret + +find_lbl: + push rbx + push r12 + push r13 + + mov rbx, [rel labels] + + call fasthash + mov r12d, eax ; store the hash in r12d + + xor r13d, r13d +.loop: + cmp r13d, dword [rbx + LabelTable.size] + jge .not_found + + mov rax, qword [rbx + LabelTable.labels] + lea rax, [rax + r13 * 8] + cmp dword [rax + Label.name_hash], r12d + je .found + inc r13d + jmp .loop +.found: + mov eax, dword [rax + Label.offset] + jmp .done +.not_found: + xor rax, rax +.done: + pop r13 + pop r12 + pop rbx + ret + global _entry_print_table _entry_print_table: call print_op_table diff --git a/stages/as0/test.rs b/stages/as0/test.rs index bf7503f..02a7c12 100644 --- a/stages/as0/test.rs +++ b/stages/as0/test.rs @@ -4,6 +4,10 @@ unsafe extern "C" { unsafe fn try_parse_reg(_: *const u8) -> (usize, Register); unsafe fn try_parse_mem(_: *const u8, _: *mut Operand) -> (usize, *const Operand); + #[link_name = "try_parse_operand"] + unsafe fn try_parse_operand_impl(_: *const u8, _: *mut Operand) -> (usize, *const Operand); + + unsafe fn fasthash(_: *const u8, _: usize) -> u32; #[link_name = "buf"] static mut BUF: [u8; 0x100]; @@ -16,10 +20,21 @@ struct Register { num: u32, } -#[repr(C)] +#[repr(u32)] +#[derive(Debug, PartialEq, Eq)] +enum OperandKind { + None = 0, + Reg = 1, + Mem = 2, + Imm = 3, + MemLabel = 4, + ImmLabel = 5, +} + +#[repr(C, align(8))] #[derive(Debug, PartialEq, Eq)] struct Operand { - kind: u32, + kind: OperandKind, size: u32, reg: Register, index: Register, @@ -27,6 +42,64 @@ struct Operand { disp: i32, } +impl Operand { + fn parse(bytes: &[u8]) -> Option<(usize, Self)> { + let mut op = core::mem::MaybeUninit::::uninit(); + let (n, ptr) = unsafe { try_parse_operand_impl(bytes.as_ptr(), op.as_mut_ptr()) }; + if n == 0 { + None + } else { + Some((n, unsafe { op.assume_init() })) + } + } + + fn label(name: &[u8]) -> Self { + let mut this = Self { + kind: OperandKind::ImmLabel, + size: 0, + reg: Register::invalid(), + index: Register::invalid(), + scale: 0, + disp: 0, + }; + unsafe { + let hash = fasthash(name.as_ptr(), name.len()); + (&raw mut this).byte_add(8).cast::().write(hash as u32); + } + this + } + + fn imm(value: i64) -> Self { + let size = if value.abs() == 0 { + value as u32 + } else { + unsafe { + (value.abs() as u64) + .checked_ilog2() + .unwrap_unchecked() + .saturating_sub(1) + .checked_ilog2() + .unwrap_or(0) + + 1 + } + }; + + let mut this = Self { + kind: OperandKind::Imm, + size, + reg: Register::invalid(), + index: Register::invalid(), + scale: 0, + disp: 0, + }; + unsafe { + (&raw mut this).byte_add(8).cast::().write(value); + } + + this + } +} + impl Register { fn new(num: u32, size: u32) -> Self { Self { @@ -54,22 +127,22 @@ mod tests { #[test] fn parse_mem() { let mut op = MaybeUninit::::uninit(); - // let (n, _) = - // unsafe { try_parse_mem(b"qword [rax + rbx * 2 + 0x10]\0".as_ptr(), op.as_mut_ptr()) }; - // assert_eq!( - // (n, unsafe { op.assume_init_ref() }), - // ( - // 28, - // &Operand { - // kind: 2, - // size: 3, - // reg: Register::new(0, 8), - // index: Register::new(3, 8), - // scale: 2, - // disp: 0x10, - // } - // ) - // ); + let (n, _) = + unsafe { try_parse_mem(b"qword [rax + rbx * 2 + 0x10]\0".as_ptr(), op.as_mut_ptr()) }; + assert_eq!( + (n, unsafe { op.assume_init_ref() }), + ( + 28, + &Operand { + kind: OperandKind::Mem, + size: 3, + reg: Register::new(0, 8), + index: Register::new(3, 8), + scale: 2, + disp: 0x10, + } + ) + ); let (n, _) = unsafe { try_parse_mem(b"byte [r12 + r13 * 4 - 0x20]\0".as_ptr(), op.as_mut_ptr()) }; @@ -78,7 +151,7 @@ mod tests { ( 27, &Operand { - kind: 2, + kind: OperandKind::Mem, size: 0, reg: Register::new(12, 8), index: Register::new(13, 8), @@ -94,7 +167,7 @@ mod tests { ( 17, &Operand { - kind: 2, + kind: OperandKind::Mem, size: 1, reg: Register::invalid(), index: Register::new(8, 8), @@ -106,81 +179,159 @@ mod tests { } #[test] - #[ignore] + fn parse_operand() { + let cases = [ + ( + &b"rax\0"[..], + Operand { + kind: OperandKind::Reg, + size: 3, + reg: Register::new(0, 8), + index: Register::invalid(), + scale: 0, + disp: 0, + }, + ), + ( + &b"qword [rax + rbx * 2 + 0x10]\0"[..], + Operand { + kind: OperandKind::Mem, + size: 3, + reg: Register::new(0, 8), + index: Register::new(3, 8), + scale: 2, + disp: 0x10, + }, + ), + ( + &b"byte [r12 + r13 * 4 - 0x20]\0"[..], + Operand { + kind: OperandKind::Mem, + size: 0, + reg: Register::new(12, 8), + index: Register::new(13, 8), + scale: 4, + disp: -0x20, + }, + ), + ( + &b"word [r8 * 8 + 5]\0"[..], + Operand { + kind: OperandKind::Mem, + size: 1, + reg: Register::invalid(), + index: Register::new(8, 8), + scale: 8, + disp: 5, + }, + ), + (&b"0x12345678\0"[..], Operand::imm(0x12345678)), + (&b"-0x12345678\0"[..], Operand::imm(-0x12345678)), + (&b"0\0"[..], Operand::imm(0)), + (&b"-1\0"[..], Operand::imm(-1)), + (&b"1\0"[..], Operand::imm(1)), + (&b"255\0"[..], Operand::imm(255)), + (&b"-255\0"[..], Operand::imm(-255)), + (&b"'wazzaah\0"[..], Operand::label(b"wazzaah")), + ( + &b"qword [rsp + 'hii]"[..], + Operand { + kind: OperandKind::MemLabel, + size: 3, + reg: Register::new(4, 8), + index: Register::invalid(), + scale: 0, + disp: unsafe { fasthash(b"hii".as_ptr(), 3) as i32 }, + }, + ), + ]; + + for (text, expected) in cases { + let (n, result) = Operand::parse(text).expect("Failed to parse operand"); + assert_eq!( + result, + expected, + "Failed to parse '{}'", + std::str::from_utf8(text).unwrap() + ); + } + } + + #[test] fn parse_reg() { let cases = [ - (&b"rax"[..], (0, 8)), - (&b"rcx"[..], (1, 8)), - (&b"rdx"[..], (2, 8)), - (&b"rbx"[..], (3, 8)), - (&b"rsp"[..], (4, 8)), - (&b"rbp"[..], (5, 8)), - (&b"rsi"[..], (6, 8)), - (&b"rdi"[..], (7, 8)), - (&b"r8"[..], (8, 8)), - (&b"r9"[..], (9, 8)), - (&b"r10"[..], (10, 8)), - (&b"r11"[..], (11, 8)), - (&b"r12"[..], (12, 8)), - (&b"r13"[..], (13, 8)), - (&b"r14"[..], (14, 8)), - (&b"r15"[..], (15, 8)), - (&b"rip"[..], (!0, 8)), - (&b"eax"[..], (0, 4)), - (&b"ecx"[..], (1, 4)), - (&b"edx"[..], (2, 4)), - (&b"ebx"[..], (3, 4)), - (&b"esp"[..], (4, 4)), - (&b"ebp"[..], (5, 4)), - (&b"esi"[..], (6, 4)), - (&b"edi"[..], (7, 4)), - (&b"r8d"[..], (8, 4)), - (&b"r9d"[..], (9, 4)), - (&b"r10d"[..], (10, 4)), - (&b"r11d"[..], (11, 4)), - (&b"r12d"[..], (12, 4)), - (&b"r13d"[..], (13, 4)), - (&b"r14d"[..], (14, 4)), - (&b"r15d"[..], (15, 4)), - (&b"r8w"[..], (8, 2)), - (&b"r9w"[..], (9, 2)), - (&b"r10w"[..], (10, 2)), - (&b"r11w"[..], (11, 2)), - (&b"r12w"[..], (12, 2)), - (&b"r13w"[..], (13, 2)), - (&b"r14w"[..], (14, 2)), - (&b"r15w"[..], (15, 2)), - (&b"r8b"[..], (8, 1)), - (&b"r9b"[..], (9, 1)), - (&b"r10b"[..], (10, 1)), - (&b"r11b"[..], (11, 1)), - (&b"r12b"[..], (12, 1)), - (&b"r13b"[..], (13, 1)), - (&b"r14b"[..], (14, 1)), - (&b"r15b"[..], (15, 1)), - (&b"ax"[..], (0, 2)), - (&b"cx"[..], (1, 2)), - (&b"dx"[..], (2, 2)), - (&b"bx"[..], (3, 2)), - (&b"sp"[..], (4, 2)), - (&b"bp"[..], (5, 2)), - (&b"si"[..], (6, 2)), - (&b"di"[..], (7, 2)), - (&b"al"[..], (0, 1)), - (&b"cl"[..], (1, 1)), - (&b"dl"[..], (2, 1)), - (&b"bl"[..], (3, 1)), - (&b"spl"[..], (4, 1)), - (&b"bpl"[..], (5, 1)), - (&b"sil"[..], (6, 1)), - (&b"dil"[..], (7, 1)), + (&b"rax\0"[..], (0, 8)), + (&b"rcx\0"[..], (1, 8)), + (&b"rdx\0"[..], (2, 8)), + (&b"rbx\0"[..], (3, 8)), + (&b"rsp\0"[..], (4, 8)), + (&b"rbp\0"[..], (5, 8)), + (&b"rsi\0"[..], (6, 8)), + (&b"rdi\0"[..], (7, 8)), + (&b"r8\0"[..], (8, 8)), + (&b"r9\0"[..], (9, 8)), + (&b"r10\0"[..], (10, 8)), + (&b"r11\0"[..], (11, 8)), + (&b"r12\0"[..], (12, 8)), + (&b"r13\0"[..], (13, 8)), + (&b"r14\0"[..], (14, 8)), + (&b"r15\0"[..], (15, 8)), + (&b"rip\0"[..], (!0, 8)), + (&b"eax\0"[..], (0, 4)), + (&b"ecx\0"[..], (1, 4)), + (&b"edx\0"[..], (2, 4)), + (&b"ebx\0"[..], (3, 4)), + (&b"esp\0"[..], (4, 4)), + (&b"ebp\0"[..], (5, 4)), + (&b"esi\0"[..], (6, 4)), + (&b"edi\0"[..], (7, 4)), + (&b"r8d\0"[..], (8, 4)), + (&b"r9d\0"[..], (9, 4)), + (&b"r10d\0"[..], (10, 4)), + (&b"r11d\0"[..], (11, 4)), + (&b"r12d\0"[..], (12, 4)), + (&b"r13d\0"[..], (13, 4)), + (&b"r14d\0"[..], (14, 4)), + (&b"r15d\0"[..], (15, 4)), + (&b"r8w\0"[..], (8, 2)), + (&b"r9w\0"[..], (9, 2)), + (&b"r10w\0"[..], (10, 2)), + (&b"r11w\0"[..], (11, 2)), + (&b"r12w\0"[..], (12, 2)), + (&b"r13w\0"[..], (13, 2)), + (&b"r14w\0"[..], (14, 2)), + (&b"r15w\0"[..], (15, 2)), + (&b"r8b\0"[..], (8, 1)), + (&b"r9b\0"[..], (9, 1)), + (&b"r10b\0"[..], (10, 1)), + (&b"r11b\0"[..], (11, 1)), + (&b"r12b\0"[..], (12, 1)), + (&b"r13b\0"[..], (13, 1)), + (&b"r14b\0"[..], (14, 1)), + (&b"r15b\0"[..], (15, 1)), + (&b"ax\0"[..], (0, 2)), + (&b"cx\0"[..], (1, 2)), + (&b"dx\0"[..], (2, 2)), + (&b"bx\0"[..], (3, 2)), + (&b"sp\0"[..], (4, 2)), + (&b"bp\0"[..], (5, 2)), + (&b"si\0"[..], (6, 2)), + (&b"di\0"[..], (7, 2)), + (&b"al\0"[..], (0, 1)), + (&b"cl\0"[..], (1, 1)), + (&b"dl\0"[..], (2, 1)), + (&b"bl\0"[..], (3, 1)), + (&b"spl\0"[..], (4, 1)), + (&b"bpl\0"[..], (5, 1)), + (&b"sil\0"[..], (6, 1)), + (&b"dil\0"[..], (7, 1)), ]; for (text, (reg, size)) in cases { let (n, result) = unsafe { try_parse_reg(text.as_ptr()) }; assert_eq!( - result, - Register::new(reg, size), + (n, result), + (text.len() - 1, Register::new(reg, size)), "Failed to parse '{}' n={n}", std::str::from_utf8(text).unwrap() );