Jumpman Reverse Engineering Notes
(for the Atari 8-bit version)
Our descriptions below are: Copyright © 2016-2020, Rob McMullen & Kay Savetz. Licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International license. See https://creativecommons.org/licenses/by-nc-sa/4.0/
The game Jumpman is: Copyright © 1983 Randy Glover
1000-2000: Scratch area for screen construction
7000 - 7e64: gameplay display memory
Level Definition Table Examples
Harvest Table & Painting Table
Notes on The Original 30 Levels
The Peanut Harvest Error Screen
Code to run when a particular peanut is taken
Code to run with any/every peanut is taken
Code to run constantly during the level
Appendix 1: Notes On Our Reference Disk Image
In the documentation for the game, you are described as running around defusing bombs by touching them. We started calling them peanuts in our documentation because Kay called them that during his original gameplay experience in the 1980s, and so we wouldn't get flagged and placed on watchlists because our email traffic contained hundreds of references to bombs.
peanut: The bombs from the original manual; what you're collecting to advance to the next level
harvest: The act of colliding with a peanut and removing it from the level
trigger function: A pointer to a routine that will run when that particular peanut is harvested. This is not the same as trigger painting described below.
trigger painting: A pointer to an entry in the trigger painting table that draws or erases level elements when the peanut is harvested. Note that this is entirely separate from the trigger function above. A peanut may have one, both, or neither defined.
Start layout name
17 25 easy does it
33 41 robots 1
49 57 bombs away
65 73 jumping blocks
81 89 vampire
97 105 invasion
113 121 GP I
129 137 builder
145 153 look out below
161 169 hotfoot
177 185 runaway
193 201 robots I
209 217 hailstones
225 233 dragonslayer
241 249 GP II
257 265 ride around
273 281 roost
289 297 roll me over
395 313 ladder challenge
321 329 figureit
337 345 jump n run
353 361 freeze
369 377 follow the leader
385 393 the jungle
401 409 mystery maze
417 425 mystery maze
433 441 mystery maze
449 457 gunfighter
465 473 robots III
481 489 now you see it
497 505 going down
513 521 GP III
677 demo (search for fc0040fd)
Our reference disk image is available on AtariMania; see the Appendix for additional notes on the format and contents of that particular disk image.
NOTE: all values in hex unless either super-obvious or otherwise noted.
034f-035e: working data for level construction?
0a00: jump target: display number of players screen and wait for keypress in location 30ff
0bb8: jump target: from 69cc, after high score
0bfc-?: local storage
0bfc: important var; used by 0f08 to see if 7de8 array needs to be cleared?
0c00: subroutine: called from 5620
0ca0: entry point: called after girders crumbled? jumps to 5300 after a few instructions
0ed0-?: local storage
0ed0: number of players pressed
0ed2: number of players copy? from 2632
0f08: after level completed; start of new level init?
1000: temporary ram for construction of screen
1800: working copy of level storage data?
1831: ? init from 5590, copied value from $0700
23eb: jump target: from 5687; game options entry point?
2476: jump target for OPTION pressed during game options list
2481: CMP #06: number of items in the game options list
248a: STA $32fe when using "ba r 32fe", so this is where the game option value is stored
24ec: called after START pressed, in turn calls 0a00
2507,x: table starting sector, low byte; index based on 2603 (starts at 1)
250d,x: table starting sector, high byte
2508: 11, 91, 31, 11, c1
250e: 00, 00, 01, 00, 00
2513: subroutine: set menu colors to default values
2603: index value (01 - 05) of level selected in GAME OPTIONS screen (01 = beginner, 05 = randomizer). Used in an a DLI interrept?
2680: subroutine:
2700: subroutine: draw an object. The object description appears to be in triples, a color and x and y offsets. As input to this function, the color is stored in $0358, x coord in $55 and y coord in $54.
27e0: subroutine:
(Labels that the Omnivore assembler recognizes are in bold. If the assembler finds these labels in the assembly code, the address of the label will be copied into the proper location in the level storage area.)
2800-2801: level number in ATASCII, copied to 4636-4637 and screen RAM
2802-2809: These 8 bytes comprise a jump table to 4 vertical blank routines that each level can use for custom game logic. In typical levels, they are used for bullet hits and levels with custom code can use them to perform special tasks like moving robots or update falling objects.
2802-2803: vbi1
2804-2805: vbi2
2806-2807: vbi3
2808-2809: vbi4 if you override vbi4, you will need to either end it with a JMP $49a0 or handle player/missile collisions yourself to test for Jumpman death.
In these routines, the address 311b is used to do nothing special. If all 4 are 311b, there will be no bullet hit recognition, no robots, etc.
If you write custom code for vbi1 through 3, end it with JMP $311b — not RTS. See the note above for vbi4, and for an example, check out Kay’s video: https://www.youtube.com/watch?v=_RRrynvRbZs
(Note that the 6502 is little-endian, so when you look at the hex values in Omnivore or in a disassembly, the address 311b will appear as 1b31 in consecutive bytes).
2802-2803 EDL superjumpman turned 1b31 to 0029
lookout: changed to 1b31, no falling stuff
2804-2805 ladder b829 - change 0029 to not die when shot
edl change 0029 everything s l o w
2806-2807 more of the same
2808-2809 edl change to 1b31 no die when shot
280a-f is always c694aa780f00
280a color of score/bonus area border
280b probably unused color register
280c color of "L=" in the score area
280d color of words SCORE and BONUS
280e color of remaining lives jumpmen
280f color of score/bonus area background
2810-2817 is always the same
2810,2811: dead_begin: addr: vector to call when start to fall to death, copied to 30b7. If you create a custom routine, end it with JMP $4200
2812,2813: dead_at_bottom: addr: vector called when dying and bounced down to the bottom, before music. copied to 30b9. If you create a custom routine, end it with JMP $4580
2814,2815: dead_falling: addr: vector called while falling to death, after the 2810 routine but before 2812. Usually points to 311b. If you create a custom routine, end it with JMP $311b
2816-2817: ???????: addr: always vector to 30e0, which does CLC, RTS.
The next 18 bytes clarify the position of ladders and down ropes. The game uses PM-playfield collision detection to know what Jumpman is touching. Girders and up-ropes are the same color (defined at 282e): if Jumpman touches that color, he will always climb. Ladders and down-ropes are also the same color (defined at 282f), but Jumpman must behave differently with each (ladders are self-selected up and down; down-ropes force downward movement.) So these 18 bytes tell the game: when Jumpman is touching that color, how to tell a ladder from a down-rope. This would be unnecessary if there was one more color to work with.
2818-2823 12 bytes that describe which vertical columns contain ladders
So ladders can appear in 12 columns.
This number is the x position of the ladder from level description plus 30.
For instance if the x position is 0c (fc 2c 40 fd 0c 05 0b fd)
this number should be 3c
Don't have to define every ladder; just every column with 1+ ladders
So multiple ladders in the same column only get one entry here.
2824-2829 6 bytes that describe which vertical columns contain down ropes.
So ropes can appear in 6 columns.
This number is the x position of the rope from level description plus 2e.
For instance if the x position is 04 (fc c0 40 fd 04 07 04 fd)
this number should be 32
282a dunno. always 0f
282b Jumpman shadow (player1(?)) color
282c player2(?) color
282d player3(?) color
282e girder and up ropes color
282f ladder and down ropes color
2830 peanut color
2831 bullet color; missiles combined to player4 color
2832 background color
2833-4 How many points is a peanut worth, low and hi bytes
2835,2836: bonus value for level (e.g. dc,05 = 1500 decimal
2837,2838: addr: start of level definition table (usually 2c00), copied to $3042
2839 Jumpman starting position X. Left is 34. Middle is 7c. Right is c6.
283a Jumpman starting position Y. Top is 20. Bottom is c0
283b,283c: gameloop: addr: vector to the level’s main game loop code. (this is usually set to 2880 or 2860), called from level setup routine at $4460. More on this in the custom code notes, below.
283d Max number of bullets. Glover’s levels use 0 (no shooting), 1, 2, and 4. 3 works. Anything else breaks things, sometimes to hilarious effect.
283e # of peanuts you need to take to complete the level. This location is decremented as you take peanuts as the level progresses, which is super useful for custom level code.
283f is always 4c (JMP) because it and the next two bytes are a jump target from the game loop
2840-2841: out_of_lives: addr: vector when lost last life. Always points to $4ffd except Grand Puzzle II. If you create a custom routine, end it with JMP $4ffd
2842 -> 30ee # sector number of next level
2843 -> 30ef
2844-2845: level_complete: addr: successful completion of level. Always points to $4c00 except on Grand Puzzle II & The Jungle. If you create a custom routine, end it with JMP $4c00
The Jungle: The next level is Mystery Maze. It's setting which maze to go to depending on the number of lives you have left. If you have less than 3, it goes to maze #1. Less than 5, goes to maze #3. 5 or more goes to maze #2.
GP II: ???
2846: X harvest offset
2847: Y harvest offset
2848: always 20 (i.e. JSR)
2849-284a: collect_callback: subroutine called every time a peanut is taken. If that functionality isn’t needed, the default is 284b (e.g. the next byte because that’s always RTS). Vampire uses a custom routine at $2b00, Look Out Below has one at $2980, etc. If you create a custom routine, end it with RTS.
284b: always 60 (i.e. RTS)
284c: always FF (target for harvest table if no action to be taken)
284e,284f: addr: start of the harvest table
2850-2851: sometimes used as data storage (flags, timers) in levels
2860 (sometimes): start of gameplay loop or setup code (pointed to by $283b)
2880 (usually): start of gameplay loop code (pointed to by $283b)
This routine is customizable per level, but tends to be the same most of the time. Makes jumpman fade in and play the appearance sound, checks to see if all peanuts are taken (if so, level complete), checks to see if you’re out of reserve jumpmen (if so, game over).
All the game mechanics (collision detection, jumpman movement, enemy logic, etc.) seem runs in the vertical blank. This routine is called in a continuous loop in the normal processing time until the end of level condition is reached, whereupon it jumps to the vector at 2844. If the player runs out of jumpmen before completing the level, it jumps to 283f which is another jump (usually to $4ffc).
2900: VBI code
2a80-4 something to do with PM graphics Missiles through PM3
2b00: VBI code
2bec-2bff: title
2c00 (usually): level definition table (see Level Definition Table Examples)
2cxx: harvest table (pointed to by 284e), usally immediately after level def table
tuples of 7 bytes: (See the Harvest Table Encoding section for more details)
0: index/identifier (x/y encoding), FF ends the table
1: x position
2: y position
3,4: addr: trigger function, called when harvested
5,6: addr: what painting action to take, points to $284c if no painting action
3000-3017: vbi_vector_table vertical blank vector table
3018: vbi_next_index next vertical blank vector number (incremented by 2)
3019: vbi_counter1 These 6 counters are incremented every VBI. Counters that levels can use for whatever? e.g. in demo level, the 301e counter is used to change colors to make "SELECT" flash
301a: vbi_counter2
301b: vbi_counter3
301c: vbi_counter4
301d: vbi_counter5
301e: vbi_counter6
301f: always set to 1 during 311b VBI
3020: joystick 0 horizontal result: 0 = centered, 1 = right, ff = left
3021: joystick 1 value
3024: joystick 0 vertical result: 0 = centered, 1 = down, ff = up
3028: joystick direction. Normally read from, but writing to this is used by the demo level to automate jumpman, and by Jumping Blocks to force jumps. See “5cb0: demo level autoplayer data” below.
302c: trig0 result: 1 = not pressed, 0 = pressed. Again, this is written to by demo level and Jumping Blocks.
3030: music_flag0 music flag for voice #0
3032: music_flag1 music flag for voice #1
3034: music_flag2 music flag for voice #2
3036: music_flag3 music flag for voice #3
3038,3039: music_list0 music list for voice #0
303a,303b: music_list1 music list for voice #1
303c,303d: music_list2 music list for voice #2
303e,303f: music_list3 music list for voice #3
3040,3041: addr: music_start_parameter for 32b0 subroutine: music list to load into VBI player
3042,3043: addr: level definition table, copied from $2837
3044: fc param #0: object code
3045: fc param #1: always $40?
3046: fd param #0: x coord
3047: fd param #1: y coord
3048: fe param #0: x step value
3049: fe param #1: y step value
304a: fd param #2: repeat count
304f: PMBASE page number for player/missile graphics page ($60?)
3050: SIZEP? value for gameplay (two bits per player, lsb = player 0)
3051: SIZEM value for gameplay
3052: SDMCTL value for gameplay
3053: GRACTL value for gameplay
3054: GPRIOR value for gameplay
3055 Combined missile-player active — 0 is off, 1 is on
3056 P0 active (always, silly, it’s jumpman)
3057 P1 active (always, it’s the shadow)
3058 P2 active — 0 is off, 1 is on
3059 P3 active — 0 is off, 1 is on
305a-e where the player image data begins for each player, low byte. See 3073-7 for details.
305a image data LB for the Combined Missile Player
305b image data LB for Player 0, Jumpman. Leave this alone.
305c image data LB for Player 1, shadow. Leave this alone.
305d image data LB for Player 2
305e image data LB for Player 3
305f-3063 where the player image data begins for each player, high byte. See 3073-7 for details.
305f image data HB for the Combined Missile Player
3060 image data HB for Player 0. Leave this alone.
3061 image data HB for Player 1. Leave this alone.
3062 image data HB for Player 2
3063 image data HB for Player 3
3064-3068 image data bytes per image. E.g. a typical PM graphic might be 8 bytes tall. See 3073-7 for details.
3064 image data bytes per image for the Combined Missile Player
3065 image data bytes per image for Player 0. Leave this alone.
3066 image data bytes per image for Player 1. Leave this alone.
3067 image data bytes per image for Player 2
3068 image data bytes per image for Player 3
3069 Horizontal position of combined-Missile Player
306a Horizontal position of player 0 (always Jumpman) hosp0 -> D000
(X location of Jumpman; p/m coords)
306b Horizontal position of player 1 hosp1 -> D001
306c Horizontal position of player 2 hosp2 -> D002
306d Horizontal position of player 3 hosp3 -> D003
306e Y position of the combined-Missile Player
306f: Y position of Jumpman; p/m coords. C6 = dead, c0 is value on lowest possible girder,
3070: Y position of the shadow, you don’t need to mess with this
3071: Y position of Player 2
3072: Y position of Player 3
3073-7 Set Player to a particular graphic from image data.
3073 Set image data for the Combined-Missile Player
3074 Set image data for Player 0 (Jumpman) — don’t do this!
3075 Set image data for Player 1 (Shadow) — don’t do this!
3076 Set image data for Player 2
3077 Set image data for Player 3
when you STA a number there, it changes the corresponding player to that graphic from the level’s list of graphics. If the image data is rooster going up, rooster going right, rooster going right with wings out, rooster going left, etc., you can STA a 4 into 3077 to turn Player 3 to rooster going right. Note that the numbering starts at 1, not 0. Before using this, initialize 305a-e (player image data, low byte), 305f-3063 (player image data, high byte) and 3064-3068 (image data bytes per image). Glover wasn’t shy about setting graphic data to the same location for all enemies.
3078 through 3086 is where the game stores the previous versions of Player location and which graphic is being used. It compares the current information to this old information, to decide which Players need to be moved around the screen. This is all done in the code around 12caf. As a custom level builder, you don’t need to mess with anything in this span except during PM graphics initialization: Glover liked to set 3080, 3081, 3085, and 3086 to 0 or 1 during PM initialization: I think this is simply to force the first frame to be drawn correctly because the position and/or image choice will have changed. I’m not sure why he didn’t also initialize 307d and 3082, for the combined missile player is also often used for enemies. I guess that is automatically handled a special case. Also: It’s probably a bad idea to mess with the locations that contain copies of the Jumpman and Shadow info.
3078 copy of 3069 (X position of Combined Missile Player)
3079 copy of 306a (X position of Player 0 (Jumpman))
307a copy of 306b (X position of Player 1 (Shadow))
307b copy of 306c (X position of Player 2)
307c copy of 306d (X position of Player 3)
307d copy of 306e (Y position of the combined-Missile Player)
307e copy of 306f (Y position of Player 0 (Jumpman))
307f copy of 3070 (Y position of Player 1 (Shadow))
3080 copy of 3071 (Y position of Player 2)
3081 copy of 3072 (Y position of Player 3)
3082 copy of 3073 (image data for Combined Missile Player)
3083 copy of 3074 (image data for Player 0 (Jumpman))
3084 copy of 3075 (image data for Player 1 (Shadow))
3085 copy of 3076 (image data for Player 2)
3086 copy of 3077 (image data for Player 3)
3090-309f: copies of $d000-d00f collision registers during vblank. See 12d92.
3090 copy of $d000
3091 copy of $d001
3092 copy of $d002
3093 copy of $d003
3094 copy of $d004
3095 copy of $d005 (P1PF)
3096 copy of $d006
3097 copy of $d007
3098 copy of $d008
3099 copy of $d009
309a copy of $d00a
309b copy of $d00b
309c copy of $d00c
309d copy of $d00d
309e copy of $d00e
309f copy of $d00f
30a0: Combined Missile Player to Playfield collision byte. E.g. > 0 means the Combined Missile Player hit something. This is an AND of D000-D003. See 12da5-12dab.
30a1: Combined Missile Player to Player collision byte. This is an AND of D008-D00B. See 12dae-12db4.
30a2: Missile 0 activated 1=enabled; 0=disabled
30a3: Missile 1 activated
30a4: Missile 2 activated
30a5: Missile 3 activated
30a6: Missile 0 X
30a7: Missile 1 X
30a8: Missile 2 X
30a9: Missile 3 X
30aa: Missile 0 Y
30ab: Missile 1 Y
30ac: Missile 2 Y
30ad: Missile 3 Y
30ae: previous Missile 0 X, used internally to detect change for redraw. You probably never need to touch this.
30af: previous Missile 1 X
30b0: previous Missile 2 X
30b1: previous Missile 3 X
30b2: previous Missile 0 Y, used internally to detect change for redraw. You probably never need to touch this.
30b3: previous Missile 1 Y
30b4: previous Missile 2 Y
30b5: previous Missile 3 Y
30b6: Missile (bullet) height, in pixels. Standard bullets are 02. Jumping Blocks is 04 (twice as tall as normal.) You can use larger numbers for very tall Missiles. Very big numbers (eg FF) tends to crash eventually.
30b7: color of divider background, used in DLI between game playfield and score area
30b8: color 0 in score area. LDA $30b8 is a good source of constantly changing colors.
30b9: color 1 in score area
30ba: color 2 in score area
30bb: color 3 in score area
30bc: background color of score area, this and previous 4 set in DLI 3c7b. 0 or 1?
30bd: Jumpman alive flag: 0 = alive, 1 = falling to his death, bump bump bump.. 2 = dead at bottom of screen with birdies floating over his head, or Super Dead (not even on screen/fading back in)
30be: Jumpman play speed/status.
0 = playing at speed 1 (fast)
1 = speed 2
Also set to 1 when he is falling to his death or the death music is playing. I think
you could change JM’s fall speed.
2 = speed 3
3 = speed 4 (default)
4 = speed 5
5 = speed 6
6 = speed 7
7 = speed 8 (slowest)
8 = when he’s not on the screen: that short period where he disappears from the floor and fades back in in his new life starting place (or end the game if out of lives.). (I call this “super dead” as a shorthand in source code comments).
This location is checked by the main loop (typically at $2880 or $2860) to see if Jumpman is dead yet. You don’t write to this location, the system takes care of it. (Caveat: Grand Puzzle III does write to this location: it stores an 8 there, apparently to stave off Jumpman’s appearance while the hidden second screen is drawn.)
30bf: somehow related to 30be
30c0: This counts how many cycles Jumpman has been jumping? If it counts up to 17 (per Game code part 2:3b3c) Jumpman falls to his death.
30c1: Something to do with the shadow and falling. Force to 0 and Jumpman will float gently down rather than fall to his death.
30c2-30ca: copies of 2810-2817
30cb: ??
30cc: 0-4 counter for animating Jumpman
30cd: ??
30ce-30d9: copies of 2818-2823 (ladder positions)
30da-30df: copies of 2824-2829 (downrope positions)
30e0-30e1: CLC, RTS used as target for the vector at 2816. If the CLC is changed to an SEC, Jumpman is killed upon returning from this subroutine. We think that this was intended to be a method to kill Jumpman during the level, but Glover ended up abandoning it despite leaving the code around. This subroutine is still called, but the SEC method of killing Jumpman is not used in any of Glover’s levels.
30e2,30e3: addr: display list interrupt. Will be stuffed into DLI pointer by VBI Vector #0 at 4150
30e5-30ed: copies of 282a-2832 (color registers)
30ee: low byte of sector
30ef: high byte of sector
30f0: number of lives in reserve. 0 means you are on the last life. FF means you are out of lives, game over.
30f1-30f3: working space to convert hex to decimal
30f4: ?
30f5-30f7: score
30ff: number in response to keypress
3100: vbi_init subroutine: initialize vertical blank
3180: vbi_joystick joystick processing
311b: vbi_driver vertical blank interrupt driver, and VBI vectors #2, 3 & 4. Replaces call to XITVBV with call to the current VBI vector stored in 3018.
313b: vbi_increment_counters increment the counters at 3019 - 301e
3155: vbi_read_porta VBI vector #1, reads $d300 to put raw joystick values in 3020, 3021
31ec: vbi_music_player VBI music player
32b0: start_music_list subroutine: music driver: start playing a tune pointed to by $3040, duration passed into via the A register (music plays during the VBI). The sound routine clobbers the X (and Y?) register; save those somewhere first if needed.
32ff: vbi_music_semaphore semaphore used to make sure music player doesn’t make changes during VBI
3300: subroutine: copy 330c-331b to 034f
330b: 16 bytes, 330c - 331b copied to 034f
331c: start of level construction. Uses address pointer from 3042 (copied from 283c). Grand Puzzle I uses this subroutine to graft new ladders into the existing playfield, at 292e-293e. Runaway uses it to disappear and reappear peanuts in Runaway. (If you need it, save the X register before calling this routine. Don’t know about Y.)
3410: init_pm initialize player/missile graphics for gameplay
3451: clear_pm clear player/missile memory? sets up self-modifying code in the 34ab routine
348b: another clear routine, sets up self-modifying code in the 34ab routine
34ab: self-modifying code to clear 5 pages of memory
34b3: initialized to value from 304f
34d0: VBI vector #7
34f1: player graphic update: loops through the 5 possible players & updates player memory if player has moved or the animation frame number has changed. It checks the X position and will update the player graphic even though the memory doesn’t have to change with a change in the X position
3600: (inside VBI) copies collision registers
3690: VBI vector #9
3780: subroutine: turns off audio, zeros out working data in the 3000 range
3800: Clear game playfield memory
3820: subroutine: more level initialization
3870: VBI vector #5
3c00: gameplay display list, nominally:
> dlist
3C00: 3x 8 BLANK
3C03: LMS 7000 MODE D
3C06: 86x MODE D
3C5C: 2x DLI MODE D
3C5E: MODE 6
3C5F: HSCROL MODE 6
3C60: DLI MODE E
3C61: MODE D
3C62: JVB 3C00
or 89*40 + 2*20 + 40 + 40 = 3680 or 0xe60 bytes
There are actually 0xe64 bytes used, maybe 4 extra bytes from the HSCROL on
that mode 6 line?
3e00: character set during gameplay, set at 3863
4100-410f: local storage
4100-4101: jump target for START
4102-4103: jump target for SELECT
4104-4105: jump target for OPTION
4106: zero flag if START allowed; otherwise 01
4107: zero flag if SELECT allowed; otherwise 02
4108: zero if OPTION allowed; otherwise 04
4109: 1 if in title screen/attract mode, 0 if regular play
4110: checks ATRACT value. if it gets > $20, sends it back to attract mode
4127: console switch driver; checks flags at 4106-4018 to see which vector to jump to
4150: VBI vector #0
41c8: VBI vector #10
41c0: ? 3 out of 4 VBLANKs it is set to zero and the other time it's set to 1. pattern is 1 0 0 0
41e0: ? 3 out of 4 VBLANKs it is set to zero and the other time it's set to 1. pattern is 0 1 0 0.
41e8: breakpoint when select/start pressed in GAME OPTIONS. Stores value of CONSOL (start/select/option keys) in ATRACT ($4D), which disables attract mode
4200: subroutine: start the Jumpman death process.
4400: load_level_from_disk subroutine to read 16 consecutive sectors from starting sector in 30EE and 30EF
4460: possible start to level initialization routine. Copies stuff from the 2800 region. It's called from the root level; i.e. nothing above it in the stack. Breaking during the 4400 routine shows this:
> stack
01FA: 42 45 4540: JSR 4400
01FC: E2 0F 0FE0: JSR 4540
01FE: 75 44 4473: JSR 0FE0
46e9: subroutine to force score display to update. The routine clobbers the X (and Y?) register; save those somewhere first if needed.
4730: vbi_update_score update store display, called from music player then exits via 311b
4780: loads bonus value from level?
47b0: part of the level initialization. copies 2801,X -> 3086,X and 2809,X -> 30b6,X
47d4: subroutine: check console switches for bit 0 or bit 1? not used during game options screen, seems to be active during level load
4800: VBI vector #6
4974: music_list_bang BANG sound data (to play, load 4974 into 3040-3041, set A to 4, then JSR 32b0)
49a0: VBI subroutine called by levels for death by bullet or baddie. If JM has collided with Players 2 or 3, or any of the missiles, or the combined missile player, he falls to his death.
49d0: subroutine: called immediately after scrolling the new level into place: play the start-of-level tone and cycle the colors to fade in jumpman. The game loop does not run at this time (VBIs do run), the fade-in of jumpman occurs in a big delay loop within this subroutine.
4a60: VBI vector #8
4b00: Some sort of harvest table setup/maintenance that happens before every Jumpman life.
4ca0: subroutine: modifies display list to set up extra DLI bit at top of screen
4ccd: gameplay DLI routine (set at 4cb2)
4cfd-4cff: total bonus points accumulated?
4d00: subroutine: initialize initial number of players
5000: driver to set up level? Calls 3800, after which screen is scrolled into view
5049: copy screen down one line?
51c7: local data
51c7-51c8: location $2710 stored here before call to $0a00? Jump vector?
5200: entry point: called on successful level completion to figure out where to go next
5290: entry point
52b8: subroutine:
5300: entry point: show final score, sets display list to $539d
5400: subroutine:
55f8: subroutine:
5590: entry point: new level? Calls 5000 to set up level. Breakpoint at 5593 returns after level has scrolled into view.
5600: jump target: Title screen!
5687: jump target: when SELECT pressed; called by VBI to return to game options screen.
56af: subroutine: 65536 cycle decrement delay
5738: jump target: continuing 5600
5cb0: demo level autoplayer data. 3-tuples: delay time, joystick value, trigger value
joystick values:
a0 = left (Huh. Jumping Blocks uses b0 for left. Both work)
e0 = up
70 = right
d0 = down
trigger values:
21 = not pressed
10 = pressed
5ff4: entry point: start the “game over” process
6000 - 67ff: gameplay player/missile memory (single line resolution, so definitions start at 6300) See Mapping The Atari: Appendix 7.
6300 — missile graphics
6400 — player 0 graphics (that’s always Jumpman)
6500 — player 1 graphics (always the shadow)
6600 — player 2 graphics
6700 — player 3 graphics
Through 67ff
606a: entry point: girder crumble routine
6800: entry point for end of game fireworks (called from $3cf9
68fd: entry point, display list to $6fcc: high score table. Ends by jumping to either $0bb8 or $5600
6d22: successful completion entry point: completing a game mode that can be completed (Beginner, Intermediate, Advanced, Grand Loop) and bonus calculation
6e00: high score screen memory (with first entry in top scores and high scores both being "ROB" with a score of 8125)
> m 6e00
6E00: 00 C2 C2 C2 00 F4 EF F0 00 F3 E3 EF F2 E5 F3 00 ................
6E10: C2 C2 C2 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6E20: 00 00 00 00 00 00 00 00 00 00 00 91 8E 00 72 6F ..............ro
6E30: 62 00 00 00 18 11 12 15 00 00 00 00 00 00 00 92 b...............
6E40: 8E 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 ................
6E50: 00 00 00 93 8E 00 00 00 00 00 00 00 00 00 00 10 ................
6E60: 00 00 00 00 00 00 00 94 8E 00 00 00 00 00 00 00 ................
6E70: 00 00 00 10 00 00 00 00 00 00 00 95 8E 00 00 00 ................
6E80: 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 96 ................
6E90: 8E 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 ................
6EA0: 00 00 00 97 8E 00 00 00 00 00 00 00 00 00 00 10 ................
6EB0: 00 00 00 00 00 00 00 98 8E 00 00 00 00 00 00 00 ................
6EC0: 00 00 00 10 00 00 00 00 00 00 00 99 8E 00 00 00 ................
6ED0: 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 ................
6EE0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6EF0: 00 C2 C2 C2 00 E8 E9 E7 E8 00 E2 EF EE F5 F3 00 ................
6F00: C2 C2 C2 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6F10: 00 00 00 00 00 00 00 00 00 00 00 91 8E 00 72 6F ..............ro
6F20: 62 00 00 00 11 15 10 10 00 00 00 00 00 00 00 92 b...............
6F30: 8E 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 ................
6F40: 00 00 00 93 8E 00 00 00 00 00 00 00 00 00 00 10 ................
6F50: 00 00 00 00 00 00 00 94 8E 00 00 00 00 00 00 00 ................
6F60: 00 00 00 10 00 00 00 00 00 00 00 95 8E 00 00 00 ................
6F70: 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 96 ................
6F80: 8E 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 ................
6F90: 00 00 00 97 8E 00 00 00 00 00 00 00 00 00 00 10 ................
6FA0: 00 00 00 00 00 00 00 98 8E 00 00 00 00 00 00 00 ................
6FB0: 00 00 00 10 00 00 00 00 00 00 00 99 8E 00 00 00 ................
6FC0: 00 00 00 00 00 00 00 10 00 00 00 00 ............
6fcc: (immediately after 6e00 above): high score display list
6FC0: 70 70 70 70 pppp
6FD0: 46 00 6E 06 06 06 06 06 06 06 06 06 06 06 06 06 F.n.............
6FE0: 06 06 06 06 06 06 06 06 06 41 CC 6F .........A.o
7de8 - 7e10: score
Sectors 2-5 are loaded at $800-$9ff, inits at $800. It sets the loading display list and tests for 32K. If successful, loads sectors 560-719 starting at $2000, then jumps to $2900 (indirect jump to the value set at $09d6).
At $2900, it checks for a keypress to clear high scores (!) then continues on to the routine at $2b00 to load sectors 548-559 at $0a00. It then jumps to $55c0 to begin the non I/O initialization. This in turn jumps to $5600 which is also the routine that can be called at any time to return to the title screen.
cb-d1 are free 0-page addresses that Jumpman sometimes uses, e.g. for indexed zeropage addressing
0200,0201 VDSLST display list interruptlow, high
022f SDMCTL DMA enable: screen width & player/missile control
0230 SDLSTL display list low
0231 SDLSTH display list high
0304 DBUFLO low byte of disk buffer
0305 DBUFHI high byte of disk buffer
030a DAUX1 low byte of sector number
030b DAUX2 high byte of sector number
d00c SIZEM size for all missiles
d016 COLPF 0 playfield colors
d017 COLPF 1
d018 COLPF 2
d019 COLPF 3
d01a COLBK
d01b PRIOR sprite priority
d01d GRACTL turn on players/missiles
d01f CONSOL R: start/select/option key bitmap. START=6, SELECT=5, OPTION=3
d20a RANDOM
d407 PMBASE page pointing to player missile graphics base
d40a WSYNC wait for horizontal sync
e453 DSKINV disk interface entry point (read a sector)
Level Definition table typically starts at $2c00 but it doesn’t absolutely need to. Jump N Run for some reason starts with with the harvest table at $2c00. Level layout starts at 2c66 rather than the usual 2c00. Why Glover did it differently, I don’t know. But as long as $284e-f points to the harvest table, and the parser can find the level description (fe …) it seems happy.
The other unusual location for level definition is GP II. Level layout is at 2e80 rather than the usual 2c00. This seems to be simply because the custom level code starting at $2b00 is so long, Glover moved the level definition from its usual location.
fe 04 00 a horizontally drawn thing: drawn X 4 pixels apart, Y 0 pixels apart
Drawing direction remains in effect until changed
This is only used for girders
fc 00 40 Draw girders (see table of objects)
fd 04 09 05 Draw the girder: X, Y, length
fd 24 09 06 ...another...
fd 64 09 06
fd 88 09 05
fd 28 19 04
fd 68 19 04
fd 04 1d 05
fd 44 1d 06
fd 88 1d 05
fd 04 2d 05
fd 24 2d 02
fd 38 2d 0c
fd 74 2d 02
fd 88 2d 05
fd 38 3d 0c
fd 04 45 06
fd 84 45 06
fd 04 55 26
fd 48 52 04
fe 04 ff an up-ramped thing: drawn X 4 pixels apart, Y -1 pixels apart
This is only used for ramped girders
fd 18 0a 01
fd 7c 0a 01
fd 1c 0b 02
fd 80 0b 02
fd 3c 08 03
fd 5c 1c 03
fd 1c 44 07
fe 04 01 a down-ramped thing: drawn X 4 pixels apart, Y 1 pixels apart
This is only used for ramped girders
fd 58 06 03
fd 38 1a 03
fd 68 3e 07
fe 00 04 a vertical (drawn top down) thing, X 0 pixels apart, Y 4 pixels apart
This is used for ladders and ropes
Note: drawing bottom up is possible (fe 00 fb) but not used by Glover
fc 2c 40 Draw ladders
fd 0c 05 0b Draw the ladder: X, Y, length
fd 0c 41 05 ...another...
fd 2c 05 05
fd 3c 29 05
fd 4c 19 05
fd 5c 29 05
fd 6c 05 05
fd 8c 05 0b
fd 8c 41 05
fc af 40 Draw up-ropes
fd 27 30 02
fd 77 30 02
fc 83 40 Draw peanuts. Length should always be 01 for peanuts.
fd 04 06 01
fd 44 03 01
fd 58 03 01
fd 98 06 01
fd 04 15 01
fd 98 15 01
fd 24 25 01
fd 78 25 01
fd 04 52 01
fd 40 47 01
fd 5c 47 01
fd 98 52 01
ff End of drawing table
Vertical girder stacks are rare but sometimes used (hotfoot and GP III): fe 00 03
00 40 = girder (or ramp, same thing :)
2c 40 = ladder
af 40 = up rope
c0 40 = down rope
83 40 = peanut
16 40 = erase a girder (erasing is used in harvest tables)
56 40 = erase a ladder
d1 40 = erase an up or down rope (only used in harvest tables of Grand Puzzle I II and III)
99 40 = erase a peanut (only used on Runaway)
5f 2e = blue block that’s painted over everything on Mystery Maze levels
50 5b = used on intro screen only - inside the sign
6d 5b = used on intro screen only - dotted outer edge of the sign
Mildly interesting: There’s overlap between these? To demonstrate, here’s the sign with the outer edge turned to down ropes and the inside turned to up ropes.
0e 40 is interesting - a blue bar that Jumpman can stand on. I think this is the top layer of the girder graphic. Also 0a, same thing. Here’s something I invented with that: the climbable blue bar stack (fe 00 03 fc 0e 40). The Y can be 02-05 and still be climbable.
It also works as a girder or ramp. Here’s fe 04 01 fc 0e 40
Rarely there’s an odd-looking thing in Jumpman’s levels, done by breaking the normal placement rules. This base part of Freeze is made by stacking girders very close together: fd 3c 54 0a fd 38 55 0c (then placing the peanut on top after)
An object code is actually a pointer to a small set of bytes that describe a command language to draw pixels. For example, here are the bytes describing the girder at 4000:
04 00 00 01 01 01 01 04 00 01 01 00 01 00 04 00 02 01 01 01 01 ff
And those describing the ladder at 402c:
02 00 00 02 02 02 06 00 02 02 02 00 01 02 02 02 06 01 02 02 08 00 02 02 02 02 02 02 02 02 02 02 00 03 02 02 02 06 03 02 02 ff
The bytes define a set of horizontal lines that describes the color of each pixel. Taken together, they describe a unit of the object. The level definition codes describe lines made up of these units, where the fe flag sets the spacing between units and the fd flag sets the position on screen and the number of units to draw.
Looking at the girder example and breaking it up the following way shows that there are 3 lines of 4 pixels each:
04 00 00 01 01 01 01
04 00 01 01 00 01 00
04 00 02 01 01 01 01
ff
The first 3 bytes of each line describe the number of pixels to draw horizontally, the x offset, and the y offset. The offsets are added to the X and Y values in the fd command to determine the location on screen.
The next 4 bytes (in this case, because the number of pixels is 4 in each of these lines) describe the color of each pixel. These pixel values are in the range of 0 to 3 because there are 4 colors available in graphics mode 7 (ANTIC mode d). This shows that the girders are drawn mostly in color 1, except the holes in the middle section of the girder are the background color 0.
This process is repeated for each line until reaching the ff flag in the data.
The ladder is more complicated, but breaking it down into the sets of lines makes it easier to follow:
02 00 00 02 02
02 06 00 02 02
02 00 01 02 02
02 06 01 02 02
08 00 02 02 02 02 02 02 02 02 02
02 00 03 02 02
02 06 03 02 02
ff
This shows that the ladder is 4 lines high and there are segments of the ladder that are 2 and 8 pixels wide. Taken all together, it is 4 pixels high and 8 pixels wide and drawn in color 2.
The codes to erase items use the same format, they just draw lots of color 0 pixels. For example, the codes at 4056 to erase a ladder are:
08 00 00 00 00 00 00 00 00 00 00
08 00 01 00 00 00 00 00 00 00 00
08 00 02 00 00 00 00 00 00 00 00
08 00 03 00 00 00 00 00 00 00 00
ff
The advantage of this pixel-level encoding is that there is no byte alignment requirement, so it’s not necessary that anything be lined up on 4 pixel boundaries like character graphics would be. The disadvantage is that it is slower than character graphics would be, but obviously it’s fast enough because the game works!
After the ff that ends the level definition table, comes the Harvest Table. (Usually. Technically it can go almost anywhere in the level, pointed to by 284e,284f.) There’s an entry for every peanut, describing what objects to make appear or disappear what that peanut is taken; and vectors to subroutine to run when that peanut is taken.
ZZ — an encoded number. Further discussion on this below.
XX YY — x y of the peanut. These match the peanut positions laid out in the level layout. In the same order, too.
4B 28 — vector to subroutine to run when peanut is taken. 4B 28 if none required. If you jump to custom code that you’ve created within the level, end that code with RTS. See https://www.youtube.com/watch?v=_RRrynvRbZs
4C 28 —vector to the part of the painting table that describes the item(s) to be removed or added when this peanut is taken.
4C 28 if nothing is removed when this peanut is taken.
If something is removed or added, it is the memory location of the item in the painting table
… repeats for every peanut …
ends with FF
Then comes the painting table, which looks a lot like the level definition table.
fe 04 00 what direction to paint
fc 00 40 what object to paint
fd x y len position and length of the thing
{optional repeat of fd x y len}
{optional additional fe and fc entries}
ff the end of painting for that particular peanut
harvest table:
22 04 06 4b 28 54 2d
62 44 03 4b 28 4c 28
82 58 03 4b 28 4c 28
c2 98 06 4b 28 64 2d
24 04 15 4b 28 3e 2d
c4 98 15 4b 28 49 2d
46 24 25 4b 28 4c 28
a6 78 25 4b 28 4c 28
2c 04 52 4b 28 4c 28
6a 40 47 4b 28 4c 28
8a 5c 47 4b 28 4c 28
cc 98 52 4b 28 4c 28
ff
painting table:
2d3e: fe 00 04 fc 56 40 fd 0c 21 02 ff
2d49: fe 00 04 fc 56 40 fd 8c 21 02 ff
2d54: fc 16 40 fd 18 0a 01 fd 1c 0b 01 fd 20 0a 01 ff
2d64: fc 16 40 fd 7c 0a 01 fd 80 0b 01 fd 84 0a 01 ff
42 18 05 07 2d 4c 28
62 38 02 07 2d 4c 28
82 64 02 07 2d 4c 28
a2 84 05 07 2d 4c 28
46 24 22 07 2d 4c 28
86 58 22 07 2d 4c 28
a6 80 22 07 2d 4c 28
68 44 32 07 2d 4c 28
a8 7b 32 07 2d 4c 28
4a 1e 42 07 2d 4c 28
8c 64 52 07 2d 4c 28
4c 18 52 07 2d 4c 28
ff
this harvest table shows the use of a function when the object is collected. It calls 2d07, which is a simple routine:
lda #$01
sta $2850
rts
which must be detected during the VBI processing.
The following image shows jumpman in coordinates 34, c0:
Jumpman can hang off the edge, e.g. here with coordinates 2e, c0:
Here’s poor ol’ dead jumpman at 2a, c6:
And here he is at the top right of this level: c4, 20:
Due to the original design of Jumpman, bomb placement is limited. The screen is divided up into 64 grid squares, and only one bomb is allowed per grid square. (If you place more than one, the wrong one can disappear when taken and things get wonky quickly.)
The code uses Jumpman’s X and Y player/missile coordinates in 306a and 306f, then runs it through this equation to get a single checksum value for the bomb location (c.f. Code at $4b1a) The coordinates (c.f. Code at $49d5) which means the X range is the 30 - c6 and the Y range is 20 - c0. An X coordinate of 30 corresponds to the left edge of the playfield graphics. It seems a lot of the levels don’t start at the left edge but instead 4 pixels in, so a common X minimum is 34. So, the equation in Jumpman coords would be:
(($306a + $2846) & #$e0) + ((($306f + $2847) & #$e0)/$10)
It turns everything into an 8x8 grid, that means only 64 possible places that can be in the table.
For the level Easy Does It, the X offset in 2846 is 0 and the Y offset in 2847 is 6. Here’s a visualization, where the blue shaded areas are OK to place bombs, and the red shaded areas must not contain bombs:
The Jumpman level editor in Omnivore includes this grid when placing bombs on the screen.
Jumpman position of 34, 20, so way at the top left of the screen (the upper-most block in the image above) would produce a checksum of (($34 + 0) & $e0) + (($20 + $6) & $e0)/$11 = $22
So to create the checksum using the bomb location, I think you’ll have to add the value of $34 to the bomb X location and the value of $20 to twice the Y bomb location, then run it through the modified equation. Because the player/missile graphics use single line resolution and the graphics is in Graphics 7, Jumpman has twice as many vertical positions as bomb vertical coordinates. The machine language routine in the code that calculates the checksum is based on there is twice the resolution for the input coordinates to this checksum routine.
((bomb_x + $34 + $2846) & #$e0) + ((((bomb_y * 2) + $20 + $2847) & #$e0)/$10)
I’m not sure of the exact offset between the bomb coordinates and the jumpman coords. It might be off by a couple, so the bombs near the edges of each one of the cells in the 8x8 grid might be wrong. We’ll have to experiment.
Experiment with Play The Demo: harvest table = XX 2e 08 4b 28 4c 28 ff. This defines a single bomb at 2e, 08. The goal is to figure out what XX should be. $2846 is 0, $2847 is 6, so the checksum equation says:
Or, in parts:
the X part = ($2e + $34 + $00) & $e0 = $60
The Y part = ($08*2 + $20 + $06) & $e0 = $20,
Together: checksum = $60 + ($20 / $10) = $62
Here’s the code that calculates the checksum:
.org $4b1a
lda $284e ;harvest table checksum calculation for bomb lookup
sta $ba ; STOPLN
lda $284f
sta $bb ; STOPLN+1
lda $306a
clc
adc $2846
and #$e0
sta $bc ; TRAPLN
lda $306f
clc
adc $2847
and #$e0
lsr a
lsr a
lsr a
lsr a
ora $bc ; TRAPLN
sta $bc ; TRAPLN
Use Omnivore to create a new level or modify an existing level.
Some levels have special features that can’t yet be created or modified in Omnivore. These include:
Easy Does It (level 1) has unused custom level code at $2900 and $2a00, plus unused graphics at $2b00. Those graphics are duplicated in all 3 Mystery Maze levels. What did Glover have in mind?
Robots I contains a copy of graphics from The Roost. So do Vampire and Invasion.
Robots I also has some unused mystery code (which looks unusable) at 2b00. And unused data at 2a6a-2ab2.
The Robots unused mystery code is duplicated in Roll Me Over at 2b00.
Was a 5th prize being considered for Grand Puzzle II?
There are unused graphics in Grand Puzzle I and III.
Bombs Away uses two simultaneous falling bombs, but the level is set up to handle three. For some reason, the combined-missile Player is deactivated. Change $29c3 from $ff to $0 to activate it. Also change $29ab to c3 so it makes the falling sound.
Bombs away has unused data at 2ac0-2ad3.
Now that Omnivore is functional as a level editor, hand-coding is not recommended. However, here are some examples for posterity:
Girder on bottom, as low and long as possible: fc 00 40 fd 04 55 26
Girder along the top: fc 00 40 fd 04 01 26
Jumpman can stand on top of that.
(Using 00 for the y seems to work, but things go bad when the game ends/the level collapses filling the screen with blue instead of black, and that goes on for a laughably long time)
Climbable girder stack on the left: fc 00 40 fe 00 03 fd 04 01 21
On the right: fc 00 40 fe 00 03 fd 98 01 21
This error screen will appear when a peanut is placed in one of the red shaded areas and is collected by Jumpman. Thanks to Omnivore’s editor, you’ll probably never see this screen.
00BC CHECKSUM: XX <-- this is the value it computed as the entry into the harvest table using the 4 params below
284E OFFSET X: XX <-- this is the X shift value for the peanut mask, the hx value in my lookup table
284F OFFSET Y: XX <-- the Y shift value for the peanut mask, the hy value in my lookup table
306A JUMPMAN X: XX <-- the player position for Jumpman
306F JUMPMAN Y: XX
PEANUT TABLE: XXXX <-- the address of the harvest table in the level. I really should rename this as HARVEST TABLE
subsequent lines are the checksum values in the harvest table. It will print out a list of all the checksum values until it hits FF
The music driver at 32b0 looks for an available voice and uses the first available slot it finds. Slots are defined by the 4 bytes at 3030, 3032, 3034, 3036. If there is a positive number there, something is using that voice.
The procedure to start playing something seems to be load 3040 & 3041 with the address of your music list, load the accumulator with a number that represents the length of each note: $01 is long and $ff is very short. (This doesn’t change the tempo, just note length.) Then, JSR to 32b0 to submit the music list to the player which plays during the vertical blank.
Here’s a routine at $3760 that plays something:
lda #$4e
sta $3040
lda #$37
sta $3041
lda #$04
jsr $32b0
where the music list at 374e is “01 81 04 01 00”. Here’s another example is at $3ae3:
lda #$ca
sta $3040
lda #$3b
sta $3041
lda #$08
jsr $32b0
Where the music list at 3bca is “01 a5 79 04 60 04 51 04 3c 04 51 04 60 04 79 04 00” . (That’s the Jump sound)
Music lists are for a single voice. For a more voices, you must make successive calls to 32b0 with separate music lists.
The list is processed two or three bytes at a time: the first byte may be a special flag byte of 1, 2 or 3. If it reads one of these flags, it processes it as a command and looks at the next one or two bytes:
1: next byte gets stored in AUDCx register
2: next byte gets stored in AUDCTL
3: take next 2 bytes and JSR there (a user defined subroutine)
If the byte is not 1, 2, or 3, it is considered a note: the byte is stored in the AUDFx register. The following byte is a priority byte of 1-4. Priority 4 notes take priority over everything else, and will override any priority 1-3 notes if all 4 channels are in use. Priority 1 notes are good for background sounds etc, as they will be overirdden by more important noises like the jumping sound, bang of bullets, etc.
The music list is zero-terminated.
So: 01 a5 79 04 60 04 51 04 3c 04 51 04 60 04 79 04 00
01 next byte gets stored in AUDCx register
a5 this goes in AUDCx register
79 a note
04 high priority
60 a note
04 high priority
…
00 end of sound data
Custom code on a level allows you to add things like Player enemies (eg Robots), custom behaviors of non-enemy objects (Ladder Challenge), and special actions upon taking peanuts (eg getting extra points and a lovely on-screen “500” for certain peanuts on Grand Puzzles.)
There are four vectors where the game can access custom code:
Kay made a short video at https://www.youtube.com/watch?v=_RRrynvRbZs that shows in the quickest possible way how to create custom code with each of the three typical vectors.
The vector pointed to by 283b-283c is typically 2880 or 2860. This code is run after the level scrolls into view: it’s responsible for doling out Jumpmen as long as there are lives left, and ending the level when you’ve gotten all the peanuts, or ending the game when you’ve run out of lives.
It makes Jumpman appear and plays the appearance sound. Then a little loop waits until you’ve either taken all the peanuts or die. If you die, it gives you another life if you have one, or ends the game, crumble crumble crumble.)
It may be a little underwhelming, but this is the main game loop. (In reality, most of the work goes on behind the scenes during the VBI.)
You might be tempted to stuff all your level initialization stuff here for P/M graphics and whatnot, but I don’t think you should. More often than not, Glover used exactly the same basic code here on every level (see Ladder Challenge:2880 for this typical code), and does any initialization routines as part of the “run constantly” code.
Well, sometimes he does do P/M graphics setup and other initialization here. My gut is that you only should do that here if you need things to reset to a known state every time Jumpman (re)appears on the level.
I reserve the right to change my mind on this later as we figure things out, but right now my feeling is that although you can use this opportunity for custom code, you probably shouldn’t.
Here’s the basic code used on several levels:
$2880 JSR $49d0 ;Play appear tone, fade Jumpman into view
$2883 JSR $4b00 ;Maintain peanut harvest
LDA $283e ;are there 0 peanuts left to take?
CMP #$00
BEQ $289e ;completed the level!
LDA $30be ;Is Jumpman super dead yet?
CMP #$08
BCC $2883 ;if it's <8, loop back, just keep
watching the peanut count and if he's dead
LDA $30f0 ;Too bad, he's dead. # lives left?
CMP #$ff ;end game if it’s -1
BNE $2880
JMP $283f ;jump to lost last life vector, crumble crumble
$2893 JMP ($2844) ;jump to successful completion of level vector
This code runs once, when a particular peanut is taken. Put the code somewhere in the free space in the level. End with RTS. Point to the start of your code from the Harvest Table entry for that peanut (See “vector to subroutine to run when peanut is taken”, above.) If you’re using the Jumpman level editor in Omnivore (which I hope you are, for your own sanity) right-click on the Peanut and choose Set Trigger Function to point it to the memory location of your code.
This code runs once each time any peanut is taken. E.g. The robots in Robots I move only when you take a peanut. Put the code somewhere in the free space in the level. End with RTS. Point to the start of your code from 2849-284a. (See “subroutine within the level to jump to, every time a peanut is taken” above). If that functionality isn’t needed, the default is at 2849-284a 4b28, which is always RTS.
Sometimes you want your custom code to run constantly during the level: e.g. the robots in Robots 2 are always moving along their path. The ladder in Ladder Challenge is always moving back and forth.
Put the code somewhere in the free space in the level. Typically Glover put it at $2900. Always end the code with JMP $311b (not RTS.) Point to your code from one (or possibly more) of the vectors at 2802-2809 (see “2802-2809”, above.) These 8 bytes comprise a jump table of 4 addresses that are called on a constant basis during the game.
So if your code is at $2900, you can set $2802 to #$00 and $2803 to #$29. There’s currently no way to set this within the Omnivore Jumpman level editor, but you can do it from its hex editor.
Note that there can be multiple entry points for your code, which are run in turn. Ladder Challenge, for instance, has entry points at $2900 (which handles initialization and ladder movement) and $29b8 (which handles collisions with the ladder and barriers.)
The best way to see how this code works is to see how Glover did it. Kay has documented the code for two levels: The Roost and Ladder Challenge. Both use Players, but in rather different ways. You can get the documented code here: http://atariage.com/forums/topic/255262-jumpman-level-design-contest/?p=3573981
Here’s how to kill Jumpman: e.g. put him into “fall to the bottom of the screen” mode. This is all you have to do, the game takes care of the rest.
LDA #1
STA $30bd
Here’s how to add to the score manually. Assuming 50 points here ($32)
ad f5 30 18 69 32 8d f5 30 ad f6 30 69 00 8d f6 30 ad f7 30 69 00 8d f7 30 20 e9 46
LDA $30F5
CLC
ADC #$32
STA $30F5
LDA $30F6
ADC #$00
STA $30F6
LDA $30F7
ADC #$00
STA $30F7
STX $2fff optional: save the X register somewhere: the score display routine clobbers it
JSR $46E9 force score display to update
LDX $2fff reload X register
Add 400 points (e.g. took a 500-point peanut. 100 regular + 400)
LDA $30F5
CLC
ADC #$90
STA $30F5
LDA $30F6
ADC #$01
STA $30F6
LDA $30F7
ADC #$00
STA $30F7
STX $2fff optional: save the X register somewhere: the score display routine clobbers it
JSR $46E9 force score display to update
LDX $2fff reload X register
The reference disk image that we have been using is the image available on AtariMania, cracked by an unknown hacker. The original disk was copy protected but at this time we do not have access to an original disk or an ATX image of the original protected disk.
There is an image available on atarionline.pl that has a different sector map (i.e. the levels are in different places than listed in the sector map above).
Referring to our working disk image, there are several places where seemingly random data is encoded into the level data. For example, Jumping Blocks at $2a13 through $2aff has what looks like some raw assembly source:
as does Builder. In fact, Builder has more of what looks to be the same assembly source. It starts at $2900 and continues through $2aff, where the part at $2a13 - $2aff is exactly the same as the code in Jumping Blocks.
My initial assumption was that this code is code left over from the hacker and not Glover’s code, but examining the version found at atarionline.pl shows that this same code exists in the same places in that version as well. Rob has imaged the pirated copy he obtained in 1984 and also contains this same code. The provenance of these 3 versions is unknown, so despite their similarity they could have all descended from the same original hack.
Circumstantially, though, it is quite possible that this is left over from Glover’s compilation process. The bytes in Builder assemble to the level data at $2d53 through $2dbd. The snippet in Jumping Blocks is a subset of the same assembly text, so it doesn’t assemble to the level data in Jumping Blocks, it assembles to a smaller chunk of the level data in Builder.