Architecture specification

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.