What follows is a chronological list of programs written for Důvěřivý. Note that many of these programs benefit from increasing the speed of the emulator; do this by decreasing the Δt and increasing the cpu cycles per frame.
This was the first program that used the scheme where you have two variables for the input, INPUT and OLDINPUT, and you only perform an action if the input has changed. This allows for keypresses to cause single movements. It also uses XOR operations to modify the bit selected by the cursor.
; ; ADDITION PROGRAM ; By blah 2016-11 ; For Důvěřivý ; ; This program allows the user to enter two numbers and add them. ; ; Use of screen space: ; Row 0 contains the cursor. ; Row 1 contains operand A. ; Row 2 contains operand B. ; Row 3 contains the result. ; All other rows are unused. ; ; Controls: ; LEFT: Rotate cursor left. ; RIGHT: Rotate cursor right. ; UP: Toggle bit of operand A which is selected by the cursor. ; DOWN: Same as UP, but with operand B. ; SELECT: Calculate A+B. ; BACK: Clear. ; CURSOR: 01 OPA: 0 OPB: 0 RESULT: 0 INPUT: 0 OLDINPUT: 0 XOR0: 0 ; XOR0 and 1 are required to compute XOR using only NORs XOR1: 0 :START MOV [INPUT] OLDINPUT ; OLDINPUT exists to ensure that keypresses are only registered once GIN INPUT EQU [INPUT] [OLDINPUT] CGO START EQU [INPUT] 00000001 CGO DOWN EQU [INPUT] 00000010 CGO LEFT EQU [INPUT] 00000100 CGO UP EQU [INPUT] 00001000 CGO RIGHT EQU [INPUT] 00010000 CGO SELECT EQU [INPUT] 00100000 CGO BACK JMP START :LEFT ; Rotates the cursor left ROL [CURSOR] CURSOR SVM 0 [CURSOR] JMP START :RIGHT ; Rotates the cursor right ROR [CURSOR] CURSOR SVM 0 [CURSOR] JMP START :UP ; Some instructions to simulate XOR with the NOR instruction; (A AND B) NOR (A NOR B) = A XOR B NOR [CURSOR] 0 XOR0 NOR [OPA] 0 XOR1 NOR [XOR0] [XOR1] XOR0 NOR [CURSOR] [OPA] XOR1 NOR [XOR0] [XOR1] OPA SVM 1 [OPA] JMP START :DOWN ; Same thing. NOR [CURSOR] 0 XOR0 NOR [OPB] 0 XOR1 NOR [XOR0] [XOR1] XOR0 NOR [CURSOR] [OPB] XOR1 NOR [XOR0] [XOR1] OPB SVM 2 [OPB] JMP START :SELECT ; Adds the operands and writes them. ADD [OPA] [OPB] RESULT SVM 3 [RESULT] JMP START :BACK ; Clears everything. MOV 0 OPA MOV 0 OPB MOV 0 RESULT SVM 1 0 SVM 2 0 SVM 3 0 JMP START
This was an optimised version of a smaller program. It breaks down around P=128 because the candidate multiples (QM) start overflowing and this breaks the logic.
; Unsimple Prime Number Program
P: 1 ; candidate prime
Q: 1 ; candidate divisor
QM: 1
TRASH: 0
PAT: 0
:START
ADD [P] 2 P
MOV 1 Q
:NEWDIVISOR
ADD [Q] 2 Q
SUB [Q] [P] TRASH
CGO PRIMEGET
; Repetition of the modulus loop code, but for special case of 2Q, where if QM > P, P is prime
ADD [Q] [Q] QM
EQU [P] [QM]
CGO START
SUB [QM] [P] TRASH
CGO PRIMEGET
:MODULUS
ADD [QM] [Q] QM
EQU [QM] [P]
CGO START
SUB [QM] [P] TRASH
CGO NEWDIVISOR
JMP MODULUS
:PRIMEGET
SVM [PAT] [P]
ADD [PAT] 1 PAT
JMP START
; Simulating Rule 90 with bitwise operations ; This one's cool to watch. STATE: 00001000 T: 0 SL: 0 SR: 0 XOR0: 0 XOR1: 0 :START SVM [T] [STATE] LSL [STATE] SL LSR [STATE] SR ; XOR code respectfully plagiarised from my addition program NOR [SL] 0 XOR0 NOR [SR] 0 XOR1 NOR [XOR0] [XOR1] XOR0 NOR [SL] [SR] XOR1 NOR [XOR0] [XOR1] STATE ADD [T] 1 T JMP START
; Rule 54 STATE: 00001001 T: 0 SL: 0 SR: 0 XOR0: 0 XOR1: 0 ORHOLD: 0 :START SVM [T] [STATE] ROL [STATE] SL ROR [STATE] SR NOR [SL] [SR] ORHOLD NOR [ORHOLD] 0 ORHOLD NOR [STATE] 0 XOR0 NOR [ORHOLD] 0 XOR1 NOR [XOR0] [XOR1] XOR0 NOR [STATE] [ORHOLD] XOR1 NOR [XOR0] [XOR1] STATE ADD [T] 1 T JMP START
;
; DRAWING PROGRAM
; By blah 2018-05
; For Důvěřivý
;
; This program allows the user to draw on the screen using a flashing cursor.
;
; It contains the first implementation of an idea I had a while ago: to have a
; function manually implemented in assembly. to call it, you simply set the
; variable RET to the location where the flow should go to after the function
; has finished, and then JMP XORFUNC.
;
; Controls:
; LEFT, RIGHT, UP, DOWN: move cursor one pixel.
; SELECT: Toggle selected pixel.
; BACK: Clear.
;
X: 10000000 ; This specifies the horizontal location of the cursor. It is
; rotated with ROL and ROR to move the cursor horizontally.
Y: 0 ; Contains the vertical position of the cursor, as an actual integer. Only
; the least significant 3 bits are used, as such the cursor goes down the
; bottom and back out the top, or vice versa.
OLDIN: 0
IN: 0
XORIN: 0 ; Multiple temporary variables are required to simulate XOR with NORs.
XOR0: 0
XOR1: 0
RET: 0
:XORFUNC
GVM [Y] XORIN
NOR [XORIN] [X] XOR0 ; Simulating XOR with NORs.
NOR [XOR0] [X] XOR1 ; If you ever make a computer, here's some advice:
NOR [XOR0] [XORIN] XOR0 ; Don't just have one logical bitwise operation.
NOR [XOR0] [XOR1] XOR0 ; Really.
NOR [XOR0] [XOR0] XORIN ; It sucks.
SVM [Y] [XORIN]
JMP [RET]
:START
MOV PNT1 RET ; We call the XOR function twice to flash the cursor.
JMP XORFUNC
:PNT1
MOV PNT2 RET
JMP XORFUNC
:PNT2
GIN IN ; We get the input overall
EQU [IN] [OLDIN] ; We then check to see if it has actually changed
CGO START
MOV [IN] OLDIN
EQU [IN] 00000001
CGO DOWN
EQU [IN] 00000010
CGO LEFT
EQU [IN] 00000100
CGO UP
EQU [IN] 00001000
CGO RIGHT
EQU [IN] 00010000
CGO SELECT
EQU [IN] 00100000
CGO BACK
JMP START
:DOWN
ADD 1 [Y] Y
JMP START
:LEFT
ROL [X] X
JMP START
:UP
SUB [Y] 1 Y
JMP START
:RIGHT
ROR [X] X
JMP START
:SELECT
MOV START RET
JMP XORFUNC ; Normally XORFUNC is called twice at the beginning of the main
; loop, to flash the cursor. However, calling it outside of that
; context will actually change the permanent value of this pixel.
:BACK
SVM 0 0 ; Write 0 to all rows of VRAM.
SVM 1 0
SVM 2 0
SVM 3 0
SVM 4 0
SVM 5 0
SVM 6 0
SVM 7 0
JMP START
The most elaborate program I have written so far. It was based on a game Jack Eisenmann made for the DUO 128 ELITE, which can be found here. This is when I started using tabs. To get an appropiate speed to run this program at using the emulator, lower the Δt to 10ms and then set cpu cycles per frame to at least ~30.
; ; DUCK GAME ; By blah 2018-06 ; For Důvěřivý ; ; An adaptation of a game that I saw implemented by Jack Eisenmann for the DUO ; 128 Elite. Run at ~6KHz. ; ; Move up and down to collect food. At the end of the game, you will see a ; screen with a face and two numbers displayed in binary on the top and bottom. ; The number on the top is how many food items you collected. The number on the ; bottom is your high score. If you collected all of them, the face will be ; smiling, otherwise it won't be. ; ; To increase the difficulty, you can simply run the simulation faster. ; ; Controls: ; UP, DOWN: Move duck. ; SELECT: Start new game. ; BACK (after game has ended): Reset highscore. ; ; Variables, roughly ordered by how temporary they are. TEMP: 0 IN: 0 OLDIN: 0 T: 0 DRAWFOODRET: 0 ; Return pointer for DRAWFOOD function FOOD: 0 DUCKPOS: 1 FOODPOS: 1 RAND: 00100111 FOODSPAWNED: 0 SCORE: 0 HIGHSCORE: 0 :GAMEEND SUB [HIGHSCORE] [SCORE] TEMP CGO NONEWHIGHSCORE MOV [SCORE] HIGHSCORE :NONEWHIGHSCORE SVM 0 [SCORE] SVM 1 00000000 SVM 2 00100100 SVM 3 00000000 SVM 4 00100100 SVM 5 00111100 SVM 6 00000000 SUB [SCORE] 0F TEMP ; SUB is used here because it sets the flag MOV 0 SCORE ; The score will be 0 to start a new game :WRITEHIGHSCORE SVM 7 [HIGHSCORE] CGO GAMEENDLOOP SVM 4 00111100 SVM 5 00100100 :GAMEENDLOOP GIN IN EQU [IN] 10 CGO START EQU [IN] 00100000 CGO RESETHIGHSCORE JMP GAMEENDLOOP :RESETHIGHSCORE MOV 0 HIGHSCORE JMP WRITEHIGHSCORE :MAINLOOP ADD 2 [T] T ; Increment the timer GIN IN CGO FOODACTION ; If the timer counted all the way up, execute a tick EQU [IN] [OLDIN] CGO MAINLOOP ADD [T] [RAND] RAND ; Use player inputs to aid PRNG MOV [IN] OLDIN EQU [IN] 00000001 CGO DOWN EQU [IN] 00000100 CGO UP JMP MAINLOOP :DOWN SVM 0 00000000 SVM 1 00000000 SVM 2 00000000 SVM 3 00000000 SVM 4 00100000 SVM 5 00110000 SVM 6 11100000 SVM 7 01100000 MOV 5 DUCKPOS JMP CALLDRAWFOODANDGOTOMAINLOOP :START :UP SVM 0 00100000 SVM 1 00110000 SVM 2 11100000 SVM 3 01100000 SVM 4 00000000 SVM 5 00000000 SVM 6 00000000 SVM 7 00000000 MOV 1 DUCKPOS :CALLDRAWFOODANDGOTOMAINLOOP MOV MAINLOOP DRAWFOODRET :DRAWFOOD ; Function that draws food, modifies VRAM and flag EQU [FOODPOS] [DUCKPOS] ; The flag is set here, and can still be used CGO DUCKFOODSAMEFUNC ; after the function exits SVM [FOODPOS] [FOOD] JMP [DRAWFOODRET] :DUCKFOODSAMEFUNC NOR [FOOD] 00110000 TEMP NOR [TEMP] 0 TEMP SVM [FOODPOS] [TEMP] JMP [DRAWFOODRET] :FOODACTION EQU [FOOD] 0 CGO NEWFOOD :MOVEFOOD LSL [FOOD] FOOD MOV 84 MOVEFOOD ; Self modifying code MOV TICKRET DRAWFOODRET JMP DRAWFOOD :TICKRET CGO DUCKFOODSAME JMP MAINLOOP :DUCKFOODSAME EQU [FOOD] 00010000 CGO FOODCAUGHT JMP MAINLOOP :FOODCAUGHT ADD 1 [SCORE] SCORE MOV 0 FOOD JMP MAINLOOP :NEWFOOD ; Generate new food item and randomly choose location MOV 94 MOVEFOOD ; Self modifying code ADD 10 [FOODSPAWNED] FOODSPAWNED CGO GAMEEND MOV 10000000 FOOD ROR [RAND] RAND NOR [RAND] 11111011 TEMP ADD 1 [TEMP] FOODPOS JMP MOVEFOOD
Uses complicated self-modifying code to display an arbitary amount of animation frames. Here it is used to display the words "THIS IS NOT THE DUO".
00000000
01111100
00010000
00010000
00010000
00010000
00000000
00000000
00000000
01000100
01000100
01111100
01000100
01000100
00000000
00000000
00000000
01111100
00010000
00010000
00010000
01111100
00000000
00000000
00000000
00111100
01000000
00111000
00000100
01111000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
01111100
00010000
00010000
00010000
01111100
00000000
00000000
00000000
00111100
01000000
00111000
00000100
01111000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
01000100
01100100
01010100
01001100
01000100
00000000
00000000
00000000
00111000
01000100
01000100
01000100
00111000
00000000
00000000
00000000
01111100
00010000
00010000
00010000
00010000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
01111100
00010000
00010000
00010000
00010000
00000000
00000000
00000000
01000100
01000100
01111100
01000100
01000100
00000000
00000000
00000000
01111100
01000000
01111000
01000000
01111100
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
01111000
01000100
01000100
01000100
01111000
00000000
00000000
00000000
01000100
01000100
01000100
01000100
00111000
00000000
00000000
00000000
00111000
01000100
01000100
01000100
00111000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
:FRAMESEND
FRAMEAT: 0
T: 0
i: 0 ; It seems unnatural to me for the iterator i to be uppercase. \('.')/
:START
ADD 1 [T] T
CGO NEXTFRAME ; The flag is set here by ADD, so CGO is triggered by overflow
JMP START
:NEXTFRAME
MOV 0 i
EQU [FRAMEAT] FRAMESEND
CGO LOOPFRAMES
:DRAWLOOP
EQU [i] 8
CGO START
ADD 1 [i] i
MOV [FRAMEAT] D1 ; Very annoying bit of self-modifying code to write.
:SVMINSTRUCTION
SVM [FRAMEAT] [0]
ADD 1 [FRAMEAT] FRAMEAT
JMP DRAWLOOP
:LOOPFRAMES
MOV 0 FRAMEAT
JMP DRAWLOOP