Created
July 17, 2015 14:00
Fractal dragon in 8086 assembly.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.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