; The CPU is a VLIW (very-long instruction-word) processor.
; It contains 9 "units", and each VLIW instruction specifies an
; operation for each of the 9 units independently and simultaneously.
; Most units are registers, and the instruction indicates which of
; several values to load that register with.
; Four primary 12-bit registers, A, B, C, D:
; A: first operand to ALU, also reads IO input
; B: second operand to ALU, also can be loaded with a constant
; C: loop counter
; D: data storage, can exchange with A
;
; Additional registers:
; W: 12-bit write-only register, IO output always comes from here
; F: 1-bit comparison result flag
;
; Additional units:
; branch control unit
; IO output
; ALU: choose between computing 13 different functions
;
; Each of the units above is independently targetable in the
; VLIW instruction set. For each unit's opcode, specify one of
; the below strings (exactly; the assembler does string matching),
; or no string at all for a no-op in that unit. Separate each of the
; unit opcodes using commas; the unit opcodes can appear in any
; order. Note that all operations occur simultaneously, so each
; operation refers to the value that was in the registers at the
; beginning of the instruction, so the order the unit opcodes are
; listed in has no effect.
;
; A unit: B unit: C unit: D unit:
; A=D B=A C=alu D=A
; A=IN1 B=constant DEC
; A=IN2 B=alu DECNZ label
; A=alu
;
; W unit: F unit: branch: output
; W=A F=ZERO(alu) JMP label OUT1=W
; W=constant F=NEG(alu) JMPT label OUT2=W
; W=alu F=POS(alu) JMPF label
;
; constant = -2048..2047, or -$800..$7FF
; replace 'alu' with one of:
; 0
; -A
; B
; C
; A+B
; B-A
; A+B+F ; F is used as a carry
; B-A-F ; F is used as a borrow
; A>>1 ; shift bits right
; A|B ; bitwise or
; A&B ; bitwise and
; A^B ; bitwise xor
; ~A ; bitwise inverse
;
; All appearances of 'alu' in a single instruction must expand to the
; same string. You can use shorthand like 'A=B=B-A' if multiple registers
; are getting the alu result. You can use F=ZERO(),F=NEG(),F=POS() to
; implicitly use the alu string specified elsewhere in the same instruction.
;
; All constants in a single instruction must be identical. All labels in
; a single instruction must be identical. If both a label and a constant
; are present, they must have the same value, or the constant must be in
; the range -32..31 and the instruction must be 0..63. (Instructions are
; numbered starting at 0, and you are limited to at most 256 instructions.)
;
; If both IN and OUT appear in the same instruction, they must have the same
; index (i.e. IN1 & OUT1 or IN2 & OUT2, but not IN1 & OUT2 or IN2 & OUT1).
;
; F=ZERO(v) sets F to true if v is 0, false otherwise.
;
; F=POS(v) sets F to true if v is positive, false otherwise. v is computed with
; extra precision, so F=POS(B-A) is equivalent to F=(B>A). Likewise, F=NEG(v)
; sets F to true if v is negative, false otherwise, with extended precision for v.
;
; A>>1 places the bottom bit into the hidden sign bit normally used
; to compute F=ZERO/POS/NEG. This means that F=NEG(A>>1) sets F if
; A was odd, and otherwise clears F. Because F is computed from all
; the bits, this means other F functions on A>>1 make little sense.
; Specifically:
; F=ZERO(A>>1) is true if and only if A was zero before the shift.
; F=POS(A>>1) is true if and only if A was positive and even before the shift
;
; JMPT means jump if F is true, JMPF means jump if F is false.
;
; DEC means 'C=C-1'. DECNZ means 'C=C-1', then branch if the result is non-zero
; (similar to 8086 "LOOP" instruction).
;
; Instructions must always begin with a space character. If there is no
; space character, the line is assumed to begin with a label. Labels are
; terminated with the ':' character. Labels can appear on a line by themselves
; and does not count as an instruction.
;
; Example instructions:
;
; A=B,B=A ; swaps A & B. Note that A=B implicitly uses the ALU to compute B.
;
; A=B-A,F=ZERO() ; compute B-A into A, and sets F to true if A & B were equal
;
; A=IN1,W=A,OUT1=W ; writes old value of W, loads W from A, and loads A from input.
;
; label2: B=D=A,C=A+B ; can use B=D=A notation even when non-ALU
;
; A=0,B=1 ; B loads immediate constant 1, ALU is set to 0 for A
;
; A=IN1,B=B-A,DECNZ label2,D=A,F=ZERO(),JMPT label2,W=A,OUT1=W ; uses every unit
;
;
; Conventional instruction ordering:
;
; The standard convention for ordering of operations within a single instruction
; is to sequence them (as much as possible) so that if they were split into separate
; instructions, they would still produce the same result. E.g. if one operation
; reads from A and another writes to A, then put the first before the second.
; Branches go at the end.
;
; There are two cases that cannot be ordered so that they behave the same as separate instructions:
; 1. A cycle or swap like "D=A,A=D"
; 2. An assignment to F in the same instruction as JMPT or JMPF
;
; The first case should be put after everything else except branches.
;
; In the second case, if the assignment to F is explicit, e.g. F=ZERO(B-A),
; then it should appear before any assignments to the ALU inputs, here A and B.
; If it is implicit, e.g. F=ZERO(), then it can be placed arbitrarily, but before
; any branches.
;
; So the last example above:
; A=IN1,B=B-A,DECNZ label2,D=A,F=ZERO(),JMPT label2,W=A,OUT1=W
; could be written:
; OUT1=W,W=A,D=A,B=B-A,A=IN1,F=ZERO(),DECNZ label2,JMPT label2
;
; ============================================================================
;;;;;;;;;;;;;
;;;;;;;;;;;;;
;;;
;;; Example problem: compute OUT1 as 8*IN1
;;;
; straightforwad logic:
loop1:
A=IN1
B=A
A=B=A+B
A=B=A+B
W=A+B
OUT1=W
JMP loop1
; load 'A' 1-cycle earlier; this loads an extra IN1, but it's harmless to read off the end
A=IN1
loop2:
B=A
A=B=A+B
A=B=A+B
W=A+B
OUT1=W
A=IN1
JMP loop2
; combine instructions where possible to get a 5-cycle loop
A=IN1
loop3:
B=A
A=B=A+B
A=B=A+B
W=A+B
OUT1=W,A=IN1,JMP loop3
; compute A=IN1 as soon as possible:
A=IN1
loop4:
B=A
A=B=A+B
A=B=A+B
W=A+B,A=IN1
OUT1=W,JMP loop4
; compute B earlier to get a 4-cycle loop
A=IN1
B=A
loop5:
A=B=A+B
A=B=A+B
W=A+B,A=IN1
OUT1=W,B=A,JMP loop5