;; ======================================================================== ;; ;; Lines Demo ;; ;; ;; ;; This demo draws 8 non-parallel lines that go from the top of the screen ;; ;; to the bottom at pseudo random rates. The lines are drawn as bitmaps ;; ;; from a pool of 48 cards that get reallocated in LRU order. If a given ;; ;; card gets recycled, its old instance on the display gets replaced with ;; ;; a blank. ;; ;; ======================================================================== ;; INCLUDE "../macro/cart.mac" CFGVAR "author" = "Joe Zbiciak" CFGVAR "name" = "SDK1600 - Bitmapped Lines Demo (2010) (JZ)" CFGVAR "short_name" = "Lines Demo" CFGVAR "year" = 2010 CFGVAR "license" = "CC CC0 v1.0" CFGVAR "jlp" = 0 ;; ======================================================================== ;; ;; ROM Header ;; ;; ======================================================================== ;; ROMSETUP 16K, 2010, "Lines Demo", START, 32 ;; ======================================================================== ;; ;; Global variables and settings. ;; ;; ======================================================================== ;; BPOOL EQU 48 MAXLN EQU 8 WORDVAR RSEED BYTEARRAY REVMAP, BPOOL BYTEARRAY LRULIST, 2*BPOOL ; Least Recently Used list BYTEVAR LRUHEAD ; Most Recently Used GRAM tile BYTEVAR LRUTAIL ; Least Recently Used GRAM tile BYTEVAR CLRGCNT BYTEARRAY CLRGLST, MAXLN ; GRAM slots to clear BYTEVAR CLRBCNT BYTEARRAY CLRBLST, MAXLN ; BTAB locations to clear BYTEVAR GRAMUCNT WORDARRAY GRAMUPD, 2*MAXLN ; GRAM slots to update BYTEVAR BTABUCNT WORDARRAY BTABUPD, 2*MAXLN ; BTAB locations to update BYTEVAR VBLFLAG ; VBlank occurred counter. WORDARRAY LINEPOS, 2*MAXLN ; Positions of the incoming lines BYTEARRAY LINEVEL, 2*MAXLN ; Velocities of the incoming lines ;; ======================================================================== ;; ;; Library includes ;; ;; ======================================================================== ;; INCLUDE "../library/randfast.asm" SHRTBL PROC @@_ SET 0 REPEAT 16 DECLE 1 SHL (15 - @@_) @@_ SET @@_ + 1 ENDR ENDP ;; ------------------------------------------------------------------------ ;; ;; BMPISR ;; ;; ;; ;; This is where all the magic happens, updating the GRAM and BTAB based ;; ;; on the various queues that got set up outside the ISR. ;; ;; ------------------------------------------------------------------------ ;; BMPISR PROC MVO R0, $20 ; \_ enable display MVI $21, R0 ; / MVI VBLFLAG, R0 ; \ INCR R0 ; |- Signal ISR completion. MVO R0, VBLFLAG ; / ;; ---------------------------------------------------------------- ;; ;; Clear out cards that got re-allocated. ;; ;; ---------------------------------------------------------------- ;; MVI CLRGCNT, R1 TSTR R1 BEQ @@skipcg CLRR R0 MVII #CLRGLST, R4 @@cgloop: MVI@ R4, R2 SLL R2, 2 SLL R2, 1 ADDI #$3800, R2 MOVR R2, R5 REPEAT 8 MVO@ R0, R5 ENDR DECR R1 BNEQ @@cgloop @@skipcg ;; ---------------------------------------------------------------- ;; ;; Update GRAM pixels for active lines ;; ;; ---------------------------------------------------------------- ;; MVI GRAMUCNT, R2 MVII #GRAMUPD, R4 TSTR R2 BEQ @@skipgupd @@gupd: MVI@ R4, R3 ; get address to change MVI@ R4, R0 ; get pixel to change MOVR R0, R1 COMR R1 AND@ R3, R1 ; \ XORR R0, R1 ; |- Merge in new pixel. MVO@ R1, R3 ; / DECR R2 BNEQ @@gupd @@skipgupd ;; ---------------------------------------------------------------- ;; ;; Clear BACKTAB locations that need to be cleared. ;; ;; ---------------------------------------------------------------- ;; MVI CLRBCNT, R1 MVII #CLRBLST, R4 TSTR R1 BEQ @@skipcb ;; R2 should be 0 here. @@cbloop: MVI@ R4, R3 ; Get BTAB offset ADDI #$200, R3 ; Offset => Screen address MVO@ R2, R3 ; Clear BTAB location DECR R1 BNEQ @@cbloop @@skipcb ;; ---------------------------------------------------------------- ;; ;; Update BACKTAB cards for reallocated cards. ;; ;; ---------------------------------------------------------------- ;; MVI BTABUCNT, R1 MVII #BTABUPD, R4 TSTR R1 BEQ @@skipbupd @@bupd: MVI@ R4, R3 ; get location of update MVI@ R4, R0 ; get value to write MVO@ R0, R3 ; make update DECR R1 BNEQ @@bupd @@skipbupd ;; ---------------------------------------------------------------- ;; ;; Reset update queues ;; ;; ---------------------------------------------------------------- ;; ;; R2 should be 0 here. MVO R2, CLRGCNT MVO R2, CLRBCNT MVO R2, GRAMUCNT MVO R2, BTABUCNT ;; ---------------------------------------------------------------- ;; ;; Increment VBlank flag. ;; ;; ---------------------------------------------------------------- ;; MVI VBLFLAG, R0 INCR R0 MVO R0, VBLFLAG B $1014 ENDP ;; ------------------------------------------------------------------------ ;; ;; BMPINIT ;; ;; ;; ;; This initializes all the bitmap-related structures. For now, all of ;; ;; the cards in the pool get allocated to the first couple rows of the ;; ;; screen, filled with blanks. This routine is intended to be called ;; ;; as an ISR. It'll set up BMPISR as the ISR when it's done. ;; ;; ------------------------------------------------------------------------ ;; BMPINIT PROC ;; ---------------------------------------------------------------- ;; ;; Set the STIC to color stack mode w/ black background, no bord. ;; ;; extension, etc., and no MOBs on display. ;; ;; ---------------------------------------------------------------- ;; MVI $21, R0 ; Color stack mode CLRR R0 MVII #$28, R4 ; \ MVO@ R0, R4 ; | MVO@ R0, R4 ; |- Black color stack, border MVO@ R0, R4 ; | MVO@ R0, R4 ; | MVO@ R0, R4 ; / MVII #$30, R4 ; \ MVO@ R0, R4 ; |_ No hdly/vdly/bord extension MVO@ R0, R4 ; | MVO@ R0, R4 ; / CLRR R4 MVII #32, R1 @@mobclr: MVO@ R0, R4 ; No MOBs DECR R1 BNEQ @@mobclr ;; ---------------------------------------------------------------- ;; ;; Reset the first BPOOL GRAM cards. ;; ;; ---------------------------------------------------------------- ;; MVII #$3800, R4 MVII #BPOOL, R1 CLRR R0 @@gclr: REPEAT 8 MVO@ R0, R4 ENDR DECR R1 BNEQ @@gclr ;; ---------------------------------------------------------------- ;; ;; Fill the first BPOOL BTAB positions with the first BPOOL GRAM ;; ;; cards. Also, set up the reverse map. ;; ;; ---------------------------------------------------------------- ;; MVII #$807, R0 MVII #BPOOL, R1 MVII #$200, R4 MVII #REVMAP, R5 @@bfill: MVO@ R4, R5 ; initialize reverse map MVO@ R0, R4 ; put card in BTAB ADDI #8, R0 ; update pattern for BTAB DECR R1 ; wash, rinse, repeat BNEQ @@bfill ;; ---------------------------------------------------------------- ;; ;; Clear the rest of BTAB. ;; ;; ---------------------------------------------------------------- ;; CLRR R0 MVII #240-BPOOL, R1 @@bclr: MVO@ R0, R4 DECR R1 BNEQ @@bclr ;; ---------------------------------------------------------------- ;; ;; Initialize the LRU so that 0 is MRU, 47 is LRU. ;; ;; Each LRU has a prev/next pointer, except first and last ;; ;; entries, for which prev (first) and next (last) aren't ;; ;; meaningful. For those, any old garbage will do. ;; ;; ---------------------------------------------------------------- ;; MVII #$FFFF, R0 ; prev ptr MVII #$0001, R1 ; next ptr MVII #LRULIST, R4 MVII #BPOOL, R2 @@lruinit: MVO@ R0, R4 ; write prev ptr MVO@ R1, R4 ; write next ptr INCR R0 INCR R1 DECR R2 BNEQ @@lruinit MVII #BPOOL-1, R0 ; Set LRU to last element. MVO R0, LRUTAIL MVO R2, LRUHEAD ;; ---------------------------------------------------------------- ;; ;; Clear all the update queues. ;; ;; ---------------------------------------------------------------- ;; ;; R2 should be 0 here MVO R2, CLRGCNT MVO R2, CLRBCNT MVO R2, GRAMUCNT MVO R2, BTABUCNT ;; ---------------------------------------------------------------- ;; ;; Set up bitmap ISR as our current ISR. ;; ;; ---------------------------------------------------------------- ;; MVII #BMPISR, R0 MVO R0, $100 SWAP R0 MVO R0, $101 B $1014 ENDP ;; ------------------------------------------------------------------------ ;; ;; PLOT ;; ;; ;; ;; Given an X/Y coordinate, schedule a pixel to be plotted. This does ;; ;; all the work of figuring out the card that needs updating allocating ;; ;; a card if necessary, etc. etc. ;; ;; ;; ;; INPUTS ;; ;; R0 X coordinate ;; ;; R1 Y coordinate ;; ;; ;; ;; ------------------------------------------------------------------------ ;; BYTEVAR XTMP BYTEVAR YTMP WORDVAR GTMP PLOT PROC PSHR R5 NOP MVO R0, XTMP MVO R1, YTMP ;; ---------------------------------------------------------------- ;; ;; Look up what GRAM card we're updating, or allocate one. ;; ;; ---------------------------------------------------------------- ;; SLR R0, 2 ; \_ X / 8 to get card column SLR R0, 1 ; / ANDI #$FFF8, R1 ; Clamp Y to card row => Row * 8 SLL R1, 1 ; Row * 16 ADDR R1, R0 ; SLR R1, 2 ; Row * 4 ADDR R0, R1 ADDI #$200, R1 ; BTAB index CMPI #$2F0, R1 ; Refuse to plot off-screen pixels BC @@leave MVI@ R1, R0 ; Get BTAB word ANDI #$FF8, R0 CMPI #$800, R0 ; \ Determine if we need to alloc BLT @@alloc_gram ; |_ a new GRAM card, or just update CMPI #$800 + BPOOL*8, R0 ; | the LRU. BLT @@update_lru ; / @@alloc_gram: ;; R0 = garbage ;; R1 = BTAB address ($200 - $2EF) MVI LRUTAIL, R2 ; Who's the unlucky sod? ; Reverse map to BTAB MVII #REVMAP, R3 ; \ ADDR R2, R3 ; |- use the reverse map to find MVI@ R3, R5 ; / reaped card so we can clear it MVO@ R1, R3 ; Update reverse map ; Schedule removing old card from BTAB MVI CLRBCNT, R4 ; \ INCR R4 ; | MVO R4, CLRBCNT ; |- Add BTAB card to BTAB clear list ADDI #CLRBLST-1, R4 ; | MVO@ R5, R4 ; / ; Mark card as reaped in BTAB ADDI #$200, R5 MVI@ R5, R4 ADDI #$200, R4 DECR R5 MVO@ R4, R5 ; Schedule removing old tile from GRAM MVI CLRGCNT, R4 INCR R4 MVO R4, CLRGCNT ADDI #CLRGLST-1, R4 MVO@ R2, R4 ; Turn newly allocated GRAM card # into GRAM offset MOVR R2, R0 ; \ SLL R0, 2 ; |_ Turn GRAM card # into GRAM SLL R0, 1 ; | offset. ADDI #$800, R0 ; / ; Merge in display attributes MOVR R0, R5 ; \_ XXX: For now draw all the pixels ADDI #$7, R5 ; / in white. ; Schedule drawing new card in BTAB MVI BTABUCNT, R3 ; \ INCR R3 ; | MVO R3, BTABUCNT; |- Add new card to BTAB update list SLL R3 ; | ADDI #BTABUPD-2, R3 ; | MVO@ R1, R3 ; | (screen address) INCR R3 ; | MVO@ R5, R3 ; / (display word) ; Mark it allocated but leave it black in BTAB MVO@ R0, R1 ; Update the LRU list so that newly alloc'd card is MRU MVI LRUHEAD, R1 ; \ MOVR R1, R4 ; |_ Make the new MRU the prev of the SLL R1 ; | old MRU ADDI #LRULIST, R1 ; | MVO@ R2, R1 ; / MVO R2, LRUHEAD ; Make us the new MRU MOVR R2, R3 ; \ SLL R3 ; | ADDI #LRULIST, R3 ; |- Make new MRU's old prev the new MVI@ R3, R1 ; | LRU. MVO R1, LRUTAIL ; / INCR R3 ; \__ ...and make old MRU the new MVO@ R4, R3 ; / MRU's next B @@done_alloc @@update_lru: ;; R0 = ((GRAM card #) SHL 3) + $800 ;; R1 = BTAB address ($200 - $2EF) MOVR R0, R2 ANDI #$7F8, R2 SLR R2, 2 ; MVII #LRULIST, R4 ; \_ R4 points to this card's LRU ADDR R2, R4 ; / entry SLR R2, 1 ; R2 = GRAM card # MVI LRUHEAD, R1 ; \ CMPR R1, R2 ; |- Skip LRU update if already MRU BEQ @@skip_lru ; / MOVR R1, R5 ; remember old MRU SLL R1, 1 ; \ ADDI #LRULIST, R1 ; |- Make new MRU the prev of MVO@ R2, R1 ; / the old MRU MVO R2, LRUHEAD ; Make R2 the new MRU MVI@ R4, R1 ; \__ Get prev/next pointers for MVI@ R4, R3 ; / new MRU DECR R4 MVO@ R5, R4 ; Make old MRU next of new MRU CMP LRUTAIL, R2 ; Were we the current tail? BNEQ @@not_tail MVO R1, LRUTAIL ; Yes: Make curr->prev new tail B @@done_alloc @@not_tail ; No: Unhook us from middle of list MOVR R1, R4 SLL R1 ; \ ADDI #LRULIST+1, R1 ; |- curr->prev->next = curr->next MVO@ R3, R1 ; / SLL R3 ; \ ADDI #LRULIST, R3 ; |- curr->next->prev = curr->prev MVO@ R4, R3 ; / @@skip_lru: @@done_alloc: ;; ---------------------------------------------------------------- ;; ;; Now that a GRAM card is looked up or allocated, convert to a ;; ;; pointer to the row to update and an update mask. ;; ;; ;; ;; R0 should have the GRAM offset. ;; ;; ---------------------------------------------------------------- ;; MVI YTMP, R1 ; Get Y offset ANDI #7, R1 ; Get just the 3 LSBs for row offset ADDR R1, R0 ; Add it to GRAM offset ADDI #$3000, R0 ; Turn it to GRAM pointer MVI GRAMUCNT, R1 INCR R1 MVO R1, GRAMUCNT SLL R1 ADDI #GRAMUPD-2, R1 MVO@ R0, R1 ; Write out GRAM update address INCR R1 MVI XTMP, R2 ANDI #7, R2 ADDI #SHRTBL+8, R2 MVI@ R2, R2 MVO@ R2, R1 ; Write out GRAM update mask. @@leave: PULR PC ENDP ;; ------------------------------------------------------------------------ ;; ;; WAITVBL ;; ;; ------------------------------------------------------------------------ ;; MACRO WAITVBL MVI VBLFLAG, R0 CMP VBLFLAG, R0 BEQ $ - 2 ENDM ;; ------------------------------------------------------------------------ ;; ;; START ;; ;; ------------------------------------------------------------------------ ;; START PROC EIS ;; ---------------------------------------------------------------- ;; ;; Start all the lines off the bottom of the screen. ;; ;; ---------------------------------------------------------------- ;; MVII #$7FFF, R0 MVII #LINEPOS, R4 MVII #MAXLN*2, R1 @@lineinit: MVO@ R0, R4 DECR R1 BNEQ @@lineinit ;; ---------------------------------------------------------------- ;; ;; Initialize the bitmap engine. ;; ;; ---------------------------------------------------------------- ;; MVII #BMPINIT, R0 MVO R0, $100 SWAP R0 MVO R0, $101 ;; ---------------------------------------------------------------- ;; ;; Main loop: ;; ;; -- Advance all the lines. If any line goes off-screen, ;; ;; re-initialize it with a random X position and vel. ;; ;; -- Plot the lines whose integral position changed. ;; ;; (Ok, for now just plot them all always.) ;; ;; -- Wait for VBlank. ;; ;; ---------------------------------------------------------------- ;; @@main_loop: WAITVBL MVII #LINEPOS, R4 MVII #LINEVEL, R5 MVII #MAXLN, R2 @@vel_loop: MVI@ R5, R0 SLL R0 ADD@ R4, R0 SUBI #$100, R0 CMPI #160 SHL 8, R0 BC @@realloc_x DECR R4 MVO@ R0, R4 MVI@ R5, R0 ADD@ R4, R0 CMPI #96 SHL 8, R0 BNC @@ok @@realloc_y: DECR R4 DECR R5 @@realloc_x: DECR R4 DECR R5 PSHR R5 CALL RANDFAST CALL RANDFAST CALL RANDFAST CALL RANDFAST ANDI #$7FFF, R0 ADDI #$1000, R0 MVO@ R0, R4 ; new random X (16 .. 143) CLRR R0 MVO@ R0, R4 ; Y == 0 MOVR R4, R3 PULR R4 ; Get velocity pointer CALL RANDFAST CALL RANDFAST MVO@ R0, R4 ; new random X velocity CALL RANDFAST CALL RANDFAST ANDI #$3F, R0 ADDI #$C0, R0 MVO@ R0, R4 ; new, positive Y velocity MOVR R4, R5 ; put vel pointer back MOVR R3, R4 ; put pos pointer back B @@next @@ok DECR R4 MVO@ R0, R4 @@next: DECR R2 BNEQ @@vel_loop MVII #LINEPOS, R4 MVII #MAXLN, R2 @@plot_loop: MVI@ R4, R0 MVI@ R4, R1 SWAP R0 SWAP R1 ANDI #$FF, R0 ANDI #$FF, R1 PSHR R4 PSHR R2 JSRD R5, PLOT EIS PULR R2 PULR R4 DECR R2 BNEQ @@plot_loop B @@main_loop ENDP