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