From 7ff831160a50eaa2d7c0b8660106ce58d5f6082e Mon Sep 17 00:00:00 2001 From: "Gabriel G. de Brito" Date: Fri, 27 Mar 2026 18:36:43 -0300 Subject: [PATCH] Refactored REDUX-V to a common package. --- main.go | 9 +- reduxK/reduxK.go | 414 ++++------------------------------- reduxK/reduxK_test.go | 2 +- reduxPia/produto_escalar.asm | 53 +++++ reduxPia/reduxPia.go | 394 +++++---------------------------- reduxPia/soma_vetorial.asm | 45 ---- reduxc/reduxc.go | 385 ++++++++++++++++++++++++++++++++ reduxv/reduxv.go | 348 ++--------------------------- reduxv/reduxv_test.go | 3 +- 9 files changed, 571 insertions(+), 1082 deletions(-) create mode 100644 reduxPia/produto_escalar.asm delete mode 100644 reduxPia/soma_vetorial.asm create mode 100644 reduxc/reduxc.go diff --git a/main.go b/main.go index d2b1694..32da57e 100644 --- a/main.go +++ b/main.go @@ -113,14 +113,11 @@ func main() { var r sagui.Sagui m = &r case "reduxv": - var r reduxv.ReduxV - m = &r + m = reduxv.ReduxV() case "reduxK": - var r reduxK.ReduxK - m = &r + m = reduxK.ReduxK() case "reduxPia": - var r reduxPia.ReduxPia - m = &r + m = reduxPia.ReduxPia() default: log.Printf(machine.InterCtx.Get("Unknown architeture: %v\n"), architeture) listArchs() diff --git a/reduxK/reduxK.go b/reduxK/reduxK.go index 4f09975..b8c0d9c 100644 --- a/reduxK/reduxK.go +++ b/reduxK/reduxK.go @@ -6,159 +6,24 @@ package reduxK import ( "errors" "fmt" - "math" - "strconv" "github.com/gboncoffee/egg/assembler" "github.com/gboncoffee/egg/machine" + "github.com/gboncoffee/egg/reduxc" ) -type ReduxK struct { - registers [4]uint8 +type ReduxKExtState struct { auxRegisters [2]uint8 - mem [math.MaxUint8 + 1]uint8 - pc uint8 } -func signExtend8(n uint8) uint8 { - sign := n >> 3 - sign8 := uint8(^(sign - 1)) << 4 - return n | sign8 -} - -func (m *ReduxK) GetCurrentInstructionAddress() uint64 { - return uint64(m.pc) -} - -func (m *ReduxK) ArchitectureInfo() machine.ArchitectureInfo { - return machine.ArchitectureInfo{ - Name: "REDUX-K", - RegistersNames: []string{"0", "1", "2", "3"}, - WordWidth: 8, - } -} - -// -// "Getters and setters" are literally copy-pasted from Sagui. Maybe we should -// factor them out in a library for creating the machines? That would be rather -// cool. Actually, imagine simply defining some stuff and BOOM you got a new -// architecture? -// - -func (m *ReduxK) SetRegister(reg uint64, value uint64) error { - if reg > 3 { - return fmt.Errorf(machine.InterCtx.Get("no such register: %v"), reg) - } - if value > math.MaxUint8 { - return fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), value) - } - m.registers[reg] = uint8(value) - return nil -} - -func (m *ReduxK) GetRegister(reg uint64) (uint64, error) { - if reg > 3 { - return 0, fmt.Errorf(machine.InterCtx.Get("no such register: %v"), reg) - } - - return uint64(m.registers[reg]), nil -} - -func (m *ReduxK) SetAuxRegister(reg uint64, value uint64) error { - if reg > 1 { - return fmt.Errorf(machine.InterCtx.Get("no such register: %v"), reg) - } - if value > math.MaxUint8 { - return fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), value) - } - m.auxRegisters[reg] = uint8(value) - return nil -} - -func (m *ReduxK) GetAuxRegister(reg uint64) (uint64, error) { - if reg > 1 { - return 0, fmt.Errorf(machine.InterCtx.Get("no such register: %v"), reg) - } - - return uint64(m.auxRegisters[reg]), nil -} - -func (m *ReduxK) SetMemory(addr uint64, value uint8) error { - if addr > math.MaxUint8 { - return fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), addr, math.MaxUint8) - } - m.mem[addr] = value - return nil -} - -func (m *ReduxK) GetMemory(addr uint64) (uint8, error) { - if addr > math.MaxUint8 { - return 0, fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), addr, math.MaxUint8) - } - return m.mem[addr], nil -} - -func (m *ReduxK) SetMemoryChunk(addr uint64, content []uint8) error { - end := addr + (uint64(len(content)) - 1) - if end > math.MaxUint8 { - return fmt.Errorf(machine.InterCtx.Get("end address %v bigger than maximum 8 bit address %v"), end, math.MaxUint8) - } - - for _, b := range content { - m.mem[addr] = b - addr++ - } - return nil -} - -func (m *ReduxK) GetMemoryChunk(addr uint64, size uint64) ([]uint8, error) { - end := addr + (size - 1) - if end > math.MaxUint8 { - return nil, fmt.Errorf(machine.InterCtx.Get("end address %v bigger than maximum 8 bit address %v"), end, math.MaxUint8) - } - return m.mem[addr:(end + 1)], nil -} - -func (m *ReduxK) LoadProgram(program []uint8) error { - m.pc = 0 - return m.SetMemoryChunk(0, program) -} - -func (m *ReduxK) NextInstruction() (*machine.Call, error) { - instr, err := m.GetMemory(m.GetCurrentInstructionAddress()) - if err != nil { - return nil, fmt.Errorf(machine.InterCtx.Get("failed to fetch instruction from memory: %v"), err) - } - - op := instr >> 4 - imm := instr & 0xf - ra := (instr >> 2) & 0x3 - rb := instr & 0x3 - - uimm := instr & 0x3 - sizev := instr & 0xf - - rav, _ := m.GetRegister(uint64(ra)) - rbv, _ := m.GetRegister(uint64(rb)) - r0v, _ := m.GetRegister(0) +func reduxKExecuteExtension(bin uint8, m *reduxc.ReduxC) (bool, *machine.Call, error) { + op := bin >> 4 + ext := m.AdditionalState().(*ReduxKExtState) switch op { - case 0x0: - if rav == 0 { - // Less one because the pc will be incremented. - m.pc = uint8(rbv) - 1 - } - case 0x1: - m.pc += signExtend8(uint8(imm)) - 1 - case 0x2: - mem, _ := m.GetMemory(rbv) - _ = m.SetRegister(uint64(ra), uint64(mem)) - case 0x3: - _ = m.SetMemory(rbv, uint8(rav)) - case 0x4: - _ = m.SetRegister(0, uint64(uint8(r0v)+signExtend8(imm))) case 0x5: - for x := int8(0); x < int8(sizev); x++ { + *m.PC() = *m.PC() + 1 + for x := int8(0); x < int8(bin&0xf); x++ { r0v, _ := m.GetRegister(0) r1v, _ := m.GetRegister(1) r2v, _ := m.GetRegister(2) @@ -168,18 +33,20 @@ func (m *ReduxK) NextInstruction() (*machine.Call, error) { _ = m.SetRegister(uint64(2), uint64(uint8(int8(r2v)+int8(r3v)))) _ = m.SetRegister(uint64(0), uint64(uint8(int8(r0v)+int8(r1v)))) } + return true, nil, nil case 0x6: - for x := int8(0); x < int8(sizev); x++ { + *m.PC() = *m.PC() + 1 + for x := int8(0); x < int8(bin&0xf); x++ { r0v, _ := m.GetRegister(0) memr0, _ := m.GetMemory(r0v) - _ = m.SetAuxRegister(0, uint64(memr0)) + ext.auxRegisters[0] = memr0 r1v, _ := m.GetRegister(1) memr1, _ := m.GetMemory(r1v) - _ = m.SetAuxRegister(1, uint64(memr1)) + ext.auxRegisters[1] = memr1 - rx, _ := m.GetAuxRegister(0) - ry, _ := m.GetAuxRegister(1) + rx := ext.auxRegisters[0] + ry := ext.auxRegisters[1] _ = m.SetRegister(uint64(3), uint64(uint8(int8(rx)+int8(ry)))) @@ -193,159 +60,58 @@ func (m *ReduxK) NextInstruction() (*machine.Call, error) { _ = m.SetRegister(uint64(x), uint64(uint8(int8(regv)+int8(1)))) } } + return true, nil, nil case 0x7: + ra := (bin >> 2) & 0x3 if ra == 0 { for x := 0; x < 4; x++ { regv, _ := m.GetRegister(uint64(x)) - _ = m.SetRegister(uint64(x), uint64(uint8(int8(regv)+int8(uimm)))) + _ = m.SetRegister(uint64(x), uint64(uint8(int8(regv)+int8(bin&0x3)))) } break } - _ = m.SetRegister(uint64(ra), uint64(uint8(int8(rav)+int8(uimm)))) - case 0x8: - if rbv == 0 { - _ = m.SetRegister(uint64(ra), 1) - } else { - _ = m.SetRegister(uint64(ra), 0) - } - case 0x9: - _ = m.SetRegister(uint64(ra), rav&rbv) + rav, _ := m.GetRegister(uint64(ra)) + _ = m.SetRegister(uint64(ra), uint64(uint8(int8(rav)+int8(bin&0x3)))) + *m.PC() = *m.PC() + 1 + return true, nil, nil case 0xa: - if rb == 0 && ra == 0 { - m.pc++ - return &machine.Call{ + if bin == 0xa0 { + *m.PC() = *m.PC() + 1 + return true, &machine.Call{ Number: machine.SYS_BREAK, - Arg1: 0, - Arg2: 0, }, nil } - - _ = m.SetRegister(uint64(ra), rav|rbv) case 0xb: - if rb == 0 && ra == 0 { - m.pc++ + if bin == 0xb0 { + *m.PC() = *m.PC() + 1 + r0v, _ := m.GetRegister(0) r1v, _ := m.GetRegister(1) r2v, _ := m.GetRegister(2) - return &machine.Call{ + return true, &machine.Call{ Number: r0v, Arg1: r1v, Arg2: r2v, }, nil } - - _ = m.SetRegister(uint64(ra), rav^rbv) - case 0xc: - _ = m.SetRegister(uint64(ra), uint64(uint8(int8(rav)+int8(rbv)))) - case 0xd: - _ = m.SetRegister(uint64(ra), uint64(uint8(int8(rav)-int8(rbv)))) - case 0xe: - _ = m.SetRegister(uint64(ra), (rav<>rbv) - } - - m.pc++ - return nil, nil -} - -func getRegisterNumber(r string) (uint64, error) { - switch r { - case "r0", "0": - return 0, nil - case "r1", "1": - return 1, nil - case "r2", "2": - return 2, nil - case "r3", "3": - return 3, nil - default: - return 0, fmt.Errorf(machine.InterCtx.Get("no such register: %v"), r) } -} - -func (m *ReduxK) GetRegisterNumber(r string) (uint64, error) { - return getRegisterNumber(r) -} -// Also heavily-based on Sagui. -func translateArgs(arg string) (uint64, error) { - if len(arg) < 1 { - return 0, errors.New(machine.InterCtx.Get("empty argument")) - } - reg, err := getRegisterNumber(arg) - if err != nil { - n, err := strconv.ParseInt(arg, 0, 64) - if n > 0xf { - return 0, fmt.Errorf(machine.InterCtx.Get("immediate bigger than immediate size: %v"), arg) - } - return uint64(n), err - } - return reg, nil + return false, nil, nil } -func assembleR(t assembler.ResolvedToken) (uint8, error) { - if len(t.Args) != 2 { - return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 2 arguments"), t.Value) - } - - ra := uint8(t.Args[0]) & 0x3 - rb := uint8(t.Args[1]) & 0x3 - var op uint8 - switch string(t.Value) { - case "brzr": - op = 0x0 - case "ld": - op = 0x2 - case "st": - op = 0x3 - case "not": - op = 0x8 - case "and": - op = 0x9 - case "or": - op = 0xa - case "xor": - op = 0xb - case "add": - op = 0xc - case "sub": - op = 0xd - case "slr": - op = 0xe - case "srr": - op = 0xf - } - - return (op << 4) | (ra << 2) | rb, nil -} - -func assembleI(t assembler.ResolvedToken) (uint8, error) { - if len(t.Args) != 1 { - return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 1 argument"), t.Value) - } - - imm := uint8(t.Args[0]) & 0xf - var op uint8 +func reduxKAssembleExtension(t assembler.ResolvedToken) (uint8, error) { switch string(t.Value) { - case "ji": - op = 0x1 - case "addi": - op = 0x4 - default: - return 0, errors.New("unreachable") - } - - return (op << 4) | imm, nil -} - -func assembleJi(t assembler.ResolvedToken) (uint8, error) { - if len(t.Args) != 1 { - return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 1 argument"), t.Value) + case "inc": + return assembleInc(t) + case "loadv", "addv": + return assembleV(t) + case "ebreak": + return 0xa0, nil + case "ecall": + return 0xb0, nil } - t.Args[0] = uint64(int8(signExtend8(uint8(t.Args[0]))) - int8(t.Address)) - return assembleI(t) + return 0, fmt.Errorf(machine.InterCtx.Get("Unknown instruction: %v"), string(t.Value)) } func assembleInc(t assembler.ResolvedToken) (uint8, error) { @@ -356,15 +122,7 @@ func assembleInc(t assembler.ResolvedToken) (uint8, error) { ra := uint8(t.Args[0]) & 0x3 uimm := uint8(t.Args[1]) & 0x3 - var op uint8 - switch string(t.Value) { - case "inc": - op = 0x7 - default: - return 0, errors.New("unreachable") - } - - return (op << 4) | (ra << 2) | uimm, nil + return (0x7 << 4) | (ra << 2) | uimm, nil } func assembleV(t assembler.ResolvedToken) (uint8, error) { @@ -386,89 +144,11 @@ func assembleV(t assembler.ResolvedToken) (uint8, error) { return (op << 4) | sizev, nil } -func assembleInstruction(code []uint8, addr int, t assembler.ResolvedToken) error { - bin := uint8(0) - var err error - - switch string(t.Value) { - case "ji": - bin, err = assembleJi(t) - case "addi": - bin, err = assembleI(t) - case "brzr", "ld", "st", "not", "and", "or", "xor", "add", "sub", "slr", "srr": - bin, err = assembleR(t) - case "loadv", "addv": - bin, err = assembleV(t) - case "inc": - bin, err = assembleInc(t) - case "ebreak": - bin = 0xa0 - case "ecall": - bin = 0xb0 - default: - return fmt.Errorf(machine.InterCtx.Get("unknown instruction: %v"), string(t.Value)) - } - - if err != nil { - return err - } - - code[addr] = bin - return nil -} - -func assemble(t []assembler.ResolvedToken) ([]uint8, error) { - size := uint64(0) - for _, i := range t { - if i.Type == assembler.TOKEN_INSTRUCTION { - size += 1 - } else { - size += uint64(len(i.Value)) - } - } - - var err error - - code := make([]uint8, size) - addr := 0 - for _, i := range t { - if i.Type == assembler.TOKEN_INSTRUCTION { - err = assembleInstruction(code, addr, i) - if err != nil { - return nil, fmt.Errorf(machine.InterCtx.Get("%v:%v: Error assembling: %v"), *i.File, i.Line, err) - } - addr++ - } else { - for _, c := range []uint8(i.Value) { - code[addr] = c - addr++ - } - } - } - - return code, nil -} - -func (m *ReduxK) Assemble(file string) ([]uint8, []assembler.DebuggerToken, error) { - tokens := []assembler.Token{} - err := assembler.Tokenize(file, &tokens) - if err != nil { - return nil, nil, err - } - - resolvedTokens, debuggerTokens, err := assembler.ResolveTokens(tokens, func(i *assembler.Instruction) error { - i.Size = 1 - return nil - }, translateArgs) - - if err != nil { - return nil, nil, err - } - - code, err := assemble(resolvedTokens) - if err != nil { - return nil, nil, err - } - - return code, debuggerTokens, nil +func ReduxK() *reduxc.ReduxC { + return reduxc.ReduxVExtension( + "REDUX-K", + reduxKExecuteExtension, + reduxKAssembleExtension, + &ReduxKExtState{}, + ) } diff --git a/reduxK/reduxK_test.go b/reduxK/reduxK_test.go index 5b462f2..9a828c5 100644 --- a/reduxK/reduxK_test.go +++ b/reduxK/reduxK_test.go @@ -12,7 +12,7 @@ func TestReduxK(t *testing.T) { _ = machine.InterCtx.AutoSetPreferedLocale() assembler.InterCtx = &machine.InterCtx - var m ReduxK + m := ReduxK() code, _, err := m.Assemble("test.asm") if err != nil { t.Fatalf("Couldn't assemble: %v", err) diff --git a/reduxPia/produto_escalar.asm b/reduxPia/produto_escalar.asm new file mode 100644 index 0000000..76a1573 --- /dev/null +++ b/reduxPia/produto_escalar.asm @@ -0,0 +1,53 @@ + xor r0, r0 + xor r1, r1 + xor r2, r2 + xor r3, r3 + + addi 5 + add r3, r0 + + xor r0, r0 +for: +;; Por conveniência, vamos usar a posição 0 da memória para guardar o +;; acumulador. Esse loop tem exatamente o tamanho limite para usar a instrução +;; loop. + xor r1, r1 + st r0, r1 + + xor r0, r0 +;; Os vetores ficam em 0x20 para salvar uma instrução aqui e manter o loop no +;; tamanho para utilizar a instrução loop. + ldui 2 + add r0, r3 + xor r2, r2 + add r1, r0 + add r2, r0 + xor r0, r0 + addi 5 + add r2, r0 + ld r1, r1 + ld r2, r2 + + xor r0, r0 + ld r0, r0 + + mac r1, r2 + + loop for + +;; ji 0 + ebreak + +.space 8 +a: +.bits8 1 +.bits8 2 +.bits8 3 +.bits8 4 +.bits8 5 +b: +.bits8 5 +.bits8 4 +.bits8 3 +.bits8 2 +.bits8 1 diff --git a/reduxPia/reduxPia.go b/reduxPia/reduxPia.go index 4302d83..3d22dc6 100644 --- a/reduxPia/reduxPia.go +++ b/reduxPia/reduxPia.go @@ -1,370 +1,96 @@ // Package reduxPia implements a REDUX-PIÁ machine for EGG. -// ji 0 performs a call. +// ji 0 performs an ebreak. or r0, r0 performs an ecall. package reduxPia import ( - "errors" "fmt" - "math" - "strconv" "github.com/gboncoffee/egg/assembler" "github.com/gboncoffee/egg/machine" + "github.com/gboncoffee/egg/reduxc" ) -type ReduxPia struct { - registers [4]uint8 - mem [math.MaxUint8 + 1]uint8 - pc uint8 -} - -func signExtend8(n uint8) uint8 { - sign := n >> 3 - sign8 := uint8(^(sign - 1)) << 4 - return n | sign8 -} - -func (m *ReduxPia) GetCurrentInstructionAddress() uint64 { - return uint64(m.pc) -} - -func (m *ReduxPia) ArchitectureInfo() machine.ArchitectureInfo { - return machine.ArchitectureInfo{ - Name: "REDUX-V", - RegistersNames: []string{"0", "1", "2", "3"}, - WordWidth: 8, - } -} - -// -// "Getters and setters" are literally copy-pasted from Sagui. Maybe we should -// factor them out in a library for creating the machines? That would be rather -// cool. Actually, imagine simply defining some stuff and BOOM you got a new -// architecture? -// - -func (m *ReduxPia) SetRegister(reg uint64, value uint64) error { - if reg > 3 { - return fmt.Errorf(machine.InterCtx.Get("no such register: %v"), reg) - } - if value > math.MaxUint8 { - return fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), value) - } - m.registers[reg] = uint8(value) - return nil -} - -func (m *ReduxPia) GetRegister(reg uint64) (uint64, error) { - if reg > 3 { - return 0, fmt.Errorf(machine.InterCtx.Get("no such register: %v"), reg) - } - - return uint64(m.registers[reg]), nil -} - -func (m *ReduxPia) SetMemory(addr uint64, value uint8) error { - if addr > math.MaxUint8 { - return fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), addr, math.MaxUint8) - } - m.mem[addr] = value - return nil -} - -func (m *ReduxPia) GetMemory(addr uint64) (uint8, error) { - if addr > math.MaxUint8 { - return 0, fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), addr, math.MaxUint8) - } - return m.mem[addr], nil -} - -func (m *ReduxPia) SetMemoryChunk(addr uint64, content []uint8) error { - end := addr + (uint64(len(content)) - 1) - if end > math.MaxUint8 { - return fmt.Errorf(machine.InterCtx.Get("end address %v bigger than maximum 8 bit address %v"), end, math.MaxUint8) - } - - for _, b := range content { - m.mem[addr] = b - addr++ - } - return nil -} - -func (m *ReduxPia) GetMemoryChunk(addr uint64, size uint64) ([]uint8, error) { - end := addr + (size - 1) - if end > math.MaxUint8 { - return nil, fmt.Errorf(machine.InterCtx.Get("end address %v bigger than maximum 8 bit address %v"), end, math.MaxUint8) - } - return m.mem[addr:(end + 1)], nil -} - -func (m *ReduxPia) LoadProgram(program []uint8) error { - m.pc = 0 - return m.SetMemoryChunk(0, program) -} - -func (m *ReduxPia) NextInstruction() (*machine.Call, error) { - instr, err := m.GetMemory(m.GetCurrentInstructionAddress()) - if err != nil { - return nil, fmt.Errorf(machine.InterCtx.Get("failed to fetch instruction from memory: %v"), err) - } - - op := instr >> 4 - imm := instr & 0xf - ra := (instr >> 2) & 0x3 - rb := instr & 0x3 - - rav, _ := m.GetRegister(uint64(ra)) - rbv, _ := m.GetRegister(uint64(rb)) - r0v, _ := m.GetRegister(0) - +func reduxPiaExecuteExtension(bin uint8, m *reduxc.ReduxC) (bool, *machine.Call, error) { + op := bin >> 4 switch op { - case 0x0: - if rav == 0 { - // Less one because the pc will be incremented. - m.pc = uint8(rbv) - 1 - } - case 0x1: - if imm == 0 { - m.pc++ - r1v, _ := m.GetRegister(1) - r2v, _ := m.GetRegister(2) - return &machine.Call{ - Number: r0v, - Arg1: r1v, - Arg2: r2v, - }, nil - } - m.pc += signExtend8(uint8(imm)) - 1 - case 0x2: - mem, _ := m.GetMemory(rbv) - _ = m.SetRegister(uint64(ra), uint64(mem)) - case 0x3: - _ = m.SetMemory(rbv, uint8(rav)) - case 0x4: - _ = m.SetRegister(0, uint64(uint8(r0v)+signExtend8(imm))) case 0x5: + *m.PC() = *m.PC() + 1 + r0v, _ := m.GetRegister(0) + imm := bin & 0xf _ = m.SetRegister(0, uint64((uint8(r0v)&0xf)|(imm<<4))) + return true, nil, nil case 0x6: + imm := bin & 0xf r3v, _ := m.GetRegister(3) r := uint64(r3v - 1) _ = m.SetRegister(3, r) if r != 0 { - m.pc -= imm + 1 - return nil, nil + *m.PC() = *m.PC() - (imm + 1) + } else { + *m.PC() = *m.PC() + 1 } + return true, nil, nil case 0x7: - _ = m.SetRegister(0, uint64(r0v)+rav*rbv) - case 0x8: - if rbv == 0 { - _ = m.SetRegister(uint64(ra), 1) - } else { - _ = m.SetRegister(uint64(ra), 0) + *m.PC() = *m.PC() + 1 + ra := (bin >> 2) & 0x3 + rb := bin & 0x3 + r0v, _ := m.GetRegister(0) + rav, _ := m.GetRegister(uint64(ra)) + rbv, _ := m.GetRegister(uint64(rb)) + _ = m.SetRegister(0, uint64(r0v+rav*rbv)) + return true, nil, nil + case 0x1: + if bin == 0x10 { + *m.PC() = *m.PC() + 1 + return true, &machine.Call{ + Number: machine.SYS_BREAK, + }, nil } - case 0x9: - _ = m.SetRegister(uint64(ra), rav&rbv) case 0xa: - _ = m.SetRegister(uint64(ra), rav|rbv) - case 0xb: - _ = m.SetRegister(uint64(ra), rav^rbv) - case 0xc: - _ = m.SetRegister(uint64(ra), uint64(uint8(int8(rav)+int8(rbv)))) - case 0xd: - _ = m.SetRegister(uint64(ra), uint64(uint8(int8(rav)-int8(rbv)))) - case 0xe: - _ = m.SetRegister(uint64(ra), (rav<>rbv) - } - - m.pc++ - return nil, nil -} - -func getRegisterNumber(r string) (uint64, error) { - switch r { - case "r0", "0": - return 0, nil - case "r1", "1": - return 1, nil - case "r2", "2": - return 2, nil - case "r3", "3": - return 3, nil - default: - return 0, fmt.Errorf(machine.InterCtx.Get("no such register: %v"), r) - } -} - -func (m *ReduxPia) GetRegisterNumber(r string) (uint64, error) { - return getRegisterNumber(r) -} - -// Also heavily-based on Sagui. -func translateArgs(arg string) (uint64, error) { - if len(arg) < 1 { - return 0, errors.New(machine.InterCtx.Get("empty argument")) - } - reg, err := getRegisterNumber(arg) - if err != nil { - n, err := strconv.ParseInt(arg, 0, 64) - if n > 0xf { - return 0, fmt.Errorf(machine.InterCtx.Get("immediate bigger than immediate size: %v"), arg) + if bin == 0xa0 { + r0v, _ := m.GetRegister(0) + r1v, _ := m.GetRegister(1) + r2v, _ := m.GetRegister(2) + *m.PC() = *m.PC() + 1 + return true, &machine.Call{ + Number: r0v, + Arg1: r1v, + Arg2: r2v, + }, nil } - return uint64(n), err - } - return reg, nil -} - -func assembleR(t assembler.ResolvedToken) (uint8, error) { - if len(t.Args) != 2 { - return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 2 arguments"), t.Value) } - ra := uint8(t.Args[0]) & 0x3 - rb := uint8(t.Args[1]) & 0x3 - var op uint8 - switch string(t.Value) { - case "brzr": - op = 0x0 - case "ld": - op = 0x2 - case "st": - op = 0x3 - case "mac": - op = 0x7 - case "not": - op = 0x8 - case "and": - op = 0x9 - case "or": - op = 0xa - case "xor": - op = 0xb - case "add": - op = 0xc - case "sub": - op = 0xd - case "slr": - op = 0xe - case "srr": - op = 0xf - } - - return (op << 4) | (ra << 2) | rb, nil + return false, nil, nil } -func assembleI(t assembler.ResolvedToken) (uint8, error) { - if len(t.Args) != 1 { - return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 1 argument"), t.Value) - } - - imm := uint8(t.Args[0]) & 0xf - var op uint8 +func reduxPiaAssembleExtension(t assembler.ResolvedToken) (uint8, error) { switch string(t.Value) { - case "ji": - op = 0x1 - case "addi": - op = 0x4 + case "mac": + if len(t.Args) != 2 { + return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 2 arguments"), string(t.Value)) + } + ra := uint8(t.Args[0]) & 0x3 + rb := uint8(t.Args[1]) & 0x3 + return 0x70 | (ra << 2) | rb, nil case "ldui": - op = 0x5 - case "loop": - op = 0x6 - imm = uint8(t.Address) - 1 - uint8(t.Args[0]) - default: - return 0, errors.New("unreachable") - } - - return (op << 4) | imm, nil -} - -func assembleJi(t assembler.ResolvedToken) (uint8, error) { - if len(t.Args) != 1 { - return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 1 argument"), t.Value) - } - - // Ugly code in the name of reuse. - t.Args[0] = uint64(int8(signExtend8(uint8(t.Args[0]))) - int8(t.Address)) - return assembleI(t) -} - -func assembleInstruction(code []uint8, addr int, t assembler.ResolvedToken) error { - bin := uint8(0) - var err error - - switch string(t.Value) { - case "ji": - bin, err = assembleJi(t) - case "addi", "ldui", "loop": - bin, err = assembleI(t) - case "brzr", "ld", "st", "not", "and", "or", "xor", "add", "sub", "slr", "srr", "mac": - bin, err = assembleR(t) - case "ecall": - bin = 0x10 - default: - return fmt.Errorf(machine.InterCtx.Get("unknown instruction: %v"), string(t.Value)) - } - - if err != nil { - return err - } - - code[addr] = bin - return nil -} - -func assemble(t []assembler.ResolvedToken) ([]uint8, error) { - size := uint64(0) - for _, i := range t { - if i.Type == assembler.TOKEN_INSTRUCTION { - size += 1 - } else { - size += uint64(len(i.Value)) + if len(t.Args) != 1 { + return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 2 arguments"), string(t.Value)) } - } - - var err error - - code := make([]uint8, size) - addr := 0 - for _, i := range t { - if i.Type == assembler.TOKEN_INSTRUCTION { - err = assembleInstruction(code, addr, i) - if err != nil { - return nil, fmt.Errorf(machine.InterCtx.Get("%v:%v: Error assembling: %v"), *i.File, i.Line, err) - } - addr++ - } else { - for _, c := range []uint8(i.Value) { - code[addr] = c - addr++ - } + return 0x50 | (uint8(t.Args[0]) & 0xf), nil + case "loop": + if len(t.Args) != 1 { + return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 2 arguments"), string(t.Value)) } + return 0x60 | (uint8(t.Address) - 1 - uint8(t.Args[0])), nil + case "ebreak": + return 0x10, nil + case "ecall": + return 0xa0, nil } - return code, nil + return 0, fmt.Errorf(machine.InterCtx.Get("Unknown instruction: %v"), string(t.Value)) } -func (m *ReduxPia) Assemble(file string) ([]uint8, []assembler.DebuggerToken, error) { - tokens := []assembler.Token{} - err := assembler.Tokenize(file, &tokens) - if err != nil { - return nil, nil, err - } - - resolvedTokens, debuggerTokens, err := assembler.ResolveTokens(tokens, func(i *assembler.Instruction) error { - i.Size = 1 - return nil - }, translateArgs) - - if err != nil { - return nil, nil, err - } - - code, err := assemble(resolvedTokens) - if err != nil { - return nil, nil, err - } - - return code, debuggerTokens, nil +func ReduxPia() *reduxc.ReduxC { + return reduxc.ReduxVExtension("REDUX-PIÁ", reduxPiaExecuteExtension, reduxPiaAssembleExtension, nil) } diff --git a/reduxPia/soma_vetorial.asm b/reduxPia/soma_vetorial.asm deleted file mode 100644 index f7bca73..0000000 --- a/reduxPia/soma_vetorial.asm +++ /dev/null @@ -1,45 +0,0 @@ -;; -;; Soma vetorial, Assembly de REDUX-PIÁ. -;; - - xor r0, r0 - xor r1, r1 - xor r2, r2 - xor r3, r3 - -;; mov r0, 42 - addi 0xA - ldui 0x2 - -;; mov r1, r0 - add r1, r0 - -;; mov r0, v - xor r0, r0 - addi 7 - ldui 1 - -;; mov r2, r0 - add r2, r0 - -;; mov r0, 3 - xor r0, r0 - addi 3 - -;; mov r3, r0 - add r3, r0 -;; mov r0, 1 - xor r0, r0 - addi 1 -for: - st r1, r2 - - add r1, r0 - add r2, r0 - - loop for - - xor r0, r0 - addi 1 - ecall -v: diff --git a/reduxc/reduxc.go b/reduxc/reduxc.go new file mode 100644 index 0000000..ba5deb3 --- /dev/null +++ b/reduxc/reduxc.go @@ -0,0 +1,385 @@ +// Package reduxc implements the commonality between all REDUX-V dialects. +// To implement a REDUX-V dialect, one needs +package reduxc + +import ( + "errors" + "fmt" + "math" + "strconv" + + "github.com/gboncoffee/egg/assembler" + "github.com/gboncoffee/egg/machine" +) + +// Executes an extension instruction. It's called before trying to execute any +// other instruction. When an extension was executed, it should return true +// indicating ReduxC it shouldn't try to execute the instruction as a normal +// one. Thus, it's possible to encode extensions also as normal instructions +// (for instance, a ji 0 can be an extension, and ReduxC won't try to execute it +// as a jump). +type ExtensionExecutionFunction func(bin uint8, m *ReduxC) (bool, *machine.Call, error) + +type ExtensionAssembleFunction func(t assembler.ResolvedToken) (uint8, error) + +type ReduxC struct { + mem [math.MaxUint8 + 1]uint8 + name string + executeExtension ExtensionExecutionFunction + assembleExtension ExtensionAssembleFunction + additionalState any + registers [4]uint8 + pc uint8 +} + +func ReduxVExtension(name string, e ExtensionExecutionFunction, a ExtensionAssembleFunction, additionalState any) *ReduxC { + return &ReduxC{ + executeExtension: e, + assembleExtension: a, + name: name, + additionalState: additionalState, + } +} + +func (m *ReduxC) PC() *uint8 { + return &m.pc +} + +func (m *ReduxC) AdditionalState() any { + return m.additionalState +} + +func signExtend8(n uint8) uint8 { + sign := n >> 3 + sign8 := uint8(^(sign - 1)) << 4 + return n | sign8 +} + +func (m *ReduxC) GetCurrentInstructionAddress() uint64 { + return uint64(m.pc) +} + +func (m *ReduxC) ArchitectureInfo() machine.ArchitectureInfo { + return machine.ArchitectureInfo{ + Name: m.name, + RegistersNames: []string{"r0", "r1", "r2", "r3"}, + WordWidth: 8, + } +} + +// Historical comment: + +// +// "Getters and setters" are literally copy-pasted from Sagui. Maybe we should +// factor them out in a library for creating the machines? That would be rather +// cool. Actually, imagine simply defining some stuff and BOOM you got a new +// architecture? +// + +func (m *ReduxC) SetRegister(reg uint64, value uint64) error { + if reg > 3 { + return fmt.Errorf(machine.InterCtx.Get("no such register: %v"), reg) + } + if value > math.MaxUint8 { + return fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), value) + } + m.registers[reg] = uint8(value) + return nil +} + +func (m *ReduxC) GetRegister(reg uint64) (uint64, error) { + if reg > 3 { + return 0, fmt.Errorf(machine.InterCtx.Get("no such register: %v"), reg) + } + + return uint64(m.registers[reg]), nil +} + +func (m *ReduxC) SetMemory(addr uint64, value uint8) error { + if addr > math.MaxUint8 { + return fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), addr, math.MaxUint8) + } + m.mem[addr] = value + return nil +} + +func (m *ReduxC) GetMemory(addr uint64) (uint8, error) { + if addr > math.MaxUint8 { + return 0, fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), addr, math.MaxUint8) + } + return m.mem[addr], nil +} + +func (m *ReduxC) SetMemoryChunk(addr uint64, content []uint8) error { + end := addr + (uint64(len(content)) - 1) + if end > math.MaxUint8 { + return fmt.Errorf(machine.InterCtx.Get("end address %v bigger than maximum 8 bit address %v"), end, math.MaxUint8) + } + + for _, b := range content { + m.mem[addr] = b + addr++ + } + return nil +} + +func (m *ReduxC) GetMemoryChunk(addr uint64, size uint64) ([]uint8, error) { + end := addr + (size - 1) + if end > math.MaxUint8 { + return nil, fmt.Errorf(machine.InterCtx.Get("end address %v bigger than maximum 8 bit address %v"), end, math.MaxUint8) + } + return m.mem[addr:(end + 1)], nil +} + +func (m *ReduxC) LoadProgram(program []uint8) error { + m.pc = 0 + return m.SetMemoryChunk(0, program) +} + +func (m *ReduxC) NextInstruction() (*machine.Call, error) { + instr, err := m.GetMemory(m.GetCurrentInstructionAddress()) + if err != nil { + return nil, fmt.Errorf(machine.InterCtx.Get("failed to fetch instruction from memory: %v"), err) + } + + wasExt, call, err := m.executeExtension(instr, m) + if call != nil || err != nil { + return call, err + } + if wasExt { + return nil, nil + } + + op := instr >> 4 + imm := instr & 0xf + ra := (instr >> 2) & 0x3 + rb := instr & 0x3 + + rav, _ := m.GetRegister(uint64(ra)) + rbv, _ := m.GetRegister(uint64(rb)) + r0v, _ := m.GetRegister(0) + + switch op { + case 0x0: + if rav == 0 { + // Less one because the pc will be incremented. + m.pc = uint8(rbv) - 1 + } + case 0x1: + m.pc += signExtend8(uint8(imm)) - 1 + case 0x2: + mem, _ := m.GetMemory(rbv) + _ = m.SetRegister(uint64(ra), uint64(mem)) + case 0x3: + _ = m.SetMemory(rbv, uint8(rav)) + case 0x4: + _ = m.SetRegister(0, uint64(uint8(r0v)+signExtend8(imm))) + // 0x5, 0x6 and 0x7 are reserved to extensions. + case 0x8: + if rbv == 0 { + _ = m.SetRegister(uint64(ra), 1) + } else { + _ = m.SetRegister(uint64(ra), 0) + } + case 0x9: + _ = m.SetRegister(uint64(ra), rav&rbv) + case 0xa: + _ = m.SetRegister(uint64(ra), rav|rbv) + case 0xb: + _ = m.SetRegister(uint64(ra), rav^rbv) + case 0xc: + _ = m.SetRegister(uint64(ra), uint64(uint8(int8(rav)+int8(rbv)))) + case 0xd: + _ = m.SetRegister(uint64(ra), uint64(uint8(int8(rav)-int8(rbv)))) + case 0xe: + _ = m.SetRegister(uint64(ra), (rav<>rbv) + } + + m.pc++ + return nil, nil +} + +func getRegisterNumber(r string) (uint64, error) { + switch r { + case "r0", "0": + return 0, nil + case "r1", "1": + return 1, nil + case "r2", "2": + return 2, nil + case "r3", "3": + return 3, nil + default: + return 0, fmt.Errorf(machine.InterCtx.Get("no such register: %v"), r) + } +} + +func (m *ReduxC) GetRegisterNumber(r string) (uint64, error) { + return getRegisterNumber(r) +} + +// Also heavily-based on Sagui. +func translateArgs(arg string) (uint64, error) { + if len(arg) < 1 { + return 0, errors.New(machine.InterCtx.Get("empty argument")) + } + reg, err := getRegisterNumber(arg) + if err != nil { + n, err := strconv.ParseInt(arg, 0, 64) + if n > 0xf { + return 0, fmt.Errorf(machine.InterCtx.Get("immediate bigger than immediate size: %v"), arg) + } + return uint64(n), err + } + return reg, nil +} + +func assembleR(t assembler.ResolvedToken) (uint8, error) { + if len(t.Args) != 2 { + return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 2 arguments"), string(t.Value)) + } + + ra := uint8(t.Args[0]) & 0x3 + rb := uint8(t.Args[1]) & 0x3 + var op uint8 + switch string(t.Value) { + case "brzr": + op = 0x0 + case "ld": + op = 0x2 + case "st": + op = 0x3 + case "not": + op = 0x8 + case "and": + op = 0x9 + case "or": + op = 0xa + case "xor": + op = 0xb + case "add": + op = 0xc + case "sub": + op = 0xd + case "slr": + op = 0xe + case "srr": + op = 0xf + } + + return (op << 4) | (ra << 2) | rb, nil +} + +func assembleI(t assembler.ResolvedToken) (uint8, error) { + if len(t.Args) != 1 { + return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 1 argument"), t.Value) + } + + imm := uint8(t.Args[0]) & 0xf + var op uint8 + switch string(t.Value) { + case "ji": + op = 0x1 + case "addi": + op = 0x4 + default: + return 0, errors.New("unreachable") + } + + return (op << 4) | imm, nil +} + +func assembleJi(t assembler.ResolvedToken) (uint8, error) { + if len(t.Args) != 1 { + return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 1 argument"), t.Value) + } + + // Ugly code in the name of reuse. + t.Args[0] = uint64(int8(signExtend8(uint8(t.Args[0]))) - int8(t.Address)) + return assembleI(t) +} + +func (m *ReduxC) assembleInstruction(code []uint8, addr int, t assembler.ResolvedToken) error { + bin := uint8(0) + var err error + + switch string(t.Value) { + case "ji": + bin, err = assembleJi(t) + case "addi": + bin, err = assembleI(t) + case "brzr", "ld", "st", "not", "and", "or", "xor", "add", "sub", "slr", "srr": + bin, err = assembleR(t) + default: + code, err := m.assembleExtension(t) + if err != nil { + return err + } + bin = code + } + + if err != nil { + return err + } + + code[addr] = bin + return nil +} + +func (m *ReduxC) assemble(t []assembler.ResolvedToken) ([]uint8, error) { + size := uint64(0) + for _, i := range t { + if i.Type == assembler.TOKEN_INSTRUCTION { + size += 1 + } else { + size += uint64(len(i.Value)) + } + } + + var err error + + code := make([]uint8, size) + addr := 0 + for _, i := range t { + if i.Type == assembler.TOKEN_INSTRUCTION { + err = m.assembleInstruction(code, addr, i) + if err != nil { + return nil, fmt.Errorf(machine.InterCtx.Get("%v:%v: Error assembling: %v"), *i.File, i.Line, err) + } + addr++ + } else { + for _, c := range []uint8(i.Value) { + code[addr] = c + addr++ + } + } + } + + return code, nil +} + +func (m *ReduxC) Assemble(file string) ([]uint8, []assembler.DebuggerToken, error) { + tokens := []assembler.Token{} + err := assembler.Tokenize(file, &tokens) + if err != nil { + return nil, nil, err + } + + resolvedTokens, debuggerTokens, err := assembler.ResolveTokens(tokens, func(i *assembler.Instruction) error { + i.Size = 1 + return nil + }, translateArgs) + + if err != nil { + return nil, nil, err + } + + code, err := m.assemble(resolvedTokens) + if err != nil { + return nil, nil, err + } + + return code, debuggerTokens, nil +} diff --git a/reduxv/reduxv.go b/reduxv/reduxv.go index 78f77d7..d7660cf 100644 --- a/reduxv/reduxv.go +++ b/reduxv/reduxv.go @@ -4,358 +4,50 @@ package reduxv import ( - "errors" "fmt" - "math" - "strconv" - "github.com/gboncoffee/egg/assembler" "github.com/gboncoffee/egg/machine" + "github.com/gboncoffee/egg/reduxc" ) -type ReduxV struct { - registers [4]uint8 - mem [math.MaxUint8 + 1]uint8 - pc uint8 -} - -func signExtend8(n uint8) uint8 { - sign := n >> 3 - sign8 := uint8(^(sign - 1)) << 4 - return n | sign8 -} - -func (m *ReduxV) GetCurrentInstructionAddress() uint64 { - return uint64(m.pc) -} - -func (m *ReduxV) ArchitectureInfo() machine.ArchitectureInfo { - return machine.ArchitectureInfo{ - Name: "REDUX-V", - RegistersNames: []string{"0", "1", "2", "3"}, - WordWidth: 8, - } -} - -// -// "Getters and setters" are literally copy-pasted from Sagui. Maybe we should -// factor them out in a library for creating the machines? That would be rather -// cool. Actually, imagine simply defining some stuff and BOOM you got a new -// architecture? -// - -func (m *ReduxV) SetRegister(reg uint64, value uint64) error { - if reg > 3 { - return fmt.Errorf(machine.InterCtx.Get("no such register: %v"), reg) - } - if value > math.MaxUint8 { - return fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), value) - } - m.registers[reg] = uint8(value) - return nil -} - -func (m *ReduxV) GetRegister(reg uint64) (uint64, error) { - if reg > 3 { - return 0, fmt.Errorf(machine.InterCtx.Get("no such register: %v"), reg) - } - - return uint64(m.registers[reg]), nil -} - -func (m *ReduxV) SetMemory(addr uint64, value uint8) error { - if addr > math.MaxUint8 { - return fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), addr, math.MaxUint8) - } - m.mem[addr] = value - return nil -} - -func (m *ReduxV) GetMemory(addr uint64) (uint8, error) { - if addr > math.MaxUint8 { - return 0, fmt.Errorf(machine.InterCtx.Get("value %v is bigger than maximum 8 bit address %v"), addr, math.MaxUint8) - } - return m.mem[addr], nil -} - -func (m *ReduxV) SetMemoryChunk(addr uint64, content []uint8) error { - end := addr + (uint64(len(content)) - 1) - if end > math.MaxUint8 { - return fmt.Errorf(machine.InterCtx.Get("end address %v bigger than maximum 8 bit address %v"), end, math.MaxUint8) - } - - for _, b := range content { - m.mem[addr] = b - addr++ - } - return nil -} - -func (m *ReduxV) GetMemoryChunk(addr uint64, size uint64) ([]uint8, error) { - end := addr + (size - 1) - if end > math.MaxUint8 { - return nil, fmt.Errorf(machine.InterCtx.Get("end address %v bigger than maximum 8 bit address %v"), end, math.MaxUint8) - } - return m.mem[addr:(end + 1)], nil -} - -func (m *ReduxV) LoadProgram(program []uint8) error { - m.pc = 0 - return m.SetMemoryChunk(0, program) -} - -func (m *ReduxV) NextInstruction() (*machine.Call, error) { - instr, err := m.GetMemory(m.GetCurrentInstructionAddress()) - if err != nil { - return nil, fmt.Errorf(machine.InterCtx.Get("failed to fetch instruction from memory: %v"), err) - } - - op := instr >> 4 - imm := instr & 0xf - ra := (instr >> 2) & 0x3 - rb := instr & 0x3 - - rav, _ := m.GetRegister(uint64(ra)) - rbv, _ := m.GetRegister(uint64(rb)) - r0v, _ := m.GetRegister(0) - +func reduxVExecuteExtension(bin uint8, m *reduxc.ReduxC) (bool, *machine.Call, error) { + op := bin >> 4 switch op { - case 0x0: - if rav == 0 { - // Less one because the pc will be incremented. - m.pc = uint8(rbv) - 1 - } - case 0x1: - m.pc += signExtend8(uint8(imm)) - 1 - case 0x2: - mem, _ := m.GetMemory(rbv) - _ = m.SetRegister(uint64(ra), uint64(mem)) - case 0x3: - _ = m.SetMemory(rbv, uint8(rav)) - case 0x4: - _ = m.SetRegister(0, uint64(uint8(r0v)+signExtend8(imm))) case 0x5: - m.pc++ - return &machine.Call{ + *m.PC() = *m.PC() + 1 + return true, &machine.Call{ Number: machine.SYS_BREAK, - Arg1: 0, - Arg2: 0, }, nil case 0x6: - m.pc++ + *m.PC() = *m.PC() + 1 + r0v, _ := m.GetRegister(0) r1v, _ := m.GetRegister(1) r2v, _ := m.GetRegister(2) - return &machine.Call{ + return true, &machine.Call{ Number: r0v, Arg1: r1v, Arg2: r2v, }, nil - // 0x7 not implemented. - case 0x8: - if rbv == 0 { - _ = m.SetRegister(uint64(ra), 1) - } else { - _ = m.SetRegister(uint64(ra), 0) - } - case 0x9: - _ = m.SetRegister(uint64(ra), rav&rbv) - case 0xa: - _ = m.SetRegister(uint64(ra), rav|rbv) - case 0xb: - _ = m.SetRegister(uint64(ra), rav^rbv) - case 0xc: - _ = m.SetRegister(uint64(ra), uint64(uint8(int8(rav)+int8(rbv)))) - case 0xd: - _ = m.SetRegister(uint64(ra), uint64(uint8(int8(rav)-int8(rbv)))) - case 0xe: - _ = m.SetRegister(uint64(ra), (rav<>rbv) - } - - m.pc++ - return nil, nil -} - -func getRegisterNumber(r string) (uint64, error) { - switch r { - case "r0", "0": - return 0, nil - case "r1", "1": - return 1, nil - case "r2", "2": - return 2, nil - case "r3", "3": - return 3, nil - default: - return 0, fmt.Errorf(machine.InterCtx.Get("no such register: %v"), r) - } -} - -func (m *ReduxV) GetRegisterNumber(r string) (uint64, error) { - return getRegisterNumber(r) -} - -// Also heavily-based on Sagui. -func translateArgs(arg string) (uint64, error) { - if len(arg) < 1 { - return 0, errors.New(machine.InterCtx.Get("empty argument")) - } - reg, err := getRegisterNumber(arg) - if err != nil { - n, err := strconv.ParseInt(arg, 0, 64) - if n > 0xf { - return 0, fmt.Errorf(machine.InterCtx.Get("immediate bigger than immediate size: %v"), arg) - } - return uint64(n), err - } - return reg, nil -} - -func assembleR(t assembler.ResolvedToken) (uint8, error) { - if len(t.Args) != 2 { - return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 2 arguments"), t.Value) } - - ra := uint8(t.Args[0]) & 0x3 - rb := uint8(t.Args[1]) & 0x3 - var op uint8 - switch string(t.Value) { - case "brzr": - op = 0x0 - case "ld": - op = 0x2 - case "st": - op = 0x3 - case "not": - op = 0x8 - case "and": - op = 0x9 - case "or": - op = 0xa - case "xor": - op = 0xb - case "add": - op = 0xc - case "sub": - op = 0xd - case "slr": - op = 0xe - case "srr": - op = 0xf - } - - return (op << 4) | (ra << 2) | rb, nil + return false, nil, nil } -func assembleI(t assembler.ResolvedToken) (uint8, error) { - if len(t.Args) != 1 { - return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 1 argument"), t.Value) - } - - imm := uint8(t.Args[0]) & 0xf - var op uint8 +func reduxVAssembleExtension(t assembler.ResolvedToken) (uint8, error) { switch string(t.Value) { - case "ji": - op = 0x1 - case "addi": - op = 0x4 - default: - return 0, errors.New("unreachable") - } - - return (op << 4) | imm, nil -} - -func assembleJi(t assembler.ResolvedToken) (uint8, error) { - if len(t.Args) != 1 { - return 0, fmt.Errorf(machine.InterCtx.Get("wrong number of arguments for instruction '%s', expected 1 argument"), t.Value) - } - - // Ugly code in the name of reuse. - t.Args[0] = uint64(int8(signExtend8(uint8(t.Args[0]))) - int8(t.Address)) - return assembleI(t) -} - -func assembleInstruction(code []uint8, addr int, t assembler.ResolvedToken) error { - bin := uint8(0) - var err error - - switch string(t.Value) { - case "ji": - bin, err = assembleJi(t) - case "addi": - bin, err = assembleI(t) - case "brzr", "ld", "st", "not", "and", "or", "xor", "add", "sub", "slr", "srr": - bin, err = assembleR(t) case "ebreak": - bin = 0x50 + return 0x50, nil case "ecall": - bin = 0x60 - default: - return fmt.Errorf(machine.InterCtx.Get("unknown instruction: %v"), string(t.Value)) + return 0x60, nil } - if err != nil { - return err - } - - code[addr] = bin - return nil + return 0, fmt.Errorf(machine.InterCtx.Get("unknown instruction: %v"), string(t.Value)) } -func assemble(t []assembler.ResolvedToken) ([]uint8, error) { - size := uint64(0) - for _, i := range t { - if i.Type == assembler.TOKEN_INSTRUCTION { - size += 1 - } else { - size += uint64(len(i.Value)) - } - } - - var err error - - code := make([]uint8, size) - addr := 0 - for _, i := range t { - if i.Type == assembler.TOKEN_INSTRUCTION { - err = assembleInstruction(code, addr, i) - if err != nil { - return nil, fmt.Errorf(machine.InterCtx.Get("%v:%v: Error assembling: %v"), *i.File, i.Line, err) - } - addr++ - } else { - for _, c := range []uint8(i.Value) { - code[addr] = c - addr++ - } - } - } - - return code, nil -} - -func (m *ReduxV) Assemble(file string) ([]uint8, []assembler.DebuggerToken, error) { - tokens := []assembler.Token{} - err := assembler.Tokenize(file, &tokens) - if err != nil { - return nil, nil, err - } - - resolvedTokens, debuggerTokens, err := assembler.ResolveTokens(tokens, func(i *assembler.Instruction) error { - i.Size = 1 - return nil - }, translateArgs) - - if err != nil { - return nil, nil, err - } - - code, err := assemble(resolvedTokens) - if err != nil { - return nil, nil, err - } - - return code, debuggerTokens, nil +func ReduxV() *reduxc.ReduxC { + return reduxc.ReduxVExtension( + "REDUX-V", + reduxVExecuteExtension, + reduxVAssembleExtension, + nil, + ) } diff --git a/reduxv/reduxv_test.go b/reduxv/reduxv_test.go index c716ec6..b239584 100644 --- a/reduxv/reduxv_test.go +++ b/reduxv/reduxv_test.go @@ -12,7 +12,8 @@ func TestReduxV(t *testing.T) { _ = machine.InterCtx.AutoSetPreferedLocale() assembler.InterCtx = &machine.InterCtx - var m ReduxV + m := ReduxV() + code, _, err := m.Assemble("test.asm") if err != nil { t.Fatalf("Couldn't assemble: %v", err)