Return to Menu
= DUO ULTIMATE DESCRIPTION =
The DUO Ultimate is a TTL 8-bit machine. The machine code allows for embedded commands, function calls, and variable scope. Its input is a keyboard. The output is a black and white monitor and a speaker. An infancy front panel is used to enter initial software into the machine.
Machine code and all long-term data are stored in 64 KB of main memory. Commands follow this format:
[opcode] [opcode suffix] [argument 1?] [argument 2?] [argument 3?]
The opcode is divided into the following sections:
[1 bit: is the command a function?]
[4 bits: destination of operation OR which operation to execute]
[1 bit: is the command yielding?]
[2 bits: how many arguments does the command accept?]
The opcode suffix is also subdivided into these sections:
[1 bit: is the command conditional?]
[3 bits: which arguments are to be called from RAM?]
[3 bits: which arguments are embedded commands?]
[1 bit: does the command modify the return value?]
RAM is divided into 128 global bytes and 16 scope levels of 128 local bytes. RAM addresses greater than 127 refer to global variables; those less than 128 refer to local variables.
When a function is called, several things occur:
- The RAM scope level becomes deeper
- The current main memory address is added to the return address stack
- The function's arguments are stored in addresses 0, 1, and 2 in RAM
- Execution skips to a predefined point in main memory
A function is terminated when a GVL command is called with one argument. This pops a return address back into the main memory address register and brings the scope level up.
About 7 KB VRAM is constantly dumped onto the black and white monitor. The resolution is 256 pixels by 240 pixels.
The composite video signal consists of 8 "blocks" of signals:
[odd lines] [6 pre-equalizing pulses] [5 long pulses] [5 post-equalizing pulses] [even lines] [5 pre-equalizing pulses] [5 long pulses] [4 post-equalizing pulses]
There are 263 odd lines and 262 even lines. Only 480 lines are visible; the first ~20 and last ~20 lines are blank. Each line contains the following signals:
[horizontal sync pulse] [back porch] [video data] [front porch]
Video data contains 256 pixels in the given line.
Video signal times are below.
- Horizontal sync: 4.7 microseconds @ 0 volts
- Back porch: 4.7 microseconds @ 0.3 volts
- Actual video data: 52.6 microseconds @ 0.3 to 1 volts
- Front porch: 1.65 microseconds @ 0.3 volts
- Total line scan time: 63.556 microseconds
- Equalizing pulses: 2 microseconds @ 0 V followed by 30 microseconds @ 0.3 volts
- Long sync pulses: 30 microseconds @ 0 V followed by 2 microseconds @ 0.3 volts
3 registers store musical notes to be generated: 2 square wave channels and 1 noise channel.
A 2 byte shift register accepts input from a PS/2 keyboard.
A 2 byte timer constantly counts up at a rate of 64 beats per second. The timer is used for creating regular program pacing and music tempo.
= MEMORY SPACES =
- 64 KB main memory: Stores all commands and long-term data.
- 2 byte main memory address: Stores the address of the byte being processed by the command parser.
- 16 * 2 bytes return address stack: Determines where execution should resume after a function terminates.
- 2 * 128 bytes function addresses: Stores the location of function bodies in main memory.
- 1 * 128 crumbs function argument amounts: Stores how many arguments are accepted in function calls.
- 1 nybble scope level: Determines which scope of local variables to access in RAM. Also determines where the next return address is in the stack.
- 16 * 128 bytes local variable RAM: Stores each scope level of local variables.
- 128 bytes global variable RAM: Shares the same chip as local variables.
- 16 * 1 bytes opcode stack: Stores each embedded command opcode of the commands being executed.
- 16 * 1 bytes opcode suffix stack
- 16 * 3 bytes argument stacks
- 16 * 1 crumbs arguments remaining stack: Determines where arguments should be loaded while counting down.
- 1 nybble embed level: Determines what level to access in the command stack.
- ~7 KB (256 * 240 bits) VRAM: Stores the pixels to be dumped onto the monitor.
- 2 bytes square wave note registers: Stores the square wave notes to be played.
- 1 bit noise register: Stores whether white noise is to be played.
- 2 bytes input shift register: Accepts input from a keyboard.
- 2 bytes timer: Constantly counts up at 64 beats per second.
- 1 bit boolean bit register: Used to conditionally inhibit a command.
= MACHINE COMMANDS =
The group of commands below are called unyielding commands. They cannot be embedded because they cannot return a value.
00000001:
SBB [boolean value] (set boolean bit)
00001011:
SMM [address] [address] [data] (set main memory)
00010010:
STM [number] [number] (set timer)
00011011:
SVM [address] [address] [data] (set VRAM)
00100010:
SNT [channel] [note] (set note)
00101010:
GO2 [address] [address] (goto)
00110010:
FN1 [name] [number of arguments] (function definition 1)
00111011:
FN2 [name] [address] [address] (function definition 2)
The following commands have two versions; one modifies RAM, one modifies the return value register. The later version does not accept a RAM destination. These commands are called yielding commands. When the last opcode suffix bit is a 0, the command writes data into RAM; if the last suffix bit is a 1, the command writes data into the return value.
00000110:
GVL [data] [destination] (get value) *
00001111:
GMM [address] [address] [destination] (get main memory)
00010110:
GTM [which byte] [destination] (get timer)
00011111:
GVM [address] [address] [destination] (get VRAM)
00100110:
GIN [which byte] [destination] (get input)
00101110:
RRM [address] (return RAM) **
00110111:
ADD [number] [number] [destination] (add)
00111111:
SUB [number] [number] [destination] (subtract)
01000111:
EQU [number] [number] [destination] (equal)
01001111:
UNE [number] [number] [destination] (unequal)
01010111:
GRE [number] [number] [destination] (greater than)
01011111:
BGT [byte] [which bit] [destination] (bit get)
01100111:
BON [byte] [which bit] [destination] (bit on)
01101111:
AND [byte] [byte] [destination] (and)
01110111:
EIT [byte] [byte] [destination] ("either", aka or)
01111110:
NOT [byte] [destination] (not)
* If the returning version of this command is called (the version with one argument), the current function is terminated. This command is affectionately called "Givle". :)
** This command is an oddball case. Although it technically IS a yielding command, it only has one working version: the version which modifies the return value register. This is because all ALU operations occur in 1 clock step, and it would be impossible to store data FROM RAM INTO RAM in a single step. This command is essential for array based RAM operations.
Return to Menu