[atari7800] Demo Code using (ZP,X)
- From: "Eric Ball" <ek-ball@xxxxxxxxxx>
- To: <atari7800@xxxxxxxxxxxxx>
- Date: Thu, 3 May 2007 21:19:16 -0400
I discovered when I went to update my bouncing ball demo to use the (zp,x)
display list builder that my skeleton code had a few problems. I've now
completed the updates to the demo (below).
The demo isn't perfect. You will notice the balls slow down once a number of
them get onscreen. This is because the display list builder plus the
position updates are taking longer than a screen to complete. I also noticed
some graphics glitches when playing on real hardware which I believe are
caused by MARIA using a display list entry which is in the middle of being
updated. Unfortunately, I can't think of an easy way to prevent this.
The ball graphics are a quick conversion from Puzzle Bobble. I'm sure some
hand tweaking would improve the look. There also may be some value in
changing the code to cycle the animation differently than just basing it on
the horizontal position.
Source & binaries from:
http://www.atariage.com/forums/index.php?act=attach&type=post&id=74675
PROCESSOR 6502
; To assemble this file you will require DASM and A78SIGN
; dasm balldem2.asm -f3 -lballdem2.lst -oballdem2.a78
; a78sign balldem2.a78
; Rather than have a separate MARIA.H, I've included all of the
; definitions in line. Note: there are a couple of errors in the
; 7800 Software Guide, the information below is correct.
INPTCTRL EQU $01 ; WO
; D3: 0=disable TIA video
; D2: 0=enable BIOS ROM, 1=enable cart A12,A14,A15
; D1: 0=MARIA disable, 1=MARIA enable
; D0: 1=lock outputs
BACKGRND EQU $20 ; RW [C3 C2 C1 C0 L3 L2 L1 L0]
P0C1 EQU $21
P0C2 EQU $22
P0C3 EQU $23
WSYNC EQU $24 ; WO wait for sync
P1C1 EQU $25
P1C2 EQU $26
P1C3 EQU $27
MSTAT EQU $28 ; RO bit 7 = 1 when VBLANK on
P2C1 EQU $29
P2C2 EQU $2A
P2C3 EQU $2B
DPPH EQU $2C ; WO MSB of DLL pointer
P3C1 EQU $2D
P3C2 EQU $2E
P3C3 EQU $2F
DPPL EQU $30 ; WO LSB of DLL pointer
P4C1 EQU $31
P4C2 EQU $32
P4C3 EQU $33
CHARBASE EQU $34 ; WO MSB of indirect sprites
P5C1 EQU $35
P5C2 EQU $36
P5C3 EQU $37
OFFSET EQU $38 ; RW set to $00
P6C1 EQU $39
P6C2 EQU $3A
P6C3 EQU $3B
CTRL EQU $3C ; WO [CK DM1 DM0 CW BC KM RM1 RM0]
; CK: 0=color, 1=B&W
; DM: 10=DMA on, 11=DMA off
; CW: 0=single, 1=double wide char
; BC: 0=black border, 1=background
; KM: 0=transparent, 1=background
; RM: 00=160 1x=320
P7C1 EQU $3D
P7C2 EQU $3E
P7C3 EQU $3F
INPT0 EQU $08 ; x000 0000 Read Pot Port 0
INPT1 EQU $09 ; x000 0000 Read Pot Port 1
INPT2 EQU $0A ; x000 0000 Read Pot Port 2
INPT3 EQU $0B ; x000 0000 Read Pot Port 3
INPT4 EQU $0C ; x000 0000 Read Input (Trigger) 0
INPT5 EQU $0D ; x000 0000 Read Input (Trigger) 1
VBLANK EQU $01 ; D7: 1=dump pot ports to ground
; D6: 1=enable INPT4/5 latches, 0=reset latches to 1
AUDC0 EQU $15 ; 0000 xxxx Audio Control 0
AUDC1 EQU $16 ; 0000 xxxx Audio Control 1
AUDF0 EQU $17 ; 000x xxxx Audio Frequency 0
AUDF1 EQU $18 ; 000x xxxx Audio Frequency 1
AUDV0 EQU $19 ; 0000 xxxx Audio Volume 0
AUDV1 EQU $1A ; 0000 xxxx Audio Volume 1
SWCHA EQU $280 ; Port A data register for joysticks:
; Bits 4-7 for player 1. Bits 0-3 for player 2.
SWACNT EQU $281 ; Port A data direction register (DDR)
SWCHB EQU $282 ; Port B data (console switches)
SWBCNT EQU $283 ; Port B DDR
INTIM EQU $284 ; Timer output
TIM1T EQU $294 ; set 1 clock interval
TIM8T EQU $295 ; set 8 clock interval
TIM64T EQU $296 ; set 64 clock interval
T1024T EQU $297 ; set 1024 clock interval
SEG.U RAM
; A few equates, mostly to reduce the use of magic numbers in the code
XMAX EQU 160 ; screen width
YNTSC EQU 200 ; NTSC screen height
YPAL EQU 240 ; PAL screen height
FNTSC EQU 60 ; NTSC frame rate
FPAL EQU 50 ; PAL frame rate
FSPR EQU 300 ; sprite frame rate
XSPR EQU 8 ; sprite width
YSPR EQU 16 ; sprite height
DLLEN EQU 256/2 ; bytes per display list
NNUMDL EQU (YNTSC+YSPR-1)/YSPR
PNUMDL EQU (YPAL+YSPR-1)/YSPR
NUMDL EQU PNUMDL ; number of display lists
; Zero page variables. These are added as required.
ORG $40
ORG (NUMDL+4)*3+7 ; normally this would be $40
; except the display list list uses the shadow
DLIDX DS NUMDL*2 ; (ZP,X) pointer to end of display list
RANDOM DS 1 ; random number
DLIFLAG DS 1 ; end of visible screen
DELAY DS 2 ; add ball delay
MAXS DS 1 ; current number of sprites
MAXX DS 1 ; maximum X position
MAXY DS 1 ; maximum Y position
ORG $100 ; ZP overflow warning
; 7800 RAM memory map (4K total)
; 1800-203F 2K + 64 bytes
; 2040-20FF 192 bytes 0040-00FF ZP shadow
; 2100-213F 64 bytes
; 2140-21FF 192 bytes 0140-01FF SP shadow
; 2200-27FF 1.5K
; Note: the 7800 Software Guide shows the RAM as being shadowed at other
; addresses (2800-3FFF), but testing by Eckhard Stolberg has shown that this
; is not true.
SPRITE EQU $1800 ; palette & sprite index for 256 sprites
; This is just used to easily calculate the memory location of the display
lists
; and the DLL etc.
ORG $1900
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
ORG $2000 ; Note: this could extend into ZP space
; This is the RAM version of the DLL and some static display lists.
DLWMSET DS 5 ; set WM=0
DLNULL DS 2 ; null display list
DLLRAM DS (NUMDL+4)*3
ORG $2100
; This extends into the stack RAM shadow, but since the stack isn't used
; heavily, it shouldn't be a problem.
DLE0 DS DLLEN
XPOSI EQU $2200 ; position & velocity for 256 sprites
XPOSF EQU $2300
XVELF EQU $2400
YPOSI EQU $2500
YPOSF EQU $2600
YVELF EQU $2700
; This automatically generates the A78 header information
SEG ROM ; move after header for 8K CC2 bin
HEADER ORG ROMTOP-128
DC.B 1 ; 0 Header version - 1 byte
DC.B "ATARI7800" ; 1..16 "ATARI7800 " - 16 bytes
DS 7,32
DC.B "Bouncing Balls Demo" ; 17..48 Cart title - 32 bytes
DS HEADER+49-.,0
DC.B $00,$00,256->ROMTOP,$00 ; 49..52 data length - 4 bytes
DC.B $00,$00 ; 53..54 cart type - 2 bytes
; bit 0 - Pokey cart
; bit 1 - Supercart bank switched
; bit 2 - Supercart RAM at $4000
; bit 3 - ROM at $4000
; bit 4 - Bank 6 at $4000
; bit 8-15 - Special
; 0 = Normal cart
DC.B 1 ; 55 controller 1 type - 1 byte
DC.B 1 ; 56 controller 2 type - 1 byte
; 0 = None
; 1 = Joystick
; 2 = Light Gun
DC.B 0 ; 57 0 = NTSC 1 = PAL
ORG HEADER+100 ; 100..127 "ACTUAL CART DATA STARTS HERE" - 28 bytes
DC.B "ACTUAL CART DATA STARTS HERE"
; 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.
; Note: l=0 is the bottom of the sprite, but in the kernel routine Y
position
; is the top of the sprite. This matters for sprites which are not full
height.
;
; The sprites are all 16 rows high, which matches the height of the zone.
Since
; 4K holey DMA is turned on for the zones, reads outside of this table will
; return zero.
; 8x16 pixels in 160A format 2 bpp (4 pixels per byte)
; 0m0l 1m1l 2m2l 3m3l -> 0m0l1m1l2m2l3m3l
ROMTOP ORG $E000
SPRDATA EQU .
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
ORG $E100
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
ORG $E200
DC.B %00010101, %10110100
DC.B %00010101, %10110100
DC.B %00011001, %10110100
DC.B %00010101, %10110100
DC.B %00010101, %10110100
DC.B %00010101, %10110100
DC.B %00010101, %10110100
DC.B %00010110, %10110100
ORG $E300
DC.B %00010101, %01100100
DC.B %00010101, %01100100
DC.B %00010101, %01100100
DC.B %00010101, %01100100
DC.B %00010101, %01100100
DC.B %00010101, %01100100
DC.B %00010101, %01100100
DC.B %00010101, %01100100
ORG $E400
DC.B %00010101, %01010100
DC.B %00010101, %01010100
DC.B %00010101, %01010100
DC.B %00010101, %01010100
DC.B %00010101, %01010100
DC.B %00010101, %01010100
DC.B %00010101, %01011000
DC.B %00010101, %01010100
ORG $E500
DC.B %01100101, %01010101
DC.B %01010101, %01010101
DC.B %01011001, %01010101
DC.B %01010101, %01010101
DC.B %01010101, %01010101
DC.B %01011010, %10010101
DC.B %01011010, %01010101
DC.B %01101010, %01011001
ORG $E600
DC.B %01101001, %01011001
DC.B %01100101, %01011001
DC.B %01010101, %01011001
DC.B %01010110, %10100101
DC.B %01011011, %11100101
DC.B %01011011, %10100101
DC.B %01011010, %10010101
DC.B %01101001, %01010101
ORG $E700
DC.B %01101001, %00011001
DC.B %01101001, %01101001
DC.B %01010101, %01101001
DC.B %01011010, %10101001
DC.B %01101001, %10101001
DC.B %01101010, %01100101
DC.B %01100101, %10010101
DC.B %01101010, %01011001
ORG $E800
DC.B %01010110, %01100101
DC.B %01010101, %10101001
DC.B %01010101, %10011001
DC.B %01100101, %10101001
DC.B %01100101, %01101001
DC.B %01010101, %01011001
DC.B %01010101, %00101001
DC.B %01100101, %10011001
ORG $E900
DC.B %01011010, %10010101
DC.B %01011010, %10100101
DC.B %01011010, %01101001
DC.B %01011010, %01011001
DC.B %01011010, %01010101
DC.B %01011010, %01011001
DC.B %01011010, %01101001
DC.B %01011010, %10110101
ORG $EA00
DC.B %01101111, %01010101
DC.B %01101111, %01010101
DC.B %01101111, %01011001
DC.B %01101111, %01010101
DC.B %01101111, %01010101
DC.B %01101111, %01010101
DC.B %01101111, %01010101
DC.B %01101111, %01010101
ORG $EB00
DC.B %00101110, %01010100
DC.B %00111110, %01010100
DC.B %00111111, %01010100
DC.B %00111111, %01010100
DC.B %00111111, %01010100
DC.B %00111111, %01010100
DC.B %00111111, %01010100
DC.B %00111111, %01010100
ORG $EC00
DC.B %00101101, %01010100
DC.B %00101101, %01010100
DC.B %00101101, %01010100
DC.B %00101101, %01010100
DC.B %00101101, %01010100
DC.B %00101101, %01010100
DC.B %00101101, %01010100
DC.B %00101101, %01010100
ORG $ED00
DC.B %00011110, %01100100
DC.B %00011111, %01100100
DC.B %00011111, %01100100
DC.B %00011111, %01100100
DC.B %00011111, %01100100
DC.B %00011111, %01100100
DC.B %00011111, %01100100
DC.B %00011111, %01100100
ORG $EE00
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
DC.B %00000110, %10010000
ORG $EF00
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
DC.B %00000001, %01000000
HOLEY ORG $F000
; This lookup table is used to initialize the MARIA registers.
; A value of 0 is no change (in order avoid writing certain registers)
; so if 0 is wanted it has to be set explictly.
MARIA DC.B $00 ; BACKGRND N/C
DC.B $05 ; P0C1 [C3 C2 C1 C0 L3 L2 L1 L0]
DC.B $0A ; P0C2
DC.B $0F ; P0C3
DC.B $00 ; WSYNC N/C
DC.B $20 ; P1C1
DC.B $27 ; P1C2
DC.B $2E ; P1C3
DC.B $00 ; MSTAT N/C
DC.B $40 ; P2C1
DC.B $47 ; P2C2
DC.B $4E ; P2C3
DC.B >DLLRAM ; DPPH MSB of DLL pointer
DC.B $60 ; P3C1
DC.B $67 ; P3C2
DC.B $6E ; P3C3
DC.B <DLLRAM ; DPPL LSB of DLL pointer
DC.B $80 ; P4C1
DC.B $87 ; P4C2
DC.B $8E ; P4C3
DC.B >SPRDATA ; CHARBASE MSB of indirect sprites
DC.B $A0 ; P5C1
DC.B $A7 ; P5C2
DC.B $AE ; P5C3
DC.B $00 ; OFFSET N/C
DC.B $C0 ; P6C1
DC.B $C7 ; P6C2
DC.B $CE ; P6C3
DC.B $00 ; CTRL N/C
DC.B $E0 ; P7C1
DC.B $E7 ; P7C2
DC.B $EE ; P7C3
MAREND EQU .
; This is a ROM version of the the non-visible part of the DLL. The onscreen
; portion is generated programatically.
; NTSC version 21 lines vblank, 200 active, 22 vblank
; Note the first 8 lines of bottom vblank handled by onscreen display lists
; This isn't a problem as long as the sprites are kept inside the screen.
NDLLTOP DC.B <HOLEY, %01000000, >HOLEY, -1, 160, 0, 0 ; set WM=0
DC.B $49, >DLWMSET, <DLWMSET ; 10 raster
DC.B $0A, >DLNULL, <DLNULL ; +11 raster = 21 blank
NDLLBOT DC.B $82, >DLNULL, <DLNULL ; NMI, 3 raster
DC.B $0A, >DLNULL, <DLNULL ; +11 raster = 22 blank
NDLLEND EQU .
; PAL version 26 lines vblank, 240 active, 27 vblank
PDLLTOP DC.B <SPRDATA, %01000000, >SPRDATA, -1, 160, 0, 0 ; set WM=0
DC.B $0C, >DLWMSET, <DLWMSET ; 13 raster
DC.B $0C, >DLNULL, <DLNULL ; +13 raster = 26 blank
PDLLBOT DC.B $8C, >DLNULL, <DLNULL ; NMI, 13 raster
DC.B $0D, >DLNULL, <DLNULL ; +14 raster = 27 blank
PDLLEND EQU .
; A RAM version of this lookup table is used by the display list builder
; routine to find the end of each display list. These tables are also used
; to programatically generated the onscreen portion of the DLL.
DLPTRW DC.W DL00, DL10, DL20, DL30, DL40, DL50, DL60, DL70
DC.W DL80, DL90, DLA0, DLB0, DLC0, DLD0, DLE0
; 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 I use it to signal when MARIA
is
; done displaying the visible screen so the display lists can be updated.
DLLNMI INC DLIFLAG
IRQRTI RTI
; This is the startup section. It has a few tasks to accomplish:
; - initialize the 6502/MARIA/TIA/RIOT registers and zero page variables
; - determine PAL or NTSC based on the number of lines per frame
; - copy the DLL from ROM to RAM
; It then waits for VBLANK to start, turns on DMA, and falls into the
display
; list builder routine.
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
NTSCPAL LDY #242
BIT MSTAT ; determine PAL or NTSC
BMI 2$ ; currently in VBLANK
1$ STA WSYNC
BIT MSTAT
BMI 2$ ; changed to VBLANK
DEY
BNE 1$
BEQ INITPAL ; more lines than NTSC, use PAL
2$ LDY #242
3$ STA WSYNC
BIT MSTAT
BMI 3$
4$ STA WSYNC
BIT MSTAT
BMI ININTSC ; changed to VBLANK
DEY
BNE 4$
INITPAL LDA #YPAL-YSPR ; set up PAL specific
STA MAXY
LDA #FSPR/FPAL
STA DELAY+1
LDY #0 ; Build the DLL automatically.
1$ LDA PDLLTOP,Y ; copy top from ROM
STA DLWMSET,Y
INY
CPY #PDLLBOT-PDLLTOP
BNE 1$
LDX #0 ; build active screen DLLs
2$ LDA #$4F ; 16 high zone, 4K (16 line) holey DMA
STA DLWMSET,Y
INY
LDA DLPTRW+1,X ; MSB
STA DLIDX+1,X
STA DLWMSET,Y
INY
LDA DLPTRW,X ; LSB
STA DLWMSET,Y
INY
INX
INX
CPX #PNUMDL*2
BNE 2$
LDX #0
3$ LDA PDLLBOT,X ; copy bottom from ROM
STA DLWMSET,Y
INY
INX
CPX #PDLLEND-PDLLBOT
BNE 3$
BEQ INITALL
ININTSC LDA #YNTSC-YSPR ; set up NTSC specific
STA MAXY
LDA #FSPR/FNTSC
STA DELAY+1
LDY #0 ; Build the DLL automatically.
1$ LDA NDLLTOP,Y ; copy top from ROM
STA DLWMSET,Y
INY
CPY #NDLLBOT-NDLLTOP
BNE 1$
LDX #0 ; build active screen DLLs
2$ LDA #$4F ; 16 high zone, 4K (16 line) holey DMA
STA DLWMSET,Y
INY
LDA DLPTRW+1,X ; MSB
STA DLIDX+1,X
STA DLWMSET,Y
INY
LDA DLPTRW,X ; LSB
STA DLWMSET,Y
INY
INX
INX
CPX #NNUMDL*2
BNE 2$
4$ LDA DLPTRW+1,X ; MSB
STA DLIDX+1,X
INX
INX
CPX #NUMDL*2
BNE 4$
LDX #0
3$ LDA NDLLBOT,X ; copy bottom from ROM
STA DLWMSET,Y
INY
INX
CPX #NDLLEND-NDLLBOT
BNE 3$
INITALL LDA #XMAX-XSPR ; set up common variables
STA MAXX
LDA #1
STA MAXS
LDX #MAREND-MARIA-1 ; init MARIA registers
1$ LDA MARIA,X
BEQ 2$ ; 0 = don't update
STA BACKGRND,X
2$ DEX
BPL 1$
INX
STX BACKGRND ; zero MARIA registers
STX OFFSET
STX VBLANK ; zero TIA registers
STX AUDV0
STX AUDV1
STX SWACNT ; set RIOT DDR registers to inputs
STX SWBCNT
STX DELAY
INIBALL JSR RAND ; randomize color, position, movement
STA YVELF,X
STA SPRITE,X
CMP MAXY ; keep onscreen
BCC 1$
LSR
1$ STA YPOSI,X
JSR RAND
STA XVELF,X
CMP MAXX ; keep onscreen
BCC 2$
LSR
2$ STA XPOSI,X
LDA #0
STA $1900,X ; clear display list space
STA $1A00,X
STA $1B00,X
STA $1C00,X
STA $1D00,X
STA $1E00,X
STA $1F00,X
STA $2100,X
INX
BNE INIBALL
; 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, transparent,
160A/B
STA CTRL
; 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 resets the end of list index
; DLCPY figures out which display list a sprite belongs in and
; 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 LDY #NUMDL*2 ; reset DLIDX pointers
1$ LDA DLPTRW-2,Y ; reset LSB
STA DLIDX-2,Y
DEY
DEY
BNE 1$ ; Y = 0
DLCPY LDA YPOSI,Y ; determine display list
AND #$F0 ; (Y / YSPR) * 2
LSR
LSR
LSR
TAX
LDA XPOSI,Y ; calculate sprite index
AND #$0E ; 2 bytes per sprite (8 sprites)
STA (DLIDX,X) ; low address (sprite index)
INC DLIDX,X
LDA SPRITE,Y ; palette/sprite index
AND #$E0 ; mask off palette
ORA #$1E ; 2 bytes wide
STA (DLIDX,X) ; palette + width
INC DLIDX,X
LDA YPOSI,Y
AND #YSPR-1 ; mask
ORA #>SPRDATA ; base address
STA (DLIDX,X) ; high address
INC DLIDX,X
LDA XPOSI,Y
STA (DLIDX,X) ; horizontal position
INC DLIDX,X
CPX #(NUMDL-1)*2 ; bottom of screen?
BEQ 1$
INX ; next display list
INX
LDA XPOSI,Y ; calculate sprite index
AND #$0E ; 2 bytes per sprite (8 sprites)
STA (DLIDX,X) ; low address
INC DLIDX,X
LDA SPRITE,Y ; palette/sprite index
AND #$E0 ; mask off palette
ORA #$1E ; 2 bytes wide
STA (DLIDX,X) ; palette + width
INC DLIDX,X
LDA YPOSI,Y
AND #YSPR-1 ; mask
ORA #>SPRDATA-YSPR ; base address
STA (DLIDX,X) ; high address
INC DLIDX,X
LDA XPOSI,Y
STA (DLIDX,X) ; horizontal position
INC DLIDX,X
1$ INY ; next sprite
CPY MAXS ; up to current max
BNE DLCPY
DLFIN LDX #NUMDL*2 ; add end of display list
LDA #0
1$ INC DLIDX-2,X
STA (DLIDX-2,X) ; null header
DEX
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.
; This is the movement code. It's fairly simple multi-byte addition. The
only
; trick is handling the sign extend for the integer portion.
; This routine also handles bouncing off the edges of the screen.
LDX #0
MOVBALL CLC
LDA XVELF,X
BMI MV1
EOR #$80 ; add 128/256
ADC XPOSF,X
STA XPOSF,X
LDA XPOSI,X
ADC #0
STA XPOSI,X
CMP MAXX
BCC MV2
JSR RAND
ORA #$80 ; make negative
STA XVELF,X
BMI MV2
MV1 EOR #$80 ; sub 128/256
ADC XPOSF,X
STA XPOSF,X
LDA XPOSI,X
ADC #-1
STA XPOSI,X
BNE MV2
JSR RAND
AND #$7F ; make positive
STA XVELF,X
MV2 CLC
LDA YVELF,X
BMI MV3
EOR #$80 ; add 128/256
ADC YPOSF,X
STA YPOSF,X
LDA YPOSI,X
ADC #0
STA YPOSI,X
CMP MAXY
BCC MV4
JSR RAND
ORA #$80 ; make negative
STA YVELF,X
BMI MV4
MV3 EOR #$80 ; sub 128/256
ADC YPOSF,X
STA YPOSF,X
LDA YPOSI,X
ADC #-1
STA YPOSI,X
BNE MV4
JSR RAND
AND #$7F ; make positive
STA YVELF,X
MV4 INX
BNE MOVBALL
; this code adds one more sprite every 256/FSPR seconds
LDX MAXS
BEQ VWAIT
CLC
LDA DELAY
ADC DELAY+1
STA DELAY
BCC VWAIT
INX
STX MAXS
; This is the end of the calculations. Hopefully everything gets done in a
; single frame.
VWAIT LDA DLIFLAG
1$ CMP DLIFLAG ; wait for end of visible screen
BEQ 1$
JMP DLINI
; After this are subroutines
RAND LDA RANDOM ; 8 bit LFSR from http://www.ece.cmu.edu/~koopman/lfsr/
LSR
BCS 1$
EOR #$C3
1$ STA RANDOM
RTS
; This is the end of the cartridge, containing the digital signature and
flag
; the NTSC ROM uses to identify 7800 vs 2600 games and as a lockout
mechanism
; against unapproved ROMs, i.e. the infamous 7800 encryption (a big thank
you
; to the a78sign guys) and the 6502 interrupt vectors
ORG $FF80
DS 120,0 ; 960 bit Rabin Digital Signature
DC.W ROMTOP + $07FF
NMI DC.W DLLNMI
RESET DC.W START
IRQ DC.W IRQRTI
Other related posts:
- » [atari7800] Demo Code using (ZP,X)