Subject: [stella] DD new build From: Glenn Saunders <cybpunks2@xxxxxxxxxxxxx> Date: Sun, 14 Oct 2001 01:27:15 -0700 |
Glenn Saunders - Producer - Cyberpunks Entertainment Personal homepage: http://www.geocities.com/Hollywood/1698 Cyberpunks Entertainment: http://cyberpunks.uni.cc
;Death Derby 2001 ; By Glenn Saunders ;LINEAGE: ; How to Draw an Asymmetric Reflected Playfield ; by Roger Williams ; with a lot of help from Nick Bensema ; (this started out as his "How to Draw a Playfield") processor 6502 include vcs.h ; CONSTANTS ; SCREEN GEOMETRY CTR ACTUAL SUBTOTAL ;----------------------- ---- --------- -------- ScoreHeight = 20; 20 20 ; TopBorder = 2 2 22 PlayfieldHeight = 88; (176) 198 ; BottomBorder = 2 2 200 ; --- ; 200 visible lines OverScanHeight = 22; 22 222 ; --- ; 222 total lines ;INITIAL COUNTER VALUES TombstoneHeight = 8 ; 8*2 = 16 (single scanlines) high per tombstone TombstoneRowsPerScreen = 11-1 ; 11 rows of tombstones per screen CarHeight = 8-1 ; 8 doubled scanlines (used as offset) ; constants that reference rotation index pointers PointUp = 0 PointRight = 4 PointDown = 8 PointLeft = 12 TurnOnSpriteReflect = %00000100 TurnOffSpriteReflect = 0 CarPage = $FF;00 CarDataAlign = $65 InitialSafeZone = %10010000 ; initial safezone border graphics SafeZone2 = %10000000 FlipThreshold = 9 ; flip sprite when ;P0_CurrentRotationIndex >= 9 SEG.U vars org $80 ;TEMPORARY VARIABLES Temp ds 1 ;CurrentScanLine ds 1 ; use Y instead ;CurrentTombstoneRow ds 1; 11->0 outer loop counter ; use X instead CurrentTombstoneSubRow ds 1; 7->0 inner loop counter ; ALTERNATING SAFEZONE BORDER STRIP CurrentSafeZone ds 1 ; PLAYFIELD BITMAP PF1DataRAM ds TombstoneRowsPerScreen+1 PF2DataRAM ds TombstoneRowsPerScreen+1 PF1bDataRAM ds TombstoneRowsPerScreen+1 PF2bDataRAM ds TombstoneRowsPerScreen+1 ;sprite positiong P0_X ds 1 P0_Y ds 1 P1_X ds 1 P1_Y ds 1 M0_X ds 1 M0_Y ds 1 M1_X ds 1 M1_Y ds 1 ;sprite graphics pointers P0_CurrentRotationSequence ds 1; 0-15 P1_CurrentRotationSequence ds 1; 0-15 ;these above reference addresses that get stored below P0_CurrentFramePtr ds 2; this will wind up being a constant $FF in the high byte P1_CurrentFramePtr ds 2; this will wind up being a constant $FF in the high byte M0_CurrentFramePtr ds 2 M1_CurrentFramePtr ds 2 ;P0_CurrentRowCounter ds 1 ;P1_CurrentRowCounter ds 1 ;M0_CurrentRowCounter ds 1 ;M1_CurrentRowCounter ds 1 ; stolen from OUTLAW source PF0Array ds 1 ; PF0 array, 18 Bytes ScoreshapeLow01 ds 1 ; Shape of player 1 lower score digit ScoreshapeLow02 ds 1 ; Shape of player 2 lower score digit ScoreshapeHi01 ds 1 ; Shape of player 1 higher score digit ScoreshapeHi02 ds 1 ; Shape of player 2 higher score digit bcdScore01Backup ds 1 ; Backups score for player 1 rightScoreOnOff ds 1 ; 0F for Right score on, 00 for off gameState ds 1 ; 0F running/EF saver/00 select/XX counter SEG code org $F000 ;FrameCounter = $93 was going to try some color cycling Start SEI ; Disable interrupts, if there are any. CLD ; Clear BCD math bit. LDX #$FF TXS ; Set stack to beginning. ; CLEAR OUT RAM LDA #0 KeepZeroing STA 0,X DEX BNE KeepZeroing SetColors LDA #0 STA COLUBK ; Background will be black. LDA #$34 ; set playfield color to deep red STA COLUPF LDA #PF_Reflect STA CTRLPF ; set playfield to reflect LDA #$FF; set the cars to white for now STA COLUP0 STA COLUP1 InitializeSprites ; set high byte for frame pointers ; this should only have to be done once for the whole game LDA CarPage; $FF STA >P0_CurrentFramePtr STA >P1_CurrentFramePtr ; set initial X,Y of P0, P1 LDA #30; start P0 on the left STA P0_X LDA #50; start P0 halfway down STA P0_Y LDA #130; start P1 on the right STA P1_X LDA #50; start P1 halfway down STA P1_Y ; set initial rotations LDX #PointRight; start P0 car pointing right LDA RotationSequence,X; get low byte pointer from index 4 STX P0_CurrentRotationSequence; store for later STA <P0_CurrentFramePtr; store low byte pointer LDX #PointLeft; start P1 car pointing left LDA RotationSequence,X; get low byte pointer from index 4 STX P1_CurrentRotationSequence; store for later STA <P1_CurrentFramePtr; store low byte pointer LDX #TombstoneRowsPerScreen InitializeTombstones; copy from ROM to RAM LDA #InitialSafeZone STA CurrentSafeZone LDA PF1Data,X STA PF1DataRAM,X LDA PF2Data,X STA PF2DataRAM,X LDA PF1bData,X STA PF1bDataRAM,X LDA PF2bData,X STA PF2bDataRAM,X DEX BPL InitializeTombstones MainLoop JSR VBArea JSR DrawScreen JSR OverScan JMP MainLoop ;Continue forever. ;SUB ClearTombstones LDX #TombstoneRowsPerScreen KeepClearing DEX STA <PF1DataRAM,X STA <PF2DataRAM,X STA <PF1bDataRAM,X STA <PF2bDataRAM,X BNE KeepClearing RTS ;SUB OverScan LDX #OverScanHeight KillLines STA WSYNC DEX BNE KillLines RTS ;HORIZONTAL POSITIONING SUBROUTINE FROM MANUEL POLIK's GUNFIGHT ;SUB (alternate beginning of below) PosP1 CLC ADC #$08 ;SUB PosP0 CMP #$A0 BCC NH2 SBC #$A0 NH2 TAY LSR LSR LSR LSR STA Temp TYA AND #15 CLC ADC Temp LDY Temp CMP #15 BCC NH SBC #15 INY NH EOR #7 ASL ASL ASL ASL STA HMP0,X INY ; Waste 5 cycles, 1 Byte STA WSYNC INY ; Waste 7(!) cycles, 1 Byte BIT 0 ; Waste 3 cycles, 2 Byte PosDelay DEY BPL PosDelay STA RESP0,X RTS ;/HORIZONTAL POSITIONING SUBROUTINE ;SUB VBArea LDX #0 ; store to HMP0 LDA P0_X; load current X value of P0 JSR PosP0; call coarse pos calc routine for P0 ;LDA #2 STA WSYNC LDX #1; store to HMP1 LDA P1_X; load current X value of P1 JSR PosP1; call coarse pos calc routine for P1 ;LDA #2 STA WSYNC ;later on we need to add the horizontal positioning ;of the two missiles ;The calculation of the object pointer (done outside the kernel, if ;Thomas didnt make that clear enough) looks like this: UpdateCurrentFramePtr ;this will have been modified by the controller routines LDX P0_CurrentRotationSequence;0-15 ; load low byte pointer to the start ; of gfx for the current frame LDA RotationSequence,X ; update the pointer to be used during active kernel ; indirect Y addressing STA <P0_CurrentFramePtr UpdateSpriteReflect ;LDA <P0_CurrentFramePtr CMP #FlipThreshold ; A < #FlipThreshold ---> N=1, Z=0, C=0 ; A = #FlipThreshold ---> N=0, Z=1, C=1 ; A > #FlipThreshold ---> N=0, Z=0, C=1 LDY #TurnOffSpriteReflect; initialize to no flip BMI .Continue ; if A < 9 then sigN will be 1 ; so do not flip, just .Continue FlipSprite LDY #TurnOnSpriteReflect .Continue STY REFP0 SEC ; set carry bit ; subtract the Y position of P0 SBC #P0_Y ; POINTER is initialized as offset -PO_Y away from the last scanline ; of CarRotation0 graphics. ; STORE THE LOW BYTE POINTER STA <P0_CurrentFramePtr ; this shouldn't be necessary because the high byte is always constant ; load a pointer to the page of memory ; data for the CarRotation0 frame (HIGH BYTE) LDA CarPage; $FF ; SUBTRACT 0 with carry. Why? SBC #0 ; STORE as the HIGH BYTE pointer to P0 graphics ; therefore this should remain the same as the last ; row of graphics for CarRotation0 frame (HIGH BYTE) STA >P0_CurrentFramePtr ; /this shouldn't be necessary because the high byte is always constant ; MANUEL'S NOTES (edited): ; Where P0_Y represents the scanline-pair where the oject starts. ; In the given example P0_Y might be set to any value between #0 and #100 ; and we're subtracting it! ; So it's as simple as: If we fall below zero here, we cross a page later, ; so our data must start above any possible value of P0_Y. ; To allow for complete movement across the screen, assuming 100-0, ; (SCANLINE COUNTER DECREMENTING) ; then data must aligned at 100 or above on the given page in order to ; prevent subtraction rollovers/extra cycle time. ; in this game, however, if you subtract the score and the border, ; the range is constrained to XX-0 (FILL THIS IN LATER) ;(Hm... uhm... Thomas: Why isn't the size of the displayed thing taken ;into account here? Could it be... Oh my god... if one'd already let dasm ;calculate it... that'd be LDA #<CarRotation0+CarHeight ... then... Oh-oh... ;I've to quit now, there's probably an optimisation run in gunfight ;necessary...) ;/UpdateCurrentFramePtr HMOVEDelay DEX BNE HMOVEDelay STA HMOVE ; Do it on cycle 74!!! STA WSYNC STA VSYNC ;Begin vertical sync. STA WSYNC ; First line of VSYNC STA WSYNC ; Second line of VSYNC. LDA #44 ;init timer for overscan STA TIM64T STA WSYNC ; Third line of VSYNC. STA VSYNC ; (0) VBLOOP LDA INTIM BNE VBLOOP ; Whew! STA WSYNC STA VBLANK ;End the VBLANK period with a zero. LDA #0 STA PF0 STA PF1 STA PF2; zero out playfield RTS ;SUB DrawScreen LDX #ScoreHeight; burn through 20 total scanlines DrawScore STA WSYNC DEX BNE DrawScore TopBorder ; 2 lines LDA #$FF STA PF0 ; draw a solid line STA PF1 STA PF2 LDA #InitialSafeZone STA WSYNC ; draw a solid line except for the safezone border STA PF0 ;ScanGo will finish the line off FinalInitialization ;INITIALIZE TOMBSTONE ROW COUNTERS LDX #TombstoneRowsPerScreen+1 ;TombstoneRowsPerScreen held in X register through kernel LDA #TombstoneHeight+1 ; NOTE: pad these by one because the first time through it decrements ; at the top of the loop STA CurrentTombstoneSubRow; store TombstoneHeight to RAM LDY #PlayfieldHeight ; store scanline counter in Y throughout ; the whole kernel. Decrement every other scanline ;THIS AREA IS A 2-LINE KERNEL ;START FIRST LINE OF LINE-PAIR ScanLoop1 ;INNER LOOP DEC CurrentTombstoneSubRow ;next row of current tombstone BNE ScanGo1 LDA #TombstoneHeight; if counter reaches zero, reset to 7 STA CurrentTombstoneSubRow; and store back to RAM DEX ;CurrentTombstoneRow = CurrentTombstoneRow - 1 ;outer loop - indexes next tombstone row ScanGo1 STA WSYNC ; Time to begin cycle counting ; 0 ;STA.W HMOVE ;+4 4 NOP ;+2 2 NOP ;+2 4 ;will probably have to do an HMOVE on every line to ;simplify logic. HMOVE is only necessary for the missiles LDA <PF1DataRAM-1,X ;+4 8 ;I'm not sure why the -1 is necessary here to make it work STA PF1 ;+3 = *11* < 29 ;PF1 visible LDA <PF2DataRAM-1,X ;+4 15 STA PF2 ;+3 = *19* < 40 ;PF2 visible NOP ;+2 20 NOP ;+2 22 NOP ;+2 24 NOP ;+2 26 NOP ;+2 28 NOP ;+2 30 NOP ;+2 32 NOP ;+2 34 LDA <PF1bDataRAM-1,X ;+4 38 ;PF1 no longer visible, safe to rewrite STA PF1 ;+3 = *41* LDA <PF2bDataRAM-1,X ;+3 44 ;PF2 rewrite must begin at exactly cycle 45!!, no more, no less STA PF2 ;+3 = *47* ; > 46 PF2 no longer visible LDA #0 ;+2 49 TXS ;+2 51 ;preserve X register NOP ;+2 53 NOP ;+2 55 NOP ;+2 57 NOP ;+2 59 NOP ;+2 61 ; CLEAR OUT PLAYFIELD FOR SPRITE HANDLING BELOW STA PF2 ;+3 64 STA PF1 ;+3 67 LDA CurrentSafeZone ;+2 69 EOR #SafeZone2 ;+2 71 STA CurrentSafeZone ;+2 73 STA PF0 ;+3 76 ;PERFECT TIMING, NO WSYNC NECESSARY ; PUT ONE BACK IN IF THE NOPs are replaced with code ; that has varying timing ;START SECOND LINE OF LINE-PAIR ScanGo2 ; do all the sprite routines here ; WSYNC PUT BACK IN IF NECESSARY ; start cycle counting again ; 0 ; THOMAS' SAMPLE CODE ;P0Loop ; TYA ; current scanline (updated every other line) ; SEC ; SBC SpriteStart ; contains the line where the car starts+1 ; ADC #SPRITEHEIGHT ; assuming the height is constant (i.E. 8) ; BCC .skipDraw ; LDA (P0_CurrentFramePtr),Y ; STA GRP0 ;.skipDraw: ;CONVERTED CODE ;P0Loop ; TXA ; SEC ; SBC P0_YC ; ADC #8 ; BCC .skipDraw ; LDY $FF ;TAY ;.skipDraw: ;STY GRP0 TSX; grab tombstone counter back again DEY ; Decrement ScanlinePair counter BNE ScanLoop1; keep going until scanline counter reaches 0 DoBorder LDA #$FF ; this may overload the timing of the sprite kernel line ; and have to be moved under WSYNC!! STA WSYNC; finish last line of sprites STA PF0 ; draw a straight line STA PF1 STA PF2 STA WSYNC; line 1 of bottom border STA WSYNC; line 2 of bottom border EndKernel STA VBLANK ; Make TIA output invisible, ; Y is already zero STY PF0 STY PF1 STY PF2 STY GRP0 STY GRP1 STY ENAM0 STY ENAM1 STY ENABL RTS; END DrawScreen ;THE GRAPHICS org $FE00 scoreshapedata: .byte $0E ; | XXX | .byte $0A ; | X X | .byte $0A ; | X X | .byte $0A ; | X X | .byte $0E ; | XXX | .byte $22 ; | X X | .byte $22 ; | X X | .byte $22 ; | X X | .byte $22 ; | X X | .byte $22 ; | X X | .byte $EE ; |XXX XXX | .byte $22 ; | X X | .byte $EE ; |XXX XXX | .byte $88 ; |X X | .byte $EE ; |XXX XXX | .byte $EE ; |XXX XXX | .byte $22 ; | X X | .byte $66 ; | XX XX | .byte $22 ; | X X | .byte $EE ; |XXX XXX | .byte $AA ; |X X X X | .byte $AA ; |X X X X | .byte $EE ; |XXX XXX | .byte $22 ; | X X | .byte $22 ; | X X | .byte $EE ; |XXX XXX | .byte $88 ; |X X | .byte $EE ; |XXX XXX | .byte $22 ; | X X | .byte $EE ; |XXX XXX | .byte $EE ; |XXX XXX | .byte $88 ; |X X | .byte $EE ; |XXX XXX | .byte $AA ; |X X X X | .byte $EE ; |XXX XXX | .byte $EE ; |XXX XXX | .byte $22 ; | X X | .byte $22 ; | X X | .byte $22 ; | X X | .byte $22 ; | X X | .byte $EE ; |XXX XXX | .byte $AA ; |X X X X | .byte $EE ; |XXX XXX | .byte $AA ; |X X X X | .byte $EE ; |XXX XXX | .byte $EE ; |XXX XXX | .byte $AA ; |X X X X | .byte $EE ; |XXX XXX | .byte $22 ; | X X | .byte $EE ; |XXX XXX | org $FF00 ; REFLECTED PLAYFIELD ; ; PF0| PF1 | PF2 || PF2b | PF1b |PF0b| ;4567|76543210|01234567||76543210|01234567|7654| ;INITIAL TITLE GRAPHICS ;TO BE COPIED TO RAM ;SafeZone ; 7654---- ; .byte %10011001 PF1Data ;D 76543210 .byte %01111001 .byte %01000101 .byte %01000101 .byte %01000101 .byte %01111001 .byte %00000000 .byte %01111001 .byte %01000101 .byte %01000101 .byte %01000101 .byte %01111001 PF2Data ;EA 01234567 .byte %00101111 .byte %10100000 .byte %11100111 .byte %00100000 .byte %11101111 .byte %00000000 .byte %00101111 .byte %00100000 .byte %11100111 .byte %00100000 .byte %11101111 PF2bData ; 76543210;<--- scanning order .byte %11011110 .byte %00010001 .byte %10011110 .byte %01010001 .byte %10011110 .byte %00000000 .byte %01000100 .byte %01000100 .byte %11000100 .byte %01000100 .byte %11011111 PF1bData; 01234567;<--- scanning order .byte %00111110 .byte %00100000 .byte %00111110 .byte %00100010 .byte %00100010 .byte %00000000 .byte %00100010 .byte %00100010 .byte %00111110 .byte %00100010 .byte %00100010 ; THE ROTATION SEQUENCE POINTERS ; when writing the driving controller code, I will ; index through this table of pointers which rotates ; through the frames. These pointers only store the low byte ; value. The high value is always going to be $FF anyway. ; since the shapes are stored upside down, the actual orientation ; is indicated in the notes. the shapes in the sourcecode will ; appear upside-down RotationSequence ;NO FLIP BIT - FACE RIGHT .byte <CarRotation0;0 - point up .byte <CarRotation1;1 - .byte <CarRotation2;2 - .byte <CarRotation3;3 - .byte <CarRotation4;4 - point right .byte <CarRotation5;5 - .byte <CarRotation6;6 - .byte <CarRotation7;7 - .byte <CarRotation8;8 - point down ; WITH FlipThreshold - FACE LEFT .byte <CarRotation7;9 - .byte <CarRotation6;10 - .byte <CarRotation5;11 - .byte <CarRotation4;12 - point left .byte <CarRotation3;13 - .byte <CarRotation2;14 - .byte <CarRotation1;15 - org $FF00+CarDataAlign ; make sure all sprite graphics are at least 'CarDataAlign' bytes ; above the page in order to make sure that the vertical ; logic routine will work properly ; THIS STORES HALF OF THE ROTATIONS. ; THE OTHER HALF ARE GENERATED BY USING THE HARDWARE PLAYER FLIP FUNCTION ; note that this rotation frame doesn't need an upside down version because it is symmetrical ;9 frames * 8 bytes per frame = 56 bytes used CarRotation0;UP (ACTUAL) .byte %01011010; .X.XX.X. .byte %01111110; .XXXXXX. .byte %01100110; .XX..XX. .byte %00011000; ...XX... .byte %01011010; .X.XX.X. .byte %01111110; .XXXXXX. .byte %01011010; .X.XX.X. .byte %00011000; ...XX... CarRotation1 .byte %00000110; ....XX.. .byte %00011110; ..XXXX.. .byte %01110100; XXX.X... .byte %00011010; XX.XX.X. .byte %00011010; ...XX.X. .byte %01011111; .X.XXXXX .byte %00111100; ..XXXX.. .byte %00101100; ..X.XX.. CarRotation2 .byte %00011000; ...XX... .byte %01111000; .XXXX... .byte %01000010; .X....X. .byte %11011011; XX.XX.XX .byte %11011100; XX.XXX.. .byte %00011110; ...XXXX. .byte %00110110; ..XX.XX. .byte %00010000; ...X.... CarRotation3 .byte %00110000; ..XX.... .byte %00110010; ..XX.X.. .byte %01100011; .XX...XX .byte %01011110; .X.XXXX. .byte %11111111; XXXXXXXX .byte %11000111; XX...XXX .byte %00011100; ...XXX.. .byte %00000100; .....X.. CarRotation4;RIGHT|LEFT .byte %00000000; ........ .byte %11101110; XXX.XXX. .byte %01100100; .XX..X.. .byte %11011111; XX.XXXXX .byte %11011111; XX.XXXXX .byte %01100100; .XX..X.. .byte %11101110; XXX.XXX. .byte %00000000; ........ CarRotation5 .byte %00000100; .....X.. .byte %00011100; ...XXX.. .byte %11000111; XX...XXX .byte %11111111; XXXXXXXX .byte %01011110; .X.XXXX. .byte %01100011; .XX...XX .byte %00110010; ..XX.X.. .byte %00110000; ..XX.... CarRotation6 .byte %00010000; ...X.... .byte %00110110; ..XX.XX. .byte %00011110; ...XXXX. .byte %11011100; XX.XXX.. .byte %11011011; XX.XX.XX .byte %01000010; .X....X. .byte %01111000; .XXXX... .byte %00011000; ...XX... CarRotation7 .byte %00101100; ..X.XX.. .byte %00111100; ..XXXX.. .byte %01011111; .X.XXXXX .byte %00011010; ...XX.X. .byte %11011010; XX.XX.X. .byte %11101000; XXX.X... .byte %00111100; ..XXXX.. .byte %00001100; ....XX.. CarRotation8;DOWN (ACTUAL) .byte %00011000; ...XX... .byte %01011010; .X.XX.X. .byte %01111110; .XXXXXX. .byte %01011010; .X.XX.X. .byte %00011000; ...XX... .byte %01100110; .XX..XX. .byte %01111110; .XXXXXX. .byte %01011010; .X.XX.X. ;missiles are used as pseudoplayers ;data is encoded as width and pixel offsets. ;76543210 ;XXXX <- OFFSET (typically 0 through +8 pixels) ; XX <- NUSIZ data ;EXAMPLE: ;LDA (M0_ShapeFrame),Y ; ;STA HMM0 ; ;LSL ; ;LSL ; ;STA NUSIZ0 ; ;32 bytes used Ped1_1; OOOOWW__ .byte %11110100 ; .XX..... .byte %11100000 ; ..X..... .byte %11011000 ; ...XXXX. .byte %11010100 ; ...XX... .byte %11111000 ; .XXXX... .byte %00000000 ; ........ .byte %11010100 ; ...XX... .byte %11010100 ; ...XX... Ped1_2; OOOOWW__ .byte %11010100 ; ...XX... .byte %11010100 ; ...XX... .byte %11010100 ; ...XX... .byte %11010100 ; ...XX... .byte %11010100 ; ...XX... .byte %00000000 ; ........ .byte %11010100 ; ...XX... .byte %11010100 ; ...XX... Ped1_3; OOOOWW__ .byte %11110100 ; .XX..... .byte %00000000 ; ..X..... .byte %11011100 ; ...XXXX. .byte %11100100 ; ..XX.... .byte %11010100 ; ...XX... .byte %00000000 ; ........ .byte %11010100 ; ...XX... .byte %11010100 ; ...XX... Ped1_4; OOOOWW__ .byte %11011100 ; ...XXXX. .byte %11010000 ; ...X.... .byte %11011100 ; ...XXXX. .byte %11010100 ; ...XX... .byte %00001100 ; XXXX.... .byte %11101100 ; ..XXXX.. .byte %11110100 ; .XX..... .byte %11110100 ; .XX..... ;add score graphics later org $FFFC .word Start .word Start
Attachment:
DD_0006.bin
Description: Binary data
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Aw: Re: [stella] Pitfall! disassemb, cybergoth | Thread | Re: [stella] DD new build, Thomas Jentzsch |
Re: [stella] Pitfall! disassembly, Thomas Jentzsch | Date | Re: [stella] DD new build, Thomas Jentzsch |
Month |