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 * ( + + + segmentSize * ) + <0> + + 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 --