Implemented register to register instructions
This commit is contained in:
parent
31845dbc48
commit
4fc1935183
@ -3,9 +3,8 @@ main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hello() {
|
hello() {
|
||||||
one := 1
|
write := 1
|
||||||
write := one
|
stdout := 1
|
||||||
stdout := one
|
|
||||||
address := 4194305
|
address := 4194305
|
||||||
length := 3
|
length := 3
|
||||||
|
|
||||||
|
@ -142,13 +142,18 @@ func (f *Function) CompileVariableDefinition(expr *expression.Expression) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// All expressions are returned to the memory pool.
|
reg, exists := f.CPU.FindFree()
|
||||||
// To avoid losing variable values, we will detach it from the expression.
|
|
||||||
expr.RemoveChild(value)
|
if !exists {
|
||||||
|
panic("no free registers")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.ExpressionToRegister(value, reg)
|
||||||
|
f.CPU.Use(reg)
|
||||||
|
|
||||||
f.Variables[name] = &Variable{
|
f.Variables[name] = &Variable{
|
||||||
Name: name,
|
Name: name,
|
||||||
Value: value,
|
Register: reg,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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 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:
|
case token.Number:
|
||||||
value := t.Text()
|
value := t.Text()
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package build
|
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.
|
// Variable represents a variable in a function.
|
||||||
type Variable struct {
|
type Variable struct {
|
||||||
Name string
|
Name string
|
||||||
Value *expression.Expression
|
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
|
package x64
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
|
||||||
// MoveRegNum32 moves a 32 bit integer into the given register.
|
// 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(
|
return append(
|
||||||
code,
|
code,
|
||||||
0xb8+register,
|
0xb8+byte(destination),
|
||||||
byte(number),
|
byte(number),
|
||||||
byte(number>>8),
|
byte(number>>8),
|
||||||
byte(number>>16),
|
byte(number>>16),
|
||||||
byte(number>>24),
|
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 {
|
for _, x := range a.Instructions {
|
||||||
switch x.Mnemonic {
|
switch x.Mnemonic {
|
||||||
case MOVE:
|
case MOVE:
|
||||||
regNum := x.Data.(*RegisterNumber)
|
switch operands := x.Data.(type) {
|
||||||
code = x64.MoveRegNum32(code, uint8(regNum.Register), uint32(regNum.Number))
|
case *RegisterNumber:
|
||||||
|
code = x64.MoveRegNum32(code, operands.Register, uint32(operands.Number))
|
||||||
|
|
||||||
|
case *RegisterRegister:
|
||||||
|
code = x64.MoveRegReg64(code, operands.Destination, operands.Source)
|
||||||
|
}
|
||||||
|
|
||||||
case RETURN:
|
case RETURN:
|
||||||
code = x64.Return(code)
|
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.
|
// Label adds a label at the current position.
|
||||||
func (a *Assembler) Label(name string) {
|
func (a *Assembler) Label(name string) {
|
||||||
a.Instructions = append(a.Instructions, Instruction{
|
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