From 52621158027c100e08b740fb9d995c5a62337a9d Mon Sep 17 00:00:00 2001 From: Adam Eury Date: Sat, 20 Jan 2024 23:49:39 -0500 Subject: [PATCH] Add support for register dereferencing in the first operand of the move instruction. --- ast/ast.go | 3 ++- gmachine.go | 58 +++++++++++++++++++++++++------------------ gmachine_test.go | 19 ++++++++++++++ parser/parser.go | 11 +++++++- parser/parser_test.go | 31 ++++++++++++++++++++--- testdata/gc.txtar | 2 +- 6 files changed, 93 insertions(+), 31 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index b9dd226..48f904e 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -62,7 +62,8 @@ func (os InstructionStatement) statementNode() {} func (os InstructionStatement) TokenLiteral() string { return os.Token.Literal } type RegisterLiteral struct { - Token token.Token // the token.REGISTER token + Token token.Token // the token.REGISTER token + Dereferenced bool } func (rl RegisterLiteral) expressionNode() {} diff --git a/gmachine.go b/gmachine.go index ce3cfa3..abcfca9 100644 --- a/gmachine.go +++ b/gmachine.go @@ -30,6 +30,7 @@ const ( OpADDA OpMULA OpMVAX + OpMVIAX OpMVAY OpMVAV OpMVVA @@ -68,28 +69,29 @@ var registers = map[string]Word{ } var opcodes = map[string]Word{ - "HALT": OpHALT, - "NOOP": OpNOOP, - "OUTA": OpOUTA, - "INCA": OpINCA, - "INCX": OpINCX, - "INCY": OpINCY, - "DECA": OpDECA, - "DECX": OpDECX, - "DECY": OpDECY, - "ADDA": OpADDA, - "MULA": OpMULA, - "MVAX": OpMVAX, - "MVAY": OpMVAY, - "MVAV": OpMVAV, - "MVVA": OpMVVA, - "SETA": OpSETA, - "SETX": OpSETX, - "SETY": OpSETY, - "PSHA": OpPSHA, - "POPA": OpPOPA, - "JUMP": OpJUMP, - "JXNZ": OpJXNZ, + "HALT": OpHALT, + "NOOP": OpNOOP, + "OUTA": OpOUTA, + "INCA": OpINCA, + "INCX": OpINCX, + "INCY": OpINCY, + "DECA": OpDECA, + "DECX": OpDECX, + "DECY": OpDECY, + "ADDA": OpADDA, + "MULA": OpMULA, + "MVAX": OpMVAX, + "MVIAX": OpMVIAX, + "MVAY": OpMVAY, + "MVAV": OpMVAV, + "MVVA": OpMVVA, + "SETA": OpSETA, + "SETX": OpSETX, + "SETY": OpSETY, + "PSHA": OpPSHA, + "POPA": OpPOPA, + "JUMP": OpJUMP, + "JXNZ": OpJXNZ, } type Word uint64 @@ -169,6 +171,8 @@ func (g *Machine) Run() { } case OpMVAX: g.X = g.A + case OpMVIAX: + g.X = g.Memory[g.MemOffset+g.A] case OpMVAY: g.Y = g.A case OpMVAV: @@ -333,11 +337,16 @@ func assembleInstructionStatement(stmt ast.InstructionStatement, program []Word, switch instruction { case "MOVE": + opcodeStr := "MV" switch operand1 := stmt.Operand1.(type) { case ast.RegisterLiteral: + if operand1.Dereferenced { + opcodeStr += "I" + } + opcodeStr += operand1.TokenLiteral() switch operand2 := stmt.Operand2.(type) { case ast.RegisterLiteral: - opcodeStr := fmt.Sprintf("%s%s%s", "MV", operand1.TokenLiteral(), operand2.TokenLiteral()) + opcodeStr += operand2.TokenLiteral() opcode, ok := opcodes[opcodeStr] if !ok { return nil, nil, fmt.Errorf("%w: %s at line %d", ErrUnknownOpcode, opcodeStr, stmt.Token.Line) @@ -359,9 +368,10 @@ func assembleInstructionStatement(stmt ast.InstructionStatement, program []Word, program = append(program, Word(0)) } case ast.Identifier: + opcodeStr += "V" switch operand2 := stmt.Operand2.(type) { case ast.RegisterLiteral: - opcodeStr := fmt.Sprintf("%s%s%s", "MV", "V", operand2.TokenLiteral()) + opcodeStr += operand2.TokenLiteral() opcode, ok := opcodes[opcodeStr] if !ok { return nil, nil, fmt.Errorf("%w: %s at line %d", ErrUnknownOpcode, opcodeStr, stmt.Token.Line) diff --git a/gmachine_test.go b/gmachine_test.go index fcdaea4..f231c70 100644 --- a/gmachine_test.go +++ b/gmachine_test.go @@ -607,6 +607,25 @@ func TestMOVE_FailsForUnknownIdentifier(t *testing.T) { } } +func TestMOVE_CopiesDereferencedRegisterAToRegisterX(t *testing.T) { + t.Parallel() + g := gmachine.New(nil) + err := assembleAndRunFromString(g, ` +JUMP start +VARB num 42 +.start +SETA num +MOVE *A -> X +`) + if err != nil { + t.Fatal("didn't expect an error:", err) + } + var wantX gmachine.Word = 42 + if wantX != g.X { + t.Errorf("want %d, got %d", wantX, g.X) + } +} + func TestADDAX(t *testing.T) { t.Parallel() g := gmachine.New(nil) diff --git a/parser/parser.go b/parser/parser.go index df42f2c..95bf0ee 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -106,6 +106,15 @@ func (p *Parser) expectOneOf(tokTypes ...token.TokenType) ast.Expression { } switch p.curToken.Type { + case token.ASTERISK: + expr := p.expectOneOf(token.REGISTER) + if expr == nil { + p.errors = append(p.errors, fmt.Errorf("%w: expected one of %+v, got %s at line %d", ErrInvalidSyntax, tokTypes, p.curToken.Type, p.curToken.Line)) + return nil + } + regLiteral := expr.(ast.RegisterLiteral) + regLiteral.Dereferenced = true + return regLiteral case token.REGISTER: return p.parseRegisterLiteral() case token.IDENT: @@ -123,7 +132,7 @@ func (p *Parser) parseInstructionStatement() ast.Statement { stmt := ast.InstructionStatement{Token: p.curToken} if stmt.TokenLiteral() == "MOVE" { - stmt.Operand1 = p.expectOneOf(token.REGISTER, token.IDENT) + stmt.Operand1 = p.expectOneOf(token.ASTERISK, token.REGISTER, token.IDENT) p.expectOneOf(token.ARROW) stmt.Operand2 = p.expectOneOf(token.REGISTER, token.IDENT) } diff --git a/parser/parser_test.go b/parser/parser_test.go index 8aed199..a38c5e5 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -420,6 +420,7 @@ func TestParseProgram_ParsesInstructionsWithARegisterLiteralOperands(t *testing. input := ` MOVE A -> X +MOVE *A -> X MOVE A -> Y ADDA X ADDA Y @@ -465,11 +466,12 @@ ADDA Y Literal: "A", Line: 3, }, + Dereferenced: true, }, Operand2: ast.RegisterLiteral{ Token: token.Token{ Type: token.REGISTER, - Literal: "Y", + Literal: "X", Line: 3, }, }, @@ -477,13 +479,20 @@ ADDA Y ast.InstructionStatement{ Token: token.Token{ Type: token.INSTRUCTION, - Literal: "ADDA", + Literal: "MOVE", Line: 4, }, Operand1: ast.RegisterLiteral{ Token: token.Token{ Type: token.REGISTER, - Literal: "X", + Literal: "A", + Line: 4, + }, + }, + Operand2: ast.RegisterLiteral{ + Token: token.Token{ + Type: token.REGISTER, + Literal: "Y", Line: 4, }, }, @@ -497,11 +506,25 @@ ADDA Y Operand1: ast.RegisterLiteral{ Token: token.Token{ Type: token.REGISTER, - Literal: "Y", + Literal: "X", Line: 5, }, }, }, + ast.InstructionStatement{ + Token: token.Token{ + Type: token.INSTRUCTION, + Literal: "ADDA", + Line: 6, + }, + Operand1: ast.RegisterLiteral{ + Token: token.Token{ + Type: token.REGISTER, + Literal: "Y", + Line: 6, + }, + }, + }, } got := program.Statements diff --git a/testdata/gc.txtar b/testdata/gc.txtar index f9d966a..3e6138e 100644 --- a/testdata/gc.txtar +++ b/testdata/gc.txtar @@ -9,6 +9,6 @@ SETA 42 OUTA -- want -- -0000000 0000 0000 0000 1000 0000 0000 0000 2a00 +0000000 0000 0000 0000 1100 0000 0000 0000 2a00 0000010 0000 0000 0000 0300 0000018