Specification (revision 3)


Core specification

This section specifies the full architecture and instruction set of the Bedrock computer system.

Program memory

The program memory is an array of 65536 bytes that holds the current program. To load a program, set every byte of program memory to zero, set the stack and instruction pointers to zero, reset every device on the device bus to their initial states, and then copy each byte of the program into program memory starting from address zero, truncating the program to fit the available memory.

To read a double from program memory, read the high byte from the given address and the low byte from the following address. To write a double to program memory, write the high byte to the given address and the low byte to the following address. Reading or writing a double from address 0xFFFF will cause undefined behaviour.

Systems with fewer than 65536 bytes of program memory are partially supported. Accessing an unimplemented memory address will cause undefined behaviour.

Stacks

A Bedrock system contains two stacks, called the working stack and the return stack. Each stack is an array of 256 bytes with an 8-bit pointer. To push a byte to a stack, write the byte to the address in the array referenced by the pointer and then increment the pointer. To pop a byte from a stack, decrement the pointer and then return the byte at the referenced address. Overflowing or underflowing the stack pointer will cause undefined behaviour.

To push a double to a stack, first push the high byte of the double and then push the low byte. To pop a double from a stack, pop two bytes; the first byte popped will be the low byte of the double and the second byte popped will be the high byte.

Systems with fewer than 256 bytes of memory per stack are partially supported. Accessing an unimplemented memory address will cause undefined behaviour.

Device bus

The device bus is an array of 256 ports, grouped evenly into 16 slots. Each port is a channel for sending or receiving bytes from an external device, and each slot is a contiguous group of 16 ports that can connect a single device to the system. Reading from a port will send a read request to the connected device, returning a byte. Writing a byte to a port will send a write request to the connected device, sending that byte. A device will see which of the 16 ports of the slot that a request was sent to, and the processor will pause indefinitely until the device completes the request. If a request is sent to a port that isn’t currently connected to a device, that request will complete immediately, returning a value of zero if it was a read request.

To read a double from the device bus, first read the high byte from the given port and then read the low byte from the following port. To write a double to the device bus, first write the high byte to the given port and then write the low byte to the following port. Reading or writing a double from port 0xFF will cause undefined behaviour.

The first 12 slots are reserved for the standard devices (see the devices specification). The remaining four slots are available for the private use of each implementation.

Processor

The processor executes the current program using a 16-bit instruction pointer. To execute the next instruction, read a byte from the address in program memory referenced by the instruction pointer, then increment the instruction pointer, and then execute the instruction represented by that byte. Overflowing the instruction pointer will cause undefined behaviour.

The upper three bits of an instruction byte are the mode flags.

  • If bit 0x80 is set, swap the working stack and the return stack for the duration of this instruction.
  • If bit 0x40 is set, values without an explicit size in the operation description will be doubles, otherwise bytes.
  • If bit 0x20 is set, the first value to be popped by this instruction will instead be read from the address in program memory referenced by the instruction pointer, incrementing the instruction pointer with each byte read.

