diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | dla.s | 269 | ||||
-rw-r--r-- | drunkwalk.s | 125 | ||||
-rw-r--r-- | mkdlatbl.pl | 9 | ||||
-rw-r--r-- | render.s | 94 |
5 files changed, 262 insertions, 237 deletions
@@ -4,7 +4,7 @@ PERL = perl all: dla.xex -dla.xex: dla.s io.s dlatbl.s xex.inc printint.s +dla.xex: dla.s io.s dlatbl.s xex.inc printint.s render.s drunkwalk.s $(CL65) $(CL65FLAGS) -l dla.list -Ln dla.labels -t none -o dla.xex dla.s dlatbl.s: mkdlatbl.pl @@ -13,8 +13,9 @@ loadaddr = $2000 screen = $4000 ; must be on a x000 (4K) boundary screen2 = screen + $1000 ; rest of screen RAM after 4K boundary + pixarray = screen + $20 linelen = $20 ; aka 32 bytes, antic F (GR.8) in narrow mode. - maxlines = $C0 ; 192 lines of display + maxlines = $aa ; 170 lines of display screenbytes = maxlines * linelen dl_len = 202 ; remember to update this if you modify the display list! dlist = screen - dl_len @@ -34,6 +35,7 @@ pixmask: .res 1 ; ditto. cursor_x: .res 1 ; cursor x/y are args to plot/unplot/locate cursor_y: .res 1 pixptr2: .res 2 ; used by drunkwalk +screenptr = pixptr2 circlesize: .res 1 ; 0 to 3 @@ -156,6 +158,7 @@ dfltseed: generate: ;;; start of generate() jsr initscreen + ; wait for shadow regs to get updated... lda RTCLOK+2 wl: @@ -240,6 +243,7 @@ checkmaxparts: bne next_particle main_done: + jsr render lda #0 sta CRITIC sta COLOR2 @@ -404,7 +408,7 @@ isloop0: bne isloop0 ; next, clear screen memory - ldx #>screenbytes ; clear this many pages + ldx #$72 ; clear this many pages tay ; 0 again isloop: sta (pixptr),y @@ -432,224 +436,20 @@ isloop: sta COLCRS+1 rts -;;; Subroutine: plotsetup -;;; - set pixptr to point to screen memory at cursor_y. -;;; - set pixmask to the mask for cursor_x. -;;; - set Y reg to the byte offset for cursor_x. -;;; - returns with cursor_x in X reg, pixmask in A reg too. -;;; Called by plot, unplot, and locate. -plotsetup: + .include "render.s" + +plot: ldx cursor_y lda lineaddrs_l,x sta pixptr lda lineaddrs_h,x sta pixptr+1 - - ldx cursor_x - ldy xoffsets,x - lda xmasks,x - sta pixmask - - rts - -;;; Subroutine: plot -;;; plots a pixel at (cursor_x, cursor_y) -plot: - jsr plotsetup - lda (pixptr),y - ora pixmask + ldy cursor_x + lda #1 sta (pixptr),y rts -oob: - rts -;;; Subroutine: drunkwalk -;;; Walk the point around randomly until it either is -;;; adjacent to a set pixel or goes out of bounds. -;;; Return with Z=0 if out of bounds, Z=1 if it hit a pixel. -;;; This is the innermost loop, so it should be as optimized as -;;; possible (we're not there yet). -drunkwalk: - ; X holds the X coord the whole time, only needs to be loaded on entry. - ; preload pixptr, too. - ldx part_x ; 3 - ldy part_y ; 3 - lda lineaddrs_l,y ; 5 - sta pixptr ; 3 - lda lineaddrs_h,y ; 5 - sta pixptr+1 ; 3 - ; 4 code paths: TODO: count - ; note that part_x and part_y are *never* zero; all the bne's here - ; are "branch always". - ; all the "cmp #0" here get their operands modified by set_limits. -dwloop: - ldy part_y ; 3 - bit RANDOM ; 4 ; use top 2 bits (probably more random, definitely faster) - bmi lr ; 2/3 - bvc down ; 2/3 - dey ; 2 ; N=1 V=1 up -selfmod_ymin = * + 1 - cpy #0 ; 2 - beq oob ; 2 - jmp check_lru -down: - iny ; 2 ; N=1 V=0 down -selfmod_ymax = * + 1 - cpy #0 ; 2 - beq oob ; 2 - jmp check_lrd -lr: - bvc right ; 2/3 - dex ; 3 ; N=0 V=1 left -selfmod_xmin = * + 1 - cpx #0 ; 2 - beq oob ; 2 - ldy xoffsets-1,x ; 4 ; moved left, check left X neighbor only. - lda xmasks-1,x ; 4 ; right X neighbor definitely empty, because - and (pixptr),y ; 5 ; we just moved out of that cell. - bne stick ; 2/3 - beq check_ud ; 3 ; still have to check Y (up/down) neighbors. -right: - inx ; 3 ; N=0 V=0 right -selfmod_xmax = * + 1 - cpx #0 ; 2 - beq oob ; 2 - ldy xoffsets+1,x ; 4 ; as above, moved right, check right X neighbor only. - lda xmasks+1,x ; 4 - and (pixptr),y ; 5 - bne stick ; 2/3 - ; fall through to check_ud - -check_ud: - ; this happens when the pixel moved left or right. - ; (0,-1) - ; subtract 32 (one line) from the pointer. one cycle faster - ; than reloading from lineaddrs_l/h table. - lda pixptr ; 3 - sec ; 2 - sbc #$20 ; 2 - sta pixptr2 ; 3 - lda pixptr+1 ; 3 - sbc #0 ; 2 - sta pixptr2+1 ; 3 - ;ldx part_x ; X already has this from before - ldy xoffsets,x ; 4 - lda xmasks,x ; 4 - sta pixmask ; 3 - and (pixptr2),y ; 5 - bne stick ; 2/3 - ; (0,1) - tya ; 2 - ora #$40 ; 2 ; add 64, AKA 2 screen lines - tay ; 2 - lda (pixptr2),y ; 5 - and pixmask ; 3 - bne stick ; 2/3 - jmp dwloop ; 3 ; too far for a branch - -stick: ; we always get here with Z flag clear - stx part_x ; only update part_x at exit. - rts - - ; this happens when the pixel moved up. -check_lru: - sty part_y ; 3 - lda lineaddrs_l,y ; 5 - sta pixptr ; 3 - lda lineaddrs_h,y ; 5 - sta pixptr+1 ; 3 - - ; 3/4 of the time, we can use a faster code path, check - ; (-1,0) and (1,0) at the same time. this happens only when - ; both pixels lie within the same byte. - ;ldx part_x ; X already has this from before - lda fastmasks,x ; 4 - beq slow_x_lru ; 2/3 - ldy xoffsets,x ; 4 - and (pixptr),y ; 5 - bne stick ; 2/3 - beq check_u ; 2/3 -slow_x_lru: - ; (-1,0) - ldy xoffsets-1,x ; 4 - lda xmasks-1,x ; 4 - and (pixptr),y ; 5 - bne stick ; 2/3 - ; (1,0) - ldy xoffsets+1,x ; 4 - lda xmasks+1,x ; 4 - and (pixptr),y ; 5 - bne stick ; 2/3 - -check_u: - ; (0,-1) - ; subtract 32 (one line) from the pointer. one cycle faster - ; than reloading from lineaddrs_l/h table. - lda pixptr ; 3 - sec ; 2 - sbc #$20 ; 2 - sta pixptr2 ; 3 - lda pixptr+1 ; 3 - sbc #0 ; 2 - sta pixptr2+1 ; 3 - ;ldx part_x ; X already has this from before - ldy xoffsets,x ; 4 - lda xmasks,x ; 4 - ;sta pixmask ; 3 - and (pixptr2),y ; 5 - bne stick ; 2/3 - jmp dwloop ; 3 ; too far for a branch - - - ; this happens when the pixel moved down. -check_lrd: - sty part_y ; 3 - lda lineaddrs_l,y ; 5 - sta pixptr ; 3 - lda lineaddrs_h,y ; 5 - sta pixptr+1 ; 3 - - ; 3/4 of the time, we can use a faster code path, check - ; (-1,0) and (1,0) at the same time. this happens only when - ; both pixels lie within the same byte. - ;ldx part_x ; X already has this from before - lda fastmasks,x ; 4 - beq slow_x_lrd ; 2/3 - ldy xoffsets,x ; 4 - and (pixptr),y ; 5 - bne stick ; 2/3 - beq check_d ; 2/3 -slow_x_lrd: - ; (-1,0) - ldy xoffsets-1,x ; 4 - lda xmasks-1,x ; 4 - and (pixptr),y ; 5 - bne stick ; 2/3 - ; (1,0) - ldy xoffsets+1,x ; 4 - lda xmasks+1,x ; 4 - and (pixptr),y ; 5 - bne stick ; 2/3 - -check_d: - ; (0,-1) - lda pixptr ; 3 - clc ; 2 - adc #$20 ; 2 - sta pixptr2 ; 3 - lda pixptr+1 ; 3 - adc #0 ; 2 - sta pixptr2+1 ; 3 - ;ldx part_x ; X already has this from before - ldy xoffsets,x ; 4 - lda xmasks,x ; 4 - ;sta pixmask ; 3 - and (pixptr2),y ; 5 - bne stick2 ; 2/3 - jmp dwloop ; 3 ; too far for a branch -stick2: - stx part_x ; only update part_x at exit. - rts + .include "drunkwalk.s" ;;; Subroutine: drawseed ;;; dispatch to appropriate seed subroutine @@ -664,9 +464,9 @@ drawseed: ;;; Subroutine: seed_point ;;; draw initial point in center seed_point: - lda #$7f + lda #center_x sta cursor_x - lda #$5f + lda #center_y sta cursor_y jmp plot @@ -675,56 +475,56 @@ seed_point: seed_long: lda #$1 sta cursor_x - lda #$5f + lda #center_y sta cursor_y slnoop: jsr plot inc cursor_x lda cursor_x - cmp #$ff + cmp #$aa bne slnoop rts ;;; Subroutine: seed_plus ;;; plus share, made of two 20px lines intersecting in the center seed_plus: - lda #$7f + lda #center_x sta cursor_x - lda #$55 + lda #center_y-10 sta cursor_y sploop: jsr plot inc cursor_y lda cursor_y - cmp #$69 + cmp #center_y+10 bne sploop - lda #$75 + lda #center_x-10 sta cursor_x - lda #$5f + lda #center_y sta cursor_y slloop: jsr plot inc cursor_x lda cursor_x - cmp #$89 + cmp #center_x+10 bne slloop rts ;;; Subroutine: seed_4pt ;;; four points, the corners of a 20px square seed_4pt: - lda #$75 + lda #center_x-10 sta cursor_x - lda #$55 + lda #center_y-10 sta cursor_y jsr plot - lda #$68 + lda #center_y+10 sta cursor_y jsr plot - lda #$88 + lda #center_x+10 sta cursor_x jsr plot - lda #$55 + lda #center_y-10 sta cursor_y jmp plot @@ -805,7 +605,7 @@ ci_done: ; banner and saveprompt must start with a clear-screen code. banner: .byte $7d, "Diffusion Limited Aggregate",$9b - .byte "Urchlay's ASM version 0.1.3",$9b,$9b + .byte "Urchlay's ASM version 0.1.99",$9b,$9b .byte "Particle count range: 1 to 65535",$9b .byte "How many particles [",$0 @@ -852,17 +652,17 @@ seeds_h: .byte >(seed_point-1),>(seed_plus-1),>(seed_4pt-1),>(seed_long-1) ; code by 384 bytes, but compared to calculating the address, is ; twice as fast! lineaddrs_l: - laddr .set screen - .repeat 192 + laddr .set pixarray + .repeat 170 .byte <laddr - laddr .set laddr + $20 + laddr .set laddr + 170 .endrep lineaddrs_h: - laddr .set screen - .repeat 192 + laddr .set pixarray + .repeat 170 .byte >laddr - laddr .set laddr + $20 + laddr .set laddr + 170 .endrep ; tables to replace X coord => mask-and-offset calculations. @@ -895,6 +695,7 @@ fastmasks: xex_org dlist .byte blank8, blank8, blank8 + .byte blank8 .byte gr8 | lms .word screen .repeat 127 diff --git a/drunkwalk.s b/drunkwalk.s new file mode 100644 index 0000000..ca80475 --- /dev/null +++ b/drunkwalk.s @@ -0,0 +1,125 @@ +;;; Subroutine: drunkwalk +;;; Walk the point around randomly until it either is +;;; adjacent to a set pixel or goes out of bounds. +;;; Return with Z=0 if out of bounds, Z=1 if it hit a pixel. +;;; This is the innermost loop, so it should be as optimized as +;;; possible. +; Y and X are backwards: Y holds the X coordinate, and X holds the Y coordinate. +; Has to be, because there's no (zpind),x addressing mode. +oob: + rts + +drunkwalk: + ldy part_x ; 3 + ldx part_y ; 3 + + ; do we need this now? + lda lineaddrs_l,x ; 5 + sta pixptr ; 3 + lda lineaddrs_h,x ; 5 + sta pixptr+1 ; 3 + + bit RANDOM ; 4 ; use top 2 bits (probably more random, definitely faster) + bmi lr ; 2/3 + bvc down ; 2/3 +up: + dex ; 2 ; N=1 V=1 up +selfmod_ymin = * + 1 + cpx #0 ; 2 + beq oob ; 2 + stx part_y + jmp check_lru +down: + inx ; 2 ; N=1 V=0 down +selfmod_ymax = * + 1 + cpx #0 ; 2 + beq oob ; 2 + stx part_y + jmp check_lrd +lr: + bvc right ; 2/3 +left: + dey ; 3 ; N=0 V=1 left +selfmod_xmin = * + 1 + cpy #0 ; 2 + beq oob ; 2 + sty part_x + ; check left neighbor (we just vacated the right one) + dey + lda (pixptr),y + bne stick + iny + jmp check_ud ; 3 ; still have to check Y (up/down) neighbors. +right: + iny ; 3 ; N=0 V=0 right +selfmod_xmax = * + 1 + cpy #0 ; 2 + beq oob ; 2 + sty part_x + ; check right neighbor (we just vacated the left one) + iny + lda (pixptr),y + bne stick + dey + ; fall through to check_ud + +check_ud: + ; check up neighbor + lda lineaddrs_l-1,x + sta pixptr2 + lda lineaddrs_h-1,x + sta pixptr2+1 + lda (pixptr2),y + bne stick + + ; check down neighbor + lda lineaddrs_l+1,x + sta pixptr2 + lda lineaddrs_h+1,x + sta pixptr2+1 + lda (pixptr2),y + bne stick + jmp drunkwalk + +check_lru: + ; check left neighbor + dey + lda (pixptr),y + bne stick + iny + ; check right neighbor + iny + lda (pixptr),y + bne stick + dey + ; check up neighbor + lda lineaddrs_l-1,x + sta pixptr2 + lda lineaddrs_h-1,x + sta pixptr2+1 + lda (pixptr2),y + bne stick + jmp drunkwalk + +check_lrd: + ; check left neighbor + dey + lda (pixptr),y + bne stick + iny + ; check right neighbor + iny + lda (pixptr),y + bne stick + dey + ; check down neighbor + lda lineaddrs_l+1,x + sta pixptr2 + lda lineaddrs_h+1,x + sta pixptr2+1 + lda (pixptr2),y + bne stick + jmp drunkwalk + +stick: + rts diff --git a/mkdlatbl.pl b/mkdlatbl.pl index a941d38..37fb31b 100644 --- a/mkdlatbl.pl +++ b/mkdlatbl.pl @@ -12,8 +12,8 @@ sub bdcos { return cos($_[0] / 128 * $PI); } -$centerx = 127; -$centery = 95; +$centerx = 85; +$centery = 85; for $r (15, 30, 45, 75) { push @xmin, $centerx - ($r + 10); @@ -28,6 +28,9 @@ for $r (15, 30, 45, 75) { } } +print "center_x = $centerx\n"; +print "center_y = $centery\n"; + print "xmin:\n"; for(@xmin) { print " .byte $_\n"; @@ -35,6 +38,7 @@ for(@xmin) { print "xmax:\n"; for(@xmax) { + $_ = 169 if $_ >= 170; print " .byte $_\n"; } @@ -45,6 +49,7 @@ for(@ymin) { print "ymax:\n"; for(@ymax) { + $_ = 169 if $_ >= 170; print " .byte $_\n"; } diff --git a/render.s b/render.s new file mode 100644 index 0000000..867003e --- /dev/null +++ b/render.s @@ -0,0 +1,94 @@ +;;; Subroutine: render +;;; Convert 1px per byte array at pixarray to packed 8px/byte at screen. +; pixarray is 170x170. screen is 256x170 (TODO: fix display list). +; each screen line is: 43 blank px, 170 graphics px, 43 blank. + screenbyte = FR0 + colcount = FR0+1 + +render: + lda #<screen + sta screenptr + lda #>screen + sta screenptr+1 + lda #<pixarray + sta pixptr + lda #>pixarray + sta pixptr+1 + + ldx #0 +rline: + lda #0 + sta colcount + sta screenbyte + +; ; clear 40px on left +; ldy #4 +;lclr: +; sta (screenptr),y +; dey +; bpl lclr +; +; ; clear 40px on right +; ldy #$1b +;rclr: +; sta (screenptr),y +; iny +; cpy #$20 +; bne rclr + +; clear whole line. this is why pixarray is offset from screen by +; one screen line. + ldy #$1f +rclr: + sta (screenptr),y + dey + bpl rclr + + ; first pixels start at column 40, plus... + lda #5 + sta screenbyte + lda #$10 ; ...mask starts out 0001000, 3 more blank px on left + sta pixmask + +rpix: + ldy colcount ; ranges 0 to 169 + lda (pixptr),y + beq notset ; 0 = not set, non-zero = set + ; if we found a set pixel, set it in the bitmap + ldy screenbyte + lda (screenptr),y + ora pixmask + sta (screenptr),y +notset: + ; pixmask >>= 1; if(pixmask == 0) { pixmask = 0x80; screenbyte++; } + lsr pixmask + bne pmok + ;ror pixmask + lda #$80 + sta pixmask + inc screenbyte +pmok: + inc colcount + lda colcount + cmp #$aa + bne rpix + ; pixptr += 0xaa; + clc + adc pixptr + sta pixptr + lda pixptr+1 + adc #0 + sta pixptr+1 + ; screenptr += 0x20; + clc + lda screenptr + adc #$20 + sta screenptr + lda screenptr+1 + adc #0 + sta screenptr+1 + inx + cpx #$aa ; hit last line yet? + bne rline ; if not, go render next line. + + rts |