This is the specification for the architecture of the Bedrock computer system.
This document is aimed at people who are implementing the Bedrock system from scratch. For people who are learning about or writing program for the Bedrock system, the architecture manual or instruction set manual will generally be more useful.
Overview
This document describes the core architecture required to run a Bedrock program.
The program memory holds the current program, the two stacks hold temporary data, the device bus connects the system to peripheral devices, and the processor runs the program and ties the whole system together.
Program memory
The program memory is a 65536 byte block of writeable memory. The initial value of each byte of memory is zero.
Systems with fewer than 65536 bytes of program memory are partially supported. Reading or writing to an unimplemented memory address will cause undefined behaviour.
Reading from memory
Reading a byte from program memory will return the byte at the given address.
Reading a double from program memory will read the high byte of the double from the given address and the low byte from the following address. Reading a double from address 0xFFFF
will cause undefined behaviour.
Writing to memory
Writing a byte to program memory will write that byte to the given address.
Writing a double to program memory will write the high byte of the double to the given address and the low byte to the following address. Writing a double to address 0xFFFF
will cause undefined behaviour.
Loading a program
Each byte of a program is loaded sequentially into memory starting at address zero. The program is truncated if it exceeds 65536 bytes.
Stacks
A stack is a 256 byte block of writeable memory with an associated 8 bit stack pointer. Bedrock has two stacks, called the working stack and the return stack. The initial value of each byte of memory is undefined, and the initial value of each stack pointer is zero.
Systems with fewer than 256 bytes of stack memory per stack are partially supported. Reading or writing to an unimplemented memory address will cause undefined behaviour.
Pushing to a stack
Pushing a byte to a stack will write that byte to the address referenced by the stack pointer, and then will increment the stack pointer by 1. Overflowing the stack pointer will cause undefined behaviour.
Pushing a double to a stack will push the high byte of the double to the stack, followed by the low byte.
Popping from a stack
Popping a byte from a stack will decrement the stack pointer by 1, and then will read the byte from the address referenced by the stack pointer. Underflowing the stack pointer will cause undefined behaviour.
Popping a double from a stack will pop the low byte of the double from the stack, followed by the high byte.
Device bus
The device bus is an array of 256 communication ports. Each group of sixteen ports is called a slot, and interfaces with a single device. Each port is addressed by an 8 bit port number, and can send or receive bytes from a device.
Reading from a device
Reading a byte from the device bus will send a read request to the given port, and then the device connected to that port will process the request and return a byte. There is no limit to the duration of a request. If the port is not connected to a device, the request will return immediately with a value of zero.
Reading a double from the device bus will read the high byte of the double from the given port and then the low byte from the following port. Reading a double from port 0xFF
will cause undefined behaviour.
Writing to a device
Writing a byte to the device bus will send a write request containing that byte to the given port, and then the device connected to that port will process the request and return. There is no limit to the duration of a request. If the port is not connected to a device, the request will return immediately.
Writing a double to the device bus will write the high byte of the double to the given port and then the low byte to the following port. Writing a double to port 0xFF
will cause undefined behaviour.
Processor
The processor executes instructions from program memory. The program counter is a 16 bit value that contains the address of the next instruction to execute. The initial value of the program counter is zero.
Executing an instruction
To execute an instruction, a byte will be loaded from the address referenced by the program counter, then the program counter is incremented by 1, and then the instruction is executed according to the instruction value. Overflowing the program counter will cause undefined behaviour.
The processor will execute instructions repeatedly. The system will halt when a zero byte is executed.
Instruction format
Each instruction is a single byte that contains three mode flags and an operation value. The upper three bits represent the mode flags, and the lower five bits represent the operation value.
Return mode
When bit 0x80
of an instruction byte is set, the operation will use the return stack in place of the working stack, and the working stack in place of the return stack.
Wide mode
When bit 0x40
of an instruction byte is set, each value (neither a byte or a double) listed in the operation description will be a double, otherwise each value will be a byte.
Immediate mode
When bit 0x20
of an instruction byte is set, the first byte or double that would be popped from either stack by the operation will instead be read from the address referenced by the program counter. The program counter will be incremented by 1 for each byte read. Overflowing the program counter will cause undefined behaviour.
Operation value
The lower five bits of an instruction determine the operation to be performed, as per the following table:
Oper. | Name | Description |
---|---|---|
0x00 |
HLT | Halt the processor. |
0x01 |
PSH | Move a value between stacks. |
0x02 |
POP | Remove a value from a stack. |
0x03 |
CPY | Copy a value between stacks. |
0x04 |
DUP | Duplicate a value. |
0x05 |
OVR | Duplicate the next value down. |
0x06 |
SWP | Swap the top two values. |
0x07 |
ROT | Rotate the top three values. |
0x08 |
JMP | Jump to an address. |
0x09 |
JMS | Jump to an address, keeping the current address. |
0x0A |
JCN | Conditionally jump. |
0x0B |
JCS | Conditionally jump, keeping the current address. |
0x0C |
LDA | Load a value from a memory address. |
0x0D |
STA | Store a value to a memory address. |
0x0E |
LDD | Load a value from a device port. |
0x0F |
STD | Store a value to a device port. |
0x10 |
ADD | Add two values. |
0x11 |
SUB | Subtract one value from another. |
0x12 |
INC | Increment a value. |
0x13 |
DEC | Decrement a value. |
0x14 |
LTH | Test if a value is less than another. |
0x15 |
GTH | Test if a value is greater than another. |
0x16 |
EQU | Test if two values are equal. |
0x17 |
NQK | Test if two values are inequal, keeping both. |
0x18 |
SHL | Shift the bits in a value to the left. |
0x19 |
SHR | Shift the bits in a value to the right. |
0x1A |
ROL | Rotate the bits in a value to the left. |
0x1B |
ROR | Rotate the bits in a value to the right. |
0x1C |
IOR | Find the bits that are set in either value. |
0x1D |
XOR | Find the bits that are different in both values. |
0x1E |
AND | Find the bits that are set in both values. |
0x1F |
NOT | Invert the bits in a value. |
Operations
HLT
Has no effect. If none of the mode flags are set, the system is halted.
The operation variants 0x40
, 0x60
, 0x80
, 0xA0
, 0xC0
, and 0xE0
can be used for triggering implementation-specific debug functionality.
PSH
The value x
is popped from the return stack, and then x
is pushed to the working stack.
POP
The value x
is popped from the working stack.
CPY
The value x
is popped from the return stack, then x
is pushed to the return stack, and then x
is pushed to the working stack.
DUP
The value x
is popped from the working stack, then x
is pushed to the working stack, and then x
is pushed to the working stack.
OVR
The value y
is popped from the working stack, then the value x
is popped from the working stack, then x
is pushed to the working stack, then y
is pushed to the working stack, and then x
is pushed to the working stack.
SWP
The value y
is popped from the working stack, then the value x
is popped from the working stack, then y
is pushed to the working stack, and then x
is pushed to the working stack.
ROT
The value z
is popped from the working stack, then the value y
is popped from the working stack, then the value x
is popped from the working stack, then y
is pushed to the working stack, then z
is pushed to the working stack, and then x
is pushed to the working stack.
JMP
The double a
is popped from the working stack and then is written to the program counter.
JMS
The double a
is popped from the working stack, then the double b
is read from the program counter and pushed to the return stack, and then a
is written to the program counter.
JCN
The double a
is popped from the working stack, then the value t
is popped from the working stack. If t
is not zero, a
is written to the program counter.
JCS
The double a
is popped from the working stack, then the value t
is popped from the working stack. If t
is not zero, the double b
is read from the program counter and pushed to the return stack, and then a
is written to the program counter.
LDA
The double a
is popped from the working stack, then the value v
is read from program memory at the address referenced by a
, and then v
is pushed to the working stack.
STA
The double a
is popped from the working stack, then the value v
is popped from the working stack, and then v
is written to program memory at the address referenced by a
.
LDD
The byte p
is popped from the working stack, then the value v
is read from the device bus at the port referenced by p
, and then v
is pushed to the working stack.
STD
The byte p
is popped from the working stack, then the value v
is popped from the working stack, and then v
is written to the device bus at the port referenced by p
.
ADD
The value y
is popped from the working stack, then the value x
is popped from the working stack, then the value r
is calculated as the wrapped addition of y
to x
, and then r
is pushed to the working stack.
SUB
The value y
is popped from the working stack, then the value x
is popped from the working stack, then the value r
is calculated as the wrapped subtraction of y
from x
, and then r
is pushed to the working stack.
INC
The value x
is popped from the working stack, then the value r
is calculated as the wrapped addition of 1 to x
, and then r
is pushed to the working stack.
DEC
The value x
is popped from the working stack, then the value r
is calculated as the wrapped subtraction of 1 from x
, and then r
is pushed to the working stack.
LTH
The value y
is popped from the working stack, and then the value x
is popped from the working stack. If x
is less than y
, the byte 0xFF
is pushed to the working stack, otherwise the byte 0x00
is pushed to the working stack.
GTH
The value y
is popped from the working stack, and then the value x
is popped from the working stack. If x
is greater than y
, the byte 0xFF
is pushed to the working stack, otherwise the byte 0x00
is pushed to the working stack.
EQU
The value y
is popped from the working stack, and then the value x
is popped from the working stack. If x
is equal to y
, the byte 0xFF
is pushed to the working stack, otherwise the byte 0x00
is pushed to the working stack.
NQK
The value y
is popped from the working stack, then the value x
is popped from the working stack, then x
is pushed to the working stack, and then y
is pushed to the working stack. If x
is not equal to y
, the byte 0xFF
is pushed to the working stack, otherwise the byte 0x00
is pushed to the working stack.
SHL
The byte y
is popped from the working stack, then the value x
is popped from the working stack, then the value r
is calculated by shifting the bits of x
leftwards y
times, and then r
is pushed to the working stack. When the bits of a value are shifted leftwards, each bit of the value is set to the original state of the next lower bit, and the lowest bit is unset.
SHR
The byte y
is popped from the working stack, then the value x
is popped from the working stack, then the value r
is calculated by shifting the bits of x
rightwards y
times, and then r
is pushed to the working stack. When the bits of a value are shifted rightwards, each bit of the value is set to the original state of the next higher bit, and the highest bit is unset.
ROL
The byte y
is popped from the working stack, then the value x
is popped from the working stack, then the value r
is calculated by rotating the bits of x
leftwards y
times, and then r
is pushed to the working stack. When the bits of a value are rotated leftwards, each bit of the value is set to the original state of the next lower bit, and the lowest bit is set to the original state of the highest bit.
ROR
The byte y
is popped from the working stack, then the value x
is popped from the working stack, then the value r
is calculated by rotating the bits of the value x
rightwards y
times, and then r
is pushed to the working stack. When the bits of a value are rotated rightwards, each bit of the value is set to the original state of the next higher bit, and the highest bit is set to the original state of the lowest bit.
IOR
The value y
is popped from the working stack, then the value x
is popped from the working stack, then the value r
is calculated by setting each bit if at least one of the corresponding bits of x
and y
is set, and then r
is pushed to the working stack.
XOR
The value y
is popped from the working stack, then the value x
is popped from the working stack, then the value r
is calculated by setting each bit if exactly one of the corresponding bits of x
and y
is set, and then r
is pushed to the working stack.
AND
The value y
is popped from the working stack, then the value x
is popped from the working stack, then the value r
is calculated by setting each bit if both of the corresponding bits of x
and y
are set, and then r
is pushed to the working stack.
NOT
The value x
is popped from the working stack, then the value r
is calculated by setting each bit if the corresponding bit of x
is not set, and then r
is pushed to the working stack.