Registers

CGGames

New member
Hi all. I've been picking away at trying to teach myself the basics of the coding language used in NESmaker's scripts, and I'm having a hard time understanding when / why one would use the X and Y registers? A lot of the coding language has these two registers directly baked into it, and I'm not able to tell why you'd send values there versus storing them as integers elsewhere or processing them using the main register. If there's a tutorial that explains this I haven't seen it yet (I'll admit I've jumped around a lot and only watched about half of them). I'm sure this is really obvious so I apologize, it's just been a bit of a hurdle for me, so any clarification would be greatly appreciated. Thanks.
 

CluckFox

Active member
Here is a quick overview of the 6502 registers. In general they're used as counters that don't occupy main memory (they're on the CPU itself). They're also used to load a byte from RAM given a base address (e.g. a variable) and an offset. So to load the 5th active object's health into the main register:

Code:
LDX #$04 ;; fifth object since index starts at zero
LDA Object_health,x ;; loads the 5'th byte from the array of bytes starting at Object_health label

More information about how X and Y are used in this way are on the same site in the Addressing section.

Hope this helps. If you have questions feel free to ask me here or on Discord.
 

puppydrum64

Active member
X and Y are often used as indexes or place markers in a table, or as loop counters. Let's say you have a table that's got the following data:

TableName:
.db #$11, #$22, #$33

If you type LDA TableName it will only load the 0th (first from the left) entry in the table into the accumulator. However, you can do this:

LDX #$02
LDA TableName, x

Which will load #$33 into A.
 

CGGames

New member
Thanks for the responses; incredibly helpful! I've been doing some self-training into 6502 in my freetime and I think I understand how and why you would use X and Y, but that just opens up a second question.

I don't understand why there is so much useage of "LDA ######, x" style code used in the code I've studied thus far.

Granted, much of it was taken from movement routines and the shooting code from the Shooter tutorial, but I'm still unsure why you would use tables (if I'm understanding what Puppydrum64's post is saying) in such places as opposed to simply replacing a single value in each case and sparing the potential additional processing. I'm pretty sure I'm either completely missing something or confused as to how values work in this language, but information on this would be much appreciated!
 

TakuikaNinja

Active member
I'll list two use cases here. Have a good think about them.

Example 1:
Say you want to have an object move in a sinusoidal pattern like those medusa heads in Castlevania. In high level languages you could get away with calulating the position on the fly thanks to modern compilers and blazing fast CPUs. However, this is the NES, with its 6502-based CPU clocked at 1.79 MHz. In most cases we simply do not have the time to manually calculate the position every time the object needs to. (Not to mention the fact that we would have to deal with units smaller than a pixel to make it look smooth). That's one reason why tables are used. Think of it like an answer sheet - if you include a table of precalculated sine values, all the CPU has to do is to fetch the corresponding answer to "calculate" the sine of a given value. Problem solved!

Example 2 (This'll get messy, I know):
Say you want to manage collisions using the positions of various objects. In high level languages you could afford to write slightly inefficient code for the same reason as example 1. However, this is the NES with limited room for code and game data. You might think it would be just fine to call the corrsponding variable each time you need it without having to look up tables. But what would happen if you wanted to change the position of a different object depending on some condition - like checking what to do when the player's shot hits an enemy? With a naive approach the sudocode might look like this:
Code:
; some code
switch(playerShotPosX):
    case enemy1PosX:
        enemy1PosX += 32 ; knockback
        removePlayerShot()
        ; hp related stuff
    case enemy2PosX:
        enemy2PosX += 32 ; knockback
        removePlayerShot()
        ; hp related stuff
    case bossPosX:
        bossPosX += 32 ; knockback
        removePlayerShot()
        ; hp related stuff
    default:
        playerShotPosX += 16
This is terribly inefficient and not pleasant to look at. There are redundant cases we have to check for, and it would get worse if we added more enemies! In this scenario the first enemy object hit by the player's shot will get knocked back 32 units, and the player's shot always gets removed after it hits an enemy.
A better approach would be to use tables like this:
Code:
; what the reserved memory space for enemy positions and HP might look like
; this part of memory might change depending on which enemies are loaded
enemyXPositions = [enemy1PosX, enemy2PosX, bossPosX]
enemyHPValues = [enemy1HP, enemy2HP, bossHP]
maxIndex = enemiesLoaded ; this variable would be changed when an enemy object spawns/despawns
; think of this like the pokedex numbers in pokemon - they don't match the order they were added in
enemyIndexes = [2, 0, 1]

; the enemy index determines the amount of damage we can do (the order doesn't change)
playerShotDamage = [4, 2, 1]


; later on...
;some code
idx = 0 ; the array/table index
while playerShotPos != enemyXPositions[idx] and idx < maxIndex:
    idx += 1 ; increment counter
if idx == maxIndex:
    playerShotPosX += 16
else:
    enemyXPositions[idx] += 32 ; knockback
    removePlayerShot()
    enemyIndex = enemyIndexes[idx]
    enemyHPValues[idx] -= playerShotDamage[enemyIndex]
    if enemyHPValues[idx] <= 0:
        removeEnemyObject(idx)
Look at how compact, efficient, scalable, and readable this has become. We could even make a routine which automatically updates the relevant tables when a new object is loaded in. See where we're going? This makes it easier to add new enemies in the future. As an added bonus, we can reuse the index we got as a parameter for other tables to speed up more routines!
Conditionals take time to execute and take up space - we should be trying to generalize the code so that barely any conditional checking needs to happen.

Example 2 is the most common reason for using "LDA tableAddress, X". The variable points to the start of the table of the variables you wish to access, and X is the index within it. In my sudocode format it would be written as "A = tableAddress[X]".

tl;dr: Tables are used for speeding up complex calculations (like sine) and for generalizing routines to improve the scalability.

(Thanks for putting through with this wall of text I made at around midnight. Let me know if I got something wrong.)
 
Last edited:

CGGames

New member
Thanks for the reply! That clears up A LOT, and the first example helps clarify a different, though less immediately pressing question I had. Thanks again!
 
Top Bottom