LIST OFF ; *** S K Y D I V E R *** ; Copyright 1978 Atari, Inc. ; Designer: Jim Huether ; ; Analyzed, labeled and commented ; by Dennis Debro ; Last Update: July 18, 2022 ; ; *** 87 BYTES OF RAM USED 41 BYTES FREE ; NTSC ROM usage stats ; ------------------------------------------- ; *** 4 BYTES OF ROM FREE ; ; PAL50 ROM usage stats ; ------------------------------------------- ; *** 0 BYTES OF ROM FREE ; ; ============================================================================== ; = THIS REVERSE-ENGINEERING PROJECT IS BEING SUPPLIED TO THE PUBLIC DOMAIN = ; = FOR EDUCATIONAL PURPOSES ONLY. THOUGH THE CODE WILL ASSEMBLE INTO THE = ; = EXACT GAME ROM, THE LABELS AND COMMENTS ARE THE INTERPRETATION OF MY OWN = ; = AND MAY NOT REPRESENT THE ORIGINAL VISION OF THE AUTHOR. = ; = = ; = THE ASSEMBLED CODE IS © 1978, ATARI, INC. = ; = = ; ============================================================================== processor 6502 ; ; NOTE: You must compile this with vcs.h version 105 or greater. ; TIA_BASE_READ_ADDRESS = $30 ; set the read address base so this runs on ; the real VCS and compiles to the exact ; ROM image ; ; NOTE: You must compile this with vcs.h version 105 or greater. ; include "tia_constants.h" include "vcs.h" include "macro.h" ; ; Make sure we are using vcs.h version 1.05 or greater. ; IF VERSION_VCS < 105 echo "" echo "*** ERROR: vcs.h file *must* be version 1.05 or higher!" echo "*** Updates to this file, DASM, and associated tools are" echo "*** available at https://dasm-assembler.github.io/" echo "" err ENDIF ; ; Make sure we are using macro.h version 1.01 or greater. ; IF VERSION_MACRO < 101 echo "" echo "*** ERROR: macro.h file *must* be version 1.01 or higher!" echo "*** Updates to this file, DASM, and associated tools are" echo "*** available at https://dasm-assembler.github.io/" echo "" err ENDIF LIST ON ;=============================================================================== ; A S S E M B L E R - S W I T C H E S ;=============================================================================== NTSC = 0 PAL50 = 1 PAL60 = 2 TRUE = 1 FALSE = 0 IFNCONST COMPILE_REGION COMPILE_REGION = NTSC ; change to compile for different regions ENDIF IF !(COMPILE_REGION = NTSC || COMPILE_REGION = PAL50 || COMPILE_REGION = PAL60) echo "" echo "*** ERROR: Invalid COMPILE_REGION value" echo "*** Valid values: NTSC = 0, PAL50 = 1, PAL60 = 2" echo "" err ENDIF ;=============================================================================== ; F R A M E - T I M I N G S ;=============================================================================== IF COMPILE_REGION = NTSC || COMPILE_REGION = PAL60 FPS = 60 ; ~60 frames per second VBLANK_TIME = 46 OVERSCAN_TIME = 33 H_SKY_DIVER_KERNEL = 140 SKY_DIVER_FALL_RATE = 3 UNSAFE_PARACHUTE_LAUNCH = 90 ELSE FPS = 50 ; ~50 frames per second VBLANK_TIME = 54 OVERSCAN_TIME = 45 H_SKY_DIVER_KERNEL = 174 SKY_DIVER_FALL_RATE = 4 UNSAFE_PARACHUTE_LAUNCH = 123 ENDIF ;=============================================================================== ; C O L O R - C O N S T A N T S ;=============================================================================== BLACK = $00 WHITE = $0E IF COMPILE_REGION = NTSC RED_ORANGE = $20 RED = $40 DK_BLUE = $90 ORANGE_GREEN = $E0 COLOR_LEFT_SKY_DIVER = ORANGE_GREEN + 6 COLOR_RIGHT_SKY_DIVER = RED + 6 COLOR_GROUND = RED_ORANGE + 2 COLOR_SKY = DK_BLUE + 4 ELSE GREEN_YELLOW = $30 BRICK_RED = $40 PURPLE = $80 BLUE = $D0 COLOR_LEFT_SKY_DIVER = BRICK_RED + 6 COLOR_RIGHT_SKY_DIVER = PURPLE + 8 COLOR_GROUND = GREEN_YELLOW + 2 COLOR_SKY = BLUE + 4 ENDIF COLOR_WIND_SOCK = WHITE - 2 BW_WIND_SOCK = WHITE - 2 BW_LEFT_SKY_DIVER = WHITE BW_RIGHT_SKY_DIVER = BLACK BW_GROUND = BLACK + 2 BW_SKY = BLACK + 4 ;=============================================================================== ; U S E R - C O N S T A N T S ;=============================================================================== ROM_BASE = $F000 XMIN = 0 XMAX = 141 XMIN_AIRPLANE = 0 XMAX_AIRPLANE = 135 H_FONT = 5 H_AIRPLANE = 8 H_JUMPING_SKY_DIVER = 8 H_PARACHUTING_SKY_DIVER = 16 H_WINDSOCK = 16 MIN_WIND_VELOCITY = -6 MAX_WIND_VELOCITY = 6 WIND_VELOCITY_ADJUSTMENT = 2 SKY_DIVER_PARACHUTE_DRIFT = 1 GAME_OVER = $FF AIRPLANE_ACTIVE = 0 AIRPLANE_INACTIVE = $FF INIT_REMAINING_JUMPS = 8 ; ; Landing Pad width values ; LANDING_PAD_WIDTH_LARGE = 10 LANDING_PAD_WIDTH_SMALL = 6 ; ; game selection values ; GS_MOVING_LANDING_PADS = 1 << 7 GS_CHICKEN = 1 << 0 LANDING_STATUS_UNSAFE = 1 << 7 LANDING_STATUS_SAFE = 1 << 6 POINT_REDUCTION_CRASHING_SKY_DIVER = 4 ;=============================================================================== ; M A C R O S ;=============================================================================== ;------------------------------------------------------- ; FILL_BOUNDARY byte# ; Original author: Dennis Debro (borrowed from Bob Smith / Thomas Jentzsch) ; ; Push data to a certain position inside a page and keep count of how ; many free bytes the programmer will have. ; ; eg: FILL_BOUNDARY 5, 234 ; position at byte #5 in page with $EA is byte filler FREE_BYTES SET 0 .BYTES_TO_SKIP SET 0 MAC FILL_BOUNDARY IF <. > {1} .BYTES_TO_SKIP SET (256 - <.) + {1} ELSE .BYTES_TO_SKIP SET (256 - <.) - (256 - {1}) ENDIF REPEAT .BYTES_TO_SKIP FREE_BYTES SET FREE_BYTES + 1 .byte {2} REPEND ENDM ;=============================================================================== ; Z P - V A R I A B L E S ;=============================================================================== SEG.U variables .org $81 landingPadGraphics ds 2 ;-------------------------------------- leftLandingPadGraphics = landingPadGraphics rightLandingPadGraphics = leftLandingPadGraphics + 1 skyDiverVertPositions ds 2 ;-------------------------------------- leftSkyDiverVertPosition = skyDiverVertPositions rightSkyDiverVertPosition = leftSkyDiverVertPosition + 1 skyDiverScanline ds 2 ;-------------------------------------- leftSkyDiverScanline = skyDiverScanline rightSkyDiverScanline = leftSkyDiverScanline + 1 skyDiverHeightValues ds 2 ;-------------------------------------- leftSkyDiverHeightValue = skyDiverHeightValues rightSkyDiverHeightValue = leftSkyDiverHeightValue + 1 skyDiverHorizPositions ds 2 ;-------------------------------------- leftSkyDiverHorizPosition = skyDiverHorizPositions rightSkyDiverHorizPosition = leftSkyDiverHorizPosition + 1 skyDiverCoarsePositionValues ds 2 ;-------------------------------------- leftSkyDiverCoarsePositionValue = skyDiverCoarsePositionValues rightSkyDiverCoarsePositionValue = leftSkyDiverCoarsePositionValue + 1 skyDiverFinePositionValues ds 2 ;-------------------------------------- leftSkyDiverFinePositionValue = skyDiverFinePositionValues rightDiverFinePositionValue = leftSkyDiverFinePositionValue + 1 skyDiverVerticalDelayValues ds 2 ;-------------------------------------- leftSkyDiverVerticalDelayValue = skyDiverVerticalDelayValues rightSkyDiverVerticalDelayValue = leftSkyDiverVerticalDelayValue + 1 playfieldControlValues ds 1 windSockGraphicsPtrs ds 4 ;-------------------------------------- leftHalfWindSockGraphicsPtrs = windSockGraphicsPtrs rightHalfWindSockGraphicsPtrs = leftHalfWindSockGraphicsPtrs + 2 skyDiverGraphicsPtrs ds 4 ;-------------------------------------- leftSkyDiverGraphicsPtrs = skyDiverGraphicsPtrs rightSkyDiverGraphicsPtrs = leftSkyDiverGraphicsPtrs + 2 airplaneVelocity ds 2 ;-------------------------------------- leftPlayerAirplaneVelocity = airplaneVelocity rightPlayerAirplaneVelocity = leftPlayerAirplaneVelocity skyDiverDescentSpeed ds 2 ;-------------------------------------- leftSkyDiverDescentSpeed = skyDiverDescentSpeed rightSkyDiverDescentSpeed = leftSkyDiverDescentSpeed + 1 windResistance ds 1 zp_unused_00 ds 1 tmpPlayerScoreGraphicValues ds 2 ;-------------------------------------- tmpLeftPlayerScoreGraphicValue = tmpPlayerScoreGraphicValues tmpRightPlayerScoreGraphicValue = tmpLeftPlayerScoreGraphicValue + 1 ;-------------------------------------- tmpHueMask = tmpLeftPlayerScoreGraphicValue ;-------------------------------------- tmpRemainingJumpsValue = tmpHueMask ;-------------------------------------- tmpRemainingJumpsGraphicData = tmpRemainingJumpsValue ;-------------------------------------- tmpPlayerScoreValue = tmpRemainingJumpsGraphicData ;-------------------------------------- tmpColorXOR = tmpRightPlayerScoreGraphicValue ;-------------------------------------- scoreLSBOffsets ds 2 ;-------------------------------------- leftPlayerScoreLSBOffset = scoreLSBOffsets rightPlayerScoreLSBOffset = leftPlayerScoreLSBOffset + 1 scoreMSBOffsets ds 2 ;-------------------------------------- leftPlayerScoreMSBOffset = scoreMSBOffsets rightPlayerScoreMSBOffset = leftPlayerScoreMSBOffset + 1 gameVariation ds 1 gameSelection ds 1 displayGameSelection ds 1 selectSwitchDebounce ds 1 varyingWindFactorRate ds 1 skyDiverDrag ds 1 frameCount ds 1 skyDiverHorizontalSpeed ds 2 ;-------------------------------------- leftSkyDiverHorizontalSpeed = skyDiverHorizontalSpeed rightSkyDiverHorizontalSpeed = leftSkyDiverHorizontalSpeed + 1 landingPadEdgeValueIndex ds 1 initLadingPadGraphicValue ds 1 landingPadLeftEdgeValues ds 2 ;-------------------------------------- leftSkyDiverPadLeftEdgeValue = landingPadLeftEdgeValues rightSkyDiverPadLeftEdgeValue = leftSkyDiverPadLeftEdgeValue + 1 landingPadRightEdgeValues ds 2 ;-------------------------------------- leftSkyDiverPadRightEdgeValue = landingPadRightEdgeValues rightSkyDiverPadRightEdgeValue = leftSkyDiverPadRightEdgeValue + 1 landingPadWidth ds 1 remainingJumps ds 1 windSockColor ds 1 groundColor ds 1 landingPadMovementIndex ds 1 remainingJumpsGraphicIdx ds 1 skyDiverLandingPadDistance ds 2 ;-------------------------------------- leftSkyDiverLandingPadDistance = skyDiverLandingPadDistance rightSkyDiverLandingPadDistance = leftSkyDiverLandingPadDistance + 1 gameState ds 1 roundPauseTimer ds 2 ;-------------------------------------- roundPauseTimerFraction = roundPauseTimer roundPauseTimerInteger = roundPauseTimerFraction + 1 soundVolumeIndexValues ds 2 ;-------------------------------------- leftPlayerVolumeIndexValue = soundVolumeIndexValues rightPlayerVolumeIndexValue = leftPlayerVolumeIndexValue + 1 suppressRightPlayerScore ds 1 colorCycleMode ds 1 skyDiverLandingStatus ds 2 ;-------------------------------------- leftSkyDiverLandingStatus = skyDiverLandingStatus rightSkyDiverLandingStatus = leftSkyDiverLandingStatus + 1 skyDiverHorizontalVelocity ds 2 ;-------------------------------------- leftSkyDiverHorizVelocity = skyDiverHorizontalVelocity rightSkyDiverHorizVelocity = leftSkyDiverHorizVelocity + 1 windVelocity ds 1 gameStartTimer ds 1 airplaneActiveState ds 2 ;-------------------------------------- leftPlayerAirplaneActiveState = airplaneActiveState rightPlayerAirplaneActiveState = leftPlayerAirplaneActiveState + 1 skyDiverReleasedStatus ds 2 ;-------------------------------------- leftSkyDiverReleasedStatus = skyDiverReleasedStatus rightSkyDiverReleasedStatus = leftSkyDiverReleasedStatus + 1 skyDiverParachuteLaunchStatus ds 2 ;-------------------------------------- leftSkyDiverParachuteLaunchStatus = skyDiverParachuteLaunchStatus rightSkyDiverParachuteLaunchStatus = leftSkyDiverParachuteLaunchStatus + 1 skyDiverLandingPoints ds 2 ;-------------------------------------- leftSkyDiverLandingPoints = skyDiverLandingPoints rightSkyDiverLandingPoints = leftSkyDiverLandingPoints + 1 skyDiverParachuteReleaseVertPos ds 2 ;-------------------------------------- leftSkyDiverParachuteReleaseVertPos = skyDiverParachuteReleaseVertPos rightSkyDiverParachuteReleaseVertPos = leftSkyDiverParachuteReleaseVertPos + 1 airplaneHorizPositions ds 2 ;-------------------------------------- leftPlayerAirplaneHorizPosition = airplaneHorizPositions rightPlayerAirplaneHorizPosition = leftPlayerAirplaneHorizPosition + 1 currentProcessingPlayerIndex ds 1 playerScores ds 2 ;-------------------------------------- leftPlayerScore = playerScores rightPlayerScore = leftPlayerScore + 1 echo "***",(* - $80 - 2)d, "BYTES OF RAM USED", ($100 - * + 2)d, "BYTES FREE" ;=============================================================================== ; R O M - C O D E ;=============================================================================== SEG Bank0 .org ROM_BASE Start ; ; Set up everything so the power up state is known. ; sei ; disable interrupts cld ; clear decimal mode lda #0 tax .clearLoop sta VSYNC,x inx bne .clearLoop dex ; x = -1 txs ; set stack to the beginning stx gameState ; set to GAME_OVER stx selectSwitchDebounce stx suppressRightPlayerScore jsr ResetGameSelectionToStartingValue MainLoop VerticalSync lda #DUMP_PORTS | DISABLE_TIA sta WSYNC ; wait for next scan line sta VBLANK ; disable TIA (i.e. D1 = 1) sta VSYNC ; start vertical sync (D1 = 1) sta WSYNC sta WSYNC lda #STOP_VERT_SYNC sta WSYNC sta VSYNC ; end vertical sync (D1 = 0) lda #VBLANK_TIME sta TIM64T ; set timer for vertical blank time clc lda frameCount ; get current frame count adc #1 sta frameCount bcc DetermineObjectColorValues inc colorCycleMode DetermineObjectColorValues lda SWCHB ; read the console switches ldx #7 ldy #9 and #BW_MASK ; keep the B/W switch value beq .determineHueMaskValue ; branch if set to B&W ldx #$F7 ldy #4 .determineHueMaskValue lda gameState ; get the current game state bmi .setHueMaskValue ; branch if GAME_OVER ldx #$FF .setHueMaskValue and colorCycleMode sta tmpColorXOR stx tmpHueMask ldx #<[COLUBK - COLUP0] .setObjectColorValues lda GameColorTable,y eor tmpColorXOR and tmpHueMask sta COLUP0,x cpx #<[COLUPF - COLUP0] bne .nextColorValue ; branch if not Wind Sock color sta windSockColor .nextColorValue dey dex bpl .setObjectColorValues lda GameColorTable,y eor tmpColorXOR and tmpHueMask sta groundColor ; set color for ground lda gameVariation ; get current game variation value lsr ; shift GS_CHICKEN to carry bcc .getPlayerDifficultySettings ; branch if not GS_CHICKEN lda #$C0 ; simulate both players set to PRO bne SetAirplaneVelocityValues ; unconditional branch .getPlayerDifficultySettings lda SWCHB ; read the console switches SetAirplaneVelocityValues ldx #1 .setAirplaneVelocityValues asl ; shift difficulty settings to carry bcc .setAmateurAirplaneVelocity ; branch if set to AMATEUR ldy ProAirplaneVelocityValues,x bcs .setAirplaneVelocity ; unconditional branch .setAmateurAirplaneVelocity ldy AmateurAirplaneVelocityValues,x .setAirplaneVelocity sty airplaneVelocity,x dex bpl .setAirplaneVelocityValues lda SWCHB ; read the console switches lsr ; shift RESET to carry bcs .checkForGameSelect ; branch if RESET not pressed lda #0 ldx #> 4 beq AnimateJumpingSkyDiver ; branch if not MOVE_DOWN lda skyDiverVertPositions,x ; get Sky Diver vertical position cmp #UNSAFE_PARACHUTE_LAUNCH bcs AnimateJumpingSkyDiver sta skyDiverParachuteReleaseVertPos,x;set parachute release vertical position lda #SKY_DIVER_PARACHUTE_DRIFT sta skyDiverDescentSpeed,x ; set Sky Diver descent for open parachute sta skyDiverParachuteLaunchStatus,x;set to non-zero for parachute launched lda #4 sta soundVolumeIndexValues,x lda #<~[H_PARACHUTING_SKY_DIVER - 1] sta skyDiverHeightValues,x txa ; move player index value to accumulator asl ; mulitply value by 2 tay lda #> 4 beq .checkJoystickRightMovement ; branch if not MOVE_LEFT sec lda skyDiverDrag ; get Sky Diver horizontal drag value sbc windResistance ; subtract current Wind resistance sta skyDiverDrag jmp .adjustDragWithWindVelocity .checkJoystickRightMovement tya ; move joystick values to accumulator and #<~[MOVE_RIGHT] >> 4 beq .adjustDragWithWindVelocity ; branch if not MOVE_RIGHT clc lda skyDiverDrag ; get Sky Diver horizontal drag value adc windResistance ; increment by current Wind resistance sta skyDiverDrag .adjustDragWithWindVelocity lda windVelocity ; get wind velocity value clc adc skyDiverDrag sta skyDiverDrag ldy soundVolumeIndexValues,x bmi .doneSkyDiverHorizontalMovement lda ParachuteOpenVolumeValues,y sta AUDV0,x lda #8 sta AUDC0,x dec soundVolumeIndexValues,x ; decrement sound volume index .doneSkyDiverHorizontalMovement jmp DetermineSkyDiverLanding CheckToReleaseSkyDiver lda INPT4,x ; read the player's fire button value bmi .doneDetermineSkyDiverLanding; branch if fire button not pressed ldy #0 sty skyDiverHorizontalVelocity,x iny sty skyDiverVertPositions,x lda airplaneHorizPositions,x ; get Airplane horizontal position clc adc #4 ; offset the horizontal position clc adc airplaneVelocity,x ; increment by Airplane velocity sta skyDiverHorizPositions,x ; set jumping Sky Diver horizontal position lda airplaneVelocity,x ; get Airplane velocity asl ; shift Airplane speed to upper nybbles asl asl asl asl sta skyDiverHorizontalSpeed,x txa ; move player index value to accumulator asl ; multiply value by 2 tay lda #WindSockGraphics sta windSockGraphicsPtrs + 1,y sta skyDiverGraphicsPtrs + 1,y dex bpl .resetGameVariables rts SetWindSockGraphicPointers and #$FF bpl .setWindSockGraphicsForEastWind;branch if wind blowing East eor #$FF ; get 1's complement value clc adc #1 ; add 1 for absolute value asl ; multiply by 8 (i.e. H_WINDSOCK / 2) asl asl clc adc #