Files
SplitBit-Emulator/Source/Assembler/assembly.h
T
AnachronautandClaude Opus 5 b6004bdcde Say "boot image" where that is what is meant
"Binary" was doing three jobs. It meant an SPBT file that the machine starts
from; it meant whatever the assembler happened to produce, which is now
either that or a loadable program; and it meant a compiled host tool. A word
that means three things means none of them, and the first of the three has a
name already - this project has been calling them boot images for a while
and the manuals had not caught up.

  Where it means an SPBT file       -> boot image
  Where it means either output      -> output
  Where it means a host executable  -> left alone
  Where it means base two           -> left alone

The user facing messages move with it:

  Error: No boot image specified.
  Usage: ./SplitBit [OPTIONS] <boot image>
  Error: This is not a SplitBit boot image.
  Error: This boot image is in format version 2, and this emulator reads 1.
  Successfully wrote SplitBit boot image to "hello.bin".

The assembler's own help was the interesting case. Its -o writes either
format, so "the binary" there was never right - it is "the output" now, and
the message that names the format is the one that says which it wrote.

No recorded output contained the word, so nothing needed re-blessing.
Checked before starting rather than after.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
2026-08-21 14:50:03 -04:00

130 lines
5.9 KiB
C

// assembly.h
// These are functions useful for translating assembly mnemonics to hex and vice-versa for the SplitBit CPU.
// Written by Anachronaut
// 10/18/2024
#include <stdint.h>
#ifndef ASSEMBLY_H
#define ASSEMBLY_H
// ---- The SplitBit boot image format ----
//
// A boot image starts with a file header, then the Program Segment, then the Data
// Segment. All multi byte numbers are stored most significant byte first.
//
// Offset Size Field
// 0 4 "SPBT", so a file that is not a boot image is spotted at once
// 4 1 Format version
// 5 4 Required feature flags
// 9 3 "PRG"
// 12 2 Program Segment length
// 14 N Program Segment
// .. 3 "DAT"
// .. 2 Data Segment length
// .. M Data Segment
// .. 3 "VEC", optional
// .. 2 Vector Segment length, in bytes
// .. K Vector Segment, four bytes per entry
//
// The Vector Segment is optional and comes last, so an image written before it existed
// simply ends after its Data Segment and still loads. Each entry is two bytes saying
// where in Program Memory the vector sits, then two bytes saying where its handler is,
// most significant byte first. It is a list rather than an image of the table, so a
// program with three handlers costs twelve bytes instead of a padded kilobyte.
//
// The feature flags are how a boot image says it needs something the base machine does
// not provide, so that an emulator which cannot provide it refuses to run the image
// rather than quietly doing the wrong thing. No features are defined yet; the field
// is here so that adding one later does not need another format version.
// ---- The vector table ----
//
// The top kilobyte of Program Memory is reserved for vectors. Both tools have to
// agree on where it begins: the CPU starts execution through it, and the assembler
// has to refuse a Program Segment that would run into it.
//
// Entries are two bytes each, most significant byte first, the same order the branch
// instructions and this file format already use.
//
// 0xFC00 Software vectors 0 to 255
// 0xFE00 Hardware vectors 0 to 255, one for each I/O port
//
// Software vectors 0 and 1 are start addresses rather than handlers. Vector 0 is
// where the machine begins at power on and vector 1 is a warm restart, so a zero in
// either of them is not "nothing installed" but the address 0x0000, which is where a
// program carrying no vector table of its own begins. A zero in any other entry does
// mean no handler is installed, and dispatching through one is a fault.
#define SOFTWARE_VECTOR_BASE 0xFC00
#define HARDWARE_VECTOR_BASE 0xFE00
#define VECTOR_ENTRY_BYTES 2
#define VECTOR_BOOT 0
#define VECTOR_SOFT_RESET 1
#define VECTOR_INVALID_OPCODE 2
// A device refused a write, because it landed inside a raised fence.
#define VECTOR_GUARD_VIOLATION 3
// A bank was named that has nothing registered in it, or an access ran past its end.
#define VECTOR_BANK_FAULT 4
// Vectors 5 to 15 are held back for faults that do not exist yet, so that each cause
// can have an entry of its own rather than sharing one and needing a cause register to
// tell them apart. Everything from 16 up belongs to programs, in two halves.
//
// PINNED, 16 to 63. Numbers in here are never handed out by the assembler; a program that
// wants one writes it down. This is where anything TWO SEPARATELY ASSEMBLED PROGRAMS have
// to agree about lives - the system's services, and any library that stays resident and is
// called through the vector table.
//
// AUTOMATIC, 64 up. Numbered by the assembler in the order they are written. These belong
// to one program and nothing outside it can name them, so what number they get does not
// matter as long as it is not somebody else's.
//
// THE SPLIT IS THE POINT. With one range for both, what number a program's own traps got
// depended on what it had included: adding a line that included the system's service names
// silently pushed every trap after it along by three. Worse, a program that did NOT include
// them was given 16, which is osPrintString, so installing its own handler would have
// replaced a system service by accident. Numbers that must agree are now written out loud,
// and numbers that need not agree are drawn from somewhere nobody else is looking.
#define VECTOR_FIRST_PINNED 16
#define VECTOR_FIRST_AUTO 64
// The first address the vector table occupies, and so the first address the Program
// Segment may not reach.
//
// The Segment rather than the code: instructions are most of what goes there, but not all
// of it. Literal bytes go there, a label named in the Program Segment puts its two byte
// address there, and #Align and #Reserve put runs of zeroes there. What the limit measures
// is how far all of that together has pushed the cursor.
#define PROGRAM_SEGMENT_LIMIT SOFTWARE_VECTOR_BASE
#define SPLITBIT_MAGIC "SPBT"
#define SPLITBIT_MAGIC_LENGTH 4
#define SPLITBIT_FORMAT_VERSION 1
#define SPLITBIT_FLAGS_LENGTH 4
#define SEGMENT_MARKER_LENGTH 3
#define SEGMENT_LENGTH_BYTES 2
// Where a vector sits, and where its handler is.
#define VECTOR_ENTRY_FILE_BYTES 4
// Everything the format costs a file, on top of the two segments themselves.
#define SPLITBIT_HEADER_BYTES (SPLITBIT_MAGIC_LENGTH + 1 + SPLITBIT_FLAGS_LENGTH \
+ 2 * (SEGMENT_MARKER_LENGTH + SEGMENT_LENGTH_BYTES))
// Features this build of the emulator can provide. An image asking for anything
// outside this set is refused.
#define SPLITBIT_FEATURES_SUPPORTED 0x00000000u
// Features the assembler currently needs to ask for. Nothing, so far.
#define SPLITBIT_FEATURES_REQUIRED 0x00000000u
const char* getMnemonic(uint8_t opcode);
uint8_t getOpcode(char* mnemonic);
// How many Data Pointer selector bytes follow the given opcode. Never more than two.
#define MAX_DATA_POINTER_OPERANDS 2
int dataPointerOperands(uint8_t opcode);
#endif // CPU_H