define memory map explicity

This commit is contained in:
Michael Miceli 2024-06-01 21:10:31 -04:00
parent 430949a484
commit 961aa789c2
18 changed files with 2063 additions and 519 deletions

View File

@ -68,6 +68,7 @@ rem show commands run in output
echo Assembling PRG Rom Banks
@echo on
ca65 -D %GAME% --debug-info -o obj\ram.o src\ram.asm
ca65 -D %GAME% --debug-info -o obj\constants.o src\constants.asm
ca65 -D %GAME% --debug-info -o obj\ines_header.o src\ines_header.asm
ca65 -D %GAME% --debug-info -o obj\bank0.o src\bank0.asm
@ -85,5 +86,5 @@ rem link assemblies together to single .nes ROM
echo "Creating .nes ROM"
@echo on
ld65 -C contra.cfg --dbgfile %DBG_NAME% .\obj\constants.o .\obj\ines_header.o .\obj\bank0.o .\obj\bank1.o .\obj\bank2.o .\obj\bank3.o .\obj\bank4.o .\obj\bank5.o .\obj\bank6.o .\obj\bank7.o -o %ROM_NAME%
ld65 -C contra.cfg --dbgfile %DBG_NAME% .\obj\ram.o .\obj\constants.o .\obj\ines_header.o .\obj\bank0.o .\obj\bank1.o .\obj\bank2.o .\obj\bank3.o .\obj\bank4.o .\obj\bank5.o .\obj\bank6.o .\obj\bank7.o -o %ROM_NAME%
@echo off

View File

@ -110,6 +110,7 @@ Write-Output "Assembling PRG Rom Banks"
# show commands run in output
Set-PSDebug -Trace 1
ca65 -D $Game --debug-info -o obj\ram.o src\ram.asm
ca65 -D $Game --debug-info -o obj\constants.o src\constants.asm
ca65 -D $Game --debug-info -o obj\ines_header.o src\ines_header.asm
ca65 -D $Game --debug-info -o obj\bank0.o src\bank0.asm
@ -127,7 +128,7 @@ Set-PSDebug -Trace 0
Write-Output "Creating .nes ROM"
Set-PSDebug -Trace 1
ld65 -C contra.cfg --dbgfile $DBG_NAME .\obj\constants.o .\obj\ines_header.o .\obj\bank0.o .\obj\bank1.o .\obj\bank2.o .\obj\bank3.o .\obj\bank4.o .\obj\bank5.o .\obj\bank6.o .\obj\bank7.o -o $ROM_NAME
ld65 -C contra.cfg --dbgfile $DBG_NAME .\obj\ram.o .\obj\constants.o .\obj\ines_header.o .\obj\bank0.o .\obj\bank1.o .\obj\bank2.o .\obj\bank3.o .\obj\bank4.o .\obj\bank5.o .\obj\bank6.o .\obj\bank7.o -o $ROM_NAME
# compare assembled ROM hash to expected hash if file exists
Set-PSDebug -Trace 0

View File

@ -105,6 +105,7 @@ done < $ASSETS_NAME
echo "$GAME" > $ASSET_GAME_TYPE
echo "Assembling PRG Rom Banks"
ca65 -D $GAME --debug-info -o obj/ram.o src/ram.asm
ca65 -D $GAME --debug-info -o obj/constants.o src/constants.asm
ca65 -D $GAME --debug-info -o obj/ines_header.o src/ines_header.asm
ca65 -D $GAME --debug-info -o obj/bank0.o src/bank0.asm
@ -117,7 +118,7 @@ ca65 -D $GAME --debug-info -o obj/bank6.o src/bank6.asm
ca65 -D $GAME --debug-info -o obj/bank7.o src/bank7.asm
echo "Creating .nes ROM"
ld65 -C contra.cfg --dbgfile $DBG_NAME ./obj/constants.o ./obj/ines_header.o ./obj/bank0.o ./obj/bank1.o ./obj/bank2.o ./obj/bank3.o ./obj/bank4.o ./obj/bank5.o ./obj/bank6.o ./obj/bank7.o -o $ROM_NAME
ld65 -C contra.cfg --dbgfile $DBG_NAME ./obj/ram.o ./obj/constants.o ./obj/ines_header.o ./obj/bank0.o ./obj/bank1.o ./obj/bank2.o ./obj/bank3.o ./obj/bank4.o ./obj/bank5.o ./obj/bank6.o ./obj/bank7.o -o $ROM_NAME
if test -f $ROM_NAME
then

View File

@ -1,5 +1,9 @@
# defines where the banks will be loaded into CPU address space
MEMORY {
# Define CPU memory map for zero page, not saved to ROM
ZEROPAGE: start = $00, size = $100, type = rw;
RAM: start = $100, size = $6ff, type = rw, fill = no;
# INES Cartridge Header, not loaded into CPU memory
HEADER: start = $0000, size = $0010, fill = yes;
@ -25,6 +29,8 @@ MEMORY {
# defines the order of the segments as they are stored in the .nes ROM file
SEGMENTS {
ZEROPAGE: load = ZEROPAGE, type = zp;
RAM: load = RAM, type = bss;
HEADER: load = HEADER, type = ro;
OAMRAM: load = OAMRAM, type = bss, define=yes, optional=yes;
BANK_0: load = BANK_0, type = ro;

View File

@ -16,7 +16,6 @@ player is in water and presses the d-pad down button.
|-------------------------------------|-------------------------------------|-------------------------------------|-------------------------------------|
| ![0](attachments/pw_0.png?raw=true) | ![1](attachments/pw_1.png?raw=true) | ![2](attachments/pw_2.png?raw=true) | ![3](attachments/pw_3.png?raw=true) |
```
@set_enter_water_sprite:
...

View File

@ -1200,7 +1200,6 @@ Other Names: Gordea, JJ, Jumping Joey, Jumping Jack Flash, Giant Boss Robot,
No attributes exist for this enemy. His health is calculated based on player's
`PLAYER_WEAPON_STRENGTH` value. The formula is below
#### Logic
* `ENEMY_VAR_1` - random number used to control boss action: jump, attack,

View File

@ -128,7 +128,6 @@ game by various other sounds.
| #$5c | n/a | high hat | n/a | n/a | low | delta modulation |
| #$ff | n/a | snowfield boss defeated door open (bug) | n/a | n/a | low | delta modulation |
The sound for pausing the game is not in the sound mode menu, presumably to not
confuse players into thinking the game is paused. As for names, I can guess at
some of the abbreviations and name meanings.

View File

@ -1,4 +1,4 @@
; Contra US Disassembly - v1.2
; Contra US Disassembly - v1.3
; https://github.com/vermiceli/nes-contra-us
; Bank 0 is used exclusively for enemy routines. Enemy routines are the logic
; controlling enemy behaviors, AI, movements, and attack patterns. Almost every
@ -2800,7 +2800,7 @@ eye_projectile_routine_00:
lda #$06 ; a = #$06 (projectile speed)
sta $06
lda #$01 ; a = #$01 (quadrant_aim_dir_01)
sta $0f ; quadrant_aim_dir_lookup_tbl offset
sta $0f ; quadrant_aim_dir_lookup_ptr_tbl offset
jsr get_quadrant_aim_dir_for_player ; set a to the aim direction within a quadrant
; based on source position ($09, $08) targeting player index $0a
jsr set_bullet_velocities ; set the projectile X and Y velocities (both high and low) based on register a (#$01)
@ -5688,7 +5688,7 @@ spinning_bubbles_routine_00:
lda spinning_bubbles_speed_tbl,y ; load bullet velocity routine table value (bullet_velocity_adjust_xx)
sta $06 ; store bullet direction velocity routine value (bullet_velocity_adjust_xx) in $06
lda #$01 ; a = #$01 (quadrant_aim_dir_01)
sta $0f ; set quadrant_aim_dir_lookup_tbl offset to #$01
sta $0f ; set quadrant_aim_dir_lookup_ptr_tbl offset to #$01
jsr get_quadrant_aim_dir_for_player ; set a to the aim direction within a quadrant
; based on source position ($09, $08) targeting player index $0a
pha ; push quadrant aim dir to the stack

View File

@ -1,4 +1,4 @@
; Contra US Disassembly - v1.2
; Contra US Disassembly - v1.3
; https://github.com/vermiceli/nes-contra-us
; Bank 1 is responsible for audio and sprites. The audio code takes up about
; 3/4 of the bank. The remaining 1/4 of the bank is for sprite data and code to
@ -323,7 +323,7 @@ set_pulse_config:
@continue:
ora SOUND_CFG_HIGH,x ; merge with high nibble of pulse config value
jsr ldx_pulse_triangle_reg ; set x to apu channel register [0, 1, 4, 5, 8, #$c]
bcs @exit
bcs @exit ; exit if there is already a sound playing on that channel that has priority
sta APU_PULSE_CONFIG,x ; set either pulse channel 1 or 2 config
; a is either (PULSE_VOLUME,x - UNKNOWN_SOUND_01) | SOUND_CFG_HIGH,x
; or #$00 | SOUND_CFG_HIGH,x
@ -517,7 +517,7 @@ read_sound_command_00:
; output
; * x - SOUND_CURRENT_SLOT [0-3]
read_high_sound_cmd:
lda SOUND_CURRENT_SLOT ; loud sound slot index
lda SOUND_CURRENT_SLOT ; load sound slot index
cmp #$03 ; compare to sound slot #$03 (noise/dmc channel)
beq parse_percussion_cmd ; branch if sound slot #$03 (noise/dmc channel)
lda ($e0),y ; not noise channel, load sound byte
@ -671,7 +671,7 @@ interpret_sound_byte:
@set_sweep_continue:
jsr ldx_pulse_triangle_reg ; set x to apu channel register [0, 1, 4, 5, 8, #$c]
bcs @next_high_control_sound_byte
bcs @next_high_control_sound_byte ; branch if there is already a sound playing on that channel that has priority
sta APU_PULSE_SWEEP,x ; enable or disable sweep
@next_high_control_sound_byte:
@ -712,7 +712,7 @@ interpret_sound_byte:
; set config register ($4000, $4004, or $400c) and period & length register
@set_cfg_period_length:
jsr ldx_pulse_triangle_reg ; set x to apu channel register [0, 1, 4, 5, 8, #$c]
bcs @load_set_period_length
bcs @load_set_period_length ; branch if there is already a sound playing on that channel that has priority
sta $4000,x ; set pulse, triangle, or noise channel configuration
@load_set_period_length:
@ -853,7 +853,7 @@ set_note:
sta SOUND_PULSE_LENGTH,x ; set in memory copy of current pulse length
ora #$08 ; set bit 0 of high timer to be 1
jsr ldx_pulse_triangle_reg ; set x to apu channel register [0, 1, 4, 5, 8, #$c]
bcs @set_period
bcs @set_period ; branch if there is already a sound playing on that channel that has priority
sta APU_PULSE_LENGTH,x ; set duration and high 3 bits of the pulse, or triangle channel
; set low period
@ -866,7 +866,7 @@ set_note:
@set_apu_period:
jsr ldx_pulse_triangle_reg ; set x to apu channel register [0, 1, 4, 5, 8, #$c]
bcs @restore_x_adv_sound_ptr ; if carry set, do not update APU register
bcs @restore_x_adv_sound_ptr ; branch if there is already a sound playing on that channel that has priority
; continue to restore x to the sound slot index, update SOUND_CMD_LOW_ADDR value, and exit
sta APU_PULSE_PERIOD,x ; update APU pulse period
@ -1007,7 +1007,9 @@ skip_3_read_sound_command_01:
; (except SOUND_FLAGS are loaded within read_sound_command_00)
; set sound channel configuration (mute), advance sound command address
; input
; * a - amount to multiply SOUND_CMD_LENGTH by
; * y - current sound_xx read offset
sound_cmd_routine_00:
jsr calc_cmd_delay ; multiply SOUND_CMD_LENGTH by a
lda #$00 ; sound config low nibble = #$00 (mute sound channel)
@ -1017,7 +1019,7 @@ sound_cmd_routine_00:
@continue:
jsr ldx_pulse_triangle_reg ; set x to apu channel register [0, 1, 4, 5, 8, #$c]
bcs @adv_read_addr
bcs @adv_read_addr ; branch if there is already a sound playing on that channel that has priority
sta $4000,x ; set pulse 1, pulse 2, or triangle configuration
@adv_read_addr:
@ -1028,12 +1030,14 @@ sound_cmd_routine_00:
jmp adv_sound_cmd_addr ; set the sound_xx command read offset to current read location + 1
; set in memory configuration for channel, set multiplier, and sometimes read_high_sound_cmd
; input
; * a - low nibble of sound byte value
; * y - current sound_xx read offset
sound_cmd_routine_01:
sta SOUND_LENGTH_MULTIPLIER,x ; store value used to calculate SOUND_CMD_LENGTH
iny
iny ; increment sound code read offset
lda ($e0),y ; load sound code byte
cpx #$02 ; compare to sound slot #$02 (triangle channel)
cpx #$02 ; compare current sound slot to sound slot #$02 (triangle channel)
beq set_sound_triangle_config ; branch if triangle channel to set triangle config in memory and read_high_sound_cmd
and #$0f ; not triangle sound slot, get low nibble
sec
@ -1293,7 +1297,7 @@ sound_exit_00:
mute_channel:
lda #$30 ; a = #$30 (mute pulse channel register)
jsr ldx_pulse_triangle_reg ; set x to apu channel register [0, 1, 4, 5, 8, #$c]
bcs @continue
bcs @continue ; branch if there is already a sound playing on that channel that has priority
sta $4000,x ; update pulse channel config (mute pulse channel 1 or 2 register)
@continue:
@ -1373,7 +1377,7 @@ sound_cmd_ptr_tbl:
; table for note period to use when writing notes to the APU (#$30 bytes)
; the frequency of the pulse channels is a division of the CPU Clock (1.789773MHz NTSC, 1.662607MHz PAL)
; the output frequency (f) of the generator can be determined by the 11-bit period value (f_pulse) written to $4002$4003/$4006$4007
; the output frequency (f) of the generator can be determined by the 11-bit period value (f_pulse) written to $4002-$4003/$4006-$4007
; note that triangle channel is one octave lower
; frequency = cpu_speed / (#$0f * (f_pulse + 1))
; ex: 1789773 / (#$0f * (#$06ae + 1)) => 65.38 Hz

View File

@ -1,4 +1,4 @@
; Contra US Disassembly - v1.2
; Contra US Disassembly - v1.3
; https://github.com/vermiceli/nes-contra-us
; Bank 2 starts with RLE-encoded level data (graphic super tiles for the level
; screens). It then contains compressed tile data and alternate tile data and

View File

@ -1,4 +1,4 @@
; Contra US Disassembly - v1.2
; Contra US Disassembly - v1.3
; https://github.com/vermiceli/nes-contra-us
; Bank 3 starts with the data that specifies which pattern table tiles comprises
; super-tiles along with the color palettes. This bank also has the routines

View File

@ -1,4 +1,4 @@
; Contra US Disassembly - v1.2
; Contra US Disassembly - v1.3
; https://github.com/vermiceli/nes-contra-us
; Bank 4 mostly contains compressed graphic data. The rest of bank 4 is the code
; for the ending scene animation and the ending credits, including the ending

View File

@ -1,4 +1,4 @@
; Contra US Disassembly - v1.2
; Contra US Disassembly - v1.3
; https://github.com/vermiceli/nes-contra-us
; Bank 5 mostly contains compressed graphic data. The rest of bank 5 is the
; code and lookup tables for automated input for the 3 demo (attract) levels.
@ -86,15 +86,16 @@ graphic_data_17:
graphic_data_18:
.incbin "assets/graphic_data/graphic_data_18.bin"
; run as part of showing the demo
; DEMO_FIRE_DELAY_TIMER starts at 0 increments to #$ff and stops
; simulates player input for demo levels for both players
; begins firing after #$e0 frames (see DEMO_FIRE_DELAY_TIMER)
load_demo_input_table:
lda CONTROLLER_STATE_DIFF ; get player input
and #$30 ; start and select button
bne end_demo_level ; exit demo if player has pressed start or select
inc DEMO_FIRE_DELAY_TIMER ; increase DEMO_FIRE_DELAY_TIMER by 1
inc DEMO_FIRE_DELAY_TIMER ; starts at 0 increments to #$ff and stops
; used by demo logic to wait #$e0 frames until begin firing
bne @player_loop ; branch when DEMO_FIRE_DELAY_TIMER is not 0 (hasn't wrapped around)
dec DEMO_FIRE_DELAY_TIMER ; decrease DEMO_FIRE_DELAY_TIMER by 1 (setting to -1), this means delay is complete
dec DEMO_FIRE_DELAY_TIMER ; wrapped around, pin to #$ff
@player_loop:
ldx #$01 ; initialize X to 1 (player loop starting at player 2)
@ -163,7 +164,7 @@ set_player_demo_input:
; for non M, nor L weapon, press b button every #$07 frames
@fire_weapon_input:
lda FRAME_COUNTER ; load frame counter
and #$07 ; checking every 7th frame
and #$07 ; checking every 8th frame
bne player_demo_input_chg_player ; move to next player without firing weapon
lda CONTROLLER_STATE_DIFF,x ; load current controller input
ora #$40 ; press b button
@ -195,7 +196,7 @@ demo_input_pointer_table:
; * second byte is number of even-numbered frames to apply the input for
; while possible, player firing isn't specified in these input tables
; instead, that is handled automatically as part of running the demo
; * m or l weapons are always firing, other weapons fire every #$07 frames
; * m or l weapons are always firing, other weapons fire every #$08 frames
; $00, $00 is filler so the demo level doesn't end by reading a #$ff
; input table for level 1 player 1 for demo (#$5A bytes)
demo_input_tbl_l1_p1:

View File

@ -1,4 +1,4 @@
; Contra US Disassembly - v1.2
; Contra US Disassembly - v1.3
; https://github.com/vermiceli/nes-contra-us
; Bank 6 contains compressed graphics data, data for short text sequences like
; level names and menu options. Bank 6 also contains the code for the players'
@ -289,7 +289,7 @@ intro_background_palette2:
; ensure player in valid state to fire a bullet, e.g. not being electrocuted
check_player_fire:
lda PLAYER_HIDDEN,x ; 0 - visible; #$01/#$ff = invisible (any non-zero)
ora $c8,x ; counter for electrocution
ora ELECTROCUTED_TIMER,x ; counter for electrocution
bne check_player_fire_exit ; exit if being electrocuted or $ba,x is set
lda PLAYER_WATER_STATE,x ; see if player in water
beq @player_shoot_test
@ -862,7 +862,7 @@ set_bullet_velocity:
asl ; double twice to get correct offset
tay
lda ($01),y ; load x velocity fast value
sta PLAYER_BULLET_VEL_X_FAST,x ; store x velocity fast value
sta PLAYER_BULLET_X_VEL_FAST,x ; store x velocity fast value
iny ; increment velocity table read offset
lda ($01),y ; load x fractional velocity value
sta PLAYER_BULLET_X_VEL_FRACT,x ; store x fractional velocity value
@ -1000,7 +1000,7 @@ set_indoor_bullet_vel:
@set_x_vel:
jsr @set_vel_for_speed_code ; determine fast and fractional velocity based on a and whether rapid fire is enabled
lda $0f ; load resulting fast velocity
sta PLAYER_BULLET_VEL_X_FAST,x ; set indoor bullet fast x velocity
sta PLAYER_BULLET_X_VEL_FAST,x ; set indoor bullet fast x velocity
lda $0e ; load resulting fractional velocity
sta PLAYER_BULLET_X_VEL_FRACT,x ; set indoor bullet fractional x velocity
rts
@ -1118,7 +1118,7 @@ s_weapon_init_bullet_velocities:
lda ($04),y
sta PLAYER_BULLET_Y_VEL_FAST,x
lda ($06),y
sta PLAYER_BULLET_VEL_X_FAST,x
sta PLAYER_BULLET_X_VEL_FAST,x
rts
; table for player aim direction (#$c bytes)
@ -1651,7 +1651,7 @@ update_player_fs_bullet_x_pos:
adc PLAYER_BULLET_X_VEL_FRACT,x ; add x fractional velocity, noting the carry being set if overflow
sta PLAYER_BULLET_VEL_FS_X_ACCUM,x ; add accumulated value back
lda PLAYER_BULLET_FS_X,x
adc PLAYER_BULLET_VEL_X_FAST,x ; add fast X velocity and any carry from accumulator
adc PLAYER_BULLET_X_VEL_FAST,x ; add fast X velocity and any carry from accumulator
sta PLAYER_BULLET_FS_X,x
rts
@ -1660,19 +1660,19 @@ update_player_bullet_pos:
jsr check_bullet_solid_bg_collision ; if specified, check for bullet collision with solid background
; and if so move bullet routine to player_bullet_collision_routine
bmi bullet_logic_exit ; exit if bullet collided with solid object
lda PLAYER_BULLET_VEL_X_ACCUM,x ; load accumulator value for bullet X velocity
lda PLAYER_BULLET_X_VEL_ACCUM,x ; load accumulator value for bullet X velocity
clc ; clear carry in preparation for addition
adc PLAYER_BULLET_X_VEL_FRACT,x ; add x fractional velocity, noting the carry being set if overflow
sta PLAYER_BULLET_VEL_X_ACCUM,x ; add accumulated value back
sta PLAYER_BULLET_X_VEL_ACCUM,x ; add accumulated value back
lda PLAYER_BULLET_X_POS,x ; load bullet X position
adc PLAYER_BULLET_VEL_X_FAST,x ; add fast X velocity and any carry from accumulator
adc PLAYER_BULLET_X_VEL_FAST,x ; add fast X velocity and any carry from accumulator
sta PLAYER_BULLET_X_POS,x ; set new X position
update_player_bullet_y_pos:
clc ; clear carry in preparation for addition
lda PLAYER_BULLET_VEL_Y_ACCUM,x ; load accumulator value for bullet Y velocity
lda PLAYER_BULLET_Y_VEL_ACCUM,x ; load accumulator value for bullet Y velocity
adc PLAYER_BULLET_Y_VEL_FRACT,x ; add y fractional velocity, noting the carry being set if overflow
sta PLAYER_BULLET_VEL_Y_ACCUM,x ; add accumulated value back
sta PLAYER_BULLET_Y_VEL_ACCUM,x ; add accumulated value back
lda PLAYER_BULLET_Y_POS,x ; load bullet Y position
adc PLAYER_BULLET_Y_VEL_FAST,x ; add fast Y velocity and any carry from accumulator
sta PLAYER_BULLET_Y_POS,x ; set new Y position
@ -1726,7 +1726,7 @@ clear_bullet_values:
sta PLAYER_BULLET_F_RAPID,x
sta PLAYER_BULLET_DIST,x
sta PLAYER_BULLET_AIM_DIR,x
sta PLAYER_BULLET_VEL_X_FAST,x
sta PLAYER_BULLET_X_VEL_FAST,x
sta PLAYER_BULLET_X_VEL_FRACT,x
sta PLAYER_BULLET_Y_VEL_FAST,x
sta PLAYER_BULLET_Y_VEL_FRACT,x
@ -1822,7 +1822,7 @@ update_s_bullet_indoor_pos:
lda PLAYER_BULLET_VEL_FS_X_ACCUM,x ; ignore, no affect
clc ; ignore, no affect
adc PLAYER_BULLET_S_ADJ_ACCUM,x ; ignore, no affect
sta PLAYER_BULLET_VEL_X_ACCUM,x ; unused result, never read for S indoor bullets !(WHY?)
sta PLAYER_BULLET_X_VEL_ACCUM,x ; unused result, never read for S indoor bullets !(WHY?)
lda PLAYER_BULLET_FS_X,x ; load center x position on screen f bullet swirls around
clc ; clear carry in preparation for addition
adc PLAYER_BULLET_S_INDOOR_ADJ,x ; add the indoor adjustment

View File

@ -1,4 +1,4 @@
; Contra US Disassembly - v1.2
; Contra US Disassembly - v1.3
; https://github.com/vermiceli/nes-contra-us
; Bank 7 is the core of the game's programming. Reset, NMI, and IRQ vectors are
; in this bank and is the entry point to the game. Bank 7 is always loaded in
@ -4889,7 +4889,6 @@ set_vel_for_speed_code_a:
sta $0e ; set $0e to #$00
ldy #$07 ; set number of bits to rotate speed code to #$07
; for a given value $0f, set fast ($0f) and fractional ($0e) velocities based on y
; negate final results if $12 is greater than or equal to #$00
; also used directly for indoor bullets
@ -9734,7 +9733,7 @@ aim_and_create_enemy_bullet:
sty $06 ; store bullet speed code in $06
sta $00 ; store bullet type temporarily
lda #$01 ; a = #$01, use quadrant_aim_dir_01
sta $0f ; quadrant_aim_dir_lookup_tbl offset (quadrant_aim_dir_01)
sta $0f ; quadrant_aim_dir_lookup_ptr_tbl offset (quadrant_aim_dir_01)
lda $0a ; load player y position
bpl @continue ; branch if >= #$00
lda $0c ; load player y position
@ -9857,10 +9856,10 @@ create_enemy_bullet:
@continue:
sta $06 ; store speed code in $06
lda $08 ; load enemy bullet y position
sta ENEMY_Y_POS,x ; store enemy bullet y position on screen
lda $09 ; load enemy bullet x position
sta ENEMY_X_POS,x ; store enemy bullet x position on screen
lda $08 ; load created bullet enemy y position
sta ENEMY_Y_POS,x ; set created bullet enemy y position
lda $09 ; load created bullet enemy y position
sta ENEMY_X_POS,x ; set created bullet enemy x position
lda $0a
and #$1f ; keep bits ...x xxxx (quadrant aim dir)
@ -10151,7 +10150,7 @@ rotate_enemy_var_1:
rts
; determines which direction to rotate based on quadrant_aim_dir_00
; targetting player index ($0a)
; targeting player index ($0a)
; input
; * $0a - player index to target, 0 = player 1, 1 = player 2
; * $08 - source y position
@ -10162,10 +10161,10 @@ rotate_enemy_var_1:
; * $0c - new enemy aim direction
get_rotate_00:
lda #$00 ; a = #$00 (use quadrant_aim_dir_00)
beq get_rotate_dir_for_index ; always jump, get enemy aim direction and rotation direction using quadrant_aim_dir_00
beq get_rotate_dir_for_index ; always branch, get enemy aim direction and rotation direction using quadrant_aim_dir_00
; determines which direction to rotate based on quadrant_aim_dir_01
; targetting player index ($0a)
; targeting player index ($0a)
; input
; * $0a - player index to target, 0 = player 1, 1 = player 2
; * $08 - source y position
@ -10177,10 +10176,10 @@ get_rotate_00:
get_rotate_01:
lda #$01 ; a = #$01 (use quadrant_aim_dir_01)
; determines which direction to rotate based on quadrant_aim_dir_lookup_tbl index offset (a)
; targetting player index ($0a)
; determines which direction to rotate based on quadrant_aim_dir_lookup_ptr_tbl index offset (a)
; targeting player index ($0a)
; input
; * a - quadrant_aim_dir_lookup_tbl offset table
; * a - quadrant_aim_dir_lookup_ptr_tbl offset table
; * $0a - player index to target, 0 = player 1, 1 = player 2
; * $08 - source y position
; * $09 - source x position
@ -10189,7 +10188,7 @@ get_rotate_01:
; * a - rotation direction, #$00 clockwise, #$01 counterclockwise, #$80 no rotation needed
; * $0c - new enemy aim direction
get_rotate_dir_for_index:
sta $0f ; set quadrant_aim_dir_lookup_tbl index offset
sta $0f ; set quadrant_aim_dir_lookup_ptr_tbl index offset
lda $0a ; load player index
bpl @get_quadrant_aim_dir ; branch if closest player has been determined
lda $0c ; no player to target, not sure when this happens (see player_enemy_x_dist)
@ -10211,14 +10210,14 @@ get_rotate_dir_for_index:
; * $07 - specifies quadrant to aim in (0 = quadrant IV, 1 = quadrant I, 2 = quadrant III, 3 = quadrant II)
; * bit 0 - 0 = bottom half of plane (quadrants III and IV), 1 = top half of plane (quadrants I and II)
; * bit 1 - 0 = right half of the plan (quadrants I and IV), 1 = left half of plane (quadrants II and III)
; * $0f - quadrant_aim_dir_lookup_tbl offset
; * $0f - quadrant_aim_dir_lookup_ptr_tbl offset
; output
; * negative flag - set when enemy is already aiming at player and no rotation is needed
; * a - rotation direction, #$00 clockwise, #$01 counterclockwise, #$80 no rotation needed
; * $0c - new enemy aim direction
get_rotate_dir:
sta $0c ; store quadrant aim direction code in $0c
lda $0f ; load quadrant_aim_dir_lookup_tbl offset (which quadrant_aim_dir_xx to use)
lda $0f ; load quadrant_aim_dir_lookup_ptr_tbl offset (which quadrant_aim_dir_xx to use)
lsr ; move bit 0 to the carry
lda #$06 ; using either quadrant_aim_dir_00, or quadrant_aim_dir_02
; midway direction, i.e. 9 o'clock
@ -10324,7 +10323,7 @@ get_rotate_dir:
dragon_arm_orb_seek_should_move:
jsr set_08_09_to_enemy_pos ; set $08 and $09 to enemy x's X and Y position
lda #$02 ; dragon arm orb is only enemy that uses quadrant_aim_dir_02
sta $0f ; set quadrant_aim_dir_lookup_tbl offset to use quadrant_aim_dir_02
sta $0f ; set quadrant_aim_dir_lookup_ptr_tbl offset to use quadrant_aim_dir_02
jsr get_quadrant_aim_dir_for_player ; set a to the aim direction within a quadrant
; based on source position ($09, $08) targeting player index $0a
sta $0c ; store enemy aim direction in $0c
@ -10393,7 +10392,7 @@ dragon_arm_orb_seek_should_move:
; determines the aim direction within a quadrant based on source position ($09, $08) targeting player index $0a
; input
; * $0f - quadrant_aim_dir_lookup_tbl offset [#$00-#$02]
; * $0f - quadrant_aim_dir_lookup_ptr_tbl offset [#$00-#$02]
; * $0a - player index of player to target (#$00 for p1 or #$01 for p2)
; * $08 - source y position
; * $09 - source x position
@ -10443,7 +10442,7 @@ get_quadrant_aim_dir_for_player:
; * $09 - source x position
; * $0a - closest player y position
; * $0b - closest player x position
; * $0f - which of the #$03 tables from quadrant_aim_dir_lookup_tbl to use
; * $0f - which of the #$03 tables from quadrant_aim_dir_lookup_ptr_tbl to use
; output
; * a - quadrant aim direction (quadrant_aim_dir_xx value)
; * $07 - specifies quadrant to aim in (0 = quadrant IV, 1 = quadrant I, 2 = quadrant III, 3 = quadrant II)
@ -10453,7 +10452,7 @@ get_quadrant_aim_dir:
ldy #$00 ; default assume player is to the right and equal to or below enemy
lda $0a ; load closest player y position
sec ; set carry flag in preparation for subtraction
sbc $08 ; subract enemy y position from player y position
sbc $08 ; subtract enemy y position from player y position
bcs @shift_get_x_diff ; branch if no overflow occurred (enemy above player or same vertical position)
eor #$ff ; enemy below player, handle overflow, flip all bits and add one
adc #$01
@ -10477,33 +10476,33 @@ get_quadrant_aim_dir:
iny ; if y was 0, now is 2, if y was 1, now is 3
@continue:
lsr ; shift the difference between player and enemy x difference 6 bits
lsr ; (every #$40 pixels difference is a new horizontal direction)
lsr ; shift the difference between player and enemy x difference 6 bits
lsr ; (every #$40 pixels difference is a new horizontal direction)
lsr
lsr
lsr
sty $07 ; store position of player relative to enemy in $07 (above/below, left/right)
lsr ; push bit 5 to the carry flag for use after plp instruction below
sta $0b ; overwrite player x position with shifted bits 6 and 7
; (values [#$00-#$03]) of horizontal distance
php ; backup CPU status flags on stack
lda $0f ; load which of the #$03 tables from quadrant_aim_dir_lookup_tbl to use
asl ; double since each entry is #$2 bytes
tay ; transfer to offset register
lda quadrant_aim_dir_lookup_tbl,y ; get low byte of quadrant_aim_dir_xx address
sta $0c ; store low byte of pointer address in $0c
lda quadrant_aim_dir_lookup_tbl+1,y ; get high byte of quadrant_aim_dir_xx address
sta $0d ; store high byte of pointer address in $0d
lda $0a ; load y difference to determine row offset
sty $07 ; store position of player relative to enemy in $07 (above/below, left/right)
lsr ; push bit 5 to the carry flag for use after plp instruction below
sta $0b ; overwrite player x position with shifted bits 6 and 7
; (values [#$00-#$03]) of horizontal distance
php ; backup CPU status flags on stack
lda $0f ; load which of the #$03 tables from quadrant_aim_dir_lookup_ptr_tbl to use
asl ; double since each entry is #$2 bytes
tay ; transfer to offset register
lda quadrant_aim_dir_lookup_ptr_tbl,y ; get low byte of quadrant_aim_dir_xx address
sta $0c ; store low byte of pointer address in $0c
lda quadrant_aim_dir_lookup_ptr_tbl+1,y ; get high byte of quadrant_aim_dir_xx address
sta $0d ; store high byte of pointer address in $0d
lda $0a ; load y difference to determine row offset
asl
asl ; quadruple since each entry is #$04 bytes to get correct row
adc $0b ; add the x distance between player and enemy as offset into the entry to load
; this gets the column of the aim direction
tay ; transfer to offset register
lda ($0c),y ; load specific byte
plp ; restore CPU status flags from stack
bcs @set_and_exit ; branch if bit 5 of difference between player and enemy was set
lsr ; this segments screen into bands for which nibble to use
asl ; quadruple since each entry is #$04 bytes to get correct row
adc $0b ; add the x distance between player and enemy as offset into the entry to load
; this gets the column of the aim direction
tay ; transfer to offset register
lda ($0c),y ; load specific byte
plp ; restore CPU status flags from stack
bcs @set_and_exit ; branch if bit 5 of difference between player and enemy was set
lsr ; this segments screen into bands for which nibble to use
lsr
lsr
lsr
@ -10512,8 +10511,8 @@ get_quadrant_aim_dir:
and #$0f ; keep low nibble
rts
; pointer table for set of quadran aim directions (#$3 * #$2 = #$6 bytes)
quadrant_aim_dir_lookup_tbl:
; pointer table for set of quadrant aim directions (#$3 * #$2 = #$6 bytes)
quadrant_aim_dir_lookup_ptr_tbl:
.addr quadrant_aim_dir_00 ; CPU address $f5b2 (soldiers, weapon boxes, red turrets, wall core)
.addr quadrant_aim_dir_01 ; CPU address $f5d2 (rotating gun, wall turrets, sniper, eye projectile, spinning bubbles, jumping soldier, white blob)
.addr quadrant_aim_dir_02 ; CPU address $f5f2 (dragon arm seeking)

View File

@ -1,438 +1,258 @@
; Contra US Disassembly - v1.2
; Contra US Disassembly - v1.3
; https://github.com/vermiceli/nes-contra-us
; constants.asm contains the list of constants with meaningful names for the
; memory addresses used by the game. It also contains constants for the various
; palette colors.
.importzp GAME_ROUTINE_INDEX ; $18
.importzp GAME_END_ROUTINE_INDEX ; $19
.importzp GAME_ROUTINE_INIT_FLAG ; $19
.importzp FRAME_COUNTER ; $1a
.importzp NMI_CHECK ; $1b
.importzp DEMO_MODE ; $1c
.importzp PLAYER_MODE_1D ; $1d
.importzp DEMO_LEVEL_END_FLAG ; $1f
.importzp PPU_READY ; $20
.importzp GRAPHICS_BUFFER_OFFSET ; $21
.importzp PLAYER_MODE ; $22
.importzp GRAPHICS_BUFFER_MODE ; $23
.importzp KONAMI_CODE_STATUS ; $24
.importzp PAUSE_STATE ; $25
.importzp DEMO_LEVEL ; $27
.importzp INTRO_THEME_DELAY ; $28
.importzp GAME_OVER_DELAY_TIMER ; $29
.importzp DELAY_TIME_LOW_BYTE ; $2a
.importzp DELAY_TIME_HIGH_BYTE ; $2b
.importzp LEVEL_ROUTINE_INDEX ; $2c
.importzp END_LEVEL_ROUTINE_INDEX ; $2d
.importzp DEMO_FIRE_DELAY_TIMER ; $2e
.importzp PLAYER_WEAPON_STRENGTH ; $2f
.importzp CURRENT_LEVEL ; $30
.importzp GAME_COMPLETION_COUNT ; $31
.importzp P1_NUM_LIVES ; $32
.importzp P2_NUM_LIVES ; $33
.importzp RANDOM_NUM ; $34
.importzp NUM_PALETTES_TO_LOAD ; $36
.importzp INDOOR_SCREEN_CLEARED ; $37
.importzp P1_GAME_OVER_STATUS ; $38
.importzp P2_GAME_OVER_STATUS ; $39
.importzp NUM_CONTINUES ; $3a
.importzp BOSS_DEFEATED_FLAG ; $3b
.importzp EXTRA_LIFE_SCORE_LOW ; $3c
.importzp EXTRA_LIFE_SCORE_HIGH ; $3d
.importzp KONAMI_CODE_NUM_CORRECT ; $3f
.importzp LEVEL_LOCATION_TYPE ; $40
.importzp LEVEL_SCROLLING_TYPE ; $41
.importzp LEVEL_SCREEN_SUPERTILES_PTR ; $42
.importzp LEVEL_SUPERTILE_DATA_PTR ; $44
.importzp LEVEL_SUPERTILE_PALETTE_DATA ; $46
.importzp LEVEL_ALT_GRAPHICS_POS ; $48
.importzp COLLISION_CODE_1_TILE_INDEX ; $49
.importzp COLLISION_CODE_0_TILE_INDEX ; $4a
.importzp COLLISION_CODE_2_TILE_INDEX ; $4b
.importzp LEVEL_PALETTE_CYCLE_INDEXES ; $4c
.importzp LEVEL_PALETTE_INDEX ; $50
.importzp LEVEL_STOP_SCROLL ; $58
.importzp LEVEL_SOLID_BG_COLLISION_CHECK ; $59
.importzp DEMO_INPUT_NUM_FRAMES ; $5a
.importzp DEMO_INPUT_VAL ; $5c
.importzp DEMO_INPUT_TBL_INDEX ; $5e
.importzp PPU_WRITE_TILE_OFFSET ; $60
.importzp LEVEL_TRANSITION_TIMER ; $61
.importzp PPU_WRITE_ADDRESS_LOW_BYTE ; $62
.importzp PPU_WRITE_ADDRESS_HIGH_BYTE ; $63
.importzp LEVEL_SCREEN_NUMBER ; $64
.importzp LEVEL_SCREEN_SCROLL_OFFSET ; $65
.importzp ATTRIBUTE_TBL_WRITE_LOW_BYTE ; $66
.importzp ATTRIBUTE_TBL_WRITE_HIGH_BYTE ; $67
.importzp FRAME_SCROLL ; $68
.importzp SUPERTILE_NAMETABLE_OFFSET ; $69
.importzp SPRITE_LOAD_TYPE ; $6a
.importzp CONT_END_SELECTION ; $6b
.importzp ALT_GRAPHIC_DATA_LOADING_FLAG ; $71
.importzp LEVEL_PALETTE_CYCLE ; $72
.importzp INDOOR_SCROLL ; $73
.importzp BG_PALETTE_ADJ_TIMER ; $74
.importzp AUTO_SCROLL_TIMER_00 ; $75
.importzp AUTO_SCROLL_TIMER_01 ; $76
.importzp TANK_AUTO_SCROLL ; $77
.importzp PAUSE_PALETTE_CYCLE ; $78
.importzp SOLDIER_GENERATION_ROUTINE ; $79
.importzp SOLDIER_GENERATION_TIMER ; $7a
.importzp SOLDIER_GENERATION_X_POS ; $7b
.importzp SOLDIER_GENERATION_Y_POS ; $7c
.importzp FALCON_FLASH_TIMER ; $7d
.importzp TANK_ICE_JOINT_SCROLL_FLAG ; $7f
.importzp ENEMY_LEVEL_ROUTINES ; $80
.importzp ENEMY_SCREEN_READ_OFFSET ; $82
.importzp ENEMY_CURRENT_SLOT ; $83
.importzp BOSS_AUTO_SCROLL_COMPLETE ; $84
.importzp BOSS_SCREEN_ENEMIES_DESTROYED ; $85
.importzp WALL_CORE_REMAINING ; $86
.importzp WALL_PLATING_DESTROYED_COUNT ; $87
.importzp INDOOR_ENEMY_ATTACK_COUNT ; $88
.importzp INDOOR_RED_SOLDIER_CREATED ; $89
.importzp GRENADE_LAUNCHER_FLAG ; $8a
.importzp ALIEN_FETUS_AIM_TIMER_INDEX ; $8b
.importzp ENEMY_ATTACK_FLAG ; $8e
.importzp PLAYER_STATE ; $90
.importzp INDOOR_TRANSITION_X_ACCUM ; $92
.importzp PLAYER_JUMP_COEFFICIENT ; $94
.importzp INDOOR_TRANSITION_X_FRACT_VEL ; $96
.importzp PLAYER_X_VELOCITY ; $98
.importzp INDOOR_TRANSITION_Y_FRACT_VEL ; $9a
.importzp INDOOR_TRANSITION_Y_FAST_VEL ; $9c
.importzp PLAYER_ANIM_FRAME_TIMER ; $9e
.importzp PLAYER_JUMP_STATUS ; $a0
.importzp PLAYER_FRAME_SCROLL ; $a2
.importzp EDGE_FALL_CODE ; $a4
.importzp PLAYER_ANIMATION_FRAME_INDEX ; $a6
.importzp PLAYER_INDOOR_ANIM_Y ; $a8
.importzp P1_CURRENT_WEAPON ; $aa
.importzp P2_CURRENT_WEAPON ; $ab
.importzp PLAYER_M_WEAPON_FIRE_TIME ; $ac
.importzp NEW_LIFE_INVINCIBILITY_TIMER ; $ae
.importzp INVINCIBILITY_TIMER ; $b0
.importzp PLAYER_WATER_STATE ; $b2
.importzp PLAYER_DEATH_FLAG ; $b4
.importzp PLAYER_ON_ENEMY ; $b6
.importzp PLAYER_FALL_X_FREEZE ; $b8
.importzp PLAYER_HIDDEN ; $ba
.importzp PLAYER_SPRITE_SEQUENCE ; $bc
.importzp PLAYER_INDOOR_ANIM_X ; $be
.importzp PLAYER_AIM_PREV_FRAME ; $c0
.importzp PLAYER_AIM_DIR ; $c2
.importzp PLAYER_Y_FRACT_VELOCITY ; $c4
.importzp PLAYER_Y_FAST_VELOCITY ; $c6
.importzp ELECTROCUTED_TIMER ; $c8
.importzp INDOOR_PLAYER_JUMP_FLAG ; $ca
.importzp PLAYER_WATER_TIMER ; $cc
.importzp PLAYER_RECOIL_TIMER ; $ce
.importzp INDOOR_PLAYER_ADV_FLAG ; $d0
.importzp PLAYER_SPECIAL_SPRITE_TIMER ; $d2
.importzp PLAYER_FAST_X_VEL_BOOST ; $d4
.importzp PLAYER_SPRITE_CODE ; $d6
.importzp PLAYER_SPRITE_FLIP ; $d8
.importzp PLAYER_BG_FLAG_EDGE_DETECT ; $da
.importzp PLAYER_GAME_OVER_BIT_FIELD ; $df
.importzp SOUND_TABLE_PTR ; $ec
.importzp CONTROLLER_STATE ; $f1
.importzp CONTROLLER_STATE_DIFF ; $f5
.importzp CTRL_KNOWN_GOOD ; $f9
.importzp VERTICAL_SCROLL ; $fc
.importzp HORIZONTAL_SCROLL ; $fd
.importzp PPUMASK_SETTINGS ; $fe
.importzp PPUCTRL_SETTINGS ; $ff
.import SOUND_CMD_LENGTH ; $0100
.import SOUND_CODE ; $0106
.import SOUND_PULSE_LENGTH ; $010c
.import SOUND_CMD_LOW_ADDR ; $0112
.import SOUND_CMD_HIGH_ADDR ; $0118
.import SOUND_VOL_ENV ; $011e
.import SOUND_CURRENT_SLOT ; $0120
.import PERCUSSION_INDEX_BACKUP ; $0121
.import INIT_SOUND_CODE ; $0122
.import SOUND_CHNL_REG_OFFSET ; $0123
.import SOUND_FLAGS ; $0124
.import LVL_PULSE_VOL_INDEX ; $012a
.import PULSE_VOL_DURATION ; $012a
.import PAUSE_STATE_01 ; $012f
.import DECRESCENDO_END_PAUSE ; $0131
.import SOUND_PITCH_ADJ ; $0132
.import UNKNOWN_SOUND_00 ; $0136
.import UNKNOWN_SOUND_01 ; $013c
.import SOUND_CFG_LOW ; $0142
.import SOUND_TRIANGLE_CFG ; $0144
.import SOUND_REPEAT_COUNT ; $0148
.import SOUND_CFG_HIGH ; $014e
.import SOUND_LENGTH_MULTIPLIER ; $0154
.import SOUND_PERIOD_ROTATE ; $015a
.import PULSE_VOLUME ; $0160
.import NEW_SOUND_CODE_LOW_ADDR ; $0166
.import NEW_SOUND_CODE_HIGH_ADDR ; $016c
.import SOUND_PULSE_PERIOD ; $0172
.import VIBRATO_CTRL ; $0178
.import SOUND_VOL_TIMER ; $017a
.import PULSE_NOTE ; $017c
.import VIBRATO_DELAY ; $017e
.import VIBRATO_AMOUNT ; $0180
.import LEVEL_END_DELAY_TIMER ; $0190
.import LEVEL_END_SQ_1_TIMER ; $0191
.import LEVEL_END_LVL_ROUTINE_STATE ; $0193
.import LEVEL_END_PLAYERS_ALIVE ; $0194
.import SOLDIER_GEN_SCREEN ; $0195
.import SCREEN_GEN_SOLDIERS ; $0196
.import OAMDMA_CPU_BUFFER ; $0200
.import CPU_SPRITE_BUFFER ; $0300
.import PLAYER_SPRITES ; $0300
.import ENEMY_SPRITES ; $030a
.import SPRITE_Y_POS ; $031a
.import ENEMY_Y_POS ; $0324
.import SPRITE_X_POS ; $0334
.import ENEMY_X_POS ; $033e
.import SPRITE_ATTR ; $034e
.import ENEMY_SPRITE_ATTR ; $0358
.import PLAYER_BULLET_SPRITE_CODE ; $0368
.import PLAYER_BULLET_SPRITE_ATTR ; $0378
.import PLAYER_BULLET_SLOT ; $0388
.import PLAYER_BULLET_Y_VEL_ACCUM ; $0398
.import PLAYER_BULLET_X_VEL_ACCUM ; $03a8
.import PLAYER_BULLET_Y_POS ; $03b8
.import PLAYER_BULLET_X_POS ; $03c8
.import PLAYER_BULLET_Y_VEL_FRACT ; $03d8
.import PLAYER_BULLET_X_VEL_FRACT ; $03e8
.import PLAYER_BULLET_Y_VEL_FAST ; $03f8
.import PLAYER_BULLET_X_VEL_FAST ; $0408
.import PLAYER_BULLET_TIMER ; $0418
.import PLAYER_BULLET_AIM_DIR ; $0428
.import PLAYER_BULLET_ROUTINE ; $0438
.import PLAYER_BULLET_OWNER ; $0448
.import PLAYER_BULLET_F_RAPID ; $0458
.import PLAYER_BULLET_S_INDOOR_ADJ ; $0458
.import PLAYER_BULLET_DIST ; $0468
.import PLAYER_BULLET_S_ADJ_ACCUM ; $0468
.import PLAYER_BULLET_FS_X ; $0478
.import PLAYER_BULLET_F_Y ; $0488
.import PLAYER_BULLET_S_RAPID ; $0488
.import PLAYER_BULLET_VEL_FS_X_ACCUM ; $0498
.import PLAYER_BULLET_VEL_F_Y_ACCUM ; $04a8
.import PLAYER_BULLET_S_BULLET_NUM ; $04a8
.import ENEMY_ROUTINE ; $04b8
.import ENEMY_Y_VEL_ACCUM ; $04c8
.import ENEMY_X_VEL_ACCUM ; $04d8
.import ENEMY_Y_VELOCITY_FAST ; $04e8
.import ENEMY_Y_VELOCITY_FRACT ; $04f8
.import ENEMY_X_VELOCITY_FAST ; $0508
.import ENEMY_X_VELOCITY_FRACT ; $0518
.import ENEMY_TYPE ; $0528
.import ENEMY_ANIMATION_DELAY ; $0538
.import ENEMY_VAR_A ; $0548
.import ENEMY_ATTACK_DELAY ; $0558
.import ENEMY_VAR_B ; $0558
.import ENEMY_FRAME ; $0568
.import ENEMY_HP ; $0578
.import ENEMY_SCORE_COLLISION ; $0588
.import ENEMY_STATE_WIDTH ; $0598
.import ENEMY_ATTRIBUTES ; $05a8
.import ENEMY_VAR_1 ; $05b8
.import ENEMY_VAR_2 ; $05c8
.import ENEMY_VAR_3 ; $05d8
.import ENEMY_VAR_4 ; $05e8
.import LEVEL_SCREEN_SUPERTILES ; $0600
.import BG_COLLISION_DATA ; $0680
.import CPU_GRAPHICS_BUFFER ; $0700
.import PALETTE_CPU_BUFFER ; $07c0
.import HIGH_SCORE_LOW ; $07e0
.import HIGH_SCORE_HIGH ; $07e1
.import PLAYER_1_SCORE_LOW ; $07e2
.import PLAYER_1_SCORE_HIGH ; $07e3
.import PLAYER_2_SCORE_LOW ; $07e4
.import PLAYER_2_SCORE_HIGH ; $07e5
.import PREVIOUS_ROM_BANK ; $07ec
.import PREVIOUS_ROM_BANK_1 ; $07ed
BANK_NUMBER = $8000
GAME_ROUTINE_INDEX = $18 ; which part of the game routine to execute (see game_routine_pointer_table)
GAME_END_ROUTINE_INDEX = $19 ; used after beating the game to know which part of the ending sequence to execute for sequencing the animations, credits, restart, etc. (see game_end_routine_tbl)
GAME_ROUTINE_INIT_FLAG = $19 ; (same address as above) used to determine if the current game_routine has been initialized, used in game_routine_02 and game_routine_03
FRAME_COUNTER = $1a ; the frame counter loops from #$00 to #$ff increments once per frame. Also known as the global timer
NMI_CHECK = $1b ; set to #$01 at start of nmi and #$00 at end
; used to track if nmi occurred during game loop
; bit 7 is set when inside play_sound, i.e. init_sound_code_vars
DEMO_MODE = $1c ; #$00 not in demo mode, #$01 demo mode on
PLAYER_MODE_1D = $1d ; #$01 for 1 player, #$07 for 2 player. Not sure why developer just didn't use PLAYER_MODE instead
DEMO_LEVEL_END_FLAG = $1f ; whether or not demo for the level is complete and new demo level should play
PPU_READY = $20 ; #$00 when at least 5 executions of nmi_start have happened since last configure_PPU call
GRAPHICS_BUFFER_OFFSET = $21 ; current write offset into CPU_GRAPHICS_BUFFER (CPU_GRAPHICS_BUFFER contains pattern table tiles that are written to PPU)
PLAYER_MODE = $22 ; #$00 = single player, #$01 = 2 player
GRAPHICS_BUFFER_MODE = $23 ; defines the format of the CPU_GRAPHICS_BUFFER. #$ff is for super-tile data, #$00 is for text strings and palette data
KONAMI_CODE_STATUS = $24 ; #$00 not entered, #$01 entered, (30 lives code)
PAUSE_STATE = $25 ; #$00 when not paused, #$01 when paused
DEMO_LEVEL = $27 ; the current level when in DEMO mode
; only ever 0, 1 or 2 as those are the only levels demoed
INTRO_THEME_DELAY = $28 ; timer to prevent starting a level until the intro theme is complete (including explosion sound).
; initialized to #a4, decrements every other frame for ~5 seconds for NTSC
GAME_OVER_DELAY_TIMER = $29 ; goes from #$60 to #$00, timer after dying before showing score
DELAY_TIME_LOW_BYTE = $2a ; the low byte of the delay
DELAY_TIME_HIGH_BYTE = $2b ; the high byte of the delay
LEVEL_ROUTINE_INDEX = $2c ; the index into level_routine_ptr_tbl of the routine to run
END_LEVEL_ROUTINE_INDEX = $2d ; offset into either end_level_sequence_ptr_tbl or end_game_sequence_ptr_tbl
DEMO_FIRE_DELAY_TIMER = $2e ; the number of frames to delay before starting to fire when demoing
PLAYER_WEAPON_STRENGTH = $2f ; the damage strength of the player's current weapon (see weapon_strength) based on bits 0-2 of P1_CURRENT_WEAPON,x
; Default = #$00, M = #$02, F = #$01, S = #$03, L = #$02
CURRENT_LEVEL = $30 ; #$00-#$09, #$00 to #$07 represent levels 1 through 8. #$9 is interpreted as game over sequence
GAME_COMPLETION_COUNT = $31 ; the number of times the game has been completed (final boss defeated)
P1_NUM_LIVES = $32 ; P1 number of lives, #$00 is last life, on game over stays #$00, but P1_GAME_OVER_STATUS becomes #$01
P2_NUM_LIVES = $33 ; P2 number of lives, #$00 is last life, on game over stays #$00, but P2_GAME_OVER_STATUS becomes #$01
RANDOM_NUM = $34 ; random number increased in forever_loop
NUM_PALETTES_TO_LOAD = $36 ; the number of palettes to load into CPU memory
INDOOR_SCREEN_CLEARED = $37 ; whether indoor screen has had all cores destroyed (0 = not cleared, 1 = cleared, #$80 = cleared and fence removed)
P1_GAME_OVER_STATUS = $38 ; #$00 not game over, #$01 game over
P2_GAME_OVER_STATUS = $39 ; #$00 not game over, #$01 game over or player 2 not playing (1 player game)
NUM_CONTINUES = $3a ; the number of continues remaining
BOSS_DEFEATED_FLAG = $3b ; whether or not the level boss has been defeated (0 = no, 1 = yes)
; after set to 1, end level sequence logic uses this value as well using values #$81 and #$02
EXTRA_LIFE_SCORE_LOW = $3c ; the low byte of the score required for the next extra life
EXTRA_LIFE_SCORE_HIGH = $3d ; the high byte of the score required for the next extra life
; $3e is the EXTRA_LIFE_SCORE_LOW for player 2
KONAMI_CODE_NUM_CORRECT = $3f ; the number of successful inputs of the Konami code sequence #$0a for all correct
; also used as player 2's EXTRA_LIFE_SCORE_HIGH byte during game play
; level header data
LEVEL_LOCATION_TYPE = $40 ; current level type #$00 outdoor, #$01 indoor (base level); #$80 on indoor/base boss screen and indoor/base levels when players advancing to next screen
LEVEL_SCROLLING_TYPE = $41 ; current level scrolling type #$00 horizontal (and indoor/base level) #$01 vertical
LEVEL_SCREEN_SUPERTILES_PTR = $42 ; $42,$43 stores 2-byte address to bank 2 containing which super-tiles to use for each screen of the level (level_x_supertiles_screen_ptr_table)
LEVEL_SUPERTILE_DATA_PTR = $44 ; current level 2-byte pointer to super-tile data, which defines pattern table tiles of the super-tiles that are used to make level blocks
LEVEL_SUPERTILE_PALETTE_DATA = $46 ; current level 2-byte pointer address to the palettes used for each super-tile, each byte describes the 4 palettes for a single super-tile
LEVEL_ALT_GRAPHICS_POS = $48 ; how far into level (in number of screens) before loading alternate graphic data
COLLISION_CODE_1_TILE_INDEX = $49 ; pattern table tiles below this tile index (but not #$00) are considered Collision Code 1 (floor)
COLLISION_CODE_0_TILE_INDEX = $4a ; pattern table tiles >= $49 and less than this tile index are considered Collision Code 0 (empty)
COLLISION_CODE_2_TILE_INDEX = $4b ; pattern table tiles >= $4a and less than this tile index are considered Collision Code 2 (water)
LEVEL_PALETTE_CYCLE_INDEXES = $4c ; palette indexes into game_palettes to cycle through for the level for the 4th nametable palette index [$4c-4f]
LEVEL_PALETTE_INDEX = $50 ; the level's initial background palettes [$50 to $54) and sprite palettes [$54 to $58). Offsets into game_palettes table
LEVEL_STOP_SCROLL = $58 ; the screen of the level to stop scrolling, set to #$ff when boss auto scroll starts
LEVEL_SOLID_BG_COLLISION_CHECK = $59 ; used to determine whether to check for bullet and weapon item solid bg collisions
; 1. When non-zero, specifies weapon item should check for solid bg collisions (weapon_item_check_bg_collision)
; 2. When negative, used to let bullet (player and enemy) collision detection code to know to look for bullet-solid background collisions
; This is for levels 6 - energy zone and 7 - hangar. (see check_bullet_solid_bg_collision and enemy_bullet_routine_01)
DEMO_INPUT_NUM_FRAMES = $5a ; used to determine how many even-numbered frames to continue pressing the button specified in $5c for demo
; $5b the DEMO_INPUT_NUM_FRAMES for player 2
DEMO_INPUT_VAL = $5c ; the current controller input pressed during a demo
; $5d is DEMO_INPUT_VAL for player 2
DEMO_INPUT_TBL_INDEX = $5e ; when in demo, this stores the offset into specific demo_input_tbl_lX_pX table
; $5f is for player 2
PPU_WRITE_TILE_OFFSET = $60 ; the current write offset of the super-tile data, number of tiles outside the current view
; horizontal levels loops #$00 to #$1f, vert starts with #$1d goes down to #$00 before looping
LEVEL_TRANSITION_TIMER = $61 ; used in vertical levels to time animation between sections for every 'up' input
; used in indoor levels between screens to animate moving forward
PPU_WRITE_ADDRESS_LOW_BYTE = $62 ; used to populate the PPU write address in the CPU_GRAPHICS_BUFFER
PPU_WRITE_ADDRESS_HIGH_BYTE = $63 ; used to populate the PPU write address in the CPU_GRAPHICS_BUFFER
LEVEL_SCREEN_NUMBER = $64 ; the screen number of the current level (how many screens into the level)
LEVEL_SCREEN_SCROLL_OFFSET = $65 ; the number of pixels into LEVEL_SCREEN_NUMBER the level has scrolled. Goes from #$00-#$ff for each screen (256 pixels)
; for horizontal levels, this is how many pixels scrolled to the right
; for vertical levels, this is how many pixels up scrolled, note this value is equal to #$f0 - VERTICAL_SCROLL
; for indoor levels, after defeating a wall, increases from #$00 to #03
ATTRIBUTE_TBL_WRITE_LOW_BYTE = $66 ; the low byte of the attribute table write address to write to (always #$c0, never read)
ATTRIBUTE_TBL_WRITE_HIGH_BYTE = $67 ; the high byte of the attribute table write address to write to
FRAME_SCROLL = $68 ; how much to scroll the screen this frame based on player velocity (usually #$00 or #$01), for vertical levels, up to #$04
; note that this is not the scroll distance within the screen
SUPERTILE_NAMETABLE_OFFSET = $69 ; base nametable offset into memory address into CPU graphics buffer starting at $0600 (LEVEL_SCREEN_SUPERTILES)
; always either #$00 (nametable 0) or #$40 (nametable 1), points to area that contains the super-tile indexes for screen
SPRITE_LOAD_TYPE = $6a ; which sprites to load #$0 for normal sprites, #$1 for HUD sprites
CONT_END_SELECTION = $6b ; #$00 when "CONTINUE" is selected, #$01 when "END" is selected, used only in game over screen (level_routine_06)
ALT_GRAPHIC_DATA_LOADING_FLAG = $71 ; #$00 means that the alternate graphics data should not be loaded, #$01 means it should be #$02 means it currently is being loaded
LEVEL_PALETTE_CYCLE = $72 ; the current iteration of the palette animation loop #$00 up to entry for level in lvl_palette_animation_count
INDOOR_SCROLL = $73 ; scrolling on indoor level changes (#$00 = not scrolling; #$01 = scrolling, #$02 = finished scrolling)
BG_PALETTE_ADJ_TIMER = $74 ; timer used for adjusting background palette colors (not sprite palettes). Used for fade-in effect of dragon and boss ufo as well as indoor transitions
AUTO_SCROLL_TIMER_00 = $75 ; used when completing scroll to show a boss, e.g. vertical level dragon screen
AUTO_SCROLL_TIMER_01 = $76 ; used when completing scroll to show a boss, e.g. alien guardian
TANK_AUTO_SCROLL = $77 ; amount to scroll every frame, regardless of AUTO_SCROLL_TIMER_xx, used for snow field tanks (dogras), breaks levels if used on other levels
PAUSE_PALETTE_CYCLE = $78 ; #$00 - nametable palettes #$03 and #$04 will cycle through colors like normal. Non-zero values will pause palette color cycling (ice field tank pauses palette cycle)
SOLDIER_GENERATION_ROUTINE = $79 ; which routine is currently in use for generating soldiers (index into soldier_generation_ptr_tbl)
SOLDIER_GENERATION_TIMER = $7a ; a timer between soldier generation. #$00 means no generation. see level_soldier_generation_timer. When used in a level, every frame decrements by 2 (unless scrolling, then only by 1)
SOLDIER_GENERATION_X_POS = $7b ; the initial x position of the generated soldier
SOLDIER_GENERATION_Y_POS = $7c ; the initial y position of the generated soldier
FALCON_FLASH_TIMER = $7d ; the number of frames to flash the screen for falcon weapon item
TANK_ICE_JOINT_SCROLL_FLAG = $7f ; whether or not to have the ice joint enemy move left while player walks right to simulate being on the background
ENEMY_LEVEL_ROUTINES = $80 ; two byte address to the correct enemy_routine_level_XX for the current level, used to retrieve enemy routines for the level-specific enemies
ENEMY_SCREEN_READ_OFFSET = $82 ; read offset into level_xx_enemy_screen_xx table, which specifies the enemies on each screen of a level
ENEMY_CURRENT_SLOT = $83 ; when in use, specifies the current enemy slot that is being executed, used to be able to restore x register after method has used it
BOSS_AUTO_SCROLL_COMPLETE = $84 ; set when boss reveal auto-scrolling has completed, see AUTO_SCROLL_TIMER_00 and AUTO_SCROLL_TIMER_01
BOSS_SCREEN_ENEMIES_DESTROYED = $85 ; used on level 3 and level 7 boss screens to keep track of how many dragon arm orbs or mortar launchers have been destroyed respectively
WALL_CORE_REMAINING = $86 ; remaining wall cores/wall platings to destroy until can advance to next screen. For level 4 boss, used to count remaining boss gemini
WALL_PLATING_DESTROYED_COUNT = $87 ; used in indoor/base boss levels to keep track of how many wall platings (ENEMY_TYPE #$0a) have been destroyed
INDOOR_ENEMY_ATTACK_COUNT = $88 ; used in indoor/base levels to specify how many 'rounds' of attack have happened per screen, max #$07 before certain enemies no longer generate
; indoor soldiers, jumping soldiers, indoor rollers, and wall core check this value
INDOOR_RED_SOLDIER_CREATED = $89 ; used in indoor/base levels to indicate if a red jumping soldier has been created, to prevent creation of another
GRENADE_LAUNCHER_FLAG = $8a ; used in indoor/base levels to indicate that a grenade launcher enemy (ENEMY_TYPE #$17) is on the screen. Prevents other indoor enemies from being generated
ALIEN_FETUS_AIM_TIMER_INDEX = $8b ; used to keep track of the index into alien_fetus_aim_timer_tbl to set the delay between re-aiming towards the player
ENEMY_ATTACK_FLAG = $8e ; whether or not enemies will fire at player, also whether or not random enemies are generated, bosses ignore this value
PLAYER_STATE = $90 ; #$00 falling into level (only run once to init fall), #$01 normal state, #$02 when dead, #$03 can't move
; $91 is for p2, if p2 not playing, set to #$00
INDOOR_TRANSITION_X_ACCUM = $92 ; a variable to store INDOOR_TRANSITION_X_FRACT_VEL being added to itself to account for overflow before adding to player x velocity when moving between screens on indoor/base levels
; $93 is for p2
PLAYER_JUMP_COEFFICIENT = $94 ; related to jump height (used by speed runners to jump higher) (https://www.youtube.com/watch?v=K7MjxHvWof8 and https://www.youtube.com/watch?v=yrnW9yQXa9I)
; used to keep track of fractional y velocity on vertical levels for overflowing fractional velocity. It isn't cleared between jumps
; also used when walking into screen for indoor screen changes to keep track of overflow of animation y fractional velocity
; $95 is for player 2
INDOOR_TRANSITION_X_FRACT_VEL = $96 ; indoor animation transition when walking into screen x fractional velocity
; $97 is for player 2
PLAYER_X_VELOCITY = $98 ; the player's fast x velocity (#$00, #$01, or #$ff)
; $99 is for p2
INDOOR_TRANSITION_Y_FRACT_VEL = $9a ; indoor animation transition when walking into screen y fractional velocity
; $9b is for player 2
INDOOR_TRANSITION_Y_FAST_VEL = $9c ; indoor animation transition when walking into screen y fast velocity
; $9d is for player 2
PLAYER_ANIM_FRAME_TIMER = $9e ; value that is incremented every frame when player is walking, used to wait #$08 frames before incrementing PLAYER_ANIMATION_FRAME_INDEX for animating player walking
; $9f is for player 2
PLAYER_JUMP_STATUS = $a0 ; the status of the player jump (facing direction); similar to EDGE_FALL_CODE
; high nibble is for facing direction
; bit 7 - set when jumping left
; low nibble is #$01 when jumping, #$00 when not
; $a1 is for player 2
PLAYER_FRAME_SCROLL = $a2 ; how much player 1 is causing the frame to scroll by, see FRAME_SCROLL
; $a3 is for player 2, larger of the 2 is set to FRAME_SCROLL
EDGE_FALL_CODE = $a4 ; similar to PLAYER_JUMP_STATUS. Used to initiate gravity pulling player down
; if bit 7 set, then falling through platform
; if bit 6 is set, then walking left off edge
; if bit 5 is set, then walking right off ledge
; can change if change direction during fall, bit 0 always set when EDGE_FALL_CODE non-zero
; $a5 is for player 2
PLAYER_ANIMATION_FRAME_INDEX = $a6 ; which frame of the player animation. Depends on player state. For example, if player is running, this cycles from #$00 to #$05
; $a7 is for player 2
PLAYER_INDOOR_ANIM_Y = $a8 ; the y position the player was at when they started walking into screen after clearing an indoor level. I believe it's always #$a8 since y pos is hard-coded for indoor levels
; $a9 is player 2
P1_CURRENT_WEAPON = $aa ; low nibble is what weapon P1 has, high nibble 1 is rapid fire flag, commonly abbreviated MFSL
; #$00 - Regular, #$01 - Machine Gun, #$02 - Flame Thrower, #$03 - Spray, #$04 - Laser, bit 4 set for rapid fire
P2_CURRENT_WEAPON = $ab ; byte 0 is what weapon P2 has, byte 1 is rapid fire flag
PLAYER_M_WEAPON_FIRE_TIME = $ac ; used when holding down the B button with the m weapon. High nibble is number of bullets generated (up to #$06), low nibble is counter before next bullet is generated (up to #$07)
; $ad is for player 2
NEW_LIFE_INVINCIBILITY_TIMER = $ae ; timer for invincibility after dying
; $af is for player 2
INVINCIBILITY_TIMER = $b0 ; timer for player invincibility (b (barrier) weapon) (decreases every 8 frames), usually set to #$80 except level 7 when set to #$90
; $b1 is for player 2
PLAYER_WATER_STATE = $b2 ; bit 1 - horizontal sprite flip flag
; bit 2 - set when player in water, or exiting water
; bit 3 - player is walking out of water
; bit 4 - finished initialization for entering water
; bit 7 - player is walking out of water
; $b3 is for player 2
PLAYER_DEATH_FLAG = $b4 ; bit 0 specifies whether player has died, bit 1 specifies player was facing left when hit, used so player dies lying in appropriate direction
; $b5 is for player 2
PLAYER_ON_ENEMY = $b6 ; whether or not the player is on top of another enemy (#$14 - mining cart, #$15 - stationary mining cart, #$10 - floating rock platform)
; $b7 is for player 2
PLAYER_FALL_X_FREEZE = $b8 ; used to prevent changing X velocity shortly after walking off/falling through ledge, set to Y post of ledge + #$14
; $b9 is for player 2
PLAYER_HIDDEN = $ba ; #$00 player visible, #$01/#$ff player invisible (any non-zero). I believe it is meant to track distance off screen the player is
; $bb is for player 2
PLAYER_SPRITE_SEQUENCE = $bc ; which animation to show for the player
; outdoor - #$00 standing (no animation), #$01 gun pointing up, #$02 crouching, #$03 walking or curled jump animation, #$04 dead animation
; indoor - (see indoor_player_sprite_tbl), #$00 standing facing back wall, #$01 electrocuted, #$02 crouching, #$03 walking left/right animation, #$05 walking into screen (advancing), #$06 dead animation
; $bd is for player 2
PLAYER_INDOOR_ANIM_X = $be ; the x position the player was at when they started walking into screen after clearing an indoor level
; $bf is player 2
PLAYER_AIM_PREV_FRAME = $c0 ; backup of PLAYER_AIM_DIR
; $c1 is for player 2
PLAYER_AIM_DIR = $c2 ; which direction the player is aiming [#$00-#$0a] depends on level and jump status (00 up facing right, 1 up-right, 2 right, 3 right-down, 4 crouching facing right, 5 crouching facing left, etc)
; there are #$02 up and #$02 down values depending on facing direction
; $c3 is for player 2
PLAYER_Y_FRACT_VELOCITY = $c4 ; the fractional portion of the player's y velocity
; $c5 is for player 2
PLAYER_Y_FAST_VELOCITY = $c6 ; the integer portion of the player's y velocity. Positive pulls down, negative pulls up
; $c7 is for player 2
ELECTROCUTED_TIMER = $c8 ; timer for player being electrocuted, used to freeze player and modify look after touching electricity
; $c9 is for player 2
INDOOR_PLAYER_JUMP_FLAG = $ca ; used when entering new screen to tell the engine to cause the player to jump
; $cb is player 2
PLAYER_WATER_TIMER = $cc ; timer used for getting into and out of water
; $cd is for player 2
PLAYER_RECOIL_TIMER = $ce ; how many frames to be pushed back/down from recoil
; $cf is for player 2
INDOOR_PLAYER_ADV_FLAG = $d0 ; whether or not the player is walking into screen when advancing between screens on indoor levels, used for animating player
; $d1 is for player 2
PLAYER_SPECIAL_SPRITE_TIMER = $d2 ; used to track animation for player death animation
; outdoor is a timer that increments once player hit, every #$08 frames updates to next animation frame until #$04
; also used to track jumping curl animation (loops from #$00-#$04)
; $d3 is for player 2
PLAYER_FAST_X_VEL_BOOST = $d4 ; the x fast velocity boost from landing on a non-dangerous enemy, e.g. moving cart or floating rock in vertical level
; $d5 is for player 2
PLAYER_SPRITE_CODE = $d6 ; sprite code of the player
; $d7 is for player 2
PLAYER_SPRITE_FLIP = $d8 ; stores player sprite horizontal (bit 6) and vertical (bit 7) flip flags before saving into SPRITE_ATTR, other bits are used
; bit 3 specifies whether the PLAYER_ANIMATION_FRAME_INDEX is even or odd (see @check_anim_frame_and_collision)
; $d9 is for player 2
PLAYER_BG_FLAG_EDGE_DETECT = $da ; bit 7 specifies the player's sprite attribute for background priority, allows player to walk behind opaque background (OAM byte 2 bit 5)
; 0 (clear) sprite in foreground, 1 (set) sprite is background
; bit 0 allows the player to keep walking horizontally off a ledge without falling
; $db is for player 2
PLAYER_GAME_OVER_BIT_FIELD = $df ; combination of both players game over status
; #$00 = p1 not game over, p2 game over (or not playing), #$01 = p1 game over, p2 not game over, #$02 = p1 nor p2 are in game over
SOUND_TABLE_PTR = $ec ; low byte of address pointing of index into sound_table_00 offset INIT_SOUND_CODE
CONTROLLER_STATE = $f1 ; stores the currently-pressed buttons for player 1
; bit 7 - A, bit 6 - B, bit 5 - select, bit 4 - start
; bit 3 - up, bit 2 - down, bit 1 - left, bit 0 - right
; $f2 stores the currently-pressed buttons for player 2
CONTROLLER_STATE_DIFF = $f5 ; stores the difference between the controller input between reads. Useful for events that should only trigger on first button press
; $f6 is for player 2
CTRL_KNOWN_GOOD = $f9 ; used in input-reading code to know the last known valid read of controller input (similar to CONTROLLER_STATE)
; $fa is for player 2
VERTICAL_SCROLL = $fc ; the number of pixels to vertically scroll down
; (y component of PPUSCROLL) (see level_vert_scroll_and_song for initial values)
; horizontal levels are always #$e0 (224 pixels or 28 tiles down), indoor/base are always #$e8 (232 or 29 tiles down)
; waterfall level (vertical level) starts at #$00 and decrements as players move up screen (wrapping)
HORIZONTAL_SCROLL = $fd ; the horizontal scroll component of the PPUSCROLL, [#$00 - #$ff]
PPUMASK_SETTINGS = $fe ; used to store value of PPUMASK before writing to PPU
PPUCTRL_SETTINGS = $ff ; used to set PPUCTRL value for next frame
SOUND_CMD_LENGTH = $0100 ; how many video frames the sound count should last for, i.e. the time to wait before reading next sound command
; #$06 bytes, one for each sound slot
SOUND_CODE = $0106 ; the sound code for the sound slot, #$06 slots
SOUND_PULSE_LENGTH = $010c ; APU_PULSE_LENGTH, #$06 slots
SOUND_CMD_LOW_ADDR = $0112 ; low byte of address to current sound command in sound_xx data. #$06 slots, one per sound slot
SOUND_CMD_HIGH_ADDR = $0118 ; high byte of address to current sound command in sound_xx data. #$06 slots, one per sound slot
SOUND_VOL_ENV = $011e ; either an offset into pulse_volume_ptr_tbl (c.f. LVL_PULSE_VOL_INDEX) which specifies the volume for the frame
; or a specific volume to use. When bit 7 is set, then the volume will auto decrescendo
SOUND_CURRENT_SLOT = $0120 ; the current sound slot [#$00-#$05]
PERCUSSION_INDEX_BACKUP = $0121 ; backup location for percussion_tbl index to restore after call to play_sound
INIT_SOUND_CODE = $0122 ; the sound code to load; sound codes greater than #$5a are dmc sounds
SOUND_CHNL_REG_OFFSET = $0123 ; sound channel configuration register offset, i.e. #$00 for first pulse channel, #$04 for second, #$08 for triangle, #$0c for noise
SOUND_FLAGS = $0124 ; sound channel flags
; bit 0 - 0 = sound_xx command byte >= #$30 (read_low_sound_cmd), 1 = sound_xx command byte 0 < #$30 (read_high_sound_cmd)
; bit 1 - 1 = DECRESCENDO_END_PAUSE has triggered and decrescendo can resume, 0 = keep volume constant
; bit 2 - 0 = use lvl_config_pulse to set volume for frame, 1 = automatic decrescendo logic (handling DECRESCENDO_END_PAUSE)
; bit 3 - used in sound_cmd_routine_03, signifies that a shared (child) sound command (sound_xx_part) is executing, specified by #$fd, or #$fe in sound command
; used to know, after finishing parsing a sound command, whether or not to done or should return to parent sound command
; bit 4 - slightly flatten note (see @flatten_note and @flip_flatten_note_adv)
; bit 5 - 1 = PULSE_VOL_DURATION has counted down and decrescendo should be paused until DECRESCENDO_END_PAUSE
; set to ignore SOUND_VOL_ENV negative check, i.e. override to decrescendo
; bit 6 - mute flag (1 = muted, 0 = not muted)
; bit 7 - sweep flag
LVL_PULSE_VOL_INDEX = $012a ; index into lvl_x_pulse_volume_xx to read
PULSE_VOL_DURATION = $012a ; the number of video frames to decrement the volume for, before stopping decrescendo and keeping final volume
PAUSE_STATE_01 = $012f ; whether or not the game is paused, used for sound logic
DECRESCENDO_END_PAUSE = $0130 ; number of video frames before end of sound command in which the decrescendo will resume
; $0131 is for pulse channel 2
SOUND_PITCH_ADJ = $0132 ; the amount added to the sound byte low nibble before loading the correct note_period_tbl values
UNKNOWN_SOUND_00 = $0136 ; amount to multiply to SOUND_CMD_LENGTH,x when calculating DECRESCENDO_END_PAUSE,x
UNKNOWN_SOUND_01 = $013c ; used to adjust volume amount when setting volume
SOUND_CFG_LOW = $0142 ; the value to merge with the high nibble before storing in apu channel config register
SOUND_TRIANGLE_CFG = $0144 ; in memory value for APU_TRIANGLE_CONFIG
SOUND_REPEAT_COUNT = $0148 ; used for #$fe sound commands to specify how many times to repeat a shared sound part, e.g. .byte $fe, $03, .addr sound_xx_part to loop 3 times. #$06 slots
SOUND_CFG_HIGH = $014e ; the value to merge with the volume when saving the pulse config
SOUND_LENGTH_MULTIPLIER = $0154 ; value used when determining how many video frames to wait before reading next sound command, #$06 bytes, one for each sound slot
; ultimately used when calculating SOUND_CMD_LENGTH, and kept around between sound commands so subsequent notes can be the same length
; for low sound codes, SOUND_LENGTH_MULTIPLIER is set to SOUND_CMD_LENGTH directly with no multiplication (see @high_nibble_not_1)
SOUND_PERIOD_ROTATE = $015a ; when not #$04, the number of times to shift the high byte of note_period_tbl into the low byte
PULSE_VOLUME = $0160 ; low nibble only, stores the volume for the pulse channels
NEW_SOUND_CODE_LOW_ADDR = $0166 ; sound command return location low byte once sound command specified in move_sound_code_read_addr executes, e.g. jungle boss siren
NEW_SOUND_CODE_HIGH_ADDR = $016c ; sound command return location high byte once sound command specified in move_sound_code_read_addr executes, e.g. jungle boss siren
SOUND_PULSE_PERIOD = $0172 ; APU_PULSE_PERIOD
VIBRATO_CTRL = $0178 ; vibrato control mode [#$00-#$03], #$80 = no vibrato
; even values cause the note to stay the same, odd values cause vibrato #$03 = pitch up, #$01 = pitch down
; $0178 is for sound slot #$00 and $0719 is for sound slot #$01
SOUND_VOL_TIMER = $017a ; sound command counter; increments up to VIBRATO_DELAY, at which vibrato will be checked
; only increments when VIBRATO_CTRL is non-negative, i.e. not #$80
PULSE_NOTE = $017c ; the note that is sustained or has the vibrato applied to for pulse channels (in Contra only ever sustained no vibrato)
; $017c is for sound slot #$00 and $071d is for sound slot #$01
VIBRATO_DELAY = $017e ; used to delay start of vibrato until SOUND_VOL_TIMER has counted up to this value
; if a note isn't as long as VIBRATO_DELAY, i.e. SOUND_CMD_LENGTH < VIBRATO_DELAY, then vibrato won't be considered for a note
; $017e is for sound slot #$00 and $071f is for sound slot #$01
VIBRATO_AMOUNT = $0180 ; the amount of vibrato to apply
LEVEL_END_DELAY_TIMER = $0190 ; a delay timer before beginning level end animation sequence
LEVEL_END_SQ_1_TIMER = $0191 ; a delay timer specifying the duration of end_level_sequence_01, decremented every other frame
LEVEL_END_LVL_ROUTINE_STATE = $0192 ; used by level end routines (end_of_lvl_routine_...) for managing animation state.
; for example, indoor level end animations have 4 states: walk to elevator, initialize elevator sprite, ride elevator
; $0193 is for player 2
LEVEL_END_PLAYERS_ALIVE = $0194 ; the number of players alive at the end of the level, used to know if should play level end music
SOLDIER_GEN_SCREEN = $0195 ; the current screen that soldiers are being generated for
SCREEN_GEN_SOLDIERS = $0196 ; the total number of soldiers that have been generated for the current screen (exe_soldier_generation)
OAMDMA_CPU_BUFFER = $0200 ; $0200-$02ff OAMDMA (sprite) read data, read once per frame, populated by load_sprite_to_CPU_mem, draw_hud_sprites, or draw_player_hud_sprites
CPU_SPRITE_BUFFER = $0300 ; sprites on screen, each byte is an entry into sprite_ptr_tbl [$0300-$0387], memory is segmented as defined below
PLAYER_SPRITES = $0300 ; player sprites, p1 and p2 sprite, then player bullets, each byte is an entry into sprite_ptr_tbl (#$0a bytes)
ENEMY_SPRITES = $030a ; enemy sprites to load on screen, each byte is an entry into sprite_ptr_tbl (#$0f bytes)
SPRITE_Y_POS = $031a ; y position on screen of each player sprite. First 2 bytes are for player sprites. Starts at #$00 for top increases downward (#$0a bytes)
ENEMY_Y_POS = $0324 ; y position on screen of each enemy sprite. Starts at #$00 for top increases downward (#$0f bytes)
SPRITE_X_POS = $0334 ; x position of screen of each player sprite. First 2 bytes are for player sprites (#$0a bytes)
ENEMY_X_POS = $033e ; x position on screen of each enemy sprite (#$0f bytes)
SPRITE_ATTR = $034e ; sprite attribute, specifies palette, vertical flip, horizontal flip (#$0a bytes)
; and whether to adjust y position
; bit 0 and 1 - sprite palette
; bit 2 - 0 to use default palette as specified in sprite code
; - 1 to use palette specified in bits 0 and 1
; bit 3 - whether to add #$01 to sprite y position, used for recoil effect firing weapon
; bit 5 - bg priority
; bit 6 - whether to flip the sprite horizontally
; bit 7 - whether to flip the sprite vertically
; bytes 0 and 1 are p1 and p2 sprite attributes, then each byte is the player bullet sprite attributes
; examples: player being electrocuted or invincible (flashes various colors)
ENEMY_SPRITE_ATTR = $0358 ; enemy sprite attribute. See specification above (#$0f bytes)
PLAYER_BULLET_SPRITE_CODE = $0368 ; The sprite codes to load for the bullet, eventually copied into CPU_SPRITE_BUFFER starting at offset 2
PLAYER_BULLET_SPRITE_ATTR = $0378 ; The sprite attributes for the bullet (see SPRITE_ATTR for specification)
; used for L bullets for flipping the angled sprites depending on direction
PLAYER_BULLET_SLOT = $0388 ; #$00 when no bullet, otherwise stores bullet type + 1, i.e. #$01 basic, #$02 M, #$03 F bullet, #$04 S, #$05 L, can be negative sometimes
PLAYER_BULLET_VEL_Y_ACCUM = $0398 ; an accumulator to keep track of PLAYER_BULLET_Y_VEL_FRACT being added to itself have elapsed before adding 1 to PLAYER_BULLET_Y_POS
PLAYER_BULLET_VEL_X_ACCUM = $03a8 ; an accumulator to keep track of PLAYER_BULLET_X_VEL_FRACT being added to itself have elapsed before adding 1 to PLAYER_BULLET_X_POS
PLAYER_BULLET_Y_POS = $03b8 ; the bullet's sprite y position
PLAYER_BULLET_X_POS = $03c8 ; the bullet's sprite x position
; for F bullets, PLAYER_BULLET_FS_X and PLAYER_BULLET_X_POS together determine x position
PLAYER_BULLET_Y_VEL_FRACT = $03d8 ; percentage out of 0-255 set number of frames until Y position is incremented by an additional 1 unit
PLAYER_BULLET_X_VEL_FRACT = $03e8 ; percentage out of 0-255 set number of frames until X position is incremented by an additional 1 unit
PLAYER_BULLET_Y_VEL_FAST = $03f8 ; player bullet velocity y integer portion
PLAYER_BULLET_VEL_X_FAST = $0408 ; player bullet velocity x integer portion
PLAYER_BULLET_TIMER = $0418 ; 'timer' starts at #$00. Used by F, S (indoor only) and L
; for indoor S, used to specify size of bullet
; For F, used to set x and y pos when traveling to create swirl (see f_bullet_outdoor_x_swirl_amt_tbl, and f_bullet_outdoor_y_swirl_amt_tbl)
; increments or decrements every frame depending on firing direction (left decrement, right increment)
; For L used to spread out 4 lasers for one shot
PLAYER_BULLET_AIM_DIR = $0428 ; the direction of the bullet #$00 for up facing right, incrementing clockwise up to #09 for up facing left
PLAYER_BULLET_ROUTINE = $0438 ; #$00, #$01, or #$03, offset into player_bullet_routine_XX_(indoor_)ptr_tbl
PLAYER_BULLET_OWNER = $0448 ; #$00 player 1 bullet, #$01 player 2 bullet, each byte is for a bullet
PLAYER_BULLET_F_RAPID = $0458 ; #$01 for player indoor bullets for F weapon when rapid fire is enabled
PLAYER_BULLET_S_INDOOR_ADJ = $0458 ; (same address as previous) for indoor S bullets, specifies whether to adjust PLAYER_BULLET_X_POS by an additional -1 (#$ff) every frame (see s_bullet_pos_mod_tbl)
PLAYER_BULLET_DIST = $0468 ; represents how far a bullet has traveled
; For S outdoor bullets, used to determine the size (scale) of the bullet
; For F on indoor levels, used to determine spiraling position based on distance from player
PLAYER_BULLET_S_ADJ_ACCUM = $0468 ; (same address as previous) for indoor S weapons, stores accumulated fractional velocity where overflow affects PLAYER_BULLET_S_INDOOR_ADJ (see update_s_bullet_indoor_pos)
PLAYER_BULLET_FS_X = $0478 ; Used to offset from general x direction of bullet for swirl effect in F bullet and spread effect in S bullet (indoor)
; Specifies center x position on screen f bullet swirls around. Used when firing f bullet either left, right, or at an angle
PLAYER_BULLET_F_Y = $0488 ; Specifies center y position on screen f bullet swirls around. Used when firing f bullet either up, down, or at an angle.
PLAYER_BULLET_S_RAPID = $0488 ; (same address as previous) for S weapon in indoor levels, specifies whether weapon is rapid fire or not, not sure why $09 wasn't used like other bullet routines
PLAYER_BULLET_VEL_FS_X_ACCUM = $0498 ; (for F weapon only) an accumulator to keep track of PLAYER_BULLET_X_VEL_FRACT being added to itself have elapsed before adding 1 to PLAYER_BULLET_X_POS
PLAYER_BULLET_VEL_F_Y_ACCUM = $04a8 ; (for F weapon only) an accumulator to keep track of PLAYER_BULLET_Y_VEL_FRACT being added to itself have elapsed before adding 1 to PLAYER_BULLET_Y_POS
PLAYER_BULLET_S_BULLET_NUM = $04a8 ; (same address as previous) for S weapon only, specifies the number the bullet in the current 'spray' for the shot
; per shot of S weapon, #$05 bullets are generated. If no other bullets exist then
; $04a8 would have #$00, $04a9 would have #$01, $04a9 would have #$02, etc.
; each enemy property is #$10 bytes, one byte per enemy
ENEMY_ROUTINE = $04b8 ; enemy routine indexes starting at offset #$f ($04c7) going to #$0 ($04b8)
; subtract 1 to get real routine, since all offsets are off by 1 (...routine_ptr_tbl -2)
; ex: for exploding bridge, setting ENEMY_ROUTINE to #$02 causes exploding_bridge_routine_01 to run the next frame
; the following 6 address ranges control the change in position of the enemy
; every frame the position is moved by VELOCITY_FAST units
; VELOCITY_FRACT can enable only moving by 1 unit every n frames
; for example, if ENEMY_Y_VELOCITY_FAST is #$00 and ENEMY_Y_VELOCITY_FRACT is #$c0, (#$c0/#$ff = 75%),
; then the enemy will move one position to the right 3 out of every 4 frames
ENEMY_Y_VEL_ACCUM = $04c8 ; an accumulator to keep track of ENEMY_Y_VELOCITY_FRACT being added to itself have elapsed before adding 1 to ENEMY_Y_POS
ENEMY_X_VEL_ACCUM = $04d8 ; an accumulator to keep track of ENEMY_X_VELOCITY_FRACT being added to itself have elapsed before adding 1 to ENEMY_X_POS
ENEMY_Y_VELOCITY_FAST = $04e8 ; the number of units to add to ENEMY_Y_POS every frame
ENEMY_Y_VELOCITY_FRACT = $04f8 ; percentage out of 0-255 of a unit to add, e.g. if #$80 (#$80/#$ff = 50%), then every other frame will cause Y pos to increment by 1
ENEMY_X_VELOCITY_FAST = $0508 ; the number of units to add to ENEMY_X_POS every frame
ENEMY_X_VELOCITY_FRACT = $0518 ; percentage out of 0-255 of a unit to add, e.g. if #$80 (#$80/#$ff = 50%), then every other frame will cause X pos to increment by 1
ENEMY_TYPE = $0528 ; enemy type, e.g. #$03 = flying capsule
ENEMY_ANIMATION_DELAY = $0538 ; used for various delays by enemy logic
ENEMY_VAR_A = $0548 ; the sound code to play when enemy hit by player bullet, also used for other logic
; dragon arm orb uses it for adjusting enemy position, fire beam uses it for animation delay
ENEMY_ATTACK_DELAY = $0558 ; the delay before an enemy attacks, for weapon items and grenades this is used for helping calculate falling arc trajectory instead of enemy delay
ENEMY_VAR_B = $0558 ; for weapon items and grenades this is used for helping calculate falling arc trajectory
ENEMY_FRAME = $0568 ; animation frame the enemy is in, typically indexes into an enemy type-specific table of sprite codes
ENEMY_SCORE_COLLISION = $0588 ; represents 3 things for an enemy
; SSSS CCCC - score code (see `score_codes_tbl`), and collision type (entry in collision_box_codes_XX)
; also explosion type
ENEMY_HP = $0578 ; the HP of the enemy
ENEMY_STATE_WIDTH = $0598 ; loaded from enemy_prop_ptr_tbl
; bit 7 set to allow bullets to travel through enemy, e.g. weapon item
; bit 6 specifies whether player can land on enemy (floating rock and moving cart), bit 4 also has to be 0 (see `beq @land_on_enemy`)
; bit 4 and 5 specify the collision box type (see collision_box_codes_tbl)
; bit 3 determines the explosion type (explosion_type_ptr_tbl), either explosion_type_00 or explosion_type_01
; bit 2 for bullets specifies whether to play sound on collision
; bit 1 specifies whether to play explosion noise; also specifies width of enemy
; bit 0 - #$00 test player-enemy collision, #$01 means to skip player-enemy collision test
ENEMY_ATTRIBUTES = $05a8 ; enemy type-specific attributes that define how an enemy behaves and/or looks
ENEMY_VAR_1 = $05b8 ; a byte available to each enemy for whatever they want to use it for (#$f bytes, 1 per enemy)
ENEMY_VAR_2 = $05c8 ; a byte available to each enemy for whatever they want to use it for (#$f bytes, 1 per enemy)
ENEMY_VAR_3 = $05d8 ; a byte available to each enemy for whatever they want to use it for (#$f bytes, 1 per enemy)
ENEMY_VAR_4 = $05e8 ; a byte available to each enemy for whatever they want to use it for (#$f bytes, 1 per enemy)
LEVEL_SCREEN_SUPERTILES = $0600 ; CPU memory address where super tiles indexes for the screens of the level are loaded (level_X_supertiles_screen_XX data)
; 2 screens are stored in the CPU buffer. The second screen loaded at $0640. Indexes are into level_x_supertile_data
; This data specifies the super-tiles (indexes) to load for the screens
BG_COLLISION_DATA = $0680 ; map of collision types for each of the super-tiles for both nametables, each 2 bits encode 1/4 of a super-tile's collision information
; first 8 nibbles are a row of the top of super-tile, the next 8 are the middle middle. Not used on base (indoor) levels
CPU_GRAPHICS_BUFFER = $0700 ; used to store data that will be then moved to the PPU later on. $700 to $750, repeating structure
; * byte $700 is multifaceted
; * if $700 is #$0, then done writing graphics buffer to PPU
; * if $700 is greater than #$0, then there is data to write, this byte is the offset into vram_address_increment
; * both #$01, and #$03 signify VRAM address increment to 0, meaning to add #$1 every write to PPU (write across)
; * #$02 signifies VRAM address increment is 1, meaning add #$20 (32 in decimal) every write to PPU (write down)
; if GRAPHICS_BUFFER_MODE is #$ff
; * byte $701 is length of the tiles being written per group
; * byte $702 is the number of $701-sized blocks to write to the PPU
; * for each block, the block prefixed with 2 bytes specifying PPU address (high byte, then low byte)
; if GRAPHICS_BUFFER_MODE is #$00
; * if byte #$00 is #$00, then no drawing takes place for frame
; * blocks of text/palette data prefixed with 2 bytes specifying PPU address (high byte, then low byte)
; the block of text is ended with a #$ff, if the byte after #$ff is the vram_address_increment offset
; then the the process continues, i.e. read #$02 PPU address bytes, read next text
PALETTE_CPU_BUFFER = $07c0 ; [$07c0-$07df] the CPU memory address of the palettes eventually loaded into the PPU $3f00 to $3f1f
HIGH_SCORE_LOW = $07e0 ; the low byte of the high score score
HIGH_SCORE_HIGH = $07e1 ; the high byte of the high score score
PLAYER_1_SCORE_LOW = $07e2 ; the low byte of player 1 high score
PLAYER_1_SCORE_HIGH = $07e3 ; the high byte of player 1 high score
PLAYER_2_SCORE_LOW = $07e4 ; the low byte of player 1 high score
PLAYER_2_SCORE_HIGH = $07e5 ; the high byte of player 1 high score
PREVIOUS_ROM_BANK = $07ec ; the previously-loaded PRG BANK ($8000-$bfff)
PREVIOUS_ROM_BANK_1 = $07ed ; the previously-loaded PRG BANK, but used only for load_bank_1 (from play_sound)
; PPU (picture processing unit)
PPUCTRL = $2000

View File

@ -1,4 +1,4 @@
; Contra US Disassembly - v1.2
; Contra US Disassembly - v1.3
; https://github.com/vermiceli/nes-contra-us
.segment "HEADER"

1714
src/ram.asm Normal file

File diff suppressed because it is too large Load Diff