;--------------------------------------------------------------------------- ; * Subject: [stella] MultiSpriteDemo update (source+binary) ; * From: Piero Cavina ; * Date: Thu, 3 Apr 1997 21:15:00 +0200 (METDST) ;--------------------------------------------------------------------------- ; Here's my latest demo debugged and updated. I've put comments in the most ; interesting parts of the code. ; I'm going to write a new game based on this code, so please ask before using ; this for yours... ; PCMSD11.ASM ; Atari 2600 MultiSprite Demo 1.1 ; (c)1987 Piero Cavina ; NTSC version processor 6502 VSYNC = $00 VBLANK = $01 WSYNC = $02 NUSIZ0 = $04 NUSIZ1 = $05 COLUP0 = $06 COLUP1 = $07 COLUPF = $08 COLUBK = $09 REFP0 = $0B PF0 = $0D PF1 = $0E PF2 = $0F RESP0 = $10 RESP1 = $11 RESM1 = $13 RESBL = $14 AUDC0 = $15 AUDF0 = $17 AUDV0 = $19 GRP0 = $1B GRP1 = $1C ENABL = $1F HMP0 = $20 HMP1 = $21 HMM1 = $23 HMBL = $24 HMOVE = $2A HMCLR = $2B CXCLR = $2C SWCHA = $0280 SWACNT = $0281 SWCHB = $0282 SWBCNT = $0283 INTIM = $0284 TIM64T = $0296 CXPPMM = $7 NUMGRP = 11 GRPHEIGHT = 7 XMIN = 8 XMAX = 150 GRPCOUNT = $80 TEMP = GRPCOUNT+1 FC_XPOS = TEMP+4 XPOS = FC_XPOS+NUMGRP XMOT = XPOS+NUMGRP GRPY = XMOT+NUMGRP PLYPOS = GRPY+1 NXTGRPY = PLYPOS+1 PLYMOT = NXTGRPY+1 PLXPOS = PLYMOT+1 FC_PLXPOS = PLXPOS+1 PLXMOT = FC_PLXPOS+1 COLL = PLXMOT+1 NOCC = COLL+NUMGRP FRAMEC = NOCC+1 PLGRD = FRAMEC+1 PLOFF = PLGRD+1 ORG $F000 START: SEI CLD LDX #$28 LDA #$00 LF006: STA NUSIZ0,X DEX BPL LF006 LDX #$FF LF00D: STA VSYNC,X DEX BMI LF00D LDX #$FF TXS STA SWBCNT STA SWACNT LDA #$FF STA COLUP0 ; Initialize objects positions and motion LDX #NUMGRP-1 INILP: TXA ASL ASL ASL CLC ADC #10 STA XPOS,X TXA LSR CLC ADC #1 STA XMOT,X DEX BPL INILP ; Player initialization LDA #40 STA PLXPOS LDA #1 STA PLXMOT LDA #10 STA PLYPOS LDA #1 STA PLYMOT ; Here's 'game logic'. Don't waste too much time on that, as it's just ; standard 6502 programming. Go to "kernel" for more interesting things... MAIN: INC FRAMEC ; Count frames... LDX #0 ; Do Vsync, start Vblank... LDA #$02 STA WSYNC STA VSYNC STA WSYNC STA WSYNC STA WSYNC STX VSYNC STA WSYNC STA VBLANK LDA #$2C STA TIM64T ; Move objects horizontally, handle collisions LDX #NUMGRP-1 MOVLP: LDA XPOS,X CLC ADC XMOT,X STA XPOS,X CMP #XMIN BCC SWPX0 CMP #XMAX BCS SWAPX JMP OKMV SWPX0: LDA #XMIN SEC SBC XPOS,X CLC ADC #XMIN STA XPOS,X SWAPX: LDA XMOT,X EOR #$FF CLC ADC #1 STA XMOT,X OKMV: LDA XPOS,X JSR CNV STA FC_XPOS,X LDA NOCC BNE NOCL LDA COLL,X BPL NOCL1 LDA XMOT,X EOR PLXMOT BMI DOSW LDA XMOT,X STA PLXMOT JMP NOTSW DOSW: LDA PLXMOT EOR #$FF CLC ADC #1 STA PLXMOT NOTSW: LDA #64 STA NOCC NOCL: DEC NOCC NOCL1: LDA #0 STA COLL,X DEX BPL MOVLP ; Player0 vertical motion LDA PLYPOS CLC ADC PLYMOT STA PLYPOS CMP #6 BCC SWAPPLYM CMP #[[1+GRPHEIGHT]*NUMGRP]-12 BCS SWAPPLYM JMP OKPLYM SWAPPLYM: LDA PLYMOT EOR #$FF CLC ADC #1 STA PLYMOT ; Player0 horizontal motion OKPLYM: LDA PLXPOS CLC ADC PLXMOT STA PLXPOS CMP #25 BCS OKX0 LDA #25 STA PLXPOS JMP SWAPPLXM OKX0: CMP #154 BCC OKPLXM LDA #154 STA PLXPOS SWAPPLXM: LDA PLXMOT EOR #$FF CLC ADC #1 STA PLXMOT OKPLXM: LDA PLXPOS ; Convert Player0 X position JSR CNV ; to FC format. STA FC_PLXPOS STA WSYNC ; Prepare to position Player0 STA HMP0 ; remember, we're still doing Vblank now AND #$0F TAY PLPSL: DEY BPL PLPSL STA RESP0 STA WSYNC STA HMOVE LDA #0 STA GRPCOUNT ; Initialize group counter STA GRPY ; First line of first group LDA #GRPHEIGHT+1 STA NXTGRPY ; First line of next (second) group LDA PLYPOS STA PLGRD LF20C: LDA INTIM ; Finish Vblank BNE LF20C STA WSYNC STA VBLANK STA HMCLR ; We're going to draw #NUMGRP groups, each made of: ; 2 scanlines for Player1 positioning with Player0, plus ; #GRPHEIGHT*2 scanlines with Player1 and Player0. KERNEL: LDA PLGRD ; Distance between Player0<->top of group CMP #GRPHEIGHT+1 ; Is Player0 inside current group? BCC DOPL ; Yes, we'll draw it... LDX #0 ; No, draw instead a BEQ GOPL ; blank sprite. DOPL: LDA NXTGRPY ; We must draw Player0, and we'll start SEC ; from the (NXTGRPY-PLYPOS)th byte. SBC PLYPOS TAX ; Put the index to the first byte into X GOPL: STX PLOFF ; and remember it. LDY GRPCOUNT ; Store any collision between Player0 and LDA CXPPMM ; Player1 happened while drawing the ORA COLL,Y ; last group. STA COLL,Y LDA FC_XPOS,Y ; Get Player1 position LDY PLPTN,X ; Get Player0 pattern LDX #0 STA WSYNC ; Start with a new scanline. STY GRP0 ; Set Player0 pattern STX GRP1 ; Blank Player1 pattern to avoid 'bleeding' STA HMP1 ; Prepare Player1 fine motion AND #$0F ; Prepare Player1 coarse positioning TAY POSLP: DEY ; Waste time BPL POSLP STA RESP1 ; Position Player1 STA WSYNC ; Wait for next scanline STA HMOVE ; Apply fine motion ; Now prepare various things for the next group LDA NXTGRPY ; Updade this group and next group STA GRPY ; top line numbers CLC ADC #GRPHEIGHT+1 STA NXTGRPY LDA PLYPOS ; Find out which 'slice' SEC ; of Player0 we'll have to draw. SBC GRPY ; We need the distance of Player0 BPL DPOS ; from the top of the group. EOR #$FF ; CLC ADC #1 ; A = ABS(PLYPOS-GRPY) DPOS: STA PLGRD ; LDX PLOFF ; Pointer to the next byte of Player0 INX ; pattern. Use X while drawing the group LDA #0 ; Clear collisions STA CXCLR LDY #GRPHEIGHT-1 ; Initialize line counter (going backwards) GRPLP: TYA ; Find the shade of Player1 color ASL ; to be used in the next line ORA #$40 STA TEMP ; ...and remember it. LDA #$51 STA WSYNC ; Wait for a new line STA COLUBK ; Set background color LDA PLPTN,X STA GRP0 ; Set Player0 shape LDA GRPPTN,Y STA GRP1 ; Set Player1 shape LDA TEMP STA COLUP1 ; Set Player1 color STA WSYNC ; Wait for a new scanline INX ; Update the index to next byte of Player0 DEY ; Decrement line counter BPL GRPLP ; Go on with this group if needed INC GRPCOUNT ; Increment current group number LDA GRPCOUNT ; CMP #NUMGRP ; Is there another group to do? BCS OUTKERNEL ; No, exit JMP KERNEL ; Yes, go back. (Using JMP because a branch ; would be out of range). OUTKERNEL: STA WSYNC ; Finish current scanline LDA #0 ; Avoid bleeding of Player1 STA GRP1 LDA #$C0 ; How many scanlines are missing...? SEC SBC #[1+GRPHEIGHT]*2*NUMGRP+2 ; It's clear, isn't it? TAY FILLER: STA WSYNC DEY BNE FILLER ; draw them. LDA #$02 ; Overscan STA WSYNC STA VBLANK LDA #$1D TAY LF305: STA WSYNC DEY BNE LF305 JMP MAIN ; Go back for another frame GRPPTN: .byte %00111100 ; Pattern for Player1 .byte %01111110 .byte %11111111 .byte %11111111 .byte %11111111 .byte %01111110 .byte %00111100 PLPTN: .BYTE $00 ; Pattern for Player0. Please note .BYTE $00 ; the leading and trailing 0's .BYTE $00 .BYTE $00 .BYTE $00 .BYTE $00 .BYTE $00 .BYTE $00 .BYTE $00 .BYTE %01111110 .BYTE %11111111 .BYTE %11111111 .BYTE %11111111 .BYTE %11111111 .BYTE %11111111 .BYTE %01111110 .BYTE $00 .BYTE $00 .BYTE $00 .BYTE $00 .BYTE $00 .BYTE $00 .BYTE $00 ; Straight from "Air sea battle", here's the routine ; to convert from standard X positions to FC positions. ; Could a good man explain me how it works? CNV: STA TEMP+1 BPL LF34B CMP #$9E BCC LF34B LDA #$00 STA TEMP+1 LF34B: LSR LSR LSR LSR TAY LDA TEMP+1 AND #$0F STY TEMP+1 CLC ADC TEMP+1 CMP #$0F BCC LF360 SBC #$0F INY LF360: CMP #$08 EOR #$0F BCS LF369 ADC #$01 DEY LF369: INY ASL ASL ASL ASL STA TEMP+1 TYA ORA TEMP+1 RTS ORG $FFFA .byte $00,$F0,$00,$F0,$00,$F0