Compare commits
7 Commits
Author | SHA1 | Date |
---|---|---|
Sasha Koshka | c7d948cdcd | |
Sasha Koshka | dcd8c56c0d | |
Sasha Koshka | a52e5d3426 | |
Sasha Koshka | b0068336cd | |
Sasha Koshka | 3be08736b4 | |
Sasha Koshka | cfae69c0c2 | |
Sasha Koshka | d7d5be6949 |
|
@ -0,0 +1 @@
|
||||||
|
/cmd/creature/creature
|
41
README.md
41
README.md
|
@ -16,6 +16,11 @@ Each documented instruction has a list of what is pushed, and what is popped.
|
||||||
the items are listed in chronological order, from first pushed/popped to last
|
the items are listed in chronological order, from first pushed/popped to last
|
||||||
pushed/popped.
|
pushed/popped.
|
||||||
|
|
||||||
|
You'll notice that words specifying an address are always at the top of the
|
||||||
|
stack. This helps increse security and reduce the likelihood of bugs. When
|
||||||
|
writing functions to extend creature, it is highly advised that you follow this
|
||||||
|
principle when deciding on an order to pop arguments off of the stack in.
|
||||||
|
|
||||||
### 0x0 PUSH
|
### 0x0 PUSH
|
||||||
|
|
||||||
Pushes the next word in program memory onto the stack.
|
Pushes the next word in program memory onto the stack.
|
||||||
|
@ -53,8 +58,8 @@ Pops:
|
||||||
Adds two words. Tip: this can be used as a logical OR.
|
Adds two words. Tip: this can be used as a logical OR.
|
||||||
|
|
||||||
Pops:
|
Pops:
|
||||||
1. left operand
|
1. right operand
|
||||||
2. right operand
|
2. left operand
|
||||||
|
|
||||||
Pushes:
|
Pushes:
|
||||||
1. left operand + right operand
|
1. left operand + right operand
|
||||||
|
@ -64,8 +69,8 @@ Pushes:
|
||||||
Subtracts two words.
|
Subtracts two words.
|
||||||
|
|
||||||
Pops:
|
Pops:
|
||||||
1. left operand
|
1. right operand
|
||||||
2. right operand
|
2. left operand
|
||||||
|
|
||||||
Pushes:
|
Pushes:
|
||||||
1. left operand - right operand
|
1. left operand - right operand
|
||||||
|
@ -75,8 +80,8 @@ Pushes:
|
||||||
Multiplies two words.
|
Multiplies two words.
|
||||||
|
|
||||||
Pops:
|
Pops:
|
||||||
1. left operand
|
1. right operand
|
||||||
2. right operand
|
2. left operand
|
||||||
|
|
||||||
Pushes:
|
Pushes:
|
||||||
1. left operand * right operand
|
1. left operand * right operand
|
||||||
|
@ -86,8 +91,8 @@ Pushes:
|
||||||
Divides two words.
|
Divides two words.
|
||||||
|
|
||||||
Pops:
|
Pops:
|
||||||
1. left operand
|
1. right operand
|
||||||
2. right operand
|
2. left operand
|
||||||
|
|
||||||
Pushes:
|
Pushes:
|
||||||
1. left operand / right operand
|
1. left operand / right operand
|
||||||
|
@ -97,8 +102,8 @@ Pushes:
|
||||||
Checks if two words are equal. Pushes 1 if true, zero if false.
|
Checks if two words are equal. Pushes 1 if true, zero if false.
|
||||||
|
|
||||||
Pops:
|
Pops:
|
||||||
1. left operand
|
1. right operand
|
||||||
2. right operand
|
2. left operand
|
||||||
|
|
||||||
Pushes:
|
Pushes:
|
||||||
1. left operand == right operand
|
1. left operand == right operand
|
||||||
|
@ -108,8 +113,8 @@ Pushes:
|
||||||
Checks if a word is greater than another word. Pushes 1 if true, zero if false.
|
Checks if a word is greater than another word. Pushes 1 if true, zero if false.
|
||||||
|
|
||||||
Pops:
|
Pops:
|
||||||
1. left operand
|
1. right operand
|
||||||
2. right operand
|
2. left operand
|
||||||
|
|
||||||
Pushes:
|
Pushes:
|
||||||
1. left operand > right operand
|
1. left operand > right operand
|
||||||
|
@ -119,8 +124,8 @@ Pushes:
|
||||||
Checks if a word is less than another word. Pushes 1 if true, zero if false.
|
Checks if a word is less than another word. Pushes 1 if true, zero if false.
|
||||||
|
|
||||||
Pops:
|
Pops:
|
||||||
1. left operand
|
1. right operand
|
||||||
2. right operand
|
2. left operand
|
||||||
|
|
||||||
Pushes:
|
Pushes:
|
||||||
1. left operand < right operand
|
1. left operand < right operand
|
||||||
|
@ -130,8 +135,8 @@ Pushes:
|
||||||
Checks if two words are *not* equal. Pushes 1 if true, zero if false.
|
Checks if two words are *not* equal. Pushes 1 if true, zero if false.
|
||||||
|
|
||||||
Pops:
|
Pops:
|
||||||
1. left operand
|
1. right operand
|
||||||
2. right operand
|
2. left operand
|
||||||
|
|
||||||
Pushes:
|
Pushes:
|
||||||
1. left operand != right operand
|
1. left operand != right operand
|
||||||
|
@ -142,8 +147,8 @@ Performs a modulo operation on two words. Tip: this can be used as a logical
|
||||||
AND.
|
AND.
|
||||||
|
|
||||||
Pops:
|
Pops:
|
||||||
1. left operand
|
1. right operand
|
||||||
2. right operand
|
2. left operand
|
||||||
|
|
||||||
Pushes:
|
Pushes:
|
||||||
1. left operand % right operand
|
1. left operand % right operand
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
0 0
|
||||||
|
F
|
||||||
|
|
||||||
|
0 1
|
||||||
|
F
|
||||||
|
|
||||||
|
0 1
|
||||||
|
0 0
|
||||||
|
E
|
Binary file not shown.
|
@ -0,0 +1,112 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
import "os"
|
||||||
|
import "fmt"
|
||||||
|
import "bufio"
|
||||||
|
import "strconv"
|
||||||
|
import cre "git.tebibyte.media/sashakoshka/creature"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) != 2 {
|
||||||
|
logLine("ERR file unspecified")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
logLine("ERR could not open file: " + err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
program, block, err := readFile(file)
|
||||||
|
if err != nil {
|
||||||
|
logLine("ERR could not read file: " + err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
machine := cre.Machine[int]{}
|
||||||
|
|
||||||
|
machine.LoadProgram(program)
|
||||||
|
machine.LoadMemory(block)
|
||||||
|
|
||||||
|
machine.Register(0, read)
|
||||||
|
machine.Register(1, write)
|
||||||
|
|
||||||
|
err = machine.Execute(0)
|
||||||
|
if err != nil {
|
||||||
|
logLine("XXX machine failed: " + err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read implements a basic input function for the creature. It waits until it
|
||||||
|
// has recieved one byte of user input, and then pushes that byte onto the
|
||||||
|
// stack.
|
||||||
|
func read(machine *cre.Machine[int]) (stop bool) {
|
||||||
|
ch := []byte{0}
|
||||||
|
os.Stdin.Read(ch)
|
||||||
|
machine.Push(int(ch[0]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// write implements a basic output function for the creature. It pops one byte
|
||||||
|
// off of the stack, and writes it to stdout.
|
||||||
|
func write(machine *cre.Machine[int]) (stop bool) {
|
||||||
|
print(string(rune(machine.Pop())))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// logLine prints a message to stderr.
|
||||||
|
func logLine(message ...any) {
|
||||||
|
fmt.Fprintln(os.Stderr, message...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readFile reads data from an io.Reader into a program slice and a block slice.
|
||||||
|
// Data in the file is represented by signed hexidecimal numbers, separated by
|
||||||
|
// whitespace. Three dashes (---) divide the block data from the program data.
|
||||||
|
// See examples/echo.
|
||||||
|
func readFile(reader io.Reader) (program []int, block []int, err error) {
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
scanner.Split(bufio.ScanWords)
|
||||||
|
|
||||||
|
blockLen := 0
|
||||||
|
block = make([]int, 8)
|
||||||
|
for scanner.Scan() {
|
||||||
|
if scanner.Text() == "---" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if blockLen >= len(block) {
|
||||||
|
newSlice := make([]int, len(block)*2)
|
||||||
|
copy(newSlice, block)
|
||||||
|
block = newSlice
|
||||||
|
}
|
||||||
|
var number int64
|
||||||
|
number, err = strconv.ParseInt(scanner.Text(), 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
block[blockLen] = int(number)
|
||||||
|
blockLen++
|
||||||
|
}
|
||||||
|
block = block[:blockLen]
|
||||||
|
|
||||||
|
programLen := 0
|
||||||
|
program = make([]int, 8)
|
||||||
|
for scanner.Scan() {
|
||||||
|
if programLen >= len(program) {
|
||||||
|
newSlice := make([]int, len(program)*2)
|
||||||
|
copy(newSlice, program)
|
||||||
|
program = newSlice
|
||||||
|
}
|
||||||
|
var number int64
|
||||||
|
number, err = strconv.ParseInt(scanner.Text(), 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
program[programLen] = int(number)
|
||||||
|
programLen++
|
||||||
|
}
|
||||||
|
program = program[:programLen]
|
||||||
|
return
|
||||||
|
}
|
89
creature.go
89
creature.go
|
@ -2,19 +2,16 @@ package creature
|
||||||
|
|
||||||
// Word is a type constraint defining possible word types for the Machine.
|
// Word is a type constraint defining possible word types for the Machine.
|
||||||
type Word interface {
|
type Word interface {
|
||||||
int8 | int16 | int32 | int64 |
|
int8 | int16 | int32 | int64 |
|
||||||
uint8 | uint16 | uint32 | uint64 |
|
uint8 | uint16 | uint32 | uint64 |
|
||||||
int | uint
|
int | uint
|
||||||
}
|
}
|
||||||
|
|
||||||
// Machine is a stack machine. It contains an array of integers as its program
|
// Machine is a stack machine. It contains an array of integers as its program
|
||||||
// data, and provides methods to run this program data, as well as interact with
|
// data, and provides methods to run this program data, as well as interact with
|
||||||
// it.
|
// it.
|
||||||
type Machine [WORD Word] struct {
|
type Machine[WORD Word] struct {
|
||||||
// Program is not modified by the machine, and can be freely set before
|
program []WORD
|
||||||
// the machine is started.
|
|
||||||
Program []WORD
|
|
||||||
|
|
||||||
stack []WORD
|
stack []WORD
|
||||||
block []WORD
|
block []WORD
|
||||||
counter WORD
|
counter WORD
|
||||||
|
@ -25,7 +22,7 @@ type Machine [WORD Word] struct {
|
||||||
// MachineFunction is a function that can extend the functionality of the stack
|
// MachineFunction is a function that can extend the functionality of the stack
|
||||||
// machine. It is passed a pointer to the machine that is calling it, and the
|
// machine. It is passed a pointer to the machine that is calling it, and the
|
||||||
// machine will halt execution if true is returned.
|
// machine will halt execution if true is returned.
|
||||||
type MachineFunction [WORD Word] func(machine *Machine[WORD]) (stop bool)
|
type MachineFunction[WORD Word] func(machine *Machine[WORD]) (stop bool)
|
||||||
|
|
||||||
// All supported opcodes
|
// All supported opcodes
|
||||||
const (
|
const (
|
||||||
|
@ -87,45 +84,41 @@ func (machine *Machine[WORD]) Reset() {
|
||||||
func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
|
func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
|
||||||
machine.counter = offset
|
machine.counter = offset
|
||||||
|
|
||||||
for int(machine.counter) < len(machine.Program) {
|
for int(machine.counter) < len(machine.program) {
|
||||||
switch machine.instruction() {
|
switch machine.instruction() {
|
||||||
case PUSH:
|
case PUSH:
|
||||||
// push the next word in program memory onto the stack
|
|
||||||
machine.counter++
|
machine.counter++
|
||||||
machine.Push(machine.instruction())
|
machine.Push(machine.instruction())
|
||||||
|
|
||||||
case POP:
|
case POP:
|
||||||
// pop the top word off of the stack, and discard it
|
|
||||||
machine.Pop()
|
machine.Pop()
|
||||||
|
|
||||||
case PEEK:
|
case PEEK:
|
||||||
// push the word at an address onto the stack
|
|
||||||
machine.Push(machine.Peek(machine.Pop()))
|
machine.Push(machine.Peek(machine.Pop()))
|
||||||
|
|
||||||
case POKE:
|
case POKE:
|
||||||
// store a word at an address
|
|
||||||
address := machine.Pop()
|
address := machine.Pop()
|
||||||
word := machine.Pop()
|
word := machine.Pop()
|
||||||
machine.Poke(address, word)
|
machine.Poke(address, word)
|
||||||
|
|
||||||
case ADD:
|
case ADD:
|
||||||
// adds the last two words on the stack
|
right := machine.Pop()
|
||||||
machine.Push(machine.Pop() + machine.Pop())
|
left := machine.Pop()
|
||||||
|
machine.Push(left + right)
|
||||||
|
|
||||||
case SUB:
|
case SUB:
|
||||||
// subtracts the second to last word on the stack from
|
right := machine.Pop()
|
||||||
// the last word on the stack
|
left := machine.Pop()
|
||||||
machine.Push(machine.Pop() - machine.Pop())
|
machine.Push(left - right)
|
||||||
|
|
||||||
case MUL:
|
case MUL:
|
||||||
// multiplies the last two words on the stack
|
right := machine.Pop()
|
||||||
machine.Push(machine.Pop() * machine.Pop())
|
left := machine.Pop()
|
||||||
|
machine.Push(left * right)
|
||||||
|
|
||||||
case DIV:
|
case DIV:
|
||||||
// divides the last word on the stack by the second to
|
|
||||||
// last word on the stack
|
|
||||||
left := machine.Pop()
|
|
||||||
right := machine.Pop()
|
right := machine.Pop()
|
||||||
|
left := machine.Pop()
|
||||||
if right == 0 {
|
if right == 0 {
|
||||||
err = ErrorDivideByZero
|
err = ErrorDivideByZero
|
||||||
return
|
return
|
||||||
|
@ -133,62 +126,56 @@ func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
|
||||||
machine.Push(left / right)
|
machine.Push(left / right)
|
||||||
|
|
||||||
case EQ:
|
case EQ:
|
||||||
// checks if the last two words on the stack are equal
|
right := machine.Pop()
|
||||||
|
left := machine.Pop()
|
||||||
equal := 0
|
equal := 0
|
||||||
if machine.Pop() == machine.Pop() {
|
if left == right {
|
||||||
equal = 1
|
equal = 1
|
||||||
}
|
}
|
||||||
machine.Push(WORD(equal))
|
machine.Push(WORD(equal))
|
||||||
|
|
||||||
case GT:
|
case GT:
|
||||||
// checks if the last word on the stack is greater than
|
right := machine.Pop()
|
||||||
// the second to last word on the stack
|
left := machine.Pop()
|
||||||
greater := 0
|
greater := 0
|
||||||
if machine.Pop() > machine.Pop() {
|
if left > right {
|
||||||
greater = 1
|
greater = 1
|
||||||
}
|
}
|
||||||
machine.Push(WORD(greater))
|
machine.Push(WORD(greater))
|
||||||
|
|
||||||
case LT:
|
case LT:
|
||||||
// checks if the last word on the stack is less than the
|
right := machine.Pop()
|
||||||
// second to last word on the stack
|
left := machine.Pop()
|
||||||
less := 0
|
less := 0
|
||||||
if machine.Pop() < machine.Pop() {
|
if left < right {
|
||||||
less = 1
|
less = 1
|
||||||
}
|
}
|
||||||
machine.Push(WORD(less))
|
machine.Push(WORD(less))
|
||||||
|
|
||||||
case NEQ:
|
case NEQ:
|
||||||
// checks if the last two words on the stack are not
|
right := machine.Pop()
|
||||||
// equal
|
left := machine.Pop()
|
||||||
notEqual := 0
|
notEqual := 0
|
||||||
if machine.Pop() != machine.Pop() {
|
if left != right {
|
||||||
notEqual = 1
|
notEqual = 1
|
||||||
}
|
}
|
||||||
machine.Push(WORD(notEqual))
|
machine.Push(WORD(notEqual))
|
||||||
|
|
||||||
case MOD:
|
case MOD:
|
||||||
// performs a modulo operation of the second to last
|
right := machine.Pop()
|
||||||
// word on the stack to the last word on the stack
|
left := machine.Pop()
|
||||||
machine.Push(machine.Pop() % machine.Pop())
|
machine.Push(left % right)
|
||||||
|
|
||||||
case HALT:
|
case HALT:
|
||||||
// stops execution
|
|
||||||
return
|
return
|
||||||
|
|
||||||
case JMP:
|
case JMP:
|
||||||
// jump to the address specified by the last word on the
|
|
||||||
// stack if the second to last word on the stack is
|
|
||||||
// nonzero
|
|
||||||
jumpTo := machine.Pop()
|
jumpTo := machine.Pop()
|
||||||
if machine.Pop() != 0 {
|
if machine.Pop() != 0 {
|
||||||
machine.counter = jumpTo - 1
|
machine.counter = jumpTo - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
case CAL:
|
case CAL:
|
||||||
// call an implementation-defined subroutine, with the
|
|
||||||
// id specified by the last word on the stack. this may
|
|
||||||
// push and pop various things from the stack
|
|
||||||
id := machine.Pop()
|
id := machine.Pop()
|
||||||
if machine.functions == nil {
|
if machine.functions == nil {
|
||||||
break
|
break
|
||||||
|
@ -212,7 +199,7 @@ func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
|
||||||
|
|
||||||
// Instruction returns the current instruction in program memory.
|
// Instruction returns the current instruction in program memory.
|
||||||
func (machine *Machine[WORD]) instruction() (instruction WORD) {
|
func (machine *Machine[WORD]) instruction() (instruction WORD) {
|
||||||
instruction = machine.Program[machine.counter]
|
instruction = machine.program[machine.counter]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +228,7 @@ func (machine *Machine[WORD]) Pop() (word WORD) {
|
||||||
if int(machine.pointer) <= 0 || int(machine.pointer) >= len(machine.stack) {
|
if int(machine.pointer) <= 0 || int(machine.pointer) >= len(machine.stack) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
word = machine.stack[machine.pointer]
|
word = machine.stack[machine.pointer]
|
||||||
machine.pointer--
|
machine.pointer--
|
||||||
|
|
||||||
|
@ -272,7 +259,7 @@ func (machine *Machine[WORD]) Poke(address WORD, word WORD) {
|
||||||
|
|
||||||
// Register registers a function at the specified ID. If there is already a
|
// Register registers a function at the specified ID. If there is already a
|
||||||
// function registered at that ID, this method will return an error.
|
// function registered at that ID, this method will return an error.
|
||||||
func (machine *Machine[WORD]) Register (
|
func (machine *Machine[WORD]) Register(
|
||||||
id WORD,
|
id WORD,
|
||||||
function MachineFunction[WORD],
|
function MachineFunction[WORD],
|
||||||
) (
|
) (
|
||||||
|
@ -301,6 +288,12 @@ func (machine *Machine[WORD]) Unregister(id WORD) {
|
||||||
delete(machine.functions, id)
|
delete(machine.functions, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadProgram loads the contents of program into the machine's program memory.
|
||||||
|
func (machine *Machine[WORD]) LoadProgram(program []WORD) {
|
||||||
|
machine.program = make([]WORD, len(program))
|
||||||
|
copy(machine.program, program)
|
||||||
|
}
|
||||||
|
|
||||||
// LoadMemory loads the contents of block into the machine's memory.
|
// LoadMemory loads the contents of block into the machine's memory.
|
||||||
func (machine *Machine[WORD]) LoadMemory(block []WORD) {
|
func (machine *Machine[WORD]) LoadMemory(block []WORD) {
|
||||||
machine.block = make([]WORD, len(block))
|
machine.block = make([]WORD, len(block))
|
||||||
|
|
|
@ -2,14 +2,15 @@ package creature
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func runMachineTest (
|
func runMachineTest(
|
||||||
program []int,
|
program []int,
|
||||||
memory []int,
|
memory []int,
|
||||||
test *testing.T,
|
test *testing.T,
|
||||||
) (
|
) (
|
||||||
machine *Machine[int],
|
machine *Machine[int],
|
||||||
) {
|
) {
|
||||||
machine = &Machine[int] { Program: program }
|
machine = &Machine[int]{}
|
||||||
|
machine.LoadProgram(program)
|
||||||
if memory != nil {
|
if memory != nil {
|
||||||
machine.LoadMemory(memory)
|
machine.LoadMemory(memory)
|
||||||
}
|
}
|
||||||
|
@ -24,12 +25,12 @@ func runMachineTest (
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPush(test *testing.T) {
|
func TestPush(test *testing.T) {
|
||||||
machine := runMachineTest ([]int {
|
machine := runMachineTest([]int{
|
||||||
PUSH, 3,
|
PUSH, 3,
|
||||||
POP,
|
POP,
|
||||||
PUSH, 654,
|
PUSH, 654,
|
||||||
}, nil, test)
|
}, nil, test)
|
||||||
|
|
||||||
result := machine.Pop()
|
result := machine.Pop()
|
||||||
test.Log("popped:", result)
|
test.Log("popped:", result)
|
||||||
|
|
||||||
|
@ -40,25 +41,25 @@ func TestPush(test *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArithmetic(test *testing.T) {
|
func TestArithmetic(test *testing.T) {
|
||||||
machine := runMachineTest([]int {
|
machine := runMachineTest([]int{
|
||||||
PUSH, 2,
|
|
||||||
PUSH, 3,
|
PUSH, 3,
|
||||||
|
PUSH, 2,
|
||||||
ADD,
|
ADD,
|
||||||
|
|
||||||
PUSH, 4,
|
|
||||||
PUSH, 10,
|
PUSH, 10,
|
||||||
|
PUSH, 4,
|
||||||
SUB,
|
SUB,
|
||||||
|
|
||||||
PUSH, 7,
|
|
||||||
PUSH, 2,
|
PUSH, 2,
|
||||||
|
PUSH, 7,
|
||||||
MUL,
|
MUL,
|
||||||
|
|
||||||
PUSH, 3,
|
|
||||||
PUSH, 12,
|
PUSH, 12,
|
||||||
|
PUSH, 3,
|
||||||
DIV,
|
DIV,
|
||||||
|
|
||||||
PUSH, 6,
|
|
||||||
PUSH, 8,
|
PUSH, 8,
|
||||||
|
PUSH, 6,
|
||||||
MOD,
|
MOD,
|
||||||
}, nil, test)
|
}, nil, test)
|
||||||
|
|
||||||
|
@ -101,23 +102,22 @@ func TestArithmetic(test *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestComparison(test *testing.T) {
|
func TestComparison(test *testing.T) {
|
||||||
machine := runMachineTest([]int {
|
machine := runMachineTest([]int{
|
||||||
PUSH, 6,
|
PUSH, 6,
|
||||||
PUSH, 6,
|
PUSH, 6,
|
||||||
EQ,
|
EQ,
|
||||||
|
|
||||||
PUSH, 4,
|
|
||||||
PUSH, 324,
|
PUSH, 324,
|
||||||
|
PUSH, 4,
|
||||||
GT,
|
GT,
|
||||||
|
|
||||||
PUSH, 324,
|
|
||||||
PUSH, 4,
|
PUSH, 4,
|
||||||
|
PUSH, 324,
|
||||||
LT,
|
LT,
|
||||||
|
|
||||||
PUSH, 6,
|
|
||||||
PUSH, 54,
|
PUSH, 54,
|
||||||
|
PUSH, 6,
|
||||||
NEQ,
|
NEQ,
|
||||||
|
|
||||||
}, nil, test)
|
}, nil, test)
|
||||||
|
|
||||||
neqResult := machine.Pop()
|
neqResult := machine.Pop()
|
||||||
|
@ -152,7 +152,7 @@ func TestComparison(test *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPeekPoke(test *testing.T) {
|
func TestPeekPoke(test *testing.T) {
|
||||||
machine := runMachineTest ([]int {
|
machine := runMachineTest([]int{
|
||||||
PUSH, 0,
|
PUSH, 0,
|
||||||
PEEK,
|
PEEK,
|
||||||
|
|
||||||
|
@ -162,14 +162,14 @@ func TestPeekPoke(test *testing.T) {
|
||||||
|
|
||||||
PUSH, 1,
|
PUSH, 1,
|
||||||
PEEK,
|
PEEK,
|
||||||
}, []int {
|
}, []int{
|
||||||
632,
|
632,
|
||||||
13,
|
13,
|
||||||
}, test)
|
}, test)
|
||||||
|
|
||||||
secondResult := machine.Pop()
|
secondResult := machine.Pop()
|
||||||
firstResult := machine.Pop()
|
firstResult := machine.Pop()
|
||||||
|
|
||||||
test.Log("first:", firstResult)
|
test.Log("first:", firstResult)
|
||||||
test.Log("second:", secondResult)
|
test.Log("second:", secondResult)
|
||||||
|
|
||||||
|
@ -185,12 +185,12 @@ func TestPeekPoke(test *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHalt(test *testing.T) {
|
func TestHalt(test *testing.T) {
|
||||||
machine := runMachineTest ([]int {
|
machine := runMachineTest([]int{
|
||||||
PUSH, 32,
|
PUSH, 32,
|
||||||
HALT,
|
HALT,
|
||||||
PUSH, 3,
|
PUSH, 3,
|
||||||
}, nil, test)
|
}, nil, test)
|
||||||
|
|
||||||
result := machine.Pop()
|
result := machine.Pop()
|
||||||
test.Log("popped:", result)
|
test.Log("popped:", result)
|
||||||
|
|
||||||
|
@ -201,11 +201,11 @@ func TestHalt(test *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJump(test *testing.T) {
|
func TestJump(test *testing.T) {
|
||||||
machine := runMachineTest ([]int {
|
machine := runMachineTest([]int{
|
||||||
PUSH, 1,
|
PUSH, 1,
|
||||||
PUSH, 8,
|
PUSH, 8,
|
||||||
JMP,
|
JMP,
|
||||||
|
|
||||||
PUSH, 3,
|
PUSH, 3,
|
||||||
HALT,
|
HALT,
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ func TestJump(test *testing.T) {
|
||||||
|
|
||||||
PUSH, 5,
|
PUSH, 5,
|
||||||
}, nil, test)
|
}, nil, test)
|
||||||
|
|
||||||
result := machine.Pop()
|
result := machine.Pop()
|
||||||
test.Log("popped:", result)
|
test.Log("popped:", result)
|
||||||
|
|
||||||
|
@ -230,7 +230,8 @@ func TestJump(test *testing.T) {
|
||||||
|
|
||||||
func TestRegister(test *testing.T) {
|
func TestRegister(test *testing.T) {
|
||||||
output := ""
|
output := ""
|
||||||
machine := &Machine[int] { Program: []int {
|
machine := &Machine[int]{}
|
||||||
|
machine.LoadProgram([]int{
|
||||||
PUSH, int('h'),
|
PUSH, int('h'),
|
||||||
PUSH, 4,
|
PUSH, 4,
|
||||||
CAL,
|
CAL,
|
||||||
|
@ -258,7 +259,7 @@ func TestRegister(test *testing.T) {
|
||||||
PUSH, int('!'),
|
PUSH, int('!'),
|
||||||
PUSH, 4,
|
PUSH, 4,
|
||||||
CAL,
|
CAL,
|
||||||
}}
|
})
|
||||||
machine.Register(4, func(machine *Machine[int]) (stop bool) {
|
machine.Register(4, func(machine *Machine[int]) (stop bool) {
|
||||||
output += string(rune(machine.Pop()))
|
output += string(rune(machine.Pop()))
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build ignore
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
@ -5,11 +6,12 @@ package main
|
||||||
import "os"
|
import "os"
|
||||||
import cre "git.tebibyte.media/sashakoshka/creature"
|
import cre "git.tebibyte.media/sashakoshka/creature"
|
||||||
|
|
||||||
func main () {
|
func main() {
|
||||||
// this is a simple echo program. it will take in input indefinetly and
|
// this is a simple echo program. it will take in input indefinetly and
|
||||||
// repeat it. due to line buffering in the terminal however, it will
|
// repeat it. due to line buffering in the terminal however, it will
|
||||||
// only print output once you have pressed enter.
|
// only print output once you have pressed enter.
|
||||||
machine := cre.Machine[int] { Program: []int {
|
machine := cre.Machine[int]{}
|
||||||
|
machine.LoadProgram([]int{
|
||||||
cre.PUSH, 0,
|
cre.PUSH, 0,
|
||||||
cre.CAL,
|
cre.CAL,
|
||||||
|
|
||||||
|
@ -19,22 +21,24 @@ func main () {
|
||||||
cre.PUSH, 1,
|
cre.PUSH, 1,
|
||||||
cre.PUSH, 0,
|
cre.PUSH, 0,
|
||||||
cre.JMP,
|
cre.JMP,
|
||||||
}}
|
})
|
||||||
|
|
||||||
machine.Register (0, read)
|
machine.Register(0, read)
|
||||||
machine.Register (1, write)
|
machine.Register(1, write)
|
||||||
err := machine.Execute(0)
|
err := machine.Execute(0)
|
||||||
if err != nil { panic(err.Error()) }
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func read (machine *cre.Machine[int]) (stop bool) {
|
func read(machine *cre.Machine[int]) (stop bool) {
|
||||||
ch := []byte { 0 }
|
ch := []byte{0}
|
||||||
os.Stdin.Read(ch)
|
os.Stdin.Read(ch)
|
||||||
machine.Push(int(ch[0]))
|
machine.Push(int(ch[0]))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func write (machine *cre.Machine[int]) (stop bool) {
|
func write(machine *cre.Machine[int]) (stop bool) {
|
||||||
print(string(rune(machine.Pop())))
|
print(string(rune(machine.Pop())))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build ignore
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
@ -5,7 +6,7 @@ package main
|
||||||
import "os"
|
import "os"
|
||||||
import cre "git.tebibyte.media/sashakoshka/creature"
|
import cre "git.tebibyte.media/sashakoshka/creature"
|
||||||
|
|
||||||
func main () {
|
func main() {
|
||||||
// this is an "ai psychiatrist" program. when it starts, it asks the
|
// this is an "ai psychiatrist" program. when it starts, it asks the
|
||||||
// user "What brings you in today?", and no matter what the user types,
|
// user "What brings you in today?", and no matter what the user types,
|
||||||
// the program will respond with "And how does that make you feel?".
|
// the program will respond with "And how does that make you feel?".
|
||||||
|
@ -13,14 +14,15 @@ func main () {
|
||||||
// point.
|
// point.
|
||||||
|
|
||||||
// constant address values
|
// constant address values
|
||||||
x := 0
|
x := 0
|
||||||
introStart := 1
|
introStart := 1
|
||||||
introLoopStart := 5
|
introLoopStart := 5
|
||||||
readLoopStart := 36
|
readLoopStart := 36
|
||||||
responseStart := 71
|
responseStart := 71
|
||||||
responseLoopStart := 50
|
responseLoopStart := 50
|
||||||
|
|
||||||
machine := cre.Machine[int] { Program: []int {
|
machine := cre.Machine[int]{}
|
||||||
|
machine.LoadProgram([]int{
|
||||||
// reset x
|
// reset x
|
||||||
cre.PUSH, introStart,
|
cre.PUSH, introStart,
|
||||||
cre.PUSH, x,
|
cre.PUSH, x,
|
||||||
|
@ -37,7 +39,7 @@ func main () {
|
||||||
cre.EQ,
|
cre.EQ,
|
||||||
cre.PUSH, readLoopStart,
|
cre.PUSH, readLoopStart,
|
||||||
cre.JMP,
|
cre.JMP,
|
||||||
|
|
||||||
// get char
|
// get char
|
||||||
cre.PUSH, x,
|
cre.PUSH, x,
|
||||||
cre.PEEK,
|
cre.PEEK,
|
||||||
|
@ -86,7 +88,7 @@ func main () {
|
||||||
cre.EQ,
|
cre.EQ,
|
||||||
cre.PUSH, readLoopStart,
|
cre.PUSH, readLoopStart,
|
||||||
cre.JMP,
|
cre.JMP,
|
||||||
|
|
||||||
// get char
|
// get char
|
||||||
cre.PUSH, x,
|
cre.PUSH, x,
|
||||||
cre.PEEK,
|
cre.PEEK,
|
||||||
|
@ -108,35 +110,37 @@ func main () {
|
||||||
cre.PUSH, 1,
|
cre.PUSH, 1,
|
||||||
cre.PUSH, responseLoopStart,
|
cre.PUSH, responseLoopStart,
|
||||||
cre.JMP,
|
cre.JMP,
|
||||||
}}
|
})
|
||||||
|
|
||||||
stringData := []byte (
|
stringData := []byte(
|
||||||
"\x00" +
|
"\x00" +
|
||||||
"== Artificial Intelligence Psychiatrist ==\n" +
|
"== Artificial Intelligence Psychiatrist ==\n" +
|
||||||
"What brings you in today?\n\x00" +
|
"What brings you in today?\n\x00" +
|
||||||
"And how does that make you feel?\n\x00")
|
"And how does that make you feel?\n\x00")
|
||||||
block := make([]int, len(stringData))
|
block := make([]int, len(stringData))
|
||||||
|
|
||||||
for index, char := range stringData {
|
for index, char := range stringData {
|
||||||
block[index] = int(char)
|
block[index] = int(char)
|
||||||
}
|
}
|
||||||
|
|
||||||
machine.LoadMemory(block)
|
machine.LoadMemory(block)
|
||||||
machine.Register (0, read)
|
machine.Register(0, read)
|
||||||
machine.Register (1, write)
|
machine.Register(1, write)
|
||||||
|
|
||||||
err := machine.Execute(0)
|
err := machine.Execute(0)
|
||||||
if err != nil { panic(err.Error()) }
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func read (machine *cre.Machine[int]) (stop bool) {
|
func read(machine *cre.Machine[int]) (stop bool) {
|
||||||
ch := []byte { 0 }
|
ch := []byte{0}
|
||||||
os.Stdin.Read(ch)
|
os.Stdin.Read(ch)
|
||||||
machine.Push(int(ch[0]))
|
machine.Push(int(ch[0]))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func write (machine *cre.Machine[int]) (stop bool) {
|
func write(machine *cre.Machine[int]) (stop bool) {
|
||||||
print(string(rune(machine.Pop())))
|
print(string(rune(machine.Pop())))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue