The dump was an address and a name. Both of the questions it gets asked were only half answered. "What is at this address" was ambiguous, because Program and Data are separate memories and an address alone does not say which one. That is easy to miss in a loadable program, where the segments are usually based far apart - and immediate in a boot image, where both start at zero: replCalculator has a Program 0003 and a Data 0003 and the old file printed both as "0003 <name>". "Where is this defined" was not answered at all, and it is the one that matters more as a program grows. A name defined once and called in forty places is hard to find by searching. Lander's table names five files besides its own; CosmOS and its libraries define over a thousand names across a dozen. So: memory, address, name, file, line, separated by tabs, sorted by memory and then address with Program first. Tabs because that makes it a table cut, awk and sort already read, and no heading line because nothing should have to know to skip one. Everything needed was already being passed to addLabel and thrown away; the file name points at the copy the include list owns, which outlives the label table. The manual describes the five fields, and docs.sh now settles that description against a real dump - the shape, not the values, so that an example cannot go stale and turn editing a program into editing a manual. Verified with break.sh three ways: a reordered field, a dropped field, and a field renamed in the manual. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
77 lines
3.1 KiB
C
77 lines
3.1 KiB
C
// secondPass.c
|
|
// Functions for the 'second pass' of the SplitBit Assembler.
|
|
// Written by Anachronaut
|
|
// 10/25/2024
|
|
|
|
#ifndef SECONDPASS_H
|
|
#define SECONDPASS_H
|
|
|
|
|
|
#include <stdint.h>
|
|
#include <ctype.h>
|
|
#include "Assm-util.h"
|
|
|
|
// Every label in a program and in everything it includes shares one table, because they
|
|
// share one namespace: a name may only be defined once across the whole assembly. So this
|
|
// is not the size of one file but the size of a program and its libraries together, and
|
|
// CosmOS with its four libraries went past 256 while still being a small system.
|
|
//
|
|
// AND PAST 1024 the day the shell learned to finish a word somebody had started. Doubled
|
|
// rather than nudged: a ceiling reached once is a ceiling that will be reached again, and
|
|
// the table is pointers into source that is already in memory - 2048 of them is sixteen
|
|
// kilobytes on a host with gigabytes of it.
|
|
#define MAX_LABELS 2048
|
|
#define MAX_VECTORS 256
|
|
|
|
typedef struct {
|
|
char* label;
|
|
uint16_t address;
|
|
int type;
|
|
// Where the name was written. Only the symbol file reads these, and it is the source
|
|
// of the whole feature: an address tells you a routine exists and a file and line tell
|
|
// you where to go and read it.
|
|
//
|
|
// NEITHER IS OWNED HERE. fileName points at the copy the include list keeps, the same
|
|
// storage every intermediateElement points at, and the include list outlives the label
|
|
// table - assemblerCleanup frees the labels first. So freeLabelList must not free it.
|
|
const char* fileName;
|
|
int lineNumber;
|
|
} Label;
|
|
|
|
// Every label and the address it was given, in address order, so that a tally of
|
|
// addresses can be turned back into a list of routine names.
|
|
void writeSymbolFile(const char *path);
|
|
|
|
// One line of the Vector Segment, once it has been worked out.
|
|
typedef struct {
|
|
char* name; // What it was called, or NULL for a device, which is named by its port.
|
|
uint8_t index; // Which vector in its table.
|
|
uint16_t base; // Which table: software or hardware.
|
|
uint16_t handler; // Where the handler ended up.
|
|
int declaredOnly; // Named and numbered, with nobody implementing it here.
|
|
} VectorEntry;
|
|
|
|
void freeLabelList();
|
|
|
|
void freeVectorList();
|
|
|
|
// Reads the Vector Segment: allocates a number to every named vector, works out which
|
|
// vector each device line means, and resolves the handlers. Runs after the labels are
|
|
// known, because a handler is named by its label.
|
|
void populateVectorTable(intermediateElement *intermediateArray, int arraySize);
|
|
|
|
// Turns each vector name used as an operand of SWI into the number it was given.
|
|
void fillInVectorReferences(intermediateElement *intermediateArray, int arraySize);
|
|
|
|
int vectorCount();
|
|
|
|
void populateLabelTable(intermediateElement *intermediateArray, int arraySize);
|
|
|
|
void fillInLabelAddresses(intermediateElement *intermediateArray, int arraySize);
|
|
|
|
void populateOutputBuffers(intermediateElement *intermediateArray, int arraySize, uint8_t *Program, int *programCount, uint8_t *Data, int *dataCount);
|
|
|
|
void writeOutputFile(const char *outputFileName, uint8_t *Program, int programCount, uint8_t *Data, int dataCount);
|
|
|
|
#endif
|