Simplified x86 encoder

This commit is contained in:
2025-03-09 23:27:40 +01:00
parent 6c659a2b0c
commit 0d562e8002
10 changed files with 39 additions and 76 deletions

View File

@ -6,7 +6,7 @@ import (
// AddRegisterNumber adds a number to the given register. // AddRegisterNumber adds a number to the given register.
func AddRegisterNumber(code []byte, register cpu.Register, number int) []byte { func AddRegisterNumber(code []byte, register cpu.Register, number int) []byte {
return encodeNum(code, AddressDirect, 0, register, number, 0x83, 0x81) return encodeNum(code, AddressDirect, 0b000, register, number, 0x83, 0x81)
} }
// AddRegisterRegister adds a register value into another register. // AddRegisterRegister adds a register value into another register.

View File

@ -19,12 +19,8 @@ func Call(code []byte, offset uint32) []byte {
// Calls a function whose address is stored in the given register. // Calls a function whose address is stored in the given register.
func CallRegister(code []byte, register cpu.Register) []byte { func CallRegister(code []byte, register cpu.Register) []byte {
if register > 0b111 { if register > 0b111 {
return append( code = append(code, 0x41)
code, register &= 0b111
0x41,
0xFF,
0xD0+byte(register&0b111),
)
} }
return append( return append(
@ -50,30 +46,6 @@ func CallAt(code []byte, address uint32) []byte {
// CallAtMemory calls a function at the address stored at the given memory address. // CallAtMemory calls a function at the address stored at the given memory address.
// The memory address is relative to the next instruction. // The memory address is relative to the next instruction.
func CallAtMemory(code []byte, register cpu.Register, offset int8) []byte { func CallAtMemory(code []byte, base cpu.Register, offset int8) []byte {
mod := AddressMemory return memoryAccess(code, 0xFF, 0xFF, 0b010, base, offset, 4)
if offset != 0 || register == RBP || register == R13 {
mod = AddressMemoryOffset8
}
reg := byte(0b010)
rm := register
if rm > 0b111 {
code = append(code, 0x41)
rm &= 0b111
}
code = append(code, 0xFF, ModRM(mod, reg, byte(rm)))
if register == RSP || register == R12 {
code = append(code, SIB(Scale1, 0b100, 0b100))
}
if mod == AddressMemoryOffset8 {
code = append(code, byte(offset))
}
return code
} }

View File

@ -4,5 +4,5 @@ import "git.urbach.dev/cli/q/src/cpu"
// LoadRegister loads from memory into a register. // LoadRegister loads from memory into a register.
func LoadRegister(code []byte, destination cpu.Register, base cpu.Register, offset int8, length byte) []byte { func LoadRegister(code []byte, destination cpu.Register, base cpu.Register, offset int8, length byte) []byte {
return memoryAccess(code, 0x8A, 0x8B, base, offset, length, destination) return memoryAccess(code, 0x8A, 0x8B, destination, base, offset, length)
} }

View File

@ -4,5 +4,5 @@ import "git.urbach.dev/cli/q/src/cpu"
// LoadDynamicRegister loads from memory with a register offset into a register. // LoadDynamicRegister loads from memory with a register offset into a register.
func LoadDynamicRegister(code []byte, destination cpu.Register, base cpu.Register, offset cpu.Register, length byte) []byte { func LoadDynamicRegister(code []byte, destination cpu.Register, base cpu.Register, offset cpu.Register, length byte) []byte {
return memoryAccessDynamic(code, 0x8A, 0x8B, base, offset, length, destination) return memoryAccessDynamic(code, 0x8A, 0x8B, destination, base, offset, length)
} }

View File

@ -10,7 +10,7 @@ const (
AddressDirect = AddressMode(0b11) AddressDirect = AddressMode(0b11)
) )
// ModRM is used to generate a ModRM suffix. // ModRM is used to generate a mode-register-memory suffix.
// - mod: 2 bits. The addressing mode. // - mod: 2 bits. The addressing mode.
// - reg: 3 bits. Register reference or opcode extension. // - reg: 3 bits. Register reference or opcode extension.
// - rm: 3 bits. Register operand. // - rm: 3 bits. Register operand.

View File

@ -41,16 +41,7 @@ func MoveRegisterNumber(code []byte, destination cpu.Register, number int) []byt
// MoveRegisterNumber32 moves an integer into the given register and sign-extends the register. // MoveRegisterNumber32 moves an integer into the given register and sign-extends the register.
func MoveRegisterNumber32(code []byte, destination cpu.Register, number int) []byte { func MoveRegisterNumber32(code []byte, destination cpu.Register, number int) []byte {
b := byte(0) code = encode(code, AddressDirect, 0, destination, 8, 0xC7)
if destination > 0b111 {
b = 1
destination &= 0b111
}
code = append(code, REX(1, 0, 0, b))
code = append(code, 0xC7)
code = append(code, ModRM(AddressDirect, 0, byte(destination)))
return binary.LittleEndian.AppendUint32(code, uint32(number)) return binary.LittleEndian.AppendUint32(code, uint32(number))
} }

View File

@ -6,9 +6,9 @@ import (
"git.urbach.dev/cli/q/src/cpu" "git.urbach.dev/cli/q/src/cpu"
) )
// StoreNumber stores a number into the memory address included in the given register. // StoreNumber writes a number to a memory address.
func StoreNumber(code []byte, register cpu.Register, offset int8, length byte, number int) []byte { func StoreNumber(code []byte, base cpu.Register, offset int8, length byte, number int) []byte {
code = memoryAccess(code, 0xC6, 0xC7, register, offset, length, 0b000) code = memoryAccess(code, 0xC6, 0xC7, 0b000, base, offset, length)
switch length { switch length {
case 8, 4: case 8, 4:
@ -21,7 +21,7 @@ func StoreNumber(code []byte, register cpu.Register, offset int8, length byte, n
return append(code, byte(number)) return append(code, byte(number))
} }
// StoreRegister stores the contents of the `source` register into the memory address included in the given register. // StoreRegister writes the contents of the register to a memory address.
func StoreRegister(code []byte, register cpu.Register, offset int8, length byte, source cpu.Register) []byte { func StoreRegister(code []byte, base cpu.Register, offset int8, length byte, register cpu.Register) []byte {
return memoryAccess(code, 0x88, 0x89, register, offset, length, source) return memoryAccess(code, 0x88, 0x89, register, base, offset, length)
} }

View File

@ -6,9 +6,9 @@ import (
"git.urbach.dev/cli/q/src/cpu" "git.urbach.dev/cli/q/src/cpu"
) )
// StoreDynamicNumber stores a number into the memory address at `destination` with a register offset. // StoreDynamicNumber writes a number to a memory address with a register offset.
func StoreDynamicNumber(code []byte, destination cpu.Register, offset cpu.Register, length byte, number int) []byte { func StoreDynamicNumber(code []byte, base cpu.Register, offset cpu.Register, length byte, number int) []byte {
code = memoryAccessDynamic(code, 0xC6, 0xC7, destination, offset, length, 0b000) code = memoryAccessDynamic(code, 0xC6, 0xC7, 0b000, base, offset, length)
switch length { switch length {
case 8, 4: case 8, 4:
@ -21,7 +21,7 @@ func StoreDynamicNumber(code []byte, destination cpu.Register, offset cpu.Regist
return append(code, byte(number)) return append(code, byte(number))
} }
// StoreDynamicRegister stores the contents of the `source` register into the memory address at `destination` with a register offset. // StoreDynamicRegister writes the contents of a register to a memory address with a register offset.
func StoreDynamicRegister(code []byte, destination cpu.Register, offset cpu.Register, length byte, source cpu.Register) []byte { func StoreDynamicRegister(code []byte, base cpu.Register, offset cpu.Register, length byte, source cpu.Register) []byte {
return memoryAccessDynamic(code, 0x88, 0x89, destination, offset, length, source) return memoryAccessDynamic(code, 0x88, 0x89, source, base, offset, length)
} }

View File

@ -3,26 +3,26 @@ package x86
import "git.urbach.dev/cli/q/src/cpu" import "git.urbach.dev/cli/q/src/cpu"
// memoryAccess encodes a memory access. // memoryAccess encodes a memory access.
func memoryAccess(code []byte, opCode8 byte, opCode32 byte, register cpu.Register, offset int8, numBytes byte, source cpu.Register) []byte { func memoryAccess(code []byte, opCode8 byte, opCode32 byte, register cpu.Register, base cpu.Register, offset int8, length byte) []byte {
opCode := opCode32 opCode := opCode32
if numBytes == 1 { if length == 1 {
opCode = opCode8 opCode = opCode8
} }
mod := AddressMemory mod := AddressMemory
if offset != 0 || register == RBP || register == R13 { if offset != 0 || base == RBP || base == R13 {
mod = AddressMemoryOffset8 mod = AddressMemoryOffset8
} }
if numBytes == 2 { if length == 2 {
code = append(code, 0x66) code = append(code, 0x66)
} }
code = encode(code, mod, source, register, numBytes, opCode) code = encode(code, mod, register, base, length, opCode)
if register == RSP || register == R12 { if base == RSP || base == R12 {
code = append(code, SIB(Scale1, 0b100, 0b100)) code = append(code, SIB(Scale1, 0b100, 0b100))
} }

View File

@ -3,7 +3,7 @@ package x86
import "git.urbach.dev/cli/q/src/cpu" import "git.urbach.dev/cli/q/src/cpu"
// memoryAccessDynamic encodes a memory access using the value of a register as an offset. // memoryAccessDynamic encodes a memory access using the value of a register as an offset.
func memoryAccessDynamic(code []byte, opCode8 byte, opCode32 byte, destination cpu.Register, offset cpu.Register, numBytes byte, source cpu.Register) []byte { func memoryAccessDynamic(code []byte, opCode8 byte, opCode32 byte, register cpu.Register, base cpu.Register, offset cpu.Register, length byte) []byte {
var ( var (
w = byte(0) w = byte(0)
r = byte(0) r = byte(0)
@ -13,21 +13,21 @@ func memoryAccessDynamic(code []byte, opCode8 byte, opCode32 byte, destination c
mod = AddressMemory mod = AddressMemory
) )
if numBytes == 1 { if length == 1 {
opCode = opCode8 opCode = opCode8
} }
if offset == RSP { if offset == RSP {
offset, destination = destination, offset offset, base = base, offset
} }
if numBytes == 8 { if length == 8 {
w = 1 w = 1
} }
if source > 0b111 { if register > 0b111 {
r = 1 r = 1
source &= 0b111 register &= 0b111
} }
if offset > 0b111 { if offset > 0b111 {
@ -35,23 +35,23 @@ func memoryAccessDynamic(code []byte, opCode8 byte, opCode32 byte, destination c
offset &= 0b111 offset &= 0b111
} }
if destination > 0b111 { if base > 0b111 {
b = 1 b = 1
destination &= 0b111 base &= 0b111
} }
if destination == RBP || destination == R13 { if base == RBP || base == R13 {
mod = AddressMemoryOffset8 mod = AddressMemoryOffset8
} }
if numBytes == 2 { if length == 2 {
code = append(code, 0x66) code = append(code, 0x66)
} }
code = append(code, REX(w, r, x, b)) code = append(code, REX(w, r, x, b))
code = append(code, opCode) code = append(code, opCode)
code = append(code, ModRM(mod, byte(source), 0b100)) code = append(code, ModRM(mod, byte(register), 0b100))
code = append(code, SIB(Scale1, byte(offset), byte(destination))) code = append(code, SIB(Scale1, byte(offset), byte(base)))
if mod == AddressMemoryOffset8 { if mod == AddressMemoryOffset8 {
code = append(code, 0x00) code = append(code, 0x00)