************************************************ * Mandelbrot Explorer for Color Computer 3 * Chris Lomont 2006 www.lomont.org * ************************************************ * push S order: PC,U,Y,X,DP,B,A,CC low byte first, S pre-decremented ************************************************ * Splash screen 32x16 ************************************************ splash equ 0 if splash org 1024 fcc "* * * " fcc "12345678901234567890123456789012" fcc " " fcc " MANDELBROT " fcc " CHRIS LOMONT 2006 " fcc " " fcc " " fcc " " fcc " " fcc " " fcc " " fcc "12345678901234567890123456789012" org 356 disk basic autostart - replace jump vectors lbra start lbra start lbra start endif org $E00 3584 decimal start clr $FFD9 high speed poke (todo - undo at end?) lbsr TestFPCode * jsr SetPMODE4 lbsr DisableInterrupts these puke once we replace top of ram lbsr SetGr320 320x200x16 mode ldx vidstart clear screen ldy #32000 this is 320*200/2 bytes, i.e. screensize lda #$11 somple color lbsr memset * test upper corder, and orientation lda #3 pixel color,x,y ldx #0 ldy #0 lbsr SetPixel leax 1,x leay 1,y lbsr SetPixel * test lower corner lda #6 ldx #319 ldy #199 lbsr SetPixel lda #3 ldx #5 ldy #5 ldb #170 ! lbsr SetPixel leax 1,x leay 1,y inca decb bne < * compute mandelbrot color for every pixel ldx #0 ldy #0 mndloop lbsr ComputeMandelbrotPoint * cmpd #0 * beq drawpix * lda #15 tfr b,a color from low pixel drawpix lbsr SetPixel ! leax 1,x next pixel cmpx #320 blt mndloop ldx #0 leay 1,y cmpy #200 blt mndloop ldx #$A000 infinite loop, making colors move ldy #0 ! sty ,x leay 1,y bra < * todo - enable cleanup rountines rts return?! TestFPCode * test the FP code ldx #AFPOne ldy #AFPTemp1 lbsr FPCopy 1 in temp1 ldx #AFPTemp1 lbsr FPNeg -1 in temp1 ldy #AFPTwo ldu #AFPTemp2 lbsr FPMul -2 in temp2 ldy #AFPTemp2 ldu #AFPTemp3 lbsr FPMul 2 in temp3 * bra TestFPCode lbsr FPMul 0.5 in temp1 ldx #AFPOne ldy #AFPHalf ldu #AFPTemp1 lbsr FPMul 0.5 in temp1 ldx #AFPTemp1 ldy #AFPTemp2 lbsr FPCopy 0.5 in temp2 ldx #AFPTemp1 lbsr FPNeg -0.5 in temp1 ldx #AFPTemp1 ldy #AFPTemp2 ldu #AFPTemp3 lbsr FPMul -0.25 in temp3 ldx #AFPTemp3 ldy #AFPTemp2 ldu #AFPTemp1 lbsr FPAdd 0.25 in temp1 ldx #AFPTemp1 ldy #AFPTemp1 ldu #AFPTemp1 lbsr FPAdd 0.5 in temp1 ldx #AFPTwo ldy #AFPTemp3 ldu #AFPTemp2 lbsr FPMul -0.5 in temp2 ldx #AFPTemp1 ldy #AFPTemp2 ldu #AFPTemp3 lbsr FPAdd 0 rts AFPOne fcb 1,0,0,0 AFPHalf fcb 0,128,0,0 AFPTwo fcb 2,0,0,0 AFPTemp1 fcb 0,0,0,0 AFPTemp2 fcb 0,0,0,0 AFPTemp3 fcb 0,0,0,0 ************************************************ * memset - fill memory * REGX = start address * REGY = length * REGA = filler byte * modifies REGX, REGY ************************************************ memset ! sta ,x+ leay -1,y bne < rts ************************************************ * Variables for current graphics state ************************************************ vidstart rmd 1 video start page for drawing ************************************************ * DrawBox - draw a box on the screen * REGA color 0-15 * REGX x coord 0-319 * REGY y coord 0-199 * REGB height * REGU width * registers changed: none ************************************************ DrawBox pshs x,y,u,a,b * stack: @top lbsr SetPixel leax 1,x leau -1,u bne @top puls x,y,u,a,b rts ************************************************ * Set a pixel in 320x200x16 color mode * REGA color 0-15 * REGX x coord 0-319 * REGY y coord 0-199 * * Registers changed: none ************************************************ SetPixel *clamp pixels for safety? todo: or add outer clamping rountine cmpx #320 bge pixexit out of bounds cmpy #200 bge pixexit out of bounds pshs x,y,d save these tfr y,d want 160*y todo - faster with shift and add tricks? lda #160 mul addd vidstart pshs d save this tfr x,d lsra divide by two to get byte offset rorb addd ,s add back rest tfr d,y final byte pointer in y puls d restore stack lda ,s get pixel value anda #$0F clamp ldb 3,s get x low nibble andb #1 beq even ldb #$F0 mask bra write even ldb #$0F mask lsla shift to high nibble lsla lsla lsla write andb ,y mask out stb ,y ora ,y write pixel sta ,y puls x,y,d restore from stack pixexit rts ************************************************ * Disable/Enable interrupts ************************************************ DisableInterrupts orcc #$50 disable interrupts rts EnableInterrupts andcc #$AF enable interrupts rts ************************************************ * Set graphics memory - sets 32K in 0x8000-0xFFFF * from MMU pages 0x60000 on up * interrupts should be disabled to use this * ************************************************ SetGraphicsMem * set graphics screen to $2000-$3FFF ldd #$8000 std vidstart lda #48 first block of graphics at $60000 ldx #$FFA4 mapped to $8000-$9FFF, then more ldb #4 ! sta ,x+ inca decb bne < *write here * ldx #$2000 * clrb *! stb ,x+ * incb * bne < * lda #$39 restore normal memory * sta $FFA1 mapped to $2000 rts ************************************************ * Set a graphics mode 320x200x16 color mode * from GIME docs: HR2 HR1 HR0 CR1 CR0 * 1 1 1 1 0 320 pixels, 16 colors * TODO REGX contains 16 bit address for video start, aligned on 8 byte boundary ************************************************ SetGr320 lda #%01001100 Coco3, MMU on, DRAM constant, standard SCS 16K internal 16K external ROM sta $FF90 lda #%10000000 sta $FF98 graphics mode, 60 hz, normal burst lda #%00111110 width 320, height 200 sta $FF99 clr $FF9C low 4 bits are vertical scroll ldd #$C000 first block of graphics screen at $60000 / 8 std $FF9D vidstart offset clr $FF9F clear horiz scroll jsr SetGraphicsMem set the memory pages ldx #$FFB0 set some palette entries ldy #palette ldb 15 ! lda ,y+ sta ,x+ decb bne < rts palette fcb %00000000,%00000111,%00111000,%00111111 fcb %00000100,%00100000,%00100100 fcb %00000010,%00010000,%00010010 fcb %00000001,%00001000,%00001001 fcb %00110110,%00101101,%00011011 ************************************************ * Set a palette entry (RGB Monitor mode - todo - map to TV mode also) * REGA is color bits (0,0,RH,GH,BH,RL,GL,BL) * REGB is color index to set 0-15 * * ************************************************ SetPaletteEntry andb #15 ldx #$FFB0 abx sta ,x rts ************************************************ * Set a graphics mode ************************************************ SetPMODE4 clr $ffc0 clr $ffc3 clr $ffc5 gives RG6 (PMODE4) lda $ff sta $ff22 rts ************************************************ * Set text mode ************************************************ TextMode clr $ffc0 clr $ffc2 clr $ffc4 gives text mode lda #8 sta $ff22 rts ************************************************ * Mandelbrot point computation * REGX x coord 0-319 * REGY y coord 0-199 * returns REGD is number of color value 0 on up * Registers changed: REGD ************************************************ MandelX fcb 0,0,0,0 current iterate MandelY fcb 0,0,0,0 MandelX2 fcb 0,0,0,0 square of iterate MandelY2 fcb 0,0,0,0 MandelX0 fcb 0,0,0,0 point to add each pass MandelY0 fcb 0,0,0,0 MandelTemp fcb 0,0,0,0 temp space MandelIt fcb 0,0 iteration count MandelItMx fcb 4,0 max iter iteration count 1024 * some constants for computations MandelXS fcb 2,104,83,118 (3/319)*256 in FP MandelYS fcb 2,146,167,60 (2/199)*256 in FP FPNegFour fcb $FC,0,0,0 -4 in FP FPNegTwo fcb $FE,0,0,0 -2 in FP FPNegOne fcb $FF,0,0,0 -1 in FP FPTwo fcb $2,0,0,0 2 in FP * mandelbrot drawn over [-2,1]x[-1,1] ComputeMandelbrotPoint pshs u,y,x * x = x0 = x co-ordinate of pixel * y = y0 = y co-ordinate of pixel ldx #MandelX zero out these lbsr FPClear ldx #MandelY lbsr FPClear ldx ,s get original x ldu #MandelX save coords/256 stx ,u ldu #MandelY sty ,u ldx #MandelX ldy #MandelXS xscale [0-319]->[0,3] ldu #MandelX0 lbsr FPMul tfr u,x ldy #FPNegTwo ldu #MandelX lbsr FPAdd x now in [-2,1] ldx #MandelX ldy #MandelX0 lbsr FPCopy x and x0 now initialized ldy #MandelY ldx #MandelYS yscale [0-199]->[0,2] ldu #MandelY0 lbsr FPMul tfr u,x ldy #FPNegOne ldu #MandelY lbsr FPAdd y now in [-1,1] ldx #MandelY ldy #MandelY0 lbsr FPCopy y and y0 now initialized * x2 = x*x * y2 = y*y ldx #MandelX initialize the squares - todo - compact with below tfr x,y ldu #MandelX2 lbsr FPMul ldx #MandelY tfr x,y ldu #MandelY2 lbsr FPMul * iteration = 0 * maxiteration = 1000 ldx #MandelIt ldd #0 std ,x ldx #MandelItMx ldd #50 todo- bigger? std ,x * while ( x2 + y2 < (2*2) AND iteration < maxiteration ) { mloop ldx #MandelX2 ldy #MandelY2 ldu #MandelTemp lbsr FPAdd tfr u,x ldy #FPNegFour todo - can check this with highest FP byte lbsr FPAdd lda ,u get highest bit, see if positive bita #$80 beq mdone jump out ldy #MandelItMx ldd MandelIt cmpd ,y bge mdone if too many iterations, bail out * iteration = iteration + 1 addd #1 *incd todo - 6309 std MandelIt * y = 2*x*y + y0 ldx #MandelX ldy #MandelY ldu #MandelTemp lbsr FPMul tfr u,x ldy #FPTwo ldu #MandelY lbsr FPMul todo- make a mul by two routine - faster tfr u,x ldy #MandelY0 lbsr FPAdd * x = x2 - y2 + x0 ldx #MandelY2 lbsr FPNeg ldy #MandelX2 ldu #MandelX lbsr FPAdd tfr u,x ldy #MandelX0 lbsr FPAdd * x2 = x*x * y2 = y*y ldx #MandelX tfr x,y ldu #MandelX2 lbsr FPMul ldx #MandelY tfr x,y ldu #MandelY2 lbsr FPMul lbra mloop * if ( iteration == maxiteration ) * colour = black * else * colour = iteration mdone ldd MandelIt cmpd MandelItMx bne colorok ldd #0 colorok puls x,y,u rts ************************************************ * Fixed point rountines - * values stored as 2's complement, 8.24 mode, 4 bytes ************************************************ ************************************************ * FPAdd - Fixed point Add * REGX - point to operand1 * REGY - point to operand2 * REGU - point to answer * all registers are allowed to point to same spots * Registers changed: none ************************************************ FPAdd pshs a lda 3,x Least sig byte adda 3,y sta 3,u lda 2,x adca 2,y sta 2,u lda 1,x adca 1,y sta 1,u lda ,x most sig byte adca ,y sta ,u puls a rts ************************************************ * FPClear - zero out fp value * REGX - point to operand * Registers changed: none ************************************************ FPClear clr ,x+ clr ,x+ clr ,x+ clr ,x+ leax -4,x rts ************************************************ * FPCopy - copy one value to another * REGX - point to source * REGY - point to dest * Registers changed: none ************************************************ FPCopy pshs a lda ,x+ sta ,y+ lda ,x+ sta ,y+ lda ,x+ sta ,y+ lda ,x sta ,y leax -3,x leay -3,y puls a rts ************************************************ * FPNeg - Fixed point negate * REGX - point to operand * Registers changed: none ************************************************ FPNeg pshs y,u com 3,x 2's complement, complement and add one com 2,x com 1,x com ,x ldy #FPEps tfr x,u dest same bsr FPAdd puls y,u rts FPEps fcb 0,0,0,1 8.24 fixed point smallest positive value ************************************************ * FPMulu - Fixed point multiply of positive numbers * REGX - point to operand1 * REGY - point to operand2 * REGU - point to answer * all registers are allowed to point to same spots * Registers changed: none ************************************************ FPMulu pshs u,y,x,b,a order PC,U,Y,X,DP,B,A,CC low byte first, S pre-decremented ldu #FPTempU temp storage ldd #0 todo - clrd is a 6309 only opcode - how to make ccasm warn me? std ,u++ zero temp storage std ,u++ std ,u++ std ,u ldd #$0303 pshs d x pos byte, y pos byte counter, each goes 3 down to 0 loop ldb ,s x counter lda 1,s y counter ldx 4,s x base ldy 6,s y base ldu #FPTempU abx x byte to mult leay a,y y byte to mult adda ,s total offset space leau a,u where to write answer lda ,x multiply the bytes ldb ,y mul addd ,u store in location std ,u carry lda ,-u prepare to add with carry to prev byte cmpu #FPTempU add carries to top of temp blt next no more byters left adca #0 sta ,u bcs carry more carry to do next dec 1,s decrement two counters, fall through when done bge loop ldb #3 stb 1,s dec ,s bge loop puls d clean stack ldx #FPTempU+1 answer is shifted off one ldu 6,s original u value for answer ldd ,x++ copy 4 bytes std ,u++ ldd ,x++ std ,u++ puls u,y,x,b,a rts FPTempU fcb 1,2,3,4,5,6,7,8 eight bytes temp space ************************************************ * FPMul - Fixed point multiply * REGX - point to operand1 * REGY - point to operand2 * REGU - point to answer * Registers changed: none ************************************************ FPMul pshs y,x,b,a lda ,x get signs ldb ,y get signs * if x < 0 or x==y or x==u then copy x to new location bita #$80 check sign on x bne movex cmpx 4,s see if x and y point to same space beq movex cmpx 6,s see if x and u point to same space bne xdone movex ldy #FPTemp1 move x to another spot lbsr FPCopy get copy ldx #FPTemp1 point here bita #$80 beq xdone lbsr FPNeg negate this copy xdone ldy 4,s get y bitb #$80 check sign on y bne movey cmpy 2,s see if x and y point to same space beq movey cmpy 6,s see if x and u point to same space bne ydone movey pshs x save this for copy tfr y,x source ldy #FPTemp2 dest lbsr FPCopy copy puls x restore bitb #$80 check sign on y beq ydone jump if nonnegative exg x,y save regs lbsr FPNeg negate y value exg x,y restore regs ydone lbsr FPMulu both positive now, so mult them pshs b eora ,s compute final sign puls b bita #$80 beq anspos tfr u,x lbsr FPNeg negate answer anspos puls y,x,b,a rts FPTemp1 fcb 1,2,3,4,5,6,7,8 eight bytes temp space FPTemp2 fcb 1,2,3,4,5,6,7,8 eight bytes temp space end start * end - Mandelbrot.asm