The lower five bits of an instruction byte represent the operation to perform, as per the following list. WST means “working stack”, RST means “return stack”, IP means “instruction pointer”. Arithmetic wraps on overflow and underflow. Pushing and popping is to the working stack by default.

  • 0x00 Halt the system if no mode flag is set, otherwise do nothing.
  • 0x01 Pop x from RST, push x to WST.
  • 0x02 Pop x from WST.
  • 0x03 Pop x from RST, push x to RST, push x to WST.
  • 0x04 Pop x, push x, x.
  • 0x05 Pop y, x, push x, y, x.
  • 0x06 Pop y, x, push y, x.
  • 0x07 Pop z, y, x, push y, z, x.
  • 0x08 Pop double a, write a to IP.
  • 0x09 Pop double a, push IP to RST, write a to IP.
  • 0x0A Pop double a, pop t. If t is not zero, write a to IP.
  • 0x0B Pop double a, pop t. If t is not zero, push IP to RST, write a to IP.
  • 0x0C Pop double a, read v from memory address a, push v.
  • 0x0D Pop double a, pop v, write v to memory address a.
  • 0x0E Pop byte p, read v from device port p, push v.
  • 0x0F Pop byte p, pop v, write v to device port p.
  • 0x10 Pop y, x, push y plus x.
  • 0x11 Pop y, x, push y minus x.
  • 0x12 Pop x, push x plus 1.
  • 0x13 Pop x, push x minus 1.
  • 0x14 Pop y, x. If x is less than y, push byte 0xFF, else 0x00.
  • 0x15 Pop y, x. If x is greater than y, push byte 0xFF, else 0x00.
  • 0x16 Pop y, x. If x is equal to y, push byte 0xFF, else 0x00.
  • 0x17 Pop y, x, push x, y. If x is not equal to y, push byte 0xFF, else 0x00.
  • 0x18 Pop byte y, pop x, push x bit-shifted left by y bits.
  • 0x19 Pop byte y, pop x, push x bit-shifted right by y bits.
  • 0x1A Pop byte y, pop x, push x bit-rotated left by y bits.
  • 0x1B Pop byte y, pop x, push x bit-rotated right by y bits.
  • 0x1C Pop y, x, push x bitwise-OR y.
  • 0x1D Pop y, x, push x bitwise-XOR y.
  • 0x1E Pop y, x, push x bitwise-AND y.
  • 0x1F Pop x, push bitwise-NOT x.

Assembler specification

This section specifies an assembler program that can convert a human-readable source file into a program that will run on the Bedrock computer system.

Source file

A source file is a sequence of characters, where each character is a Unicode scalar value. To assemble a program, parse the source file as a sequence of tokens, convert each token into a sequence of bytes, and concatenate the sequences of bytes to create the assembled program. If the source file is invalid according to the rules of the assembler, no program will be created.

Tokens

A token is a sequence of characters from the source file that represents a single language element. The address of a token is equal to the number of bytes assembled before it in the program.

To parse the source file as a sequence of tokens, iterate over the characters of the source file and collect them into tokens according to the following rules:

  • The characters U+0000 to U+0020 are ignored if no token is in progress.
  • The characters ', ", or ( start a span token. Collect all characters up to and including the matching terminator, ending the token. The matching terminators for ', ", and ( are ', ", and ), respectively. The source file is invalid if a matching terminator cannot be found.
  • All other characters start a word token. If the character is ), [, ], {, }, ;, or :, no further characters are collected, ending the token. Otherwise, collect all characters up to and including the next :, or up to and excluding the next (, ), [, ], {, }, ;, or a character in the range U+0000 to U+0020, or until the end of the source file, whichever comes first.

Language elements

The first character of a token determines how it will assemble into a sequence of bytes, as follows:

  • (, ), [, and ] denote a comment, which assembles to nothing. The source file is invalid if it contains an unmatched
  • { and } denote an opening or closing block delimiter, respectively. A closing block delimiter matches the closest previous unmatched opening block delimiter, forming a pair. The opening delimiter assembles to a double with value equal to the address of the closing delimiter. The closing delimiter assembles to nothing. The source file is invalid if it contains an unmatched block delimiter, or if a delimiter inside a macro definition is matched with a delimiter outside that definition, or if the address of a closing delimiter is greater than 0xFFFF.
  • @ and & denote a global or local label definition, respectively. The remaining characters of the token are called the identifier. The name of a global label definition is given by the identifier. The name of a local label definition is given by the name of the most recent global label definition (or a blank string if none), followed by a / character, followed by the identifier. A label definition assembles to nothing. The source file is invalid if a label definition shares a name with another label or macro definition, or if the name of a label definition is longer than 63 characters, or if the address of a label definition is greater than 0xFFFF. The maximum number of label definitions allowed in a program is implementation defined.
  • % and ; denote a macro definition or terminator, respectively. A macro definition token matches the next terminator token, and the sequence of tokens between the two is called the macro body. The name of the macro definition is given by the remaining characters of the macro definition token. The macro definition, macro body, and terminator each assemble to nothing. The source file is invalid if it contains an unmatched macro definition or terminator token, or if a macro body contains a label or macro definition token, or if a macro definition shares a name with another label or macro definition, or if the name of a macro definition is longer than 63 characters. The maximum number of macro definitions allowed in a program is implementation defined.
  • ' and " denote a raw or terminated string, respectively. The remaining characters of the token, excluding the final character, are called the string content. A raw string assembles to the string content as a UTF-8 encoded byte sequence. A terminated string assembles to the string content as a UTF-8 encoded byte sequence, followed by a zero byte.
  • # denotes a padding token. The remaining characters of the token are called the pad value, and represent an integer value as a hexadecimal number. A padding token assembles to a sequence of zero bytes with length equal to this value. The source file is invalid if the pad value is not exactly two or four characters long, or if the pad value contains characters that don’t fall within the range 0 to 9, a to f, or A to F.

If the first character of the token is any other character, the token will either be a literal or a symbol:

  • A literal is exactly two or four characters long, where each character falls within the range 0 to 9, a to f, or A to F. The characters of the token represent an integer value as a hexadecimal number. A two or four character literal token assembles to a byte or a double, respectively, with value equal to this value.
  • Any other token is a symbol. The name of the symbol is given by the characters of the token. If the first character of the name is ~, replace this character with the name of the most recent global label (or a blank string if none), followed by a / character. If the name of the symbol matches the name of a previous or future label definition token, it assembles to a double with value equal to the address of that label definition token. If the name of the symbol is equal to the name of a previous macro definition token, replace this token with a copy of the tokens in the associated macro body and assemble. The name of a symbol inside a macro body is determined when the macro body is first collected. The source file is invalid if the name of a symbol is longer than 63 characters, or if the name is not equal to the name of a label or macro definition token, or if the name is equal to the name of a macro definition token that comes after the symbol token in the source file.

Pre-defined macros

The assembler will prepend the following block of code to every source file before assembly:

%HLT 00;  %NOP  20;  %DB1  40;  %DB2   60;  %DB3  80;  %DB4   A0;  %DB5   C0;  %DB6    E0;
%PSH 01;  %PSH: 21;  %PSH* 41;  %PSH*: 61;  %PSHr 81;  %PSHr: A1;  %PSHr* C1;  %PSHr*: E1;
             %: 21;                %*: 61;                %r: A1;                 %r*: E1;
%POP 02;  %POP: 22;  %POP* 42;  %POP*: 62;  %POPr 82;  %POPr: A2;  %POPr* C2;  %POPr*: E2;
%CPY 03;  %CPY: 23;  %CPY* 43;  %CPY*: 63;  %CPYr 83;  %CPYr: A3;  %CPYr* C3;  %CPYr*: E3;
%DUP 04;  %DUP: 24;  %DUP* 44;  %DUP*: 64;  %DUPr 84;  %DUPr: A4;  %DUPr* C4;  %DUPr*: E4;
%OVR 05;  %OVR: 25;  %OVR* 45;  %OVR*: 65;  %OVRr 85;  %OVRr: A5;  %OVRr* C5;  %OVRr*: E5;
%SWP 06;  %SWP: 26;  %SWP* 46;  %SWP*: 66;  %SWPr 86;  %SWPr: A6;  %SWPr* C6;  %SWPr*: E6;
%ROT 07;  %ROT: 27;  %ROT* 47;  %ROT*: 67;  %ROTr 87;  %ROTr: A7;  %ROTr* C7;  %ROTr*: E7;
%JMP 08;  %JMP: 28;  %JMP* 48;  %JMP*: 68;  %JMPr 88;  %JMPr: A8;  %JMPr* C8;  %JMPr*: E8;
%JMS 09;  %JMS: 29;  %JMS* 49;  %JMS*: 69;  %JMSr 89;  %JMSr: A9;  %JMSr* C9;  %JMSr*: E9;
%JCN 0A;  %JCN: 2A;  %JCN* 4A;  %JCN*: 6A;  %JCNr 8A;  %JCNr: AA;  %JCNr* CA;  %JCNr*: EA;
%JCS 0B;  %JCS: 2B;  %JCS* 4B;  %JCS*: 6B;  %JCSr 8B;  %JCSr: AB;  %JCSr* CB;  %JCSr*: EB;
%LDA 0C;  %LDA: 2C;  %LDA* 4C;  %LDA*: 6C;  %LDAr 8C;  %LDAr: AC;  %LDAr* CC;  %LDAr*: EC;
%STA 0D;  %STA: 2D;  %STA* 4D;  %STA*: 6D;  %STAr 8D;  %STAr: AD;  %STAr* CD;  %STAr*: ED;
%LDD 0E;  %LDD: 2E;  %LDD* 4E;  %LDD*: 6E;  %LDDr 8E;  %LDDr: AE;  %LDDr* CE;  %LDDr*: EE;
%STD 0F;  %STD: 2F;  %STD* 4F;  %STD*: 6F;  %STDr 8F;  %STDr: AF;  %STDr* CF;  %STDr*: EF;
%ADD 10;  %ADD: 30;  %ADD* 50;  %ADD*: 70;  %ADDr 90;  %ADDr: B0;  %ADDr* D0;  %ADDr*: F0;
%SUB 11;  %SUB: 31;  %SUB* 51;  %SUB*: 71;  %SUBr 91;  %SUBr: B1;  %SUBr* D1;  %SUBr*: F1;
%INC 12;  %INC: 32;  %INC* 52;  %INC*: 72;  %INCr 92;  %INCr: B2;  %INCr* D2;  %INCr*: F2;
%DEC 13;  %DEC: 33;  %DEC* 53;  %DEC*: 73;  %DECr 93;  %DECr: B3;  %DECr* D3;  %DECr*: F3;
%LTH 14;  %LTH: 34;  %LTH* 54;  %LTH*: 74;  %LTHr 94;  %LTHr: B4;  %LTHr* D4;  %LTHr*: F4;
%GTH 15;  %GTH: 35;  %GTH* 55;  %GTH*: 75;  %GTHr 95;  %GTHr: B5;  %GTHr* D5;  %GTHr*: F5;
%EQU 16;  %EQU: 36;  %EQU* 56;  %EQU*: 76;  %EQUr 96;  %EQUr: B6;  %EQUr* D6;  %EQUr*: F6;
%NQK 17;  %NQK: 37;  %NQK* 57;  %NQK*: 77;  %NQKr 97;  %NQKr: B7;  %NQKr* D7;  %NQKr*: F7;
%SHL 18;  %SHL: 38;  %SHL* 58;  %SHL*: 78;  %SHLr 98;  %SHLr: B8;  %SHLr* D8;  %SHLr*: F8;
%SHR 19;  %SHR: 39;  %SHR* 59;  %SHR*: 79;  %SHRr 99;  %SHRr: B9;  %SHRr* D9;  %SHRr*: F9;
%ROL 1A;  %ROL: 3A;  %ROL* 5A;  %ROL*: 7A;  %ROLr 9A;  %ROLr: BA;  %ROLr* DA;  %ROLr*: FA;
%ROR 1B;  %ROR: 3B;  %ROR* 5B;  %ROR*: 7B;  %RORr 9B;  %RORr: BB;  %RORr* DB;  %RORr*: FB;
%IOR 1C;  %IOR: 3C;  %IOR* 5C;  %IOR*: 7C;  %IORr 9C;  %IORr: BC;  %IORr* DC;  %IORr*: FC;
%XOR 1D;  %XOR: 3D;  %XOR* 5D;  %XOR*: 7D;  %XORr 9D;  %XORr: BD;  %XORr* DD;  %XORr*: FD;
%AND 1E;  %AND: 3E;  %AND* 5E;  %AND*: 7E;  %ANDr 9E;  %ANDr: BE;  %ANDr* DE;  %ANDr*: FE;
%NOT 1F;  %NOT: 3F;  %NOT* 5F;  %NOT*: 7F;  %NOTr 9F;  %NOTr: BF;  %NOTr* DF;  %NOTr*: FF;

Program metadata specification

This section specifies a format for embedding machine-readable metadata into a Bedrock program.

The presence of the 10-byte marker sequence E8 00 18 42 45 44 52 4F 43 4B at the start of a Bedrock program indicates that the following 14 bytes of the program are a table of pointers to metadata values. Each pointer in the table is a double that holds the address of a specific metadata element. If a pointer has a value of zero, the corresponding metadata element has not been provided.

Addr. Bytes Name Description
0x0000 10 Marker Indicates that metadata is present.
0x000A 2 Program identifier Pointer to the program name and version
0x000C 2 Author list Pointer to a list of author names.
0x000E 2 Description Pointer to a description of the program.
0x0010 2 Background colour Pointer to the icon background colour.
0x0012 2 Foreground colour Pointer to the icon foreground colour.
0x0014 2 Small icon Pointer to a 24x24 pixel monochrome icon.
0x0016 2 Large icon Pointer to a 64x64 pixel monochrome icon.

Metadata elements

  • The program identifier is a UTF-8 encoded string followed by a zero byte. The string is the program name, followed by the / character, followed by the program version. The program name and version cannot contain a / character or any character in the range U+0000 to U+001F. The maximum length of the program identifier is 255 bytes, excluding the zero byte. The address of the program identifier is given by the pointer at address 0x000A.
  • The author list is a UTF-8 encoded string followed by a zero byte. The string is a concatenated list of the names of the program authors, with every name but the last followed by a newline character U+000A. The name of each author cannot contain any character in the range U+0000 to U+001F. The maximum length of each name is 255 bytes, excluding the newline character and zero byte, and the maximum number of names in the list is 16. The address of the author list is given by the pointer at address 0x000C.
  • The description is a UTF-8 encoded string followed by a zero byte. The string is a short description of the program, summarising category and purpose. The description cannot contain any character in the range U+0000 to U+001F. The maximum length of the description is 255 bytes, excluding the zero byte. The address of the description is given by the pointer at address 0x000E.
  • The background colour is a double that represents the 12-bit RGB colour to use when drawing the background of the program icons. The address of the background colour is given by the pointer at address 0x0010.
  • The foreground colour is a double that represents the 12-bit RGB colour to use when drawing the foreground of the program icons. The address of the foreground colour is given by the pointer at address 0x0012.
  • The small icon is a sequence of nine 1-bit sprites that represents a 24x24 pixel program icon. The sprites are ordered left-to-right and then top-to-bottom, forming a 3x3 grid. The total length of the sprite data is 72 bytes. The address of the small icon is given by the pointer at address 0x0014.
  • The large icon is a sequence of 64 1-bit sprites that represents a 64x64 pixel program icon. The sprites are ordered left-to-right and then top-to-bottom, forming an 8x8 grid. The total length of the sprite data is 512 bytes. The address of the large icon is given by the pointer at address 0x0016.

The following data types are used in some elements:

  • A 12-bit RGB colour is stored as a double. The highest group of four bits in the double is ignored, and the remaining groups of four bits in the double represent the intensities of the red, green, and blue colour channels, respectively.
  • A 1-bit sprite is an 8x8 pixel image stored as an 8-byte sequence. Each byte represents a row of the sprite, ordered top to bottom, and each bit of a byte represents a pixel in the row, ordered left to right, from highest bit to lowest. If a bit is set, the corresponding pixel of the sprite will be drawn as the foreground colour, else as the background colour.

Devices specification

System device

This section specifies the standard system device of the Bedrock computer system. This device connects to slot 0x0 of the device bus.

Overview

This device provides information about the Bedrock system, and allows a program to sleep, fork, or reset the system.

A text buffer is an array of bytes with an associated pointer. The array contains a UTF-8 encoded string followed by a zero byte. The initial value of the pointer is zero, and the maximum value of the pointer is at least one greater than the address of the zero byte. Reading a byte from the text buffer will return the byte at the array address referenced by the pointer, and then the pointer will increment. Reading a byte from the text buffer when the value of the pointer is greater than the address of the zero byte will cause undefined behaviour. Restarting the text buffer will set the value of the pointer to zero.

This device contains six text buffers: a system identifier text buffer, a system authors text buffer, and a text buffer for each of the custom device slots 0xC to 0xF.

The system identifier identifies this Bedrock system implementation. The string stored in the buffer is the implementation name, followed by the / character, followed by the implementation version. The implementation name and version cannot contain a / character or any character in the range U+0000 to U+001F. The maximum length of the system identifier is 255 bytes, excluding the zero byte.

Ports

  • 0x000x01
    Writing to this port group will request that the system be put to sleep. Write access is atomic.
  • 0x02
    Reading from this port will return the slot number of the device that most recently woke the system from sleep. If the system has not yet been woken from sleep, the value returned will be 0x00.
  • 0x03
    Writing a non-zero value to this port will create a new Bedrock system instance, with separate core and devices to the current instance, and then the program memory of the current instance will be copied into the new instance. If a new instance cannot be created, the current instance will be reset as if a zero value had been written to this port. Writing a zero value to this port will reset this Bedrock system instance, by setting the instruction pointer and stack pointers to zero, leaving the program memory unmodified, and resetting all devices to their initial states.
  • 0x04
    This port is associated with the slot 0xC text buffer. Reading from this port will read and return a byte from the associated text buffer. Writing any value to this port will restart the associated text buffer.
  • 0x05
    This port is associated with the slot 0xD text buffer. It behaves the same as port 0x04.
  • 0x06
    This port is associated with the slot 0xE text buffer. It behaves the same as port 0x04.
  • 0x07
    This port is associated with the slot 0xF text buffer. It behaves the same as port 0x04.
  • 0x08
  • 0x09
  • 0x0A0x0B
    Reading from this port group will return the number of bytes of program memory implemented on this system. If the number of bytes is 65536, the value returned will be 0x0000.
  • 0x0C
    This port is associated with the working stack of the Bedrock system. Reading from this port will return the number of bytes of memory implemented for the associated stack on this system. If the number of bytes of memory implemented is 256, the value returned will be 0x00.
  • 0x0D
    This port is associated with the return stack of the Bedrock system. It behaves the same as port 0x0C.
  • 0x0E0x0F
    Reading from this port group returns a device list, where each bit in the list is set only if a device is connected to the corresponding slot on the device bus.

Memory device

This section specifies the standard memory device of the Bedrock computer system. This device connects to slot 0x1 of the device bus.

Overview

This device provides access to up to 16 megabytes of additional memory. The memory managed by this device does not share an address space with the program memory.

Memory is allocated in 256-byte chunks, called pages, which form a contiguous array of bytes starting from address zero. Pages are allocated and deallocated from the end of this array. Accessing an unallocated memory address will cause undefined behaviour. When a page is allocated, the value of each byte on that page is zero. The initial number of pages allocated is zero, and the maximum number of pages that can be allocated is implementation defined.

Memory is accessed via two independent read-write heads (called head 1 and head 2). Each head contains an unsigned 16-bit page offset value and an unsigned 16-bit address offset value. The memory address that will be accessed by a given head is calculated by multiplying the page offset by 256 and then adding the address offset, causing undefined behaviour if the result exceeds the range of an unsigned 24-bit integer.

Ports

  • 0x100x11
    Reading from this port group will return the number of pages allocated. Writing to this port group will request that the number of pages allocated be changed to the value written. Pages will be sequentially allocated or deallocated until either the requested value or an implementation defined maximum value is reached. Write access is atomic.
  • 0x120x13
    This port group is associated with head 1. Reading from this port group will return the page offset of the associated head. Writing to this port group will set the page offset of the associated head to the value written.
  • 0x140x15
    This port group is associated with head 1. Reading from this port group will return the address offset of the associated head. Writing to this port group will set the address offset of the associated head to the value written.
  • 0x16
    This port is associated with head 1. Reading from this port will return the byte stored at the memory address referenced by the associated head, and then will increment the address offset of the associated head by 1, wrapping to zero on overflow.
  • 0x17
    This port is an alias for port 0x16.
  • 0x180x19
    Writing to this port group will request that a number of pages be copied equal to the value written. The initial source and destination page are given by the page offset value of head 2 and head 1, respectively. The contents of the source page will be copied into the destination page, the page following the source or destination page will become the new source or destination page, respectively, and the process will repeat until the requested number of pages has been copied. Copying to or from an unallocated page will cause undefined behaviour. Write access is atomic.
  • 0x1A0x1B
    This port group is associated with head 2. It behaves the same as port group 0x120x13.
  • 0x1C0x1D
    This port group is associated with head 2. It behaves the same as port group 0x140x15.
  • 0x1E
    This port is associated with head 2. It behaves the same as port 0x16.
  • 0x1F
    This port is an alias for port 0x1E.

Math device

Clock device

Input device

Screen device

Tone device

Waveform device

Stream device

File device

Clipboard device

Registry device