; ; ; To assembly this, either use the zxasm.bat file: ; zxasm mandelbrot02 ; or... assemble with the following options: ; tasm -80 -b -s mandelbrot02.asm mandelbrot02.p ; ;============================================== ; ZX81 "MANDELBROT02" ;============================================== ; ; .ORG 16393 ; System variables live here VERSN: .BYTE 0 E_PPC: .WORD 2 D_FILE: .WORD Display DF_CC: .WORD Display+1 ; First character of display VARS: .WORD Variables DEST: .WORD 0 E_LINE: .WORD BasicEnd CH_ADD: .WORD BasicEnd+4 ; Simulate SAVE "X" X_PTR: .WORD 0 STKBOT: .WORD BasicEnd+5 STKEND: .WORD BasicEnd+5 ; Empty stack BREG: .BYTE 0 MEM: .WORD MEMBOT UNUSED1: .BYTE 0 DF_SZ: .BYTE 2 S_TOP: .WORD 1 ; Top program line number LAST_K: .WORD $fdbf DEBOUN: .BYTE 15 MARGIN: .BYTE 55 NXTLIN: .WORD Line0 ; Next line address OLDPPC: .WORD 0 FLAGX: .BYTE 0 STRLEN: .WORD 0 T_ADDR: .WORD $0c8d SEED: .WORD 0 FRAMES: .WORD $f5a3 COORDS: .WORD 0 PR_CC: .BYTE $bc S_POSN: .WORD $1821 CDFLAG: .BYTE $40 PRBUFF: .BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,$76 ; 32 Spaces + Newline MEMBOT: .BYTE 0,0,0,0,0,0,0,0,0,0,$84,$20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; 30 zeros UNUSED2: .WORD 0 ; End of ZX81 system variables ; This program calculates the Mandelbrot set ; ;the standard REM statement that will contain our 'hex' code Line0: .BYTE $00,$00 ; Line 0 line number "0" .WORD Line0End-Line0Text ; Line 0 length Line0Text: .BYTE $EA,$76,$76 ; REM token and two ENTERs to hide line from listing ;------------------------------------------------------------ ; code starts here and gets added to the end of the REM ; USE "RAND USR 16516" to start program ;------------------------------------------------------------ JP Start CaStart .WORD -8192 ; 4.12 fixed point value (-2.0) CbiStart .WORD -6144 ; 4.12 fixed point value (-1.5) CaInc .WORD 160 ; 4.12 fixed point value (2.5/64) CbiInc .WORD 256 ; 4.12 fixed point value (3.0/48) MaxIntr .BYTE 12 ; Integer Maximum interation depth Za .WORD 0 ; Z.real 4.12 fixed point value Zbi .WORD 0 ; Z.img 4.12 fixed point value Zaa .WORD 0 ; Z.real^2 4.12 fixed point value Zbbi .WORD 0 ; Z.img^2 4.12 fixed point value Ca .WORD 0 ; C.real 4.12 fixed point value Cbi .WORD 0 ; C.img 4.12 fixed point value ItrCnt .BYTE 0 ; Interation counter XCntr .BYTE 0 ; X plot position counter YCntr .BYTE 0 ; Y plot position counter ; ; Draw Mandelbrot set using formula ; Z = Z^2 + C ; Where Z and C are complex values: ; Z=Za + Zbi C=Ca + Cbi Where a=real magnitude and b=imaginary magnitude ; ; For each pixel: ; Z start off at zero (Za=0, Zb=0) ; C set to the pixel location Ca=X Cbi=Y ; Now we count how many times we can perform Z=Z^2 + C before Z exceeds 2.0 or we reach the max interation count ; That count is used to color the pixel. Since we only have two colors we will use odd=black, even=white ; Start: ; Set constant "Cbi" to the starting "Cbi" value (Cbi is Y axis and is the same for this whole line) LD HL,(CbiStart) LD (Cbi),HL ; Set Y counter to 47 (screen is 48 pixels high) LD A,47 LD (YCntr),A HLine: ; Start a new horizontal line ; Set constant "Ca" to the starting "Ca" value (Ca is X axis and is the same for this pixel) LD HL,(CaStart) LD (Ca),HL ; Set X counter to 63 (screen is 64 pixels wide) LD A,63 LD (XCntr),A NextPixel: ; Set iteration counter to maximum LD A,(MaxIntr) LD (ItrCnt),A ; Set Z=0.0 and Z^2=0.0 LD HL,0 LD (Za),HL LD (Zbi),HL LD (Zaa),HL ; Zaa is Za^2 LD (Zbbi),HL ; Zbbi is Zbi^2 ; Main iteration loop ItrLoop: ; Decrease interaction counter LD HL,ItrCnt DEC (HL) JR Z,ItrLoopDone ; Too many iterations jump out of loop ; Zbi = 2 * Za * Zbi + Cbi LD HL,(Za) ; HL=Za ADD HL,HL ; HL=2 * Za LD DE,(Zbi) ; DE=Zbi LD B,H ; BC=HL LD C,L CALL Mult ; HL = DE(Zbi) * BC(2 * Za) LD DE,(Cbi) ; DE=Cbi ADD HL,DE ; HL=(2 * Za * Zbi) + Cbi LD (Zbi),HL ; Zbi = 2 * Za * ZBi + Cbi ; Za = Zaa - Zbbi + Ca ; Note that even though we have modified Zbi, Zbbi is still what is was before modified ZBi LD HL,(Zaa) ; HL=Zaa LD DE,(Zbbi) ; DE=Zbbi AND A ; Clear carry SBC HL,DE ; HL = Zaa - Zbbi LD DE,(Ca) ; DE = Ca ADD HL,DE ; HL = (Zaa - Zbbi) + Ca LD (Za),HL ; Za = (Zaa - Zbbi) + Ca ; Zaa = Za*Za LD DE,(Za) ; DE = Za LD B,D ; BC = Za LD C,E CALL Mult ; HL = DE(Za) * BC(Za) LD (Zaa),HL ; Zaa = Za * Za ; Zbbi = Zbi*Zbi LD DE,(Zbi) ; DE = Zbi LD B,D ; BC = Zbi LD C,E CALL Mult ; HL = DE(Zbi) * BC(Zbi) LD (Zbbi),HL ; Zbbi = Zbi * Zbi ; If magnitude of Z is < 2.0 then loop again ; Magnitude of a complex value is sqr(Za^2 + Zbi^2) ; We want to see if sqr(Za^2 + Zbi^2) < 2.0, so we square each side to get Za^2 + Zbi^2 < 4.0 ; If Zaa + Zbb < 4.0 then goto ItrLoop LD HL,(Zaa) ; HL=Zaa LD DE,(Zbbi) ; DE=Zbbi ADD HL,DE ; HL=Zaa + Zbbi ; HL is in 4.12 fixed point, the value (4.0) in 4.12 fixed point format is 64,0 LD A,H ; Check if H is less than 64 CP 64 JR C,ItrLoop ; If it is, loop again ItrLoopDone: ; We are out of the iteration loop, so either the magnitude of Z exceeded 2.0, or ; we performed the maximum number of iteration loops. ; ; Plot pixel based on ItrCnt ; If ItrCnt.0 is 1, then plot the point LD A,(ItrCnt) AND 1 JR NZ,NoPlot ; Plot pixel X,Y LD A,(XCntr) ; X pixel is at 63-XCntr LD H,A LD A,63 SUB H LD H,A LD A,(YCntr) ; Y pixel is at YCntr LD L,A CALL PlotHL ; H=X, L=Y NoPlot: ; Adjust Ca to value for next pixel on this line ; Ca = Ca + CaInc LD HL,(Ca) ; HL = Ca LD DE,(CaInc) ; DE = CaInc ADD HL,DE ; HL = Ca + CaInc LD (Ca),HL ; Ca = Ca + CaInc ; Are there more pixels on this line ? LD HL,XCntr DEC (HL) JP NZ,NextPixel ; No more pixels on this line, prepare to do next line ; Adjust Cbi to value for next line of pixels ; Cbi = Cbi + CbiInc LD HL,(Cbi) ; HL = Cbi LD DE,(CbiInc) ; DE = CbiInc ADD HL,DE ; HL = Cbi + CbiInc LD (Cbi),HL ; Cbi = Cbi + CbiInc ; Return if SHIFT key was pressed LD A,$FE ; Get status of lower left keys IN A,($FE) AND 1 ; Was shift held ? RET Z ; Shift key was held, abort... ; Are there more lines ? LD HL,YCntr DEC (HL) JP NZ,HLine ; No more lines, drawing is done RET ; ======================================================= ; ---------------------------------------------- ; ---------------------------------------------- Mult: ; HL(4.12) = DE(4.12) * BC(4.12) ; Find sign of product LD A,0 ; Assume positive BIT 7,D ; Is DE negative ? JR Z, DEOK ; No, then it's okay ; DE is negative, so negate it LD HL,0 ; Prepare to do DE = 0 - DE AND A ; Clear carry SBC HL,DE ; HL = 0 - DE LD D,H ; DE = HL LD E,L LD A,1 ; Assume product is negative DEOK: BIT 7,B ; Is BC negative ? JR Z, BCOK ; No, then it's okay ; BC is negative, so negate it LD HL,0 ; Prepare to do BC = 0 - BC AND A ; Clear carry SBC HL,BC ; HL = 0 - BC LD B,H ; BC = HL LD C,L XOR 1 ; Change sign of product BCOK: PUSH AF ; save sign of product ; From www.z80-heaven.wikidot.com/math ; DEHL = DE*BC LD HL,0 LD A,16 Mul_Loop_1: ADD HL,HL RL E RL D JR NC,Mul_NoAdd ADD HL,BC JR NC,Mul_NoCarry INC DE Mul_NoCarry: Mul_NoAdd: DEC A JR NZ,Mul_Loop_1 ; We need to do HL=DEHL >> 12 since the values are (4.12) format ; Move registers to do shift right by 8 LD L,H LD H,E LD E,D ; Shift right by 4 additional bits SRL E RR H RR L SRL E RR H RR L SRL E RR H RR L SRL E RR H RR L ; Should result be negative ? POP AF AND 1 RET Z ; Positive result, we're done ; Product should be negative so negate HL LD D,H ; DE = HL LD E,L LD HL,0 ; HL = 0 AND A ; Clear carry SBC HL,DE ; HL = 0 - HL RET ; --------------------------------------------------------------------------- PlotHL: ; PLOT H,L PUSH AF ; Save registers PUSH DE PUSH HL ; H=XCoord, L=YCoord XOR A ; Zero A to hold pixel mask SRL H ; H = X / 2 (carry will be set if X is odd) ADC A,1 ; If carry, make mask = 2 otherwise mask = 1 SRL L ; L = Y / 2 (carry will be set if Y is odd) JR NC,MASK1 ADD A,A ; If carry, make mask 4 or 8 ADD A,A MASK1: LD C,A ; C = pixel mask ; C NOW HOLDS: 1 2 ; 4 8 ; H and L now hold the column and row of the screen location ; Prepare to do HL = L(Y) * 33 + H(X) LD A,H ; A = X ADD A,L ; A = X + Y LD H,0 ; HL = Y LD D,H ; DE = X + Y LD E,A ADD HL,HL ; HL = Y * 2 ADD HL,HL ; HL = Y * 4 ADD HL,HL ; HL = Y * 8 ADD HL,HL ; HL = Y * 16 ADD HL,HL ; HL = Y * 32 ADD HL,DE ; HL = Y * 32 + (X + Y) or (Y * 33) + X LD DE,(D_FILE) ; DE = (D_FILE) ADD HL,DE ; HL = (D_FILE) + Y*33 + X INC HL ; Skip first $76 byte in D_FILE ; HL NOW POINTS TO THE PROPER DISPLAY BYTE TO BE MODIFIED LD A,(HL) ; Get the character currently on the screen BIT 7,A ; Convert to a normal pixel mask JR Z,NOXOR1 XOR 143 NOXOR1: OR C ;OR new pixel mask to set pixel BIT 3,A ; Convert to a ZX81 pixel mask JR Z,NOXOR2 XOR 143 NOXOR2: LD (HL),A ; Put modified byte back in display memory POP HL ; Restore registers POP DE POP AF RET ; ======================================================== ; ======================================================== .BYTE $76 ; Newline at end of REM line Line0End: ;Line1: .BYTE $00,$00 ; Line 0 line number "1" ; .WORD Line1End-Line1Text ; Line 0 length ;Line1Text: .BYTE 234,212,197,11,29,34,33,29,34,11 ; RAND USR VAL"16516" ; 249 ; .BYTE $76 Line1End: ; Display file (ZX81 screen) - low res screen Display .BYTE $76 .BYTE $76 ; Line 0 .BYTE $76 ; Line 1 .BYTE $76 ; Line 2 .BYTE $76 ; Line 3 .BYTE $76 ; Line 4 .BYTE $76 ; Line 5 .BYTE $76 ; Line 6 .BYTE $76 ; Line 7 .BYTE $76 ; Line 8 .BYTE $76 ; Line 9 .BYTE $76 ; Line 10 .BYTE $76 ; Line 11 .BYTE $76 ; Line 12 .BYTE $76 ; Line 13 .BYTE $76 ; Line 14 .BYTE $76 ; Line 15 .BYTE $76 ; Line 16 .BYTE $76 ; Line 17 .BYTE $76 ; Line 18 .BYTE $76 ; Line 19 .BYTE $76 ; Line 20 .BYTE $76 ; Line 21 .BYTE $76 ; Line 22 .BYTE $76 ; Line 23 ; close out the BASIC program structure ; BASIC Variables area Variables: VariablesEnd: .BYTE $80 BasicEnd: #END