LIST OFF ; *** D O N K E Y K O N G *** ; Copyright 1982 Coleco Industries, Inc. ; Programmer: Garry Kitchen ; Analyzed, labeled and commented ; by Dennis Debro ; Last Update: 25.11.2003 ; ; This was Garry's second Atari VCS game. For a second game I think this a great ; accomplishment. Remember, Garry learned to program the VCS by reverse engineering ; the hardware and various Activision games. ; ; His MainLoop is structured as... ; ; MainLoop ; -overscan ; -vertical blank ; -kernel ; jmp MainLoop ; ; Garry uses a horizontal position routine that *seems* to first appear in his ; Space Jockey game. This routine was modified over the years and has been seen ; in a number of games. ; ; To produce the PAL listing I used the CBS version. The PAL version adjusts the ; vertical blank time and overscan time to make the game produce 312 scan lines. ; The colors were also adjusted but it seems they missed the place in the kernel ; where Garry colors Mario directly. The speeds were not adjusted but the sound ; frequencies were. ; ; This game uses a lot overlays and a lot of offsets so I might have missed some ; variable meanings or table positions. processor 6502 include vcs.h LIST ON ;=============================================================================== ; A S S E M B L E R - S W I T C H E S ;=============================================================================== NTSC = 1 ; 1 = NTSC -or- 0 = PAL ;=============================================================================== ; T I A - C O N S T A N T S ;=============================================================================== HMOVE_L7 = $70 HMOVE_L6 = $60 HMOVE_L5 = $50 HMOVE_L4 = $40 HMOVE_L3 = $30 HMOVE_L2 = $20 HMOVE_L1 = $10 HMOVE_0 = $00 HMOVE_R1 = $F0 HMOVE_R2 = $E0 HMOVE_R3 = $D0 HMOVE_R4 = $C0 HMOVE_R5 = $B0 HMOVE_R6 = $A0 HMOVE_R7 = $90 HMOVE_R8 = $80 ; values for NUSIZx: ONE_COPY = %000 TWO_COPIES = %001 TWO_WIDE_COPIES = %010 THREE_COPIES = %011 DOUBLE_SIZE = %101 THREE_MED_COPIES = %110 QUAD_SIZE = %111 ; values for REFPx: NO_REFLECT = %0000 REFLECT = %1000 ; SWCHA joystick bits: MOVE_RIGHT = %01111111 MOVE_LEFT = %10111111 MOVE_DOWN = %00100000 MOVE_UP = %00010000 NO_MOVE = %11111111 ;============================================================================ ; U S E R - C O N S T A N T S ;============================================================================ ; color constants: BLACK = $00 WHITE = $0F IF NTSC ORANGE = $2F BROWN = $34 TURQUISE = $A8 BLUE = $8A YELLOW = $1E BLONDE = YELLOW RED = $46 RED_2 = RED SASH_RED = RED PURPLE = $6A LIGHT_GREEN = $DA BLUE_GREEN = $A6 ELSE ORANGE = $2B BROWN = $44 TURQUISE = $B7 BLUE = $C8 YELLOW = $28 BLONDE = $2E RED = $44 RED_2 = $46 ; this seemed to a mistake in the PAL translation SASH_RED = $69 PURPLE = $88 LIGHT_GREEN = $38 BLUE_GREEN = $C8 ENDIF IF NTSC VBLANK_TIME = $2D OVERSCAN_TIME = $23 ELSE VBLANK_TIME = $4B OVERSCAN_TIME = $40 ENDIF ; game state values GAME_OVER = $00 START_NEW_LEVEL = $01 LEVEL_COMPLETED = $02 MAX_OBSTACLES = $04 WALKWAY_HEIGHT = $1B HAMMER_GROUP = $04 NUM_WALKWAYS = $05 STARTING_MARIOS = $02 ; number of lives at the start of a game MARIO_MOVE_RATE = $02 ; decrement for faster mario -- increase for slower MAX_HAMMER_TIME = $03 HAMMER_HEIGHT = $0D JUMP_HANGTIME = $1F MARIO_STARTX = $31 BONUS_TIMER_DELAY = $7F PLAYER_HEIGHT = $14 MARIO_HEIGHT = $11 NUMBER_HEIGHT = $06 JUMPING_HEIGHT = $07 ; increase this value for the high jump :-) LADDER_RANGE = $03 FAIR_PIXEL_DELTA = $09 OBSTACLE_HEIGHT = $24 OBSTACLE_GRAPHIC_HEIGHT = $08 TOP_PLATFORM_VALUE = $0C BARREL_SPRITE_NUMBER = $00 FALLING_BARREL_SPRITE_NUMBER = $01 FIREFOX_SPRITE_NUMBER = $02 ; obstacle moving state OBSTACLE_MOVING_RIGHT = $01 OBSTACLE_MOVING_LEFT = $00 OBSTACLE_MOVING_DOWN = $F0 ; rivit constants HORIZ_LEFT_RIVIT = $36 ; horizontal position of left rivit HORIZ_RIGHT_RIVIT = $6A ; horizontal position of right rivit COMPLETE_RIVIT_WALKWAY = $12 LEFT_RIVIT_VALUE = $06 RIGHT_RIVIT_VALUE = $0C MAX_FIREFOX_LADDERS = $10 XMIN_LEVEL1 = $24 XMAX_LEVEL1 = $7C ; barrel constants XMIN_ODD_LEVEL0 = $29 XMAX_ODD_LEVEL0 = $75 YMIN_LEVEL0 = $0F STARTING_BARREL_VERT = TOP_PLATFORM_VALUE STARTING_BARREL_HORIZ = $25 BOTTOM_BARREL_PLATFORM_VALUE = $91 ; score constants STARTING_BONUS = $50 SMASHING_OBSTACLE = $08 ;============================================================================ ; Z P - V A R I A B L E S ;============================================================================ verPosP1 = $80 ; $80 - $83 jumpHangTime = $85 hammerTime = $86 playerScore = $87 ; $87-$88 marioFrameDelay = $89 attractModeTimer = $8A ; incremented every other frame when it reaches ; 0 then attract mode (color cycling) begins soundIndex = $8B gameState = $8C ; 10000000 = game in progress ; 00000010 = level complete (play music) ; 00000001 = start of new level ; 00000000 = game over losingLifeFlag = $8D ; set to #$FF when Mario loses a life attractMode = $8E ; when D7 = 1 then the game is in attract mode soundDuration = $8F gameScreen = $90 ; $00 = barrels $01 = firefox consoleDebounce = $91 ; timer to check the console switches backgroundColor = $92 horPosMario = $93 marioDirection = $94 ; Mario or Donkey Kong missilePointer = $95 ; $95 - $96 ballPointer = $97 ; $97 - $98 holdObstacleNumber = $99 ; used in overscan to loop through obstacles playfieldColor = $9A verPosMario = $9B horPosHammer = $9C zpZero = $9D ; always 0 -- seems not to be used zpZeroOrOne = $9E ; 0 for barrels 1 for firefox -- seems not to be used rivits = $9F ; $9F-$A2 numberOfLives = $A3 bonusTimer = $A4 zpMarioGraphics = $A5 ; $A5 - $C0 horPosP1 = $C1 ; $C1 - $C4 directionP1 = $C5 ; $C5 - $C8 ; %xxxx0001 = moving right ; %xxxx0000 = moving left ; %1111xxxx = moving down (ladder or off edge) barrelLadderNumber = $C9 ; $C9 - $CC coarseHorPosP1 = $CD ; $CD - $D2 fineHorPosP1 = $D3 ; $D3 - $D8 rightPF1Pointer = $D9 ; $D9 - $DA pf0Pointer = $DB ; $DB - $DC digitPointer = $DD ; $DD - $E5 ;------------------------------------------- pf2Pointer = digitPointer leftPF1Pointer = digitPointer+2 obstaclePointer = digitPointer+4 ; $E1 - $E2 marioGraphicPointer = digitPointer+6 ; $E3 - $E4 ;------------------------------------------- marioColorPointer = marioGraphicPointer audioFrequecyPointer = marioGraphicPointer marioOffset = marioGraphicPointer+2 ; $E5 ;------------------------------------------- joystickValue = marioOffset ; $E5 groupCount = joystickValue ; $E5 ;------------------------------------------- obstacleOffset = groupCount ; $E5 ;------------------------------------------- loopCount = $E6 randomSeed = $E7 frameCount = $E8 fireButtonDebounce = $E9 ladderNumber = $EA ; holds the ladder number Mario is on ; from top down ; ($00 - $08) for barrels ; ($12 - $21) for firefox jumpingDirection = $EB ; hold whether the player was pushing right or ; left on the joystick while jumping jumpingObstacle = $EC ; when 0 -- Mario is jumping obstacle (add points) walkwayNumber = $ED ; holds the walkway section Mario is in (0 - 5) ;------------------------------------------- obstacleNumber = walkwayNumber ;------------------------------------------- missile1Size = obstacleNumber ;------------------------------------------- temp = missile1Size hammerKernelVector = $EE ; pointer to hammer kernel ($EE - $EF) kernelVector = $F0 ; pointer to no ladder kerenel ($F0 - $F1) obstaclePointerLSB = $F2 ; $F2 - $F7 marioColorPointerLSB = $F8 ; $F8 - $FD ;=============================================================================== ; R O M - C O D E (Part 1) ;=============================================================================== SEG code org $F000 Start ; ; Set up everything so the power up state is known. ; sei cld ldx #$FF txs inx txa .clearLoop sta VSYNC,x inx bne .clearLoop jsr InitializeGame lda #STARTING_MARIOS sta numberOfLives dec consoleDebounce dec attractMode MainLoop lda backgroundColor sta COLUBK ldy #$FF sty WSYNC ; end the current scan line sty VBLANK ; start the vertical blank period (turn off TIA) lda #OVERSCAN_TIME sta TIM64T inc randomSeed lda #= 3 then check the next obstacle bcs .checkNextObstacle lda verPosMario ; get Mario's vertical position sec sbc verPosP1,x ; subtract it by the obstacle's vertical position cmp #$04 bcs .checkNextObstacle ; if it's >= 4 then check the next obstacle lda jumpingObstacle bmi JumpingMario ; already awarded the 100 points dec jumpingObstacle jsr Add100Points jmp JumpingMario .doneJumpingOverCheck inx stx jumpingObstacle JumpingMario lda soundDuration cmp #$01 bne .reduceHangtime lda #$0C sta AUDC0 lda jumpHangTime lsr sta AUDV0 .reduceHangtime dec jumpHangTime bne LF2CE lda verPosMario clc adc #JUMPING_HEIGHT sta verPosMario lda #$00 sta $84 beq LF2CE ReadFirebuttonForJump lda INPT4 ora losingLifeFlag bmi .clearFireButtonDebounce lda hammerTime bne .clearFireButtonDebounce lda marioDirection bpl LF2AD ; if Mario is moving horiz then branch jsr DetermineLadderMovement bcs .clearFireButtonDebounce lda #$00 sta marioDirection LF2AD: ldy fireButtonDebounce bne LF2CE iny sty soundDuration dec fireButtonDebounce lda SWCHA sta jumpingDirection lda verPosMario sec sbc #JUMPING_HEIGHT sta verPosMario lda #JUMP_HANGTIME sta jumpHangTime sta soundIndex bne LF2CE .clearFireButtonDebounce lda #$00 sta fireButtonDebounce LF2CE: ldx marioDirection ; Mario moving horiz then branch bpl LF2DD ldx #$05 jsr DetermineLadderMovement bcs LF2DA inx LF2DA: txa bne LF2E8 LF2DD: lda $84 and #$06 lsr ldx jumpHangTime beq LF2E8 lda #$04 LF2E8: tay lda verPosMario ; get the vertical position of Mario ldx #NUM_WALKWAYS .walkwayLoop cmp #$2E bcc SetMarioGraphicPointers ; no need to calculate walkway number dex ; reduce the walkway value sbc #WALKWAY_HEIGHT+1 ; subtract Mario's position by walkway height bcs .walkwayLoop SetMarioGraphicPointers sty temp sta marioOffset adc MarioColorTable,y sta marioColorPointerLSB,x lda #>MarioGraphics sta marioGraphicPointer+1 lda marioOffset clc adc MarioAnimationTable,y sta marioGraphicPointer jsr StoreMarioGraphics lda marioOffset sec sbc #WALKWAY_HEIGHT+2 bcc CheckMarioWithHammer sta temp cpx #$01 bcc CheckMarioWithHammer lda marioColorPointerLSB,x sbc #WALKWAY_HEIGHT+1 sta marioColorPointerLSB-1,x lda marioGraphicPointer sec sbc #WALKWAY_HEIGHT+1 sta marioGraphicPointer ldy #WALKWAY_HEIGHT LF32A: dec temp bmi LF336 lda (marioGraphicPointer),y sta zpMarioGraphics,y dey bpl LF32A LF336: lda #$06 sta temp lda #$00 LF33C: sta zpMarioGraphics,y dey dec temp bpl LF33C CheckMarioWithHammer lda hammerTime ; if Mario currently has the hammer then don't bne .setHammerHorizPosition ; reset the hammer time lda CXM1P bpl LF381 lda jumpHangTime beq LF381 lda #MAX_HAMMER_TIME ; set the time for Mario to hold the hammer sta hammerTime .setHammerHorizPosition lda #$09 ldy marioDirection bne .offsetHammerPosition ; Mario moving right so branch lda #$FE .offsetHammerPosition clc adc horPosMario sta horPosHammer ldy #= then don't change directions lda LF900,y ; store the number table value sta randomSeed ; in the randomSeed .changeDirection lda directionP1,x eor #$01 tay bpl .setFirefoxDirection ; unconditional branch .skipRandomDirection lda verPosP1,x ; get the vertical position of the firefox clc adc #FAIR_PIXEL_DELTA ; if the difference between the vertical position cmp verPosMario ; and Mario is not between FAIR_PIXEL_DELTA then bne .doneMovingCurrentFirefox ; finish firefox movement ldy #OBSTACLE_MOVING_LEFT lda horPosP1,x ; if the firefox is to the right of Mario cmp horPosMario ; then move the firefox left bcs .setFirefoxDirection ; if the firefox is to the left of Mario ldy #OBSTACLE_MOVING_RIGHT ; then more the firefox right .setFirefoxDirection sty directionP1,x .doneMovingCurrentFirefox jmp MoveNextObstacle MoveBarrelObject SUBROUTINE lda directionP1,x beq .moveBarrelLeft ; barrel moving left bmi .moveBarrelDown ; barrel falling down .barrelMovingRight inc horPosP1,x ; increment barrel horiz position bne BarrelRampMovement ; same as jmp but saves a byte (never 0) .moveBarrelLeft dec horPosP1,x ; decrement barrel horiz position BarrelRampMovement cpy #TOP_PLATFORM_VALUE ; if the barrel is on the top platform then beq .doneBarrelRampMovement ; branch -- no ramps there cpy #BOTTOM_BARREL_PLATFORM_VALUE; if the barrel is on the bottom platform then beq .checkBarrelDone ; branch -- no ramps ldy horPosP1,x ; get the horizontal position of the barrel asl bne LF4F0 ; branch if not moving left iny LF4F0: tya ldy #$07 .rampLoop dey bmi .doneBarrelRampMovement cmp RampHorizValues,y ; if the barrel horiz position is less than bcc .doneBarrelRampMovement ; the table value then branch bne .rampLoop inc verPosP1,x ; move barrel down the ramp .doneBarrelRampMovement ldy playerScore lda randomSeed cmp LF900,y ldy #$0C bcs LF50C ldy #$FF LF50C: lda verPosP1+1,x cpx #$03 bne LF514 lda verPosP1 LF514: sta obstacleOffset LF516: lda obstacleOffset sbc verPosP1,x LF51A: iny cpy #$12 bcs MoveNextObstacle cmp LFC50,y bcc LF51A lda horPosP1,x sbc #$01 cmp LadderHorizValues,y bne LF516 lda DownLadderTable,y sec sbc #FAIR_PIXEL_DELTA cmp verPosP1,x bne LF516 sty barrelLadderNumber,x lda directionP1,x ora #OBSTACLE_MOVING_DOWN bmi .setBarrelDirection .moveBarrelDown inc verPosP1,x inc verPosP1,x ldy barrelLadderNumber,x lda UpLadderTable,y sec sbc #FAIR_PIXEL_DELTA cmp verPosP1,x beq LF551 bcs MoveNextObstacle LF551: sta verPosP1,x lda directionP1,x eor #OBSTACLE_MOVING_DOWN | OBSTACLE_MOVING_RIGHT .setBarrelDirection sta directionP1,x jmp MoveNextObstacle .checkBarrelDone lda horPosP1,x cmp #XMIN_LEVEL1-1 ; if the barrel is not finished (off the screen) bne MoveNextObstacle ; then move the next barrel lda #$00 ; reset the vertical position of the barrel so sta verPosP1,x ; it can be reused MoveNextObstacle dex cpx loopCount beq DoneMovingObstacles jmp MoveObstacleLoop DoneMovingObstacles ldx #MAX_OBSTACLES-1 LF570 ldy #FALLING_BARREL_SPRITE_NUMBER lda verPosP1,x ; check to see if the obstacle is there beq .gotoNextObstacle ; if not then branch lda directionP1,x ; if the barrel is not falling down a ladder bpl .setRollingBarrelSprite ; then change the sprite to the barrel sprite lda barrelLadderNumber,x cmp #$0D bcc LF585 cmp verPosMario bcs .removeObstacle .setRollingBarrelSprite dey ; y = 0 -or- BARREL_SPRITE_NUMBER LF585: lda gameScreen beq LF58B ldy #FIREFOX_SPRITE_NUMBER LF58B: lda verPosP1,x stx obstacleNumber ldx #NUM_WALKWAYS+1 sec .walkwayLoop dex sbc #WALKWAY_HEIGHT+1 bcs .walkwayLoop adc #WALKWAY_HEIGHT+1 cpx #HAMMER_GROUP ; if the obstacle is not on a hammer bne .calculateObstaclePointers ; walkway then compute pointers asl CXP1FB ; check if the obstacle was hit by the hammer bpl .calculateObstaclePointers ; obstacle not hit -- compute pointers lda #SMASHING_OBSTACLE ; add the smashing score to the player's jsr IncrementScore ; score ldx obstacleNumber .removeObstacle lda #$00 sta verPosP1,x ; clear the verPos of the obstacle .gotoNextObstacle beq NextObstacle ; same as jmp -- saves a byte .calculateObstaclePointers sta obstacleOffset clc adc ObstacleTable,y sta obstaclePointerLSB,x lda obstacleOffset cmp #$12 bcc StoreObstaclePosition ror barrelLadderNumber+3,x cmp #$13 bcc StoreObstaclePosition txa beq StoreObstaclePosition lda obstaclePointerLSB,x sbc #WALKWAY_HEIGHT+1 sta obstaclePointerLSB-1,x ; ; the horizontal motion values are stored in RAM for each obstacle to save time ; in the kernel ; StoreObstaclePosition SUBROUTINE ldy obstacleNumber lda horPosP1,y ldy #$FD sec .coarseMoveLoop iny sbc #$0F bcs .coarseMoveLoop sty coarseHorPosP1,x eor #$0F asl asl asl asl adc #HMOVE_R7 sta fineHorPosP1,x ldx obstacleNumber NextObstacle dex bmi CheckToPlayDeathSound jmp LF570 CheckToPlayDeathSound lda losingLifeFlag bmi .continueDeathSound lda CXPPMM bpl .clearCollisions jsr PlayDeathSound .continueDeathSound lda soundDuration cmp #$04 beq .clearCollisions lda #$00 sta losingLifeFlag jsr StartNewScreen ldy #START_NEW_LEVEL dec numberOfLives bpl .setGameState jsr PlayGameOverMusic ldy #GAME_OVER sty numberOfLives .setGameState sty gameState .clearCollisions lda #$FF sta CXCLR jsr SetupKernelJumpVector ldx #$03 lda horPosHammer jsr PositionHammer ldy #$10 ; ball size 2 clocks ldx #$20 ; ball size 4 clocks lda missilePointer cmp #ObstacleSprites ; 2 set the MSB for the obstacles sta obstaclePointer+1 ; 3 ldy numberOfLives ; 3 .drawDonkeyKongLoop lda DonkeyKong-1,x ; 4 sta WSYNC ;-------------------------------------- sta GRP0 ; 3 = @3 draw Donkey Kong character lda #$00 ; 2 sta PF1 ; 3 = @8 clear the PF1 register lda Girlfriend-1,x ; 4 sta GRP1 ; 3 = @15 draw girlfriend character lda GirlfriendColors-2,x ; 4 sta COLUP1 ; 3 = @22 color girfriend character cpx #$0D ; 2 bcs .nextX ; 2³ cpx #$05 ; 2 bcc .nextX ; 2³ lda BarrelPFDataTable-5,x ; 4 sta rightPF1Pointer-5,x ; 4 lda LivesPFPattern,y ; 4 sta PF1 ; 3 = @45 draw lives indicators .nextX dex ; 2 bne .drawDonkeyKongLoop ; 2³ stx NUSIZ0 ; 3 x = 0 lda missile1Size ; 3 sta NUSIZ1 ; 3 lda frameCount ; 3 sta REFP1 ; 3 ldy #$01 ; 2 .drawDonkeyKongPlatform stx PF1 ; 3 clear the playfield registers to stx PF2 ; 3 avoid bleeding on next scan line sta WSYNC ;-------------------------------------- lda #$0F ; 2 sta PF1 ; 3 = @5 lda #$FF ; 2 sta PF2 ; 3 = @10 ldx #$06 ; 2 lda gameScreen ; 3 get the current game screen beq .zeroXLoop ; 2³ branch if the barrel level ldx rivits+3 ; 3 = @20 lda FireFoxLeftPF1Table,x ; 4 sta leftPF1Pointer ; 3 lda FireFoxPF2Table,x ; 4 sta pf2Pointer ; 3 lda FireFoxPF0Table,x ; 4 sta pf0Pointer ; 3 lda #>InitializationTable1 ; 2 sta leftPF1Pointer+1 ; 3 sta pf2Pointer+1 ; 3 sta pf0Pointer+1 ; 3 ldx #$01 ; 2 stx REFP1 ; 3 .zeroXLoop dex ; 2 bne .zeroXLoop ; 2³ dey ; 2 bpl .drawDonkeyKongPlatform; 2³ stx PF1 ; 3 stx PF2 ; 3 sec ; 2 sta WSYNC ;-------------------------------------- lda horPosMario ; 3 .coarseMoveMario sbc #$0F bcs .coarseMoveMario eor #$0F asl asl asl asl adc #HMOVE_R7 sta RESP0 sta HMP0 lda #RED_2 ; it seems they forgot about this in the PAL sta COLUP0 ; translation -- it's the same value as NTSC lda verPosMario sta WSYNC ;-------------------------------------- sta HMOVE ; 3 cmp #$0F ; 2 bcs .skipMarioDraw ; 2³ ldy #$1C ; 2 first byte of Mario sprite bne .drawMario ; 2³ .skipMarioDraw ldy #$00 ; 2 nop ; 2 .drawMario sty GRP0 ; 3 = @15 lda fineHorPosP1+5 ; 3 ldy coarseHorPosP1+5 ; 3 LF782: dey ; 2 bpl LF782 ; 2³ ldy #WALKWAY_HEIGHT ; 2 nop ; 2 sta RESP1 ; 3 sta HMP1 ; 3 stx HMP0 ; 3 clear player 0 horizontal movement (x = 0) lda #>MarioColors ; 2 sta marioColorPointer+1 ; 3 sta WSYNC ;-------------------------------------- sta HMOVE ; 3 bcc .drawMario_a ; 2³ bcs .skipMarioDraw_a ; 2² .drawMario_a ldx #$7E ; 2 second byte of Mario sprite .skipMarioDraw_a stx VSYNC,y ; 4 = @12 waste a cycle and store the byte in GRP0 (y = #$1B) ldx #NUM_WALKWAYS+1 ; 2 jmp EnterKernel ; 3 ; ; I'm not sure what the following byte is used for. Tracing the program shows that ; this bytes is never accessed. They're here to make the compiled ROM identical to ; the cart. ; IF NTSC .byte $9D ELSE brk ENDIF EndKernel jmp MainLoop BarrelHammerKernel SUBROUTINE stx PF2 ; 3 = @58 JumpBarrelHammerKernel lda (marioColorPointer),y ; 5 stx PF1 ; 3 clear the playfield registers (x = 0) beq .skipMarioDraw ; 2³ ldx zpMarioGraphics,y ; 4 .skipMarioDraw sta WSYNC ;-------------------------------------- sta COLUP0 ; 3 lda (missilePointer),y ; 5 sta ENAM1 ; 3 = @11 lda (ballPointer),y ; 5 sta ENABL ; 3 = @19 lda (obstaclePointer),y ; 5 sta GRP1 ; 3 = @27 stx GRP0 ; 3 = @30 lda (pf2Pointer),y ; 5 sta PF2 ; 3 = @38 lda (rightPF1Pointer),y ; 5 ldx #$00 ; 2 sta PF1 ; 3 = @48 dey ; 2 cpy #WALKWAY_HEIGHT - HAMMER_HEIGHT ; 2 bcs BarrelHammerKernel ; 2³ BarrelKernel SUBROUTINE stx PF2 ; 3 lda (marioColorPointer),y ; 5 stx PF0 ; 3 = @65 JumpBarrelKernel beq .skipMarioDraw ; 2³ ldx zpMarioGraphics,y ; 4 .skipMarioDraw sta WSYNC ;-------------------------------------- sta COLUP0 ; 3 = @3 stx GRP0 ; 3 = @6 ldx #$00 ; 2 lda (obstaclePointer),y ; 5 sta GRP1 ; 3 = @16 lda (leftPF1Pointer),y ; 5 sta PF1 ; 3 = @24 lda (pf2Pointer),y ; 5 sta PF2 ; 3 = @32 lda (pf0Pointer),y ; 5 sta PF0 ; 3 = @40 lda (rightPF1Pointer),y ; 5 sta PF1 ; 3 = @48 dey ; 2 cpy loopCount ; 3 bne BarrelKernel ; 2³ ContinueKernel SUBROUTINE lda (marioColorPointer),y ; 5 stx PF2 ; 3 = @63 sta COLUP0,x ; 4 = @67 bne .drawMario ; 2³ sta GRP0,x ; 4 = @73 beq .skipMarioDraw ; 3 .drawMario lda zpMarioGraphics+2 ; 3 sta GRP0 ; 3 = @76 ;-------------------------------------- .skipMarioDraw stx PF1 ; 3 = @3 stx PF0 ; 3 = @6 lda (obstaclePointer),y ; 5 sta GRP1 ; 3 = @14 ldx groupCount ; 3 beq EndKernel ; 2³ ldy coarseHorPosP1-1,x ; 4 bmi SkipObstacleMove ; 2³ .coarseMoveObstacle dey ; 2 bpl .coarseMoveObstacle ; 2³ sta RESP1 ; 3 lda fineHorPosP1-1,x ; 4 sta HMP1 ; 3 SkipObstacleMove SUBROUTINE ldy #$01 ; 2 sta WSYNC ;-------------------------------------- sta HMOVE ; 3 lda (obstaclePointer),y ; 5 sta GRP1 ; 3 = @11 lda (marioColorPointer),y ; 5 sta.w COLUP0 ; 4 = @20 bne .drawMario ; 2³ sta.w GRP0 ; 4 = @26 beq .skipMarioDraw ; 3 always 0 .drawMario lda zpMarioGraphics+1 ; 3 sta GRP0 ; 3 = @29 .skipMarioDraw lda gameScreen ; 3 beq .loadBarrelPFPointers ; 2³ ldy rivits-3,x ; 4 = @38 lda FireFoxLeftPF1Table,y ; 4 sta leftPF1Pointer ; 3 lda FireFoxPF2Table,y ; 4 sta pf2Pointer ; 3 lda FireFoxPF0Table,y ; 4 ldy #$00 ; 2 sta pf0Pointer,y ; 5 = @63 beq .skipBarrelPFPointers ; 2³ .loadBarrelPFPointers lda BarrelLeftPF1Table-1,x ; 4 = @39 sta leftPF1Pointer ; 3 lda BarrelPF2Table,x ; 4 sta pf2Pointer ; 3 lda BarrelPF0Table,x ; 4 sta pf0Pointer ; 3 lda BarrelRightPF1Table,x ; 4 sta rightPF1Pointer ; 3 dey ; 2 = @65 .skipBarrelPFPointers lda (obstaclePointer),y ; 5 sta GRP1 ; 3 = @73 (barrels) @74(firefox) lda (marioColorPointer),y ; 5 ;-------------------------------------- sta.w COLUP0 ; 4 = @5 (barrles) @6(firefox) bne .drawMario_a ; 2³ sta.w GRP0 ; 4 = @11 (barrels) @12 (firefox) beq .skipDrawMario_a ; 3 .drawMario_a lda zpMarioGraphics ; 3 sta GRP0 ; 3 = @14 (barrels) @15 (firefox) .skipDrawMario_a ldy #WALKWAY_HEIGHT ; 2 EnterKernel lda obstaclePointerLSB-1,x ; 4 sta obstaclePointer ; 3 lda marioColorPointerLSB-1,x ; 4 sta marioColorPointer ; 3 sta HMCLR ; 3 clear horizontal movement dex ; 2 nop ; 2 stx groupCount ; 3 cpx #HAMMER_GROUP ; 2 bne .skipHammerKernel ; 2³ ldx #$00 ; 2 jmp (hammerKernelVector) ; 5 .skipHammerKernel lda LoopCountTable,x ; 4 sta loopCount ; 3 ldx #$00 ; 2 lda (marioColorPointer),y ; 5 jmp (kernelVector) ; 5 FirefoxHammerKernel SUBROUTINE nop ; 2 = @54 lda (marioColorPointer),y ; 5 bne .drawMario ; 2³ nop ; 2 beq .skipMarioDraw ; 2³ .drawMario ldx zpMarioGraphics,y ; 4 .skipMarioDraw sta COLUP0 ; 3 = @69 lda FireFoxLeftPF2Data_1,y ; 4 sta PF2 ; 3 ;-------------------------------------- stx GRP0 ; 3 = @3 lda (missilePointer),y ; 5 sta ENAM1 ; 3 = @11 lda (obstaclePointer),y ; 5 sta GRP1 ; 3 = @19 lda (ballPointer),y ; 5 sta ENABL ; 3 = @27 lda FireFoxLeftPF1Data_1-3,y ; 4 sta PF1 ; 3 = @34 lda (marioColorPointer),y ; 5 lda marioColorPointer,y ; 4 ldx #$00 ; 2 dey ; 2 cpy #WALKWAY_HEIGHT - HAMMER_HEIGHT ; 2 bcs FirefoxHammerKernel ; 2³ FirefoxKernel SUBROUTINE lda (marioColorPointer),y ; 5 JumpFirefoxKernel beq .skipMarioDraw ; 2³ ldx zpMarioGraphics,y ; 4 .skipMarioDraw sta WSYNC ;-------------------------------------- sta COLUP0 ; 3 = @3 stx GRP0 ; 3 = @6 lda (obstaclePointer),y ; 5 sta GRP1 ; 3 = @14 lda (leftPF1Pointer),y ; 5 sta PF1 ; 3 = @22 lda (pf2Pointer),y ; 5 sta PF2 ; 3 = @30 ldx #$00 ; 2 lda (pf0Pointer),y ; 5 lda (pf0Pointer),y ; 5 dey ; 2 cpy loopCount ; 3 sta PF2 ; 3 = @50 bne FirefoxKernel ; 2³ jmp ContinueKernel ; 3 LF900: .byte $30,$50,$70,$90,$B0,$D0,$D0,$D0,$FF,$FF ;=============================================================================== ; R O M - C O D E (Part 2) ;=============================================================================== DetermineLadderMovement ldy ladderNumber lda verPosMario cmp UpLadderTable,y beq .allowVerticalMovement cmp DownLadderTable,y beq .allowVerticalMovement sec rts .allowVerticalMovement clc rts SetupKernelJumpVector lda gameScreen ; get the current game screen asl asl adc #$03 ; a = 3 for barrels and 7 for firefox tax ldy #$03 .vectorLoadLoop lda KernelVectorTable,x sta hammerKernelVector,y dex dey bpl .vectorLoadLoop ldx #$0A ldy #$00 lda gameState bpl BCD2DigitPtrs ldy #bonusTimer - playerScore - 1 ; set y to have an offset to the number of lives ;---------------------------------------------------------------BCD2DigitPtrs ; ; Garry uses y as an offset to load the value he is going to display. If the ; game is in progress then he is going to show the timer bonus. That's why y ; is set to #$1C here. ; ; Notice that the offset of #$1C is the number of lives. The number of lives ; will never be greater than 15 so when he masks the upper nibble, it will ; always be 0. ; BCD2DigitPtrs lda playerScore,y and #$F0 lsr adc #$00 sta digitPointer-4,x lda #>NumberFonts sta digitPointer-3,x dex dex lda playerScore,y and #$0F asl asl asl adc #$00 sta digitPointer-4,x lda #>NumberFonts sta digitPointer-3,x iny dex dex bpl BCD2DigitPtrs lda gameState bpl ExitBCD2DigitPtrs lda #