Skip to content

Commit

Permalink
Add JXNZ instruction.
Browse files Browse the repository at this point in the history
  • Loading branch information
aleury committed Jan 15, 2024
1 parent 0c372d8 commit 0775772
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 3 deletions.
4 changes: 2 additions & 2 deletions examples/factorial.g
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
; Tasks:
; 1. Implement MULA - done
; 2. Implement SETX - done
; 3. Implement DECX (and DECA, DECY)
; 4. Implement JXNZ
; 3. Implement DECX (and DECA, DECY) - done
; 4. Implement JXNZ - done
; 5. Implement CALL and RTRN

.factorial
Expand Down
11 changes: 10 additions & 1 deletion gmachine.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const (
OpPSHA
OpPOPA
OpJUMP
OpJXNZ
)

const (
Expand Down Expand Up @@ -79,6 +80,7 @@ var opcodes = map[string]Word{
"PSHA": OpPSHA,
"POPA": OpPOPA,
"JUMP": OpJUMP,
"JXNZ": OpJXNZ,
}

type Word uint64
Expand Down Expand Up @@ -173,10 +175,17 @@ func (g *Machine) Run() {
g.A = g.Memory[g.S]
case OpJUMP:
g.P = g.Memory[g.MemOffset+g.P]
case OpJXNZ:
if g.X != 0 {
g.P = g.Memory[g.MemOffset+g.P]
} else {
g.P++
}
default:
g.E = ExceptionIllegalInstruction
return
}

}
}

Expand Down Expand Up @@ -294,7 +303,7 @@ func assembleOpcodeStatement(stmt *ast.OpcodeStatement, program []Word, refs []R
}
program = append(program, register)
case *ast.Identifier:
if !slices.Contains([]Word{OpSETA, OpJUMP}, opcode) {
if !slices.Contains([]Word{OpSETA, OpJUMP, OpJXNZ}, opcode) {
return nil, nil, fmt.Errorf("%w: %s at line %d", ErrInvalidOperand, stmt.TokenLiteral(), stmt.Token.Line)
}
ref := Ref{
Expand Down
63 changes: 63 additions & 0 deletions gmachine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"gmachine/parser"
"io"
"os"
Expand Down Expand Up @@ -424,6 +425,68 @@ func TestJUMPWithInvalidNumber(t *testing.T) {
}
}

func TestJXNZ(t *testing.T) {
t.Parallel()
g := gmachine.New(nil)
var wantA gmachine.Word = 10
var wantX gmachine.Word = 0
err := assembleAndRunFromString(g, `
SETA 0
SETX 10
.loop
INCA
DECX
JXNZ loop
HALT
`)
if err != nil {
t.Fatal("didn't expect an error", err)
}
if wantA != g.A {
t.Errorf("want A value %d, got %d", wantA, g.A)
}
if wantX != g.X {
t.Errorf("want X value %d, got %d", wantX, g.X)
}
}

func TestFactorial(t *testing.T) {
t.Parallel()
tests := []struct {
factorial int
wantA gmachine.Word
}{
{1, 1},
{2, 2},
{3, 6},
{4, 24},
{5, 120},
{6, 720},
{7, 5040},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("%d!", tt.factorial), func(t *testing.T) {
g := gmachine.New(nil)
program := fmt.Sprintf(`
SETA 1
SETX %d
.factorial
MULA X
DECX
JXNZ factorial
HALT
`, tt.factorial)
err := assembleAndRunFromString(g, program)
if err != nil {
t.Fatal("didn't expect an error", err)
}
if tt.wantA != g.A {
t.Errorf("want A value %d, got %d", tt.wantA, g.A)
}
})
}
}

func TestAssemble_SkipsComments(t *testing.T) {
t.Parallel()
want := []gmachine.Word{}
Expand Down
1 change: 1 addition & 0 deletions token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var opcodes = map[string]TokenType{
"PSHA": OPCODE,
"POPA": OPCODE,
"JUMP": OPCODE,
"JXNZ": OPCODE,
}

var pragmas = map[string]TokenType{
Expand Down
1 change: 1 addition & 0 deletions token/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func TestLookupIdent(t *testing.T) {
{"PSHA", token.OPCODE},
{"POPA", token.OPCODE},
{"JUMP", token.OPCODE},
{"JXNZ", token.OPCODE},
{"X", token.REGISTER},
{"test", token.IDENT},
{".test", token.IDENT},
Expand Down

0 comments on commit 0775772

Please sign in to comment.