Zanac (NES)/RAM map: Difference between revisions

From Data Crystal
Jump to navigation Jump to search
No edit summary
m (Hawk moved page Zanac/RAM map to Zanac (NES)/RAM map)
 
(6 intermediate revisions by 3 users not shown)
Line 1: Line 1:
A preliminary and tentative list of RAM locations and their usage during gameplay. Regions are listed as address, size, then description.
{{rammap}}


   F 1 Currently swapped-in bank. This is set by the bank-swapping
Addr Sz Description
        routines and is consulted by routines that push it onto the
--------------------------------------------------------------------------------
        stack and restore it later.
   F   1 Currently swapped-in bank. This is set by the bank-swapping
   26 1 ROM bank holding compressed data.
        routines and is consulted by routines that push it onto the
   2B 1 Decompression control byte.
        stack and restore it later.
   2C 2 Pointer to current position in compressed data
  14  6 Scratch space for many routines
   32 1 Lives. 0 = Game Over, any value up to 255 is valid
   26   1 ROM bank holding compressed data.
   33 1 Upgrade level of basic gun, from 0 (two single shots) through 5 (three double shots)
   2B   1 Decompression control byte.
   34 1 Last direction pressed while Zanac is pilotable (From 0-8, SW, W, NW, S, Neutral, N, SE, E, NE)
   2C   2 Pointer to current position in compressed data
   35 1 Basic gun stat: number of shots allowed on-screen at once
   32   1 Lives. 0 = Game Over, any value up to 255 is valid
   36 1 Basic gun type: 1-3 for single/double/triple shot, 4 for supergun
   33   1 Upgrade level of basic gun, from 0 (two single shots) through 5 (three
   37 1 Basic gun bullet speed, ranging from -5 to -11
        double shots)
   51 1 Autofire delay on the basic gun. Starts at $0F, but can be dropped all the way to 1 (a new bullet every frame!)
   34   1 Last direction pressed while Zanac is pilotable (From 0-8, SW, W, NW, S,
   55 1 Total number of basic shots fired, mod 256
        Neutral, N, SE, E, NE)
   56 1 Number of basic shots fired without scoring a kill, capped at 255
   35   1 Basic gun stat: number of shots allowed on-screen at once
   5C 1 Current weapon, 0-7
   36   1 Basic gun type: 1-3 for single/double/triple shot, 4 for supergun
   5E 1 Weapon timer/ammo. Defaults to $14 (20) on free-fire weapons.
   37   1 Basic gun bullet speed, ranging from -5 to -11
   60 1 Current weapon power (number of times the weapon has been picked up?)
   51   1 Autofire delay on the basic gun. Starts at $0F, but can be dropped all
   61 1 Current weapon visage (Increases more slowly than 60, seems to be related to what the weapon's power level looks like when fired.)
        the way to 1 (a new bullet every frame!)
   66 1 Selector for graphics in tiles $0F8-$0FF: 0=Illegal; 1=AREA;
   55   1 Total number of basic shots fired, mod 256
        2=TIME; 3=GAME OVER; 4=BONUS; 5=PONYCA; 6-255=blank
   56   1 Number of basic shots fired without scoring a kill, capped at 255
   83 1 Frame count, in game time. (Updated by main loop, not VBLANK.)
   5C   1 Current weapon, 0-7
   90 1 VRAM Blit Control. Bit 7 = VBLANK should copy target values to
   5E   1 Weapon timer/ammo. Defaults to $14 (20) on free-fire weapons.
        shadow registers.
   60   1 Current weapon power (number of times the weapon has been picked up?)
   98 1 Target Y Scroll value.
   61   1 Current weapon visage (Increases more slowly than 60, seems to be
   99 1 If nonzero, use the lower nametables as the base.
        related to what the weapon's power level looks like when fired.)
   9A 1 If nonzero, use the right-hand nametables as the base. Since
   66   1 Selector for graphics in tiles $0F8-$0FF: 0=Illegal; 1=AREA;
        Zanac is horizontally mirrored, this control byte has no visible
        2=TIME; 3=GAME OVER; 4=BONUS; 5=PONYCA; 6-255=blank
        effect.
  6C  2 Pointer into current enemy-spawn script.
   9B 1 Target X Scroll Value
  6E  1 Countdown (in 512px units) to next enemy-spawn script command
   A9 1 Vertical Scroll subpixel count (256 subpixels = 1 pixel)
  7D  1 Current Area.
   AA 2 Current scroll speed, in subpixels/frame
   83   1 Frame count, in game time. (Updated by main loop, not VBLANK.)
   AC 2 Target scroll speed in normal play, in subpixels/frame
   90   1 VRAM Blit Control. Bit 7 = VBLANK should copy target values to
   AE 1 Actual number of full pixels scrolled this frame
        shadow registers.
   FC 1 Register shadow for Y Scroll value.
   98   1 Target Y Scroll value.
   FD 1 Register shadow for X Scroll value.
   99   1 If nonzero, use the lower nametables as the base.
   FE 1 Register shadow for PPU_MASK.
   9A   1 If nonzero, use the right-hand nametables as the base. Since
   FF 1 Register shadow for PPU_CTRL. Other logic may alter the actual
        Zanac is horizontally mirrored, this control byte has no visible
        value of PPU_CTRL as needed by that logic, but it will preserve
        effect.
        all other settings based on this and will restore to this value
   9B   1 Target X Scroll Value
        when it is done.
  A2  2 Current distance into map, in units of 16px
  102 2 Magic Number. A fresh power-on is distinguished from a RESET by
  A4  1 Total pixels scrolled since last map update (when 16+, mapgen runs)
        whether or not these two bytes are 35 53. If they are, the high
  A8  1 Currently selected base map pattern
        score is not reset and the reset count at 19A is incremented.
   A9   1 Vertical Scroll subpixel count (256 subpixels = 1 pixel)
        If they are not, first-time initialization is done (including
   AA   2 Current scroll speed, in subpixels/frame
        setting these bytes to the right values).
   AC   2 Target scroll speed in normal play, in subpixels/frame
  17A 8 Score. Big-endian, one digit per byte, so a score of 12,300 is
   AE   1 Actual number of full pixels scrolled this frame
        the bytes 00 00 00 01 02 03 00 00.
  AF  1 Index into supplemental enemy-spawn script
  185  7 High Score. Works like 17A, but only 7 bytes long and the ones
  B0  2 Supplemental enemy-spawn script.
        digit is assumed to be zero.
  B2  1 Index within the current base map pattern.
  19A 1 Number of times you have soft-reset the console since power-on.
  B3  2 Location of next mapscript command, in units of 16px
        This count is used to unlock Stage Select.
  B5  2 Pointer to next mapscript command
  200 FF CPU-RAM copy of OAM data
  B8  2 Base pointer for base map patterns for this area (indexed by A8)
  460 1 Number of bytes of data ready to send to PPU.
  BA  2 Base pointer for translation table from metatiles to real NES tiles
  461 2 PPU address for data, HIGH BYTE FIRST.
  C0  9 Bossfight control bytes
  463 4D Data to copy to VRAM.
   FC   1 Register shadow for Y Scroll value.
   FD   1 Register shadow for X Scroll value.
   FE   1 Register shadow for PPU_MASK.
   FF   1 Register shadow for PPU_CTRL. Other logic may alter the actual
        value of PPU_CTRL as needed by that logic, but it will preserve
        all other settings based on this and will restore to this value
        when it is done.
  102   2 Magic Number. A fresh power-on is distinguished from a RESET by
        whether or not these two bytes are 35 53. If they are, the high
        score is not reset and the reset count at 19A is incremented.
        If they are not, first-time initialization is done (including
        setting these bytes to the right values).
145  1 Background color
146  C Map palettes (four sets of three non-transparent colors)
152  C Sprite palettes (four sets of three non-transparent colors)
16E  8 Map tile color remap table
  17A   8 Score. Big-endian, one digit per byte, so a score of 12,300 is
        the bytes 00 00 00 01 02 03 00 00.
  182  7 High Score. Works like 17A, but only 7 bytes long and the ones
        digit is assumed to be zero.
  19A   1 Number of times you have soft-reset the console since power-on.
        This count is used to unlock Stage Select.
  200 100 CPU-RAM copy of OAM data
400  60 Map feature control bytes. 8 channels, 12 fields.
  460   1 Number of bytes of data ready to send to PPU.
  461   2 PPU address for data, HIGH BYTE FIRST.
  463 4D Data to copy to VRAM.
528 2D8 Game object control data. 26 channels, 28 fields.
 
== Channels and Fields ==
 
Several large blocks of RAM are dedicated to what is essentially an array of structures. However, instead of having a series of X blocks, where each block is N bytes long, it instead has a series of N byte arrays, each of then X bytes long. This turns out to be somewhat more efficient to access in 6502 machine code. The RAM map documents these as "channels" within "fields"; a channel is conceptually equivalent to an instance of some complex data structure, and a "field" corresponds to the members of those data types.


== Register Shadows ==
== Register Shadows ==
Line 73: Line 104:
* The VRAM data for transfer during VBLANK seems to have a number of possible formats, given the complexity of the function that does it. The characterization of 460-49F is almost certainly incomplete.
* The VRAM data for transfer during VBLANK seems to have a number of possible formats, given the complexity of the function that does it. The characterization of 460-49F is almost certainly incomplete.
* Where and how is rank/System aggressiveness stored, what influences it, and what does it influence?
* Where and how is rank/System aggressiveness stored, what influences it, and what does it influence?
{{Internal Data}}

Latest revision as of 20:36, 28 January 2024

Chip tiny.png The following article is a RAM map for Zanac (NES).

Addr Sz Description

--------------------------------------------------------------------------------
  F   1 Currently swapped-in bank. This is set by the bank-swapping
        routines and is consulted by routines that push it onto the
        stack and restore it later.
 14   6 Scratch space for many routines
 26   1 ROM bank holding compressed data.
 2B   1 Decompression control byte.
 2C   2 Pointer to current position in compressed data
 32   1 Lives. 0 = Game Over, any value up to 255 is valid
 33   1 Upgrade level of basic gun, from 0 (two single shots) through 5 (three
        double shots)
 34   1 Last direction pressed while Zanac is pilotable (From 0-8, SW, W, NW, S,
        Neutral, N, SE, E, NE)
 35   1 Basic gun stat: number of shots allowed on-screen at once
 36   1 Basic gun type: 1-3 for single/double/triple shot, 4 for supergun
 37   1 Basic gun bullet speed, ranging from -5 to -11
 51   1 Autofire delay on the basic gun. Starts at $0F, but can be dropped all
        the way to 1 (a new bullet every frame!)
 55   1 Total number of basic shots fired, mod 256
 56   1 Number of basic shots fired without scoring a kill, capped at 255
 5C   1 Current weapon, 0-7
 5E   1 Weapon timer/ammo. Defaults to $14 (20) on free-fire weapons.
 60   1 Current weapon power (number of times the weapon has been picked up?)
 61   1 Current weapon visage (Increases more slowly than 60, seems to be
        related to what the weapon's power level looks like when fired.)
 66   1 Selector for graphics in tiles $0F8-$0FF: 0=Illegal; 1=AREA;
        2=TIME; 3=GAME OVER; 4=BONUS; 5=PONYCA; 6-255=blank
 6C   2 Pointer into current enemy-spawn script.
 6E   1 Countdown (in 512px units) to next enemy-spawn script command
 7D   1 Current Area.
 83   1 Frame count, in game time. (Updated by main loop, not VBLANK.)
 90   1 VRAM Blit Control. Bit 7 = VBLANK should copy target values to
        shadow registers.
 98   1 Target Y Scroll value.
 99   1 If nonzero, use the lower nametables as the base.
 9A   1 If nonzero, use the right-hand nametables as the base. Since
        Zanac is horizontally mirrored, this control byte has no visible
        effect.
 9B   1 Target X Scroll Value
 A2   2 Current distance into map, in units of 16px
 A4   1 Total pixels scrolled since last map update (when 16+, mapgen runs)
 A8   1 Currently selected base map pattern
 A9   1 Vertical Scroll subpixel count (256 subpixels = 1 pixel)
 AA   2 Current scroll speed, in subpixels/frame
 AC   2 Target scroll speed in normal play, in subpixels/frame
 AE   1 Actual number of full pixels scrolled this frame
 AF   1 Index into supplemental enemy-spawn script
 B0   2 Supplemental enemy-spawn script.
 B2   1 Index within the current base map pattern.
 B3   2 Location of next mapscript command, in units of 16px
 B5   2 Pointer to next mapscript command
 B8   2 Base pointer for base map patterns for this area (indexed by A8)
 BA   2 Base pointer for translation table from metatiles to real NES tiles
 C0   9 Bossfight control bytes
 FC   1 Register shadow for Y Scroll value.
 FD   1 Register shadow for X Scroll value.
 FE   1 Register shadow for PPU_MASK.
 FF   1 Register shadow for PPU_CTRL. Other logic may alter the actual
        value of PPU_CTRL as needed by that logic, but it will preserve
        all other settings based on this and will restore to this value
        when it is done.
102   2 Magic Number. A fresh power-on is distinguished from a RESET by
        whether or not these two bytes are 35 53. If they are, the high
        score is not reset and the reset count at 19A is incremented.
        If they are not, first-time initialization is done (including
        setting these bytes to the right values).
145   1 Background color
146   C Map palettes (four sets of three non-transparent colors)
152   C Sprite palettes (four sets of three non-transparent colors)
16E   8 Map tile color remap table
17A   8 Score. Big-endian, one digit per byte, so a score of 12,300 is
        the bytes 00 00 00 01 02 03 00 00.
182   7 High Score. Works like 17A, but only 7 bytes long and the ones
        digit is assumed to be zero.
19A   1 Number of times you have soft-reset the console since power-on.
        This count is used to unlock Stage Select.
200 100 CPU-RAM copy of OAM data
400  60 Map feature control bytes. 8 channels, 12 fields.
460   1 Number of bytes of data ready to send to PPU.
461   2 PPU address for data, HIGH BYTE FIRST.
463  4D Data to copy to VRAM.
528 2D8 Game object control data. 26 channels, 28 fields.

Channels and Fields

Several large blocks of RAM are dedicated to what is essentially an array of structures. However, instead of having a series of X blocks, where each block is N bytes long, it instead has a series of N byte arrays, each of then X bytes long. This turns out to be somewhat more efficient to access in 6502 machine code. The RAM map documents these as "channels" within "fields"; a channel is conceptually equivalent to an instance of some complex data structure, and a "field" corresponds to the members of those data types.

Register Shadows

In order to keep the display consistent at all times, PPU registers are *triple-buffered* in Zanac's engine. Scroll, mask, and control data are directly stored in the PPU's registers, and the values from $FC-$FF hold the values to reload them with each frame. On top of this, additional "target" values are held in $98-$9B which are updated quasi-atomically, assisted by bit 7 in $90.

Interesting Interactions

  • The "super gun" does not actually exist on the main gun upgrade path. The super gun is bestowed by a routine at ROM offset $1973F which simply writes new hardcoded values to $35-$37 while leaving $33 untouched. This is why collecting additional power chips after getting the super gun downgrades you unless you had the three triple-shot gun (level 5) before gaining the super gun; the old value was still intact.
  • When a Sart or Cargo enemy is destroyed, it will drop a Blue Lander pickup if $55 (the number of times the main gun has been fired) and $17e (thousands digit of your score) are equal to each other mod 64. This is why you always were urged to destroy the first Sart with your special weapon before doing anything else - zero equals zero, so it was the easiest way to get a free Lander.

Major open questions

  • Where and how are the non-sprite parts of enemy/player objects held? There are a series of 26-element arrays from $0528 through at least $0715 that appear to describe object properties. This is a promising avenue of exploration.
  • How do weapon selection and upgrades work? Zanac has some unusual corner cases in its mechanics where certain kinds of upgrades "lock" the player's upgrade status unless something dramatic happens.
  • The VRAM data for transfer during VBLANK seems to have a number of possible formats, given the complexity of the function that does it. The characterization of 460-49F is almost certainly incomplete.
  • Where and how is rank/System aggressiveness stored, what influences it, and what does it influence?