gigatron/rom/Docs/GT1-files.txt
2025-01-28 19:17:01 +03:00

246 lines
11 KiB
Plaintext

I want to set a standard for precompiled vCPU programs. There are
many emulators getting born, and BabelFish.ino will need to adopt
something as well. Both users of real Gigatrons and users of
emulators benefit from a single standard.
Format
======
The format is based on the same byte sequence that the ROM loader
routine SYS_Exec_88 sees in ROM v1. That is essentially the in-ROM
sequence of operand bytes, but skipping the trampolines in the top
5 words of every ROM page. Those instructions are not part of the
program but part of the mechanism for accessing the data. Two extra
bytes are added for a start address.
vCPU programs are fragmented in RAM because the RAM organisation
is fragmented. Both Snake and Racer protrude into the unused bytes
of the video area. In essence, the file format is a list of n>0
segments of 1 to 256 bytes each. A segment isn't allowed to cross
a page boundary.
Gt1File :=
n * ( <hiAddress>
+ <loAddress>
+ <segmentSize & 255>
+ segmentSize * <dataByte> )
+ <0>
+ <hiStart>
+ <loStart>
EOF
<..> represents a single byte. The rest is meta. Hope the idea is
clear. To be specific: The number "n" itself will not appear at the
start of the file.
The first two bytes are the RAM address of the first byte to be
loaded, in big-endian order. (I'm a little-endian guy, but this
order is useful in the ROM-to-RAM loader). Segments may be in any
order in the file, with one exception described below. Segments
are loaded sequentially. In case of overlap, the later one overwrites
the earlier one. (We can make movies with that!) The end of the
segment list is implied by the terminating zero-byte as high address.
This format implies that there's a translation to do in BabelFish.ino
when loading such files into a Gigatron, but I think that is ok:
the limitations on the Loader packets (60 bytes) are influenced by
what the video signal looks like. I suggest we keep that kind of
entanglement out of the file format.
Starting address
================
Additionally, 2 bytes are always appended that indicate the start
address for execution. The bytes must be present and zero ($0000)
means "no execution". Although the Gigatron ROM is little-endian
where possible, all addresses in this format are stored as big-endian.
The original in-ROM format doesn't have these bytes. This is an
extension of the layout as used in ROM v1.
Zero-page data
==============
Segments may be in any order in the file, except when data has to
go into the zero-page: if you have that, it MUST be the first in
the sequence. So there can be no more than one of those segments.
This is an extension of the layout as used in ROM v1, which can't
load data into the zero-page. But it's useful for initializing
variables.
Also there's no encoding for a zero-length program. Use an zero-length
file for that use case, for all I care... or encode a known constant,
such as [0]=0 as follows: 00 00 01 00 00 00
Avoid loading data in the system variables area and in the top of
the zero page, where the stack lives. Although it might seem to
work with Loader and in emulators, the ROM loader (SYS_Exec_88)
uses the stack to setup its inner loop. As a rule of thumb,
[$30..$bf] are free to load data into, with the note that [$80]
must always be 1. After loading, the top area is free to use in any
way, as long as the program's own stack usage permits.
Extension
=========
File name extension: .gt1 (For Gigatron-1)
I feel that .vcpu is the lesser choice because it's a generic
abbreviation for virtual CPU. For example, the MyCPU uses the same
abbreviation for something similar. And ... retro-extensions should
be 3 characters of course...
ROM bindings
============
GT1 files contain vCPU instructions and rely on things that are
defined by the ROM on the target platform. The ROM bindings (ABI)
for ROM v1 are listed in interface.json. If your program depends
on some extension of this, then it's nice not to crash other people's
machines. ROMs with different bindings should use a different romType
value in address $0021. GT1 programs can then check this value and
not continue when seeing an unexpected value.
The romType is in RAM, so it's always possible for the user to
modify its value prior before loading an unwilling GT1 file.
BUT: If your program requires a custom ROM, then GT1 is probably not
the right format. Just publish it as a ROM dump instead.
On using romType
================
ROM v1 0x1c Initial Gigatron kit release
ROM v2 0x20 ledState_v2 (write-only, settable to 0 for run and 1 for stop)
SYS_SetMemory_v2_54
SYS_SetMode_v2_80
ROM v3 0x28 SYS_SendSerial1_v3_80
SYS_Sprite6*_v3_64
Purpose of romType variable
---------------------------
The intent is that GT1 applications can detect the presence of ROM
features by inspecting only this memory location. There are two
types of ROM features: those listed in interface.json, and those
not listed there. Features might still be described elsewhere, but
just by being documented doesn't make a feature part of the intended,
stable, interface. The goal of interface.json is to provide a STABLE
interface between GT1 files and ROM versions that can load such
files. We only add new things to interface.json, never(*) remove,
rename or change bindings once they are defined. This should help
with future compatibility of GT1 files. Things that are not in
interface.json can, and will, disappear or change at any time and
should not be relied upon. (For example: some zero-page variables
used by the video loop, certain SYS functions, specific entry points
in code pages, location of application ROM images, built-in pictures,
the shape of the font, values in ROM addresses etc...).
Value increases with ROM releases
---------------------------------
romType is an unsigned 8-bit value that strictly goes up with each
"official" ROM release, no matter how small the delta. So if you
want to test for the presence of a feature, compare the magnitude
of this value to that of the first ROM that introduced it. For
historic reasons, the first romType value was 0x1c for ROMv1. Don't
rely on the meaning of individual bits. Also, the value can go up
by an arbitrary number with each release, so there no way to
"calculate" the displayed version name as will be known by the user.
With this it's not possible that two "official" ROM releases have
the same romType value. However, third-party ROMs with compatible
GT1 loading capability are advised to use the same romType value
as the ROM version they were derived from.
End user is in control
----------------------
The romType value is in RAM, not ROM! This is on purpose, so that
the end user always has an escape hatch and can put a value in there
that a troublesome GT1 file expects to see. The user has the final
say on what happens on the system, not the file... So please don't
try to second-guess the romType value as a programmer.
Don't crash
-----------
GT1 files that need a certain interface feature that is not present
in earlier ROMs should degrade in performance or functionality, or
fail gracefully, but definitely NOT crash.
Example:
$800 p= {Top of screen}
\romType, \romTypeValue_ROMv3- {Test for ROM v3}
[if<0 [do p. 1+ loop]] {Halt and blink pixel on older ROM}
See Apps/Smallest.gcl for a smaller busy loop, and much cooler too!
By publishing a file as GT1 you're signaling that you expect the
file can be loaded with any past, present or future ROM version.
To help remind the programmer which features were introduced after
ROM v1, and therefore require a romType check before use, we'll add
a ROM version number to their names.
More thoughts on the above
==========================
32K vs. 64K: "_64K.gt1"
-----------------------
It is understood that a program that really requires an extended
64K memory crashes while loading on a 32K system. In that case
please end the filename with "_64K.gt1". The main idea is still
that a user doesn't get unexpected surprises.
Hack files: ".gt1x"
-------------------
Of course the Gigatron is an open system, so there will be plenty
of methods to detect what kind of ROM you're running on. If you
make a GT1 file that you know depends on undocumented features (or
better phrased: features not listed in interface.json), it's advised
to give it a different extension. For example 'Demo3_ROMv1only.gt1x'
(I always append a 'x' to such experimental files with no expectation
of compatibility.)
Evolution and process
---------------------
We should be pragmatic about these rules. A list of simple numbers
won't ever fully describe the exact boundary of the INTENDED
interface: there will always be some assumptions that remain implicit,
such as the meaning of certain values or the exact sequence of
certain events. It is also possible that some feature wasn't declared
as a stable interface, but that there are good reasons to make it
so retro-actively. Please feel free to discuss in GitHub or in the
user forum. Basically you're then asking "I'm using this implicit
feature now and I would like it to keep working in future ROMs, can
we fixate it and document it?".
Incompatible ROM upgrades
-------------------------
If we ever decide to make a truly incompatible step in ROM versions,
one that breaks many GT1 files out there, we should start using a
different extension for object files for what then has effectively
become a new platform (GT2 comes to mind). This is conceivable if
we ever change something really big about the architecture, such
as the memory map or the vCPU instruction set. Of course we hope
we can get very far without ever doing something like this...
(*) Have said that, we retro-actively removed 'SYS_Reset_36',
'SYS_NextByteIn_32' and 'SYS_LoaderPayloadCopy_34' from the interface
files. Their inclusion turned out to be a bit premature and they
had to change after ROM v1. There will be no alternative for
SYS_ReadNextInput_32 and SYS_LoaderPayloadCopy_34 in interface.json
soon: it's part of Loader, but that is logically not a GT1 file.
'SYS_Reset_36' was just an intermediate in the soft reset sequence.
The proper way to initiate that is by redirecting the vCPU to vReset
(0x1f0). So we have retro-actively added vReset to the interface.json
files instead.
ROM v4: romType and channelMask
-------------------------------
As of ROM v4, the romType variable lends its lower three bits to
the new channelMask function. For version comparisons this doesn't
matter, and it's not needed to treat the value at $21 differently
due to this. For example, there's no need to mask these bits out
prior to your version check. Just be aware that (in)equality tests
may not work, because in newer ROMs these lower bits could be
different than expected. (But you shouldn't use == or != for this
purpose anyway.)
-- End of document --