processor 6502 include "vcs.h" include "macro.h" ; DEFINES ;------------------------------------------------------------------------------ DISABLE = %00 ENABLE = %10 PATTERN = $80 ; storage location (1st byte in RAM) TIMETOCHANGE = 20 ; speed of "animation" - change as desired roverShpPtr = $88 ; colorPtr = $8A objectPtr = $8C animCount = $8E tempVar = $8F nextSky = $91 objColor = $92 btnPressed = $93 ; this is inefficient - I need only one bit. satX = $94 roverDir = $95 currZone = $96 jmpPtr = $98 sky1 = #0 sky2 = #1 sky3 = #2 sky4 = #3 hills = #4 fore = #5 ;------------------------------------------------------------------------------ ; UNDEFINED VARS ;------------------------------------------------------------------------------ SEG.U vars ORG $80 PlayerX ds 2 ; x coordinate PS_temp ds 1 SoundOn ds 1 flipRover ds 1 xParam ds 2 tempOne ds 1 tempTwo ds 1 tempThree ds 1 ;------------------------------------------------------------------------------ ; PROGRAM START ;------------------------------------------------------------------------------ SEG ORG $F000 ;------------------------------------------------ ; Once-only initialisation... Reset ; Clear RAM and all TIA registers ldx #0 lda #0 Clear sta 0,x inx bne Clear lda colorAddrLo,1 sta colorPtr lda colorAddrHi,1 sta colorPtr+1 lda #$FA sta COLUP0 ; player color ; lda #80 ; sta PlayerX+1 lda #$6F ; sound stuff sta AUDF0 lda #6 sta AUDC0 ldx #$30 stx objColor stx COLUP1 stx PlayerX ldx #0 stx flipRover stx animCount stx nextSky stx INTIM stx COLUBK stx currZone ldx #155 stx satX lda roverAddrLo,1 sta roverShpPtr lda roverAddrHi,1 sta roverShpPtr+1 lda objectiveAddrLo,1 sta objectPtr lda objectiveAddrHi,1 sta objectPtr+1 ;------------------------------------------------ StartOfFrame ; Start of new frame ; Start of vertical blank processing ; ldx objColor ; inx ; cpx #$B8 ; bne objColLoop ldx #$40 stx objColor objColLoop stx COLUP1 stx objColor ldy hills lda (colorPtr),y sta COLUPF ; set the playfield color ldx #$00 ldy #$00 lda #%01000000 ;Left? bit SWCHA bne SkipMoveLeft lda #%00001000 ;a 1 in D3 of REFP0 says mirror sprite dec PlayerX jmp playerMotion SkipMoveLeft lda #%10000000 ;Right? bit SWCHA bne skipFlip ; if you've gotten this far, and not detected a move, there is none lda #%00000000 ; a 0 in d3 of REFP0 cancels mirroring inc PlayerX playerMotion sta roverDir ldx PlayerX cpx #145 bne notRightEdge ldx #1 stx PlayerX notRightEdge cpx #0 bne notLeftEdge ldx #144 stx PlayerX notLeftEdge ldy animCount ; loads animCount into y iny cpy #4 bne skipFlip ldy #0 sty animCount ; resets animCount lda flipRover ; alternate the flip value eor #1 sta flipRover tax lda roverAddrLo,x ; set up the rover animation pointers sta roverShpPtr lda roverAddrHi,x sta roverShpPtr+1 skipFlip sty animCount stx SoundOn skyAnimation ldy INTIM bne skipSkyAnim ldy nextSky ; iny cpy #4 bne contSky ldy #0 contSky sty nextSky lda colorAddrLo,y sta colorPtr lda colorAddrHi,y sta colorPtr+1 lda #255 sta T1024T skipSkyAnim lda #0 sta btnPressed lda INPT4 bmi ButtonNotPressed inc btnPressed ButtonNotPressed ; CXPPMM is address for player/player collision dec satX ldx satX cpx #5 bne satComplete ldx # 155 stx satX satComplete ; call the positioning sr for the satellite ldx #0 lda satX jsr moveWithoutSync sty HMP0,x ; Fine positioning value ldx #1 lda satX ADC #7 jsr moveWithoutSync sty HMP0,x sta WSYNC sta HMOVE lda #$FA sta COLUP0 ; color for satellite sta COLUP1 lda #0 sta VBLANK lda #2 sta VSYNC sta WSYNC sta WSYNC sta WSYNC ; 3 scanlines of VSYNC signal lda #0 sta VSYNC lda #0 sta GRP0 sta GRP1 sta REFP0 sta REFP1 ldx #%00000000 stx PF0 stx PF1 stx PF2 jmpPoint0 jmpPoint1 jmpPoint2 jmpPoint3 ;------------------------------------------------ ; 37 scanlines of vertical blank... VerticalBlank ldx #0 stx COLUBK VerticalBlankRepeat sta WSYNC inx cpx #37 bne VerticalBlankRepeat skyZone0 ldy #0 lda (colorPtr),y sta COLUBK ldx #0 skyZone0Repeat sta WSYNC inx cpx #3 bne skyZone0Repeat satelliteZone ldy #0 lda (colorPtr),y sta COLUBK ldx #0 ldy #4 ;set y for satellite size satelliteZoneRepeat sta WSYNC dey lda tblSatellite1,y sta GRP0 lda tblSatellite2,y sta GRP1 cpy #0 inx cpx #4 bne satelliteZoneRepeat sta WSYNC ;reset sprites? lda #0 sta REFP0 sty GRP0 sty GRP1 skyZone1 ldy #0 ;color index for area 3 lda (colorPtr),y ;get color from current color pointer sta COLUBK ldx #0 skyZone1Repeat sta WSYNC inx cpx #4 bne skyZone1Repeat skyZone2 ldy #2 ;color index for area 2 lda (colorPtr),y ;get color from current color pointer sta COLUBK ;store it in background color ldx #0 ;reset vertical counter skyZone2Repeat sta WSYNC ;wait for sync inx ;increment x cpx #4 ;compare x to 4 bne skyZone2Repeat ;if not 4, back to skyzone2Repeat - fills 4 lines of color skyZone3 ldy #3 ;color index for area 3 lda (colorPtr),y ;get color from current color pointer sta COLUBK ldx #0 skyZone3Repeat sta WSYNC inx cpx #80 bne skyZone3Repeat calculateRoverPosition ldx #0 lda PlayerX jsr SetHorizPos2 lda roverDir sta REFP0 duneZone ldx #0 ;reset x for dune bitmaps duneZoneRepeat sta WSYNC ;fill in dune bitmaps lda tblHILL0,x sta PF0 lda tblHILL1,x sta PF1 lda tblHILL2,x sta PF2 inx cpx #10 bne duneZoneRepeat duneZone1 ldy #3 ;color index for area 3 lda (colorPtr),y ;get color from current color pointer sta COLUBK ldx #0 duneZone1Repeat sta WSYNC inx cpx #3 bne duneZone1Repeat fillSolidBackground ldx #$FF stx PF0 stx PF1 stx PF2 roverZone ldy #8 ;set y for rover size roverZoneRepeat sta WSYNC dey lda (roverShpPtr),y sta GRP0 lda tblROVERCol,y sta COLUP0 cpy #0 bne roverZoneRepeat sta WSYNC ;reset sprites? lda #0 sta REFP0 sta REFP1 sty GRP0 sty GRP1 grndZone ldx #0 ;reset x for ground grndZoneRepeat sta WSYNC inx cpx #15 bne grndZoneRepeat foreZone ldy fore ;color index for foreground lda (colorPtr),y sta COLUBK ldx #0 foreZoneRepeat sta WSYNC lda tblFORE0,x sta PF0 lda tblFORE1,x sta PF1 lda tblFORE2,x sta PF2 inx cpx #4 bne foreZoneRepeat ldx #$00 ;reset sprites stx PF0 stx PF1 stx PF2 stx COLUP0 stx COLUP1 ldx #36 zonenine sta WSYNC dex bne zonenine ldx #0 lda #40 jsr moveWithoutSync sty HMP0,x ; Fine positioning value ldx #1 lda #45 jsr moveWithoutSync sty HMP0,x sta WSYNC sta HMOVE controlZone ldx #5 controlZoneRepeat sta WSYNC dex lda meterlegend1,x sta GRP0 lda meterlegend2,x sta GRP1 ; lda meterlegend3,x ; sta GRP0 ; lda meterlegend4,x ; sta GRP1 cpx #0 bne controlZoneRepeat sta WSYNC ;reset sprites lda #0 sta REFP0 sta REFP1 sta GRP0 sta GRP1 endZone sta WSYNC inx cpx #21 bne endZone ;------------------------------------------------ lda #%01000010 sta VBLANK ; end of screen - enter blanking ; 30 scanlines of overscan... Overscan ldx #0 OverscanRepeat sta WSYNC inx cpx #30 bne OverscanRepeat ; sound stuff lda #$06 and SoundOn ; Kills sound if no game on by ANDing sta AUDV0 ; volume value w/$00 no-game value jmp StartOfFrame ;------------------------------------------------------------------------------ PositionSprites ; Set the horizontal position of the two sprites, based upon their coordinates ; This uses the tricky 2600 method of positioning (RESPx, HMPx, etc) ; Algorithm invented a looong time ago by persons unknown ; Re-invented Feb2001 by yours truly, then optimised according to code by Thomas Jenztsch sta WSYNC ; finish scanline sta HMCLR ; clear any previous movement PosSP lda xParam ; The functions x position ; A = xParam = 172 tay ; copy A to Y ; Y = A = xParam = 172 lsr ; divide A by 2 logical shift rt. lsr ; again lsr ; again lsr ; again ; A = xParam/16 = 172 sta PS_temp ; store A in PS_temp. ; PS_temp = A = 10 tya ; copy Y to A ; A = Y = xParam = 172 and #15 ; logical AND A with #15 ; A = xParam AND 15 = 12 ;** at this point: xParam = PS_Temp * 16 + A ;** example: 172 = 10 * 16 + 12 clc ; clear carry flag adc PS_temp ; add with carry ; A = A + PS_temp = 12 + 10 = 22 ldy PS_temp ; load Y with PS_temp ; Y = PS_temp = 10 cmp #15 ; compare A with #15 ; A ?= 15 bcc NH ; branch on carry clear to NH sbc #15 ; subtract with carry : A - #15 iny ; increment Y ; Y = PS_temp + 1 NH ; Use remainder for fine adjustment eor #7 ; logical OR with #7 ; A = 22 = 10101010 | 111 = 10101111 = 175 asl ; arithmetic shift left ; ??? asl ; again asl ; again asl ; again ; fine movement sta HMP0 ; store accum in HORIZ MOTION PLAYER 0 sta WSYNC ; finish scanline jsr Ret ; jump to RET bit 0 ; bit test A with 0 ; 15 cycles = 3 loops :) Jiggle dey ; decrement Y bpl Jiggle ; branch if plus to Jiggle sta RESP0 ; store A in RESET PLAYER 0 dex ; decrement X - for multiple players... bne PosSP ; branch if != 0 to PosSP sta WSYNC ; finish scanline sta HMOVE ; move all objects sta WSYNC ; finish scanline rts ; return from sub. Ret rts ; return from sub. (delay) moveWithoutSync CMP #17 ;2 compare A with 17 BCS doMove ;2 Branch if Carry is set to doMove SBC #4 ;2 Subtract(with carry) A from 4 BCS doMove ;2 Branch if Carry is set to doMove ADC #165 ;2 add with carry to 165 (159 + 6...) doMove STA WSYNC ;3 reset sync before HMOVE dloop SBC #15 ;2 Subtract(with carry) A from 15 BCS dloop ;2 ->5 cycles per iteration! EOR #7 ;2 xor A with 0000 0111 ASL ;2 Arith Shift Left A ASL ;2 Arith Shift Left A ASL ;2 Arith Shift Left A ASL ;2 Arith Shift Left A TAY ;2 Transfer A to Y STA RESP0,X ;4 Store A in RESP0,X (where X is the sprite (0 or 1)) RTS ;6 return from sub. ; SetHorizPos2 - Sets the horizontal position of an object. ; The X register contains the index of the desired object: ; X=0: player 0 ; X=1: player 1 ; X=2: missile 0 ; X=3: missile 1 ; X=4: ball ; This routine does a WSYNC both before and after, followed by ; HMOVE. So it takes two scanlines to complete. SetHorizPos2 sec ; set carry flag sta WSYNC ; start a new line sta HMCLR DivideLoop sbc #15 ; subtract 15 bcs DivideLoop ; branch until negative eor #7 ; calculate fine offset asl asl asl asl sta HMP0,x ; set fine offset sta RESP0,x ; fix coarse position sta WSYNC sta HMOVE ; apply the previous fine position(s) rts ; return to caller ;--------- some tables ------------------------ align 256 .tblJump .word jmpPoint0, jmpPoint1, jmpPoint2, jmpPoint3 ;tblJumpHI ; .byte >[jmpPoint0-1], >[jmpPoint1-1], >[jmpPoint2-1], >[jmpPoint3-1] ;tblJumpLO ; .byte <[jmpPoint0-1], <[jmpPoint1-1], <[jmpPoint2-1], <[jmpPoint3-1] zoneCounter .byte 8,4,2,2 tblHILL0 .byte 0,0,0,128,128,64,32,240,240,240 tblHILL1 .byte 0,128,192,96,120,126,255,255,255,255 tblHILL2 .byte 0,0,0,0,0,0,3,255,255,255 ;tblHILLB0 ; .byte ;tblHILLB1 ; .byte ;tblHILLB2 ; .byte tblFORE0 .byte 224,192,128,0 tblFORE1 .byte 255,231,131,1 tblFORE2 .byte 31,3,0,0 tblROVER .byte 116,170,92,255,124,8,8,12 tblROVER2 .byte 92,170,116,255,124,8,8,12 tblROVERCol .byte $90,$90,$90,$90,$02,$08,$08,$08 roverAddrLo .byte #tblROVER, #>tblROVER2 ; master color tables ; band1,band2,band3,mainsky,hills,foreground tblDAYcolor .byte $F8,$FA,$FC,$FE,$22,$20 tblDUSKcolor .byte $F6,$F8,$FA,$FC,$F0,$00 tblNIGHTcolor .byte $F2,$F4,$F6,$F6,$00,$00 colorAddrLo .byte #tblDAYcolor, #>tblDUSKcolor, #>tblNIGHTcolor, #>tblDUSKcolor objectiveAddrLo .byte #tblICONgeo,#>tblICONspect,#>tblICONatmo,#>tblICONmicro tblICONgeo .byte 255,223,189,145,129,129,129,126 ; geologic tblICONspect .byte 255,171,171,171,137,129,129,126 ; spectral tblICONatmo .byte 255,137,161,197,145,133,161,126 ; atmosphere tblICONmicro .byte 255,129,129,153,153,129,129,126 ; microscopic ;tblSKY ; .byte 248,248,248,250,250,250,252,254,254,254,254 tblSatellite1 .byte %11110000 .byte %11111011 .byte %01111101 .byte %00111100 .byte %00000000 tblSatellite2 .byte %00111100 .byte %10111110 .byte %11011111 .byte %10001111 .byte %00000000 tblRadio .byte %01111110 .byte %10000001 .byte %00111100 .byte %01000010 .byte %00011000 energy0 .byte %10000000 .byte %10000000 .byte %11100000 .byte %10010000 .byte %11100000 meterlegend1 .byte %11111111 .byte %10100000 .byte %10100000 .byte %10100000 .byte %10000000 .byte %10000000 meterlegend2 .byte %11111111 .byte %10000100 .byte %10000100 .byte %10000100 .byte %10000100 .byte %10000100 meterlegend3 .byte %11111111 .byte %00000001 .byte %00000001 .byte %00000001 .byte %00000001 .byte %00000001 meterlegend4 .byte %11111111 .byte %00000001 .byte %00000001 .byte %00000001 .byte %00000001 .byte %00000001 ;tblSIGNALH ; .byte 2,9,37,149,149,37,9,2 ;tblSIGNALV ; .byte 126,129,60,66,24,36,0,24 ;------------------------------------------------------------------------------ ORG $FFFA InterruptVectors .word Reset ; NMI .word Reset ; RESET .word Reset ; IRQ END ; main drawing scanlines ; determine zone number ; load colorPtr with the appropriate color per zone ; load the iterator with the appropriate count per zone ; mainiter ; ldy currZone ; cpy #3 ; beq afterjump ; --- not hit ; lda (colorPtr),y ; sta COLUBK ; lda zoneCounter,y ; this is the number of iterations for any given zone ; tax ; this is the local counter skyZone1 ; sta WSYNC ; sta HMOVE ; lda tblJumpHI,Y ; these get the address for the jmppoint and ; pha ; load it onto the stack ; lda tblJumpLO,Y ; the next rts will call it! ; pha ; rts ; this sends us to the current jumppoint ; here is a new trick i learned. Self modifying? ; asl ; adc #(.tblJump&$ff) ; this can be removed if jump_table is page aligned ; sta .out+1 ;.out jmp (.tblJump) ;rtnMain ; dex ; bne skyZone0 ; inc currZone ; jmp mainiter jmpPoint0 ; jmp rtnMain jmpPoint1 ; lda tblSatellite1,y ; sta GRP0 ; lda tblSatellite2,y ; sta GRP1 ; jmp rtnMain jmpPoint2 ; lda #0 ; sta GRP0 ; sta GRP1 ; jmp rtnMain jmpPoint3 ; jmp rtnMain