This is hopefully the start of a series of 7800 source code skeletons, each building on the next (or variations on a theme). I'll also put this on the mailing list for good measure.
This skeleton contains both a simple DLL builder and display list builder. Nothing fancy, no scrolling backgrounds or wrap-around, not even any error checking to make sure you don't add too many sprites to a display list. This is also not a complete program, though I've tried to include almost everything required. (I've already found one problem, I forgot the5 byte display list entry to set the write mode.)
YRES EQU 192 ; screen resolution DLHIGH EQU 16 ; display list height NUMDL EQU YRES/DLHIGH ; number of display lists DLLEN EQU NUMSPR EQU DLIFLAG DS 1 DLPTR DS 2 DLTEMP DS 1 DLIDX DS NUMDL SPRYPOS DS NUMSPR ; sprite Y postion, 0 = top SPRXPOS DS NUMSPR ; sprite X position, 0 = left SPRIDX DS NUMSPR ; sprite table index SPRPALW DS NUMSPR ; palette/width DLNULL DS 2 ; null display list DLLON DS (NUMDL+4)*3 DL00 DS DLLEN DL10 DS DLLEN DL20 DS DLLEN DL30 DS DLLEN DL40 DS DLLEN DL50 DS DLLEN DL60 DS DLLEN DL70 DS DLLEN DL80 DS DLLEN DL90 DS DLLEN DLA0 DS DLLEN DLB0 DS DLLEN DLC0 DS DLLEN DLD0 DS DLLEN DLE0 DS DLLEN ; A flag in each DLL header causes an NMI when MARIA completes DMA for the; previous zone. This can be used for a variety of things (changing colours,
; resolutions, scrolling backgrounds). Here it is just used to signal when ; MARIA is done displaying the visible screen so the display lists can be ; updated. DLLNMI INC DLIFLAG IRQRTI RTI ; Start routine (stub) START SEI ; disable IRQ CLD ; binary arithmetic LDA #$67 STA INPTCTRL ; lock in 7800 mode STA CTRL ; disable DMA LDX #$FF TXS ; set stack pointer ; Missing: Initialize MARIA/TIA/RIOT registers and zero page variables ; Build the DLL automatically. CPYDLL LDY #0 ; copy DLL from ROM to RAM STY DLNULL+1 1$ LDA DLLTOP,Y ; copy top from ROM STA DLLON,Y INY CPY #DLLBOT-DLLTOP BNE 1$ LDX #0 ; build active screen DLLs 2$ LDA #$4F ; 16 high zone, 4K (16 line) holey DMA STA DLLON,Y INY LDA DLPTRM,X STA DLLON,Y INY LDA DLPTRL,X STA DLLON,Y INY INX CPX #NUMDL BNE 2$ LDX #0 3$ LDA DLLBOT,X ; copy bottom from ROM STA DLLON,Y INY INX CPX #DLLEND-DLLBOT BNE 3$ ; Wait for VBLANK to start, turn on DMA, and fall into the display ; list builder routine. MSTAT0 BIT MSTAT ; wait for MARIA enabled BMI MSTAT0 MSTAT1 BIT MSTAT ; wait for MARIA disabled BPL MSTAT1 LDA #%01000000 ; color, DMA on, single char, black border STA CTRL ; transparent, 160res ; This is the display list builder which could be considered the kernel of ; a 7800 game. The code takes the X/Y positions (top left) of each sprite,; figures out which display list they belong it and adds a sprite header to the
; end of the list. There are several sections to this routine. ; ; DLINI zeros the end of list index ; DLCPY figures out which display list a sprite belongs in (via a ; table lookup of Ypos/16), puts the end of list index in Y, ; copies the data for each sprite into two display lists ; DLFIN writes a zero one byte beyond the end of the current display ; list. This is the null header which tells MARIA it's ; reached the end of the list. DLINI LDA #0 ; skip over background tile headers LDX #NUMDL 1$ STA DLIDX-1,X ; reset end of list index DEX BNE 1$ ; X = 0 DLCPY LDA SPRYPOS,X ; determine display list LSR ; Y / DLHIGH LSR LSR LSR TAY LDA DLPTRL,Y ; display list lookup table STA DLPTR LDA DLPTRM,Y STA DLPTR+1 LDA DLIDX,Y ; fetch end of list pointer STY DLTEMP ; save for later TAY LDA SPRIDX,X STA (DLPTR),Y ; low address INY LDA SPRPALW,X STA (DLPTR),Y ; palette + width INY LDA SPRYPOS,X AND #$DLHIGH-1 ; mask ORA #>SPRDATA ; base address STA (DLPTR),Y ; high address INY LDA SPRXPOS,X STA (DLPTR),Y ; horizontal position INY TYA ; update end of list pointer LDY DLTEMP STA DLIDX,Y CPY #NUMDL-1 ; bottom of screen? BEQ 1$ INY LDA DLPTRL,Y ; display list lookup table STA DLPTR LDA DLPTRM,Y STA DLPTR+1 LDA DLIDX,Y ; fetch end of list pointer TAY LDA SPRIDX,X STA (DLPTR),Y ; low address INY LDA SPRPALW,X STA (DLPTR),Y ; palette + width INY LDA SPRYPOS,X AND #$DLHIGH-1 ; mask ORA #>SPRDATA-DLHIGH; base address STA (DLPTR),Y ; high address INY LDA SPRXPOS,X STA (DLPTR),Y ; horizontal position INY TYA ; update end of list pointer LDY DLTEMP INY ; cheaper to increment again than STY STA DLIDX,Y 1$ INX ; next sprite CPX #NUMSPR BEQ DLCPY DLFIN LDX #NUMDL LDA #0 1$ LDY DLPTRL-1,X ; display list lookup table STY DLPTR LDY DLPTRM-1,X STY DLPTR+1 LDY DLIDX-1,X ; end of display list index INY STA (DLPTR),Y ; null header DEX BNE 1$ ; Now that the display list is built (hopefully before the active screen ; begins) it is now time to start preparing for the next screen. Once this ; processing is done, the code waits for the DLL NMI flag to be tripped and ; returns to DLINI to rebuild the display lists again. VWAIT LDA DLIFLAG 1$ CMP DLIFLAG ; wait for end of visible screen BEQ 1$ JMP DLINI ; This is a ROM version of the the non-visible part of the DLL. The onscreen ; portion is generated programatically. DLLTOP DC.B $4B, >DLNULL, <DLNULL ; 12 raster DC.B $0C, >DLNULL, <DLNULL ; +13 raster = 25 blank DLLBOT DC.B $8C, >DLNULL, <DLNULL ; NMI, 13 raster DC.B $0C, >DLNULL, <DLNULL ; +13 raster = 26 blank DLLEND EQU . ; These are the two lookup tables used by the display list builder routine ; to find the correct display list. These tables are also used ; to programatically generated the onscreen portion of the DLL. DLPTRM DC.B >DL00, >DL10, >DL20, >DL30, >DL40, >DL50, >DL60, >DL70 DC.B >DL80, >DL90, >DLA0, >DLB0, >DLC0, >DLD0, >DLE0 DLPTRL DC.B <DL00, <DL10, <DL20, <DL30, <DL40, <DL50, <DL60, <DL70 DC.B <DL80, <DL90, <DLA0, <DLB0, <DLC0, <DLD0, <DLE0; The next chunk is the sprite graphics laid out in 7800 order - $Elnn, where
; l is the line number (so the sprites are upside down with each line on a ; separate page) and nn is the sprite index. SPRDATA ORG $E000 ; sprite table