[atari7800] sample 7800 source code

  • From: "Eric Ball" <ek-ball@xxxxxxxxxx>
  • To: <atari7800@xxxxxxxxxxxxx>
  • Date: Wed, 21 Mar 2007 19:46:13 -0400

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


Other related posts:

  • » [atari7800] sample 7800 source code