132 lines
2.9 KiB
Go
132 lines
2.9 KiB
Go
package main
|
|
|
|
type Memory [65535]byte
|
|
|
|
func (m *Memory) Read(address uint16) byte {
|
|
return m[address]
|
|
}
|
|
|
|
func (m *Memory) ReadLong(address uint16) uint16 {
|
|
return To16(m[address], m[address+1])
|
|
}
|
|
|
|
func (m *Memory) Read2(address uint16) (byte, byte) {
|
|
return m[address], m[address+1] // May error if called at max index for the low
|
|
}
|
|
|
|
func (m *Memory) Write(address uint16, value byte) {
|
|
m[address] = value
|
|
}
|
|
|
|
func (m *Memory) WriteLong(address uint16, value uint16) {
|
|
h, l := To8s(value)
|
|
m.Write(address, h)
|
|
address++
|
|
if address >= MaxUint16-1 {
|
|
address = 0
|
|
}
|
|
m.Write(address, l)
|
|
}
|
|
|
|
func (m *Memory) Write2(address uint16, high, low byte) {
|
|
m.Write(address, high)
|
|
address++
|
|
if address >= MaxUint16-1 {
|
|
address = 0
|
|
}
|
|
m.Write(address, low)
|
|
}
|
|
|
|
type Stack struct {
|
|
pointer byte
|
|
data [255]byte
|
|
}
|
|
|
|
func (s Stack) Peek() byte {
|
|
if s.pointer == 0 {
|
|
return s.data[len(s.data)-1]
|
|
}
|
|
return s.data[s.pointer-1]
|
|
}
|
|
|
|
func (s Stack) Peek2() (byte, byte) {
|
|
low := s.Peek()
|
|
var high byte
|
|
if s.pointer == 1 {
|
|
high = s.data[len(s.data)-1]
|
|
} else if s.pointer == 0 {
|
|
high = s.data[len(s.data)-2]
|
|
} else {
|
|
high = s.data[s.pointer-2]
|
|
}
|
|
return high, low
|
|
}
|
|
|
|
func (s *Stack) Push(b byte) {
|
|
s.data[s.pointer] = b
|
|
if s.pointer == MaxByte-1 {
|
|
s.pointer = 0
|
|
} else {
|
|
s.pointer++
|
|
}
|
|
}
|
|
|
|
func (s *Stack) Push2(h, l byte) {
|
|
s.Push(h)
|
|
s.Push(l)
|
|
}
|
|
|
|
func (s *Stack) PushLong(n uint16) {
|
|
h, l := To8s(n)
|
|
s.Push2(h, l)
|
|
}
|
|
|
|
func (s *Stack) Pop() byte {
|
|
if s.pointer == 0 {
|
|
s.pointer = 254
|
|
} else {
|
|
s.pointer--
|
|
}
|
|
return s.data[s.pointer]
|
|
}
|
|
|
|
func (s *Stack) Pop2() (byte, byte) {
|
|
low := s.Pop()
|
|
return s.Pop(), low
|
|
}
|
|
|
|
func (s *Stack) PopLong() (uint16) {
|
|
h, l := s.Pop2()
|
|
return To16(h,l)
|
|
}
|
|
|
|
/* The idea of the heap pointer is that after all
|
|
* data and ops are written to the memory space the
|
|
* heap space will start to get used up. However,
|
|
* when a subroutine is called with `cal`, the current
|
|
* HP will be added to the return stack along with the
|
|
* return address. When `ret` is called, it will change
|
|
* the PC to the return address and HP to its previous
|
|
* position. This allows for temporary variable usage
|
|
* within the heap space and clear things up as needed.
|
|
* The only issue is how to do something like dynamically
|
|
* read in a string to a memory space. Since the length is
|
|
* not known in advance, the space cannot be reserved in
|
|
* advance. As a result, there is a case to be made for
|
|
* not doing this at all, and essentially just fillling
|
|
* up space without freeing it, especially since many of
|
|
* the references in question will have their sizes known
|
|
* prior to runtime. Doing all sized references in a "data"
|
|
* section makes sense. That would leave the heap section
|
|
* for large dynamic runtime things...
|
|
*/
|
|
|
|
type Vm struct {
|
|
PC uint16 // Current Position
|
|
Mem Memory
|
|
Prog Memory
|
|
Data Stack
|
|
Return Stack
|
|
HP uint16
|
|
}
|