Skip to content

Instantly share code, notes, and snippets.

Created July 17, 2015 14:00
Fractal dragon in 8086 assembly.
.MODEL compact
.STACK 256
;-- EQU and = equates
c_red equ 4d;
c_yellow equ 44d;
c_green equ 2d;
c_skyblue equ 11d;
; For shift row
rowLeft equ 0;
rowRight equ 1;
; For shift col
colUp equ 0;
colDown equ 1;
;-- Variable declarations
.DATA
; Initialize the videoBuffer that we will use to compute the next state of the
; screen before drawing it to the video memory
videoBuffer db 64000 dup(0);
; Strings!
welcome db ' Hello. Please select action:', 13, 10, 13, 10, '$'
choice1 db ' Press 1 to create fractal twin dragon', 13, 10, '$'
choice2 db ' Press ESC to exit', 13, 10, '$'
invalidInput1 db ' Invalid input', 13, 10, 13, 10, '$'
invalidInput2 db ' Press any key to try again', 13, 10, '$'
; Variables for passing parameters to drawLine
x0Line dw 0;
x1Line dw 0;
y0Line dw 0;
y1Line dw 0;
dxLine dw 0;
dyLine dw 0;
; Variables for passing parameters to putPixel
putPixelX dw 0;
putPixelY dw 0;
colourArray db 6,39,44,2,1,5;
colourNumber db 0;
currentColour db 1;
; Variables for shift row and column functions
; Row
shiftRowStart dw 0;
shiftRowEnd dw 0;
shiftRowAmount dw 0;
shiftRowDirection dw 0;
; Col
shiftColStart dw 0;
shiftColEnd dw 0;
shiftColAmount dw 0;
shiftColDirection dw 0;
; Both
tempShiftAmount dw 0;
; Move pixel variables
moveFromX dw 0;
moveFromY dw 0;
moveToX dw 0;
moveToY dw 0;
;-- Program macros
.CODE
;;;;;;;;;;;;;;;;;;
; Use the rep MOVSB instruction to copy the contents of the videoBuffer into the
; video memory.
; Before this routine is called ES shall contain the address of the video memory
; and DS shall contain the address of the videoBuffer array.
;;;;;;;;;;;;;;;;;;
clearVideoBuffer:
push CX;
push DI;
push SI;
; ES already stores the address for the screen buffer, so we can use this
; here without modification.
; The address of videoBuffer is already stored in DS, so we can use this for
; the string move operation.
MOV DI, 0d;
MOV SI, 0d;
MOV CX, 64000d; Set the number of operations we want to perform
cld; Clear the direction flag for string operations using rep MOVSB
; Call ES:[DI] = DS:[SI] CX number of times using movsb, copying the data
; in the videoBuffer to the video memory.
rep MOVSB;
pop SI;
pop DI;
pop CX;
ret;
;;;;;;;;;;;;;;;;;;
; Function to print a single pixel to the VGA memory in ES
; Takes putPixelX and putPixelY as arguments.
;;;;;;;;;;;;;;;;;;
putPixel:
push AX;
push BX;
push CX;
push DX; DX modified when using MUL
MOV AX, 0320d;
mul putPixelY; Calculate memory offset for y parameter
add AX, putPixelX; Calculate total memory offset
MOV CL, currentColour;
MOV BX, AX;
MOV ES:[BX], CL; Move colour selected into screen buffer
pop DX;
pop CX;
pop BX;
pop AX;
ret;
;;;;;;;;;;;;;;;;;;
; Function to draw vertical line
; Takes x0Line, x1Line, y0Line, y1Line as inputs and draws on the screen buffer
;;;;;;;;;;;;;;;;;;
vertLine:
push AX;
push BX;
push CX;
push DX;
MOV AX, y1Line;
CMP y0Line, AX;
JG vertLiney0Bigger; If y0 > y1
vertLiney1Bigger:
MOV BX, y0Line; BX store smallest value
MOV CX, y1Line; CX store largest value
JMP vertLineDraw;
vertLiney0Bigger:
MOV BX, y1Line; BX store smallest value
MOV CX, y0Line; CX store largest value
vertLineDraw:
MOV DX, x0Line;
MOV putPixelX, DX; X is static since it is a vertical line
; BX stores the lowest yvalue, CX stores the highest y value
; Check and draw first point in case it is a point needing to be drawn
MOV putPixelY, BX;
call putPixel;
INC BX;
INC CX; Easier to compare to CX+1
CMP BX, CX;
JNE vertLineDrawLoop; If CX >= BX continue drawing
JMP vertLineEnd; Go to end if BX > CX - "line" was only a point
vertLineDrawLoop:
MOV putPixelY, BX;
call putPixel;
INC BX;
CMP BX, CX
JNE vertLineDrawLoop;
; Do final draw
MOV BX, putPixelY;
call putPixel;
vertLineEnd:
pop DX;
pop CX;
pop BX;
pop AX;
ret;
;;;;;;;;;;;;;;;;;;
; Function to draw horizontal line
; Takes x0Line, x1Line, y0Line, y1Line as inputs and draws on the screen buffer
;;;;;;;;;;;;;;;;;;
horzLine:
push AX;
push BX;
push CX;
push DX;
MOV AX, x1Line;
CMP x0Line, AX;
JG horzLinex0Bigger; If x0 > x1
horzLinex1Bigger:
MOV BX, x0Line; BX store smallest value
MOV CX, x1Line; CX store largest value
JMP horzLineDraw;
horzLinex0Bigger:
MOV BX, x1Line; BX store smallest value
MOV CX, x0Line; CX store largest value
ADD CX, 1; Easier to compare to CX + 1
horzLineDraw:
MOV DX, y0Line;
MOV putPixelY, DX; Y is static since it is a horizontal line
; BX stores the lowest yvalue, CX stores the highest y value
; Check and draw first point in case it is a point needing to be drawn
MOV putPixelX, BX;
call putPixel;
INC BX;
CMP BX, CX;
JNGE horzLineDrawLoop; If CX >= BX continue drawing
JMP horzLineEnd; Go to end if BX > CX - "line" was only a point
horzLineDrawLoop:
MOV putPixelX, BX;
call putPixel;
INC BX;
CMP BX, CX
JNE horzLineDrawLoop;
; Do final draw
;MOV BX, putPixelX;
;call putPixel;
horzLineEnd:
pop DX;
pop CX;
pop BX;
pop AX;
ret;
;;;;;;
; Function to draw initial state for twin dragon fractal
;;;;;;
drawTwinDragonStart:
push AX;
push BX;
; Draw four squares as starting point
MOV x0Line, 96;
MOV x1Line, 160;
MOV y0Line, 36;
MOV y1Line, 36;
MOV currentColour, c_red;
MOV AX, 99+1;
MOV BX, 163+1;
dragonTwoFirstSquares:
CALL horzLine;
INC y0Line;
INC y1Line;
CMP y0Line, BX;
JE endTFS;
CMP y0Line, AX;
JNE dragonTwoFirstSquares
MOV currentColour, c_yellow;
JMP dragonTwoFirstSquares
endTFS:
MOV x0Line, 160;
MOV x1Line, 224;
MOV y0Line, 36;
MOV y1Line, 36;
MOV currentColour, c_green;
dragonTwoSecondSquares:
CALL horzLine;
INC y0Line;
INC y1Line;
CMP y0Line, BX;
JE endTSS;
CMP y0Line, AX;
JNE dragonTwoSecondSquares
MOV currentColour, c_skyblue;
JMP dragonTwoSecondSquares
endTSS:
pop BX;
pop AX;
ret;
;;;;;;
; Function to move a pixel from one point to another
;;;;;;
movePixel:
push AX;
push BX;
push CX;
push DX;
XOR DX, DX;
MOV AX, 320;
MUL moveFromY;
ADD AX, moveFromX;
MOV BX, AX;
XOR DX, DX;
MOV CL, ES:[BX];
MOV AX, 320;
MUL moveToY;
ADD AX, moveToX;
MOV BX, AX;
MOV ES:[BX], CL; Finalize move.
pop DX;
pop CX;
pop BX;
pop AX;
ret;
;;;;;;
; Function shift a row left or right depending on the direction flag
;;;;;;
shiftRow:
push AX;
push BX;
push CX;
MOV BX, shiftRowEnd; Ending column
INC BX; Easier to compare to end+1
MOV AX, shiftRowAmount;
MOV tempShiftAmount, AX; Iterated until 0
CMP shiftRowDirection, rowLeft; Check if we're shifting left or right
JNE shiftRowRight;
JMP shiftRowLeft;
shiftRowRight: ; Iterating until tempShiftAmount == 0
MOV AX, shiftRowStart; Starting column and column iterator
sRRColumnIter: ; Iterating until AX == endingColumn, use end + 1 to terminate
MOV moveFromY, AX;
MOV moveToY, AX;
MOV CX, 300; Use CX as row indicator for move operations
sRRRowIter: ; Iterating until row = CX == 20
MOV moveFromX, CX;
MOV moveToX, CX;
INC moveToX;
CALL movePixel;
DEC CX;
CMP CX, 20; sRRRowIter
JNE sRRRowIter;
INC AX; sRRColumnIter
CMP AX, BX;
JNE sRRColumnIter;
DEC tempShiftAmount;
CMP tempShiftAmount, 0;
JNE shiftRowRight;
JMP endShiftRow;
shiftRowLeft: ; Iterating until tempShiftAmount == 0
MOV AX, shiftRowStart; Starting column and column iterator
sRLColumnIter: ; Iterating until AX == endingColumn, use end + 1 to terminate
MOV moveFromY, AX;
MOV moveToY, AX;
MOV CX, 20; Use CX as row indicator for move operations
sRLRowIter: ; Iterating until row = CX == 300
MOV moveFromX, CX;
MOV moveToX, CX;
DEC moveToX;
CALL movePixel;
INC CX;
CMP CX, 300; sRLRowIter
JNE sRLRowIter;
INC AX; sRLColumnIter
CMP AX, BX;
JNE sRLColumnIter;
DEC tempShiftAmount;
CMP tempShiftAmount, 0;
JNE shiftRowLeft;
endShiftRow:
pop CX;
pop BX;
pop AX;
ret;
;;;;;;
; Function shift a column up or down depending on the direction flag
;;;;;;
shiftCol:
push AX;
push BX;
push CX;
MOV BX, shiftColEnd; Ending row
INC BX; Easier to compare to end+1
MOV AX, shiftColAmount;
MOV tempShiftAmount, AX; Iterated until 0
CMP shiftColDirection, colUp; Check if we're shifting up or down
JNE shiftColDown;
JMP shiftColUp;
shiftColDown: ; Iterating until tempShiftAmount == 0
MOV AX, shiftColStart; Starting row and row iterator
sCDRowIterator: ; Iterating until AX == shiftColEnd, use end + 1 to terminate
MOV moveFromX, AX;
MOV moveToX, AX;
MOV CX, 198; Use CX as col inducatior for move operations
sCDColIter: ; Iterating until row = CX == 0
MOV moveFromY, CX;
MOV moveToY, CX;
INC moveToY;
CALL movePixel;
DEC CX;
CMP CX, 0; sCDColIter
JNE sCDColIter;
INC AX; sCDRowIterator
CMP AX, BX;
JNE sCDRowIterator;
DEC tempShiftAmount;
CMP tempShiftAmount, 0;
JNE shiftColDown;
JMP endShiftCol;
shiftColUp: ; Iterating until tempShiftAmount == 0
MOV AX, shiftColStart; Starting row and row iterator
sCURowIterator: ; Iterating until AX == shiftColEnd, use end + 1 to terminate
MOV moveFromX, AX;
MOV moveToX, AX;
MOV CX, 1; Use CX as col inducatior for move operations
sCUColIterator: ; Iterating until row = CX == 199
MOV moveFromY, CX;
MOV moveToY, CX;
DEC moveToY;
CALL movePixel;
INC CX;
CMP CX, 199; sCUColIterator
JNE sCUColIterator;
INC AX; sCDRowIterator
CMP AX, BX;
JNE sCURowIterator;
DEC tempShiftAmount;
CMP tempShiftAmount, 0;
JNE shiftColUp;
endShiftCol:
pop CX;
pop BX;
pop AX;
ret;
;;;;;;
; Animation delay thingamajigger
;;;;;;
createDelay:
push AX;
push CX;
MOV AX, 255;
delA:
MOV CX, 255; Reset CX counter
DEC AX; Decrement AX by 1
delC:
DEC CX;
CMP CX, 0;
JNE delC;
CMP AX, 0;
JE endDelay; If done go to end
JMP delA;
endDelay:
pop CX;
pop AX;
ret;
;;;;;;
; Function to animate dragonTwoSecondSquares
;;;;;;
animateDragon:
push CX;
push DX;
; Set initial state
CALL clearVideoBuffer;
call drawTwinDragonStart;
; First iteration
MOV DX, 32;
MOV shiftRowAmount, DX;
MOV shiftRowStart, 36;
MOV shiftRowEnd, 99;
MOV shiftRowDirection, rowLeft;
CALL shiftRow;
MOV shiftRowStart, 100;
MOV shiftRowEnd, 163;
MOV shiftRowDirection, rowRight;
CALL shiftRow;
; Second iteration
MOV CX, 16;
MOV shiftColAmount, CX;
SHL CX, 1; CX = 32;
MOV shiftColStart, 64;
MOV shiftColEnd, 95;
MOV shiftColDirection, colDown;
CALL shiftCol;
secondItLoop:
ADD shiftColStart, CX;
ADD shiftColEnd, CX;
XOR shiftColDirection, colDown;
call shiftCol;
call createDelay;
CMP shiftColEnd, 320; Continue until hitting edge of screen
JNGE secondItLoop;
; Third iteration
SHR CX, 2; CX = 8
MOV shiftRowAmount, CX;
SHL CX, 1; CX = 16;
MOV shiftRowStart, 20; Column for starting iteration
MOV shiftRowEnd, 35; Column for ending iteration
MOV shiftRowDirection, rowLeft;
CALL shiftRow;
thirdItLoop:
ADD shiftRowStart, CX;
ADD shiftRowEnd, CX;
XOR shiftRowDirection, rowRight; Alternate direction
CALL shiftRow;
call createDelay;
CMP shiftRowEnd, 200;
JNGE thirdItLoop;
; Fourth iteration
SHR CX, 2; CX = 4
MOV shiftColAmount, CX;
SHL CX, 1; CX = 8;
MOV shiftColStart, 56;
MOV shiftColEnd, 63;
MOV shiftColDirection, colDown;
CALL shiftCol;
fourthItLoop:
ADD shiftColStart, CX;
ADD shiftColEnd, CX;
XOR shiftColDirection, colDown;
call shiftCol;
call createDelay;
CMP shiftColEnd, 320; Continue until hitting edge of screen
JNGE fourthItLoop;
; Fifth iteration
SHR CX, 2; CX = 2
MOV shiftRowAmount, CX;
SHL CX, 1; CX = 4;
MOV shiftRowStart, 16; Column for starting iteration
MOV shiftRowEnd, 19; Column for ending iteration
MOV shiftRowDirection, rowLeft;
CALL shiftRow;
fifthItLoop:
ADD shiftRowStart, CX;
ADD shiftRowEnd, CX;
XOR shiftRowDirection, rowRight; Alternate direction
CALL shiftRow;
call createDelay;
CMP shiftRowEnd, 200;
JNGE fifthItLoop;
; Final iteration
SHR CX, 2; CX = 1
MOV shiftColAmount, CX;
SHL CX, 1; CX = 2;
MOV shiftColStart, 54;
MOV shiftColEnd, 55;
MOV shiftColDirection, colDown;
CALL shiftCol;
finalItLoop:
ADD shiftColStart, CX;
ADD shiftColEnd, CX;
XOR shiftColDirection, colDown;
call shiftCol;
call createDelay;
call createDelay;
CMP shiftColEnd, 320; Continue until hitting edge of screen
JNGE finalItLoop;
; Wait for keypress
XOR AX, AX;
MOV ah,00
int 16h
call clearVideoBuffer;
pop DX;
pop CX;
ret;
;;;;;;
; Reset char position for text placement
;;;;;;
resetCharPos:
push AX;
push BX;
push DX;
MOV AH, 02h;
MOV BH, 0;
MOV DH, 0;
MOV DL, 0;
INT 10h;
pop DX;
pop BX;
pop AX;
ret
;-- Program code
START:
initialization:
; Clear all general purpouse registers
xor AX, AX;
xor BX, BX;
xor CX, CX;
xor DX, DX;
; Initialize the data segment
MOV AX, @DATA;
MOV DS, AX;
; Initialize ES to point to the graphics memory to easy blanking of the
; cideo buffer
MOV AX, 0A000h;
MOV ES, AX;
; Set keyboard to low delay
; ref: http://www.ctyme.com/intr/rb-1757.htm
MOV AX, 0305h;
xor BX, BX;
int 016h;
enterVgaMode:
MOV AX, 013h;
int 010h;
setupLoop:
mainLoop:
call clearVideoBuffer;
call resetCharPos;
mov dx, offset welcome
mov ah, 9h ; string output
int 21h ; display string
mov dx, offset choice1
mov ah, 9h ; string output
int 21h ; display string
mov dx, offset choice2
mov ah, 9h ; string output
int 21h ; display string
mov ah, 1h
int 21h
CMP AL, '1';
JE doDragon;
CMP AX, 011Bh;
JE EXIT;
JMP invalidSel;
doDragon:
call animateDragon;
JMP mainLoop;
invalidSel:
call resetCharPos;
call clearVideoBuffer;
mov dx, offset invalidInput1
mov ah, 9h ; string output
int 21h ; display string
mov dx, offset invalidInput2
mov ah, 9h ; string output
int 21h ; display string
mov ah, 1h
int 21h
JMP mainLoop;
EXIT:
exitVgaMode:
MOV AX, 03h;
int 10h;
MOV ax, 4c00h
int 21h;
END START
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment