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.
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.

View File

@ -19,12 +19,8 @@ func Call(code []byte, offset uint32) []byte {
// Calls a function whose address is stored in the given register.
func CallRegister(code []byte, register cpu.Register) []byte {
if register > 0b111 {
return append(
code,
0x41,
0xFF,
0xD0+byte(register&0b111),
)
code = append(code, 0x41)
register &= 0b111
}
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.
// The memory address is relative to the next instruction.
func CallAtMemory(code []byte, register cpu.Register, offset int8) []byte {
mod := AddressMemory
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
func CallAtMemory(code []byte, base cpu.Register, offset int8) []byte {
return memoryAccess(code, 0xFF, 0xFF, 0b010, base, offset, 4)
}

View File

@ -4,5 +4,5 @@ import "git.urbach.dev/cli/q/src/cpu"
// LoadRegister loads from memory into a register.
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.
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)
)
// ModRM is used to generate a ModRM suffix.
// ModRM is used to generate a mode-register-memory suffix.
// - mod: 2 bits. The addressing mode.
// - reg: 3 bits. Register reference or opcode extension.
// - 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.
func MoveRegisterNumber32(code []byte, destination cpu.Register, number int) []byte {
b := byte(0)
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)))
code = encode(code, AddressDirect, 0, destination, 8, 0xC7)
return binary.LittleEndian.AppendUint32(code, uint32(number))
}

View File

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

View File

@ -6,9 +6,9 @@ import (
"git.urbach.dev/cli/q/src/cpu"
)
// StoreDynamicNumber stores a number into the memory address at `destination` with a register offset.
func StoreDynamicNumber(code []byte, destination cpu.Register, offset cpu.Register, length byte, number int) []byte {
code = memoryAccessDynamic(code, 0xC6, 0xC7, destination, offset, length, 0b000)
// StoreDynamicNumber writes a number to a memory address with a register offset.
func StoreDynamicNumber(code []byte, base cpu.Register, offset cpu.Register, length byte, number int) []byte {
code = memoryAccessDynamic(code, 0xC6, 0xC7, 0b000, base, offset, length)
switch length {
case 8, 4:
@ -21,7 +21,7 @@ func StoreDynamicNumber(code []byte, destination cpu.Register, offset cpu.Regist
return append(code, byte(number))
}
// StoreDynamicRegister stores the contents of the `source` register into the memory address at `destination` with a register offset.
func StoreDynamicRegister(code []byte, destination cpu.Register, offset cpu.Register, length byte, source cpu.Register) []byte {
return memoryAccessDynamic(code, 0x88, 0x89, destination, offset, length, source)
// StoreDynamicRegister writes the contents of a register to a memory address with a register offset.
func StoreDynamicRegister(code []byte, base cpu.Register, offset cpu.Register, length byte, source cpu.Register) []byte {
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"
// 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
if numBytes == 1 {
if length == 1 {
opCode = opCode8
}
mod := AddressMemory
if offset != 0 || register == RBP || register == R13 {
if offset != 0 || base == RBP || base == R13 {
mod = AddressMemoryOffset8
}
if numBytes == 2 {
if length == 2 {
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))
}

View File

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