Compare commits
5 Commits
v1.0.0
...
b0068336cd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0068336cd | ||
|
|
3be08736b4 | ||
| cfae69c0c2 | |||
| d7d5be6949 | |||
| e342d9b254 |
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
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
Pops:
|
||||
1. left operand
|
||||
2. right operand
|
||||
1. right operand
|
||||
2. left operand
|
||||
|
||||
Pushes:
|
||||
1. left operand + right operand
|
||||
@@ -64,8 +69,8 @@ Pushes:
|
||||
Subtracts two words.
|
||||
|
||||
Pops:
|
||||
1. left operand
|
||||
2. right operand
|
||||
1. right operand
|
||||
2. left operand
|
||||
|
||||
Pushes:
|
||||
1. left operand - right operand
|
||||
@@ -75,8 +80,8 @@ Pushes:
|
||||
Multiplies two words.
|
||||
|
||||
Pops:
|
||||
1. left operand
|
||||
2. right operand
|
||||
1. right operand
|
||||
2. left operand
|
||||
|
||||
Pushes:
|
||||
1. left operand * right operand
|
||||
@@ -86,8 +91,8 @@ Pushes:
|
||||
Divides two words.
|
||||
|
||||
Pops:
|
||||
1. left operand
|
||||
2. right operand
|
||||
1. right operand
|
||||
2. left operand
|
||||
|
||||
Pushes:
|
||||
1. left operand / right operand
|
||||
@@ -97,8 +102,8 @@ Pushes:
|
||||
Checks if two words are equal. Pushes 1 if true, zero if false.
|
||||
|
||||
Pops:
|
||||
1. left operand
|
||||
2. right operand
|
||||
1. right operand
|
||||
2. left operand
|
||||
|
||||
Pushes:
|
||||
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.
|
||||
|
||||
Pops:
|
||||
1. left operand
|
||||
2. right operand
|
||||
1. right operand
|
||||
2. left operand
|
||||
|
||||
Pushes:
|
||||
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.
|
||||
|
||||
Pops:
|
||||
1. left operand
|
||||
2. right operand
|
||||
1. right operand
|
||||
2. left operand
|
||||
|
||||
Pushes:
|
||||
1. left operand < right operand
|
||||
@@ -130,8 +135,8 @@ Pushes:
|
||||
Checks if two words are *not* equal. Pushes 1 if true, zero if false.
|
||||
|
||||
Pops:
|
||||
1. left operand
|
||||
2. right operand
|
||||
1. right operand
|
||||
2. left operand
|
||||
|
||||
Pushes:
|
||||
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.
|
||||
|
||||
Pops:
|
||||
1. left operand
|
||||
2. right operand
|
||||
1. right operand
|
||||
2. left operand
|
||||
|
||||
Pushes:
|
||||
1. left operand % right operand
|
||||
|
||||
77
creature.go
77
creature.go
@@ -11,10 +11,7 @@ type Word interface {
|
||||
// data, and provides methods to run this program data, as well as interact with
|
||||
// it.
|
||||
type Machine [WORD Word] struct {
|
||||
// Program is not modified by the machine, and can be freely set before
|
||||
// the machine is started.
|
||||
Program []WORD
|
||||
|
||||
program []WORD
|
||||
stack []WORD
|
||||
block []WORD
|
||||
counter WORD
|
||||
@@ -87,45 +84,41 @@ func (machine *Machine[WORD]) Reset() {
|
||||
func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
|
||||
machine.counter = offset
|
||||
|
||||
for int(machine.counter) < len(machine.Program) {
|
||||
for int(machine.counter) < len(machine.program) {
|
||||
switch machine.instruction() {
|
||||
case PUSH:
|
||||
// push the next word in program memory onto the stack
|
||||
machine.counter++
|
||||
machine.Push(machine.instruction())
|
||||
|
||||
case POP:
|
||||
// pop the top word off of the stack, and discard it
|
||||
machine.Pop()
|
||||
|
||||
case PEEK:
|
||||
// push the word at an address onto the stack
|
||||
machine.Push(machine.Peek(machine.Pop()))
|
||||
|
||||
case POKE:
|
||||
// store a word at an address
|
||||
address := machine.Pop()
|
||||
word := machine.Pop()
|
||||
machine.Poke(address, word)
|
||||
|
||||
case ADD:
|
||||
// adds the last two words on the stack
|
||||
machine.Push(machine.Pop() + machine.Pop())
|
||||
right := machine.Pop()
|
||||
left := machine.Pop()
|
||||
machine.Push(left + right)
|
||||
|
||||
case SUB:
|
||||
// subtracts the second to last word on the stack from
|
||||
// the last word on the stack
|
||||
machine.Push(machine.Pop() - machine.Pop())
|
||||
right := machine.Pop()
|
||||
left := machine.Pop()
|
||||
machine.Push(left - right)
|
||||
|
||||
case MUL:
|
||||
// multiplies the last two words on the stack
|
||||
machine.Push(machine.Pop() * machine.Pop())
|
||||
right := machine.Pop()
|
||||
left := machine.Pop()
|
||||
machine.Push(left * right)
|
||||
|
||||
case DIV:
|
||||
// divides the last word on the stack by the second to
|
||||
// last word on the stack
|
||||
left := machine.Pop()
|
||||
right := machine.Pop()
|
||||
left := machine.Pop()
|
||||
if right == 0 {
|
||||
err = ErrorDivideByZero
|
||||
return
|
||||
@@ -133,62 +126,56 @@ func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
|
||||
machine.Push(left / right)
|
||||
|
||||
case EQ:
|
||||
// checks if the last two words on the stack are equal
|
||||
right := machine.Pop()
|
||||
left := machine.Pop()
|
||||
equal := 0
|
||||
if machine.Pop() == machine.Pop() {
|
||||
if left == right {
|
||||
equal = 1
|
||||
}
|
||||
machine.Push(WORD(equal))
|
||||
|
||||
case GT:
|
||||
// checks if the last word on the stack is greater than
|
||||
// the second to last word on the stack
|
||||
right := machine.Pop()
|
||||
left := machine.Pop()
|
||||
greater := 0
|
||||
if machine.Pop() > machine.Pop() {
|
||||
if left > right {
|
||||
greater = 1
|
||||
}
|
||||
machine.Push(WORD(greater))
|
||||
|
||||
case LT:
|
||||
// checks if the last word on the stack is less than the
|
||||
// second to last word on the stack
|
||||
right := machine.Pop()
|
||||
left := machine.Pop()
|
||||
less := 0
|
||||
if machine.Pop() < machine.Pop() {
|
||||
if left < right {
|
||||
less = 1
|
||||
}
|
||||
machine.Push(WORD(less))
|
||||
|
||||
case NEQ:
|
||||
// checks if the last two words on the stack are not
|
||||
// equal
|
||||
right := machine.Pop()
|
||||
left := machine.Pop()
|
||||
notEqual := 0
|
||||
if machine.Pop() != machine.Pop() {
|
||||
if left != right {
|
||||
notEqual = 1
|
||||
}
|
||||
machine.Push(WORD(notEqual))
|
||||
|
||||
case MOD:
|
||||
// performs a modulo operation of the second to last
|
||||
// word on the stack to the last word on the stack
|
||||
machine.Push(machine.Pop() % machine.Pop())
|
||||
right := machine.Pop()
|
||||
left := machine.Pop()
|
||||
machine.Push(left % right)
|
||||
|
||||
case HALT:
|
||||
// stops execution
|
||||
return
|
||||
|
||||
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()
|
||||
if machine.Pop() != 0 {
|
||||
machine.counter = jumpTo - 1
|
||||
}
|
||||
|
||||
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()
|
||||
if machine.functions == nil {
|
||||
break
|
||||
@@ -212,7 +199,7 @@ func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
|
||||
|
||||
// Instruction returns the current instruction in program memory.
|
||||
func (machine *Machine[WORD]) instruction() (instruction WORD) {
|
||||
instruction = machine.Program[machine.counter]
|
||||
instruction = machine.program[machine.counter]
|
||||
return
|
||||
}
|
||||
|
||||
@@ -263,7 +250,7 @@ func (machine *Machine[WORD]) Peek(address WORD) (word WORD) {
|
||||
// Poke sets the value at address in the block to word.
|
||||
func (machine *Machine[WORD]) Poke(address WORD, word WORD) {
|
||||
if int(address) >= len(machine.block) {
|
||||
reallocatedBlock := make([]WORD, address*3/2)
|
||||
reallocatedBlock := make([]WORD, address*3/2+1)
|
||||
copy(reallocatedBlock, machine.block)
|
||||
machine.block = reallocatedBlock
|
||||
}
|
||||
@@ -301,6 +288,12 @@ func (machine *Machine[WORD]) Unregister(id WORD) {
|
||||
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.
|
||||
func (machine *Machine[WORD]) LoadMemory(block []WORD) {
|
||||
machine.block = make([]WORD, len(block))
|
||||
|
||||
@@ -9,7 +9,8 @@ func runMachineTest (
|
||||
) (
|
||||
machine *Machine[int],
|
||||
) {
|
||||
machine = &Machine[int] { Program: program }
|
||||
machine = &Machine[int] {}
|
||||
machine.LoadProgram(program)
|
||||
if memory != nil {
|
||||
machine.LoadMemory(memory)
|
||||
}
|
||||
@@ -41,24 +42,24 @@ func TestPush(test *testing.T) {
|
||||
|
||||
func TestArithmetic(test *testing.T) {
|
||||
machine := runMachineTest([]int {
|
||||
PUSH, 2,
|
||||
PUSH, 3,
|
||||
PUSH, 2,
|
||||
ADD,
|
||||
|
||||
PUSH, 4,
|
||||
PUSH, 10,
|
||||
PUSH, 4,
|
||||
SUB,
|
||||
|
||||
PUSH, 7,
|
||||
PUSH, 2,
|
||||
PUSH, 7,
|
||||
MUL,
|
||||
|
||||
PUSH, 3,
|
||||
PUSH, 12,
|
||||
PUSH, 3,
|
||||
DIV,
|
||||
|
||||
PUSH, 6,
|
||||
PUSH, 8,
|
||||
PUSH, 6,
|
||||
MOD,
|
||||
}, nil, test)
|
||||
|
||||
@@ -106,16 +107,16 @@ func TestComparison(test *testing.T) {
|
||||
PUSH, 6,
|
||||
EQ,
|
||||
|
||||
PUSH, 4,
|
||||
PUSH, 324,
|
||||
PUSH, 4,
|
||||
GT,
|
||||
|
||||
PUSH, 324,
|
||||
PUSH, 4,
|
||||
PUSH, 324,
|
||||
LT,
|
||||
|
||||
PUSH, 6,
|
||||
PUSH, 54,
|
||||
PUSH, 6,
|
||||
NEQ,
|
||||
|
||||
}, nil, test)
|
||||
@@ -230,7 +231,8 @@ func TestJump(test *testing.T) {
|
||||
|
||||
func TestRegister(test *testing.T) {
|
||||
output := ""
|
||||
machine := &Machine[int] { Program: []int {
|
||||
machine := &Machine[int] {}
|
||||
machine.LoadProgram([]int {
|
||||
PUSH, int('h'),
|
||||
PUSH, 4,
|
||||
CAL,
|
||||
@@ -258,7 +260,7 @@ func TestRegister(test *testing.T) {
|
||||
PUSH, int('!'),
|
||||
PUSH, 4,
|
||||
CAL,
|
||||
}}
|
||||
})
|
||||
machine.Register(4, func(machine *Machine[int]) (stop bool) {
|
||||
output += string(rune(machine.Pop()))
|
||||
return
|
||||
|
||||
@@ -9,7 +9,8 @@ func main () {
|
||||
// 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
|
||||
// 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.CAL,
|
||||
|
||||
@@ -19,7 +20,7 @@ func main () {
|
||||
cre.PUSH, 1,
|
||||
cre.PUSH, 0,
|
||||
cre.JMP,
|
||||
}}
|
||||
})
|
||||
|
||||
machine.Register (0, read)
|
||||
machine.Register (1, write)
|
||||
|
||||
@@ -20,7 +20,8 @@ func main () {
|
||||
responseStart := 71
|
||||
responseLoopStart := 50
|
||||
|
||||
machine := cre.Machine[int] { Program: []int {
|
||||
machine := cre.Machine[int] {}
|
||||
machine.LoadProgram([]int {
|
||||
// reset x
|
||||
cre.PUSH, introStart,
|
||||
cre.PUSH, x,
|
||||
@@ -108,7 +109,7 @@ func main () {
|
||||
cre.PUSH, 1,
|
||||
cre.PUSH, responseLoopStart,
|
||||
cre.JMP,
|
||||
}}
|
||||
})
|
||||
|
||||
stringData := []byte (
|
||||
"\x00" +
|
||||
|
||||
Reference in New Issue
Block a user