-
A "showstopper" problem
05/31/2023 at 20:08 • 0 commentsMy first program was to test some concepts (File: SerialTest.txt). Can I read a source program and output binary? Actually, for ease of debugging, the "object code" from 1st pass will be readable hex.
This failed. A bug in the simulated computer of the badge, means that if there are characters queued in the FIFO input buffer, and you write one character, then subsequent reads return that written character. It works if the FIFO is empty. That killed the idea of simply spooling the source file to the computer from a host laptop and capturing the output (ie full duplex, no local echo, in a terminal program).
There was also a 2nd bug in the Serial implementation - it could not use the same pins as the loader does, Setting bit 0 WrFlag didnt have an effect, meaning the RX pin switches position between program and loading.
Possible workarounds for the duplex
- Use some hardware flow control. It may need a non-standard USB/Serial adaptor. [Not tested, yet. It may not be "fast enough" to stop characters - USB/Serial tend to batch data]
- The external device (a host laptop, typically) only sends one line, then waits for a response.
- Don't use the Serial UART for output but implement a bit-bang serial on one of the 4 output lines.
Initial code will use (2)
-
Architectual Limitations
05/31/2023 at 19:39 • 0 commentsThese are the design thoughts at the beginning,. Is this at all possible?
The computer is Harvard Architecture. That means the assembler can not generate the machinecode to program memory directly. The solution is the output is sent via the Serial to an external device where it is stored, so it can be loaded with hardware loader from Serial.
The computer is also severly RAM limited - 191 nibbles are available (a bit more if we avoid some instructions). Thus the source text has to be stored externally, read in via the Serial, parsed and generate the machine code, character by character, as there is no room to buffer anything.
Labels for jumps need a symbol table. 12bit program location means 3 nibbles. If a label is two characters long 9 nibbles are needed per label meaning we have room for about 20 labels using all RAM. Syntax of labels is therefore limited to one the 26 letters of the alphabet. Thus we do not store the labelname/character, but use a fixed size table and labelnames are inferred by position.
Secondly, backward references can be fully resolved in the 1st pass. These can be redefined at the programmers responsibility. Single characters here, too, and for clarity the "0"-"9" are used. This is necessary as 26 labels in total is too few; this way the global symbols are only used for "major" labels, and when a forward jump is unavoidable.
Thus the symbol table only needs 106 nibbles, leaving us room for other stuff. I am undecided on symbolic names for data locations. Restricting them to one character [A-J], only using 2 nibbles for an 8 bit address uses 20 nibbles, leaving 65 nibblesfor buffers or something - or longer symbol tables.
Any decent assembler needs to handle forward references, typically done as a two pass assembler. (The other way is backward patching, but the binary loader does not support that) The first pass collects the global symbols in memory and generates "object"-code (to the Serial), with placeholder for unresolved symbols;. The second pass reads this and generates the final machine code joining it with in-memory symbol table and thus generates the final binary format suitable for the loader. One might call the 2nd pass a Linker.
Coding mantra: Waste program memory (bulky code) to save on RAM. Use backwards jumps so we use fewer global symbols, and save on stack (it is only 5 deep). It ain't gonna be pretty, sorry.
Michael Möller