Implemented register to register instructions
This commit is contained in:
parent
31845dbc48
commit
4fc1935183
@ -3,9 +3,8 @@ main() {
|
||||
}
|
||||
|
||||
hello() {
|
||||
one := 1
|
||||
write := one
|
||||
stdout := one
|
||||
write := 1
|
||||
stdout := 1
|
||||
address := 4194305
|
||||
length := 3
|
||||
|
||||
|
@ -142,13 +142,18 @@ func (f *Function) CompileVariableDefinition(expr *expression.Expression) error
|
||||
return err
|
||||
}
|
||||
|
||||
// All expressions are returned to the memory pool.
|
||||
// To avoid losing variable values, we will detach it from the expression.
|
||||
expr.RemoveChild(value)
|
||||
reg, exists := f.CPU.FindFree()
|
||||
|
||||
if !exists {
|
||||
panic("no free registers")
|
||||
}
|
||||
|
||||
f.ExpressionToRegister(value, reg)
|
||||
f.CPU.Use(reg)
|
||||
|
||||
f.Variables[name] = &Variable{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Name: name,
|
||||
Register: reg,
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -197,7 +202,8 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error {
|
||||
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
|
||||
}
|
||||
|
||||
return f.ExpressionToRegister(variable.Value, register)
|
||||
f.Assembler.MoveRegisterRegister(register, variable.Register)
|
||||
return nil
|
||||
|
||||
case token.Number:
|
||||
value := t.Text()
|
||||
|
@ -1,9 +1,11 @@
|
||||
package build
|
||||
|
||||
import "git.akyoto.dev/cli/q/src/build/expression"
|
||||
import (
|
||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||
)
|
||||
|
||||
// Variable represents a variable in a function.
|
||||
type Variable struct {
|
||||
Name string
|
||||
Value *expression.Expression
|
||||
Name string
|
||||
Register cpu.Register
|
||||
}
|
||||
|
9
src/build/arch/x64/ModRM.go
Normal file
9
src/build/arch/x64/ModRM.go
Normal file
@ -0,0 +1,9 @@
|
||||
package x64
|
||||
|
||||
// ModRM is used to generate a ModRM suffix.
|
||||
// - mod: 2 bits
|
||||
// - reg: 3 bits
|
||||
// - rm: 3 bits
|
||||
func ModRM(mod byte, reg byte, rm byte) byte {
|
||||
return (mod << 6) | (reg << 3) | rm
|
||||
}
|
34
src/build/arch/x64/ModRM_test.go
Normal file
34
src/build/arch/x64/ModRM_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
package x64_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/build/arch/x64"
|
||||
"git.akyoto.dev/go/assert"
|
||||
)
|
||||
|
||||
func TestModRM(t *testing.T) {
|
||||
testData := []struct{ mod, reg, rm, expected byte }{
|
||||
{0b_00, 0b_111, 0b_000, 0b_00_111_000},
|
||||
{0b_00, 0b_110, 0b_001, 0b_00_110_001},
|
||||
{0b_00, 0b_101, 0b_010, 0b_00_101_010},
|
||||
{0b_00, 0b_100, 0b_011, 0b_00_100_011},
|
||||
{0b_00, 0b_011, 0b_100, 0b_00_011_100},
|
||||
{0b_00, 0b_010, 0b_101, 0b_00_010_101},
|
||||
{0b_00, 0b_001, 0b_110, 0b_00_001_110},
|
||||
{0b_00, 0b_000, 0b_111, 0b_00_000_111},
|
||||
{0b_11, 0b_111, 0b_000, 0b_11_111_000},
|
||||
{0b_11, 0b_110, 0b_001, 0b_11_110_001},
|
||||
{0b_11, 0b_101, 0b_010, 0b_11_101_010},
|
||||
{0b_11, 0b_100, 0b_011, 0b_11_100_011},
|
||||
{0b_11, 0b_011, 0b_100, 0b_11_011_100},
|
||||
{0b_11, 0b_010, 0b_101, 0b_11_010_101},
|
||||
{0b_11, 0b_001, 0b_110, 0b_11_001_110},
|
||||
{0b_11, 0b_000, 0b_111, 0b_11_000_111},
|
||||
}
|
||||
|
||||
for _, test := range testData {
|
||||
modRM := x64.ModRM(test.mod, test.reg, test.rm)
|
||||
assert.Equal(t, modRM, test.expected)
|
||||
}
|
||||
}
|
@ -1,13 +1,43 @@
|
||||
package x64
|
||||
|
||||
import "git.akyoto.dev/cli/q/src/build/cpu"
|
||||
|
||||
// MoveRegNum32 moves a 32 bit integer into the given register.
|
||||
func MoveRegNum32(code []byte, register uint8, number uint32) []byte {
|
||||
func MoveRegNum32(code []byte, destination cpu.Register, number uint32) []byte {
|
||||
if destination >= 8 {
|
||||
code = append(code, REX(0, 0, 0, 1))
|
||||
destination -= 8
|
||||
}
|
||||
|
||||
return append(
|
||||
code,
|
||||
0xb8+register,
|
||||
0xb8+byte(destination),
|
||||
byte(number),
|
||||
byte(number>>8),
|
||||
byte(number>>16),
|
||||
byte(number>>24),
|
||||
)
|
||||
}
|
||||
|
||||
// MoveRegReg64 moves a register value into another register.
|
||||
func MoveRegReg64(code []byte, destination cpu.Register, source cpu.Register) []byte {
|
||||
r := byte(0) // Extension to the "reg" field in ModRM.
|
||||
b := byte(0) // Extension to the "rm" field in ModRM or the SIB base (r8 up to r15 use this).
|
||||
|
||||
if source >= 8 {
|
||||
r = 1
|
||||
source -= 8
|
||||
}
|
||||
|
||||
if destination >= 8 {
|
||||
b = 1
|
||||
destination -= 8
|
||||
}
|
||||
|
||||
return append(
|
||||
code,
|
||||
REX(1, r, 0, b),
|
||||
0x89,
|
||||
ModRM(0b11, byte(source), byte(destination)),
|
||||
)
|
||||
}
|
||||
|
7
src/build/arch/x64/REX.go
Normal file
7
src/build/arch/x64/REX.go
Normal file
@ -0,0 +1,7 @@
|
||||
package x64
|
||||
|
||||
// REX is used to generate a REX prefix.
|
||||
// w, r, x and b can only be set to either 0 or 1.
|
||||
func REX(w, r, x, b byte) byte {
|
||||
return 0b_0100_0000 | (w << 3) | (r << 2) | (x << 1) | b
|
||||
}
|
34
src/build/arch/x64/REX_test.go
Normal file
34
src/build/arch/x64/REX_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
package x64_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/build/arch/x64"
|
||||
"git.akyoto.dev/go/assert"
|
||||
)
|
||||
|
||||
func TestREX(t *testing.T) {
|
||||
testData := []struct{ w, r, x, b, expected byte }{
|
||||
{0, 0, 0, 0, 0b_0100_0000},
|
||||
{0, 0, 0, 1, 0b_0100_0001},
|
||||
{0, 0, 1, 0, 0b_0100_0010},
|
||||
{0, 0, 1, 1, 0b_0100_0011},
|
||||
{0, 1, 0, 0, 0b_0100_0100},
|
||||
{0, 1, 0, 1, 0b_0100_0101},
|
||||
{0, 1, 1, 0, 0b_0100_0110},
|
||||
{0, 1, 1, 1, 0b_0100_0111},
|
||||
{1, 0, 0, 0, 0b_0100_1000},
|
||||
{1, 0, 0, 1, 0b_0100_1001},
|
||||
{1, 0, 1, 0, 0b_0100_1010},
|
||||
{1, 0, 1, 1, 0b_0100_1011},
|
||||
{1, 1, 0, 0, 0b_0100_1100},
|
||||
{1, 1, 0, 1, 0b_0100_1101},
|
||||
{1, 1, 1, 0, 0b_0100_1110},
|
||||
{1, 1, 1, 1, 0b_0100_1111},
|
||||
}
|
||||
|
||||
for _, test := range testData {
|
||||
rex := x64.REX(test.w, test.r, test.x, test.b)
|
||||
assert.Equal(t, rex, test.expected)
|
||||
}
|
||||
}
|
@ -28,8 +28,13 @@ func (a *Assembler) Finalize() ([]byte, []byte) {
|
||||
for _, x := range a.Instructions {
|
||||
switch x.Mnemonic {
|
||||
case MOVE:
|
||||
regNum := x.Data.(*RegisterNumber)
|
||||
code = x64.MoveRegNum32(code, uint8(regNum.Register), uint32(regNum.Number))
|
||||
switch operands := x.Data.(type) {
|
||||
case *RegisterNumber:
|
||||
code = x64.MoveRegNum32(code, operands.Register, uint32(operands.Number))
|
||||
|
||||
case *RegisterRegister:
|
||||
code = x64.MoveRegReg64(code, operands.Destination, operands.Source)
|
||||
}
|
||||
|
||||
case RETURN:
|
||||
code = x64.Return(code)
|
||||
|
@ -13,6 +13,17 @@ func (a *Assembler) MoveRegisterNumber(reg cpu.Register, number uint64) {
|
||||
})
|
||||
}
|
||||
|
||||
// MoveRegisterRegister moves a register value into another register.
|
||||
func (a *Assembler) MoveRegisterRegister(destination cpu.Register, source cpu.Register) {
|
||||
a.Instructions = append(a.Instructions, Instruction{
|
||||
Mnemonic: MOVE,
|
||||
Data: &RegisterRegister{
|
||||
Destination: destination,
|
||||
Source: source,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Label adds a label at the current position.
|
||||
func (a *Assembler) Label(name string) {
|
||||
a.Instructions = append(a.Instructions, Instruction{
|
||||
|
18
src/build/asm/RegisterRegister.go
Normal file
18
src/build/asm/RegisterRegister.go
Normal file
@ -0,0 +1,18 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||
)
|
||||
|
||||
// RegisterRegister operates with two registers.
|
||||
type RegisterRegister struct {
|
||||
Destination cpu.Register
|
||||
Source cpu.Register
|
||||
}
|
||||
|
||||
// String returns a human readable version.
|
||||
func (data *RegisterRegister) String() string {
|
||||
return fmt.Sprintf("%s, %s", data.Destination, data.Source)
|
||||
}
|
Loading…
Reference in New Issue
Block a user