initial commit
This commit is contained in:
commit
380acbdc5f
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2022, 2023 ipc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,10 @@
|
|||
all: hypo hypoc
|
||||
|
||||
hypo: $(wildcard cmd/hypo/*.go) $(wildcard cpu/*.go)
|
||||
go build ./cmd/hypo
|
||||
|
||||
hypoc: $(wildcard cmd/hypoc/*.go) $(wildcard asm/*.go)
|
||||
go build ./cmd/hypoc
|
||||
|
||||
clean:
|
||||
rm -f hypo hypoc
|
|
@ -0,0 +1,18 @@
|
|||
# hypo
|
||||
|
||||
hypo is an interpreter for the hypo architecture.
|
||||
|
||||
# hypoc
|
||||
|
||||
hypoc is the hypo assembler. Samples of assembler code are
|
||||
included in the sample directory.
|
||||
|
||||
# Install
|
||||
|
||||
To compile, type in:
|
||||
|
||||
`make`
|
||||
|
||||
To install the binaries:
|
||||
|
||||
`make install`
|
|
@ -0,0 +1,368 @@
|
|||
package asm
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const (
|
||||
Id = iota
|
||||
Label
|
||||
Reg
|
||||
Addr
|
||||
Eof
|
||||
)
|
||||
|
||||
const ErrThreshold = 8
|
||||
|
||||
type Symbol struct {
|
||||
Type int
|
||||
Val string
|
||||
Line int
|
||||
}
|
||||
|
||||
type Instruction struct {
|
||||
Op byte
|
||||
Params []int
|
||||
}
|
||||
|
||||
type Reader struct {
|
||||
sym []Symbol
|
||||
nsym int
|
||||
}
|
||||
|
||||
type Writer struct {
|
||||
buf bytes.Buffer
|
||||
pc uint32
|
||||
lab map[string]uint32
|
||||
addr map[uint32]string
|
||||
f io.Writer
|
||||
}
|
||||
|
||||
var Hdr = []byte{0x48, 0x59, 0x50, 0x00}
|
||||
|
||||
var syms = map[byte]int{
|
||||
'%': Reg,
|
||||
'$': Addr,
|
||||
}
|
||||
|
||||
var inst = map[string]Instruction{
|
||||
"nop": {OpNop, []int{}},
|
||||
"ld": {OpLd, []int{Reg, Reg}},
|
||||
"lr": {OpLr, []int{Addr, Reg}},
|
||||
"st": {OpSt, []int{Reg, Reg}},
|
||||
"add": {OpAdd, []int{Reg, Reg, Reg}},
|
||||
"sub": {OpSub, []int{Reg, Reg, Reg}},
|
||||
"addi": {OpAddi, []int{Reg, Addr, Reg}},
|
||||
"subi": {OpSubi, []int{Reg, Addr, Reg}},
|
||||
"p": {OpP, []int{Reg}},
|
||||
"beq": {OpBeq, []int{Reg, Reg, Addr}},
|
||||
"bne": {OpBne, []int{Reg, Reg, Addr}},
|
||||
"bgt": {OpBgt, []int{Reg, Reg, Addr}},
|
||||
"blt": {OpBlt, []int{Reg, Reg, Addr}},
|
||||
"j": {OpJ, []int{Addr}},
|
||||
"jr": {OpJr, []int{Reg}},
|
||||
"call": {OpCall, []int{Addr}},
|
||||
"exit": {OpExit, []int{}},
|
||||
}
|
||||
|
||||
var lc int
|
||||
|
||||
func NewReader(s []Symbol) *Reader {
|
||||
r := new(Reader)
|
||||
r.sym = s
|
||||
return r
|
||||
}
|
||||
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
r := new(Writer)
|
||||
r.lab = make(map[string]uint32)
|
||||
r.addr = make(map[uint32]string)
|
||||
r.f = w
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *Reader) Read() (Symbol, error) {
|
||||
if s.nsym == len(s.sym) {
|
||||
return Symbol{}, errors.New("bad argument count")
|
||||
}
|
||||
|
||||
sym := s.sym[s.nsym]
|
||||
s.nsym++
|
||||
return sym, nil
|
||||
}
|
||||
|
||||
func (s *Reader) Expect(t int) (Symbol, error) {
|
||||
sym, err := s.Read()
|
||||
|
||||
if err != nil {
|
||||
return sym, err
|
||||
}
|
||||
|
||||
switch t {
|
||||
case Id:
|
||||
if sym.Type != t && sym.Type != Label {
|
||||
return sym, fmt.Errorf("expected identifier got '%s'", sym.Val)
|
||||
}
|
||||
default:
|
||||
if sym.Type != t && sym.Type != Id {
|
||||
tval := "register"
|
||||
if t == Addr {
|
||||
tval = "immediate"
|
||||
}
|
||||
|
||||
return sym, fmt.Errorf("expected %s got '%s'", tval, sym.Val)
|
||||
}
|
||||
}
|
||||
|
||||
return sym, nil
|
||||
}
|
||||
|
||||
func (w *Writer) WriteAddr(addr uint32) {
|
||||
binary.Write(&w.buf, binary.LittleEndian, addr)
|
||||
w.pc += 4
|
||||
}
|
||||
|
||||
func (w *Writer) WriteSymbol(sym Symbol) error {
|
||||
switch sym.Type {
|
||||
case Id:
|
||||
if f, ok := inst[sym.Val]; ok {
|
||||
w.buf.WriteByte(f.Op)
|
||||
w.pc++
|
||||
} else {
|
||||
w.addr[w.pc] = sym.Val
|
||||
w.WriteAddr(0)
|
||||
}
|
||||
case Label:
|
||||
_, ok := w.lab[sym.Val]
|
||||
|
||||
if ok {
|
||||
return fmt.Errorf("redefining label '%s'", sym.Val)
|
||||
}
|
||||
|
||||
w.lab[sym.Val] = w.pc
|
||||
case Reg:
|
||||
r, err := strconv.Atoi(sym.Val)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("bad register '%s'", sym.Val)
|
||||
}
|
||||
|
||||
w.buf.WriteByte(byte(r))
|
||||
w.pc++
|
||||
case Addr:
|
||||
addr, err := strconv.ParseInt(sym.Val, 16, 32)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("bad address '%s'", sym.Val)
|
||||
}
|
||||
|
||||
w.WriteAddr(uint32(addr))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Writer) Write() (int, error) {
|
||||
b := w.buf.Bytes()
|
||||
|
||||
for i, j := range w.addr {
|
||||
l, ok := w.lab[j]
|
||||
|
||||
if !ok {
|
||||
return -1, fmt.Errorf("%s: no such label", j)
|
||||
}
|
||||
|
||||
b[i] = byte(l)
|
||||
b[i+1] = byte(l >> 8)
|
||||
b[i+2] = byte(l >> 16)
|
||||
b[i+3] = byte(l >> 24)
|
||||
}
|
||||
|
||||
if n, err := w.f.Write(Hdr); err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
return w.f.Write(b)
|
||||
}
|
||||
|
||||
func ReadToken(r *bufio.Reader) (string, error) {
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
for {
|
||||
c, err := r.ReadByte()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if c == '\n' {
|
||||
lc++
|
||||
}
|
||||
|
||||
if unicode.IsSpace(rune(c)) {
|
||||
break
|
||||
}
|
||||
|
||||
b.WriteByte(c)
|
||||
}
|
||||
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
func Read(r *bufio.Reader) (sym Symbol, err error) {
|
||||
sym.Type = -1
|
||||
|
||||
for {
|
||||
c, err := r.ReadByte()
|
||||
|
||||
if err != nil {
|
||||
sym.Type = Eof
|
||||
break
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '\n':
|
||||
lc++
|
||||
case '#':
|
||||
r.ReadBytes('\n')
|
||||
lc++
|
||||
return sym, nil
|
||||
}
|
||||
|
||||
if unicode.IsSpace(rune(c)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !unicode.IsGraphic(rune(c)) {
|
||||
return sym, fmt.Errorf("invalid character '%02x'", c)
|
||||
}
|
||||
|
||||
if t, ok := syms[c]; ok {
|
||||
s, err := ReadToken(r)
|
||||
|
||||
if err != nil {
|
||||
sym.Type = Eof
|
||||
} else {
|
||||
sym = Symbol{t, s, lc}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if sym.Type == -1 && unicode.IsLetter(rune(c)) {
|
||||
r.UnreadByte()
|
||||
s, err := ReadToken(r)
|
||||
|
||||
if err != nil {
|
||||
sym.Type = Eof
|
||||
} else {
|
||||
if s[len(s)-1] == ':' {
|
||||
sym = Symbol{Label, strings.TrimSuffix(s, ":"), lc}
|
||||
lc++
|
||||
} else {
|
||||
sym = Symbol{Id, s, lc}
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return sym, nil
|
||||
}
|
||||
|
||||
// Gen takes the code from r and writes a machine code representation
|
||||
// to w. Any errors are outputted to e.
|
||||
func Gen(r io.Reader, w io.Writer, e io.Writer) (sym []Symbol, err error) {
|
||||
b := bufio.NewReader(r)
|
||||
errc := 0
|
||||
|
||||
werr := func(s Symbol, err error) {
|
||||
if errc <= ErrThreshold {
|
||||
fmt.Fprintf(e, "%d: %s\n", s.Line, err)
|
||||
}
|
||||
errc++
|
||||
}
|
||||
|
||||
for {
|
||||
s, err := Read(b)
|
||||
|
||||
if err != nil {
|
||||
werr(s, err)
|
||||
}
|
||||
|
||||
if errc > ErrThreshold {
|
||||
return sym, errors.New("invalid file")
|
||||
}
|
||||
|
||||
if s.Type != -1 {
|
||||
sym = append(sym, s)
|
||||
}
|
||||
|
||||
if s.Type == Eof {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
reader := NewReader(sym)
|
||||
writer := NewWriter(w)
|
||||
|
||||
for {
|
||||
s, err := reader.Expect(Id)
|
||||
|
||||
if s.Type == Eof {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
werr(s, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if s.Type != Label {
|
||||
f, ok := inst[s.Val]
|
||||
|
||||
if !ok {
|
||||
werr(s, fmt.Errorf("bad instruction '%s'", s.Val))
|
||||
continue
|
||||
}
|
||||
|
||||
writer.WriteSymbol(s)
|
||||
for _, t := range f.Params {
|
||||
s, err = reader.Expect(t)
|
||||
|
||||
if err != nil {
|
||||
werr(s, err)
|
||||
} else if err = writer.WriteSymbol(s); err != nil {
|
||||
werr(s, err)
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err = writer.WriteSymbol(s); err != nil {
|
||||
werr(s, err)
|
||||
}
|
||||
}
|
||||
|
||||
if errc > ErrThreshold {
|
||||
return sym, fmt.Errorf("%d errors (%d shown)", errc, ErrThreshold)
|
||||
} else if errc > 0 {
|
||||
return sym, fmt.Errorf("%d errors", errc)
|
||||
}
|
||||
|
||||
if _, err := writer.Write(); err != nil {
|
||||
fmt.Fprintf(e, "%s\n", err)
|
||||
}
|
||||
|
||||
return sym, nil
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package asm
|
||||
|
||||
const (
|
||||
OpNop = iota
|
||||
OpLd
|
||||
OpLr
|
||||
OpSt
|
||||
OpAdd
|
||||
OpSub
|
||||
OpAddi
|
||||
OpSubi
|
||||
OpP
|
||||
OpBeq
|
||||
OpBne
|
||||
OpBgt
|
||||
OpBlt
|
||||
OpJ
|
||||
OpJr
|
||||
OpCall
|
||||
OpExit
|
||||
)
|
|
@ -0,0 +1,35 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/rtcall/hypo/cpu"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Printf("usage: %s file\n", os.Args[0])
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := os.ReadFile(os.Args[1])
|
||||
if err != nil {
|
||||
fmt.Printf("error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
c, err := cpu.New(buf)
|
||||
if err != nil {
|
||||
fmt.Printf("error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for c.State() {
|
||||
if err := c.Step(); err != nil {
|
||||
fmt.Printf("fatal: %s\n\n", err)
|
||||
c.WriteTrace(os.Stdout)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/rtcall/hypo/asm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
outPath := flag.String("o", "out", "output path")
|
||||
flag.Parse()
|
||||
|
||||
if len(flag.Args()) == 0 {
|
||||
fmt.Printf("usage: %s [-o path] file\n", os.Args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
inPath := flag.Arg(0)
|
||||
|
||||
f, err := os.OpenFile(*outPath, os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
f.Truncate(0)
|
||||
|
||||
in, err := os.Open(inPath)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
os.Remove(*outPath)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer in.Close()
|
||||
|
||||
_, err = asm.Gen(in, f, os.Stderr)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
os.Remove(*outPath)
|
||||
fmt.Printf("%s: %s\n", inPath, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
package cpu
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/rtcall/hypo/asm"
|
||||
)
|
||||
|
||||
type Cpu struct {
|
||||
reg [8]uint32
|
||||
mem [8192]byte
|
||||
pc uint32
|
||||
flags uint32
|
||||
err error
|
||||
buf *bytes.Reader
|
||||
}
|
||||
|
||||
func New(buf []byte) (c Cpu, err error) {
|
||||
c.buf = bytes.NewReader(buf)
|
||||
|
||||
hdr := make([]byte, len(asm.Hdr))
|
||||
if _, err = c.buf.Read(hdr); err != nil {
|
||||
return c, errors.New("could not read header")
|
||||
} else if !bytes.Equal(hdr, asm.Hdr) {
|
||||
return c, errors.New("bad header")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Cpu) read(ins any) {
|
||||
if err := binary.Read(c.buf, binary.LittleEndian, ins); err != nil {
|
||||
c.err = errors.New("bad read")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cpu) checkReg(r byte) error {
|
||||
if r > byte(len(c.reg)) {
|
||||
c.err = fmt.Errorf("invalid register %02x", r)
|
||||
c.flags |= 1
|
||||
}
|
||||
|
||||
return c.err
|
||||
}
|
||||
|
||||
func (c *Cpu) readReg(r byte) uint32 {
|
||||
if c.checkReg(r) == nil {
|
||||
return c.reg[r]
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *Cpu) writeReg(r byte, i uint32) {
|
||||
if c.checkReg(r) == nil {
|
||||
c.reg[r] = i
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cpu) readImm(addr uint32) (uint32, error) {
|
||||
if addr > uint32(len(c.mem)) {
|
||||
return 0, fmt.Errorf("illegal read %08x", addr)
|
||||
}
|
||||
|
||||
i := c.mem[addr:4]
|
||||
return uint32(i[3])<<24 | uint32(i[2])<<16 | uint32(i[1])<<8 | uint32(i[0]), nil
|
||||
}
|
||||
|
||||
func (c *Cpu) writeImm(addr, imm uint32) error {
|
||||
if addr > uint32(len(c.mem)) {
|
||||
return fmt.Errorf("illegal write %08x (at %08x)", imm, addr)
|
||||
}
|
||||
|
||||
c.mem[addr] = byte(imm)
|
||||
c.mem[addr+1] = byte(imm >> 8)
|
||||
c.mem[addr+2] = byte(imm >> 16)
|
||||
c.mem[addr+3] = byte(imm >> 24)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cpu) jump(pc uint32) error {
|
||||
if _, err := c.buf.Seek(int64(pc+uint32(len(asm.Hdr))), io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.pc = pc
|
||||
return nil
|
||||
}
|
||||
|
||||
var ops = map[byte]func(*Cpu) int{
|
||||
asm.OpNop: func(*Cpu) int {
|
||||
return 0
|
||||
},
|
||||
asm.OpLd: func(c *Cpu) int {
|
||||
var ins struct {
|
||||
R1, R2 byte
|
||||
}
|
||||
|
||||
if c.read(&ins); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
i, err := c.readImm(c.readReg(ins.R2))
|
||||
c.err = err
|
||||
|
||||
if err != nil {
|
||||
c.writeReg(ins.R1, i)
|
||||
}
|
||||
|
||||
return 2
|
||||
},
|
||||
asm.OpLr: func(c *Cpu) int {
|
||||
var ins struct {
|
||||
I uint32
|
||||
R byte
|
||||
}
|
||||
|
||||
if c.read(&ins); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.writeReg(ins.R, ins.I)
|
||||
return 5
|
||||
},
|
||||
asm.OpSt: func(c *Cpu) int {
|
||||
var ins struct {
|
||||
R1, R2 byte
|
||||
}
|
||||
|
||||
if c.read(&ins); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.err = c.writeImm(c.readReg(ins.R1), c.readReg(ins.R2))
|
||||
return 2
|
||||
},
|
||||
asm.OpAdd: func(c *Cpu) int {
|
||||
var ins struct {
|
||||
R1, R2, R3 byte
|
||||
}
|
||||
|
||||
if c.read(&ins); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.writeReg(ins.R3, c.readReg(ins.R1)+c.readReg(ins.R2))
|
||||
return 3
|
||||
},
|
||||
asm.OpSub: func(c *Cpu) int {
|
||||
var ins struct {
|
||||
R1, R2, R3 byte
|
||||
}
|
||||
|
||||
if c.read(&ins); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.writeReg(ins.R3, c.readReg(ins.R1)-c.readReg(ins.R2))
|
||||
return 3
|
||||
},
|
||||
asm.OpAddi: func(c *Cpu) int {
|
||||
var ins struct {
|
||||
R1 byte
|
||||
I uint32
|
||||
R2 byte
|
||||
}
|
||||
|
||||
if c.read(&ins); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.writeReg(ins.R2, c.readReg(ins.R1)+ins.I)
|
||||
return 6
|
||||
},
|
||||
asm.OpSubi: func(c *Cpu) int {
|
||||
var ins struct {
|
||||
R1 byte
|
||||
I uint32
|
||||
R2 byte
|
||||
}
|
||||
|
||||
if c.read(&ins); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.writeReg(ins.R2, c.readReg(ins.R1)-ins.I)
|
||||
return 6
|
||||
},
|
||||
asm.OpP: func(c *Cpu) int {
|
||||
var R byte
|
||||
|
||||
if c.read(&R); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
fmt.Print(string(rune(c.reg[R])))
|
||||
return 1
|
||||
},
|
||||
asm.OpBeq: func(c *Cpu) int {
|
||||
var ins struct {
|
||||
R1, R2 byte
|
||||
I uint32
|
||||
}
|
||||
|
||||
if c.read(&ins); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if c.readReg(ins.R1) == c.readReg(ins.R2) {
|
||||
c.jump(ins.I)
|
||||
return 0
|
||||
}
|
||||
|
||||
return 6
|
||||
},
|
||||
asm.OpBne: func(c *Cpu) int {
|
||||
var ins struct {
|
||||
R1, R2 byte
|
||||
I uint32
|
||||
}
|
||||
|
||||
if c.read(&ins); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if c.readReg(ins.R1) != c.readReg(ins.R2) {
|
||||
c.jump(ins.I)
|
||||
return 0
|
||||
}
|
||||
|
||||
return 6
|
||||
},
|
||||
asm.OpBgt: func(c *Cpu) int {
|
||||
var ins struct {
|
||||
R1, R2 byte
|
||||
I uint32
|
||||
}
|
||||
|
||||
if c.read(&ins); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if c.readReg(ins.R1) > c.readReg(ins.R2) {
|
||||
c.jump(ins.I)
|
||||
return 0
|
||||
}
|
||||
|
||||
return 6
|
||||
},
|
||||
asm.OpBlt: func(c *Cpu) int {
|
||||
var ins struct {
|
||||
R1, R2 byte
|
||||
I uint32
|
||||
}
|
||||
|
||||
if c.read(&ins); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if c.readReg(ins.R1) < c.readReg(ins.R2) {
|
||||
c.jump(ins.I)
|
||||
return 0
|
||||
}
|
||||
|
||||
return 6
|
||||
},
|
||||
asm.OpJ: func(c *Cpu) int {
|
||||
var I uint32
|
||||
|
||||
if c.read(&I); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.jump(I)
|
||||
return 0
|
||||
},
|
||||
asm.OpJr: func(c *Cpu) int {
|
||||
var R byte
|
||||
|
||||
if c.read(&R); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.jump(c.readReg(R))
|
||||
return 0
|
||||
},
|
||||
asm.OpCall: func(c *Cpu) int {
|
||||
var I uint32
|
||||
|
||||
if c.read(&I); c.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.writeReg(3, c.pc+4)
|
||||
c.jump(I)
|
||||
return 0
|
||||
},
|
||||
asm.OpExit: func(c *Cpu) int {
|
||||
c.flags |= 1
|
||||
return 0
|
||||
},
|
||||
}
|
||||
|
||||
func (c *Cpu) State() bool {
|
||||
return c.flags != 1
|
||||
}
|
||||
|
||||
func (c *Cpu) Step() error {
|
||||
var op byte
|
||||
|
||||
if c.read(&op); c.err != nil {
|
||||
return c.err
|
||||
}
|
||||
|
||||
c.pc++
|
||||
|
||||
f, ok := ops[op]
|
||||
if !ok {
|
||||
c.pc--
|
||||
return fmt.Errorf("invalid opcode: %02x", op)
|
||||
}
|
||||
|
||||
pc := f(c)
|
||||
c.pc += uint32(pc)
|
||||
return c.err
|
||||
}
|
||||
|
||||
func (c *Cpu) WriteTrace(w io.Writer) {
|
||||
fmt.Fprintln(w, "register trace:")
|
||||
for i, j := range c.reg {
|
||||
fmt.Fprintf(w, "%02x: %08x\n", i, j)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "pc: %08x\n", c.pc)
|
||||
fmt.Fprintln(w, "memory trace:")
|
||||
for i, j := range c.mem {
|
||||
if i > 0xff {
|
||||
break
|
||||
}
|
||||
if i > 0 && i%16 == 0 {
|
||||
fmt.Fprintln(w, "")
|
||||
}
|
||||
fmt.Fprintf(w, "%02x ", j)
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, "")
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
main:
|
||||
lr $61 %0 # a
|
||||
lr $7b %1 # z + 1
|
||||
|
||||
loop:
|
||||
p %0
|
||||
addi %0 $1 %0
|
||||
bne %0 %1 loop
|
||||
|
||||
nop
|
||||
lr $0a %0 # \n
|
||||
p %0
|
||||
|
||||
lr $40 %0
|
||||
call func
|
||||
exit
|
||||
|
||||
func:
|
||||
lr $0 %1
|
||||
func_loop:
|
||||
addi %1 $2 %1
|
||||
blt %1 %0 func_loop
|
||||
jr %3
|
Loading…
Reference in New Issue