Skip to content

Instantly share code, notes, and snippets.

@pixjuan
Last active June 9, 2024 15:23
Show Gist options
  • Save pixjuan/e3e26a070b958bd415e467a2e93dbdda to your computer and use it in GitHub Desktop.
Save pixjuan/e3e26a070b958bd415e467a2e93dbdda to your computer and use it in GitHub Desktop.
Apple II Sabotage memory map

Apple II Sabotage Reverse Engineering

The goal is to try to understand the behaviour of the AI in Sabotage (Copyright 1981, Mark Allen), and more generally to do some reverse engineering on this game for educational purpose. I'm taking notes in this gist, I will try to produce something easier to read later. Archive.org got an online playable version of this game : https://archive.org/details/a2_asimov_sabotage

Method

I first used LinApple emulator to make a dump of the memory, then, I used a disassembler to try to understand the game. I also used the Mame debugger to disable various parts of the game and understand their purpose.

Structures

I isolated some structures, it was easy to guess ther size because of the increment that was done on some loops, but it was harder to figure out the exact use of all the fields.

struct bullet

BYTE notsure

WORD bullet_X         : X position of the bullet

WORD bullet_Y         : Y position of the bullet

WORD bullet_DX        : X speed of the bullet

WORD bullet_DY        : Y speed of the bullet

struct para

BYTE notsure

BYTE parastatus:   0          : paratrooper is inactive?
                   FF         : special
                   >80        : free fall

BYTE index_in_zone40: X position for the paratrooper (the screen is split in 40 columns)

BYTE YPos:          : Y position of the paratrooper

BYTE notsure2:

struct helijet

BYTE index_for_data   : index to fetch graphic data
                              2       : heli going right
                              4       : heli going left
                              6       : jet going right
                              8       : jet going left

BYTE Ypos             : Y position

BYTE XPos             : X position

BYTE frame_index      : frame index for the animation

BYTE gfx_type         : a bit like index_for_data? 0 means exploding

BYTE new_Ypos         : Y position after update

BYTE new_XPos         : X position after update

BYTE new_frame_index  :next frame for animation

BYTE XSpeed_maybe     : horizontal speed

BYTE YSpeed_maybe     : vertical speed

memory map

 Address        function / var

0000:4000       init_score()
0000:400E       startscreen_400E()
0000:412A       mainloop_412A()
0000:4207       wait_function()
0000:4212       check_paddle()
0000:423F       check_paddle_fire()
0000:4266       endless_loop()
0000:427A       gameloop_427A()
0000:4299       loop_4299()
0000:42E1       angle_tourelle
0000:42E4       random_var_42E4
0000:42E5       random_var_42E5
0000:42E6       highscore_l
0000:42E7       highscore_h
0000:42E9       angle_tourelle3
0000:42EA       angle_tourelle2
0000:42EB       score_l
0000:42EC       score_h
0000:42F5       BulletArray
0000:4345       HeliJet_array
0000:436D       para_array
0000:43E5       para_on_floor_array
0000:440D       lookup_table_440D
0000:4413       lookup_table_4413
0000:441C       send_enemy_441C()
0000:454D       drop_para_454D()
0000:455F       loop_455F()
0000:45A1       zone40
0000:460C       altitude_table_460C
0000:4614       left_alt_array_4614
0000:461C       right_alt_array_461C
0000:4624       draw_tourelle
0000:469D       lookup_div3_469D
0000:4ABE       parafall_4ABE()
0000:4BC3       wait_function2()
0000:4BCB       draw_fall_para()
0000:4BE0       draw_para()
0000:4C32       draw_man()
0000:4CE6       paras_stuff_4CE6()
0000:4D36       para_stuff_4D36()
0000:4D4A       parastack_height_table
0000:4D56       power_of_two
0000:4D5E       LineAddrHigh
0000:4E1E       LineAddrLow
0000:4EDE       LookupTable_abs7
0000:4FF6       LookupTable_Mod7
0000:510E       sub_510E()
0000:52D5       lookup_52D5
0000:52E8       frameIndex_52E8
0000:52E9       XPos_52E9
0000:52EA       YPos_52EA
0000:52EB       XSpeed_52EB
0000:52EC       YSpeed_52EC
0000:52EE       explosion_animation_52EE()
0000:5402       init_collide_76_5658()
0000:5420       draw_xplod_frag_5420()
0000:5460       frag_collide_detect_5460()
0000:5491       detect_parafrag_5491()
0000:5552       frag_collide_5552()
0000:560B       burst_kill_HLC()
0000:5646       frag_array_5646
0000:5658       ptr_array_5658
0000:58F8       parattack_58F8
0000:5A6B       draw_para_walking()
0000:5B58       para_bitmap
0000:5BB8       inc_score()
0000:5BE6       dec_score()
0000:5C12       score_gfx_upd()
0000:5CEC       init_stuff()
0000:5DEA       fire_stuff()
0000:5E83       bullet_origin_5E83
0000:5EAC       draw_move_bullet()
0000:5FCF       bullet_related_stuff()
0000:6018       bullet_para_collide()
0000:609B       shoot_para_stuff
0000:60AD       steerable_bullets
0000:60B8       Bullet_traj
0000:619A       send_helijet_619A()
0000:6277       array_6277
0000:628A       move_helijet()
0000:6367       locret_6367
0000:6368       draw_helico_jet()
0000:641F       helijet_data_ptr
0000:642A       helijet_data2
0000:6452       helijet_data3
0000:64C2       helijet_data4
0000:6542       helijet_data5
0000:688A       bullet_heli_collide_688A()
0000:68EC       no_collision_68EC
0000:68F8       shoot_HLC_stuff
0000:6928       jet_drop_bomb_maybe2()
0000:69D6       jet_drop_bomb_maybe()
0000:6A87       draw_bomb()
0000:6AC9       bullet_stuff_6AC9()
0000:6ACF       bullet_stuff_6ACF()
0000:6B1D       shootbomb_stuff()
0000:6B2F       bomb_frag_6B2F()
0000:6C06       gameloop_6C06()
0000:6C31       draw_stuff_6C31()
0000:6C5C       bullet_sound()
0000:7131       init_7131()
0000:7155       init_7155()
0000:7166       init_7166()
0000:718A       init_718A()
0000:719A       init_719A()
0000:71A4       index_memcpy
@pixjuan
Copy link
Author

pixjuan commented Oct 10, 2021

@fadden Thank you for link!
I didn't know your tool 6502bench, it seems very nice, I like the inline bitmap display.
For Sabotage, I used a more generic tools, IDA first, then Ghidra.
I did a Python script that rips most of the graphics, and started writing a python remake.

Your message reminds me that I really need to get my code and my Ghidra reverse in a publishable state and push it to a repo!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment