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 Assembling PRG Rom Banks
@echo on @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\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\ines_header.o src\ines_header.asm
ca65 -D %GAME% --debug-info -o obj\bank0.o src\bank0.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 "Creating .nes ROM"
@echo on @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 @echo off

View File

@ -110,6 +110,7 @@ Write-Output "Assembling PRG Rom Banks"
# show commands run in output # show commands run in output
Set-PSDebug -Trace 1 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\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\ines_header.o src\ines_header.asm
ca65 -D $Game --debug-info -o obj\bank0.o src\bank0.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" Write-Output "Creating .nes ROM"
Set-PSDebug -Trace 1 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 # compare assembled ROM hash to expected hash if file exists
Set-PSDebug -Trace 0 Set-PSDebug -Trace 0

View File

@ -105,6 +105,7 @@ done < $ASSETS_NAME
echo "$GAME" > $ASSET_GAME_TYPE echo "$GAME" > $ASSET_GAME_TYPE
echo "Assembling PRG Rom Banks" 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/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/ines_header.o src/ines_header.asm
ca65 -D $GAME --debug-info -o obj/bank0.o src/bank0.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 ca65 -D $GAME --debug-info -o obj/bank7.o src/bank7.asm
echo "Creating .nes ROM" 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 if test -f $ROM_NAME
then then

View File

@ -1,5 +1,9 @@
# defines where the banks will be loaded into CPU address space # defines where the banks will be loaded into CPU address space
MEMORY { 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 # INES Cartridge Header, not loaded into CPU memory
HEADER: start = $0000, size = $0010, fill = yes; 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 # defines the order of the segments as they are stored in the .nes ROM file
SEGMENTS { SEGMENTS {
ZEROPAGE: load = ZEROPAGE, type = zp;
RAM: load = RAM, type = bss;
HEADER: load = HEADER, type = ro; HEADER: load = HEADER, type = ro;
OAMRAM: load = OAMRAM, type = bss, define=yes, optional=yes; OAMRAM: load = OAMRAM, type = bss, define=yes, optional=yes;
BANK_0: load = BANK_0, type = ro; 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) | | ![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: @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 No attributes exist for this enemy. His health is calculated based on player's
`PLAYER_WEAPON_STRENGTH` value. The formula is below `PLAYER_WEAPON_STRENGTH` value. The formula is below
#### Logic #### Logic
* `ENEMY_VAR_1` - random number used to control boss action: jump, attack, * `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 | | #$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 | | #$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 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 confuse players into thinking the game is paused. As for names, I can guess at
some of the abbreviations and name meanings. 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 ; https://github.com/vermiceli/nes-contra-us
; Bank 0 is used exclusively for enemy routines. Enemy routines are the logic ; Bank 0 is used exclusively for enemy routines. Enemy routines are the logic
; controlling enemy behaviors, AI, movements, and attack patterns. Almost every ; controlling enemy behaviors, AI, movements, and attack patterns. Almost every
@ -2800,7 +2800,7 @@ eye_projectile_routine_00:
lda #$06 ; a = #$06 (projectile speed) lda #$06 ; a = #$06 (projectile speed)
sta $06 sta $06
lda #$01 ; a = #$01 (quadrant_aim_dir_01) 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 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 ; 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) 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) 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 sta $06 ; store bullet direction velocity routine value (bullet_velocity_adjust_xx) in $06
lda #$01 ; a = #$01 (quadrant_aim_dir_01) 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 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 ; based on source position ($09, $08) targeting player index $0a
pha ; push quadrant aim dir to the stack 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 ; https://github.com/vermiceli/nes-contra-us
; Bank 1 is responsible for audio and sprites. The audio code takes up about ; 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 ; 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: @continue:
ora SOUND_CFG_HIGH,x ; merge with high nibble of pulse config value 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] 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 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 ; a is either (PULSE_VOLUME,x - UNKNOWN_SOUND_01) | SOUND_CFG_HIGH,x
; or #$00 | SOUND_CFG_HIGH,x ; or #$00 | SOUND_CFG_HIGH,x
@ -517,7 +517,7 @@ read_sound_command_00:
; output ; output
; * x - SOUND_CURRENT_SLOT [0-3] ; * x - SOUND_CURRENT_SLOT [0-3]
read_high_sound_cmd: 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) cmp #$03 ; compare to sound slot #$03 (noise/dmc channel)
beq parse_percussion_cmd ; branch if 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 lda ($e0),y ; not noise channel, load sound byte
@ -671,7 +671,7 @@ interpret_sound_byte:
@set_sweep_continue: @set_sweep_continue:
jsr ldx_pulse_triangle_reg ; set x to apu channel register [0, 1, 4, 5, 8, #$c] 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 sta APU_PULSE_SWEEP,x ; enable or disable sweep
@next_high_control_sound_byte: @next_high_control_sound_byte:
@ -712,7 +712,7 @@ interpret_sound_byte:
; set config register ($4000, $4004, or $400c) and period & length register ; set config register ($4000, $4004, or $400c) and period & length register
@set_cfg_period_length: @set_cfg_period_length:
jsr ldx_pulse_triangle_reg ; set x to apu channel register [0, 1, 4, 5, 8, #$c] 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 sta $4000,x ; set pulse, triangle, or noise channel configuration
@load_set_period_length: @load_set_period_length:
@ -853,7 +853,7 @@ set_note:
sta SOUND_PULSE_LENGTH,x ; set in memory copy of current pulse length sta SOUND_PULSE_LENGTH,x ; set in memory copy of current pulse length
ora #$08 ; set bit 0 of high timer to be 1 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] 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 sta APU_PULSE_LENGTH,x ; set duration and high 3 bits of the pulse, or triangle channel
; set low period ; set low period
@ -866,7 +866,7 @@ set_note:
@set_apu_period: @set_apu_period:
jsr ldx_pulse_triangle_reg ; set x to apu channel register [0, 1, 4, 5, 8, #$c] 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 ; 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 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) ; (except SOUND_FLAGS are loaded within read_sound_command_00)
; set sound channel configuration (mute), advance sound command address ; set sound channel configuration (mute), advance sound command address
; input
; * a - amount to multiply SOUND_CMD_LENGTH by ; * a - amount to multiply SOUND_CMD_LENGTH by
; * y - current sound_xx read offset
sound_cmd_routine_00: sound_cmd_routine_00:
jsr calc_cmd_delay ; multiply SOUND_CMD_LENGTH by a jsr calc_cmd_delay ; multiply SOUND_CMD_LENGTH by a
lda #$00 ; sound config low nibble = #$00 (mute sound channel) lda #$00 ; sound config low nibble = #$00 (mute sound channel)
@ -1017,7 +1019,7 @@ sound_cmd_routine_00:
@continue: @continue:
jsr ldx_pulse_triangle_reg ; set x to apu channel register [0, 1, 4, 5, 8, #$c] 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 sta $4000,x ; set pulse 1, pulse 2, or triangle configuration
@adv_read_addr: @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 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 ; set in memory configuration for channel, set multiplier, and sometimes read_high_sound_cmd
; input
; * a - low nibble of sound byte value ; * a - low nibble of sound byte value
; * y - current sound_xx read offset
sound_cmd_routine_01: sound_cmd_routine_01:
sta SOUND_LENGTH_MULTIPLIER,x ; store value used to calculate SOUND_CMD_LENGTH 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 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 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 and #$0f ; not triangle sound slot, get low nibble
sec sec
@ -1293,7 +1297,7 @@ sound_exit_00:
mute_channel: mute_channel:
lda #$30 ; a = #$30 (mute pulse channel register) 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] 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) sta $4000,x ; update pulse channel config (mute pulse channel 1 or 2 register)
@continue: @continue:
@ -1373,7 +1377,7 @@ sound_cmd_ptr_tbl:
; table for note period to use when writing notes to the APU (#$30 bytes) ; 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 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 ; note that triangle channel is one octave lower
; frequency = cpu_speed / (#$0f * (f_pulse + 1)) ; frequency = cpu_speed / (#$0f * (f_pulse + 1))
; ex: 1789773 / (#$0f * (#$06ae + 1)) => 65.38 Hz ; 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 ; https://github.com/vermiceli/nes-contra-us
; Bank 2 starts with RLE-encoded level data (graphic super tiles for the level ; 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 ; 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 ; https://github.com/vermiceli/nes-contra-us
; Bank 3 starts with the data that specifies which pattern table tiles comprises ; 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 ; 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 ; https://github.com/vermiceli/nes-contra-us
; Bank 4 mostly contains compressed graphic data. The rest of bank 4 is the code ; 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 ; 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 ; https://github.com/vermiceli/nes-contra-us
; Bank 5 mostly contains compressed graphic data. The rest of bank 5 is the ; 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. ; code and lookup tables for automated input for the 3 demo (attract) levels.
@ -86,15 +86,16 @@ graphic_data_17:
graphic_data_18: graphic_data_18:
.incbin "assets/graphic_data/graphic_data_18.bin" .incbin "assets/graphic_data/graphic_data_18.bin"
; run as part of showing the demo ; simulates player input for demo levels for both players
; DEMO_FIRE_DELAY_TIMER starts at 0 increments to #$ff and stops ; begins firing after #$e0 frames (see DEMO_FIRE_DELAY_TIMER)
load_demo_input_table: load_demo_input_table:
lda CONTROLLER_STATE_DIFF ; get player input lda CONTROLLER_STATE_DIFF ; get player input
and #$30 ; start and select button and #$30 ; start and select button
bne end_demo_level ; exit demo if player has pressed start or select 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) 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: @player_loop:
ldx #$01 ; initialize X to 1 (player loop starting at player 2) 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 ; for non M, nor L weapon, press b button every #$07 frames
@fire_weapon_input: @fire_weapon_input:
lda FRAME_COUNTER ; load frame counter 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 bne player_demo_input_chg_player ; move to next player without firing weapon
lda CONTROLLER_STATE_DIFF,x ; load current controller input lda CONTROLLER_STATE_DIFF,x ; load current controller input
ora #$40 ; press b button 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 ; * second byte is number of even-numbered frames to apply the input for
; while possible, player firing isn't specified in these input tables ; while possible, player firing isn't specified in these input tables
; instead, that is handled automatically as part of running the demo ; 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 ; $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) ; input table for level 1 player 1 for demo (#$5A bytes)
demo_input_tbl_l1_p1: 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 ; https://github.com/vermiceli/nes-contra-us
; Bank 6 contains compressed graphics data, data for short text sequences like ; 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' ; 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 ; ensure player in valid state to fire a bullet, e.g. not being electrocuted
check_player_fire: check_player_fire:
lda PLAYER_HIDDEN,x ; 0 - visible; #$01/#$ff = invisible (any non-zero) 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 bne check_player_fire_exit ; exit if being electrocuted or $ba,x is set
lda PLAYER_WATER_STATE,x ; see if player in water lda PLAYER_WATER_STATE,x ; see if player in water
beq @player_shoot_test beq @player_shoot_test
@ -862,7 +862,7 @@ set_bullet_velocity:
asl ; double twice to get correct offset asl ; double twice to get correct offset
tay tay
lda ($01),y ; load x velocity fast value 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 iny ; increment velocity table read offset
lda ($01),y ; load x fractional velocity value lda ($01),y ; load x fractional velocity value
sta PLAYER_BULLET_X_VEL_FRACT,x ; store 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: @set_x_vel:
jsr @set_vel_for_speed_code ; determine fast and fractional velocity based on a and whether rapid fire is enabled 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 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 lda $0e ; load resulting fractional velocity
sta PLAYER_BULLET_X_VEL_FRACT,x ; set indoor bullet fractional x velocity sta PLAYER_BULLET_X_VEL_FRACT,x ; set indoor bullet fractional x velocity
rts rts
@ -1118,7 +1118,7 @@ s_weapon_init_bullet_velocities:
lda ($04),y lda ($04),y
sta PLAYER_BULLET_Y_VEL_FAST,x sta PLAYER_BULLET_Y_VEL_FAST,x
lda ($06),y lda ($06),y
sta PLAYER_BULLET_VEL_X_FAST,x sta PLAYER_BULLET_X_VEL_FAST,x
rts rts
; table for player aim direction (#$c bytes) ; 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 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 sta PLAYER_BULLET_VEL_FS_X_ACCUM,x ; add accumulated value back
lda PLAYER_BULLET_FS_X,x 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 sta PLAYER_BULLET_FS_X,x
rts rts
@ -1660,19 +1660,19 @@ update_player_bullet_pos:
jsr check_bullet_solid_bg_collision ; if specified, check for bullet collision with solid background 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 ; and if so move bullet routine to player_bullet_collision_routine
bmi bullet_logic_exit ; exit if bullet collided with solid object 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 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 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 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 sta PLAYER_BULLET_X_POS,x ; set new X position
update_player_bullet_y_pos: update_player_bullet_y_pos:
clc ; clear carry in preparation for addition 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 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 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 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 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_F_RAPID,x
sta PLAYER_BULLET_DIST,x sta PLAYER_BULLET_DIST,x
sta PLAYER_BULLET_AIM_DIR,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_X_VEL_FRACT,x
sta PLAYER_BULLET_Y_VEL_FAST,x sta PLAYER_BULLET_Y_VEL_FAST,x
sta PLAYER_BULLET_Y_VEL_FRACT,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 lda PLAYER_BULLET_VEL_FS_X_ACCUM,x ; ignore, no affect
clc ; ignore, no affect clc ; ignore, no affect
adc PLAYER_BULLET_S_ADJ_ACCUM,x ; 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 lda PLAYER_BULLET_FS_X,x ; load center x position on screen f bullet swirls around
clc ; clear carry in preparation for addition clc ; clear carry in preparation for addition
adc PLAYER_BULLET_S_INDOOR_ADJ,x ; add the indoor adjustment 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 ; https://github.com/vermiceli/nes-contra-us
; Bank 7 is the core of the game's programming. Reset, NMI, and IRQ vectors are ; 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 ; 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 sta $0e ; set $0e to #$00
ldy #$07 ; set number of bits to rotate speed code to #$07 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 ; 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 ; negate final results if $12 is greater than or equal to #$00
; also used directly for indoor bullets ; also used directly for indoor bullets
@ -9734,7 +9733,7 @@ aim_and_create_enemy_bullet:
sty $06 ; store bullet speed code in $06 sty $06 ; store bullet speed code in $06
sta $00 ; store bullet type temporarily sta $00 ; store bullet type temporarily
lda #$01 ; a = #$01, use quadrant_aim_dir_01 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 lda $0a ; load player y position
bpl @continue ; branch if >= #$00 bpl @continue ; branch if >= #$00
lda $0c ; load player y position lda $0c ; load player y position
@ -9857,10 +9856,10 @@ create_enemy_bullet:
@continue: @continue:
sta $06 ; store speed code in $06 sta $06 ; store speed code in $06
lda $08 ; load enemy bullet y position lda $08 ; load created bullet enemy y position
sta ENEMY_Y_POS,x ; store enemy bullet y position on screen sta ENEMY_Y_POS,x ; set created bullet enemy y position
lda $09 ; load enemy bullet x position lda $09 ; load created bullet enemy y position
sta ENEMY_X_POS,x ; store enemy bullet x position on screen sta ENEMY_X_POS,x ; set created bullet enemy x position
lda $0a lda $0a
and #$1f ; keep bits ...x xxxx (quadrant aim dir) and #$1f ; keep bits ...x xxxx (quadrant aim dir)
@ -10151,7 +10150,7 @@ rotate_enemy_var_1:
rts rts
; determines which direction to rotate based on quadrant_aim_dir_00 ; determines which direction to rotate based on quadrant_aim_dir_00
; targetting player index ($0a) ; targeting player index ($0a)
; input ; input
; * $0a - player index to target, 0 = player 1, 1 = player 2 ; * $0a - player index to target, 0 = player 1, 1 = player 2
; * $08 - source y position ; * $08 - source y position
@ -10162,10 +10161,10 @@ rotate_enemy_var_1:
; * $0c - new enemy aim direction ; * $0c - new enemy aim direction
get_rotate_00: get_rotate_00:
lda #$00 ; a = #$00 (use quadrant_aim_dir_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 ; determines which direction to rotate based on quadrant_aim_dir_01
; targetting player index ($0a) ; targeting player index ($0a)
; input ; input
; * $0a - player index to target, 0 = player 1, 1 = player 2 ; * $0a - player index to target, 0 = player 1, 1 = player 2
; * $08 - source y position ; * $08 - source y position
@ -10177,10 +10176,10 @@ get_rotate_00:
get_rotate_01: get_rotate_01:
lda #$01 ; a = #$01 (use quadrant_aim_dir_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) ; determines which direction to rotate based on quadrant_aim_dir_lookup_ptr_tbl index offset (a)
; targetting player index ($0a) ; targeting player index ($0a)
; input ; 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 ; * $0a - player index to target, 0 = player 1, 1 = player 2
; * $08 - source y position ; * $08 - source y position
; * $09 - source x position ; * $09 - source x position
@ -10189,7 +10188,7 @@ get_rotate_01:
; * a - rotation direction, #$00 clockwise, #$01 counterclockwise, #$80 no rotation needed ; * a - rotation direction, #$00 clockwise, #$01 counterclockwise, #$80 no rotation needed
; * $0c - new enemy aim direction ; * $0c - new enemy aim direction
get_rotate_dir_for_index: 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 lda $0a ; load player index
bpl @get_quadrant_aim_dir ; branch if closest player has been determined 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) 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) ; * $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 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) ; * 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 ; output
; * negative flag - set when enemy is already aiming at player and no rotation is needed ; * 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 ; * a - rotation direction, #$00 clockwise, #$01 counterclockwise, #$80 no rotation needed
; * $0c - new enemy aim direction ; * $0c - new enemy aim direction
get_rotate_dir: get_rotate_dir:
sta $0c ; store quadrant aim direction code in $0c 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 lsr ; move bit 0 to the carry
lda #$06 ; using either quadrant_aim_dir_00, or quadrant_aim_dir_02 lda #$06 ; using either quadrant_aim_dir_00, or quadrant_aim_dir_02
; midway direction, i.e. 9 o'clock ; midway direction, i.e. 9 o'clock
@ -10324,7 +10323,7 @@ get_rotate_dir:
dragon_arm_orb_seek_should_move: 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 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 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 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 ; based on source position ($09, $08) targeting player index $0a
sta $0c ; store enemy aim direction in $0c 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 ; determines the aim direction within a quadrant based on source position ($09, $08) targeting player index $0a
; input ; 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) ; * $0a - player index of player to target (#$00 for p1 or #$01 for p2)
; * $08 - source y position ; * $08 - source y position
; * $09 - source x position ; * $09 - source x position
@ -10443,7 +10442,7 @@ get_quadrant_aim_dir_for_player:
; * $09 - source x position ; * $09 - source x position
; * $0a - closest player y position ; * $0a - closest player y position
; * $0b - closest player x 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 ; output
; * a - quadrant aim direction (quadrant_aim_dir_xx value) ; * 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) ; * $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 ldy #$00 ; default assume player is to the right and equal to or below enemy
lda $0a ; load closest player y position lda $0a ; load closest player y position
sec ; set carry flag in preparation for subtraction 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) 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 eor #$ff ; enemy below player, handle overflow, flip all bits and add one
adc #$01 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 iny ; if y was 0, now is 2, if y was 1, now is 3
@continue: @continue:
lsr ; shift the difference between player and enemy x difference 6 bits lsr ; shift the difference between player and enemy x difference 6 bits
lsr ; (every #$40 pixels difference is a new horizontal direction) lsr ; (every #$40 pixels difference is a new horizontal direction)
lsr lsr
lsr lsr
lsr lsr
sty $07 ; store position of player relative to enemy in $07 (above/below, left/right) 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 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 sta $0b ; overwrite player x position with shifted bits 6 and 7
; (values [#$00-#$03]) of horizontal distance ; (values [#$00-#$03]) of horizontal distance
php ; backup CPU status flags on stack php ; backup CPU status flags on stack
lda $0f ; load which of the #$03 tables from quadrant_aim_dir_lookup_tbl to use 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 asl ; double since each entry is #$2 bytes
tay ; transfer to offset register tay ; transfer to offset register
lda quadrant_aim_dir_lookup_tbl,y ; get low byte of quadrant_aim_dir_xx address 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 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 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 sta $0d ; store high byte of pointer address in $0d
lda $0a ; load y difference to determine row offset lda $0a ; load y difference to determine row offset
asl asl
asl ; quadruple since each entry is #$04 bytes to get correct row 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 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 ; this gets the column of the aim direction
tay ; transfer to offset register tay ; transfer to offset register
lda ($0c),y ; load specific byte lda ($0c),y ; load specific byte
plp ; restore CPU status flags from stack plp ; restore CPU status flags from stack
bcs @set_and_exit ; branch if bit 5 of difference between player and enemy was set 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 ; this segments screen into bands for which nibble to use
lsr lsr
lsr lsr
lsr lsr
@ -10512,8 +10511,8 @@ get_quadrant_aim_dir:
and #$0f ; keep low nibble and #$0f ; keep low nibble
rts rts
; pointer table for set of quadran aim directions (#$3 * #$2 = #$6 bytes) ; pointer table for set of quadrant aim directions (#$3 * #$2 = #$6 bytes)
quadrant_aim_dir_lookup_tbl: 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_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_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) .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 ; https://github.com/vermiceli/nes-contra-us
; constants.asm contains the list of constants with meaningful names for the ; 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 ; memory addresses used by the game. It also contains constants for the various
; palette colors. ; 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 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) ; PPU (picture processing unit)
PPUCTRL = $2000 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 ; https://github.com/vermiceli/nes-contra-us
.segment "HEADER" .segment "HEADER"

1714
src/ram.asm Normal file

File diff suppressed because it is too large Load Diff