# Table of Contents - [Introduction | Assembly for the SNES](#introduction-assembly-for-the-snes) - [Getting started | Assembly for the SNES](#getting-started-assembly-for-the-snes) - [The SNES memory | Assembly for the SNES](#the-snes-memory-assembly-for-the-snes) - [Hexadecimal | Assembly for the SNES](#hexadecimal-assembly-for-the-snes) - [Binary | Assembly for the SNES](#binary-assembly-for-the-snes) - [Loading and storing | Assembly for the SNES](#loading-and-storing-assembly-for-the-snes) - [Unknown](#unknown) - [Contributing | Assembly for the SNES](#contributing-assembly-for-the-snes) - [Jumping to subroutines | Assembly for the SNES](#jumping-to-subroutines-assembly-for-the-snes) - [Little-endian | Assembly for the SNES](#little-endian-assembly-for-the-snes) - [The processor flags | Assembly for the SNES](#the-processor-flags-assembly-for-the-snes) - [Transfers | Assembly for the SNES](#transfers-assembly-for-the-snes) - [Addressing modes | Assembly for the SNES](#addressing-modes-assembly-for-the-snes) - [Shorter addresses | Assembly for the SNES](#shorter-addresses-assembly-for-the-snes) - [Changing the processor flags | Assembly for the SNES](#changing-the-processor-flags-assembly-for-the-snes) - [The SNES registers | Assembly for the SNES](#the-snes-registers-assembly-for-the-snes) - [Hardware math | Assembly for the SNES](#hardware-math-assembly-for-the-snes) - [Bit shifting operations | Assembly for the SNES](#bit-shifting-operations-assembly-for-the-snes) - [Glossary | Assembly for the SNES](#glossary-assembly-for-the-snes) - [8-bit and 16-bit mode | Assembly for the SNES](#8-bit-and-16-bit-mode-assembly-for-the-snes) - [Bitwise operations | Assembly for the SNES](#bitwise-operations-assembly-for-the-snes) - [Comparing, branching, labels | Assembly for the SNES](#comparing-branching-labels-assembly-for-the-snes) - [Tables and indexing | Assembly for the SNES](#tables-and-indexing-assembly-for-the-snes) - [Copying data | Assembly for the SNES](#copying-data-assembly-for-the-snes) - [Miscellaneous opcodes | Assembly for the SNES](#miscellaneous-opcodes-assembly-for-the-snes) - [Hardware vectors | Assembly for the SNES](#hardware-vectors-assembly-for-the-snes) - [Machine cycles | Assembly for the SNES](#machine-cycles-assembly-for-the-snes) - [The stack | Assembly for the SNES](#the-stack-assembly-for-the-snes) - [Stack pointer register | Assembly for the SNES](#stack-pointer-register-assembly-for-the-snes) - [Arithmetic operations | Assembly for the SNES](#arithmetic-operations-assembly-for-the-snes) - [Addressing modes revisted | Assembly for the SNES](#addressing-modes-revisted-assembly-for-the-snes) - [Programming cautions | Assembly for the SNES](#programming-cautions-assembly-for-the-snes) - [Techniques | Assembly for the SNES](#techniques-assembly-for-the-snes) - [Common assembler syntax | Assembly for the SNES](#common-assembler-syntax-assembly-for-the-snes) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Hexadecimal | Assembly for the SNES](#hexadecimal-assembly-for-the-snes) - [Unknown](#unknown) - [Loading and storing | Assembly for the SNES](#loading-and-storing-assembly-for-the-snes) - [The processor flags | Assembly for the SNES](#the-processor-flags-assembly-for-the-snes) - [Unknown](#unknown) - [Unknown](#unknown) - [Arithmetic operations | Assembly for the SNES](#arithmetic-operations-assembly-for-the-snes) - [Tables and indexing | Assembly for the SNES](#tables-and-indexing-assembly-for-the-snes) - [Addressing modes revisted | Assembly for the SNES](#addressing-modes-revisted-assembly-for-the-snes) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) --- # Introduction | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/readme.md) . This tutorial is an online version of my 65c816 assembly tutorial which is hosted on [SMW Central](https://www.smwcentral.net/) . I originally wrote this tutorial in order to teach the SMW Central community the 65c816 assembly language in plain English. Nowadays, it's read by various people in the ROM hacking scene in general. Therefore, I decided to open source this tutorial on GitHub, so that people can make improvements or translations. Although I'm a member of SMW Central, this tutorial is not associated with Super Mario World, thus this tutorial is not tailored towards that game. Instead, this tutorial can be applied in all SNES context. [](https://ersanio.gitbook.io/assembly-for-the-snes#the-language) The language ----------------------------------------------------------------------------------- 65c816 assembly is the language used by the Super Nintendo Entertainment System's (SNES) Ricoh 5A22 chip. Breaking down the different parts of the acronym 65c816: 816 means that the processor can be either 8-bit mode or 16-bit mode. The c stands for CMOS, 65 means that this processor is from the 65xx CPU family. The processor is supposed to be pretty revolutionary for its time. This tutorial explains mnemonics/instructions (i.e. opcodes) and how to use them properly. This tutorial does not focus on SNES-specific topics, such as hardware registers. With 65c816 ASM you can code things for SNES games (such as custom features for Super Mario World). ASM is a 2nd generation programming language, which is low-level compared to C# for example. It is readable machine code, which eventually gets translated into hexadecimal machine code. All the opcodes consist of 3 letters, along with various parameters. [](https://ersanio.gitbook.io/assembly-for-the-snes#special-thanks) Special thanks --------------------------------------------------------------------------------------- Many special thanks go to the following people for reviewing the original tutorial on SMW Central: [**spigmike**](https://www.smwcentral.net/?p=profile&id=132) **,** [**Roy**](https://www.smwcentral.net/?p=profile&id=845) **,** [**smkdan**](https://www.smwcentral.net/?p=profile&id=411) **,** [**S.N.N**](https://www.smwcentral.net/?p=profile&id=23) **,** [**andy\_k\_250**](https://www.smwcentral.net/?p=profile&id=67) **,** [**Domiok**](https://www.smwcentral.net/?p=profile&id=7211) **,** [**reghrhre**](https://www.smwcentral.net/?p=profile&id=4176) **,** [**ChaoticFox**](https://www.smwcentral.net/?p=profile&id=3462) **,** [**Tails\_155**](https://www.smwcentral.net/?p=profile&id=6151) **,** [**GreenHammerBro**](https://www.smwcentral.net/?p=profile&id=18802) **,** [**Vitor Vilela**](https://www.smwcentral.net/?p=profile&id=8251) Many special thanks also go to the [contributors](https://github.com/Ersanio/snes-assembly-book/graphs/contributors) of this repository! [NextGetting started](https://ersanio.gitbook.io/assembly-for-the-snes/getting-started) Last updated 6 years ago This site uses cookies to deliver its service and to analyze traffic. By browsing this site, you accept the [privacy policy](https://policies.gitbook.com/privacy/cookies) . AcceptReject --- # Getting started | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/getting-started.md) . [](https://ersanio.gitbook.io/assembly-for-the-snes/getting-started#ide) IDE --------------------------------------------------------------------------------- There are no dedicated IDEs for 65c816 assembly. You can use any ASCII editor, such as Notepad or VS Code. However, some people made various plugins for existing code editors to add extra features, such as syntax highlighting: * Josh Neta's "[65816 Assembly](https://marketplace.visualstudio.com/items?itemName=joshneta.65816-assembly) " plugin for VS Code * Vice's "[65816 SNES Assembly Language Server](https://marketplace.visualstudio.com/items?itemName=vicerust.snes-asm) " plugin for VS Code * lx5's "[Asar syntax highlight](https://github.com/TheLX5/AsarSyntaxHighlight-VSCode) " plugin for VS Code. * Telinc1's "[65c818 ASM syntax for highlight.js](https://github.com/telinc1/smwcentral-highlightjs-asar) " as a JavaScript plugin Assembly files are generally saved with the file extension ".asm". [](https://ersanio.gitbook.io/assembly-for-the-snes/getting-started#assemblers) Assemblers ----------------------------------------------------------------------------------------------- This tutorial uses syntax which is used by an assembler called "Asar", originally written by Alcaro, now maintained by various members of the SMW Central community. This assembler is hosted on SMW Central and can be downloaded [here](https://www.smwcentral.net/?p=section&a=details&id=19043) . The GitHub repository of Asar can be found [here](https://github.com/RPGHacker/asar) . [PreviousIntroduction](https://ersanio.gitbook.io/assembly-for-the-snes) [NextContributing](https://ersanio.gitbook.io/assembly-for-the-snes/contributing) Last updated 11 months ago --- # The SNES memory | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/memory.md) . Writing assembly involves writing a bunch of instructions where you load a "value" and store it at an "address" in order to get a desired effect, such as changing the player's powerup. When writing assembly, you will work with the SNES memory most of the time. The SNES memory basically is a region of bytes, and each byte is located at an "address". Think of it like a chessboard: ![](https://ersanio.gitbook.io/assembly-for-the-snes/~gitbook/image?url=https%3A%2F%2F378501219-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-LypdhuQyau2iik8UpMt%252F-Lysktgn51vJoJswj1WQ%252F-Lysl9clYphWvPa7dbYP%252Fchessboard.png%3Falt%3Dmedia%26token%3D4760e755-8faf-41b0-b49e-c52c48a8ed78&width=768&dpr=3&quality=100&sign=aa566610&sv=2) You can see that in order to refer to a certain cell, the image makes use of column and cell names. The "address" of the shown queen (the "value") would be address D8, for example. Also, a single cell can't hold two units. This same concept applies to the SNES memory. The SNES memory is mapped from address $000000 to $FFFFFF, although only $000000-$7FFFFF is used in most cases. The format of an address is as follows: $BBHHDD. * BB is the "bank byte" of the address * HH is the "high byte" of the address * DD is the "low byte" of the address Addresses can be written in 3 ways: $BBHHDD, $HHDD and $DD, such as $7E0003, $0003 and $03. * Addresses in the $DD-notation are called "direct page" * Addresses in the $HHDD-notation are called "absolute addresses" * Addresses in the $BBHHDD-notation are called "long addresses" As established earlier, an address may contain only one byte. If you access a certain address in 16-bit mode, it means you actually access both "address" and "address+1", because a 16-bit number consists of two bytes. Here's a drawing to get a general overview of the basic SNES memory (also known as a memory map): ![](https://ersanio.gitbook.io/assembly-for-the-snes/~gitbook/image?url=https%3A%2F%2F378501219-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-LypdhuQyau2iik8UpMt%252F-Lysktgn51vJoJswj1WQ%252F-LyslKE0owAcmmoN-Ipa%252Fmemory.png%3Falt%3Dmedia%26token%3D7978c9ae-1e99-434a-a80a-6ca91e70f70d&width=768&dpr=3&quality=100&sign=6ffb96ed&sv=2) The “LoROM” Memory Map This memory map is in the "LoROM" format. If you're a SMW hacker, you don't have to worry about what this means; just take this memory map for granted. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/memory#rom) ROM ----------------------------------------------------------------------------------------- ROM stands for "Read-Only Memory" and it's exactly that: memory that can be only read. This means that you cannot change the ROM by storing values to it with ASM. You can say it is the game or program itself, which contains all the ASM code and data tables, as well as assets like graphics, music, and so on. Alternatively: It's the .smc/.sfc/.fig/etc. file which you load in emulators. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/memory#ram) RAM ----------------------------------------------------------------------------------------- RAM stands for "Random-Access Memory". This is the dynamic memory which allows anything to be written to it at any time. You could say that this is the place where you have variables which are important and have meaning. RAM can be written to in order to achieve a certain effect. For example, if you write $04 to the player's extra lives, then the player will have exactly 4 extra lives. The SNES RAM is 128kB big, and it is located at addresses $7E0000-$7FFFFF. The SNES RAM is completely generic. There is no such rule as "address $7E0120 is used for lives in every SNES game ever". You define the purpose of RAM yourself, by writing ASM code. The memory map shows that banks $00-3F contain a RAM "mirror". Mirrored RAM addresses are addresses which contain the same value across every bank. This means that RAM address $001234 contains the exact same value as $0F1234 at all times. Having the RAM mirrored means that code executing in the ROM at those banks can access RAM $7E0000-$7E1FFF more "easily". Conversely, code executing at banks $40-6F have more trouble accessing the RAM because the RAM isn't mirrored there. For the sake of simplicity, you can **always** assume that bank $00 equals bank $7E. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/memory#sram) SRAM ------------------------------------------------------------------------------------------- SRAM stands for "Static Random-Access Memory". It is also 128kB big, and it is located in blocks of 32kB at $700000-$707FFF, $710000-$717FFF, $720000-$727FFF and $730000-$737FFF, although the final size of SRAM depends on the ROM specifications itself, thanks to something called the "internal ROM header. The SRAM isn't mirrored in other banks. SRAM behaves exactly like RAM; you can store anything and load anything from it, but the values do not get cleared when the SNES resets. The SRAM is kept alive with an actual button-cell battery on a real SNES cartridge. When the battery dies, or is removed, SRAM won't function properly and will possibly lose data after every reset. On emulators, SRAM is stored in the well-known ".srm"-files. SRAM is usually used for save files, although it can also be used as extra RAM. [PreviousBinary](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/binary) [NextThe SNES registers](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers) Last updated 4 years ago --- # Hexadecimal | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/hexadecimal.md) . To program in 65c816 ASM, you will need to grasp the basics of hexadecimal. Hexadecimal, also known as "hex", is a counting system much like decimal, which is the everyday counting system people use. In hexadecimal, there are additional 6 digits per place value, which are denoted through the values A-F, as seen in the table below. Decimal Hexadecimal 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 A 11 B 12 C 13 D 14 E 15 F 16 10 17 11 ... ... 255 FF There are various ways to write hex numbers so readers cannot confuse them with actual decimal numbers. They are as follows: * Prefix hexadecimal numbers with "0x" (e.g. 0x42) * Prefix hexadecimal numbers with "$" (e.g. $42) * Sufffix hexadecimal numbers with "H" (e.g. 42H) In this tutorial, the convention is to prefix hexadecimal numbers with "$". In assembly, a hexadecimal number with two digits is called a "byte". This means that values between $00-$FF are considered a byte. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/hexadecimal#signed-and-unsigned-values) Signed and unsigned values -------------------------------------------------------------------------------------------------------------------------------------------- In the real world, numbers can be positive or negative. In assembly, depending on the code, values can be treated as "signed" or "unsigned". Signed values mean that they can also be negative: The value $80 and higher are considered to be negative numbers in decimal, starting from -128, and counting down as the hex number is counting up, as you can see in the table below. Decimal Hexadecimal 126 $7E 127 $7F \-128 $80 \-127 $81 ... ... \-1 $FF The presence of negative numbers depends on the game's programming. For example, a player can have positive and negative speed (resulting in going forward or backward), but a player cannot have negative extra lives or points (because normally that doesn't make sense). Needless to say, the value -0 does not exist. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/hexadecimal#four-digit-hexadecimal-values) Four-digit hexadecimal values -------------------------------------------------------------------------------------------------------------------------------------------------- Hexadecimal numbers can count well past two digits, as you can see below. Decimal Hexadecimal 254 $FE 255 $FF 256 $0100 257 $0101 ... ... 65535 $FFFF The format of such a hexadecimal number is as follows: $HHLL. * HH is the "high byte" of the number * LL is the "low byte" of the number [PreviousContributing](https://ersanio.gitbook.io/assembly-for-the-snes/contributing) [NextBinary](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/binary) Last updated 4 years ago --- # Binary | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/binary.md) . Another important counting system is "binary". Binary has only two possible digits for each place value: 0 and 1. A binary digit is also called a "bit". In assembly syntax, bits are prefixed by "%". A byte is made of eight "bits". Because a binary digit has two possible values, and a byte has 8 bits, this means there are 2⁸ possible values in a byte. For example, a byte can consist of the following bits: `1001 0110` or `1001 0101`. The first bit from the left is called "bit 7" and the final bit is called "bit 0". They are NOT called bits 0-7, nor bits 8-1. Here's an overview: Copy Bit 7654 3210 1001 0110 1001 0101 .... .... The table below shows a relatively easy way to memorize binary, as it displays a pattern. Binary Hexadecimal `%0000 0001` `$01` `%0000 0010` `$02` `%0000 0100` `$04` `%0000 1000` `$08` `%0001 0000` `$10` `%0010 0000` `$20` `%0100 0000` `$40` `%1000 0000` `$80` Note that there is a space inbetween 4 bits for easier readability, although assemblers generally don't accept this syntax. Groups of 4 bits are called "nibbles" and for the purposes of this chapter, they are there to make binary easier to read, because one nibble corresponds to one digit in hexadecimal. The SNES is capable of working with both 8-bit and 16-bit numbers. While 8-bit numbers are called a byte, 16-bit numbers are called a "word". They would look like they have 16 bits in binary (e.g. `10000101 11010101`, which is `$85D5` in hexadecimal). In the case of 16-bit numbers, the leftmost bit is called "bit 15" while the rightmost bit is called "bit 0": Copy 1111 11 (read from top to bottom) Bit 5432 1098 7654 3210 1000 0101 1101 0101 0000 0000 1001 0110 .... .... .... .... [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/binary#flags) Flags --------------------------------------------------------------------------------------------- Binary is immensely useful when you're giving a hex value multiple purposes, kind of like an on/off toggle for certain features. These bits are called "flags" and are generally used to save space in the working memory of games. For example, you can divide a byte into 8 bits with each bit having a different meaning. Bit 7 could indicate that a level has rain or not. Bit 6 could indicate that a level layout is horizontal or vertical. Bit 5 could indicate that the level setting is during day or night, etc. This way you can compress information into a single byte. It would look like this in binary: Copy 10100000 ││└───── "Is daytime" flag │└───── "Is horizontal level" flag └───── "Is raining" flag Finally, here's an overview of how to count up in decimal, hexadecimal and binary: Decimal Hexadecimal Binary `00` `$00` `%0000 0000` `01` `$01` `%0000 0001` `02` `$02` `%0000 0010` `03` `$03` `%0000 0011` `04` `$04` `%0000 0100` `05` `$05` `%0000 0101` `06` `$06` `%0000 0110` `07` `$07` `%0000 0111` `08` `$08` `%0000 1000` `09` `$09` `%0000 1001` `10` `$0A` `%0000 1010` `11` `$0B` `%0000 1011` `12` `$0C` `%0000 1100` `13` `$0D` `%0000 1101` `14` `$0E` `%0000 1110` `15` `$0F` `%0000 1111` `16` `$10` `%0001 0000` `17` `$11` `%0001 0001` ... ... ... `254` `$FE` `%1111 1110` `255` `$FF` `%1111 1111` [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/binary#notation) Notation --------------------------------------------------------------------------------------------------- Sometimes, bits could be written inconsistently, like `11` or `110 0000`. This makes the binary number harder to read, because the general convention is to write bits in groups of eight. In order to read them, you will need to add leading 0s to the digits until there are either 8 bits or 16 bits in total. In 8-bit: * `11` becomes `00000011` * `1100000` becomes `01100000` In 16-bit: * `11` becomes `00000000 00000011` * `1100000` becomes `00000000 01100000` You can convert between decimal, hexadecimal and binary, by using Windows calculator's "programming" mode. There are also many calculators online which can do this. Assembly syntax accepts decimal and binary also, so you usually don't need to convert between decimal and hexadecimal. [PreviousHexadecimal](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/hexadecimal) [NextThe SNES memory](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/memory) Last updated 4 years ago --- # Loading and storing | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/loading-and-storing.md) . The first thing you definitely should know when starting off with assembly, is how to load and store data using various the SNES registers. The basic opcodes for loading and storing data are `LDA` and `STA`. As mentioned earlier, there are 3 main registers: * A (the accumulator) * X (index) * Y (index) Although these registers can be either in 8-bit or 16-bit mode, in this tutorial we will consider them 8-bit by default. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/loading-and-storing#lda-and-sta) LDA and STA ---------------------------------------------------------------------------------------------------------------- Opcode Full name Explanation **LDA** Load into accumulator Load a value into A **STA** Store from accumulator Store A's value into an address We will use RAM addresses for the sake of simplicity. Here is an example for loading and storing values. Copy LDA #$03 ; A = $03 STA $7E0001 We will look at this code line by line. Copy LDA #$03 This loads the value `$03` into A. The "#" means that we're loading an actual value, not an address. After this instruction, the content of the A register is now `$03`. LDA can load values into A, ranging from `$00-$FF` in 8-bit mode and `$0000-$FFFF` in 16-bit mode. Copy STA $7E0001 This stores A's value into the RAM address $7E0001. Because A's value was $03, RAM address $7E0001's value also is now $03. The contents of the A register is _not_ cleared. This means you can chain multiple stores, like this: Copy LDA #$03 STA $7E0001 STA $7E0053 A common beginner's mistake is writing `STA #$7E0001` or any form of "STA #$". This instruction doesn't exist. It also doesn't make sense; there's no logic behind storing the value of A into another value. Remember, using $ instead of #$ after an opcode means that the parameter is an address, not an immediate value. Putting a semicolon (;) will allow everything beyond that to be ignored by the assembler, during the assembly of the code. In other words, ; is used to place comments. Example: Copy LDA #$03 ; This is a comment! ### [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/loading-and-storing#loading-and-storing-addresses) Loading and storing addresses Of course, what would be the use to store things to a RAM address when you don't know how to access the address again? You can load a RAM address' contents into the A register by using LDA with a different addressing mode. Here is an example. Copy LDA $7E0013 STA $7E0042 Again, we will look at this example line by line. Copy LDA $7E0013 This will load the contents of the RAM address `$7E0013` into A. Let's assume that the contents were `$33`. So now, A has the value `$33`. The content of `$7E0013` remains unchanged, because LDA copies the number rather than extracting it from the address. Note that this time we have used $ instead of #$. This is because we wanted to access a RAM address. In the end, A has `$33` and RAM `$7E0013` has the value `$33` also. Copy STA $7E0042 This instruction will store the contents of the A register into the RAM address `$7E0042`. Of course, A will remain unchanged. RAM address `$7E0042` is now `$33`. In short: the full example will copy the contents of `$7E0013` over to `$7E0042`. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/loading-and-storing#ldy-sty-ldx-stx) LDY, STY, LDX, STX --------------------------------------------------------------------------------------------------------------------------- Now that we have learned the basics of loading and store values into addresses, let's introduce the same loading opcodes, but for the index registers: Opcode Full name Explanation **LDY** Load into Y Load a value into Y **STY** Store from Y Store Y's value into an address **LDX** Load into X Load a value into X **STX** Store from X Store X's value into an address The above opcodes behave exactly like LDA and STA. The only difference is that these make use of the X and the Y registers instead of the accumulator. For example: Copy LDY #$03 STY $0001 Would store the number $03 into RAM address $7E0001, by utilizing the Y register. To use the X register, use LDX and STX. As for why the address is $0001 instead of $7E0001, please refer to this chapter: [Shorter addresses](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/shorter-addresses) [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/loading-and-storing#stz) STZ ------------------------------------------------------------------------------------------------ There is another opcode which stores the number $00 into addresses directly. Opcode Full name Explanation **STZ** Store zero to memory Sets the value of an address to 0 This opcode stores the number $00 into an address. It doesn't even need the A, X or Y registers to load $00 first. If you want to make a code that directly stores $00 in a RAM address, you could make it use 1 line: Copy STZ $01 ; $7E0001 = $00. The A register is unaffected. STZ will store zero to a specified RAM address. After this opcode, RAM address $7E0001 will now contain the number $00. Using STZ when A is in 16-bit mode **will** store $0000 to both RAM addresses $7E0001 and $7E0002. [PreviousGlossary](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/glossary) [NextShorter addresses](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/shorter-addresses) Last updated 4 years ago --- # Unknown \# Assembly for the SNES ## Assembly for the SNES - \[Introduction\](https://ersanio.gitbook.io/assembly-for-the-snes/readme.md) - \[Getting started\](https://ersanio.gitbook.io/assembly-for-the-snes/getting-started.md) - \[Contributing\](https://ersanio.gitbook.io/assembly-for-the-snes/contributing.md) - \[Hexadecimal\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/hexadecimal.md) - \[Binary\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/binary.md) - \[The SNES memory\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/memory.md) - \[The SNES registers\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers.md) - \[Addressing modes\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing.md) - \[Little-endian\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/endian.md) - \[Glossary\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/glossary.md) - \[Loading and storing\](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/loading-and-storing.md) - \[Shorter addresses\](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/shorter-addresses.md) - \[8-bit and 16-bit mode\](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/816.md) - \[Comparing, branching, labels\](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches.md) - \[Jumping to subroutines\](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/subroutine.md) - \[Tables and indexing\](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/indexing.md) - \[The stack\](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack.md) - \[Copying data\](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves.md) - \[The processor flags\](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags.md) - \[Changing the processor flags\](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/repsep.md) - \[Transfers\](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/transfer.md) - \[Stack pointer register\](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/stackpointer.md) - \[Arithmetic operations\](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic.md) - \[Bit shifting operations\](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift.md) - \[Bitwise operations\](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic.md) - \[Hardware math\](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/math.md) - \[Addressing modes revisted\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing.md) - \[Miscellaneous opcodes\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc.md) - \[Machine cycles\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cycles.md) - \[Hardware vectors\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector.md) - \[Techniques\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques.md) - \[Common assembler syntax\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax.md) - \[Programming cautions\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cautions.md) --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on a page URL with the \`ask\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/readme.md?ask= \`\`\` The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Contributing | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/contributing.md) . This chapter concerns the writers of this tutorial. When writing, there are a few standards to follow in order to maximize consistency throughout the tutorial. [](https://ersanio.gitbook.io/assembly-for-the-snes/contributing#address-explanations) Address explanations ---------------------------------------------------------------------------------------------------------------- This section concerns the way addresses are written, as to avoid confusion. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/contributing#direct-page) Direct page * For the sake of simplicity and explanation, all direct page addresses ("`$xx`") shall use address `$7E0000` as its base. This shall be emphasized in code block comments, as well as explanations. Example: Copy LDA #$42 STA $08 ; Store the value $42 into address $7E0008 ### [](https://ersanio.gitbook.io/assembly-for-the-snes/contributing#absolute-addressing) Absolute addressing * All absolute addresses ("$xxxx") use address `$7E0000` as its base, if the address is between $0000-$7FFF inclusive. For addresses between $8000-$FFFF, address `$000000` shall be used as its base instead. This shall be emphasized in code block comments, as well as explanations. Example: Copy LDA $8002 ; Load the value at address $008002 into A STA $1008 ; Store that value into address $7E1008 [](https://ersanio.gitbook.io/assembly-for-the-snes/contributing#styling) Styling -------------------------------------------------------------------------------------- This section concern the styling of the document. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/contributing#inline-code) Inline code * All opcodes and instructions shall be surrounded by the markdown backtick (\`), regardless of the context. Example: The opcode `LDA` is used to load values into the A register. Thus, `LDA #$49` loads the value $49 into A. This then can be increased to the value $4A by using `INC A`. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/contributing#markdown-tables) Markdown tables * Sentences and phrases within table cells generally shouldn't end with a period. * Tables introducing opcodes should have the opcodes in **bold** and have at least the three columns in below example: Opcode Full name Explanation **LDA** Load into accumulator Load a value into A [](https://ersanio.gitbook.io/assembly-for-the-snes/contributing#terminology) Terminology ---------------------------------------------------------------------------------------------- This section concerns the usage of certain words in certain context. Rule Example When referring to the `Ricoh 5A22` processor, use `the SNES` instead `The SNES` is capable of entering 8-bit or 16-bit mode When referring to a specific area in the SNES memory, always prepend `address` to it, preferably with the memory area (i.e. `RAM`) (The) `RAM address` $7E0000 contains \[...\] When referring to the accumulator (A) or the index registers (X and Y), just use `A`, `X` or `Y` `A` is now in 8-bit mode. `X` is used to index addresses When referring to values, always prepend `value` to it A contains the `value` $00 [](https://ersanio.gitbook.io/assembly-for-the-snes/contributing#example-codes) Example codes -------------------------------------------------------------------------------------------------- * Code shall use indentation when there are labels, sublabels or plus/minus labels on the same line as an instruction. The amount of indentation equals the length of the longest aforementioned type of label in the code block, including colon ("`:`"), plus two additional spaces. * Code shall use whitespace for indentation, not tabs. * Opcodes are written entirely in uppercase (e.g.: `LDA`). * Labels are written in PascalCase (e.g.: `Label1:`). * Sublabels are written entirely in lower, underscore-case and the name should semantically suit the parent label, without redundancy (e.g.: `.return`). * Defines are written in PascalCase (e.g.: `!SomeDefine`). * Direct data (`db`, `dw`, `dl`, `dd`), indexers (`,x`, `,y`, `,s`) and opcode length specifiers (`.b`, `.w`, `.l`) are written entirely in lowercase. * Comment indicators (i.e. `;`) shall start on column 20, and left-padded by whitespace, not tabs. * If there's no space for a comment on column 20, it should start on the same line anyway. * In fact, never use tabs. * Comment indicators shall be followed by a whitespace, before the comment itself. * There will be an extra new line after the opcodes `RTS`, `RTL`, `RTI`, `JMP`, `JML`, `BRA`, `BRL`. * These are guidelines which are to be followed as strictly as possible, but there may be exceptional cases. Use your best judgment. Examples: Copy SomeLabel: ; This label is on its own line LDA.b #$42 STA $00 ; This is a comment RTS .table: db $01,$02,$03,$04 ; Another comment .second_table: db $01,$02,$03,$04,$01,$02,$03,$04 ; An exceptional comment Copy TestLabel: LDA #$02 ; This label is on the same line as an instruction STA $01 BNE + NOP + RTS ; The code is indented according to "TestLabel", not "+" [PreviousGetting started](https://ersanio.gitbook.io/assembly-for-the-snes/getting-started) [NextHexadecimal](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/hexadecimal) Last updated 4 years ago --- # Jumping to subroutines | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/subroutine.md) . What if you want to use the same code twice, but you don't want to write, for example, the exact same 200 lines of codes again? You can turn code into "subroutines" and use jumping opcodes to run these subroutines. There are four jumping opcodes in total. There's a difference between "subroutine" and "code" in this tutorial, as you'll see in the coming paragraphs. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/subroutine#jsr-and-jsl) JSR and JSL ------------------------------------------------------------------------------------------------------- There are two opcodes which pretty much act like a function call in higher-level programming languages. Opcode Full name Explanation **JSR** Jump to subroutine Jumps to a subroutine using an absolute address **JSL** Jump to subroutine long Jumps to a subroutine using a long address These opcodes call a piece of code, then continue executing code below said opcode. Here is an example of the usage of JSR: Copy LDA #$01 STA $01 JSR Label1 ; Execute the "subroutine" located at Label1 (current bank) LDA #$03 ; The RTS in Label1 will return to this line STA $00 RTS Label1: LDA #$02 STA $02 RTS This code will store $01 into $7E0001, will store $03 into $7E0000 AND execute the codes at Label1 at the current bank, so it also stores $02 into $7E0002. JSL has the same purpose as JSR, except it can jump everywhere. Above example can also be applied to JSL, except that the `RTS` is now an `RTL`. The opcode JSR will get assembled as `JSR $XXXX` by the assembler (because labels get turned into addresses), but you shouldn't worry about that. Furthermore, because JSR uses an absolute address as its parameter, it is limited to its current bank. Changing the data bank register doesn't affect JSR. The opcode JSL will get assembled as `JSL $XXXXXX`, instead. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/subroutine#rts-and-rtl) RTS and RTL ------------------------------------------------------------------------------------------------------- When you call a subroutine, you also need a way to return back to your original code. For that, there are two return opcodes. Opcode Full name Explanation **RTS** Return from subroutine Returns from a JSR **RTL** Return from subroutine long Returns from a JSL Code called by JSR and JSL should end with RTS and RTL, respectively. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/subroutine#jmp-and-jml) JMP and JML ------------------------------------------------------------------------------------------------------- If you'd like to control the code flow rather than doing a function call, you will use regular jumps instead. Opcode Full name Explanation **JMP** Jump Jumps to code using an absolute address **JML** Jump long Jumps to code using a long address What JMP and JML do is jumping to another location and executing the code there, ignoring everything after the JMP/JML opcode. The RTS in Label1 does NOT jump back to LDA #$03. Instead, it just finishes the current subroutine it was in. Copy JML Label1 LDA #$03 ; Ignored STA $00 ; Ignored RTS ; Ignored Label1: STZ $00 RTS JMP is limited to the current bank, like JSR. JML can jump anywhere like JSL. JMPs and JMLs don't have a return instruction, but you can still use an RTS or RTL to return from a JMP/JMP depending on the current situation. Example: Copy LDA #$01 STA $00 JSR Label1 ; Jump to subroutine "Label1" RTS ; Label2 returns here. The code ends here. Label1: JMP Label2 ; Jump to Label2 Label2: RTS ; This DOESN'T return to Label1. Instead, it returns to the RTS above. It is good practice to put a blank line after a JMP or JML, so the reader knows that the program flow changes from that point on. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/subroutine#additional-notes) Additional notes ----------------------------------------------------------------------------------------------------------------- The Data Bank does NOT get updated when you use a JSL or JML. You will have to do that yourself. Remember how JSL/JML can jump everywhere? They can even jump to RAM, which implies code can be executed in RAM. You can write values to RAM which can be interpreted as code which can be executed. [PreviousComparing, branching, labels](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches) [NextTables and indexing](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/indexing) Last updated 4 years ago --- # Little-endian | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/endian.md) . Inside the SNES memory, 16-bit and 24-bit values are always stored in "little-endian". Take for example the value $1234 which we store in the RAM; $1234 does not appear as $12 $34. It appears as $34 $12, instead. This is how the SNES works. When this number is read in 16-bit mode, it reads $1234, NOT $3412. The SNES reverses this automatically again. 24-bit values are no exception. Values, such as $123456, are stored in the memory as $56 $34 $12. You can write everything in normal ASM without worrying about little-endian, because everything is dealt with automatically by the SNES and the assembler! You can worry about little-endian when you deal with 16-bit values in 8-bit mode. For example: if you ever store the value $1234 at address $7E0000, it is stored as $34 $12. Then, if you ever want to access the low byte of $1234 (which is $34), you would need to read $7E0000, NOT $7E0001. The concept of little-endian is especially important when dealing with "pointers", which is explained later in this tutorial. [PreviousAddressing modes](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing) [NextGlossary](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/glossary) Last updated 5 years ago --- # The processor flags | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags.md) . As you saw in the "8-bit and 16-bit mode" chapter earlier, the SNES can switch between 8-bit and 16-bit mode by using the opcodes REP and SEP. These affect the processor flags, which affect the behaviour of the SNES. Two of the processor flags are dedicated to A and X & Y being 8-bit or 16-bit mode. There are in total 9 processor flags stored in the processor flags register as a single byte: Copy Processor flags Bits: 7 6 5 4 3 2 1 0 |e├─── Emulation: 0 = Native Mode |n| |v| |m| |x| |d| |i| |z| |c| └┼───┼───┼───┼───┼───┼───┼───┼┘ │ │ │ │ │ │ │ └──────── Carry: 1 = Carry set │ │ │ │ │ │ └───────────── Zero: 1 = Result is zero │ │ │ │ │ └────────── IRQ Disable: 1 = Disabled │ │ │ │ └───────────── Decimal Mode: 1 = Decimal, 0 = Hexadecimal │ │ │ └──────── Index Register Select: 1 = 8-bit, 0 = 16-bit │ │ └─────────────── Accumulator Select: 1 = 8-bit, 0 = 16-bit │ └───────────────────────────── Overflow: 1 = Overflow set └───────────────────────────────── Negative: 1 = Negative set The processor flags are usually displayed as a single mnemonic in debuggers: "nvmxdizc". This chapter will explain all the processor flags in detail. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#negative-flag-n) Negative flag (n) ------------------------------------------------------------------------------------------------------------------------------- Most opcodes modify the negative flag depending on the results of that opcode. These opcodes generally are opcodes which are handled in the mathematics chapter, but also loads, comparisons and pulls. The negative flag doesn't affect the behaviour of the SNES. Rather, there are some branches which make use of the negative flag. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#overflow-flag-v) Overflow flag (v) ------------------------------------------------------------------------------------------------------------------------------- There are 3 opcodes which affect the overflow flag after a calculation: [`ADC`, `SBC`](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic) and [`BIT`](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic) . For detailed explanations of when the overflow flag is set, see the links. The overflow flag doesn't affect the behaviour of the SNES. Rather, there are some branches which make use of the overflow flag. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#memory-select-m) Memory select (m) ------------------------------------------------------------------------------------------------------------------------------- The memory select processor flag determines whether the accumulator is 8-bit or 16-bit. When it's set to 1, the accumulator is 8-bit. When it's set to 0, the accumulator is 16-bit. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#index-select-x) Index select (x) ----------------------------------------------------------------------------------------------------------------------------- The index select processor flag determines whether the X and Y registers are 8-bit or 16-bit. When it's set to 1, both X and Y are 8-bit. When it's set to 0, both X and Y are 16-bit. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#decimal-mode-flag-d) Decimal mode flag (d) --------------------------------------------------------------------------------------------------------------------------------------- Setting this flag to 1 means the SNES enters decimal mode. This _only_ affects the `ADC`, `SBC` and `CMP` opcodes. These opcodes adjust the accumulator on-the-fly. This means that, if you for example add `$03` to `$09`, the result is `$12` instead of `$0C`. Although decimal-mode math properly affects the carry flag and negative flag, it doesn't do this with the overflow flag. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#binary-coded-decimal) Binary-coded decimal Because numbers are treated as decimal, they are called "binary-coded decimal" (BCD). BCD is basically the same as hexadecimal, with the values $0A-0F, $1A-1F, etc. ignored. Here's a table showing how counting in BCD goes like: Binary Hexadecimal Decimal BCD %0000 0000 $00 #00 %0000 0000 %0000 0001 $01 #01 %0000 0001 %0000 0010 $02 #02 %0000 0010 %0000 0011 $03 #03 %0000 0011 %0000 0100 $04 #04 %0000 0100 %0000 0101 $05 #05 %0000 0101 %0000 0110 $06 #06 %0000 0110 %0000 0111 $07 #07 %0000 0111 %0000 1000 $08 #08 %0000 1000 %0000 1001 $09 #09 %0000 1001 %0000 1010 $0A Not available Not available %0000 1011 $0B Not available Not available %0000 1100 $0C Not available Not available %0000 1101 $0D Not available Not available %0000 1110 $0E Not available Not available %0000 1111 $0F Not available Not available %0001 0000 $10 #10 %0001 0000 %0001 0001 $11 #11 %0001 0001 %0001 0010 $12 #12 %0001 0010 ... ... ... ... %1001 1000 $98 #98 %1001 1000 %1001 1001 $99 #99 %1001 1001 %1001 1010 $9A Not available Not available %1001 1011 $9B Not available Not available ... ... ... ... In decimal mode, the SNES supports calculation results from `$00` to `$99`. In 16-bit A mode, this would be from `$0000` to `$9999`. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#interrupt-disable-flag-i) Interrupt disable flag (i) ------------------------------------------------------------------------------------------------------------------------------------------------- This flag determines whether the IRQ of the SNES is disabled or not. When it's set to 1, IRQ is disabled. When it's set to 0, IRQ is enabled. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#zero-flag-z) Zero flag (z) ----------------------------------------------------------------------------------------------------------------------- Most opcodes modify the zero flag depending on the results of that opcode. These opcodes generally are opcodes which are handled in the mathematics chapter, but also loads, comparisons and pulls. The zero flag doesn't affect the behaviour of the SNES. Rather, there are some branches which make use of the zero flag. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#carry-flag-c) Carry flag (c) ------------------------------------------------------------------------------------------------------------------------- The SNES supports math in the form of adding and subtracting numbers. It also supports bitwise operations such as bitshifting. The "carry flag" is a processor flag used for most of these arithmetic and bitshifting operations. Additionally, the carry flag is also used for branching. The carry flag is the same concept as the "carry" you learn in elementary school. In a typical pencil-and-paper addition, you'd write it out like this: Copy ¹ 27 + 59 ---- 86 7+9 equals 16, thus _carry_ the 1 to the left. Considering the carry is a "flag", when the carry flag is clear, the carry (C) will be 0. When it's set, the carry will be 1. You can safely assume that the carry flag is the "9th bit" of the A register when A is in 8-bit mode, and the "17th bit" when A is in 16-bit mode. Assuming A is in 8-bit mode, the carry flag will look like this: Copy AAAAAAAA C Where C is the Carry Flag and A are the bits of the A register. Depending on the carry flag, various mathematical and bit shifting instructions will behave differently. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#emulation-mode-flag-e) Emulation mode flag (e) ------------------------------------------------------------------------------------------------------------------------------------------- Setting this flag causes the 65c816 to behave as the 6502. When you enter emulation mode: * The stack pointer register's high byte remains static as $01 * The A, X and Y registers are always 8-bit * The program bank and data bank registers are set to $00 * The direct page register is initialized to $0000, and the high byte remains static as $00 The emulation mode of the 65c816 also fixes some of the bugs that the 6502 had. For example, indirect addressing mode `JMP` now wraps addresses properly. For example: `JMP ($10FF)` will now get the high byte from `$1100`, rather than `$1000`. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#processor-flags) Processor flags The emulation mode has a different set of processor flags. Copy Processor flags Bits: 7 6 5 4 3 2 1 0 |e├─── Emulation: 0 = Native Mode |n| |v| |1| |b| |d| |i| |z| |c| └┼───┼───┼───┼───┼───┼───┼───┼┘ │ │ │ │ │ │ │ └──────── Carry: 1 = Carry set │ │ │ │ │ │ └───────────── Zero: 1 = Result is zero │ │ │ │ │ └────────── IRQ Disable: 1 = Disabled │ │ │ │ └───────────── Decimal Mode: 1 = Decimal, 0 = Hexadecimal │ │ │ └─────────────────── Break Flag: 1 = Break executed │ │ └─────────────────────────── Unused: 1 │ └───────────────────────────── Overflow: 1 = Overflow set └───────────────────────────────── Negative: 1 = Negative set As you can see, it's very similar to the processor flags of the SNES, with a few exceptions. The A and X & Y memory select bits are replaced. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#unused-flag) Unused flag This processor flag is unused and is always set to 1. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags#break-flag) Break flag This processor flag is set to 1 when the emulation mode comes across a `BRK` opcode, thus it only indicates that there was a break; the processor flag doesn't actually affect the SNES. [PreviousCopying data](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves) [NextChanging the processor flags](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/repsep) Last updated 4 years ago --- # Transfers | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/transfer.md) . Imagine the following situation: You're using the index register X and you'd like to increase it by #$40. There's `INX` which increases the value in X by one, but what about increasing it by $40? There's no opcode which increases the value in X by $40, [but there is one](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic) which increases the value in A by $40. How do you get the value in X into A? There are a series of opcodes for exactly that purpose, and they're called "transfers". There are a bunch of transfer opcodes, all starting with the letter T. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/transfer#transferring-a-x-and-y) Transferring A, X and Y ----------------------------------------------------------------------------------------------------------------------------------------------- There are opcodes which transfers the values between A, X and Y registers. All the combinations are possible: Opcode Full name Explanation **TAX** Transfer A to X Copies over the value of A to X **TAY** Transfer A to Y Copies over the value of A to Y **TXA** Transfer X to A Copies over the value of X to A **TXY** Transfer X to Y Copies over the value of X to Y **TYA** Transfer Y to A Copies over the value of Y to A **TYX** Transfer Y to X Copies over the value of Y to X The opcodes are self-explanatory. Here's an example of a transfer: Copy LDA #$45 ; A = $45 LDY #$99 ; Y = $99 TAY ; A = $45, Y = $45 As you can see, the values are _copied_ over to the target register. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/transfer#id-8-bit-and-16-bit-mode) 8-bit and 16-bit mode ----------------------------------------------------------------------------------------------------------------------------------------------- Transfers work as you'd expect when the source and target registers are both 16-bit mode. You transfer a 16-bit value, like so: Copy LDA #$4512 ; A = $4512 LDY #$9900 ; Y = $9900 TAY ; A = $4512, Y = $4512 If you want to transfer an 8-bit register's value to a 16-bit register, the SNES looks at the target register's size, and transfers the value's low byte accordingly. Here's an example involving an 8-bit transfer to the 16-bit A register: Copy LDA #$4512 ; A = $4512 LDY #$99 ; Y = $0099 TYA ; A = $4599, Y = $0099 Here's an example involving a 16-bit transfer to the 8-bit A register: Copy LDA #$45 ; A = $xx45 LDY #$99AA ; Y = $99AA TYA ; A = $xxAA, Y = $99AA In the second example, the `xx` could be anything. Remember that [in the introduction of the A, X and Y registers](https://github.com/Ersanio/snes-assembly-book/tree/c7ff1077207f8a20e73f4ef3148c976dd96dcc25/docs/the-basics/register.md) , it's mentioned that the A register can actually be treated to be always 16-bit. The high byte isn't touched during the transfer, even if A is in 8-bit mode. If A was `$1245` before the transfer, it'll be `$12AA` after the transfer. This doesn't apply to X and Y, as their high bytes are immediately cleared when they exit 16-bit mode. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/transfer#transferring-general-registers) Transferring general registers -------------------------------------------------------------------------------------------------------------------------------------------------------------- There are other opcodes which involve the general SNES registers. Opcode Full name Explanation **TCD** Transfer A to direct page register Transfers the 16-bit value in A, to the direct page register, regardless of A being in 16-bit mode or not **TDC** Transfer direct page register to A Transfer the 16-bit value in the direct page register to the A register, regardless of A being in 16-bit mode or not **TCS** Transfer A to stack pointer register Transfers the 16-bit value in A to the stack pointer register, regardless of A being in 16-bit mode or not **TSC** Transfer stack pointer register to A Transfers the 16-bit value in the stack pointer register to A, regardless of A being in 16-bit mode or not **TXS** Transfer X to stack pointer register Transfers the 16-bit value in X to the stack pointer register, regardless of X being in 16-bit mode or not. Note that when X is in 8-bit mode, the high byte is always $00 **TSX** Transfer stack pointer register to X Transfers the 16-bit value in the stack pointer register to X. Note that when X is in 8-bit mode, the high byte stays $00 after the transfer [PreviousChanging the processor flags](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/repsep) [NextStack pointer register](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/stackpointer) Last updated 4 years ago --- # Addressing modes | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing.md) . There are different addressing modes in 65c816. Addressing modes are used to make opcodes access addresses and values differently, such as "indexed" or "direct indirect" (explained later in this tutorial). Using them wisely, you can access values and memory addresses in many ways. For example, you can immediately load a value into a register, such as A, or load a byte from the ROM into A. Keep in mind that not all the opcodes supports all the types of addressing modes. Here are some of the important addressing modes you'll find yourself use very often. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing#immediate-8-16-bit) Immediate 8/16 bit --------------------------------------------------------------------------------------------------------------------------- This addressing mode defines an absolute value, which is written as `#$XX` in 8-bit mode, or `#$XXXX` in 16-bit mode. The # means "immediate value", while the $ stands for hexadecimal. Using # alone makes the input decimal. For example, #10 is the same as #$0A. Think of an immediate value as a number you're directly defining. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing#direct-page) Direct page ------------------------------------------------------------------------------------------------------------- This addressing mode defines a direct page address, which is written as `$XX`. The direct page is the last 2 hex digits of a long address. For example, address $7E0011 as direct page would be $11. When loading from a direct page address, the bank byte is ALWAYS treated as $00, no exceptions. If you write "LDA $11" for example, you would load the contents of $000011 into the accumulator, which is also mirrored at $7E0011 (remember the illustration of the SNES memory). Therefor, you load $7E0011's contents into A. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing#absolute) Absolute ------------------------------------------------------------------------------------------------------- This addressing mode defines an absolute address, which is written as `$XXXX`. An absolute address is the last 4 hex digits of a long address. Using the example from earlier, address $7E0011 as an absolute address would be $0011. The bank byte of the absolute address is determined by the data bank register. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing#long) Long ----------------------------------------------------------------------------------------------- This addressing mode defines a long address, which is written as `$XXXXXX`. Long addresses deliver fewer complications when dealing with banks and mirroring. You also don't have to worry about what the data bank register currently contains. With long addresses, you can access any address in the SNES memory. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing#other-addressing-modes) Other addressing modes ----------------------------------------------------------------------------------------------------------------------------------- The SNES supports more addressing modes. The above addressing modes are the basics. There are also addressing modes, such as: indexed versions of direct page, absolute and long addresses, and much more. They will be explained near the end of this tutorial because you don't need them at this point. It would make things only more confusing at the moment. [PreviousThe SNES registers](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers) [NextLittle-endian](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/endian) Last updated 4 years ago --- # Shorter addresses | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/shorter-addresses.md) . It's possible to shorten addresses, but there are prerequisites. In order to shorten a long RAM address into an absolute (4-digit) address, the address has to be between $7E0000-$7E1FFF. $7E1234 can be shortened to $1234 for example. If you shorten address $7E2000 or higher into a 4-digit address, you'll write to areas other than the RAM. It has to do with the data bank register and the SNES memory map. If you want to shorten long RAM addresses to a direct page (2-digit) address, the high and low bytes of the long address must never exceed the value $00FF. The address you want to store to must be in bank $00 or $7E. So you can shorten `LDA $7E0001` to `LDA $01` and `STA $000001` to `STA $01`. It's often necessary to write shorter addresses as parameters, as certain opcodes don't support certain addressing modes. For example, the STZ opcode does not support long addresses, so you can't write `STZ $7E1234`. You'll have to write `STZ $1234` instead. Keep in mind that when you use 2 digits for loading and storing, the bank is always $00 by default, regardless of the data bank! You can use 2-digit addresses for RAM addresses $7E0000-$7E00FF, because RAM $7E0000-$7E1FFF is mirrored in banks $00-$3F by default. [PreviousLoading and storing](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/loading-and-storing) [Next8-bit and 16-bit mode](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/816) Last updated 4 years ago --- # Changing the processor flags | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/repsep.md) . As seen in the [previous chapter](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags) , the SNES supports 9 processor flags. There are ways to affect these processor flags, which are explained in this chapter. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/repsep#sep) SEP ------------------------------------------------------------------------------------------------------ Opcode Full name Explanation **SEP** Set processor flags Sets specified processor flags to 1 SEP sets the selected processor flag bits to 1. SEP is used as following: Copy ;nvmxdizc = 0000 0000 SEP #$80 ;= 1000 0000 ;Nvmxdizc = 1000 0000 The uppercased letters are the activated processor flags. This code sets the negative flag. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/repsep#rep) REP ------------------------------------------------------------------------------------------------------ Opcode Full name Explanation **REP** Reset processor flags Sets specified processor flags to 0 REP resets the selected processor flag bits to 0. REP is used as following: Copy ;NvmxDizc = 1000 1000 REP #$08 ;= 0000 1000 ;Nvmxdizc = 1000 0000 In the beginning, the decimal mode was enabled, and the negative flag was set, but after `REP #$08`, the decimal mode flag got disabled and the negative flag is still set. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/repsep#xce-and-the-emulation-mode) XCE and the emulation mode ---------------------------------------------------------------------------------------------------------------------------------------------------- Because the emulation mode bit is "hidden" above the carry flag, there's a dedicated opcode which exchanges the carry flag with the emulation mode flag. Opcode Full name Explanation **XCE** Exchange carry and emulation Swaps the values of carry flag and emulation mode flag You can access the emulation mode by using `SEC` then `XCE`, and leave it using `CLC` then `XCE`. By default, SNES starts up in emulation mode. This is why you often see the opcodes `CLC` and `XCE` among the very first opcodes in disassemblies. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/repsep#other-opcodes) Other opcodes -------------------------------------------------------------------------------------------------------------------------- There are other opcodes which immediately affect a single processor flag. Opcode Full name Explanation **SEI** Set interrupt disable flag Sets the interrupt disable flag, setting it to 1, thus disabling IRQ **CLI** Clear interrupt disable flag Clears the interrupt disable flag, setting it to 0, thus enabling IRQ **SED** Set decimal flag Sets the decimal mode flag, setting it to 1, changing the SNES into decimal mode **CLD** Clear decimal flag Clears the decimal flag, setting it to 0, changing the SNES into hexadecimal mode **SEC** Set carry flag Sets the carry flag, setting itt o 1 **CLC** Clear carry flag Clears the carry flag, setting it to 0 **CLV** Clear overflow flag Clears the overflow flag, setting it to 0 [PreviousThe processor flags](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags) [NextTransfers](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/transfer) Last updated 4 years ago --- # The SNES registers | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers.md) . In SNES, there are several "registers" used for different purposes. They cannot be missed; they're one of the reasons why the SNES can function properly. Basically, registers are "global variables" which can be used to hold values, or can be used for math and logic and all those fancy stuffs! These registers can be accessed anytime. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers#accumulator) Accumulator ------------------------------------------------------------------------------------------------------------ The accumulator, also known as **A**, is used for general math, bit shifts, bitwise operations and loading indirect values. A can also hold general-purpose variables to store things to the memory and other registers. This register can hold either an 8-bit or 16-bit value The accumulator sometimes is referred to as `B` or `C` in some opcodes. B means the high byte of the accumulator, while C means the full 16-bit accumulator. In reality, this register can be considered to be always 16-bit. When A is in 8-bit mode, you access the low byte of this register. When A is in 16-bit mode, you access both the high and low bytes of this register. The high byte doesn't get cleared when A enters 8-bit mode, even when new values are written to A, which is why the high byte can be considered to be "hidden". Also, certain instructions use both high and low bytes of the A register, regardless of whether A is in 8-bit or 16-bit mode. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers#indexers) Indexers ------------------------------------------------------------------------------------------------------ The indexers are two registers, known as **X** and **Y**. Even though they are separate registers, they have exactly the same purposes and behave exactly the same. These registers are made for indexing, explained later in this tutorial. These registers can also be 8-bit or 16-bit. X and Y can also hold general-purpose variables to store things to the memory and other registers. X and Y are "paired" – they can be 8-bit or 16-bit mode only at the same time. One of them can't be 8-bit while the other one is 16-bit. When X and Y leave 16-bit mode, their high bytes get cleared to the value $00, unlike the A register where the high byte remains intact. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers#direct-page) Direct Page ------------------------------------------------------------------------------------------------------------ The direct page register is a 16-bit register, used for the direct page addressing mode (explained later in this tutorial). When you access a memory address by its direct page notation, the value in the direct page is added to that address. You can generally ignore this register if you're just beginning with assembly. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers#stack-pointer) Stack Pointer ---------------------------------------------------------------------------------------------------------------- The stack pointer register is a 16-bit that holds the pointer to the stack in the RAM (explained later in this tutorial), relative to memory address $000000. The register dynamically changes, as you push and pull values to the stack (explained later in the tutorial). [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers#processor-status) Processor Status ---------------------------------------------------------------------------------------------------------------------- The processor status register holds the current processor flags in 8-bit format. There are 8 processor flags, and they all occupy one bit. Changing this register would alter the SNES behaviour greatly. Processor flags are explained later in this tutorial. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers#data-bank) Data bank -------------------------------------------------------------------------------------------------------- The data bank register holds the current data bank address as a single byte. When you access an address using the "absolute address" notation, the SNES will use this register to determine the bank of the address. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers#program-bank) Program bank -------------------------------------------------------------------------------------------------------------- The program bank register keeps track of the current bank of the currently executed instruction. So, if there is a code executed at address $018009, this register will hold the value $01. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers#program-counter) Program counter -------------------------------------------------------------------------------------------------------------------- This register keeps track of the high and low bytes of the address of the currently executed instruction. So, if there is an instruction executed at $018009, this register will hold the value $8009. [PreviousThe SNES memory](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/memory) [NextAddressing modes](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing) Last updated 4 years ago --- # Hardware math | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/math.md) . The SNES processor is capable of [basic multiplication and division by 2ⁿ](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift) , but if you'd like to multiply or divide by other numbers, you'll have to make use of certain SNES hardware registers. [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/math#hardware-unsigned-multiplication) Hardware Unsigned Multiplication ------------------------------------------------------------------------------------------------------------------------------------------------------- The SNES has a set of hardware registers used for unsigned multiplication: Register Access Description $4202 Write Multiplicand, 8-bit, unsigned. $4203 Write Multiplier, 8-bit, unsigned. Writing to this also starts the multiplication process. $4216 Read Unsigned multiply 16-bit product, low byte $4217 Read Unsigned multiply 16-bit product, high byte After you write to `$4203` to start the multiplication process, you will need to wait 8 [machine cycles](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cycles) , which is typically done by adding four `NOP` instructions to the code. If you don't wait 8 machine cycles, the results are unpredictable. Here's an example of `42 * 129 = 5418` (in hexadecimal: `$2A * $81 = $152A`): Copy LDA #$2A ; 42 STA $4202 LDA #$81 ; 129 STA $4203 NOP ; Wait 8 machine cycles NOP NOP NOP LDA $4216 ; A = $2A (result low byte) LDA $4217 ; A = $15 (result high byte) [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/math#hardware-signed-multiplication) Hardware Signed Multiplication --------------------------------------------------------------------------------------------------------------------------------------------------- There's a set of hardware registers which can be used for fast, signed multiplication: Register Access Description $211B Write twice Multiplicand, 16-bit, signed. First write: Low byte of multiplicand. Second write: High byte of multiplicand $211C Write Multiplier, 8-bit. $2134 Read Signed multiply 24-bit product, low byte $2135 Read Signed multiply 24-bit product, middle byte $2136 Read Signed multiply 24-bit product, high byte There's a catch to using these hardware registers, however, as they double as certain Mode 7 registers as well: * You can only use them for **signed** multiplication * The result is signed 24-bit, meaning the results range from `-8,388,608` to `8,388,607`. * The results are instant. That means you don't have to use `NOP` to wait for the results. * You cannot use them when Mode 7 graphics are being rendered on the screen. * This means that when Mode 7 is enabled, you can only use them inside NMI (V-blank). * This also means that you can use them without any restrictions, outside of Mode 7. Note that register `$211B` is "write twice". This means that you have to write an 8-bit value twice to this same register which in total makes up a 16-bit value. First, you write the low byte, then the high byte of the 16-bit value. Here's an example of `-30000 * 9 = -270000` (in hexadecimal: `$8AD0 * $09 = $FBE150`): Copy LDA #$D0 ; Low byte of $8AD0 STA $211B LDA #$8A ; High byte of $8AD0 STA $211B ; This sets up the multiplicand LDA #$09 ; $09 STA $211C ; This sets up multiplier LDA $2134 ; A = $50 (result low byte) LDA $2135 ; A = $E1 (result middle byte) LDA $2136 ; A = $FB (result high byte) ; (= $FBE150) [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/math#hardware-unsigned-division) Hardware Unsigned Division ------------------------------------------------------------------------------------------------------------------------------------------- The SNES has a set of hardware registers used for unsigned division. They are laid out as follows: Register Access Description $4204 Write Dividend, 16-bit, unsigned, low byte. $4205 Write Dividend, 16-bit, unsigned, high byte. $4206 Write Divisor, 8-bit, unsigned. Writing to this also starts the division process. $4214 Read Unsigned division 16-bit quotient, low byte $4215 Read Unsigned division 16-bit quotient, high byte $4216 Read Unsigned division remainder, low byte $4217 Read Unsigned division remainder, high byte Quotient means how many times the dividend can "fit" in the divisor. For example: `6 / 3 = 2`. Thus, the quotient is 2. Another way you can read this is: You can extract 3 **two** times from 6 and end up with exactly 0 as leftover. Modulo is an operation that determines the remainder of the dividend that couldn't "fit" into the divisor. For example: `8 / 3 = 2`. You can subtract 3 two times from 8, but in the end, you have a 2 as a remainder. Thus, the modulo for this operation is `2`. Because there are hardware registers that support remainders, the SNES also supports the modulo operation. After you write to `$4206` to start the division process, you will need to wait 16 [machine cycles](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cycles) , which is typically done by adding eight `NOP` instructions to the code. If you don't wait 16 machine cycles, the results are unpredictable. Here's an example of `256 / 2 = 128` (in hexadecimal: `$0100 / $02 = $0080`): Copy LDA #$00 STA $4204 LDA #$01 ; Write $0100 to dividend STA $4205 LDA #$02 ; Write $02 to divisor STA $4206 NOP ; Wait 16 machine cycles NOP NOP NOP NOP NOP NOP NOP LDA $4214 ; A = $80 (result low byte) LDA $4215 ; A = $00 (result high byte) LDA $4216 ; A = $00, as there are no remainders LDA $4217 ; A = $00, as there are no remainders Here's an example demonstrating modulo: `257 / 2 = 128, remainder 1` (in hexadecimal: `$0101 / $02 = $0080, remainder $0001`) Copy LDA #$01 STA $4204 LDA #$01 ; Write $0101 to dividend STA $4205 LDA #$02 ; Write $02 to divisor STA $4206 NOP ; Wait 16 machine cycles NOP NOP NOP NOP NOP NOP NOP LDA $4214 ; A = $80 (result low byte) LDA $4215 ; A = $00 (result high byte) LDA $4216 ; A = $01, as there is a remainder (remainder low byte) LDA $4217 ; A = $00 (remainder high byte) There is no hardware signed division. [PreviousBitwise operations](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic) [NextAddressing modes revisted](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing) Last updated 4 years ago --- # Bit shifting operations | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift.md) . "Bit shifting" is a bitwise operation in which you move bits left or right. As a result, practically speaking, you divide or multiply a number by two. [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift#asl-and-lsr) ASL and LSR -------------------------------------------------------------------------------------------------------------- There are two opcodes which shift bits to the left or right. Opcode Full name Explanation **ASL** A or memory shift left Moves bits left once without carry, practically multiplying a value by 2 **LSR** A or memory shift right Moves bits right once without carry, practically dividing a value by 2 ASL and LSR can affect either A or an address, just like INC and DEC. Here's an example of ASL in action: Copy LDA #$02 ; Load the value $02 into A ASL A ; Multiply A by 2 ; A is now $04 This is what appears on the surface. When you look at the numbers in binary though, this is what it looks like: Copy LDA #$02 ; Load the value %00000010 into A ASL A ; Shift bits left once ; A is now %00000100 As you can see, the digit "1" has moved to the left once. That is what ASL does, moving bits left. ASL can also move bits in an address without affecting A. Copy LDA #$02 ; Load the value $02 into A STA $00 ; Store this into address $7E0000 ASL $00 ; Shift bits of $7E0000 left once ; A is still $02, while $7E0000 is now $04 You can also shift bits to the right by using LSR. Copy LDA #$02 ; Load the value $02 into A LSR A ; Divide A by 2 ; A is now $01 And when you look at it on the level of binary, rather than hexadecimal: Copy LDA #$02 ; Load the value %00000010 into A LSR A ; Shift bits right once ; A is now %00000001 As you can see, the digit "1" has moved to the right once. That is what LSR does, moving bits right. LSR can also move bits in an address without affecting A. Copy LDA #$02 ; Load the value $02 into A STA $00 ; Store this into address $7E0000 LSR $00 ; Shift bits of $7E0000 right once ; A is still $02, while $7E0000 is now $01 ### [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift#edge-cases-and-carry-flag) Edge-cases and carry flag You might be wondering what happens if you bit shift `%1000 0001` to the right once, by using LSR. Bit 7 is currently set, but there's nothing to shift into bit 7. At the same time, bit 0 is also set, but it has nowhere to shift into. When this happens, bit 0 will move into the carry flag. At the same time, bit 7 will be set to 0. The opposite is also true when you shift `%1000 0001` to the left once, by using ASL. Bit 7 will move into the carry flag, while bit 0 will be set to 0. Here are some examples of a bit moving into the carry flag. Copy CLC ; Ensuring carry (C) = 0 LDA #$80 ; A = %1000 0000 | C = 0 ASL A ; A = %0000 0000 | C = 1 ASL A ; A = %0000 0000 | C = 0 Copy CLC ; C = 0 LDA #$01 ; A = %0000 0001 | C = 0 LSR A ; A = %0000 0000 | C = 1 LSR A ; A = %0000 0000 | C = 0 ### [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift#chaining-asl-and-lsr) Chaining ASL and LSR By chaining ASL and LSR, you can multiply or divide a value by 2 several times, therefore multiplying or dividing it by 2, 4, 8, 16, and so on. It's 2ⁿ, where n is the amount of ASL or LSR instructions you're chaining. Here's an example of multiplying a value by 8. Copy LDA #$07 ; A is now $07 = %0000 0111 ASL A ; A is now $0E = %0000 1110 ASL A ; A is now $1C = %0001 1100 ASL A ; A is now $38 = %0011 1000 ; This is basically $07 * 2³ Here's an example of dividing a value by 4 Copy LDA #$07 ; A is now $07 = %0000 0111 LSR A ; A is now $03 = %0000 0011 LSR A ; A is now $01 = %0000 0001 ; This is basically $07 / 2² [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift#rol-and-ror) ROL and ROR -------------------------------------------------------------------------------------------------------------- There are two opcodes which _rotate_ bits to the left or right, rather than shifting. Opcode Full name Explanation **ROL** Rotate A or memory left Moves bits left once with carry, wrapping the bits around **ROR** Rotate A or memory right Moves bits right once with carry, wrapping the bits around They behave the same as LSR and ASL, except they are using the carry flag as an extra bit in order to make the bits "wrap" around. This means that the value can't be "stuck" at $00 eventually, like it happens in ASL and LSR. This is why they're called rotate, rather than shift. Here's an example of ROL: Copy CLC ; Ensuring carry (C) = 0 LDA #$80 ; A = %1000 0000 | C = 0 ROL A ; A = %0000 0000 | C = 1 ROL A ; A = %0000 0001 | C = 0 ; A is now $01 As you can see, bit 7 wrapped all the way around to bit 0, basically "rotating" the bits without losing any information. Copy CLC ; C = 0 LDA #$01 ; A = %0000 0001 | C = 0 ROR A ; A = %0000 0000 | C = 1 ROR A ; A = %1000 0000 | C = 0 ; A is now $80 As you can see, bit 0 wrapped all the way around to bit 7, basically "rotating" the bits without losing any information. Copy SEC ; C = 1 LDA #$00 ; A = %0000 0000 | C = 1 ROL A ; A = %0000 0001 | C = 0 ; A is now $01 As you can see, you can set the carry flag and rotate. This will result in A being modified anyway, even though A started out as $00. Rotation can also affect addresses, just like ASL and LSR. Here's an example: Copy CLC ; Clear carry LDA #$02 ; Load the value $02 into A STA $00 ; Store this into address $7E0000 ROR $00 ; Shift bits of $7E0000 right once ; A is still $02, while $7E0000 is now $01 ; and carry is still cleared [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift#id-16-bit-mode-bit-shifting) 16-bit mode bit shifting ------------------------------------------------------------------------------------------------------------------------------------------- All the previous explanations and behaviours apply to 16-bit bit shifting as well. It's just that you're working with 16 bits and the carry flag, not 8 bits. [PreviousArithmetic operations](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic) [NextBitwise operations](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic) Last updated 4 years ago --- # Glossary | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/glossary.md) . Here are some definitions of the most commonly used terms throughout the tutorial. For the sake of context and continuity, it's best read from top to bottom. Terminology Definition SNES Super Nintendo Entertainment System Memory The working space in the SNES in which the ROM, RAM and SRAM are present ROM Read-only memory; the well-known .smc/.sfc/.fig files (W)RAM (Work) Random-access memory SRAM Static random-access memory; the well-known .srm files Register A variable in the SNES not part of the standard SNES memory Opcode A three-letter instruction; a mnemonic Addressing mode An optional parameter for an opcode denoting a value or an address Instruction/Operation The combination of an opcode and optionally an addressing mode Machine code An instruction assembled into bytes, which can be understood by processors Value A magnitude, quantity, or number; a number representing information Signed A value that semantically allows itself to be negative as well Unsigned A value that semantically allows itself to be positive-only, allowing for greater positive numbers Address A location in the memory of the SNES. Ranges from $000000 to $FFFFFF Long address An address represented by a 6-digit hexadecimal notation (e.g. $001200) Absolute address An address represented by a 4-digit hexadecimal notation of the final 4 digits (e.g. $1200) Direct page An address represented by a 2-digit hexadecimal notation of the final 2 digits (e.g. $00) Byte An 8-bit value Word A 16-bit value Long A 24-bit value Double A 32-bit value Bank byte The first two digits of a long address or a long value (e.g. "$12" in $**12**3456) High byte The middle two digits of a long address, absolute address, long value or word value (e.g. "$34" in both $12**34**56 and $**34**56) Low byte The final two digits of a long address, absolute address, long value or word value (e.g. "$56" in both $1234**56** and $34**56**) [PreviousLittle-endian](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/endian) [NextLoading and storing](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/loading-and-storing) Last updated 4 years ago --- # 8-bit and 16-bit mode | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/816.md) . The SNES is able to enter 8-bit or 16-bit modes. This means that the A, X and Y registers contain 16-bit values instead of 8-bit values. There are a few ways to switch between 8-bit and 16-bit modes. Here are all the possible manners: Operation Explanation REP #$10 Sets X and Y to 16-bit mode REP #$20 Sets A to 16-bit mode REP #$30 Sets A, X and Y to 16-bit mode SEP #$10 Sets X and Y to 8-bit mode SEP #$20 Sets A to 8-bit mode SEP #$30 Sets A, X and Y to 8-bit mode The opcodes REP and SEP are explained later in this tutorial. For now, this is all you need to know. In 16-bit A mode, the following features take effect: * When accessing the memory, you will involve 2 RAM addresses as opposed to 1. * Those two RAM addresses are always adjacent to each other. * Immediate values (#$) are now 16-bit. * Loaded and stored values are little-endian in the memory, but you don't have to worry about that at all. Let's begin with an example immediately: Copy REP #$20 LDA #$0001 STA $7E0000 SEP #$20 And breaking it down: Copy REP #$20 This sets the A register to 16-bit mode. Copy LDA #$0001 This will load the 16-bit value $0001 into A, so A now has the value $0001. If you write $01 instead of $0001, the code would most-likely crash! This is because the code expects a 16-bit parameter, but you only give it an 8-bit one. The code therefore takes the next opcode as part of the 16-bit parameter, causing the following opcodes to become bogus. Each opcode (disregarding the parameters) becomes an 8-bit value when assembled. This is why you only see hexadecimal values when you open a ROM in a hex editor. To disassemble these values into human-readable ASM, you'll need to use a "disassembler". Copy STA $7E0000 This will store the 16-bit A value into the RAM address $7E0000 AND $7E0001. Why two addresses? Because a 16-bit value won't fit into one address. Remember that an address represents an 8-bit value, so two addresses can represent a 16-bit one. $7E0000 and $7E0001 combined will now have the value $0001. Copy SEP #$20 Exits A from 16-bit mode. After executing these 4 instructions, if we take a peek into the RAM, we see will see something like this: Copy $7E0000 | [01] [00] [XX] [XX] [XX] […] The first value, $01, is the value of RAM address $7E0000. The second value belongs to $7E0001. The third value belongs to $7E0002, etc. As you can see, the stored value became little-endian, but normally we don't have to worry about this. If we try to load it back into A, we would have to use `LDA $7E0000`. It would load the value $0001 into A again if A is in 16-bit mode. If you tried to load it back when A was in 8-bit mode it would load the value $01 into A instead. There's a 16-bit X and Y mode too. This is not related to 16-bit A mode at all. If A is 8-bit mode and XY are 16-bit mode, the following is definitely possible: Copy LDA #$13 LDX #$4242 [PreviousShorter addresses](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/shorter-addresses) [NextComparing, branching, labels](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches) Last updated 4 years ago --- # Bitwise operations | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic.md) . Bitwise operations are fundamental when it comes to assembly. The 65c816 supports several bitwise opcodes, which are explained in this chapter. Bitwise operators mainly work on bits rather than bytes, so in this chapter, vague terms such as `x` and `y` refer to bits rather than bytes. In this chapter, we will sometimes refer to binary value 1 as `true` and binary value 0 as `false`. [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic#and) AND ---------------------------------------------------------------------------------------------- AND is an opcode which affects the accumulator by applying a logical AND on all bits. Opcode Full name Explanation **AND** Logical AND Logical AND, also known as a "`&`" in some other programming languages The result of `x AND y` is `true` if both `x` and `y` evaluate to `true`. Otherwise, the result is `false`. Here's an example of a logical AND. Copy LDA #$F0 ; A = 1111 0000 AND #$98 ; AND 1001 1000 ; A = 1001 0000 = $90 To understand this code, you'll have to read the comments from top to bottom. As you can see, the first line contains the accumulator's binary value, while the second line contains the AND operator's parameter binary value. When two 1 come together, the result is a 1 as well. This is what it means when both x and y evaluate to true. If x = 1 and y = 1, then the result is 1 also. This rule can be written in a "truth table". Compared bit AND operation Result 1 1 1 0 1 0 1 0 0 0 0 0 In short, whenever there's a 0 in A or in the AND value's bit, the resulting bit is also 0. [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic#ora) ORA ---------------------------------------------------------------------------------------------- ORA is an opcode which affects the accumulator by applying a logical OR on all bits. Opcode Full name Explanation **ORA** Logical OR Logical OR, also known as a pipe character in some other programming languages The result of `x OR y` is true if either `x` or `y` evaluates to `true`. Otherwise, the result is `false`. Here's an example of an ORA: Copy LDA #$F0 ; A = 1111 0000 ORA #$87 ; ORA 1000 0111 ; A = 1111 0111 = $F7 If one of the bits has 1, the resulting bit will be 1. After ORA, the result will be stored in A. Here's the truth table for ORA: Compared bit OR operation Result 1 1 1 0 1 1 1 0 1 0 0 0 So basically, whenever A or the ORA value's bit is 1, the resulting bit is also 1. [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic#eor) EOR ---------------------------------------------------------------------------------------------- EOR is an opcode which affects the accumulator by applying a logical exclusive OR (XOR) on all bits. Opcode Full name Explanation **EOR** Logical XOR Logical XOR, also known as a "`^`" in some other programming languages The result of `x XOR y` is `true` if `x` evaluates to `true` and `y` evaluates to `false`, or `x` evaluates to `false` and `y` evaluates to `true`. Otherwise, the result is `false`. Here's an example of an XOR: Copy LDA #$99 ; A = 1001 1001 EOR #$F0 ; EOR 1111 0000 ; A = 0110 1001 = $69 Basically, when the accumulator and the given value's bits are both 1, the result is 0. When they're both 0, the result is 0. When they're not equal, then the result is 1 also. Here's the truth table for EOR: Compared bit XOR operation Result 1 1 0 0 1 1 1 0 1 0 0 0 [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic#bit) BIT ---------------------------------------------------------------------------------------------- BIT is an opcode which essentially does a logical AND, but the result is NOT stored in the accumulator. Instead, it only affects the processor flags. Opcode Full name Explanation **BIT** Bit test Tests certain bits of the accumulator or memory Here's an example of BIT in usage: Copy LDA #$04 ; A = 0000 0100 = $04 BIT #$00 ; AND 0000 0000. None of the bits are set ; so normally, A should be $00, but it's still $04 ; However, the zero flag has been set because ; the outcome *should* be $00. BIT has a feature which distinguishes it from a regular AND, involving processor flags other than the zero flag. When you use BIT on an address, rather than the accumulator, it can also affect the "negative" and "overflow" processor flags. If bit 7 of the address' value is set, the negative flag would be set as a result. If bit 6 of the address' value is set, the overflow flag would be set as a result. Here's an example of using BIT on an address: Copy BIT $04 ; Bit test $7E0004's value If $7E0004's value was $80 (1000 0000), the negative flag would be set and the overflow flag would be clear. It's useful to check really fast if the value of an address is negative. If $7E0004's value was $40 (0100 0000), the negative flag would be clear and the overflow flag would be set. Useful to check if specifically bit 6 is set. If $7E0004's value was $C0 (1100 0000), both the negative and overflow flags would be set. Coincidentally enough, the bits for negative (bit 7) and overflow (bit 6) correspond to the bits in the processor flag register: `nvmxdizc`. Set bits BIT result Bit 7 Negative flag set Bit 6 Overflow flag set Bits 7, 6 Negative & overflow flags set When you are performing a BIT operation on a RAM address, the N and V flags will be set or cleared, regardless of the value in the accumulator. The zero flag depends on the accumulator's value and the RAM address' value. So, BIT with a RAM address does both AND, and an inevitable check of bits 7 and 6 of the RAM address. [PreviousBit shifting operations](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift) [NextHardware math](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/math) Last updated 4 years ago --- # Comparing, branching, labels | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches.md) . You can run certain pieces of code depending on certain conditions. For this, you'll have to make use of comparison and branching opcodes. Comparison opcodes compare the contents of A, X or Y with another value. A branching opcode then controls the program flow, depending on the comparison. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches#branches) Branches ----------------------------------------------------------------------------------------------- Branches are opcodes which control the flow of the code depending on the outcome of comparisons. Branches jump to a **label**. The branching opcodes have a range of -128 to 127 bytes. This means they can either jump 128 bytes backwards, or they can jump 127 bytes forward, relative to the program counter. One exception is BRL (Branch Long). BRL has a range of 32768 bytes (8000 in hex), which is a whole bank. If the branch goes out of range, the assembler gives an error. You'll have to find a way to put the destination label into the branch's reach. The "tips and tricks" chapter covers this. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches#labels) Labels ------------------------------------------------------------------------------------------- Labels are basically text placed in code to locate an entry point of a jump or a "table". Labels are no opcodes or anything. It's basically an easier way to specify an offset/address, because the labels get turned into numbers by the assembler. It is good practice to give labels meaningful names, for your own sake. Examples codes in this chapter will make use of labels. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches#cmp) CMP ------------------------------------------------------------------------------------- To make comparisons, you usually compare the contents of A with something else. The primary way for that is the opcode `CMP`. Opcode Full name Explanation **CMP** Compare A Compares A with something else CMP takes whatever is in A, and compares it with a specified parameter. After using a CMP instruction, you need to use an opcode that will perform the type of "branch" that you wish to occur. It's also possible to compare 16-bit values. Just change `CMP #$xx` to `CMP #$xxxx`. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches#beq-and-bne) BEQ and BNE ----------------------------------------------------------------------------------------------------- There are branch opcodes which branch depending on if a value equals or doesn't equal. Opcode Full name Explanation **BEQ** Branch if equals Branches if the comparison equals with the compared value **BNE** Branch if not equals Branches if the comparison doesn't equal with the compared value BEQ branches if the comparison is equal with the compared value. Here's an example: Copy LDA $00 ; Loads the current value of RAM address $7E0000 into A CMP #$02 ; Compares A with the immediate value $02 BEQ Label1 ; If A = $02, go to the codes in Label1. NOTE: Case-Sensitive LDA #$01 ; \ Else STA $1245 ; / Store value $01 into RAM $7E1245. RTS ; This instruction is used to end a routine. Label1: STZ $19 ; Store zero in $7E0019 RTS ; End. This code will store zero ($00) in $7E0019 when $7E0000 contains the value $02. If it doesn't have $02 as its value, the code will then store value $01 in $7E1245. As you can see, BEQ will "jump" to a portion of the code when compared values are equal, skipping certain code. In this case, the code jumps to the code located at the label "Label1". BNE branches if the comparison doesn't equal with the compared value. Here's an example: Copy LDA $00 ; Loads the current value of RAM address $7E0000 into A CMP #$02 ; Compares A with $02 BNE Label1 ; A = NOT $02, finish the code, do nothing. LDA #$01 ; \ Else STA $1245 ; / Store something in RAM $7E1245 Label1: ; RTS ; End. This code will store $01 to $7E1245, if $7E0000 has the value $02. If RAM address $7E0000 doesn't have the value $02, the code will instead do nothing and simply return. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches#comparing-addresses) Comparing addresses --------------------------------------------------------------------------------------------------------------------- You can also compare RAM addresses with each other. For example: Copy LDA $00 ; Load $7E0000's value into A CMP $02 ; Compare A with $7E0002 BEQ Equal ; Branch if equal When RAM addresses $7E0000 and $7E0002 have the same values, the branch will be taken. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches#cpx-and-cpy) CPX and CPY ----------------------------------------------------------------------------------------------------- You can also compare values by using the registers X and Y. Opcode Full name Explanation **CPX** Compare X Compares X with something else **CPY** Compare Y Compares Y with something else It's not just A which is capable of doing comparisons. For example, you can load a value into X or Y and compare it with something else. Here's an example using X: Copy LDX $00 ; Load $7E0000's value into X CPX $02 ; Compare X with $7E0002 BEQ Equal ; Branch if equal It will have the same result as the example with comparing addresses. You can compare Y too by using CPY. However, you cannot mix registers. The the following is wrong: Copy LDX $00 CMP $02 BEQ Equal CMP $02 would try to compare address $7E0002 with the register A, instead of X. This will cause unexpected results. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches#bmi-and-bpl) BMI and BPL ----------------------------------------------------------------------------------------------------- These are branch opcodes which branch depending on if a value is signed or unsigned. Opcode Full name Explanation **BMI** Branch if minus Branches if the last operation resulted in a negative value (thus, negative flag set) **BPL** Branch if plus Branches if the last operation resulted in a positive value (thus, negative flag clear) BMI branches if the last operation is a minus/negative value. Minus values are the values $80-$FF. BPL branches if the last operation is not a minus value; it branches when the value is $00-$7F. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches#bcs-and-bcc) BCS and BCC ----------------------------------------------------------------------------------------------------- These are branch opcodes which branch depending on if a value is greater than or less than. Opcode Full name Explanation **BCS** Branch if carry set Basically branches if the loaded value is greater than or equal to the compared value (thus, carry flag set) **BCC** Branch if carry clear Basically branches if the loaded value is less than the compared value (thus, carry flag clear) BCS branches if the loaded value is equal or greater than the compared value. Alternatively, this also branches when the carry flag is set. BCC branches if the loaded value is lesser than the compared value. Alternatively, this also branches when the carry flag is clear. Please note that this BCC doesn't get taken if the compared value is equal, unlike BCS. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches#bvs-and-bvc) BVS and BVC ----------------------------------------------------------------------------------------------------- These are branch opcodes which branch depending on if a value results in a mathematical overflow or not, thus, when the overflow flag is set or clear. Opcode Full name Explanation **BVS** Branch if overflow set Branches if the comparison causes a mathematical overflow (thus, overflow flag set) **BVC** Branch if overflow clear Branches if the comparison doesn't cause a mathematical overflow (thus, overflow flag clear) The "overflow" flag is a processor flag, explained later in the tutorial. [](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches#bra-and-brl) BRA and BRL ----------------------------------------------------------------------------------------------------- These are unconditional branches which are always taken. Opcode Full name Explanation **BRA** Branch always Always branches **BRL** Branch always long Always branches, but with greater reach BRA will ALWAYS branch; it doesn't even check for conditions. BRL does the same, but it has a longer reach, enough to cover half a bank for each direction. [Previous8-bit and 16-bit mode](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/816) [NextJumping to subroutines](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/subroutine) Last updated 4 years ago --- # Tables and indexing | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/indexing.md) . Indexing is the act of accessing a collection of data from a certain offset, that offset being determined by an indexer. The registers X and Y are unmissable when it comes to indexing. They're called the "index" registers, after all. [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/indexing#tables) Tables ----------------------------------------------------------------------------------------------------- "Tables" are simply a sequence of values. They are used in at least two cases: * Conditional cases (e.g. a collection of speed values, depending on the entity's direction) * Values that represent an "asset" (e.g. graphics data, music data, level data) Tables have very practical applications in SNES games. You can, for example, store text data in tables. Or level data. Or palette data. Tables can contain anything and can have any size, as long as they fit within the ROM. There are four types of values which you can use to write tables: Instruction Full name Explanation **db** direct byte A value denoting a byte (8-bit value, e.g. $XX) **dw** direct word A value denoting a word (16-bit value, e.g. $XXXX) **dl** direct long A value denoting a long (24-bit value, e.g. $XXXXXX) **dd** direct double A value denoting a double (32-bit value, e.g. $XXXXXXXX) Here's an example of a table definition: Copy ValuesTableExample: db $11,$86,$91,$38,$22 As you can see, we first define the value type as "byte" by writing `db`. Then, we follow with byte-values, seperated by a comma (,). In order to access a table, we always use a label. In this case, we used the label `ValuesTableExample`. [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/indexing#indexing) Indexing --------------------------------------------------------------------------------------------------------- Tables can be accessed using a label. Labels are translated into ROM addresses, after all. Expanding on above table example, the following code would read out a value from the table: Copy LDA ValuesTableExample STA $00 RTS ValuesTableExample: db $11,$86,$91,$38,$22 However, what this code does is always reading the first value in that table, the value $11, and storing it into RAM $7E0000. This is because we haven't used any indexer. Copy LDY #$03 ; Y is now loaded with the number $03 LDA ValuesTableExample,y ;Load a number from the table into A, using Y as index STA $00 RTS ValuesTableExample: db $11,$86,$91,$38,$22 The code will load a value from the table into A. Basically, this does `LDA ValuesTableExample`, and the value in Y, which is $03, is added to the address. In other words, in higher-level programming languages it'd be something like `ValuesTableExample[3]`. You start counting index from **zero**. Index 0 of that table has the value $11, index 1 has the value $86, and so on. Index 3 has the value $38 in this case, so this code example actually loads $38 in A, and stores it in RAM $7E0000. Indexing is quite useful when you don't want to write very repetitive instructions all the time. Indexing can be performed with the X register too. X and Y behave exactly the same, after all. Indexing is actually one of the many addressing modes. The basic addressing modes are covered [here](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing) and the more advanced addressing modes [here](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing) . [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/indexing#indexing-with-ram) Indexing with RAM --------------------------------------------------------------------------------------------------------------------------- So far, the above examples were about ROM. You can also index values in RAM. You can treat the RAM as one giant table you could index. You simply replace the label with an address. For example: Copy LDX #$03 LDA $7E1000,x STA $00 In this case, the LDA would resolve into address `$7E1003`. The code loads $7E1003's value into A, and stores it into $7E0000. [PreviousJumping to subroutines](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/subroutine) [NextThe stack](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack) Last updated 4 years ago --- # Copying data | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves.md) . 65c816 assembly has two opcodes dedicated to moving huge blocks of data from one memory location to another. Opcode Full name Explanation **MVN** Move block negative Moves a block of data, byte by byte, starting from the beginning and working towards the end **MVP** Move block positive Moves a block of data, byte by byte, starting from the end and working towards the beginning MVP and MVN practically do a mass amount of LDA and STA to some RAM addresses. You can't move data to ROM, because ROM is read-only. It's highly recommended to have the A, X and Y registers at 16-bit mode. It's also highly recommended to preserve the data bank using the stack, as the move opcodes implicitly change this. Here's an example of a proper block move setup: Copy PHB ; Preserve data bank REP #$30 ; 16-bit AXY ; ← Move instructions are located here SEP #$30 ; 8-bit AXY PLB ; Recover data bank [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves#mvn) MVN -------------------------------------------------------------------------------------------- When using MVN, the three main registers all have a special purpose. Register Purpose A Specifies the amount of bytes to transfer, plus 1 X Specifies the high and low bytes of the data source memory address Y Specifies the high and low bytes of the destination memory address The A register is "plus 1". This means that if you want to move 4 bytes of data, you load $0003, as this means $0003+1, thus 4 bytes. MVN can be written in two ways: Copy MVN $xxyy ; or MVN $yy, $xx Where `xx` is the source bank, and `yy` is the destination bank. When executing the MVN opcode, the SNES stalls at that same opcode for each byte transferred. When a byte is transferred, the following happens: Register Event A Decreases by 1 X Increases by 1 Y Increases by 1 Data bank Is set to the bank of the destination address Seeing that A decreases by 1, eventually it will reach the value $0000, then it'll wrap to $FFFF. Once that wrap happens, the block move finishes and the SNES proceeds to execute the opcodes that follow. Here's an example of a block move: Copy PHB ; Preserve data bank REP #$30 ; 16-bit AXY LDA #$0004 ; \ LDX #$8908 ; | LDY #$A000 ; | Move 5 bytes of data from $1F8908 to $7FA000 MVN $7F, $1F ; / SEP #$30 ; 8-bit AXY PLB ; Recover data bank This example will move 5 bytes of data from address $1F8098 to $7FA000. [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves#mvp) MVP -------------------------------------------------------------------------------------------- When using MVP, the three main registers all have a special purpose. Register Purpose A Specifies the amount of bytes to transfer, plus 1 X Specifies the high and low bytes of the data source memory address Y Specifies the high and low bytes of the destination memory address The A register is "plus 1". This means that if you want to move 4 bytes of data, you load $0003, as this means $0003+1, thus 4 bytes. MVP can be written in two ways: Copy MVP $xxyy ; or MVP $yy, $xx Where `xx` is the source bank, and `yy` is the destination bank. Note that the `MVP/MVN $yy, $xx` notation is indeed written as `MVP/MVN destination, source`. Various sources indicate that it should be the other way around (`source, destination`), however, this tutorial follows Asar's assembly syntax. When executing the MVP opcode, the SNES loops at that same opcode for each byte transferred. From this point on, this is where MVP differs from MVN. When a byte is transferred, the following happens: Register Event A Decreases by 1 X Decreases by 1 Y Decreases by 1 Data bank Is set to the bank of the destination address Considering that X and Y decrease, rather than increase, this means that MVP moves blocks of data from the end towards the beginning. Here's an example of a block move: Copy PHB ; Preserve data bank REP #$30 ; 16-bit AXY LDA #$0004 ; \ LDX #$8908 ; | LDY #$A000 ; | Move 5 bytes of data from ($1F8908-$0004) to ($7FA000-$0004) MVP $7F, $1F ; / SEP #$30 ; 8-bit AXY PLB ; Recover data bank This example will move 5 bytes of data from address $1F8904 to $7F9FFC. Although the transfer happens backwards, the transferred data isn't reversed. It still copies over as you'd expect. [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves#edge-cases) Edge cases ---------------------------------------------------------------------------------------------------------- * When you set the A register to $0000, it means you will move 1 byte. * When you set the A register to $FFFF, it means you will move 65536 bytes. * When either the source or destination addresses cross a bank boundary, the high and low bytes reset to $0000, while the data bank remains unchanged. [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves#easy-notation) Easy notation ---------------------------------------------------------------------------------------------------------------- Asar supports labels as parameters for LDA, LDX and LDY, so you can write block moves without having to calculate the source table size or address locations. The following examples allow for tables of all sizes, and assume the destination to be memory address $7FA000. An example for MVN: Copy PHB REP #$30 LDA.w #SomeTable_end-SomeTable-$01 LDX.w #SomeTable LDY #$A000 MVN $7F, SomeTable>>16 SEP #$30 PLB RTS SomeTable: db $00,$01,$02,$03,$04 .end An example for MVP: Copy PHB REP #$30 LDA.w #SomeTable_end-SomeTable-$01 LDX.w #SomeTable+SomeTable_end-SomeTable-$01 LDY.w #$A000+SomeTable_end-SomeTable-$01 MVP $7F, SomeTable>>16 SEP #$30 PLB RTS SomeTable: db $00,$01,$02,$03,$04 .end As you can see, MVP is considerably more complicated to setup. [PreviousThe stack](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack) [NextThe processor flags](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags) Last updated 4 years ago --- # Miscellaneous opcodes | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc.md) . This chapter explains opcodes which don't really fit in any other chapter. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc#nop) NOP --------------------------------------------------------------------------------- Opcode Full name Explanation **NOP** No operation Does absolutely nothing It is often used to disable existing opcodes in a ROM without shifting around the machine code. It can also be used to give time for the [math hardware registers](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/math) to do their work. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc#xba) XBA --------------------------------------------------------------------------------- Opcode Full name Explanation **XBA** Exchange B and A Swaps the high and the low bytes of the A register, regardless of the register size. Here's an example: Copy LDA #$0231 ; A = $0231 XBA ; A is now $3102 This even works with 8-bit A, as the high byte of A is "hidden" rather than set to $00: Copy LDA #$12 ; A = $xx12 XBA ; A = $12xx LDA #$05 ; A = $1205 [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc#wai) WAI --------------------------------------------------------------------------------- Opcode Full name Explanation **WAI** Wait for interrupt Halts the SNES CPU until either an NMI or IRQ occurs. Exactly what it says, it halts the SNES CPU until either an NMI or IRQ (both are interrupts) occurs. Practically speaking, the opcode loops itself infinitely until an interrupt occurs. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc#stp) STP --------------------------------------------------------------------------------- Opcode Full name Explanation **STP** Stop the clock The "clock" being the SNES CPU in this case, it halts the CPU completely until either a soft or hard reset occurs. Exactly what it says, it stops the SNES CPU until you hit reset or power cycle the SNES. It does lower the power consumption of the SNES, if the couple of cents it'll shave off your power bill means that much to you. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc#brk) BRK --------------------------------------------------------------------------------- Opcode Full name Explanation **BRK** Software break Causes a break to occur This opcode causes the SNES to jump to the [break vector](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector) . It also takes a byte parameter. Example usage: Copy BRK #$02 The parameter is not used for anything in particular, but if you write a meaningful "catch" to the BRK, you could probably read what the value of the BRK was supposed to be, and do certain things depending on the value. When the BRK opcode is executed, the following events happen: * First, the (24-bit) address of the instruction after `BRK #$xx` is pushed onto the stack * Then, the (8-bit) processor flag register is pushed onto the stack. * The interrupt disable flag is set (akin to `SEI`) * The decimal mode flag is cleared (akin to `CLD`) * The program bank register is cleared to $00 * The program counter is loaded from the break vector. In other words, the code jumps to the address at the break vector. If the emulators are made properly, the opcode BRK makes debuggers snap. You could also program the break vector to do meaningful things. In fact, on SMWCentral, [p4plus2 released a patch which does exactly this](https://www.smwcentral.net/?p=section&a=details&id=20796) ; it shows debug information about the crash. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc#cop) COP --------------------------------------------------------------------------------- Opcode Full name Explanation **COP** Coprocessor empowerment Similar effects as BRK, as the SNES has no coprocessor to empower. This opcode causes the SNES to jump to the [COP hardware vector](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector) . It also takes a byte parameter. Example usage: Copy COP #$04 The parameter is not used for anything in particular, but if you write a meaningful "catch" to the COP, you could probably read what the value of the COP was supposed to be, and do certain things depending on the value. When the COP opcode is executed, the following events happen: * First, the (24-bit) address of the instruction after `COP #$xx` is pushed onto the stack * Then, the (8-bit) processor flag register is pushed onto the stack. * The interrupt disable flag is set (akin to `SEI`) * The decimal mode flag is cleared (akin to `CLD`) * The program bank register is cleared to $00 * The program counter is loaded from the COP hardware vector. In other words, the code jumps to the address at the COP hardware vector. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc#wdm) WDM --------------------------------------------------------------------------------- Opcode Full name Explanation **WDM** Reserved for future expansion Does absolutely nothing WDM stands for "[William (Bill) David Mensch, Jr.](https://en.wikipedia.org/wiki/Bill_Mensch) ", who designed the 65c816. This opcode was reserved for the possibility of multi-byte opcodes. Therefore, this opcode actually takes a parameter. Example: Copy WDM #$01 This opcode has the same effect as NOP however. Therefore, it does nothing. [PreviousAddressing modes revisted](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing) [NextMachine cycles](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cycles) Last updated 4 years ago --- # Hardware vectors | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector.md) . Hardware vectors are a group of pointers that define how the SNES should handle certain interrupts within the ROM. Each vector is two bytes in size and point to instructions located in bank `$00`. The hardware vectors are grouped by the [two modes of the SNES](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags) : "emulation mode" vectors and "native mode" vectors. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector#native-mode-vectors) Native mode vectors ------------------------------------------------------------------------------------------------------------------- These vectors come into play when the SNES runs in native mode (E=0). ROM address Vector Additional notes `$00:FFE0` Unused `$00:FFE2` Unused `$00:FFE4` COP vector Triggered by the [COP opcode](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc) `$00:FFE6` BRK vector Triggered by the [BRK opcode](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc) `$00:FFE8` ABORT vector Unused by the SNES `$00:FFEA` NMI vector Triggered by the SNES V-Blank Interrupt `$00:FFEC` Unused `$00:FFEE` IRQ vector Triggered by the SNES H/V-Timer or external interrupt [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector#emulation-mode-vectors) Emulation mode vectors ------------------------------------------------------------------------------------------------------------------------- These vectors come into play when the SNES runs in emulation mode (E=1). ROM address Vector Additional notes `$00:FFF0` Unused `$00:FFF2` Unused `$00:FFF4` COP vector `$00:FFF6` Unused `$00:FFF8` ABORT vector Unused by the SNES `$00:FFFA` NMI vector `$00:FFFC` RESET vector Triggered by SNES soft/hard reset `$00:FFFE` IRQ/BRK vector It's possible to differentiate between the two using the [BRK flag](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags) [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector#example-setup) Example setup ------------------------------------------------------------------------------------------------------- Here's an example setup with the most commonly used vectors, which can be used in homebrew SNES ROMs. Note that the labels must be located in bank `$00` (which is located within the same bank as this setup). Copy ORG $00FFE0 ; Native mode dw $FFFF ; Unused dw $FFFF ; Unused dw $FFFF ; COP dw $FFFF ; BRK dw $FFFF ; ABORT dw NativeNMI ; NMI dw $FFFF ; Unused dw NativeIRQ ; IRQ ; Emulation mode dw $FFFF ; Unused dw $FFFF ; Unused dw $FFFF ; COP dw $FFFF ; Unused dw $FFFF ; ABORT dw $FFFF ; NMI dw Reset ; RESET dw $FFFF ; IRQ/BRK Because homebrew programmers generally want to run their program in native mode, all vectors in emulation mode, save for reset, have been ignored. Furthermore, nothing is done with the `BRK` and `COP` vectors as these are opcodes which generally are unused. [PreviousMachine cycles](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cycles) [NextTechniques](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques) Last updated 5 years ago --- # Machine cycles | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cycles.md) . The SNES processes instructions, but each instruction takes up a predetermined amount of time to execute. The time an instruction takes to execute is called "machine cycle" (or "cycle" in short). Each instruction has its own cycle. [This page](https://wiki.superfamicom.org/65816-reference) has a full reference of how many cycles each instruction takes. Pay attention to the footnotes, as the amount of used cycles can differ depending on the context of the code. For example, a taken branch takes 1 cycle longer compared to a branch that's not taken. The less cycles, the less slowdown the code suffers from. Slowdown is often noticeable in games with many sprites on the screen. To avoid slowdown, you need to write efficient code. Here is an example of inefficient vs. efficient code: Copy ; Inefficient LDA #$00 ; 2 cycles STA $7E0000 ; 5 cycles ; = 7 cycles ; Efficient LDA #$00 ; 2 cycles STA $00 ; 3 cycles ; = 5 cycles ; Very efficient STZ $00 ; 3 cycles ; = 3 cycles At first, we use a full notation to write the value $00 to address $7E0000. But then, in the next example, we shorten the address, saving 2 cycles. Finally, we figure that we can use `STZ`, as we store zero to an address anyway. Having a low cycle count is especially important when executing code during an NMI, because there are limited machine cycles there. Exceeding that limit causes black scanlines to flicker on the top of the SNES display. [PreviousMiscellaneous opcodes](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc) [NextHardware vectors](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector) Last updated 4 years ago --- # The stack | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack.md) . The stack is a special type of "table" which is located inside the SNES RAM. Imagine the stack as a stack of plates. You can only add (push) or remove (pull/pop) a plate from the top. You can visualize it as something like this: Copy | | |[ ]| |[ ]| |[ ]| |[ ]| ‾‾‾‾‾ The empty "boxes" in above example are basically the values inside the stack, and they can hold any value. The collection of boxes are closed off from all sides, except from the top. Technically speaking, because the stack is an area inside the RAM, you can access any value you want, using special stack-relative addressing modes rather than using only push and pull opcodes. For the purposes of this chapter, we'll assume the stack indeed is a perfect stack of plates. [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack#push) Push ---------------------------------------------------------------------------------------------- "Pushing" is the act of adding a value on top of the stack. Here's an example: Copy [$32] | push v | | |[$32]| |[$B0]| |[$B0]| |[$A9]| > |[$A9]| |[$82]| |[$B2]| |[$14]| |[$14]| ‾‾‾‾‾‾‾ ‾‾‾‾‾‾‾ As you can see, the value $32 is added on top of the stack. The stack now has five values instead of four. [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack#pull-pop) Pull/Pop ------------------------------------------------------------------------------------------------------ "Pulling" (or "popping") is the act of reading a value from the top of the stack. Here's an example: Copy [$32] ^ pull | |[$32]| | | |[$B0]| |[$B0]| |[$A9]| > |[$A9]| |[$82]| |[$B2]| |[$14]| |[$14]| ‾‾‾‾‾‾‾ ‾‾‾‾‾‾‾ As you can see, the value $32 is retrieved from the top of the stack. _All_ pulling instructions affect the Negative and the Zero processor flags. [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack#push-a-x-y) Push A, X, Y ------------------------------------------------------------------------------------------------------------ There are opcodes to push the current value inside the A, X or Y registers onto the stack: Opcode Full name Explanation **PHA** Push A onto stack Pushes the current value of A onto the stack **PHX** Push X onto stack Pushes the current value of X onto the stack **PHY** Push Y onto stack Pushes the current value of Y onto the stack [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack#pull-a-x-y) Pull A, X, Y ------------------------------------------------------------------------------------------------------------ There are also opcodes to pull the current value from the stack into the A, X or Y registers: Opcode Full name Explanation **PLA** Pull into A Pulls a value from the stack into the A register **PLX** Pull into X Pulls a value from the stack into the X register **PLY** Pull into Y Pulls a value from the stack into the Y register [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack#example) Example ---------------------------------------------------------------------------------------------------- Imagine the following scenario: The X register has to stay at the value $19, no matter what, but you really have to use X for something else. How would one do that? You use PHX to preserve the value in X in the stack, before you use an instruction which modifies the contents of X: Copy ; Imagine X has the value $19 in the stack PHX ; Push X ($19) onto stack. Result: Stack 1st value = $19 LDX $91 ; Load the value in address $7E0091 into X LDA $1000,x ; \ X is now modified, and we use it to index RAM STA $0100 ; / PLX ; Restore X. X is now $19 again The A, X and Y registers do not have a separate stack. There is only one stack, specified by the "stack pointer register". For detailed explanations about the stack pointer register, see: [Stack pointer register](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/stackpointer) [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack#id-16-bit-mode-stack-operations) 16-bit mode stack operations ------------------------------------------------------------------------------------------------------------------------------------------------- All the previous explanations and behaviours apply to 16-bit stack operations as well. It's just that you're pushing and pulling 16-bit values, not 8-bit values. This also means that if you push two 8-bit values onto the stack, you can pull a single 16-bit value later. [](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack#other-push-and-pull-operations) Other push and pull operations -------------------------------------------------------------------------------------------------------------------------------------------------- There are also other push and pull commands, which are not affected by 8-bit or 16-bit mode A, X and Y: Opcode Full name Explanation Value size **PHB** Push data bank register Pushes the data bank register's current value onto the stack 8-bit **PLB** Pull data bank register Pulls a value from the stack into the data bank register 8-bit **PHD** Push direct page register Pushes the direct page register's current value onto the stack 16-bit **PLD** Pull direct page register Pulls a value from the stack into the direct page register 16-bit **PHP** Push processor flags Pushes the processor flags register's current value onto the stack 8-bit **PLP** Pull processor flags Pulls a value from the stack into the processor flag register 8-bit **PHK** Push program bank Pushes the bank value of the currently executed opcode (which is PHK) onto the stack 8-bit **PEA $XXXX** Push effective address Pushes the specified 16-bit value onto the stack. Don't let the name of the opcode mislead you. This doesn't read out any address 16-bit **PEI ($XX)** Push effective indirect address Pushes the value at the given direct page address onto the stack. The direct page register can affect the given address 16-bit **PER** _**label**_ Push program counter relative A rather complicated push. In short: it pushes the absolute address of the given label onto the stack. What happens during assembly is that the assembler calculates the relative distance between PER and the given label. This relative distance is a signed 16-bit value and is used as the parameter for PER. The opcode finally assembles into `PER $XXXX`. Because of its relative nature, the opcode can also be used in the SNES RAM 16-bit [PreviousTables and indexing](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/indexing) [NextCopying data](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves) Last updated 4 years ago --- # Stack pointer register | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/stackpointer.md) . The stack pointer register is a 16-bit register which the processor uses to determine the current stack location in the SNES RAM. After each push, the stack pointer _decreases_, so the bytes are pushed backwards. They overwrite the RAM address' contents. Conversely, after each pull, the stack pointer _increases_, but the pulled value still remains in the stack. Therefore, a pull is actually a read. The stack pointer increases or decreases by 1 when it deals with an 8-bit value, and by 2 when it deals with a 16-bit value. This can be summarized as follows: Operation Where Stack pointer modification Push (8-bit) Stack pointer location \-1 Pull (8-bit) Stack pointer location +1 +1 Push (16-bit) Stack pointer location \-2 Pull (16-bit) Stack pointer location +1 +2 The stack pointer register assumes that it works with bank $00, regardless of the current value of the data bank register. This means that from the SNES memory mapping point of view, if the stack pointer ever starts pointing to absolute address $8000 or above, it'll start pointing to the ROM. If it points to absolute address $2000 or above, it'll start pointing to the SNES hardware registers. Therefore, the only useable stack area is $000000-$001FFF. The stack doesn't have a defined size. Instead, you, as a programmer, just reserve an area of RAM for the stack you think you need. The reason it doesn't have a defined size is because as long as you keep pushing, the stack pointer keeps decreasing without any set limits. If you push too many values, you might accidentally overwrite other RAM addresses which have other predetermined purposes. [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/stackpointer#push) Push -------------------------------------------------------------------------------------------------------------- Here is an example of how the stack works from the RAM's point of view, when you push an 8-bit value: Copy ;Stack: .. 55 55 55 55 55 55 .. LDA #$42 └─Stack pointer points to this value PHA ;Stack: .. 55 55 55 55 55 42 .. LDA #$AA └─Stack pointer now points to this value PHA ;Stack: .. 55 55 55 55 AA 42 .. └─Stack pointer now points to this value Here is an example of a 16-bit push: Copy REP #$20 ;16-bit A mode ;Stack: .. 55 55 55 55 55 55 .. LDA #$4210 └─Stack pointer points to this value PHA ;Stack: .. 55 55 55 55 10 42 .. LDA #$AA99 └─Stack pointer now points to this value PHA ;Stack: .. 55 55 99 AA 10 42 .. └─Stack pointer now points to this value [](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/stackpointer#pull) Pull -------------------------------------------------------------------------------------------------------------- When you pull something from the stack, it gets pulled from the location of the stack pointer, +1. After each pull, the stack pointer increases depending on the size of the pulled value. You do not literally extract the byte out of the RAM. You just copy the byte to the register you pull it into. The byte in the stack does not reset or anything. It remains the same. Here's an example of pulling an 8-bit value: Copy ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer points to this value PLA ; A is now $34 ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer now points to this value PLA ; A is now $56 ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer now points to this value Here's an example of pulling a 16-bit value: Copy REP #$20 ;16-bit A mode ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer points to this value PLA ; A is now $3412 ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer now points to this value PLA ; A is now $7856 ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer now points to this value [PreviousTransfers](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/transfer) [NextArithmetic operations](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic) Last updated 4 years ago --- # Arithmetic operations | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic.md) . At some point, you would probably want to _increase_ RAM address $7E000F by $01, but a simple LDA and STA won't work, because this simply changes the RAM Address' contents to $01 – not increase it by one. Maybe you'd like to increase it by 2 instead, or even multiply by 2. The SNES has a few instructions capable of doing basic math operations. [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic#inc-and-dec) INC and DEC ------------------------------------------------------------------------------------------------------------------- These are opcodes to increase or decrease a value by 1. Opcode Full name Explanation **INC** Increase Increases accumulator or memory by 1 **DEC** Decrease Decreases accumulator or memory by 1 INC and DEC support both the accumulator as well as addresses. Here's an example of increasing the accumulator by 1: Copy LDA #$02 ; Load the value $02 into the accumulator INC A ; Increase the accumulator by 1. It is now $03 Here's an example to decrease the accumulator by 1: Copy LDA #$02 ; Load the value $02 into the accumulator DEC A ; Decrease the accumulator by 1. It is now $01 Here's an example to increase the value of an address by 1, without affecting the accumulator at all: Copy INC $0F ; Increase the value in $7E000F by one RTS ; Return. A remains unchanged Here's also an example to decrease a value by one Copy DEC $0F ; Decrease the value in $7E000F by one RTS ; Return. A remains unchanged There are also edge cases with these operations. When you use INC when the value that is being modified is currently $FF, it would result in a $00 and the zero flag being set. When you use DEC and the value that is being modified is currently $00, it would result in a $FF. INC and DEC don't work with long addressing modes. They only work with absolute or direct page addressing modes. Therefore, instructions like `INC $7E000F` do not exist. Instead, you should use `INC $000F` or `INC $0F`. Why isn't there a long addressing mode? The processor is simply made that way, so you'll have to deal with it, one way or another. [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic#inx-dex-iny-and-dey) INX, DEX, INY and DEY ------------------------------------------------------------------------------------------------------------------------------------- There are also instructions to increase or decrease the value inside the index registers: Opcode Full name Explanation **INX** Increase X Increases X by 1 **DEX** Decrease X Decreases X by 1 **INY** Increase Y Increases Y by 1 **DEY** Decrease Y Decreases Y by 1 You cannot use the above 4 opcodes to manipulate an address. They are solely used for the X and the Y registers. [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic#adc-and-sbc) ADC and SBC ------------------------------------------------------------------------------------------------------------------- If you wanted to increase or decrease a value by, say, 95, you wouldn't want to write INC or DEC 95 times. Thankfully, the SNES has instructions for such situations as well. Opcode Full name Explanation **ADC** Add with carry Adds a given value to A **SBC** Subtract with carry Subtracts a given value from A Just for emphasis: ADC adds a value to the accumulator, not a RAM address. SBC subtracts a value from the accumulator. With ADC and SBC, you can do add and subtract operations on addresses. Here's an example which adds 4 to the value of an address: Copy LDA $0F ; Load the address value in A CLC ; Clear carry flag ADC #$04 ; What you will add to A. It is $04 in this case. STA $0F ; Store A in $0F Because ADC modifies A rather than addresses, you must first load the address value into A, then do the addition, then store the result back into the address. If the carry flag wasn't cleared, the addition would've been $05 instead of $04, depending on the state of the carry flag at that moment. Therefore, it's important to be extra sure about the carry flag and just clear it with a `CLC`, before the ADC. The opposite is also true for subtracting: Copy LDA $0F ; Load the current value in A SEC ; Set Carry Flag SBC #$04 ; How many you will subtract from A. It is $04 in this case. STA $0F ; Store A in $0F This will subtract 4 from the RAM address' content (`$0F-#$04`). You'll notice that for subtracting, we set the carry flag rather than clear it. If you didn't set the carry flag, it would subtract $05 instead of $04. This might seem backwards, but it's just how the processor works. In short: when adding, use `CLC`. When subtracting, use `SEC`. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic#carry-flag) Carry flag When you use ADC, and the value in A wraps from $FF to $00, the carry flag will be set. This is also true for 16-bit A, when the value wraps from $FFFF to $0000. When you use SBC, and the value in A wraps from $00 to $FF, the carry flag will be cleared. This is also true for 16-bit A, when the value wraps from $0000 to $FFFF. With this, you can use the carry flag to check if some sort of value wrapping has occured. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic#overflow-flag) Overflow flag The opcodes ADC and SBC are unique in the sense that they're two of the three opcodes which can affect the _signed overflow_ processor flag as a result of a calculation. The overflow flag is especially relevant when you decide to treat values as signed values. Remember the hexadecimal chapter with the signed and unsigned values? Values `$00-$7F` are considered positive, and values `$80-$FF` are considered negative. The overflow flag is set when the result of an operation doesn't make sense in the math world: * Adding a negative value to a negative value, and getting a positive value as a result, when it should be negative * Adding a positive value to a positive value, and getting a negative value as a result, when it should be positive * Subtracting a positive value from a negative value, and getting a positive value as a result, when the negative value should be even more negative * Subtracting a negative value from a positive value, and getting a negative value as a result, when the value should be even more positive Math rules are at play here. Adding two negative numbers results in a negative number (e.g. `-10 + -1 = -11`). Subtracting two negative numbers results in a negative number (e.g. `-10 - -1 = -9`). Subtracting a negative value equals an addition (e.g. `10 - -1 = 11`). Adding a negative value equals a subtraction (e.g. `10 + -1 = 9`). The scenarios described in those bullet points break these rules. Here are some examples of the overflow flag getting set: Copy LDA #$88 ; Number -120 CLC ; We do -120 + -16, which should result in -136 ADC #$F0 ; $88 + $F0 = $78, which is 120, which doesn't make sense mathematically Copy LDA #$80 ; Number -128 SEC ; We do -128 - 16, which should result in -144 SBC #$10 ; $80 - $10 = $70, which is 112, which doesn't make sense mathematically Copy LDA #$30 ; Number 48 CLC ; We do 48 + 112, which should result in 160 ADC #$70 ; $30 + $70 = $A0, which is -96, which doesn't make sense mathematically Copy LDA #$10 ; Number 16 SEC ; We do 16 - -128, which should result in 144 SBC #$80 ; $10 - $80 = $90, which is -112, which doesn't make sense mathematically [](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic#id-16-bit-mode-math) 16-bit mode math -------------------------------------------------------------------------------------------------------------------------------- All the previous explanations and behaviours apply to 16-bit math as well. [PreviousStack pointer register](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/stackpointer) [NextBit shifting operations](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift) Last updated 4 years ago --- # Addressing modes revisted | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing.md) . In the [basic addressing modes](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing) chapter, we briefly looked at the most commonly used addressing modes: "Immediate 8-bit and 16-bit", "direct page", "absolute" and "long". In this chapter, we will look at the more advanced addressing modes: "indirect", "indexed" and "stack relative". These advanced addressing modes expand upon the earlier introduced addressing modes. This chapter also introduces the concept of pointers. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#pointers) Pointers ------------------------------------------------------------------------------------------------- This is where "pointers" come into play. Pointers are values which pretty much "point" to a certain memory location. Imagine the following SNES memory: Copy ; $7E0000: 12 80 00 55 55 55 .. In this example: the RAM at address $7E0000 contains the values $12, $80 and $00. This is in little-endian, so reverse the values and you get $00, $80, $12. Treat this as a 24-bit "long" address and you have $008012. This means that RAM address $7E0000 has a 24-bit pointer to $008012. This is what "indirect" is; accessing address $7E0000 "indirectly" accesses address $008012. You can access indirect pointers in two ways: 16-bits and 24-bits. They have a special assembler syntax: Syntax Terminology Pointer size ( ) Indirect 16-bit \[ \] Indirect long 24-bit The bank byte of the 16-bit pointer depends on the type of instruction. When it comes to a `JSR`\-opcode, which can only jump inside the current bank, the bank byte isn"t determined nor used. However, when it comes to a loading instruction, such as `LDA`, the bank byte is determined by the _data bank register_. Pointers can point to both _code_ and _data_. Depending on the type of instruction, the pointers are accessed differently. For example, a `JSR` which utilizes a 16-bit pointer accesses the pointed location as code, while an `LDA` accesses the pointed location as a bank. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#indirect) Indirect ------------------------------------------------------------------------------------------------- Indirect addressing modes are basically accessing addresses in such a way, that you access the address they point to, rather than directly accessing the contents of the specified address. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#direct-indirect) Direct, Indirect As contradicting as it may sound, the naming actually makes sense. "Direct" stands for direct page addressing mode, while "indirect" means that we're accessing a pointer at the direct page address, rather than a value. Here's an example: Copy ; Setup indirect pointer REP #$20 LDA #$1FFF ; $1FFF to RAM $7E0000 STA $00 SEP #$20 ; $7E0000: FF 1F .. .. .. .. .. ; Access indirect pointer LDA ($00) ; Load the value at address $1FFF into A This accesses a 16-bit pointer at address $000000, due to the nature of direct page always accessing bank $00. Due to the effects of mirroring in the SNES mirroring, practically speaking, this accesses a 16-bit pointer at RAM $7E0000. You might think that `LDA ($00)` loads the `value $1FFF` into A. However, it doesn't work that way. It loads the `value in address $1FFF` into A, because we use an indirect addressing mode. As we established earlier, parentheses denote 16-bit pointers. The bank of the indirect address, in the case of an LDA, depends on the data bank register. As a result, the `LDA ($00)` resolves into `LDA $1FFF`. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#direct-indexed-with-x-indirect) Direct Indexed with X, Indirect As is the case with the previous addressing mode, the naming may seem contradicting. "Direct" stands for direct page addressing mode. "Indexed with X" means that this direct page address is indexed with X. "Indirect" means that the previous elements are treated as an indirect address. Here's an example usage: Copy ; Setup indirect pointers REP #$20 LDA #$1FFF ; $1FFF to RAM $7E0000 STA $00 LDA #$0FFF ; $0FFF to RAM $7E0002 STA $02 SEP #$20 ; $7E0000: FF 1F FF 0F .. .. .. ; Access indirect pointer LDX #$02 ; Set X to $02 LDA ($00,x) ; Loads the value at address $0FFF into A * The 16-bit value in address $7E0000 + $7E0001 is `$1FFF`. * The 16-bit value in address $7E0002 + $7E0003 is `$0FFF`. Thanks to using X as an indexer to the direct page address, `LDA ($00,x)` is resolved into `LDA ($02)`. This then resolves into `LDA $0FFF` because RAM $7E0002 points to `address $0FFF`. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#direct-indirect-indexed-with-y) Direct, Indirect Indexed with Y This is practically the same as `Direct, Indirect` but the pointer is then indexed with the Y register: Copy ; Setup indirect pointer REP #$20 LDA #$1FF0 ; $1FF0 to RAM $7E0000 STA $00 SEP #$20 ; $7E0000: F0 1F .. .. .. .. .. ; Access indirect pointer LDY #$01 ; Set Y to $01 LDA ($00),y ; Load value at address $1FF1 into A As a result, `LDA ($00),y` resolves into `LDA $1FF0,y`, which then resolves into `LDA $1FF1` because Y contains the value $01. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#absolute-indirect) Absolute, Indirect Exactly the same as `Direct, Indirect`, except the specified address is now 16-bit instead of 8-bit. This addressing mode is only used by jumping instructions. Example: Copy ; Setup indirect pointer REP #$20 LDA #$8000 ; $8000 to RAM $7E0000 STA $00 SEP #$20 ; $7E0000: 00 80 .. .. .. .. .. ; Access indirect pointer JMP ($0000) ; Jumps to $8000. This has the same exact effect as the example in `Direct, Indirect`. As a result, the `JMP ($0000)` resolves into `JMP $8000`, thus jumps to `address $8000` in the current bank. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#absolute-indexed-with-x-indirect) Absolute Indexed with X, Indirect Exactly the same as `Direct Indexed with X, Indirect`, except the specified address is now 16-bit instead of 8-bit. This addressing mode is only used by jumping instructions. Example: Copy REP #$20 LDA #$8000 ; $8000 to RAM $7E0000 STA $00 LDA #$9000 ; $9000 to RAM $7E0002 STA $02 SEP #$20 ; $7E0000: 00 80 00 90 .. .. .. ; Access indirect pointer LDX #$02 ; Set X to $02 JMP ($0000,x) ; Jumps to $9000 * The 16-bit value in address $7E0000 + $7E0001 is `$8000`. * The 16-bit value in address $7E0002 + $7E0003 is `$9000`. Thanks to using X as an indexer to the direct page address, `JMP ($0000,x)` is resolved into `JMP ($0002)`. This then resolves into `JMP $9000` because RAM $7E0002 points to `address $9000`. This addressing mode is handy for jump tables. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#direct-indirect-long) Direct, Indirect Long Exactly the same as `Direct, Indirect`, except the pointer located at an address is now 24-bits instead of 16-bits, meaning the bank byte of a pointer is also specified. Example: Copy REP #$20 LDA #$1FFF ; $1FFF to RAM $7E0000 STA $00 SEP #$20 LDX #$7F ; $7F to RAM $7E0002 STX $02 ; $7E0000 now contains the 24-bit pointer $7F1FFF ; $7E0000: FF 1F 7F .. .. .. .. LDA [$00] ; Load the value at address $7F1FFF into A `LDA [$00]` resolves into `LDA $7F1FFF`. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#direct-indirect-indexed-long-with-y) Direct, Indirect Indexed Long with Y Exactly the same as `Direct, Indirect Indexed with Y`, except the pointer located at an address is now 24-bits instead of 16-bits, meaning the bank byte of a pointer is also specified. Example: Copy ; Setup indirect pointer REP #$20 LDA #$1FF0 ; $1FF0 to RAM $7E0000 STA $00 SEP #$20 LDX #$7F ; $7F to RAM $7E0002 STX $02 ; $7E0000 now contains the 24-bit pointer $7F1FF0 ; $7E0000: F0 1F 7F .. .. .. .. ; Access indirect pointer LDY #$01 ; Set Y to $01 LDA [$00],y ; Load value at address $7F1FF1 into A As a result, `LDA [$00],y` resolves into `LDA $7F1FF0,y` (practically speaking), which then resolves into `LDA $7F1FF1` because Y contains the value $01. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#indexed) Indexed ----------------------------------------------------------------------------------------------- Indexed addressing mode was actually briefly touched upon in an [earlier chapter](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/indexing) . This chapter will discuss all the possible indexed addressing modes. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#direct-indexed-with-x) Direct, Indexed with X This addressing mode indexes a direct page address with X. Example: Copy LDX #$02 LDA $00,x ; Loads the value at address $7E0002 into A ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#direct-indexed-with-y) Direct, Indexed with Y This addressing mode indexes a direct page address with Y. This addressing mode only exists on the `LDX` and `STX`\-opcodes. Example: Copy LDY #$02 LDX $00,y ; Loads the value at address $7E0002 into X ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#absolute-indexed-with-x) Absolute, Indexed with X This addressing mode indexes an absolute address with X. Example: Copy LDX #$02 LDA $0000,x ; Loads the value at address $7E0002 into A ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#absolute-indexed-with-y) Absolute, Indexed with Y This addressing mode indexes an absolute address with Y. Example: Copy LDY #$02 LDA $0000,y ; Loads the value at address $7E0002 into A ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#absolute-long-indexed-with-x) Absolute, Long Indexed with X This addressing mode indexes a long address with X. Example: Copy LDX #$02 LDA $7E0000,x ; Loads the value at address $7E0002 into A [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#stack-relative) Stack Relative ------------------------------------------------------------------------------------------------------------- Stack relative is a special type of an indexing addressing mode. It uses the stack pointer register as a 16-bit index, rather than using the X or Y register. The index is **always** 16-bit, regardless of the register size of A, X and Y. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#stack-relative-1) Stack Relative This loads a value from the RAM, relative to the stack pointer. The bank byte is always $00. Example: Copy ; Stack pointer register: $1FF0 LDA $00,s ; ($001FF0) Loads the value in the current free slot in the stack, into A. LDA $01,s ; ($001FF1) Loads the last pushed value into A. LDA $02,s ; ($001FF2) Loads the second last pushed value into A. LDA $03,s ; ($001FF3) ... In 16-bit A mode, these instructions would read 16-bit values, rather than 8-bit values. If you don't use increments of 2, you'll start reading overlapping values. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing#stack-relative-indirect-indexed-with-y) Stack Relative, Indirect Indexed with Y This is pretty much the same as `Direct, Indirect Indexed with Y`, except the value is loaded from a stack relative address. Example: Copy ; Stack pointer register = $01FD REP #$20 LDA #$0100 PHA SEP #$20 LDY #$03 LDA ($01,s),y ; → LDA ($01FE),y → LDA $0100,y → LDA $0103 `$01,s` refers to the last pushed value into A, which is $0100 in the case of this example. The parentheses applies on this stack relative address, resolving the instruction to an `LDA $0100,y`. This finally resolves into `LDA $0103` because of the indexer. This addressing mode is handy if you'd like to treat certain pushed values as an indexed memory address. [PreviousHardware math](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/math) [NextMiscellaneous opcodes](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc) Last updated 4 years ago --- # Programming cautions | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cautions.md) . When you're coding, you will have to keep an eye out on (common) mistakes. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cautions#confusing-8-bit-values-with-16-bit-values) Confusing 8-bit values with 16-bit values ----------------------------------------------------------------------------------------------------------------------------------------------------------------- Don't try to load an 8-bit value into AXY when AXY is in 16-bit mode, and the other way around. For example, don't write `LDA #$0000` when A is in 8-bit mode, as the third byte of this opcode will be interpreted as an opcode rather than a value. Consequence(s): The game will most likely crash by interpreting instructions you never wrote. Fixing the issue: Use the correct value size. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cautions#looping-conditions) Looping conditions ------------------------------------------------------------------------------------------------------------------- When creating loops, don't make a small mistake which results in an infinite loop (a loop which doesn't exit). Consequence(s): The game will lock up; the only way to exit is to reset the SNES. Fixing the issue: Check at the end of the loop (often a comparison) to see why it doesn't allow the loop to exit. You might need a debugger for this. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cautions#bank-boundaries-and-edge-cases) Bank boundaries and edge cases ------------------------------------------------------------------------------------------------------------------------------------------- Make sure your code doesn't cross bank-boundaries ($XX:FFFF → $XX:0000) inside the ROM. Consequence(s): The SNES would read bogus instructions and most likely crash. Fixing the issue: The code should remain within a bank. If your code doesn't fit inside a bank, you should split up your code across banks and make use of the `JSL` and `JML` instructions. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cautions#pushes-and-pulls) Pushes and pulls --------------------------------------------------------------------------------------------------------------- Make sure you pull the same amount of bytes as you have pushed, before a return instruction (RTS, RTL). Consequence(s): If you don't, the SNES won't get the right return address from the stack and most likely crash. Fixing the issue: Keep the amounts of pushes and pulls at an equilibrium. Especially keep an eye out on different A, X and Y modes (8-bit and 16-bit), as pushing in 16-bit mode means pulling twice in 8-bit mode. The other way around is also true. [PreviousCommon assembler syntax](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax) Last updated 4 years ago --- # Techniques | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques.md) . This chapter focuses on general techniques you can use in SNES ASM. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#byte-counting) Byte counting ----------------------------------------------------------------------------------------------------------- Each instruction assembles into one or more bytes. This happens as follows: First, the opcode is guaranteed assembled into a byte. Then the byte is followed by 0-3 bytes which serves as the parameter of the opcode. Every 2 hex digits equals 1 byte. This means that the maximum amount of bytes an instruction in SNES can use, is four bytes: opcode + a 24-bit parameter. Here is an example on how LDA would look like, when it's assembled with different addressing modes: Copy LDA #$00 ; A9 00 LDA #$0011 ; A9 11 00 LDA $00 ; A5 00 LDA $0011 ; AD 11 00 LDA $001122 ; AF 22 11 00 An instruction without a hexadecimal parameter is only 1 byte, like `INC A` or `TAX`. An instruction with an 8-bit parameter is 2 bytes, like `LDA #$00`. An instruction with a 16-bit parameter is 3 bytes, like `LDA $0000`. An instruction with a 24-bit parameter is 4 bytes, like `LDA $000000`. It doesn't matter if the addressing mode is indexed, direct indirect or something else. It all depends on the length of the `$`\-value. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#shorthand-zero-comparison) Shorthand zero comparison ----------------------------------------------------------------------------------------------------------------------------------- Faster comparison makes use of processor flags effectively, because branches actually depend on the processor flags. As mentioned in this tutorial earlier, `BEQ` branches if the zero flag is set, `BNE` branches if the zero flag is clear, `BCC` branches if the carry flag is clear, and so on. Often, if the result of ​any​ operation is zero (`$00`, or `$0000` in 16-bit mode), the zero flag gets set. For example, if you do `LDA #$00`, the zero flag is set. Nearly all opcodes which modify an address or register affect the zero flag. By making use of the zero flag, it's possible to check if a certain instruction has zero as its result (including loads). This way, you can write shorthand methods to check if an address actually contains the value `$00` or `$0000`. Here's an example: Copy LDA $59 BEQ IsZero LDA #$01 STA $02 IsZero: RTS This code checks if address $7E0059 contains the value `$00`. If it does contain this value, then it immediately returns. If it does not contain the value `$00`, then it stores the value `$01` into address $7E0002. The reverse is also possible: checking if an address does _not_ contain zero. here's an example: Copy LDA $59 BNE IsNotZero LDA #$01 STA $02 IsNotZero: RTS This code checks if address $7E0059 does _not_ contain the value `$00`. If it indeed does not contain this value, then it immediately returns. If it does contain the value `$00`, then it stores the value `$01` into address $7E0002, instead. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#looping) Looping ----------------------------------------------------------------------------------------------- Loops are certain type of code flow which allow you to execute code repeatedly. It is especially useful when you need to read out a table or a memory region value-by-value. A practical example is reading out level data from the SNES ROM, in order to build a playable level. Levels are essentially a long list of object and sprite data, therefore, this data can be read out in a repetetive way, until you reach the end of such data. The examples within this section only loops through tables to copy them to RAM addresses, but of course, loops can be used to implement much more complex logic. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#looping-with-a-loop-counter-check) Looping with a loop counter check It's possible to loop from the beginning to the end of a table. The following code demonstrates this. Copy LDX.b #$00 ; Initialize the loop counter - LDA Table,x ; Reads out the values in the table STA $00,x ; Store them to addresses $7E0000-$7E0003 INX ; Increase loop counter by one CPX.b #Table_end-Table ; Use the size of the table as the check BNE - ; If the loop counter doesn't equal this, then continue looping. RTS Table: db $01,$02,$04,$08 ; The values are read out in order .end The code initializes the loop counter with the value `$00`. With every iteration, this loop counter increases by one, and is then compared with the size of the table. Therefore, the loop executes for every byte within this table. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#looping-with-a-negative-check) Looping with a negative check While it's possible to loop from the beginning towards the end of a table, it's also possible to loop from the end towards the beginning of the table. This method has a "shorthand" check in order to see if the loop should be terminated, by making clever use of the "negative" processor flag. When using this method, your loop will essentially run backwards. Copy LDX.b #Table_end-Table-1 ; Get the length of the table ($03). The -1 is necessary, - LDA Table,x ; else the loop will be off-by-one. Use that as a loop counter. STA $00,x ; One by one store the values to addresses $7E0003 to $7E0000, in that order. DEX ; Decrease the loop counter by one. BPL - ; If the loop counter isn't negative, continue looping. RTS Table: db $01,$02,$04,$08 ; The values are read out backwards as well .end Because the loop counter serves as an index to address $7E0000, as well as the table, it reads and stores the values in a backwards order, as the counter starts with the value `$03`. The `BPL` ensures that the loop breaks once the loop counter reaches the value `$FF`. That means the negative flag will be set, thus BPL will not branch to the beginning of the loop. This loop method has a drawback however. When the loop counter is `$81-$FF`, the loop will execute once, then break immediately, as BPL will not branch. This is because after `DEX`, the loop counter is immediately negative. Remember that values `$80` to `$FF` are considered negative. However, if your initial loop counter is `$80`, it will first execute the code within the loop, decrease the loop counter by 1, _then_ check if the loop counter is negative. Therefore with this loop, you can loop through 129 bytes of data at most. It's also possible to have the loop counter at 16-bit mode while having the accumulator at 8-bit mode. The following example demonstrates this: Copy REP #$10 LDX.w #Table_end-Table-1 ; Get the length of the table ($03). Notice the ".w" to force - LDA Table,x ; the assembler to use a 16-bit value. STA $00,x ; One by one store the values into address $7E0000-$7E0003. DEX ; Decrease the loop counter by one. BPL - ; If the loop counter isn't negative, continue looping. SEP #$10 RTS Table: db $01,$02,$04,$08 .end In this case, it's possible to loop through ‭32769 bytes of data at most. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#looping-with-an-end-of-data-check) Looping with an "end-of-data" check. This method basically keeps looping and iterating through values, until it reaches some kind of an "end-of-data" marker. Generally speaking, this value is something that the code normally never uses as an actual value. The general consensus for this value is `$FF` or `$FFFF`, although the decision is entirely up to the programmer. Here's an example which uses such a marker. This type of loop is especially handy when it needs to process multiple tables with the exact same logic, but with varying table sizes. One example of such implementation is loading levels; The logic to parse levels is always the same, but the levels vary in size. Copy LDX.b #$00 ; Initialize the index - LDA Table,x ; Read the value from the data table CMP #$FF ; If it's an end-of-data marker, then exit the loop. BEQ + STA $00,x ; If it's not, then store the value. INX ; Increase the index to the table BRA - ; Continue the loop + RTS Table: db $01,$02,$04,$FF .end In the case of this example, the code loops through four bytes of data, three of which are actual data and one of which is the end-of-data marker. Once the loop encounters this marker, in this case the value `$FF`, the loop is immediately terminated. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#bigger-branch-reach) Bigger branch reach ----------------------------------------------------------------------------------------------------------------------- In the [branches chapter](https://github.com/Ersanio/snes-assembly-book/blob/master/docs/programming/branches/README.md) , it is mentioned that branches have a limited distance of -128 to 127 bytes. When you do exceed that limit, the assembler automatically detects that and throws some kind of error, such as the following: Copy LDA $00 CMP #$03 BEQ SomeLabel ; Branch when address $7E0000 contains the value $03 NOP #1000 ; 1000 times "NOP" SomeLabel: RTS Would cause the following error in Asar, during assembly: Copy file.asm:3: error: (E5037): Relative branch out of bounds. (Distance is 1000). [BEQ SomeLabel] This means that the distance between the branch and the label is 1000 bytes, which definitely exceeds the 127 bytes limit. If you necessarily have to jump to that one label, you can invert the conditional and make use of the `JMP` opcode for a longer jump reach: Copy LDA $00 CMP #$03 BNE + ; Skip the jump when address $7E0000 doesn't contain the value $03 JMP SomeLabel ; This runs when address $7E0000 *does* contain the value $03 + NOP #1000 ; Shorthand for 1000 times "NOP" SomeLabel: RTS This way, the logic still remains the same, namely, the thousand NOPs run when address $7E0000 doesn't contain the value $03. The out-of-bounds error is solved. Additionally, replacing the `JMP` with `JML` will allow you to jump _anywhere_ instead of being restricted to the current bank. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#pointer-tables) Pointer tables ------------------------------------------------------------------------------------------------------------- A pointer table is a table with a list of pointers. Depending on the context, the pointers can either point to code or data. Pointer tables are especially useful if you need to run certain routines or access certain data for an exhaustive list of values. Without pointer tables, you would have to do a massive amount of manual comparisons instead. Here's an example of a manual version: Copy LDA $14 CMP #$00 BNE + JMP FirstRoutine + CMP #$01 BNE + JMP SecondRoutine + CMP #$02 BNE + JMP ThirdRoutine + RTS FirstRoutine: LDA #$95 STA $15 RTS SecondRoutine: LDA #$95 STA $15 RTS ThirdRoutine: LDA #$95 STA $15 RTS You can imagine that with a lot of values that are associated with routines, this comparison logic can get huge pretty quickly. This is where pointer tables come in handy. This here is a pointer table: Copy Pointers: dw Label1 dw Label2 dw Label3 dw Label4 As you can see, it's nothing but a bunch of table entries pointing to addresses. You can use labels in order to point to ROM, or defines to point to RAM. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#pointer-tables-for-code) Pointer tables for code There are a few instructions designed for making use of pointer tables. They are as follows: Instruction Example Explanation **JMP (**_**absolute address**_**)** JMP ($0000) Jumps to the absolute address located at address $7E0000 **JMP (**_**absolute address**_**,x)** JMP ($0000,x) Jumps to the absolute address located at address $7E0000, which is indexed by X **JML \[**_**absolute address**_**\]** JML \[$0000\] Jumps to the long address located at address $7E0000 **JSR (**_**absolute address**_**,x)** JSR ($0000,x) Jumps to the absolute address located at address $7E0000, which is indexed by X, then returns With these opcodes, as well as a pointer table, it is possible to run a subroutine depending on the value of a certain RAM address. Here's an example which runs a routine depending on the value of RAM address $7E0014: Copy LDA $14 ; Load the value into A... ASL A ; ...Multiply it by two... TAX ; ...then transfer it into X JSR (Pointers,x) ; Execute routines. RTS Pointers: dw Label1 ; $7E0014 = $00 dw Label2 ; $7E0014 = $01 dw Label3 ; $7E0014 = $02 dw Label4 ; $7E0014 = $03 Label1: LDA #$01 STA $09 RTS Label2: LDA #$02 STA $09 RTS Label3: LDA #$03 STA $99 RTS Label4: LDA #$55 STA $66 RTS The short explanation is that depending on the value of RAM address $14, the four routines are executed. For value `$00`, the routine at `Label1` is executed. For value `$01`, the routine at `Label2` is executed, and so on. The long explanation is that we load the value into A and multiply it by two, because we use _words_ for our pointer tables. Thus, we need to index every two bytes instead of every byte. This means that value `$00` stays as index value `$00` thus reading the `Label1` pointer. Value `$01` becomes index value `$02`, thus reading the `Label2` pointer. Value `$02` becomes index value `$04`, thus reading the `Label3` pointer. Value `$03` becomes index value `$06`, thus reading the `Label4` pointer. Because the JSR uses an "absolute, indirect" addressing mode, the labels are also absolute, thus they only run in the same bank as that JSR. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#pointer-tables-for-data) Pointer tables for data The same concept can be applied for data instead of just subroutines. Imagine you want to read level data, depending on the level number. A pointer table would be a perfect solution for that. Here's an example: Copy LDA $14 ; Load the level number into A... ASL A ; ...Multiply it by three... CLC ADC $14 TAY ; ...then transfer it into Y LDA Pointers,y STA $00 LDA Pointers+1,y STA $01 LDA Pointers+2,y ; Store the pointed address into RAM STA $02 ; To use as an indirect pointer REP #$10 LDY #$0000 - LDA [$00],y ; Read level data until you reach an end-of-data marker CMP #$FF BEQ Return ; Do something with the loaded level data here INY BRA - Return: SEP #$10 RTS Pointers: dl Level1 ; $7E0014 = $00 dl Level2 ; $7E0014 = $01 dl Level3 ; $7E0014 = $02 dl Level4 ; $7E0014 = $03 Level1: db $01,$02,$91,$86,$01,$82,$06,$FF Level2: db $AB,$91,$06,$78,$75,$FF Level3: db $D9,$B0,$A0,$21,$FF Level4: db $C0,$92,$84,$81,$82,$99,$FF In the first section, we use the same concept of multiplying a value to access a pointer table. Except this time, we multiply by three, because the pointer tables contain values that are _long_. We use this value as an index to the pointer table, and store the pointer in `RAM $7E0000` to `$7E0002`, in little endian. After that, in the second section, we use `RAM $7E0000` as an indirect pointer and start looping through its values, using Y as an index again. We keep looping indefinitely, until we hit an "end-of-data" marker, in this case the value `$FF`. We use this method because levels could be variable in length. We also use 16-bit Y because level data _could_ be bigger than 256 bytes in size. Finally, we finish the routine by setting Y back to 8-bit and then returning. This example also shows how to use 24-bit pointers rather than 16-bit pointers. The pointer table contains long values. We use this in combination with a "direct, indirect _long_" addressing mode (i.e. the square brackets). [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#pseudo-16-bit-math) Pseudo 16-bit math --------------------------------------------------------------------------------------------------------------------- It is possible to perform 16-­bit `ADC` and `SBC` without actually switching to 16-­bit mode. This is actually quite useful in cases a 16-bit value is stored across two separate addresses as two 8-bit values. This is possible with the help of the carry flag as well as the behaviour of the opcodes `ADC` and `SBC`. Pseudo 16-bit math also works with `INC` and `DEC`, although you'd have to use them on the addresses instead of the A, X and Y registers. By making clever usage of the negative flag, it's possible to perform pseudo 16-bit math with this opcode also. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#adc) ADC Here's an example of a pseudo 16-bit `ADC`: Copy LDA #$F0 STA $00 ; Initialize address $7E0000 to value $F0 for this example LDA #$05 STA $59 ; Initialize address $7E0059 to value $05 for this example ; These would make the 16-bit value $05F0 LDA $00 ; Load the value $F0 into A CLC ; Clear Carry flag for addition. C = 0 ADC #$20 ; $F0+$20 = $10, C = 1 STA $00 ; $7E0000 has now the value $10 LDA $59 ; Load the value $05 into A ADC #$00 ; Normally adds $00 to value $05. BUT C = 1, so this adds $01 to $05 instead STA $59 ; A is now $06, and we store it into $7E0059 ; These would now make the 16-bit value $0610 ; across two addresses The carry flag is set after the first `ADC`. This means that the value has wrapped back to `$00` and increased from there. Because the carry flag is set, the second `ADC` adds $00 + carry, thus `$01`, thus increasing the second address by one. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#sbc) SBC Here's an example of a pseudo 16-bit `SBC`: Copy LDA #$10 STA $00 ; Initialize address $7E0000 to value $10 for this example LDA #$05 STA $59 ; Initialize address $7E0059 to value $05 for this example ; These would make the 16-bit value $0510 LDA $00 ; Load the value $10 into A SEC ; Set Carry flag for subtraction. C = 1 SBC #$20 ; $10-$20 = $F0, C = 0 STA $00 ; $7E0000 has now the value $F0 LDA $59 ; Load the value $05 into A SBC #$00 ; Normally subtracts $00 from value $05. BUT C = 0, so this subtracts $01 from $05 instead STA $59 ; A is now $04, and we store it into $7E0059 ; These would now make the 16-bit value $04F0 ; across two addresses The carry flag is cleared after the first `SBC`. This means that the value has wrapped back to `$FF` and decreased from there. Because the carry flag is cleared, the second `SBC` subtracts $00 + carry, thus `$01`, thus decreasing the second address by one. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#inc) INC Here's an example of a pseudo 16-bit `INC`: Copy LDA #$FF STA $00 ; Initialize address $7E0000 to value $FF for this example LDA #$03 STA $59 ; Initialize address $7E0059 to value $03 for this example ; These would make the 16-bit value $$03FF INC $00 ; The value in $7E0000 is increased by 1, making it have the value $00 BNE + ; This sets the zero flag, thus the branch is not taken INC $59 ; Thus, the value in $7E0059 is also increased by 1 + RTS ; These would now make the 16-bit value $0400 ; across two addresses By making clever usage of the zero flag, we know that the result of `INC $00` is actually the value `$00`, because that's the only time the zero flag is set. If the result is indeed the value `$00`, then the other address needs to be increased also. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#dec) DEC Here's an example of a pseudo 16-bit `DEC`: Copy LDA #$00 STA $00 ; Initialize address $7E0000 to value $00 for this example LDA #$03 STA $59 ; Initialize address $7E0059 to value $03 for this example ; These would make the 16-bit value $$0300 DEC $00 ; Decrease the value in $7E0000 by 1 LDA $00 ; If it results in $FF, then we wrapped from the value $00 to $FF CMP #$FF ; Thus, we need to decrease the value in $7E0059 also BNE + DEC $59 + RTS ; These would now make the 16-bit value $02FF ; across two addresses As you can see, there's an extra check for the value `$FF`, because there's no shorthand way to check if the result of a `DEC` is exactly the value `$FF`. If the result indeed is the value `$FF`, then the other address needs to be decreased also. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#adc-and-sbc-on-x-and-y) ADC and SBC on X and Y ----------------------------------------------------------------------------------------------------------------------------- Increasing and decreasing A by a certain amount is easy because of `ADC` and `SBC`. However, these kind of instructions do not exist for X and Y. If you want to increase or decrease X and Y by a small amount, you would have to use `INX`, `DEX`, `INY` and `DEY`. This quickly gets impractical if you have to increase or decrease X and Y by great numbers (5 or more) though. In order to do that, you can temporarily transfer X or Y to A, then perform an `ADC` or `SBC`, then transfer it back to X or Y. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#addition) Addition Here's an example using `ADC`: Copy TXA ; Transfer X to A. A = X CLC ; ADC #$42 ; Add $42 to A TAX ; Transfer A to X. X has now increased by $42 By temporarily transferring X to A and back, the `ADC` practically is used on the X register, instead. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#subtraction) Subtraction Here's an example using `SBC`: Copy TXA ; Transfer X to A. A = X SEC ; SBC #$42 ; Subtract $42 from A TAX ; Transfer A to X. X has now decreased by $42 By temporarily transferring X to A and back, the `SBC` practically is used on the X register, instead. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#checking-flags) Checking flags ------------------------------------------------------------------------------------------------------------- [Flags](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/binary) , are bits which serve as some sort of an "on/off" switch on a certain property. One byte contains 8 bits, but how do you actually _check_ if a flag is on or off? In ASM, when you usually use comparison and branching opcodes, you check for whole bytes rather than a single bit within that byte. There are two opcodes which are suitable for this. We will use the example from the binary chapter: Copy 10100000 ││└───── "Is daytime" flag │└───── "Is horizontal level" flag └───── "Is raining" flag Imagine this byte is stored in address $7E0095 for the examples to follow. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#the-and-opcode) The "AND" opcode `AND` is handled in the [Bitwise Operations](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic) chapter. By using AND, you can basically "isolate" bits and check if any of them are set or cleared. For example, if we want to check if the "daytime" flag is set, you would load the address into A, then `AND` just that one bit: Copy LDA $95 AND #%00100000 ; can also be written as #$20 BNE DayTimeIsSet ; branches when the daytime flag is set ... If that one bit in address $7E0095 is set, then the result of that `AND` will also be that A gets the value `$20`. Thus, the zero flag is cleared, and the branch is taken. If you want to check if either of the two flags are set (e.g. the daytime flag _or_ the raining flag), you'd use `AND` to check two bits, rather than one: Copy LDA $95 AND #%10100000 ; can also be written as #$A0 BNE IsRainingOrDaytime ; branches when both rain OR daytime flags are set ... If you want to check if both of the two flags are set (e.g. the daytime flag _and_ the raining flag), you'd have to use `AND` and then a `CMP`. Then, you'd branch if the value resulting from the `AND` is equal to the flags you want set: Copy LDA $95 AND #%10100000 ; First filter the bits CMP #%10100000 ; Then check if both bits are set BEQ IsRainingOrDaytime ; branches when both rain AND daytime flags are set ... ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques#the-bit-opcode) The "BIT" opcode The `BIT` opcode, which is also handled in the [Bitwise Operations](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic) chapter, is special because it can actually check if bits 7 and 6 (bits 15 and 14 in 16-bit mode) of an address' value are set, without having to modify A. Here's an example: Copy BIT $95 BMI IsRaining ; Branches when bit 7 (negative flag) is set BVS IsHorizontalLevel ; Branches when bit 6 (overflow flag) is set ... [PreviousHardware vectors](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector) [NextCommon assembler syntax](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax) Last updated 3 months ago --- # Common assembler syntax | Assembly for the SNES For the complete documentation index, see [llms.txt](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt) . This page is also available as [Markdown](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax.md) . In this chapter, you'll learn the art of proper assembler syntax, so that in theory, you'll can write "relative"-only code. This means that when your code shifts around (by inserting or removing new lines of code), opcodes that make use of addresses and relative addresses, such as branches or jumps, will keep their correct values. Note that everything discussed here can also be found in Asar's documentation. However, they're important enough topics to be mentioned in this tutorial. [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax#defines) Defines ------------------------------------------------------------------------------------------- Defines are basically variable definitions you can use, so your code suffers less from so-called "magic numbers". Here's an example which defines an immediate value: Copy !Value = $03 LDA #!Value STA $01 Here's an example which defines an address: Copy !Address = $01 LDA #$03 STA !Address Be aware that Asar actually does a simple text search and replace, rather than evaluating the expression in a define. In other words, Asar isn't smart enough to figure out that a define is an "address", "immediate value" or anything else. Here's an example of improper define usage: Copy !Value = #$03 ; Note the # LDA #!Value ; A search and replace turns this into "LDA ##$03" STA $01 ; Therefore, it will throw an error! [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax#labels) Labels ----------------------------------------------------------------------------------------- As discussed in the [branches](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches) and [subroutines](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/subroutine) chapters, the SNES processor can make use of labels to determine locations it can jump to. The labels used by the opcodes are replaced by actual values which denote the locations to jump to, either relative or absolute addresses. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax#sublabels) Sublabels Sublabels are special type of labels which have a parent label, and are prefixed with a dot ("`.`"), and aren't suffixed with a colon ("`:`"). Sublabels are useful if you tend to use labels which aren't unique (e.g. "return"). Here's an example of a recurring "return" sublabel: Copy JSR Main JSR Sub RTS Main: LDA $10 BEQ .return STA $11 .return RTS Sub: LDA $20 BEQ .return STA $21 .return RTS Sublabels don't have any rules in terms of writing style. You could capitalize or keep it all lowercase. In this example, it's all lowercase. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax#relative-labels) Relative labels Relative labels are an alternate solution to sublabels and are often used when the code is already self-documenting enough, for example, when the code needs to skip a single store depending on a branch. It saves you from thinking up a label name, such as "skipstorewhenplayerisbig". Relative labels are written using `+` and `-`. The plus is ahead of the branch instruction, while the minus is behind the branch instruction. They can be repeated as often as needed to denote different levels of depth. Here's an example of relative labels: Copy LDA $10 BEQ + STA $11 + RTS This code skips a single store to `$11` when address `$10` has the value `$00`. Here's another example, demonstrating a backwards branch, causing an infinite loop: Copy - BRA - Here's another example, demonstrating different levels of relative label depth: Copy LDA $10 BEQ ++ LDA $11 BEQ + STZ $12 + STZ $13 ++ STZ $14 RTS * If address `$7E0010` has the value `$00`, address `$7E0014` is cleared. * If address `$7E0011` has the value `$00`, addresses `$7E0013` and `$7E0014` are cleared * Else, addresses `$7E0012`, `$7E0013` and `$7E0014` all are cleared [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax#the-art-of-relativity) The art of relativity ----------------------------------------------------------------------------------------------------------------------- It's possible to write programs completely devoid of fixed values and addresses (also known as "magic numbers"), by making smart use of labels and defines outside of branches and jumps. When you use labels with loading instructions, for example, it'll grab the address of the label and use it as a parameter. This was seen in the [indexing](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/indexing) chapter. However, you can also use labels as values, rather than addresses. This is especially useful when setting up indirect pointers, which is why it's important to be able to grab certain _parts_ of an address rather than the full address. This is also demonstrated in the [moves](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves) chapter, in the "Easy notation" section. In the following example, an `LDA` loading the address of a label as a value would look like this: Copy LDA #somelabel STA $00 This is problematic, because the label assembles into a 24-bit value which is the address, and there's no LDA which accepts a 24-bit value. Instead, LDA tries to grab the largest possible supported value, thus grabs the high and low byte of the value instead (because it's 16-bit). But what if you're writing 8-bit code at that moment? The code won't run as expected, and will crash. ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax#opcode-length-specifiers) Opcode length specifiers In order to read a value at a well-defined, fixed width, you can make use of "opcode length specifiers". These are special notations appended to opcodes: Syntax Definition Description .b byte (8-bit) Forces the parameter to be 8-bit .w word (16-bit) Forces the parameter to be 16-bit .l long (24-bit) Forces the parameter to be 24-bit In the previous example, you can force the assembler to use the low bytes of the label only, by using `.b`: Copy LDA.b #somelabel STA $00 The same applies to defines. You can use defines as values, and by using an opcode length specification, you can only grab certain parts of the defines rather than the full value. For example: Copy !Size = $7FFF LDA #!Size STA $00 This would assemble as: Copy LDA #$7FFF STA $00 This would be problematic in 8-bit mode, as this assembles in 16-bit mode. To fix this problem, you can use `.b`: Copy !Size = $7FFF LDA.b #!Size STA $00 This would assemble as: Copy LDA #$FF STA $00 ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax#bitshifts) Bitshifts Expanding upon the previous example: Copy !Size = $7FFF LDA.b #!Size STA $00 If you wanted to store the high byte of this define in address `$7E0001`, instead of the low byte, you'd need a way to grab _only_ the high byte of the definition. In order to do that, you'll have to use bitshifts: Syntax Definition Description \>> Shift right Shifts bits right n times << Shift left Shifts bits left n times Remember that a byte consists of 8 bits, thus you need to "skip" 8 bits to grab the next 8 bits we need. By bitshifting 8 times to the right, you discard the low byte of the value: Copy !Size = $7FFF LDA.b #!Size>>8 ; Only $7F remains STA $01 Bitshifts are incredibly valuable when grabbing certain portions of addresses or values. They can also be used on labels, and thus, you can also grab bank bytes: Copy LDA.b #somelabel>>16 ; Grab the bank byte of a label STA $01 The same goes for defines: Copy !Address = $7E8000 LDA.b #!Address>>16 ; Grab $7E of the define STA $02 ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax#constructing-addresses) Constructing addresses By making use of bitshifts and opcode length specifiers, it's possible to supply addresses to certain subroutines, or as indirect addresses. Here's an example which constructs an indirect address: Copy LDA.b #Sometable STA $00 LDA.b #Sometable>>8 STA $01 LDA.b #Sometable>>16 STA $02 LDA [$00] ; This loads the value $01 into A RTS Sometable: db $01,$02,$04,$08 ### [](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax#table-sizes) Table sizes There are situations where it's handy to know the size of tables, such as for [moves](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves) . To get the size of a table, you put a label at both begin and end of a table, such as this: Copy Sometable: db $01,$02,$04,$08 .end Then, by using the subtract operator, "`-`", you subtract the starting and the ending address of the table, effectively getting the size of the table. For example: Copy LDA.b #Sometable_end-Sometable ; #$04 STA $00 RTS Sometable: db $01,$02,$04,$08 .end Note that it's important to use opcode length specifiers, as we're still dealing with labels, thus, 24-bit values. [PreviousTechniques](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques) [NextProgramming cautions](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cautions) Last updated 4 years ago --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/getting-started.md). # Getting started ## IDE There are no dedicated IDEs for 65c816 assembly. You can use any ASCII editor, such as Notepad or VS Code. However, some people made various plugins for existing code editors to add extra features, such as syntax highlighting: \* Josh Neta's "\[65816 Assembly\](https://marketplace.visualstudio.com/items?itemName=joshneta.65816-assembly)" plugin for VS Code \* Vice's "\[65816 SNES Assembly Language Server\](https://marketplace.visualstudio.com/items?itemName=vicerust.snes-asm)" plugin for VS Code \* lx5's "\[Asar syntax highlight\](https://github.com/TheLX5/AsarSyntaxHighlight-VSCode)" plugin for VS Code. \* Telinc1's "\[65c818 ASM syntax for highlight.js\](https://github.com/telinc1/smwcentral-highlightjs-asar)" as a JavaScript plugin Assembly files are generally saved with the file extension ".asm". ## Assemblers This tutorial uses syntax which is used by an assembler called "Asar", originally written by Alcaro, now maintained by various members of the SMW Central community. This assembler is hosted on SMW Central and can be downloaded \[here\](https://www.smwcentral.net/?p=section\\&a=details\\&id=19043). The GitHub repository of Asar can be found \[here\](https://github.com/RPGHacker/asar). --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/getting-started.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/memory.md). # The SNES memory Writing assembly involves writing a bunch of instructions where you load a "value" and store it at an "address" in order to get a desired effect, such as changing the player's powerup. When writing assembly, you will work with the SNES memory most of the time. The SNES memory basically is a region of bytes, and each byte is located at an "address". Think of it like a chessboard: !\[\](/files/-Lysl9clYphWvPa7dbYP) You can see that in order to refer to a certain cell, the image makes use of column and cell names. The "address" of the shown queen (the "value") would be address D8, for example. Also, a single cell can't hold two units. This same concept applies to the SNES memory. The SNES memory is mapped from address $000000 to $FFFFFF, although only $000000-$7FFFFF is used in most cases. The format of an address is as follows: $BBHHDD. \* BB is the "bank byte" of the address \* HH is the "high byte" of the address \* DD is the "low byte" of the address Addresses can be written in 3 ways: $BBHHDD, $HHDD and $DD, such as $7E0003, $0003 and $03. \* Addresses in the $DD-notation are called "direct page" \* Addresses in the $HHDD-notation are called "absolute addresses" \* Addresses in the $BBHHDD-notation are called "long addresses" As established earlier, an address may contain only one byte. If you access a certain address in 16-bit mode, it means you actually access both "address" and "address+1", because a 16-bit number consists of two bytes. Here's a drawing to get a general overview of the basic SNES memory (also known as a memory map): !\[The “LoROM” Memory Map\](/files/-LyslKE0owAcmmoN-Ipa) This memory map is in the "LoROM" format. If you're a SMW hacker, you don't have to worry about what this means; just take this memory map for granted. ## ROM ROM stands for "Read-Only Memory" and it's exactly that: memory that can be only read. This means that you cannot change the ROM by storing values to it with ASM. You can say it is the game or program itself, which contains all the ASM code and data tables, as well as assets like graphics, music, and so on. Alternatively: It's the .smc/.sfc/.fig/etc. file which you load in emulators. ## RAM RAM stands for "Random-Access Memory". This is the dynamic memory which allows anything to be written to it at any time. You could say that this is the place where you have variables which are important and have meaning. RAM can be written to in order to achieve a certain effect. For example, if you write $04 to the player's extra lives, then the player will have exactly 4 extra lives. The SNES RAM is 128kB big, and it is located at addresses $7E0000-$7FFFFF. The SNES RAM is completely generic. There is no such rule as "address $7E0120 is used for lives in every SNES game ever". You define the purpose of RAM yourself, by writing ASM code. The memory map shows that banks $00-3F contain a RAM "mirror". Mirrored RAM addresses are addresses which contain the same value across every bank. This means that RAM address $001234 contains the exact same value as $0F1234 at all times. Having the RAM mirrored means that code executing in the ROM at those banks can access RAM $7E0000-$7E1FFF more "easily". Conversely, code executing at banks $40-6F have more trouble accessing the RAM because the RAM isn't mirrored there. For the sake of simplicity, you can \*\*always\*\* assume that bank $00 equals bank $7E. ## SRAM SRAM stands for "Static Random-Access Memory". It is also 128kB big, and it is located in blocks of 32kB at $700000-$707FFF, $710000-$717FFF, $720000-$727FFF and $730000-$737FFF, although the final size of SRAM depends on the ROM specifications itself, thanks to something called the "internal ROM header. The SRAM isn't mirrored in other banks. SRAM behaves exactly like RAM; you can store anything and load anything from it, but the values do not get cleared when the SNES resets. The SRAM is kept alive with an actual button-cell battery on a real SNES cartridge. When the battery dies, or is removed, SRAM won't function properly and will possibly lose data after every reset. On emulators, SRAM is stored in the well-known ".srm"-files. SRAM is usually used for save files, although it can also be used as extra RAM. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/memory.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/hexadecimal.md). # Hexadecimal To program in 65c816 ASM, you will need to grasp the basics of hexadecimal. Hexadecimal, also known as "hex", is a counting system much like decimal, which is the everyday counting system people use. In hexadecimal, there are additional 6 digits per place value, which are denoted through the values A-F, as seen in the table below. | Decimal | Hexadecimal | | ------- | ----------- | | 0 | 0 | | 1 | 1 | | 2 | 2 | | 3 | 3 | | 4 | 4 | | 5 | 5 | | 6 | 6 | | 7 | 7 | | 8 | 8 | | 9 | 9 | | 10 | A | | 11 | B | | 12 | C | | 13 | D | | 14 | E | | 15 | F | | 16 | 10 | | 17 | 11 | | ... | ... | | 255 | FF | There are various ways to write hex numbers so readers cannot confuse them with actual decimal numbers. They are as follows: \* Prefix hexadecimal numbers with "0x" (e.g. 0x42) \* Prefix hexadecimal numbers with "$" (e.g. $42) \* Sufffix hexadecimal numbers with "H" (e.g. 42H) In this tutorial, the convention is to prefix hexadecimal numbers with "$". In assembly, a hexadecimal number with two digits is called a "byte". This means that values between $00-$FF are considered a byte. ## Signed and unsigned values In the real world, numbers can be positive or negative. In assembly, depending on the code, values can be treated as "signed" or "unsigned". Signed values mean that they can also be negative: The value $80 and higher are considered to be negative numbers in decimal, starting from -128, and counting down as the hex number is counting up, as you can see in the table below. | Decimal | Hexadecimal | | ------- | ----------- | | 126 | $7E | | 127 | $7F | | -128 | $80 | | -127 | $81 | | ... | ... | | -1 | $FF | The presence of negative numbers depends on the game's programming. For example, a player can have positive and negative speed (resulting in going forward or backward), but a player cannot have negative extra lives or points (because normally that doesn't make sense). Needless to say, the value -0 does not exist. ## Four-digit hexadecimal values Hexadecimal numbers can count well past two digits, as you can see below. | Decimal | Hexadecimal | | ------- | ----------- | | 254 | $FE | | 255 | $FF | | 256 | $0100 | | 257 | $0101 | | ... | ... | | 65535 | $FFFF | The format of such a hexadecimal number is as follows: $HHLL. \* HH is the "high byte" of the number \* LL is the "low byte" of the number --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/hexadecimal.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/loading-and-storing.md). # Loading and storing The first thing you definitely should know when starting off with assembly, is how to load and store data using various the SNES registers. The basic opcodes for loading and storing data are \`LDA\` and \`STA\`. As mentioned earlier, there are 3 main registers: \* A (the accumulator) \* X (index) \* Y (index) Although these registers can be either in 8-bit or 16-bit mode, in this tutorial we will consider them 8-bit by default. ## LDA and STA | Opcode | Full name | Explanation | | ------- | ---------------------- | ------------------------------- | | \*\*LDA\*\* | Load into accumulator | Load a value into A | | \*\*STA\*\* | Store from accumulator | Store A's value into an address | We will use RAM addresses for the sake of simplicity. Here is an example for loading and storing values. \`\`\` LDA #$03 ; A = $03 STA $7E0001 \`\`\` We will look at this code line by line. \`\`\` LDA #$03 \`\`\` This loads the value \`$03\` into A. The "#" means that we're loading an actual value, not an address. After this instruction, the content of the A register is now \`$03\`. LDA can load values into A, ranging from \`$00-$FF\` in 8-bit mode and \`$0000-$FFFF\` in 16-bit mode. \`\`\` STA $7E0001 \`\`\` This stores A's value into the RAM address $7E0001. Because A's value was $03, RAM address $7E0001's value also is now $03. The contents of the A register is \*not\* cleared. This means you can chain multiple stores, like this: \`\`\` LDA #$03 STA $7E0001 STA $7E0053 \`\`\` A common beginner's mistake is writing \`STA #$7E0001\` or any form of "STA #$". This instruction doesn't exist. It also doesn't make sense; there's no logic behind storing the value of A into another value. {% hint style="info" %} Remember, using $ instead of #$ after an opcode means that the parameter is an address, not an immediate value. {% endhint %} Putting a semicolon (;) will allow everything beyond that to be ignored by the assembler, during the assembly of the code. In other words, ; is used to place comments. Example: \`\`\` LDA #$03 ; This is a comment! \`\`\` ### Loading and storing addresses Of course, what would be the use to store things to a RAM address when you don't know how to access the address again? You can load a RAM address' contents into the A register by using LDA with a different addressing mode. Here is an example. \`\`\` LDA $7E0013 STA $7E0042 \`\`\` Again, we will look at this example line by line. \`\`\` LDA $7E0013 \`\`\` This will load the contents of the RAM address \`$7E0013\` into A. Let's assume that the contents were \`$33\`. So now, A has the value \`$33\`. The content of \`$7E0013\` remains unchanged, because LDA copies the number rather than extracting it from the address. Note that this time we have used $ instead of #$. This is because we wanted to access a RAM address. In the end, A has \`$33\` and RAM \`$7E0013\` has the value \`$33\` also. \`\`\` STA $7E0042 \`\`\` This instruction will store the contents of the A register into the RAM address \`$7E0042\`. Of course, A will remain unchanged. RAM address \`$7E0042\` is now \`$33\`. In short: the full example will copy the contents of \`$7E0013\` over to \`$7E0042\`. ## LDY, STY, LDX, STX Now that we have learned the basics of loading and store values into addresses, let's introduce the same loading opcodes, but for the index registers: | Opcode | Full name | Explanation | | ------- | ------------ | ------------------------------- | | \*\*LDY\*\* | Load into Y | Load a value into Y | | \*\*STY\*\* | Store from Y | Store Y's value into an address | | \*\*LDX\*\* | Load into X | Load a value into X | | \*\*STX\*\* | Store from X | Store X's value into an address | The above opcodes behave exactly like LDA and STA. The only difference is that these make use of the X and the Y registers instead of the accumulator. For example: \`\`\` LDY #$03 STY $0001 \`\`\` Would store the number $03 into RAM address $7E0001, by utilizing the Y register. To use the X register, use LDX and STX. As for why the address is $0001 instead of $7E0001, please refer to this chapter: \[Shorter addresses\](/assembly-for-the-snes/the-basics/shorter-addresses.md) ## STZ There is another opcode which stores the number $00 into addresses directly. | Opcode | Full name | Explanation | | ------- | -------------------- | --------------------------------- | | \*\*STZ\*\* | Store zero to memory | Sets the value of an address to 0 | This opcode stores the number $00 into an address. It doesn't even need the A, X or Y registers to load $00 first. If you want to make a code that directly stores $00 in a RAM address, you could make it use 1 line: \`\`\` STZ $01 ; $7E0001 = $00. The A register is unaffected. \`\`\` STZ will store zero to a specified RAM address. After this opcode, RAM address $7E0001 will now contain the number $00. Using STZ when A is in 16-bit mode \*\*will\*\* store $0000 to both RAM addresses $7E0001 and $7E0002. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/loading-and-storing.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift.md). # Bit shifting operations "Bit shifting" is a bitwise operation in which you move bits left or right. As a result, practically speaking, you divide or multiply a number by two. ## ASL and LSR There are two opcodes which shift bits to the left or right. | Opcode | Full name | Explanation | | ------- | ----------------------- | ------------------------------------------------------------------------ | | \*\*ASL\*\* | A or memory shift left | Moves bits left once without carry, practically multiplying a value by 2 | | \*\*LSR\*\* | A or memory shift right | Moves bits right once without carry, practically dividing a value by 2 | ASL and LSR can affect either A or an address, just like INC and DEC. Here's an example of ASL in action: \`\`\` LDA #$02 ; Load the value $02 into A ASL A ; Multiply A by 2 ; A is now $04 \`\`\` This is what appears on the surface. When you look at the numbers in binary though, this is what it looks like: \`\`\` LDA #$02 ; Load the value %00000010 into A ASL A ; Shift bits left once ; A is now %00000100 \`\`\` As you can see, the digit "1" has moved to the left once. That is what ASL does, moving bits left. ASL can also move bits in an address without affecting A. \`\`\` LDA #$02 ; Load the value $02 into A STA $00 ; Store this into address $7E0000 ASL $00 ; Shift bits of $7E0000 left once ; A is still $02, while $7E0000 is now $04 \`\`\` You can also shift bits to the right by using LSR. \`\`\` LDA #$02 ; Load the value $02 into A LSR A ; Divide A by 2 ; A is now $01 \`\`\` And when you look at it on the level of binary, rather than hexadecimal: \`\`\` LDA #$02 ; Load the value %00000010 into A LSR A ; Shift bits right once ; A is now %00000001 \`\`\` As you can see, the digit "1" has moved to the right once. That is what LSR does, moving bits right. LSR can also move bits in an address without affecting A. \`\`\` LDA #$02 ; Load the value $02 into A STA $00 ; Store this into address $7E0000 LSR $00 ; Shift bits of $7E0000 right once ; A is still $02, while $7E0000 is now $01 \`\`\` ### Edge-cases and carry flag You might be wondering what happens if you bit shift \`%1000 0001\` to the right once, by using LSR. Bit 7 is currently set, but there's nothing to shift into bit 7. At the same time, bit 0 is also set, but it has nowhere to shift into. When this happens, bit 0 will move into the carry flag. At the same time, bit 7 will be set to 0. The opposite is also true when you shift \`%1000 0001\` to the left once, by using ASL. Bit 7 will move into the carry flag, while bit 0 will be set to 0. Here are some examples of a bit moving into the carry flag. \`\`\` CLC ; Ensuring carry (C) = 0 LDA #$80 ; A = %1000 0000 | C = 0 ASL A ; A = %0000 0000 | C = 1 ASL A ; A = %0000 0000 | C = 0 \`\`\` \`\`\` CLC ; C = 0 LDA #$01 ; A = %0000 0001 | C = 0 LSR A ; A = %0000 0000 | C = 1 LSR A ; A = %0000 0000 | C = 0 \`\`\` ### Chaining ASL and LSR By chaining ASL and LSR, you can multiply or divide a value by 2 several times, therefore multiplying or dividing it by 2, 4, 8, 16, and so on. It's 2ⁿ, where n is the amount of ASL or LSR instructions you're chaining. Here's an example of multiplying a value by 8. \`\`\` LDA #$07 ; A is now $07 = %0000 0111 ASL A ; A is now $0E = %0000 1110 ASL A ; A is now $1C = %0001 1100 ASL A ; A is now $38 = %0011 1000 ; This is basically $07 \* 2³ \`\`\` Here's an example of dividing a value by 4 \`\`\` LDA #$07 ; A is now $07 = %0000 0111 LSR A ; A is now $03 = %0000 0011 LSR A ; A is now $01 = %0000 0001 ; This is basically $07 / 2² \`\`\` ## ROL and ROR There are two opcodes which \*rotate\* bits to the left or right, rather than shifting. | Opcode | Full name | Explanation | | ------- | ------------------------ | ---------------------------------------------------------- | | \*\*ROL\*\* | Rotate A or memory left | Moves bits left once with carry, wrapping the bits around | | \*\*ROR\*\* | Rotate A or memory right | Moves bits right once with carry, wrapping the bits around | They behave the same as LSR and ASL, except they are using the carry flag as an extra bit in order to make the bits "wrap" around. This means that the value can't be "stuck" at $00 eventually, like it happens in ASL and LSR. This is why they're called rotate, rather than shift. Here's an example of ROL: \`\`\` CLC ; Ensuring carry (C) = 0 LDA #$80 ; A = %1000 0000 | C = 0 ROL A ; A = %0000 0000 | C = 1 ROL A ; A = %0000 0001 | C = 0 ; A is now $01 \`\`\` As you can see, bit 7 wrapped all the way around to bit 0, basically "rotating" the bits without losing any information. \`\`\` CLC ; C = 0 LDA #$01 ; A = %0000 0001 | C = 0 ROR A ; A = %0000 0000 | C = 1 ROR A ; A = %1000 0000 | C = 0 ; A is now $80 \`\`\` As you can see, bit 0 wrapped all the way around to bit 7, basically "rotating" the bits without losing any information. \`\`\` SEC ; C = 1 LDA #$00 ; A = %0000 0000 | C = 1 ROL A ; A = %0000 0001 | C = 0 ; A is now $01 \`\`\` As you can see, you can set the carry flag and rotate. This will result in A being modified anyway, even though A started out as $00. Rotation can also affect addresses, just like ASL and LSR. Here's an example: \`\`\` CLC ; Clear carry LDA #$02 ; Load the value $02 into A STA $00 ; Store this into address $7E0000 ROR $00 ; Shift bits of $7E0000 right once ; A is still $02, while $7E0000 is now $01 ; and carry is still cleared \`\`\` ## 16-bit mode bit shifting All the previous explanations and behaviours apply to 16-bit bit shifting as well. It's just that you're working with 16 bits and the carry flag, not 8 bits. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/shift.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/math.md). # Hardware math The SNES processor is capable of \[basic multiplication and division by 2ⁿ\](/assembly-for-the-snes/mathemathics-and-logic/shift.md), but if you'd like to multiply or divide by other numbers, you'll have to make use of certain SNES hardware registers. ## Hardware Unsigned Multiplication The SNES has a set of hardware registers used for unsigned multiplication: | Register | Access | Description | | -------- | ------ | ------------------------------------------------------------------------------------ | | $4202 | Write | Multiplicand, 8-bit, unsigned. | | $4203 | Write | Multiplier, 8-bit, unsigned. Writing to this also starts the multiplication process. | | $4216 | Read | Unsigned multiply 16-bit product, low byte | | $4217 | Read | Unsigned multiply 16-bit product, high byte | After you write to \`$4203\` to start the multiplication process, you will need to wait 8 \[machine cycles\](/assembly-for-the-snes/deep-dives/cycles.md), which is typically done by adding four \`NOP\` instructions to the code. If you don't wait 8 machine cycles, the results are unpredictable. Here's an example of \`42 \* 129 = 5418\` (in hexadecimal: \`$2A \* $81 = $152A\`): \`\`\` LDA #$2A ; 42 STA $4202 LDA #$81 ; 129 STA $4203 NOP ; Wait 8 machine cycles NOP NOP NOP LDA $4216 ; A = $2A (result low byte) LDA $4217 ; A = $15 (result high byte) \`\`\` ## Hardware Signed Multiplication There's a set of hardware registers which can be used for fast, signed multiplication: | Register | Access | Description | | -------- | ----------- | ------------------------------------------------------------------------------------------------------------ | | $211B | Write twice | Multiplicand, 16-bit, signed. First write: Low byte of multiplicand. Second write: High byte of multiplicand | | $211C | Write | Multiplier, 8-bit. | | $2134 | Read | Signed multiply 24-bit product, low byte | | $2135 | Read | Signed multiply 24-bit product, middle byte | | $2136 | Read | Signed multiply 24-bit product, high byte | There's a catch to using these hardware registers, however, as they double as certain Mode 7 registers as well: \* You can only use them for \*\*signed\*\* multiplication \* The result is signed 24-bit, meaning the results range from \`-8,388,608\` to \`8,388,607\`. \* The results are instant. That means you don't have to use \`NOP\` to wait for the results. \* You cannot use them when Mode 7 graphics are being rendered on the screen. \* This means that when Mode 7 is enabled, you can only use them inside NMI (V-blank). \* This also means that you can use them without any restrictions, outside of Mode 7. Note that register \`$211B\` is "write twice". This means that you have to write an 8-bit value twice to this same register which in total makes up a 16-bit value. First, you write the low byte, then the high byte of the 16-bit value. Here's an example of \`-30000 \* 9 = -270000\` (in hexadecimal: \`$8AD0 \* $09 = $FBE150\`): \`\`\` LDA #$D0 ; Low byte of $8AD0 STA $211B LDA #$8A ; High byte of $8AD0 STA $211B ; This sets up the multiplicand LDA #$09 ; $09 STA $211C ; This sets up multiplier LDA $2134 ; A = $50 (result low byte) LDA $2135 ; A = $E1 (result middle byte) LDA $2136 ; A = $FB (result high byte) ; (= $FBE150) \`\`\` ## Hardware Unsigned Division The SNES has a set of hardware registers used for unsigned division. They are laid out as follows: | Register | Access | Description | | -------- | ------ | --------------------------------------------------------------------------- | | $4204 | Write | Dividend, 16-bit, unsigned, low byte. | | $4205 | Write | Dividend, 16-bit, unsigned, high byte. | | $4206 | Write | Divisor, 8-bit, unsigned. Writing to this also starts the division process. | | $4214 | Read | Unsigned division 16-bit quotient, low byte | | $4215 | Read | Unsigned division 16-bit quotient, high byte | | $4216 | Read | Unsigned division remainder, low byte | | $4217 | Read | Unsigned division remainder, high byte | Quotient means how many times the dividend can "fit" in the divisor. For example: \`6 / 3 = 2\`. Thus, the quotient is 2. Another way you can read this is: You can extract 3 \*\*two\*\* times from 6 and end up with exactly 0 as leftover. Modulo is an operation that determines the remainder of the dividend that couldn't "fit" into the divisor. For example: \`8 / 3 = 2\`. You can subtract 3 two times from 8, but in the end, you have a 2 as a remainder. Thus, the modulo for this operation is \`2\`. Because there are hardware registers that support remainders, the SNES also supports the modulo operation. After you write to \`$4206\` to start the division process, you will need to wait 16 \[machine cycles\](/assembly-for-the-snes/deep-dives/cycles.md), which is typically done by adding eight \`NOP\` instructions to the code. If you don't wait 16 machine cycles, the results are unpredictable. Here's an example of \`256 / 2 = 128\` (in hexadecimal: \`$0100 / $02 = $0080\`): \`\`\` LDA #$00 STA $4204 LDA #$01 ; Write $0100 to dividend STA $4205 LDA #$02 ; Write $02 to divisor STA $4206 NOP ; Wait 16 machine cycles NOP NOP NOP NOP NOP NOP NOP LDA $4214 ; A = $80 (result low byte) LDA $4215 ; A = $00 (result high byte) LDA $4216 ; A = $00, as there are no remainders LDA $4217 ; A = $00, as there are no remainders \`\`\` Here's an example demonstrating modulo: \`257 / 2 = 128, remainder 1\` (in hexadecimal: \`$0101 / $02 = $0080, remainder $0001\`) \`\`\` LDA #$01 STA $4204 LDA #$01 ; Write $0101 to dividend STA $4205 LDA #$02 ; Write $02 to divisor STA $4206 NOP ; Wait 16 machine cycles NOP NOP NOP NOP NOP NOP NOP LDA $4214 ; A = $80 (result low byte) LDA $4215 ; A = $00 (result high byte) LDA $4216 ; A = $01, as there is a remainder (remainder low byte) LDA $4217 ; A = $00 (remainder high byte) \`\`\` There is no hardware signed division. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/math.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack.md). # The stack The stack is a special type of "table" which is located inside the SNES RAM. Imagine the stack as a stack of plates. You can only add (push) or remove (pull/pop) a plate from the top. You can visualize it as something like this: \`\`\` | | |\[ \]| |\[ \]| |\[ \]| |\[ \]| ‾‾‾‾‾ \`\`\` The empty "boxes" in above example are basically the values inside the stack, and they can hold any value. The collection of boxes are closed off from all sides, except from the top. {% hint style="info" %} Technically speaking, because the stack is an area inside the RAM, you can access any value you want, using special stack-relative addressing modes rather than using only push and pull opcodes. For the purposes of this chapter, we'll assume the stack indeed is a perfect stack of plates. {% endhint %} ## Push "Pushing" is the act of adding a value on top of the stack. Here's an example: \`\`\` \[$32\] | push v | | |\[$32\]| |\[$B0\]| |\[$B0\]| |\[$A9\]| > |\[$A9\]| |\[$82\]| |\[$B2\]| |\[$14\]| |\[$14\]| ‾‾‾‾‾‾‾ ‾‾‾‾‾‾‾ \`\`\` As you can see, the value $32 is added on top of the stack. The stack now has five values instead of four. ## Pull/Pop "Pulling" (or "popping") is the act of reading a value from the top of the stack. Here's an example: \`\`\` \[$32\] ^ pull | |\[$32\]| | | |\[$B0\]| |\[$B0\]| |\[$A9\]| > |\[$A9\]| |\[$82\]| |\[$B2\]| |\[$14\]| |\[$14\]| ‾‾‾‾‾‾‾ ‾‾‾‾‾‾‾ \`\`\` As you can see, the value $32 is retrieved from the top of the stack. {% hint style="info" %} \*All\* pulling instructions affect the Negative and the Zero processor flags. {% endhint %} ## Push A, X, Y There are opcodes to push the current value inside the A, X or Y registers onto the stack: | Opcode | Full name | Explanation | | ------- | ----------------- | -------------------------------------------- | | \*\*PHA\*\* | Push A onto stack | Pushes the current value of A onto the stack | | \*\*PHX\*\* | Push X onto stack | Pushes the current value of X onto the stack | | \*\*PHY\*\* | Push Y onto stack | Pushes the current value of Y onto the stack | ## Pull A, X, Y There are also opcodes to pull the current value from the stack into the A, X or Y registers: | Opcode | Full name | Explanation | | ------- | ----------- | ------------------------------------------------ | | \*\*PLA\*\* | Pull into A | Pulls a value from the stack into the A register | | \*\*PLX\*\* | Pull into X | Pulls a value from the stack into the X register | | \*\*PLY\*\* | Pull into Y | Pulls a value from the stack into the Y register | ## Example Imagine the following scenario: The X register has to stay at the value $19, no matter what, but you really have to use X for something else. How would one do that? You use PHX to preserve the value in X in the stack, before you use an instruction which modifies the contents of X: \`\`\` ; Imagine X has the value $19 in the stack PHX ; Push X ($19) onto stack. Result: Stack 1st value = $19 LDX $91 ; Load the value in address $7E0091 into X LDA $1000,x ; \\ X is now modified, and we use it to index RAM STA $0100 ; / PLX ; Restore X. X is now $19 again \`\`\` {% hint style="info" %} The A, X and Y registers do not have a separate stack. There is only one stack, specified by the "stack pointer register". For detailed explanations about the stack pointer register, see: \[Stack pointer register\](/assembly-for-the-snes/processor-flags-and-registers/stackpointer.md) {% endhint %} ## 16-bit mode stack operations All the previous explanations and behaviours apply to 16-bit stack operations as well. It's just that you're pushing and pulling 16-bit values, not 8-bit values. This also means that if you push two 8-bit values onto the stack, you can pull a single 16-bit value later. ## Other push and pull operations There are also other push and pull commands, which are not affected by 8-bit or 16-bit mode A, X and Y: | Opcode | Full name | Explanation | Value size | | ------------------------ | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | | \*\*PHB\*\* | Push data bank register | Pushes the data bank register's current value onto the stack | 8-bit | | \*\*PLB\*\* | Pull data bank register | Pulls a value from the stack into the data bank register | 8-bit | | \*\*PHD\*\* | Push direct page register | Pushes the direct page register's current value onto the stack | 16-bit | | \*\*PLD\*\* | Pull direct page register | Pulls a value from the stack into the direct page register | 16-bit | | \*\*PHP\*\* | Push processor flags | Pushes the processor flags register's current value onto the stack | 8-bit | | \*\*PLP\*\* | Pull processor flags | Pulls a value from the stack into the processor flag register | 8-bit | | \*\*PHK\*\* | Push program bank | Pushes the bank value of the currently executed opcode (which is PHK) onto the stack | 8-bit | | \*\*PEA $XXXX\*\* | Push effective address | Pushes the specified 16-bit value onto the stack. Don't let the name of the opcode mislead you. This doesn't read out any address | 16-bit | | \*\*PEI ($XX)\*\* | Push effective indirect address | Pushes the value at the given direct page address onto the stack. The direct page register can affect the given address | 16-bit | | \*\*PER \*\*\*\*\*label\*\*\* | Push program counter relative | A rather complicated push. In short: it pushes the absolute address of the given label onto the stack. What happens during assembly is that the assembler calculates the relative distance between PER and the given label. This relative distance is a signed 16-bit value and is used as the parameter for PER. The opcode finally assembles into \`PER $XXXX\`. Because of its relative nature, the opcode can also be used in the SNES RAM | 16-bit | --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/stack.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/endian.md). # Little-endian Inside the SNES memory, 16-bit and 24-bit values are always stored in "little-endian". Take for example the value $1234 which we store in the RAM; $1234 does not appear as $12 $34. It appears as $34 $12, instead. This is how the SNES works. When this number is read in 16-bit mode, it reads $1234, NOT $3412. The SNES reverses this automatically again. 24-bit values are no exception. Values, such as $123456, are stored in the memory as $56 $34 $12. You can write everything in normal ASM without worrying about little-endian, because everything is dealt with automatically by the SNES and the assembler! You can worry about little-endian when you deal with 16-bit values in 8-bit mode. For example: if you ever store the value $1234 at address $7E0000, it is stored as $34 $12. Then, if you ever want to access the low byte of $1234 (which is $34), you would need to read $7E0000, NOT $7E0001. The concept of little-endian is especially important when dealing with "pointers", which is explained later in this tutorial. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/endian.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic.md). # Bitwise operations Bitwise operations are fundamental when it comes to assembly. The 65c816 supports several bitwise opcodes, which are explained in this chapter. Bitwise operators mainly work on bits rather than bytes, so in this chapter, vague terms such as \`x\` and \`y\` refer to bits rather than bytes. In this chapter, we will sometimes refer to binary value 1 as \`true\` and binary value 0 as \`false\`. ## AND AND is an opcode which affects the accumulator by applying a logical AND on all bits. | Opcode | Full name | Explanation | | ------- | ----------- | ---------------------------------------------------------------------- | | \*\*AND\*\* | Logical AND | Logical AND, also known as a "\`&\`" in some other programming languages | The result of \`x AND y\` is \`true\` if both \`x\` and \`y\` evaluate to \`true\`. Otherwise, the result is \`false\`. Here's an example of a logical AND. \`\`\` LDA #$F0 ; A = 1111 0000 AND #$98 ; AND 1001 1000 ; A = 1001 0000 = $90 \`\`\` To understand this code, you'll have to read the comments from top to bottom. As you can see, the first line contains the accumulator's binary value, while the second line contains the AND operator's parameter binary value. When two 1 come together, the result is a 1 as well. This is what it means when both x and y evaluate to true. If x = 1 and y = 1, then the result is 1 also. This rule can be written in a "truth table". | Compared bit | AND operation | Result | | ------------ | ------------- | ------ | | 1 | 1 | 1 | | 0 | 1 | 0 | | 1 | 0 | 0 | | 0 | 0 | 0 | In short, whenever there's a 0 in A or in the AND value's bit, the resulting bit is also 0. ## ORA ORA is an opcode which affects the accumulator by applying a logical OR on all bits. | Opcode | Full name | Explanation | | ------- | ---------- | ------------------------------------------------------------------------------ | | \*\*ORA\*\* | Logical OR | Logical OR, also known as a pipe character in some other programming languages | The result of \`x OR y\` is true if either \`x\` or \`y\` evaluates to \`true\`. Otherwise, the result is \`false\`. Here's an example of an ORA: \`\`\` LDA #$F0 ; A = 1111 0000 ORA #$87 ; ORA 1000 0111 ; A = 1111 0111 = $F7 \`\`\` If one of the bits has 1, the resulting bit will be 1. After ORA, the result will be stored in A. Here's the truth table for ORA: | Compared bit | OR operation | Result | | ------------ | ------------ | ------ | | 1 | 1 | 1 | | 0 | 1 | 1 | | 1 | 0 | 1 | | 0 | 0 | 0 | So basically, whenever A or the ORA value's bit is 1, the resulting bit is also 1. ## EOR EOR is an opcode which affects the accumulator by applying a logical exclusive OR (XOR) on all bits. | Opcode | Full name | Explanation | | ------- | ----------- | ---------------------------------------------------------------------- | | \*\*EOR\*\* | Logical XOR | Logical XOR, also known as a "\`^\`" in some other programming languages | The result of \`x XOR y\` is \`true\` if \`x\` evaluates to \`true\` and \`y\` evaluates to \`false\`, or \`x\` evaluates to \`false\` and \`y\` evaluates to \`true\`. Otherwise, the result is \`false\`. Here's an example of an XOR: \`\`\` LDA #$99 ; A = 1001 1001 EOR #$F0 ; EOR 1111 0000 ; A = 0110 1001 = $69 \`\`\` Basically, when the accumulator and the given value's bits are both 1, the result is 0. When they're both 0, the result is 0. When they're not equal, then the result is 1 also. Here's the truth table for EOR: | Compared bit | XOR operation | Result | | ------------ | ------------- | ------ | | 1 | 1 | 0 | | 0 | 1 | 1 | | 1 | 0 | 1 | | 0 | 0 | 0 | ## BIT BIT is an opcode which essentially does a logical AND, but the result is NOT stored in the accumulator. Instead, it only affects the processor flags. | Opcode | Full name | Explanation | | ------- | --------- | ----------------------------------------------- | | \*\*BIT\*\* | Bit test | Tests certain bits of the accumulator or memory | Here's an example of BIT in usage: \`\`\` LDA #$04 ; A = 0000 0100 = $04 BIT #$00 ; AND 0000 0000. None of the bits are set ; so normally, A should be $00, but it's still $04 ; However, the zero flag has been set because ; the outcome \*should\* be $00. \`\`\` BIT has a feature which distinguishes it from a regular AND, involving processor flags other than the zero flag. When you use BIT on an address, rather than the accumulator, it can also affect the "negative" and "overflow" processor flags. If bit 7 of the address' value is set, the negative flag would be set as a result. If bit 6 of the address' value is set, the overflow flag would be set as a result. Here's an example of using BIT on an address: \`\`\` BIT $04 ; Bit test $7E0004's value \`\`\` If $7E0004's value was $80 (1000 0000), the negative flag would be set and the overflow flag would be clear. It's useful to check really fast if the value of an address is negative. If $7E0004's value was $40 (0100 0000), the negative flag would be clear and the overflow flag would be set. Useful to check if specifically bit 6 is set. If $7E0004's value was $C0 (1100 0000), both the negative and overflow flags would be set. Coincidentally enough, the bits for negative (bit 7) and overflow (bit 6) correspond to the bits in the processor flag register: \`nvmxdizc\`. | Set bits | BIT result | | --------- | ----------------------------- | | Bit 7 | Negative flag set | | Bit 6 | Overflow flag set | | Bits 7, 6 | Negative & overflow flags set | When you are performing a BIT operation on a RAM address, the N and V flags will be set or cleared, regardless of the value in the accumulator. The zero flag depends on the accumulator's value and the RAM address' value. So, BIT with a RAM address does both AND, and an inevitable check of bits 7 and 6 of the RAM address. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/logic.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/repsep.md). # Changing the processor flags As seen in the \[previous chapter\](/assembly-for-the-snes/processor-flags-and-registers/flags.md), the SNES supports 9 processor flags. There are ways to affect these processor flags, which are explained in this chapter. ## SEP | Opcode | Full name | Explanation | | ------- | ------------------- | ----------------------------------- | | \*\*SEP\*\* | Set processor flags | Sets specified processor flags to 1 | SEP sets the selected processor flag bits to 1. SEP is used as following: \`\`\` ;nvmxdizc = 0000 0000 SEP #$80 ;= 1000 0000 ;Nvmxdizc = 1000 0000 \`\`\` The uppercased letters are the activated processor flags. This code sets the negative flag. ## REP | Opcode | Full name | Explanation | | ------- | --------------------- | ----------------------------------- | | \*\*REP\*\* | Reset processor flags | Sets specified processor flags to 0 | REP resets the selected processor flag bits to 0. REP is used as following: \`\`\` ;NvmxDizc = 1000 1000 REP #$08 ;= 0000 1000 ;Nvmxdizc = 1000 0000 \`\`\` In the beginning, the decimal mode was enabled, and the negative flag was set, but after \`REP #$08\`, the decimal mode flag got disabled and the negative flag is still set. ## XCE and the emulation mode Because the emulation mode bit is "hidden" above the carry flag, there's a dedicated opcode which exchanges the carry flag with the emulation mode flag. | Opcode | Full name | Explanation | | ------- | ---------------------------- | ------------------------------------------------------ | | \*\*XCE\*\* | Exchange carry and emulation | Swaps the values of carry flag and emulation mode flag | You can access the emulation mode by using \`SEC\` then \`XCE\`, and leave it using \`CLC\` then \`XCE\`. By default, SNES starts up in emulation mode. This is why you often see the opcodes \`CLC\` and \`XCE\` among the very first opcodes in disassemblies. ## Other opcodes There are other opcodes which immediately affect a single processor flag. | Opcode | Full name | Explanation | | ------- | ---------------------------- | --------------------------------------------------------------------------------- | | \*\*SEI\*\* | Set interrupt disable flag | Sets the interrupt disable flag, setting it to 1, thus disabling IRQ | | \*\*CLI\*\* | Clear interrupt disable flag | Clears the interrupt disable flag, setting it to 0, thus enabling IRQ | | \*\*SED\*\* | Set decimal flag | Sets the decimal mode flag, setting it to 1, changing the SNES into decimal mode | | \*\*CLD\*\* | Clear decimal flag | Clears the decimal flag, setting it to 0, changing the SNES into hexadecimal mode | | \*\*SEC\*\* | Set carry flag | Sets the carry flag, setting itt o 1 | | \*\*CLC\*\* | Clear carry flag | Clears the carry flag, setting it to 0 | | \*\*CLV\*\* | Clear overflow flag | Clears the overflow flag, setting it to 0 | --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/repsep.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cycles.md). # Machine cycles The SNES processes instructions, but each instruction takes up a predetermined amount of time to execute. The time an instruction takes to execute is called "machine cycle" (or "cycle" in short). Each instruction has its own cycle. \[This page\](https://wiki.superfamicom.org/65816-reference) has a full reference of how many cycles each instruction takes. Pay attention to the footnotes, as the amount of used cycles can differ depending on the context of the code. For example, a taken branch takes 1 cycle longer compared to a branch that's not taken. The less cycles, the less slowdown the code suffers from. Slowdown is often noticeable in games with many sprites on the screen. To avoid slowdown, you need to write efficient code. Here is an example of inefficient vs. efficient code: \`\`\` ; Inefficient LDA #$00 ; 2 cycles STA $7E0000 ; 5 cycles ; = 7 cycles ; Efficient LDA #$00 ; 2 cycles STA $00 ; 3 cycles ; = 5 cycles ; Very efficient STZ $00 ; 3 cycles ; = 3 cycles \`\`\` At first, we use a full notation to write the value $00 to address $7E0000. But then, in the next example, we shorten the address, saving 2 cycles. Finally, we figure that we can use \`STZ\`, as we store zero to an address anyway. Having a low cycle count is especially important when executing code during an NMI, because there are limited machine cycles there. Exceeding that limit causes black scanlines to flicker on the top of the SNES display. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cycles.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing.md). # Addressing modes revisted In the \[basic addressing modes\](/assembly-for-the-snes/the-fundamentals/addressing.md) chapter, we briefly looked at the most commonly used addressing modes: "Immediate 8-bit and 16-bit", "direct page", "absolute" and "long". In this chapter, we will look at the more advanced addressing modes: "indirect", "indexed" and "stack relative". These advanced addressing modes expand upon the earlier introduced addressing modes. This chapter also introduces the concept of pointers. ## Pointers This is where "pointers" come into play. Pointers are values which pretty much "point" to a certain memory location. Imagine the following SNES memory: \`\`\` ; $7E0000: 12 80 00 55 55 55 .. \`\`\` In this example: the RAM at address $7E0000 contains the values $12, $80 and $00. This is in little-endian, so reverse the values and you get $00, $80, $12. Treat this as a 24-bit "long" address and you have $008012. This means that RAM address $7E0000 has a 24-bit pointer to $008012. This is what "indirect" is; accessing address $7E0000 "indirectly" accesses address $008012. You can access indirect pointers in two ways: 16-bits and 24-bits. They have a special assembler syntax: | Syntax | Terminology | Pointer size | | ------ | ------------- | ------------ | | ( ) | Indirect | 16-bit | | \\\[ \] | Indirect long | 24-bit | The bank byte of the 16-bit pointer depends on the type of instruction. When it comes to a \`JSR\`-opcode, which can only jump inside the current bank, the bank byte isn"t determined nor used. However, when it comes to a loading instruction, such as \`LDA\`, the bank byte is determined by the \*data bank register\*. Pointers can point to both \*code\* and \*data\*. Depending on the type of instruction, the pointers are accessed differently. For example, a \`JSR\` which utilizes a 16-bit pointer accesses the pointed location as code, while an \`LDA\` accesses the pointed location as a bank. ## Indirect Indirect addressing modes are basically accessing addresses in such a way, that you access the address they point to, rather than directly accessing the contents of the specified address. ### Direct, Indirect As contradicting as it may sound, the naming actually makes sense. "Direct" stands for direct page addressing mode, while "indirect" means that we're accessing a pointer at the direct page address, rather than a value. Here's an example: \`\`\` ; Setup indirect pointer REP #$20 LDA #$1FFF ; $1FFF to RAM $7E0000 STA $00 SEP #$20 ; $7E0000: FF 1F .. .. .. .. .. ; Access indirect pointer LDA ($00) ; Load the value at address $1FFF into A \`\`\` This accesses a 16-bit pointer at address $000000, due to the nature of direct page always accessing bank $00. Due to the effects of mirroring in the SNES mirroring, practically speaking, this accesses a 16-bit pointer at RAM $7E0000. You might think that \`LDA ($00)\` loads the \`value $1FFF\` into A. However, it doesn't work that way. It loads the \`value in address $1FFF\` into A, because we use an indirect addressing mode. As we established earlier, parentheses denote 16-bit pointers. The bank of the indirect address, in the case of an LDA, depends on the data bank register. As a result, the \`LDA ($00)\` resolves into \`LDA $1FFF\`. ### Direct Indexed with X, Indirect As is the case with the previous addressing mode, the naming may seem contradicting. "Direct" stands for direct page addressing mode. "Indexed with X" means that this direct page address is indexed with X. "Indirect" means that the previous elements are treated as an indirect address. Here's an example usage: \`\`\` ; Setup indirect pointers REP #$20 LDA #$1FFF ; $1FFF to RAM $7E0000 STA $00 LDA #$0FFF ; $0FFF to RAM $7E0002 STA $02 SEP #$20 ; $7E0000: FF 1F FF 0F .. .. .. ; Access indirect pointer LDX #$02 ; Set X to $02 LDA ($00,x) ; Loads the value at address $0FFF into A \`\`\` \* The 16-bit value in address $7E0000 + $7E0001 is \`$1FFF\`. \* The 16-bit value in address $7E0002 + $7E0003 is \`$0FFF\`. Thanks to using X as an indexer to the direct page address, \`LDA ($00,x)\` is resolved into \`LDA ($02)\`. This then resolves into \`LDA $0FFF\` because RAM $7E0002 points to \`address $0FFF\`. ### Direct, Indirect Indexed with Y This is practically the same as \`Direct, Indirect\` but the pointer is then indexed with the Y register: \`\`\` ; Setup indirect pointer REP #$20 LDA #$1FF0 ; $1FF0 to RAM $7E0000 STA $00 SEP #$20 ; $7E0000: F0 1F .. .. .. .. .. ; Access indirect pointer LDY #$01 ; Set Y to $01 LDA ($00),y ; Load value at address $1FF1 into A \`\`\` As a result, \`LDA ($00),y\` resolves into \`LDA $1FF0,y\`, which then resolves into \`LDA $1FF1\` because Y contains the value $01. ### Absolute, Indirect Exactly the same as \`Direct, Indirect\`, except the specified address is now 16-bit instead of 8-bit. This addressing mode is only used by jumping instructions. Example: \`\`\` ; Setup indirect pointer REP #$20 LDA #$8000 ; $8000 to RAM $7E0000 STA $00 SEP #$20 ; $7E0000: 00 80 .. .. .. .. .. ; Access indirect pointer JMP ($0000) ; Jumps to $8000. \`\`\` This has the same exact effect as the example in \`Direct, Indirect\`. As a result, the \`JMP ($0000)\` resolves into \`JMP $8000\`, thus jumps to \`address $8000\` in the current bank. ### Absolute Indexed with X, Indirect Exactly the same as \`Direct Indexed with X, Indirect\`, except the specified address is now 16-bit instead of 8-bit. This addressing mode is only used by jumping instructions. Example: \`\`\` REP #$20 LDA #$8000 ; $8000 to RAM $7E0000 STA $00 LDA #$9000 ; $9000 to RAM $7E0002 STA $02 SEP #$20 ; $7E0000: 00 80 00 90 .. .. .. ; Access indirect pointer LDX #$02 ; Set X to $02 JMP ($0000,x) ; Jumps to $9000 \`\`\` \* The 16-bit value in address $7E0000 + $7E0001 is \`$8000\`. \* The 16-bit value in address $7E0002 + $7E0003 is \`$9000\`. Thanks to using X as an indexer to the direct page address, \`JMP ($0000,x)\` is resolved into \`JMP ($0002)\`. This then resolves into \`JMP $9000\` because RAM $7E0002 points to \`address $9000\`. This addressing mode is handy for jump tables. ### Direct, Indirect Long Exactly the same as \`Direct, Indirect\`, except the pointer located at an address is now 24-bits instead of 16-bits, meaning the bank byte of a pointer is also specified. Example: \`\`\` REP #$20 LDA #$1FFF ; $1FFF to RAM $7E0000 STA $00 SEP #$20 LDX #$7F ; $7F to RAM $7E0002 STX $02 ; $7E0000 now contains the 24-bit pointer $7F1FFF ; $7E0000: FF 1F 7F .. .. .. .. LDA \[$00\] ; Load the value at address $7F1FFF into A \`\`\` \`LDA \[$00\]\` resolves into \`LDA $7F1FFF\`. ### Direct, Indirect Indexed Long with Y Exactly the same as \`Direct, Indirect Indexed with Y\`, except the pointer located at an address is now 24-bits instead of 16-bits, meaning the bank byte of a pointer is also specified. Example: \`\`\` ; Setup indirect pointer REP #$20 LDA #$1FF0 ; $1FF0 to RAM $7E0000 STA $00 SEP #$20 LDX #$7F ; $7F to RAM $7E0002 STX $02 ; $7E0000 now contains the 24-bit pointer $7F1FF0 ; $7E0000: F0 1F 7F .. .. .. .. ; Access indirect pointer LDY #$01 ; Set Y to $01 LDA \[$00\],y ; Load value at address $7F1FF1 into A \`\`\` As a result, \`LDA \[$00\],y\` resolves into \`LDA $7F1FF0,y\` (practically speaking), which then resolves into \`LDA $7F1FF1\` because Y contains the value $01. ## Indexed Indexed addressing mode was actually briefly touched upon in an \[earlier chapter\](/assembly-for-the-snes/collection-of-values/indexing.md). This chapter will discuss all the possible indexed addressing modes. ### Direct, Indexed with X This addressing mode indexes a direct page address with X. Example: \`\`\` LDX #$02 LDA $00,x ; Loads the value at address $7E0002 into A \`\`\` ### Direct, Indexed with Y This addressing mode indexes a direct page address with Y. This addressing mode only exists on the \`LDX\` and \`STX\`-opcodes. Example: \`\`\` LDY #$02 LDX $00,y ; Loads the value at address $7E0002 into X \`\`\` ### Absolute, Indexed with X This addressing mode indexes an absolute address with X. Example: \`\`\` LDX #$02 LDA $0000,x ; Loads the value at address $7E0002 into A \`\`\` ### Absolute, Indexed with Y This addressing mode indexes an absolute address with Y. Example: \`\`\` LDY #$02 LDA $0000,y ; Loads the value at address $7E0002 into A \`\`\` ### Absolute, Long Indexed with X This addressing mode indexes a long address with X. Example: \`\`\` LDX #$02 LDA $7E0000,x ; Loads the value at address $7E0002 into A \`\`\` ## Stack Relative Stack relative is a special type of an indexing addressing mode. It uses the stack pointer register as a 16-bit index, rather than using the X or Y register. The index is \*\*always\*\* 16-bit, regardless of the register size of A, X and Y. ### Stack Relative This loads a value from the RAM, relative to the stack pointer. The bank byte is always $00. Example: \`\`\` ; Stack pointer register: $1FF0 LDA $00,s ; ($001FF0) Loads the value in the current free slot in the stack, into A. LDA $01,s ; ($001FF1) Loads the last pushed value into A. LDA $02,s ; ($001FF2) Loads the second last pushed value into A. LDA $03,s ; ($001FF3) ... \`\`\` In 16-bit A mode, these instructions would read 16-bit values, rather than 8-bit values. If you don't use increments of 2, you'll start reading overlapping values. ### Stack Relative, Indirect Indexed with Y This is pretty much the same as \`Direct, Indirect Indexed with Y\`, except the value is loaded from a stack relative address. Example: \`\`\` ; Stack pointer register = $01FD REP #$20 LDA #$0100 PHA SEP #$20 LDY #$03 LDA ($01,s),y ; → LDA ($01FE),y → LDA $0100,y → LDA $0103 \`\`\` \`$01,s\` refers to the last pushed value into A, which is $0100 in the case of this example. The parentheses applies on this stack relative address, resolving the instruction to an \`LDA $0100,y\`. This finally resolves into \`LDA $0103\` because of the indexer. This addressing mode is handy if you'd like to treat certain pushed values as an indexed memory address. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/addressing.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Hexadecimal | Assembly for the SNES This site uses cookies to deliver its service and to analyze traffic. By browsing this site, you accept the [privacy policy](https://policies.gitbook.com/privacy/cookies) . AcceptReject --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/binary.md). # Binary Another important counting system is "binary". Binary has only two possible digits for each place value: 0 and 1. A binary digit is also called a "bit". In assembly syntax, bits are prefixed by "%". A byte is made of eight "bits". Because a binary digit has two possible values, and a byte has 8 bits, this means there are 2⁸ possible values in a byte. For example, a byte can consist of the following bits: \`1001 0110\` or \`1001 0101\`. The first bit from the left is called "bit 7" and the final bit is called "bit 0". They are NOT called bits 0-7, nor bits 8-1. Here's an overview: \`\`\` Bit 7654 3210 1001 0110 1001 0101 .... .... \`\`\` The table below shows a relatively easy way to memorize binary, as it displays a pattern. | Binary | Hexadecimal | | ------------ | ----------- | | \`%0000 0001\` | \`$01\` | | \`%0000 0010\` | \`$02\` | | \`%0000 0100\` | \`$04\` | | \`%0000 1000\` | \`$08\` | | \`%0001 0000\` | \`$10\` | | \`%0010 0000\` | \`$20\` | | \`%0100 0000\` | \`$40\` | | \`%1000 0000\` | \`$80\` | Note that there is a space inbetween 4 bits for easier readability, although assemblers generally don't accept this syntax. Groups of 4 bits are called "nibbles" and for the purposes of this chapter, they are there to make binary easier to read, because one nibble corresponds to one digit in hexadecimal. The SNES is capable of working with both 8-bit and 16-bit numbers. While 8-bit numbers are called a byte, 16-bit numbers are called a "word". They would look like they have 16 bits in binary (e.g. \`10000101 11010101\`, which is \`$85D5\` in hexadecimal). In the case of 16-bit numbers, the leftmost bit is called "bit 15" while the rightmost bit is called "bit 0": \`\`\` 1111 11 (read from top to bottom) Bit 5432 1098 7654 3210 1000 0101 1101 0101 0000 0000 1001 0110 .... .... .... .... \`\`\` ## Flags Binary is immensely useful when you're giving a hex value multiple purposes, kind of like an on/off toggle for certain features. These bits are called "flags" and are generally used to save space in the working memory of games. For example, you can divide a byte into 8 bits with each bit having a different meaning. Bit 7 could indicate that a level has rain or not. Bit 6 could indicate that a level layout is horizontal or vertical. Bit 5 could indicate that the level setting is during day or night, etc. This way you can compress information into a single byte. It would look like this in binary: \`\`\` 10100000 ││└───── "Is daytime" flag │└───── "Is horizontal level" flag └───── "Is raining" flag \`\`\` Finally, here's an overview of how to count up in decimal, hexadecimal and binary: | Decimal | Hexadecimal | Binary | | ------- | ----------- | ------------ | | \`00\` | \`$00\` | \`%0000 0000\` | | \`01\` | \`$01\` | \`%0000 0001\` | | \`02\` | \`$02\` | \`%0000 0010\` | | \`03\` | \`$03\` | \`%0000 0011\` | | \`04\` | \`$04\` | \`%0000 0100\` | | \`05\` | \`$05\` | \`%0000 0101\` | | \`06\` | \`$06\` | \`%0000 0110\` | | \`07\` | \`$07\` | \`%0000 0111\` | | \`08\` | \`$08\` | \`%0000 1000\` | | \`09\` | \`$09\` | \`%0000 1001\` | | \`10\` | \`$0A\` | \`%0000 1010\` | | \`11\` | \`$0B\` | \`%0000 1011\` | | \`12\` | \`$0C\` | \`%0000 1100\` | | \`13\` | \`$0D\` | \`%0000 1101\` | | \`14\` | \`$0E\` | \`%0000 1110\` | | \`15\` | \`$0F\` | \`%0000 1111\` | | \`16\` | \`$10\` | \`%0001 0000\` | | \`17\` | \`$11\` | \`%0001 0001\` | | ... | ... | ... | | \`254\` | \`$FE\` | \`%1111 1110\` | | \`255\` | \`$FF\` | \`%1111 1111\` | ## Notation Sometimes, bits could be written inconsistently, like \`11\` or \`110 0000\`. This makes the binary number harder to read, because the general convention is to write bits in groups of eight. In order to read them, you will need to add leading 0s to the digits until there are either 8 bits or 16 bits in total. In 8-bit: \* \`11\` becomes \`00000011\` \* \`1100000\` becomes \`01100000\` In 16-bit: \* \`11\` becomes \`00000000 00000011\` \* \`1100000\` becomes \`00000000 01100000\` You can convert between decimal, hexadecimal and binary, by using Windows calculator's "programming" mode. There are also many calculators online which can do this. Assembly syntax accepts decimal and binary also, so you usually don't need to convert between decimal and hexadecimal. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/binary.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Loading and storing | Assembly for the SNES This site uses cookies to deliver its service and to analyze traffic. By browsing this site, you accept the [privacy policy](https://policies.gitbook.com/privacy/cookies) . AcceptReject --- # The processor flags | Assembly for the SNES This site uses cookies to deliver its service and to analyze traffic. By browsing this site, you accept the [privacy policy](https://policies.gitbook.com/privacy/cookies) . AcceptReject --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/contributing.md). # Contributing This chapter concerns the writers of this tutorial. When writing, there are a few standards to follow in order to maximize consistency throughout the tutorial. ## Address explanations This section concerns the way addresses are written, as to avoid confusion. ### Direct page \* For the sake of simplicity and explanation, all direct page addresses ("\`$xx\`") shall use address \`$7E0000\` as its base. This shall be emphasized in code block comments, as well as explanations. Example: \`\`\` LDA #$42 STA $08 ; Store the value $42 into address $7E0008 \`\`\` ### Absolute addressing \* All absolute addresses ("$xxxx") use address \`$7E0000\` as its base, if the address is between $0000-$7FFF inclusive. For addresses between $8000-$FFFF, address \`$000000\` shall be used as its base instead. This shall be emphasized in code block comments, as well as explanations. Example: \`\`\` LDA $8002 ; Load the value at address $008002 into A STA $1008 ; Store that value into address $7E1008 \`\`\` ## Styling This section concern the styling of the document. ### Inline code \* All opcodes and instructions shall be surrounded by the markdown backtick (\\\`), regardless of the context. Example: The opcode \`LDA\` is used to load values into the A register. Thus, \`LDA #$49\` loads the value $49 into A. This then can be increased to the value $4A by using \`INC A\`. ### Markdown tables \* Sentences and phrases within table cells generally shouldn't end with a period. \* Tables introducing opcodes should have the opcodes in \*\*bold\*\* and have at least the three columns in below example: | Opcode | Full name | Explanation | | ------- | --------------------- | ------------------- | | \*\*LDA\*\* | Load into accumulator | Load a value into A | ## Terminology This section concerns the usage of certain words in certain context. | Rule | Example | | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | | When referring to the \`Ricoh 5A22\` processor, use \`the SNES\` instead | \`The SNES\` is capable of entering 8-bit or 16-bit mode | | When referring to a specific area in the SNES memory, always prepend \`address\` to it, preferably with the memory area (i.e. \`RAM\`) | (The) \`RAM address\` $7E0000 contains \\\[...\] | | When referring to the accumulator (A) or the index registers (X and Y), just use \`A\`, \`X\` or \`Y\` | \`A\` is now in 8-bit mode. \`X\` is used to index addresses | | When referring to values, always prepend \`value\` to it | A contains the \`value\` $00 | ## Example codes \* Code shall use indentation when there are labels, sublabels or plus/minus labels on the same line as an instruction. The amount of indentation equals the length of the longest aforementioned type of label in the code block, including colon ("\`:\`"), plus two additional spaces. \* Code shall use whitespace for indentation, not tabs. \* Opcodes are written entirely in uppercase (e.g.: \`LDA\`). \* Labels are written in PascalCase (e.g.: \`Label1:\`). \* Sublabels are written entirely in lower, underscore-case and the name should semantically suit the parent label, without redundancy (e.g.: \`.return\`). \* Defines are written in PascalCase (e.g.: \`!SomeDefine\`). \* Direct data (\`db\`, \`dw\`, \`dl\`, \`dd\`), indexers (\`,x\`, \`,y\`, \`,s\`) and opcode length specifiers (\`.b\`, \`.w\`, \`.l\`) are written entirely in lowercase. \* Comment indicators (i.e. \`;\`) shall start on column 20, and left-padded by whitespace, not tabs. \* If there's no space for a comment on column 20, it should start on the same line anyway. \* In fact, never use tabs. \* Comment indicators shall be followed by a whitespace, before the comment itself. \* There will be an extra new line after the opcodes \`RTS\`, \`RTL\`, \`RTI\`, \`JMP\`, \`JML\`, \`BRA\`, \`BRL\`. \* These are guidelines which are to be followed as strictly as possible, but there may be exceptional cases. Use your best judgment. Examples: \`\`\` SomeLabel: ; This label is on its own line LDA.b #$42 STA $00 ; This is a comment RTS .table: db $01,$02,$03,$04 ; Another comment .second\_table: db $01,$02,$03,$04,$01,$02,$03,$04 ; An exceptional comment \`\`\` \`\`\` TestLabel: LDA #$02 ; This label is on the same line as an instruction STA $01 BNE + NOP + RTS ; The code is indented according to "TestLabel", not "+" \`\`\` --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/contributing.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/readme.md). # Introduction This tutorial is an online version of my 65c816 assembly tutorial which is hosted on \[SMW Central\](https://www.smwcentral.net/). I originally wrote this tutorial in order to teach the SMW Central community the 65c816 assembly language in plain English. Nowadays, it's read by various people in the ROM hacking scene in general. Therefore, I decided to open source this tutorial on GitHub, so that people can make improvements or translations. Although I'm a member of SMW Central, this tutorial is not associated with Super Mario World, thus this tutorial is not tailored towards that game. Instead, this tutorial can be applied in all SNES context. ## The language 65c816 assembly is the language used by the Super Nintendo Entertainment System's (SNES) Ricoh 5A22 chip. Breaking down the different parts of the acronym 65c816: 816 means that the processor can be either 8-bit mode or 16-bit mode. The c stands for CMOS, 65 means that this processor is from the 65xx CPU family. The processor is supposed to be pretty revolutionary for its time. This tutorial explains mnemonics/instructions (i.e. opcodes) and how to use them properly. This tutorial does not focus on SNES-specific topics, such as hardware registers. With 65c816 ASM you can code things for SNES games (such as custom features for Super Mario World). ASM is a 2nd generation programming language, which is low-level compared to C# for example. It is readable machine code, which eventually gets translated into hexadecimal machine code. All the opcodes consist of 3 letters, along with various parameters. ## Special thanks Many special thanks go to the following people for reviewing the original tutorial on SMW Central: \[\*\*spigmike\*\*\](https://www.smwcentral.net/?p=profile\\&id=132)\*\*,\*\* \[\*\*Roy\*\*\](https://www.smwcentral.net/?p=profile\\&id=845)\*\*,\*\* \[\*\*smkdan\*\*\](https://www.smwcentral.net/?p=profile\\&id=411)\*\*,\*\* \[\*\*S.N.N\*\*\](https://www.smwcentral.net/?p=profile\\&id=23)\*\*,\*\* \[\*\*andy\\\_k\\\_250\*\*\](https://www.smwcentral.net/?p=profile\\&id=67)\*\*,\*\* \[\*\*Domiok\*\*\](https://www.smwcentral.net/?p=profile\\&id=7211)\*\*,\*\* \[\*\*reghrhre\*\*\](https://www.smwcentral.net/?p=profile\\&id=4176)\*\*,\*\* \[\*\*ChaoticFox\*\*\](https://www.smwcentral.net/?p=profile\\&id=3462)\*\*,\*\* \[\*\*Tails\\\_155\*\*\](https://www.smwcentral.net/?p=profile\\&id=6151)\*\*,\*\* \[\*\*GreenHammerBro\*\*\](https://www.smwcentral.net/?p=profile\\&id=18802)\*\*,\*\* \[\*\*Vitor Vilela\*\*\](https://www.smwcentral.net/?p=profile\\&id=8251) Many special thanks also go to the \[contributors\](https://github.com/Ersanio/snes-assembly-book/graphs/contributors) of this repository! --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/readme.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Arithmetic operations | Assembly for the SNES This site uses cookies to deliver its service and to analyze traffic. By browsing this site, you accept the [privacy policy](https://policies.gitbook.com/privacy/cookies) . AcceptReject --- # Tables and indexing | Assembly for the SNES This site uses cookies to deliver its service and to analyze traffic. By browsing this site, you accept the [privacy policy](https://policies.gitbook.com/privacy/cookies) . AcceptReject --- # Addressing modes revisted | Assembly for the SNES This site uses cookies to deliver its service and to analyze traffic. By browsing this site, you accept the [privacy policy](https://policies.gitbook.com/privacy/cookies) . AcceptReject --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/stackpointer.md). # Stack pointer register The stack pointer register is a 16-bit register which the processor uses to determine the current stack location in the SNES RAM. After each push, the stack pointer \*decreases\*, so the bytes are pushed backwards. They overwrite the RAM address' contents. Conversely, after each pull, the stack pointer \*increases\*, but the pulled value still remains in the stack. Therefore, a pull is actually a read. The stack pointer increases or decreases by 1 when it deals with an 8-bit value, and by 2 when it deals with a 16-bit value. This can be summarized as follows: | Operation | Where | Stack pointer modification | | ------------- | ------------------------- | -------------------------- | | Push (8-bit) | Stack pointer location | -1 | | Pull (8-bit) | Stack pointer location +1 | +1 | | Push (16-bit) | Stack pointer location | -2 | | Pull (16-bit) | Stack pointer location +1 | +2 | The stack pointer register assumes that it works with bank $00, regardless of the current value of the data bank register. This means that from the SNES memory mapping point of view, if the stack pointer ever starts pointing to absolute address $8000 or above, it'll start pointing to the ROM. If it points to absolute address $2000 or above, it'll start pointing to the SNES hardware registers. Therefore, the only useable stack area is $000000-$001FFF. The stack doesn't have a defined size. Instead, you, as a programmer, just reserve an area of RAM for the stack you think you need. The reason it doesn't have a defined size is because as long as you keep pushing, the stack pointer keeps decreasing without any set limits. If you push too many values, you might accidentally overwrite other RAM addresses which have other predetermined purposes. ## Push Here is an example of how the stack works from the RAM's point of view, when you push an 8-bit value: \`\`\` ;Stack: .. 55 55 55 55 55 55 .. LDA #$42 └─Stack pointer points to this value PHA ;Stack: .. 55 55 55 55 55 42 .. LDA #$AA └─Stack pointer now points to this value PHA ;Stack: .. 55 55 55 55 AA 42 .. └─Stack pointer now points to this value \`\`\` Here is an example of a 16-bit push: \`\`\` REP #$20 ;16-bit A mode ;Stack: .. 55 55 55 55 55 55 .. LDA #$4210 └─Stack pointer points to this value PHA ;Stack: .. 55 55 55 55 10 42 .. LDA #$AA99 └─Stack pointer now points to this value PHA ;Stack: .. 55 55 99 AA 10 42 .. └─Stack pointer now points to this value \`\`\` ## Pull When you pull something from the stack, it gets pulled from the location of the stack pointer, +1. After each pull, the stack pointer increases depending on the size of the pulled value. You do not literally extract the byte out of the RAM. You just copy the byte to the register you pull it into. The byte in the stack does not reset or anything. It remains the same. Here's an example of pulling an 8-bit value: \`\`\` ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer points to this value PLA ; A is now $34 ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer now points to this value PLA ; A is now $56 ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer now points to this value \`\`\` Here's an example of pulling a 16-bit value: \`\`\` REP #$20 ;16-bit A mode ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer points to this value PLA ; A is now $3412 ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer now points to this value PLA ; A is now $7856 ;Stack: .. 55 55 12 34 56 78 .. └─Stack pointer now points to this value \`\`\` --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/stackpointer.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/shorter-addresses.md). # Shorter addresses It's possible to shorten addresses, but there are prerequisites. In order to shorten a long RAM address into an absolute (4-digit) address, the address has to be between $7E0000-$7E1FFF. $7E1234 can be shortened to $1234 for example. If you shorten address $7E2000 or higher into a 4-digit address, you'll write to areas other than the RAM. It has to do with the data bank register and the SNES memory map. If you want to shorten long RAM addresses to a direct page (2-digit) address, the high and low bytes of the long address must never exceed the value $00FF. The address you want to store to must be in bank $00 or $7E. So you can shorten \`LDA $7E0001\` to \`LDA $01\` and \`STA $000001\` to \`STA $01\`. It's often necessary to write shorter addresses as parameters, as certain opcodes don't support certain addressing modes. For example, the STZ opcode does not support long addresses, so you can't write \`STZ $7E1234\`. You'll have to write \`STZ $1234\` instead. Keep in mind that when you use 2 digits for loading and storing, the bank is always $00 by default, regardless of the data bank! You can use 2-digit addresses for RAM addresses $7E0000-$7E00FF, because RAM $7E0000-$7E1FFF is mirrored in banks $00-$3F by default. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/shorter-addresses.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector.md). # Hardware vectors Hardware vectors are a group of pointers that define how the SNES should handle certain interrupts within the ROM. Each vector is two bytes in size and point to instructions located in bank \`$00\`. The hardware vectors are grouped by the \[two modes of the SNES\](/assembly-for-the-snes/processor-flags-and-registers/flags.md): "emulation mode" vectors and "native mode" vectors. ## Native mode vectors These vectors come into play when the SNES runs in native mode (E=0). | ROM address | Vector | Additional notes | | ----------- | ------------ | ------------------------------------------------------------------------ | | \`$00:FFE0\` | Unused | | | \`$00:FFE2\` | Unused | | | \`$00:FFE4\` | COP vector | Triggered by the \[COP opcode\](/assembly-for-the-snes/deep-dives/misc.md) | | \`$00:FFE6\` | BRK vector | Triggered by the \[BRK opcode\](/assembly-for-the-snes/deep-dives/misc.md) | | \`$00:FFE8\` | ABORT vector | Unused by the SNES | | \`$00:FFEA\` | NMI vector | Triggered by the SNES V-Blank Interrupt | | \`$00:FFEC\` | Unused | | | \`$00:FFEE\` | IRQ vector | Triggered by the SNES H/V-Timer or external interrupt | ## Emulation mode vectors These vectors come into play when the SNES runs in emulation mode (E=1). | ROM address | Vector | Additional notes | | ----------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | \`$00:FFF0\` | Unused | | | \`$00:FFF2\` | Unused | | | \`$00:FFF4\` | COP vector | | | \`$00:FFF6\` | Unused | | | \`$00:FFF8\` | ABORT vector | Unused by the SNES | | \`$00:FFFA\` | NMI vector | | | \`$00:FFFC\` | RESET vector | Triggered by SNES soft/hard reset | | \`$00:FFFE\` | IRQ/BRK vector | It's possible to differentiate between the two using the \[BRK flag\](/assembly-for-the-snes/processor-flags-and-registers/flags.md) | ## Example setup Here's an example setup with the most commonly used vectors, which can be used in homebrew SNES ROMs. Note that the labels must be located in bank \`$00\` (which is located within the same bank as this setup). \`\`\` ORG $00FFE0 ; Native mode dw $FFFF ; Unused dw $FFFF ; Unused dw $FFFF ; COP dw $FFFF ; BRK dw $FFFF ; ABORT dw NativeNMI ; NMI dw $FFFF ; Unused dw NativeIRQ ; IRQ ; Emulation mode dw $FFFF ; Unused dw $FFFF ; Unused dw $FFFF ; COP dw $FFFF ; Unused dw $FFFF ; ABORT dw $FFFF ; NMI dw Reset ; RESET dw $FFFF ; IRQ/BRK \`\`\` Because homebrew programmers generally want to run their program in native mode, all vectors in emulation mode, save for reset, have been ignored. Furthermore, nothing is done with the \`BRK\` and \`COP\` vectors as these are opcodes which generally are unused. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/vector.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/transfer.md). # Transfers Imagine the following situation: You're using the index register X and you'd like to increase it by #$40. There's \`INX\` which increases the value in X by one, but what about increasing it by $40? There's no opcode which increases the value in X by $40, \[but there is one\](/assembly-for-the-snes/mathemathics-and-logic/arithmetic.md) which increases the value in A by $40. How do you get the value in X into A? There are a series of opcodes for exactly that purpose, and they're called "transfers". There are a bunch of transfer opcodes, all starting with the letter T. ## Transferring A, X and Y There are opcodes which transfers the values between A, X and Y registers. All the combinations are possible: | Opcode | Full name | Explanation | | ------- | --------------- | ------------------------------- | | \*\*TAX\*\* | Transfer A to X | Copies over the value of A to X | | \*\*TAY\*\* | Transfer A to Y | Copies over the value of A to Y | | \*\*TXA\*\* | Transfer X to A | Copies over the value of X to A | | \*\*TXY\*\* | Transfer X to Y | Copies over the value of X to Y | | \*\*TYA\*\* | Transfer Y to A | Copies over the value of Y to A | | \*\*TYX\*\* | Transfer Y to X | Copies over the value of Y to X | The opcodes are self-explanatory. Here's an example of a transfer: \`\`\` LDA #$45 ; A = $45 LDY #$99 ; Y = $99 TAY ; A = $45, Y = $45 \`\`\` As you can see, the values are \*copied\* over to the target register. ## 8-bit and 16-bit mode Transfers work as you'd expect when the source and target registers are both 16-bit mode. You transfer a 16-bit value, like so: \`\`\` LDA #$4512 ; A = $4512 LDY #$9900 ; Y = $9900 TAY ; A = $4512, Y = $4512 \`\`\` If you want to transfer an 8-bit register's value to a 16-bit register, the SNES looks at the target register's size, and transfers the value's low byte accordingly. Here's an example involving an 8-bit transfer to the 16-bit A register: \`\`\` LDA #$4512 ; A = $4512 LDY #$99 ; Y = $0099 TYA ; A = $4599, Y = $0099 \`\`\` Here's an example involving a 16-bit transfer to the 8-bit A register: \`\`\` LDA #$45 ; A = $xx45 LDY #$99AA ; Y = $99AA TYA ; A = $xxAA, Y = $99AA \`\`\` In the second example, the \`xx\` could be anything. Remember that \[in the introduction of the A, X and Y registers\](https://github.com/Ersanio/snes-assembly-book/tree/c7ff1077207f8a20e73f4ef3148c976dd96dcc25/docs/the-basics/register.md), it's mentioned that the A register can actually be treated to be always 16-bit. The high byte isn't touched during the transfer, even if A is in 8-bit mode. If A was \`$1245\` before the transfer, it'll be \`$12AA\` after the transfer. This doesn't apply to X and Y, as their high bytes are immediately cleared when they exit 16-bit mode. ## Transferring general registers There are other opcodes which involve the general SNES registers. | Opcode | Full name | Explanation | | ------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \*\*TCD\*\* | Transfer A to direct page register | Transfers the 16-bit value in A, to the direct page register, regardless of A being in 16-bit mode or not | | \*\*TDC\*\* | Transfer direct page register to A | Transfer the 16-bit value in the direct page register to the A register, regardless of A being in 16-bit mode or not | | \*\*TCS\*\* | Transfer A to stack pointer register | Transfers the 16-bit value in A to the stack pointer register, regardless of A being in 16-bit mode or not | | \*\*TSC\*\* | Transfer stack pointer register to A | Transfers the 16-bit value in the stack pointer register to A, regardless of A being in 16-bit mode or not | | \*\*TXS\*\* | Transfer X to stack pointer register | Transfers the 16-bit value in X to the stack pointer register, regardless of X being in 16-bit mode or not. Note that when X is in 8-bit mode, the high byte is always $00 | | \*\*TSX\*\* | Transfer stack pointer register to X | Transfers the 16-bit value in the stack pointer register to X. Note that when X is in 8-bit mode, the high byte stays $00 after the transfer | --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/transfer.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing.md). # Addressing modes There are different addressing modes in 65c816. Addressing modes are used to make opcodes access addresses and values differently, such as "indexed" or "direct indirect" (explained later in this tutorial). Using them wisely, you can access values and memory addresses in many ways. For example, you can immediately load a value into a register, such as A, or load a byte from the ROM into A. Keep in mind that not all the opcodes supports all the types of addressing modes. Here are some of the important addressing modes you'll find yourself use very often. ## Immediate 8/16 bit This addressing mode defines an absolute value, which is written as \`#$XX\` in 8-bit mode, or \`#$XXXX\` in 16-bit mode. The # means "immediate value", while the $ stands for hexadecimal. Using # alone makes the input decimal. For example, #10 is the same as #$0A. Think of an immediate value as a number you're directly defining. ## Direct page This addressing mode defines a direct page address, which is written as \`$XX\`. The direct page is the last 2 hex digits of a long address. For example, address $7E0011 as direct page would be $11. When loading from a direct page address, the bank byte is ALWAYS treated as $00, no exceptions. If you write "LDA $11" for example, you would load the contents of $000011 into the accumulator, which is also mirrored at $7E0011 (remember the illustration of the SNES memory). Therefor, you load $7E0011's contents into A. ## Absolute This addressing mode defines an absolute address, which is written as \`$XXXX\`. An absolute address is the last 4 hex digits of a long address. Using the example from earlier, address $7E0011 as an absolute address would be $0011. The bank byte of the absolute address is determined by the data bank register. ## Long This addressing mode defines a long address, which is written as \`$XXXXXX\`. Long addresses deliver fewer complications when dealing with banks and mirroring. You also don't have to worry about what the data bank register currently contains. With long addresses, you can access any address in the SNES memory. ## Other addressing modes The SNES supports more addressing modes. The above addressing modes are the basics. There are also addressing modes, such as: indexed versions of direct page, absolute and long addresses, and much more. They will be explained near the end of this tutorial because you don't need them at this point. It would make things only more confusing at the moment. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/addressing.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cautions.md). # Programming cautions When you're coding, you will have to keep an eye out on (common) mistakes. ## Confusing 8-bit values with 16-bit values Don't try to load an 8-bit value into AXY when AXY is in 16-bit mode, and the other way around. For example, don't write \`LDA #$0000\` when A is in 8-bit mode, as the third byte of this opcode will be interpreted as an opcode rather than a value. Consequence(s): The game will most likely crash by interpreting instructions you never wrote. Fixing the issue: Use the correct value size. ## Looping conditions When creating loops, don't make a small mistake which results in an infinite loop (a loop which doesn't exit). Consequence(s): The game will lock up; the only way to exit is to reset the SNES. Fixing the issue: Check at the end of the loop (often a comparison) to see why it doesn't allow the loop to exit. You might need a debugger for this. ## Bank boundaries and edge cases Make sure your code doesn't cross bank-boundaries ($XX:FFFF → $XX:0000) inside the ROM. Consequence(s): The SNES would read bogus instructions and most likely crash. Fixing the issue: The code should remain within a bank. If your code doesn't fit inside a bank, you should split up your code across banks and make use of the \`JSL\` and \`JML\` instructions. ## Pushes and pulls Make sure you pull the same amount of bytes as you have pushed, before a return instruction (RTS, RTL). Consequence(s): If you don't, the SNES won't get the right return address from the stack and most likely crash. Fixing the issue: Keep the amounts of pushes and pulls at an equilibrium. Especially keep an eye out on different A, X and Y modes (8-bit and 16-bit), as pushing in 16-bit mode means pulling twice in 8-bit mode. The other way around is also true. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/cautions.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags.md). # The processor flags As you saw in the "8-bit and 16-bit mode" chapter earlier, the SNES can switch between 8-bit and 16-bit mode by using the opcodes REP and SEP. These affect the processor flags, which affect the behaviour of the SNES. Two of the processor flags are dedicated to A and X & Y being 8-bit or 16-bit mode. There are in total 9 processor flags stored in the processor flags register as a single byte: \`\`\` Processor flags Bits: 7 6 5 4 3 2 1 0 |e├─── Emulation: 0 = Native Mode |n| |v| |m| |x| |d| |i| |z| |c| └┼───┼───┼───┼───┼───┼───┼───┼┘ │ │ │ │ │ │ │ └──────── Carry: 1 = Carry set │ │ │ │ │ │ └───────────── Zero: 1 = Result is zero │ │ │ │ │ └────────── IRQ Disable: 1 = Disabled │ │ │ │ └───────────── Decimal Mode: 1 = Decimal, 0 = Hexadecimal │ │ │ └──────── Index Register Select: 1 = 8-bit, 0 = 16-bit │ │ └─────────────── Accumulator Select: 1 = 8-bit, 0 = 16-bit │ └───────────────────────────── Overflow: 1 = Overflow set └───────────────────────────────── Negative: 1 = Negative set \`\`\` The processor flags are usually displayed as a single mnemonic in debuggers: "nvmxdizc". This chapter will explain all the processor flags in detail. ## Negative flag (n) Most opcodes modify the negative flag depending on the results of that opcode. These opcodes generally are opcodes which are handled in the mathematics chapter, but also loads, comparisons and pulls. The negative flag doesn't affect the behaviour of the SNES. Rather, there are some branches which make use of the negative flag. ## Overflow flag (v) There are 3 opcodes which affect the overflow flag after a calculation: \[\`ADC\`, \`SBC\`\](/assembly-for-the-snes/mathemathics-and-logic/arithmetic.md) and \[\`BIT\`\](/assembly-for-the-snes/mathemathics-and-logic/logic.md). For detailed explanations of when the overflow flag is set, see the links. The overflow flag doesn't affect the behaviour of the SNES. Rather, there are some branches which make use of the overflow flag. ## Memory select (m) The memory select processor flag determines whether the accumulator is 8-bit or 16-bit. When it's set to 1, the accumulator is 8-bit. When it's set to 0, the accumulator is 16-bit. ## Index select (x) The index select processor flag determines whether the X and Y registers are 8-bit or 16-bit. When it's set to 1, both X and Y are 8-bit. When it's set to 0, both X and Y are 16-bit. ## Decimal mode flag (d) Setting this flag to 1 means the SNES enters decimal mode. This \*only\* affects the \`ADC\`, \`SBC\` and \`CMP\` opcodes. These opcodes adjust the accumulator on-the-fly. This means that, if you for example add \`$03\` to \`$09\`, the result is \`$12\` instead of \`$0C\`. Although decimal-mode math properly affects the carry flag and negative flag, it doesn't do this with the overflow flag. ### Binary-coded decimal Because numbers are treated as decimal, they are called "binary-coded decimal" (BCD). BCD is basically the same as hexadecimal, with the values $0A-0F, $1A-1F, etc. ignored. Here's a table showing how counting in BCD goes like: | Binary | Hexadecimal | Decimal | BCD | | ---------- | ----------- | ------------- | ------------- | | %0000 0000 | $00 | #00 | %0000 0000 | | %0000 0001 | $01 | #01 | %0000 0001 | | %0000 0010 | $02 | #02 | %0000 0010 | | %0000 0011 | $03 | #03 | %0000 0011 | | %0000 0100 | $04 | #04 | %0000 0100 | | %0000 0101 | $05 | #05 | %0000 0101 | | %0000 0110 | $06 | #06 | %0000 0110 | | %0000 0111 | $07 | #07 | %0000 0111 | | %0000 1000 | $08 | #08 | %0000 1000 | | %0000 1001 | $09 | #09 | %0000 1001 | | %0000 1010 | $0A | Not available | Not available | | %0000 1011 | $0B | Not available | Not available | | %0000 1100 | $0C | Not available | Not available | | %0000 1101 | $0D | Not available | Not available | | %0000 1110 | $0E | Not available | Not available | | %0000 1111 | $0F | Not available | Not available | | %0001 0000 | $10 | #10 | %0001 0000 | | %0001 0001 | $11 | #11 | %0001 0001 | | %0001 0010 | $12 | #12 | %0001 0010 | | ... | ... | ... | ... | | %1001 1000 | $98 | #98 | %1001 1000 | | %1001 1001 | $99 | #99 | %1001 1001 | | %1001 1010 | $9A | Not available | Not available | | %1001 1011 | $9B | Not available | Not available | | ... | ... | ... | ... | In decimal mode, the SNES supports calculation results from \`$00\` to \`$99\`. In 16-bit A mode, this would be from \`$0000\` to \`$9999\`. ## Interrupt disable flag (i) This flag determines whether the IRQ of the SNES is disabled or not. When it's set to 1, IRQ is disabled. When it's set to 0, IRQ is enabled. ## Zero flag (z) Most opcodes modify the zero flag depending on the results of that opcode. These opcodes generally are opcodes which are handled in the mathematics chapter, but also loads, comparisons and pulls. The zero flag doesn't affect the behaviour of the SNES. Rather, there are some branches which make use of the zero flag. ## Carry flag (c) The SNES supports math in the form of adding and subtracting numbers. It also supports bitwise operations such as bitshifting. The "carry flag" is a processor flag used for most of these arithmetic and bitshifting operations. Additionally, the carry flag is also used for branching. The carry flag is the same concept as the "carry" you learn in elementary school. In a typical pencil-and-paper addition, you'd write it out like this: \`\`\` ¹ 27 + 59 ---- 86 \`\`\` 7+9 equals 16, thus \*carry\* the 1 to the left. Considering the carry is a "flag", when the carry flag is clear, the carry (C) will be 0. When it's set, the carry will be 1. You can safely assume that the carry flag is the "9th bit" of the A register when A is in 8-bit mode, and the "17th bit" when A is in 16-bit mode. Assuming A is in 8-bit mode, the carry flag will look like this: \`\`\` AAAAAAAA C \`\`\` Where C is the Carry Flag and A are the bits of the A register. Depending on the carry flag, various mathematical and bit shifting instructions will behave differently. ## Emulation mode flag (e) Setting this flag causes the 65c816 to behave as the 6502. When you enter emulation mode: \* The stack pointer register's high byte remains static as $01 \* The A, X and Y registers are always 8-bit \* The program bank and data bank registers are set to $00 \* The direct page register is initialized to $0000, and the high byte remains static as $00 The emulation mode of the 65c816 also fixes some of the bugs that the 6502 had. For example, indirect addressing mode \`JMP\` now wraps addresses properly. For example: \`JMP ($10FF)\` will now get the high byte from \`$1100\`, rather than \`$1000\`. ### Processor flags The emulation mode has a different set of processor flags. \`\`\` Processor flags Bits: 7 6 5 4 3 2 1 0 |e├─── Emulation: 0 = Native Mode |n| |v| |1| |b| |d| |i| |z| |c| └┼───┼───┼───┼───┼───┼───┼───┼┘ │ │ │ │ │ │ │ └──────── Carry: 1 = Carry set │ │ │ │ │ │ └───────────── Zero: 1 = Result is zero │ │ │ │ │ └────────── IRQ Disable: 1 = Disabled │ │ │ │ └───────────── Decimal Mode: 1 = Decimal, 0 = Hexadecimal │ │ │ └─────────────────── Break Flag: 1 = Break executed │ │ └─────────────────────────── Unused: 1 │ └───────────────────────────── Overflow: 1 = Overflow set └───────────────────────────────── Negative: 1 = Negative set \`\`\` As you can see, it's very similar to the processor flags of the SNES, with a few exceptions. The A and X & Y memory select bits are replaced. ### Unused flag This processor flag is unused and is always set to 1. ### Break flag This processor flag is set to 1 when the emulation mode comes across a \`BRK\` opcode, thus it only indicates that there was a break; the processor flag doesn't actually affect the SNES. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/processor-flags-and-registers/flags.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/816.md). # 8-bit and 16-bit mode The SNES is able to enter 8-bit or 16-bit modes. This means that the A, X and Y registers contain 16-bit values instead of 8-bit values. There are a few ways to switch between 8-bit and 16-bit modes. Here are all the possible manners: | Operation | Explanation | | --------- | ------------------------------ | | REP #$10 | Sets X and Y to 16-bit mode | | REP #$20 | Sets A to 16-bit mode | | REP #$30 | Sets A, X and Y to 16-bit mode | | SEP #$10 | Sets X and Y to 8-bit mode | | SEP #$20 | Sets A to 8-bit mode | | SEP #$30 | Sets A, X and Y to 8-bit mode | The opcodes REP and SEP are explained later in this tutorial. For now, this is all you need to know. In 16-bit A mode, the following features take effect: \* When accessing the memory, you will involve 2 RAM addresses as opposed to 1. \* Those two RAM addresses are always adjacent to each other. \* Immediate values (#$) are now 16-bit. \* Loaded and stored values are little-endian in the memory, but you don't have to worry about that at all. Let's begin with an example immediately: \`\`\` REP #$20 LDA #$0001 STA $7E0000 SEP #$20 \`\`\` And breaking it down: \`\`\` REP #$20 \`\`\` This sets the A register to 16-bit mode. \`\`\` LDA #$0001 \`\`\` This will load the 16-bit value $0001 into A, so A now has the value $0001. {% hint style="info" %} If you write $01 instead of $0001, the code would most-likely crash! This is because the code expects a 16-bit parameter, but you only give it an 8-bit one. The code therefore takes the next opcode as part of the 16-bit parameter, causing the following opcodes to become bogus. Each opcode (disregarding the parameters) becomes an 8-bit value when assembled. This is why you only see hexadecimal values when you open a ROM in a hex editor. To disassemble these values into human-readable ASM, you'll need to use a "disassembler". {% endhint %} \`\`\` STA $7E0000 \`\`\` This will store the 16-bit A value into the RAM address $7E0000 AND $7E0001. Why two addresses? Because a 16-bit value won't fit into one address. Remember that an address represents an 8-bit value, so two addresses can represent a 16-bit one. $7E0000 and $7E0001 combined will now have the value $0001. \`\`\` SEP #$20 \`\`\` Exits A from 16-bit mode. After executing these 4 instructions, if we take a peek into the RAM, we see will see something like this: \`\`\` $7E0000 | \[01\] \[00\] \[XX\] \[XX\] \[XX\] \[…\] \`\`\` The first value, $01, is the value of RAM address $7E0000. The second value belongs to $7E0001. The third value belongs to $7E0002, etc. As you can see, the stored value became little-endian, but normally we don't have to worry about this. If we try to load it back into A, we would have to use \`LDA $7E0000\`. It would load the value $0001 into A again if A is in 16-bit mode. If you tried to load it back when A was in 8-bit mode it would load the value $01 into A instead. There's a 16-bit X and Y mode too. This is not related to 16-bit A mode at all. If A is 8-bit mode and XY are 16-bit mode, the following is definitely possible: \`\`\` LDA #$13 LDX #$4242 \`\`\` --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/816.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/indexing.md). # Tables and indexing Indexing is the act of accessing a collection of data from a certain offset, that offset being determined by an indexer. The registers X and Y are unmissable when it comes to indexing. They're called the "index" registers, after all. ## Tables "Tables" are simply a sequence of values. They are used in at least two cases: \* Conditional cases (e.g. a collection of speed values, depending on the entity's direction) \* Values that represent an "asset" (e.g. graphics data, music data, level data) Tables have very practical applications in SNES games. You can, for example, store text data in tables. Or level data. Or palette data. Tables can contain anything and can have any size, as long as they fit within the ROM. There are four types of values which you can use to write tables: | Instruction | Full name | Explanation | | ----------- | ------------- | -------------------------------------------------------- | | \*\*db\*\* | direct byte | A value denoting a byte (8-bit value, e.g. $XX) | | \*\*dw\*\* | direct word | A value denoting a word (16-bit value, e.g. $XXXX) | | \*\*dl\*\* | direct long | A value denoting a long (24-bit value, e.g. $XXXXXX) | | \*\*dd\*\* | direct double | A value denoting a double (32-bit value, e.g. $XXXXXXXX) | Here's an example of a table definition: \`\`\` ValuesTableExample: db $11,$86,$91,$38,$22 \`\`\` As you can see, we first define the value type as "byte" by writing \`db\`. Then, we follow with byte-values, seperated by a comma (,). In order to access a table, we always use a label. In this case, we used the label \`ValuesTableExample\`. ## Indexing Tables can be accessed using a label. Labels are translated into ROM addresses, after all. Expanding on above table example, the following code would read out a value from the table: \`\`\` LDA ValuesTableExample STA $00 RTS ValuesTableExample: db $11,$86,$91,$38,$22 \`\`\` However, what this code does is always reading the first value in that table, the value $11, and storing it into RAM $7E0000. This is because we haven't used any indexer. \`\`\` LDY #$03 ; Y is now loaded with the number $03 LDA ValuesTableExample,y ;Load a number from the table into A, using Y as index STA $00 RTS ValuesTableExample: db $11,$86,$91,$38,$22 \`\`\` The code will load a value from the table into A. Basically, this does \`LDA ValuesTableExample\`, and the value in Y, which is $03, is added to the address. In other words, in higher-level programming languages it'd be something like \`ValuesTableExample\[3\]\`. You start counting index from \*\*zero\*\*. Index 0 of that table has the value $11, index 1 has the value $86, and so on. Index 3 has the value $38 in this case, so this code example actually loads $38 in A, and stores it in RAM $7E0000. Indexing is quite useful when you don't want to write very repetitive instructions all the time. Indexing can be performed with the X register too. X and Y behave exactly the same, after all. Indexing is actually one of the many addressing modes. The basic addressing modes are covered \[here\](/assembly-for-the-snes/the-fundamentals/addressing.md) and the more advanced addressing modes \[here\](/assembly-for-the-snes/deep-dives/addressing.md). ## Indexing with RAM So far, the above examples were about ROM. You can also index values in RAM. You can treat the RAM as one giant table you could index. You simply replace the label with an address. For example: \`\`\` LDX #$03 LDA $7E1000,x STA $00 \`\`\` In this case, the LDA would resolve into address \`$7E1003\`. The code loads $7E1003's value into A, and stores it into $7E0000. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/indexing.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/subroutine.md). # Jumping to subroutines What if you want to use the same code twice, but you don't want to write, for example, the exact same 200 lines of codes again? You can turn code into "subroutines" and use jumping opcodes to run these subroutines. There are four jumping opcodes in total. There's a difference between "subroutine" and "code" in this tutorial, as you'll see in the coming paragraphs. ## JSR and JSL There are two opcodes which pretty much act like a function call in higher-level programming languages. | Opcode | Full name | Explanation | | ------- | ----------------------- | ----------------------------------------------- | | \*\*JSR\*\* | Jump to subroutine | Jumps to a subroutine using an absolute address | | \*\*JSL\*\* | Jump to subroutine long | Jumps to a subroutine using a long address | These opcodes call a piece of code, then continue executing code below said opcode. Here is an example of the usage of JSR: \`\`\` LDA #$01 STA $01 JSR Label1 ; Execute the "subroutine" located at Label1 (current bank) LDA #$03 ; The RTS in Label1 will return to this line STA $00 RTS Label1: LDA #$02 STA $02 RTS \`\`\` This code will store $01 into $7E0001, will store $03 into $7E0000 AND execute the codes at Label1 at the current bank, so it also stores $02 into $7E0002. JSL has the same purpose as JSR, except it can jump everywhere. Above example can also be applied to JSL, except that the \`RTS\` is now an \`RTL\`. The opcode JSR will get assembled as \`JSR $XXXX\` by the assembler (because labels get turned into addresses), but you shouldn't worry about that. Furthermore, because JSR uses an absolute address as its parameter, it is limited to its current bank. Changing the data bank register doesn't affect JSR. The opcode JSL will get assembled as \`JSL $XXXXXX\`, instead. ## RTS and RTL When you call a subroutine, you also need a way to return back to your original code. For that, there are two return opcodes. | Opcode | Full name | Explanation | | ------- | --------------------------- | ------------------ | | \*\*RTS\*\* | Return from subroutine | Returns from a JSR | | \*\*RTL\*\* | Return from subroutine long | Returns from a JSL | Code called by JSR and JSL should end with RTS and RTL, respectively. ## JMP and JML If you'd like to control the code flow rather than doing a function call, you will use regular jumps instead. | Opcode | Full name | Explanation | | ------- | --------- | --------------------------------------- | | \*\*JMP\*\* | Jump | Jumps to code using an absolute address | | \*\*JML\*\* | Jump long | Jumps to code using a long address | What JMP and JML do is jumping to another location and executing the code there, ignoring everything after the JMP/JML opcode. The RTS in Label1 does NOT jump back to LDA #$03. Instead, it just finishes the current subroutine it was in. \`\`\` JML Label1 LDA #$03 ; Ignored STA $00 ; Ignored RTS ; Ignored Label1: STZ $00 RTS \`\`\` JMP is limited to the current bank, like JSR. JML can jump anywhere like JSL. JMPs and JMLs don't have a return instruction, but you can still use an RTS or RTL to return from a JMP/JMP depending on the current situation. Example: \`\`\` LDA #$01 STA $00 JSR Label1 ; Jump to subroutine "Label1" RTS ; Label2 returns here. The code ends here. Label1: JMP Label2 ; Jump to Label2 Label2: RTS ; This DOESN'T return to Label1. Instead, it returns to the RTS above. \`\`\` It is good practice to put a blank line after a JMP or JML, so the reader knows that the program flow changes from that point on. ## Additional notes The Data Bank does NOT get updated when you use a JSL or JML. You will have to do that yourself. Remember how JSL/JML can jump everywhere? They can even jump to RAM, which implies code can be executed in RAM. You can write values to RAM which can be interpreted as code which can be executed. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/subroutine.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax.md). # Common assembler syntax In this chapter, you'll learn the art of proper assembler syntax, so that in theory, you'll can write "relative"-only code. This means that when your code shifts around (by inserting or removing new lines of code), opcodes that make use of addresses and relative addresses, such as branches or jumps, will keep their correct values. Note that everything discussed here can also be found in Asar's documentation. However, they're important enough topics to be mentioned in this tutorial. ## Defines Defines are basically variable definitions you can use, so your code suffers less from so-called "magic numbers". Here's an example which defines an immediate value: \`\`\` !Value = $03 LDA #!Value STA $01 \`\`\` Here's an example which defines an address: \`\`\` !Address = $01 LDA #$03 STA !Address \`\`\` Be aware that Asar actually does a simple text search and replace, rather than evaluating the expression in a define. In other words, Asar isn't smart enough to figure out that a define is an "address", "immediate value" or anything else. Here's an example of improper define usage: \`\`\` !Value = #$03 ; Note the # LDA #!Value ; A search and replace turns this into "LDA ##$03" STA $01 ; Therefore, it will throw an error! \`\`\` ## Labels As discussed in the \[branches\](/assembly-for-the-snes/the-basics/branches.md) and \[subroutines\](/assembly-for-the-snes/the-basics/subroutine.md) chapters, the SNES processor can make use of labels to determine locations it can jump to. The labels used by the opcodes are replaced by actual values which denote the locations to jump to, either relative or absolute addresses. ### Sublabels Sublabels are special type of labels which have a parent label, and are prefixed with a dot ("\`.\`"), and aren't suffixed with a colon ("\`:\`"). Sublabels are useful if you tend to use labels which aren't unique (e.g. "return"). Here's an example of a recurring "return" sublabel: \`\`\` JSR Main JSR Sub RTS Main: LDA $10 BEQ .return STA $11 .return RTS Sub: LDA $20 BEQ .return STA $21 .return RTS \`\`\` Sublabels don't have any rules in terms of writing style. You could capitalize or keep it all lowercase. In this example, it's all lowercase. ### Relative labels Relative labels are an alternate solution to sublabels and are often used when the code is already self-documenting enough, for example, when the code needs to skip a single store depending on a branch. It saves you from thinking up a label name, such as "skipstorewhenplayerisbig". Relative labels are written using \`+\` and \`-\`. The plus is ahead of the branch instruction, while the minus is behind the branch instruction. They can be repeated as often as needed to denote different levels of depth. Here's an example of relative labels: \`\`\` LDA $10 BEQ + STA $11 + RTS \`\`\` This code skips a single store to \`$11\` when address \`$10\` has the value \`$00\`. Here's another example, demonstrating a backwards branch, causing an infinite loop: \`\`\` - BRA - \`\`\` Here's another example, demonstrating different levels of relative label depth: \`\`\` LDA $10 BEQ ++ LDA $11 BEQ + STZ $12 + STZ $13 ++ STZ $14 RTS \`\`\` \* If address \`$7E0010\` has the value \`$00\`, address \`$7E0014\` is cleared. \* If address \`$7E0011\` has the value \`$00\`, addresses \`$7E0013\` and \`$7E0014\` are cleared \* Else, addresses \`$7E0012\`, \`$7E0013\` and \`$7E0014\` all are cleared ## The art of relativity It's possible to write programs completely devoid of fixed values and addresses (also known as "magic numbers"), by making smart use of labels and defines outside of branches and jumps. When you use labels with loading instructions, for example, it'll grab the address of the label and use it as a parameter. This was seen in the \[indexing\](/assembly-for-the-snes/collection-of-values/indexing.md) chapter. However, you can also use labels as values, rather than addresses. This is especially useful when setting up indirect pointers, which is why it's important to be able to grab certain \*parts\* of an address rather than the full address. This is also demonstrated in the \[moves\](/assembly-for-the-snes/collection-of-values/moves.md) chapter, in the "Easy notation" section. In the following example, an \`LDA\` loading the address of a label as a value would look like this: \`\`\` LDA #somelabel STA $00 \`\`\` This is problematic, because the label assembles into a 24-bit value which is the address, and there's no LDA which accepts a 24-bit value. Instead, LDA tries to grab the largest possible supported value, thus grabs the high and low byte of the value instead (because it's 16-bit). But what if you're writing 8-bit code at that moment? The code won't run as expected, and will crash. ### Opcode length specifiers In order to read a value at a well-defined, fixed width, you can make use of "opcode length specifiers". These are special notations appended to opcodes: | Syntax | Definition | Description | | ------ | ------------- | --------------------------------- | | .b | byte (8-bit) | Forces the parameter to be 8-bit | | .w | word (16-bit) | Forces the parameter to be 16-bit | | .l | long (24-bit) | Forces the parameter to be 24-bit | In the previous example, you can force the assembler to use the low bytes of the label only, by using \`.b\`: \`\`\` LDA.b #somelabel STA $00 \`\`\` The same applies to defines. You can use defines as values, and by using an opcode length specification, you can only grab certain parts of the defines rather than the full value. For example: \`\`\` !Size = $7FFF LDA #!Size STA $00 \`\`\` This would assemble as: \`\`\` LDA #$7FFF STA $00 \`\`\` This would be problematic in 8-bit mode, as this assembles in 16-bit mode. To fix this problem, you can use \`.b\`: \`\`\` !Size = $7FFF LDA.b #!Size STA $00 \`\`\` This would assemble as: \`\`\` LDA #$FF STA $00 \`\`\` ### Bitshifts Expanding upon the previous example: \`\`\` !Size = $7FFF LDA.b #!Size STA $00 \`\`\` If you wanted to store the high byte of this define in address \`$7E0001\`, instead of the low byte, you'd need a way to grab \*only\* the high byte of the definition. In order to do that, you'll have to use bitshifts: | Syntax | Definition | Description | | ------ | ----------- | ------------------------- | | >> | Shift right | Shifts bits right n times | | << | Shift left | Shifts bits left n times | Remember that a byte consists of 8 bits, thus you need to "skip" 8 bits to grab the next 8 bits we need. By bitshifting 8 times to the right, you discard the low byte of the value: \`\`\` !Size = $7FFF LDA.b #!Size>>8 ; Only $7F remains STA $01 \`\`\` Bitshifts are incredibly valuable when grabbing certain portions of addresses or values. They can also be used on labels, and thus, you can also grab bank bytes: \`\`\` LDA.b #somelabel>>16 ; Grab the bank byte of a label STA $01 \`\`\` The same goes for defines: \`\`\` !Address = $7E8000 LDA.b #!Address>>16 ; Grab $7E of the define STA $02 \`\`\` ### Constructing addresses By making use of bitshifts and opcode length specifiers, it's possible to supply addresses to certain subroutines, or as indirect addresses. Here's an example which constructs an indirect address: \`\`\` LDA.b #Sometable STA $00 LDA.b #Sometable>>8 STA $01 LDA.b #Sometable>>16 STA $02 LDA \[$00\] ; This loads the value $01 into A RTS Sometable: db $01,$02,$04,$08 \`\`\` ### Table sizes There are situations where it's handy to know the size of tables, such as for \[moves\](/assembly-for-the-snes/collection-of-values/moves.md). To get the size of a table, you put a label at both begin and end of a table, such as this: \`\`\` Sometable: db $01,$02,$04,$08 .end \`\`\` Then, by using the subtract operator, "\`-\`", you subtract the starting and the ending address of the table, effectively getting the size of the table. For example: \`\`\` LDA.b #Sometable\_end-Sometable ; #$04 STA $00 RTS Sometable: db $01,$02,$04,$08 .end \`\`\` Note that it's important to use opcode length specifiers, as we're still dealing with labels, thus, 24-bit values. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/syntax.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic.md). # Arithmetic operations At some point, you would probably want to \*increase\* RAM address $7E000F by $01, but a simple LDA and STA won't work, because this simply changes the RAM Address' contents to $01 – not increase it by one. Maybe you'd like to increase it by 2 instead, or even multiply by 2. The SNES has a few instructions capable of doing basic math operations. ## INC and DEC These are opcodes to increase or decrease a value by 1. | Opcode | Full name | Explanation | | ------- | --------- | ------------------------------------ | | \*\*INC\*\* | Increase | Increases accumulator or memory by 1 | | \*\*DEC\*\* | Decrease | Decreases accumulator or memory by 1 | INC and DEC support both the accumulator as well as addresses. Here's an example of increasing the accumulator by 1: \`\`\` LDA #$02 ; Load the value $02 into the accumulator INC A ; Increase the accumulator by 1. It is now $03 \`\`\` Here's an example to decrease the accumulator by 1: \`\`\` LDA #$02 ; Load the value $02 into the accumulator DEC A ; Decrease the accumulator by 1. It is now $01 \`\`\` Here's an example to increase the value of an address by 1, without affecting the accumulator at all: \`\`\` INC $0F ; Increase the value in $7E000F by one RTS ; Return. A remains unchanged \`\`\` Here's also an example to decrease a value by one \`\`\` DEC $0F ; Decrease the value in $7E000F by one RTS ; Return. A remains unchanged \`\`\` There are also edge cases with these operations. When you use INC when the value that is being modified is currently $FF, it would result in a $00 and the zero flag being set. When you use DEC and the value that is being modified is currently $00, it would result in a $FF. {% hint style="info" %} INC and DEC don't work with long addressing modes. They only work with absolute or direct page addressing modes. Therefore, instructions like \`INC $7E000F\` do not exist. Instead, you should use \`INC $000F\` or \`INC $0F\`. Why isn't there a long addressing mode? The processor is simply made that way, so you'll have to deal with it, one way or another. {% endhint %} ## INX, DEX, INY and DEY There are also instructions to increase or decrease the value inside the index registers: | Opcode | Full name | Explanation | | ------- | ---------- | ---------------- | | \*\*INX\*\* | Increase X | Increases X by 1 | | \*\*DEX\*\* | Decrease X | Decreases X by 1 | | \*\*INY\*\* | Increase Y | Increases Y by 1 | | \*\*DEY\*\* | Decrease Y | Decreases Y by 1 | You cannot use the above 4 opcodes to manipulate an address. They are solely used for the X and the Y registers. ## ADC and SBC If you wanted to increase or decrease a value by, say, 95, you wouldn't want to write INC or DEC 95 times. Thankfully, the SNES has instructions for such situations as well. | Opcode | Full name | Explanation | | ------- | ------------------- | ------------------------------ | | \*\*ADC\*\* | Add with carry | Adds a given value to A | | \*\*SBC\*\* | Subtract with carry | Subtracts a given value from A | {% hint style="warning" %} Just for emphasis: ADC adds a value to the accumulator, not a RAM address. SBC subtracts a value from the accumulator. {% endhint %} With ADC and SBC, you can do add and subtract operations on addresses. Here's an example which adds 4 to the value of an address: \`\`\` LDA $0F ; Load the address value in A CLC ; Clear carry flag ADC #$04 ; What you will add to A. It is $04 in this case. STA $0F ; Store A in $0F \`\`\` Because ADC modifies A rather than addresses, you must first load the address value into A, then do the addition, then store the result back into the address. If the carry flag wasn't cleared, the addition would've been $05 instead of $04, depending on the state of the carry flag at that moment. Therefore, it's important to be extra sure about the carry flag and just clear it with a \`CLC\`, before the ADC. The opposite is also true for subtracting: \`\`\` LDA $0F ; Load the current value in A SEC ; Set Carry Flag SBC #$04 ; How many you will subtract from A. It is $04 in this case. STA $0F ; Store A in $0F \`\`\` This will subtract 4 from the RAM address' content (\`$0F-#$04\`). You'll notice that for subtracting, we set the carry flag rather than clear it. If you didn't set the carry flag, it would subtract $05 instead of $04. This might seem backwards, but it's just how the processor works. {% hint style="info" %} In short: when adding, use \`CLC\`. When subtracting, use \`SEC\`. {% endhint %} ### Carry flag When you use ADC, and the value in A wraps from $FF to $00, the carry flag will be set. This is also true for 16-bit A, when the value wraps from $FFFF to $0000. When you use SBC, and the value in A wraps from $00 to $FF, the carry flag will be cleared. This is also true for 16-bit A, when the value wraps from $0000 to $FFFF. With this, you can use the carry flag to check if some sort of value wrapping has occured. ### Overflow flag The opcodes ADC and SBC are unique in the sense that they're two of the three opcodes which can affect the \*signed overflow\* processor flag as a result of a calculation. The overflow flag is especially relevant when you decide to treat values as signed values. Remember the hexadecimal chapter with the signed and unsigned values? Values \`$00-$7F\` are considered positive, and values \`$80-$FF\` are considered negative. The overflow flag is set when the result of an operation doesn't make sense in the math world: \* Adding a negative value to a negative value, and getting a positive value as a result, when it should be negative \* Adding a positive value to a positive value, and getting a negative value as a result, when it should be positive \* Subtracting a positive value from a negative value, and getting a positive value as a result, when the negative value should be even more negative \* Subtracting a negative value from a positive value, and getting a negative value as a result, when the value should be even more positive Math rules are at play here. Adding two negative numbers results in a negative number (e.g. \`-10 + -1 = -11\`). Subtracting two negative numbers results in a negative number (e.g. \`-10 - -1 = -9\`). Subtracting a negative value equals an addition (e.g. \`10 - -1 = 11\`). Adding a negative value equals a subtraction (e.g. \`10 + -1 = 9\`). The scenarios described in those bullet points break these rules. Here are some examples of the overflow flag getting set: \`\`\` LDA #$88 ; Number -120 CLC ; We do -120 + -16, which should result in -136 ADC #$F0 ; $88 + $F0 = $78, which is 120, which doesn't make sense mathematically \`\`\` \`\`\` LDA #$80 ; Number -128 SEC ; We do -128 - 16, which should result in -144 SBC #$10 ; $80 - $10 = $70, which is 112, which doesn't make sense mathematically \`\`\` \`\`\` LDA #$30 ; Number 48 CLC ; We do 48 + 112, which should result in 160 ADC #$70 ; $30 + $70 = $A0, which is -96, which doesn't make sense mathematically \`\`\` \`\`\` LDA #$10 ; Number 16 SEC ; We do 16 - -128, which should result in 144 SBC #$80 ; $10 - $80 = $90, which is -112, which doesn't make sense mathematically \`\`\` ## 16-bit mode math All the previous explanations and behaviours apply to 16-bit math as well. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/mathemathics-and-logic/arithmetic.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/glossary.md). # Glossary Here are some definitions of the most commonly used terms throughout the tutorial. For the sake of context and continuity, it's best read from top to bottom. | Terminology | Definition | | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | SNES | Super Nintendo Entertainment System | | Memory | The working space in the SNES in which the ROM, RAM and SRAM are present | | ROM | Read-only memory; the well-known .smc/.sfc/.fig files | | (W)RAM | (Work) Random-access memory | | SRAM | Static random-access memory; the well-known .srm files | | Register | A variable in the SNES not part of the standard SNES memory | | Opcode | A three-letter instruction; a mnemonic | | Addressing mode | An optional parameter for an opcode denoting a value or an address | | Instruction/Operation | The combination of an opcode and optionally an addressing mode | | Machine code | An instruction assembled into bytes, which can be understood by processors | | Value | A magnitude, quantity, or number; a number representing information | | Signed | A value that semantically allows itself to be negative as well | | Unsigned | A value that semantically allows itself to be positive-only, allowing for greater positive numbers | | Address | A location in the memory of the SNES. Ranges from $000000 to $FFFFFF | | Long address | An address represented by a 6-digit hexadecimal notation (e.g. $001200) | | Absolute address | An address represented by a 4-digit hexadecimal notation of the final 4 digits (e.g. $1200) | | Direct page | An address represented by a 2-digit hexadecimal notation of the final 2 digits (e.g. $00) | | Byte | An 8-bit value | | Word | A 16-bit value | | Long | A 24-bit value | | Double | A 32-bit value | | Bank byte | The first two digits of a long address or a long value (e.g. "$12" in $\*\*12\*\*3456) | | High byte | The middle two digits of a long address, absolute address, long value or word value (e.g. "$34" in both $12\*\*34\*\*56 and $\*\*34\*\*56) | | Low byte | The final two digits of a long address, absolute address, long value or word value (e.g. "$56" in both $1234\*\*56\*\* and $34\*\*56\*\*) | --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/glossary.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc.md). # Miscellaneous opcodes This chapter explains opcodes which don't really fit in any other chapter. ## NOP | Opcode | Full name | Explanation | | ------- | ------------ | ----------------------- | | \*\*NOP\*\* | No operation | Does absolutely nothing | It is often used to disable existing opcodes in a ROM without shifting around the machine code. It can also be used to give time for the \[math hardware registers\](/assembly-for-the-snes/mathemathics-and-logic/math.md) to do their work. ## XBA | Opcode | Full name | Explanation | | ------- | ---------------- | ------------------------------------------------------------------------------------ | | \*\*XBA\*\* | Exchange B and A | Swaps the high and the low bytes of the A register, regardless of the register size. | Here's an example: \`\`\` LDA #$0231 ; A = $0231 XBA ; A is now $3102 \`\`\` This even works with 8-bit A, as the high byte of A is "hidden" rather than set to $00: \`\`\` LDA #$12 ; A = $xx12 XBA ; A = $12xx LDA #$05 ; A = $1205 \`\`\` ## WAI | Opcode | Full name | Explanation | | ------- | ------------------ | ----------------------------------------------------- | | \*\*WAI\*\* | Wait for interrupt | Halts the SNES CPU until either an NMI or IRQ occurs. | Exactly what it says, it halts the SNES CPU until either an NMI or IRQ (both are interrupts) occurs. Practically speaking, the opcode loops itself infinitely until an interrupt occurs. ## STP | Opcode | Full name | Explanation | | ------- | -------------- | ------------------------------------------------------------------------------------------------------------------ | | \*\*STP\*\* | Stop the clock | The "clock" being the SNES CPU in this case, it halts the CPU completely until either a soft or hard reset occurs. | Exactly what it says, it stops the SNES CPU until you hit reset or power cycle the SNES. It does lower the power consumption of the SNES, if the couple of cents it'll shave off your power bill means that much to you. ## BRK | Opcode | Full name | Explanation | | ------- | -------------- | ----------------------- | | \*\*BRK\*\* | Software break | Causes a break to occur | This opcode causes the SNES to jump to the \[break vector\](/assembly-for-the-snes/deep-dives/vector.md). It also takes a byte parameter. Example usage: \`\`\` BRK #$02 \`\`\` The parameter is not used for anything in particular, but if you write a meaningful "catch" to the BRK, you could probably read what the value of the BRK was supposed to be, and do certain things depending on the value. When the BRK opcode is executed, the following events happen: \* First, the (24-bit) address of the instruction after \`BRK #$xx\` is pushed onto the stack \* Then, the (8-bit) processor flag register is pushed onto the stack. \* The interrupt disable flag is set (akin to \`SEI\`) \* The decimal mode flag is cleared (akin to \`CLD\`) \* The program bank register is cleared to $00 \* The program counter is loaded from the break vector. In other words, the code jumps to the address at the break vector. If the emulators are made properly, the opcode BRK makes debuggers snap. You could also program the break vector to do meaningful things. In fact, on SMWCentral, \[p4plus2 released a patch which does exactly this\](https://www.smwcentral.net/?p=section\\&a=details\\&id=20796); it shows debug information about the crash. ## COP | Opcode | Full name | Explanation | | ------- | ----------------------- | ------------------------------------------------------------------ | | \*\*COP\*\* | Coprocessor empowerment | Similar effects as BRK, as the SNES has no coprocessor to empower. | This opcode causes the SNES to jump to the \[COP hardware vector\](/assembly-for-the-snes/deep-dives/vector.md). It also takes a byte parameter. Example usage: \`\`\` COP #$04 \`\`\` The parameter is not used for anything in particular, but if you write a meaningful "catch" to the COP, you could probably read what the value of the COP was supposed to be, and do certain things depending on the value. When the COP opcode is executed, the following events happen: \* First, the (24-bit) address of the instruction after \`COP #$xx\` is pushed onto the stack \* Then, the (8-bit) processor flag register is pushed onto the stack. \* The interrupt disable flag is set (akin to \`SEI\`) \* The decimal mode flag is cleared (akin to \`CLD\`) \* The program bank register is cleared to $00 \* The program counter is loaded from the COP hardware vector. In other words, the code jumps to the address at the COP hardware vector. ## WDM | Opcode | Full name | Explanation | | ------- | ----------------------------- | ----------------------- | | \*\*WDM\*\* | Reserved for future expansion | Does absolutely nothing | WDM stands for "\[William (Bill) David Mensch, Jr.\](https://en.wikipedia.org/wiki/Bill\_Mensch)", who designed the 65c816. This opcode was reserved for the possibility of multi-byte opcodes. Therefore, this opcode actually takes a parameter. Example: \`\`\` WDM #$01 \`\`\` This opcode has the same effect as NOP however. Therefore, it does nothing. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/misc.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves.md). # Copying data 65c816 assembly has two opcodes dedicated to moving huge blocks of data from one memory location to another. | Opcode | Full name | Explanation | | ------- | ------------------- | -------------------------------------------------------------------------------------------- | | \*\*MVN\*\* | Move block negative | Moves a block of data, byte by byte, starting from the beginning and working towards the end | | \*\*MVP\*\* | Move block positive | Moves a block of data, byte by byte, starting from the end and working towards the beginning | MVP and MVN practically do a mass amount of LDA and STA to some RAM addresses. You can't move data to ROM, because ROM is read-only. It's highly recommended to have the A, X and Y registers at 16-bit mode. It's also highly recommended to preserve the data bank using the stack, as the move opcodes implicitly change this. Here's an example of a proper block move setup: \`\`\` PHB ; Preserve data bank REP #$30 ; 16-bit AXY ; ← Move instructions are located here SEP #$30 ; 8-bit AXY PLB ; Recover data bank \`\`\` ## MVN When using MVN, the three main registers all have a special purpose. | Register | Purpose | | -------- | ------------------------------------------------------------------ | | A | Specifies the amount of bytes to transfer, plus 1 | | X | Specifies the high and low bytes of the data source memory address | | Y | Specifies the high and low bytes of the destination memory address | The A register is "plus 1". This means that if you want to move 4 bytes of data, you load $0003, as this means $0003+1, thus 4 bytes. MVN can be written in two ways: \`\`\` MVN $xxyy ; or MVN $yy, $xx \`\`\` Where \`xx\` is the source bank, and \`yy\` is the destination bank. When executing the MVN opcode, the SNES stalls at that same opcode for each byte transferred. When a byte is transferred, the following happens: | Register | Event | | --------- | --------------------------------------------- | | A | Decreases by 1 | | X | Increases by 1 | | Y | Increases by 1 | | Data bank | Is set to the bank of the destination address | Seeing that A decreases by 1, eventually it will reach the value $0000, then it'll wrap to $FFFF. Once that wrap happens, the block move finishes and the SNES proceeds to execute the opcodes that follow. Here's an example of a block move: \`\`\` PHB ; Preserve data bank REP #$30 ; 16-bit AXY LDA #$0004 ; \\ LDX #$8908 ; | LDY #$A000 ; | Move 5 bytes of data from $1F8908 to $7FA000 MVN $7F, $1F ; / SEP #$30 ; 8-bit AXY PLB ; Recover data bank \`\`\` This example will move 5 bytes of data from address $1F8098 to $7FA000. ## MVP When using MVP, the three main registers all have a special purpose. | Register | Purpose | | -------- | ------------------------------------------------------------------ | | A | Specifies the amount of bytes to transfer, plus 1 | | X | Specifies the high and low bytes of the data source memory address | | Y | Specifies the high and low bytes of the destination memory address | The A register is "plus 1". This means that if you want to move 4 bytes of data, you load $0003, as this means $0003+1, thus 4 bytes. MVP can be written in two ways: \`\`\` MVP $xxyy ; or MVP $yy, $xx \`\`\` Where \`xx\` is the source bank, and \`yy\` is the destination bank. {% hint style="warning" %} Note that the \`MVP/MVN $yy, $xx\` notation is indeed written as \`MVP/MVN destination, source\`. Various sources indicate that it should be the other way around (\`source, destination\`), however, this tutorial follows Asar's assembly syntax. {% endhint %} When executing the MVP opcode, the SNES loops at that same opcode for each byte transferred. From this point on, this is where MVP differs from MVN. When a byte is transferred, the following happens: | Register | Event | | --------- | --------------------------------------------- | | A | Decreases by 1 | | X | Decreases by 1 | | Y | Decreases by 1 | | Data bank | Is set to the bank of the destination address | Considering that X and Y decrease, rather than increase, this means that MVP moves blocks of data from the end towards the beginning. Here's an example of a block move: \`\`\` PHB ; Preserve data bank REP #$30 ; 16-bit AXY LDA #$0004 ; \\ LDX #$8908 ; | LDY #$A000 ; | Move 5 bytes of data from ($1F8908-$0004) to ($7FA000-$0004) MVP $7F, $1F ; / SEP #$30 ; 8-bit AXY PLB ; Recover data bank \`\`\` This example will move 5 bytes of data from address $1F8904 to $7F9FFC. Although the transfer happens backwards, the transferred data isn't reversed. It still copies over as you'd expect. ## Edge cases \* When you set the A register to $0000, it means you will move 1 byte. \* When you set the A register to $FFFF, it means you will move 65536 bytes. \* When either the source or destination addresses cross a bank boundary, the high and low bytes reset to $0000, while the data bank remains unchanged. ## Easy notation Asar supports labels as parameters for LDA, LDX and LDY, so you can write block moves without having to calculate the source table size or address locations. The following examples allow for tables of all sizes, and assume the destination to be memory address $7FA000. An example for MVN: \`\`\` PHB REP #$30 LDA.w #SomeTable\_end-SomeTable-$01 LDX.w #SomeTable LDY #$A000 MVN $7F, SomeTable>>16 SEP #$30 PLB RTS SomeTable: db $00,$01,$02,$03,$04 .end \`\`\` An example for MVP: \`\`\` PHB REP #$30 LDA.w #SomeTable\_end-SomeTable-$01 LDX.w #SomeTable+SomeTable\_end-SomeTable-$01 LDY.w #$A000+SomeTable\_end-SomeTable-$01 MVP $7F, SomeTable>>16 SEP #$30 PLB RTS SomeTable: db $00,$01,$02,$03,$04 .end \`\`\` As you can see, MVP is considerably more complicated to setup. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/collection-of-values/moves.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques.md). # Techniques This chapter focuses on general techniques you can use in SNES ASM. ## Byte counting Each instruction assembles into one or more bytes. This happens as follows: First, the opcode is guaranteed assembled into a byte. Then the byte is followed by 0-3 bytes which serves as the parameter of the opcode. Every 2 hex digits equals 1 byte. This means that the maximum amount of bytes an instruction in SNES can use, is four bytes: opcode + a 24-bit parameter. Here is an example on how LDA would look like, when it's assembled with different addressing modes: \`\`\` LDA #$00 ; A9 00 LDA #$0011 ; A9 11 00 LDA $00 ; A5 00 LDA $0011 ; AD 11 00 LDA $001122 ; AF 22 11 00 \`\`\` An instruction without a hexadecimal parameter is only 1 byte, like \`INC A\` or \`TAX\`. An instruction with an 8-bit parameter is 2 bytes, like \`LDA #$00\`. An instruction with a 16-bit parameter is 3 bytes, like \`LDA $0000\`. An instruction with a 24-bit parameter is 4 bytes, like \`LDA $000000\`. It doesn't matter if the addressing mode is indexed, direct indirect or something else. It all depends on the length of the \`$\`-value. ## Shorthand zero comparison Faster comparison makes use of processor flags effectively, because branches actually depend on the processor flags. As mentioned in this tutorial earlier, \`BEQ\` branches if the zero flag is set, \`BNE\` branches if the zero flag is clear, \`BCC\` branches if the carry flag is clear, and so on. Often, if the result of ​any​ operation is zero (\`$00\`, or \`$0000\` in 16-bit mode), the zero flag gets set. For example, if you do \`LDA #$00\`, the zero flag is set. Nearly all opcodes which modify an address or register affect the zero flag. By making use of the zero flag, it's possible to check if a certain instruction has zero as its result (including loads). This way, you can write shorthand methods to check if an address actually contains the value \`$00\` or \`$0000\`. Here's an example: \`\`\` LDA $59 BEQ IsZero LDA #$01 STA $02 IsZero: RTS \`\`\` This code checks if address $7E0059 contains the value \`$00\`. If it does contain this value, then it immediately returns. If it does not contain the value \`$00\`, then it stores the value \`$01\` into address $7E0002. The reverse is also possible: checking if an address does \*not\* contain zero. here's an example: \`\`\` LDA $59 BNE IsNotZero LDA #$01 STA $02 IsNotZero: RTS \`\`\` This code checks if address $7E0059 does \*not\* contain the value \`$00\`. If it indeed does not contain this value, then it immediately returns. If it does contain the value \`$00\`, then it stores the value \`$01\` into address $7E0002, instead. ## Looping Loops are certain type of code flow which allow you to execute code repeatedly. It is especially useful when you need to read out a table or a memory region value-by-value. A practical example is reading out level data from the SNES ROM, in order to build a playable level. Levels are essentially a long list of object and sprite data, therefore, this data can be read out in a repetetive way, until you reach the end of such data. The examples within this section only loops through tables to copy them to RAM addresses, but of course, loops can be used to implement much more complex logic. ### Looping with a loop counter check It's possible to loop from the beginning to the end of a table. The following code demonstrates this. \`\`\` LDX.b #$00 ; Initialize the loop counter - LDA Table,x ; Reads out the values in the table STA $00,x ; Store them to addresses $7E0000-$7E0003 INX ; Increase loop counter by one CPX.b #Table\_end-Table ; Use the size of the table as the check BNE - ; If the loop counter doesn't equal this, then continue looping. RTS Table: db $01,$02,$04,$08 ; The values are read out in order .end \`\`\` The code initializes the loop counter with the value \`$00\`. With every iteration, this loop counter increases by one, and is then compared with the size of the table. Therefore, the loop executes for every byte within this table. ### Looping with a negative check While it's possible to loop from the beginning towards the end of a table, it's also possible to loop from the end towards the beginning of the table. This method has a "shorthand" check in order to see if the loop should be terminated, by making clever use of the "negative" processor flag. When using this method, your loop will essentially run backwards. \`\`\` LDX.b #Table\_end-Table-1 ; Get the length of the table ($03). The -1 is necessary, - LDA Table,x ; else the loop will be off-by-one. Use that as a loop counter. STA $00,x ; One by one store the values to addresses $7E0003 to $7E0000, in that order. DEX ; Decrease the loop counter by one. BPL - ; If the loop counter isn't negative, continue looping. RTS Table: db $01,$02,$04,$08 ; The values are read out backwards as well .end \`\`\` Because the loop counter serves as an index to address $7E0000, as well as the table, it reads and stores the values in a backwards order, as the counter starts with the value \`$03\`. The \`BPL\` ensures that the loop breaks once the loop counter reaches the value \`$FF\`. That means the negative flag will be set, thus BPL will not branch to the beginning of the loop. This loop method has a drawback however. When the loop counter is \`$81-$FF\`, the loop will execute once, then break immediately, as BPL will not branch. This is because after \`DEX\`, the loop counter is immediately negative. Remember that values \`$80\` to \`$FF\` are considered negative. However, if your initial loop counter is \`$80\`, it will first execute the code within the loop, decrease the loop counter by 1, \*then\* check if the loop counter is negative. Therefore with this loop, you can loop through 129 bytes of data at most. It's also possible to have the loop counter at 16-bit mode while having the accumulator at 8-bit mode. The following example demonstrates this: \`\`\` REP #$10 LDX.w #Table\_end-Table-1 ; Get the length of the table ($03). Notice the ".w" to force - LDA Table,x ; the assembler to use a 16-bit value. STA $00,x ; One by one store the values into address $7E0000-$7E0003. DEX ; Decrease the loop counter by one. BPL - ; If the loop counter isn't negative, continue looping. SEP #$10 RTS Table: db $01,$02,$04,$08 .end \`\`\` In this case, it's possible to loop through ‭32769 bytes of data at most. ### Looping with an "end-of-data" check. This method basically keeps looping and iterating through values, until it reaches some kind of an "end-of-data" marker. Generally speaking, this value is something that the code normally never uses as an actual value. The general consensus for this value is \`$FF\` or \`$FFFF\`, although the decision is entirely up to the programmer. Here's an example which uses such a marker. This type of loop is especially handy when it needs to process multiple tables with the exact same logic, but with varying table sizes. One example of such implementation is loading levels; The logic to parse levels is always the same, but the levels vary in size. \`\`\` LDX.b #$00 ; Initialize the index - LDA Table,x ; Read the value from the data table CMP #$FF ; If it's an end-of-data marker, then exit the loop. BEQ + STA $00,x ; If it's not, then store the value. INX ; Increase the index to the table BRA - ; Continue the loop + RTS Table: db $01,$02,$04,$FF .end \`\`\` In the case of this example, the code loops through four bytes of data, three of which are actual data and one of which is the end-of-data marker. Once the loop encounters this marker, in this case the value \`$FF\`, the loop is immediately terminated. ## Bigger branch reach In the \[branches chapter\](https://github.com/Ersanio/snes-assembly-book/blob/master/docs/programming/branches/README.md), it is mentioned that branches have a limited distance of -128 to 127 bytes. When you do exceed that limit, the assembler automatically detects that and throws some kind of error, such as the following: \`\`\` LDA $00 CMP #$03 BEQ SomeLabel ; Branch when address $7E0000 contains the value $03 NOP #1000 ; 1000 times "NOP" SomeLabel: RTS \`\`\` Would cause the following error in Asar, during assembly: \`\`\` file.asm:3: error: (E5037): Relative branch out of bounds. (Distance is 1000). \[BEQ SomeLabel\] \`\`\` This means that the distance between the branch and the label is 1000 bytes, which definitely exceeds the 127 bytes limit. If you necessarily have to jump to that one label, you can invert the conditional and make use of the \`JMP\` opcode for a longer jump reach: \`\`\` LDA $00 CMP #$03 BNE + ; Skip the jump when address $7E0000 doesn't contain the value $03 JMP SomeLabel ; This runs when address $7E0000 \*does\* contain the value $03 + NOP #1000 ; Shorthand for 1000 times "NOP" SomeLabel: RTS \`\`\` This way, the logic still remains the same, namely, the thousand NOPs run when address $7E0000 doesn't contain the value $03. The out-of-bounds error is solved. Additionally, replacing the \`JMP\` with \`JML\` will allow you to jump \*anywhere\* instead of being restricted to the current bank. ## Pointer tables A pointer table is a table with a list of pointers. Depending on the context, the pointers can either point to code or data. Pointer tables are especially useful if you need to run certain routines or access certain data for an exhaustive list of values. Without pointer tables, you would have to do a massive amount of manual comparisons instead. Here's an example of a manual version: \`\`\` LDA $14 CMP #$00 BNE + JMP FirstRoutine + CMP #$01 BNE + JMP SecondRoutine + CMP #$02 BNE + JMP ThirdRoutine + RTS FirstRoutine: LDA #$95 STA $15 RTS SecondRoutine: LDA #$95 STA $15 RTS ThirdRoutine: LDA #$95 STA $15 RTS \`\`\` You can imagine that with a lot of values that are associated with routines, this comparison logic can get huge pretty quickly. This is where pointer tables come in handy. This here is a pointer table: \`\`\` Pointers: dw Label1 dw Label2 dw Label3 dw Label4 \`\`\` As you can see, it's nothing but a bunch of table entries pointing to addresses. You can use labels in order to point to ROM, or defines to point to RAM. ### Pointer tables for code There are a few instructions designed for making use of pointer tables. They are as follows: | Instruction | Example | Explanation | | -------------------------------------- | ------------- | --------------------------------------------------------------------------------------------- | | \*\*JMP (\*\*\*\*\*absolute address\*\*\*\*\*)\*\* | JMP ($0000) | Jumps to the absolute address located at address $7E0000 | | \*\*JMP (\*\*\*\*\*absolute address\*\*\*\*\*,x)\*\* | JMP ($0000,x) | Jumps to the absolute address located at address $7E0000, which is indexed by X | | \*\*JML \\\[\*\*\*\*\*absolute address\*\*\*\*\*\]\*\* | JML \\\[$0000\] | Jumps to the long address located at address $7E0000 | | \*\*JSR (\*\*\*\*\*absolute address\*\*\*\*\*,x)\*\* | JSR ($0000,x) | Jumps to the absolute address located at address $7E0000, which is indexed by X, then returns | With these opcodes, as well as a pointer table, it is possible to run a subroutine depending on the value of a certain RAM address. Here's an example which runs a routine depending on the value of RAM address $7E0014: \`\`\` LDA $14 ; Load the value into A... ASL A ; ...Multiply it by two... TAX ; ...then transfer it into X JSR (Pointers,x) ; Execute routines. RTS Pointers: dw Label1 ; $7E0014 = $00 dw Label2 ; $7E0014 = $01 dw Label3 ; $7E0014 = $02 dw Label4 ; $7E0014 = $03 Label1: LDA #$01 STA $09 RTS Label2: LDA #$02 STA $09 RTS Label3: LDA #$03 STA $99 RTS Label4: LDA #$55 STA $66 RTS \`\`\` The short explanation is that depending on the value of RAM address $14, the four routines are executed. For value \`$00\`, the routine at \`Label1\` is executed. For value \`$01\`, the routine at \`Label2\` is executed, and so on. The long explanation is that we load the value into A and multiply it by two, because we use \*words\* for our pointer tables. Thus, we need to index every two bytes instead of every byte. This means that value \`$00\` stays as index value \`$00\` thus reading the \`Label1\` pointer. Value \`$01\` becomes index value \`$02\`, thus reading the \`Label2\` pointer. Value \`$02\` becomes index value \`$04\`, thus reading the \`Label3\` pointer. Value \`$03\` becomes index value \`$06\`, thus reading the \`Label4\` pointer. Because the JSR uses an "absolute, indirect" addressing mode, the labels are also absolute, thus they only run in the same bank as that JSR. ### Pointer tables for data The same concept can be applied for data instead of just subroutines. Imagine you want to read level data, depending on the level number. A pointer table would be a perfect solution for that. Here's an example: \`\`\` LDA $14 ; Load the level number into A... ASL A ; ...Multiply it by three... CLC ADC $14 TAY ; ...then transfer it into Y LDA Pointers,y STA $00 LDA Pointers+1,y STA $01 LDA Pointers+2,y ; Store the pointed address into RAM STA $02 ; To use as an indirect pointer REP #$10 LDY #$0000 - LDA \[$00\],y ; Read level data until you reach an end-of-data marker CMP #$FF BEQ Return ; Do something with the loaded level data here INY BRA - Return: SEP #$10 RTS Pointers: dl Level1 ; $7E0014 = $00 dl Level2 ; $7E0014 = $01 dl Level3 ; $7E0014 = $02 dl Level4 ; $7E0014 = $03 Level1: db $01,$02,$91,$86,$01,$82,$06,$FF Level2: db $AB,$91,$06,$78,$75,$FF Level3: db $D9,$B0,$A0,$21,$FF Level4: db $C0,$92,$84,$81,$82,$99,$FF \`\`\` In the first section, we use the same concept of multiplying a value to access a pointer table. Except this time, we multiply by three, because the pointer tables contain values that are \*long\*. We use this value as an index to the pointer table, and store the pointer in \`RAM $7E0000\` to \`$7E0002\`, in little endian. After that, in the second section, we use \`RAM $7E0000\` as an indirect pointer and start looping through its values, using Y as an index again. We keep looping indefinitely, until we hit an "end-of-data" marker, in this case the value \`$FF\`. We use this method because levels could be variable in length. We also use 16-bit Y because level data \*could\* be bigger than 256 bytes in size. Finally, we finish the routine by setting Y back to 8-bit and then returning. This example also shows how to use 24-bit pointers rather than 16-bit pointers. The pointer table contains long values. We use this in combination with a "direct, indirect \*long\*" addressing mode (i.e. the square brackets). ## Pseudo 16-bit math It is possible to perform 16-­bit \`ADC\` and \`SBC\` without actually switching to 16-­bit mode. This is actually quite useful in cases a 16-bit value is stored across two separate addresses as two 8-bit values. This is possible with the help of the carry flag as well as the behaviour of the opcodes \`ADC\` and \`SBC\`. Pseudo 16-bit math also works with \`INC\` and \`DEC\`, although you'd have to use them on the addresses instead of the A, X and Y registers. By making clever usage of the negative flag, it's possible to perform pseudo 16-bit math with this opcode also. ### ADC Here's an example of a pseudo 16-bit \`ADC\`: \`\`\` LDA #$F0 STA $00 ; Initialize address $7E0000 to value $F0 for this example LDA #$05 STA $59 ; Initialize address $7E0059 to value $05 for this example ; These would make the 16-bit value $05F0 LDA $00 ; Load the value $F0 into A CLC ; Clear Carry flag for addition. C = 0 ADC #$20 ; $F0+$20 = $10, C = 1 STA $00 ; $7E0000 has now the value $10 LDA $59 ; Load the value $05 into A ADC #$00 ; Normally adds $00 to value $05. BUT C = 1, so this adds $01 to $05 instead STA $59 ; A is now $06, and we store it into $7E0059 ; These would now make the 16-bit value $0610 ; across two addresses \`\`\` The carry flag is set after the first \`ADC\`. This means that the value has wrapped back to \`$00\` and increased from there. Because the carry flag is set, the second \`ADC\` adds $00 + carry, thus \`$01\`, thus increasing the second address by one. ### SBC Here's an example of a pseudo 16-bit \`SBC\`: \`\`\` LDA #$10 STA $00 ; Initialize address $7E0000 to value $10 for this example LDA #$05 STA $59 ; Initialize address $7E0059 to value $05 for this example ; These would make the 16-bit value $0510 LDA $00 ; Load the value $10 into A SEC ; Set Carry flag for subtraction. C = 1 SBC #$20 ; $10-$20 = $F0, C = 0 STA $00 ; $7E0000 has now the value $F0 LDA $59 ; Load the value $05 into A SBC #$00 ; Normally subtracts $00 from value $05. BUT C = 0, so this subtracts $01 from $05 instead STA $59 ; A is now $04, and we store it into $7E0059 ; These would now make the 16-bit value $04F0 ; across two addresses \`\`\` The carry flag is cleared after the first \`SBC\`. This means that the value has wrapped back to \`$FF\` and decreased from there. Because the carry flag is cleared, the second \`SBC\` subtracts $00 + carry, thus \`$01\`, thus decreasing the second address by one. ### INC Here's an example of a pseudo 16-bit \`INC\`: \`\`\` LDA #$FF STA $00 ; Initialize address $7E0000 to value $FF for this example LDA #$03 STA $59 ; Initialize address $7E0059 to value $03 for this example ; These would make the 16-bit value $$03FF INC $00 ; The value in $7E0000 is increased by 1, making it have the value $00 BNE + ; This sets the zero flag, thus the branch is not taken INC $59 ; Thus, the value in $7E0059 is also increased by 1 + RTS ; These would now make the 16-bit value $0400 ; across two addresses \`\`\` By making clever usage of the zero flag, we know that the result of \`INC $00\` is actually the value \`$00\`, because that's the only time the zero flag is set. If the result is indeed the value \`$00\`, then the other address needs to be increased also. ### DEC Here's an example of a pseudo 16-bit \`DEC\`: \`\`\` LDA #$00 STA $00 ; Initialize address $7E0000 to value $00 for this example LDA #$03 STA $59 ; Initialize address $7E0059 to value $03 for this example ; These would make the 16-bit value $$0300 DEC $00 ; Decrease the value in $7E0000 by 1 LDA $00 ; If it results in $FF, then we wrapped from the value $00 to $FF CMP #$FF ; Thus, we need to decrease the value in $7E0059 also BNE + DEC $59 + RTS ; These would now make the 16-bit value $02FF ; across two addresses \`\`\` As you can see, there's an extra check for the value \`$FF\`, because there's no shorthand way to check if the result of a \`DEC\` is exactly the value \`$FF\`. If the result indeed is the value \`$FF\`, then the other address needs to be decreased also. ## ADC and SBC on X and Y Increasing and decreasing A by a certain amount is easy because of \`ADC\` and \`SBC\`. However, these kind of instructions do not exist for X and Y. If you want to increase or decrease X and Y by a small amount, you would have to use \`INX\`, \`DEX\`, \`INY\` and \`DEY\`. This quickly gets impractical if you have to increase or decrease X and Y by great numbers (5 or more) though. In order to do that, you can temporarily transfer X or Y to A, then perform an \`ADC\` or \`SBC\`, then transfer it back to X or Y. ### Addition Here's an example using \`ADC\`: \`\`\` TXA ; Transfer X to A. A = X CLC ; ADC #$42 ; Add $42 to A TAX ; Transfer A to X. X has now increased by $42 \`\`\` By temporarily transferring X to A and back, the \`ADC\` practically is used on the X register, instead. ### Subtraction Here's an example using \`SBC\`: \`\`\` TXA ; Transfer X to A. A = X SEC ; SBC #$42 ; Subtract $42 from A TAX ; Transfer A to X. X has now decreased by $42 \`\`\` By temporarily transferring X to A and back, the \`SBC\` practically is used on the X register, instead. ## Checking flags \[Flags\](/assembly-for-the-snes/the-fundamentals/binary.md), are bits which serve as some sort of an "on/off" switch on a certain property. One byte contains 8 bits, but how do you actually \*check\* if a flag is on or off? In ASM, when you usually use comparison and branching opcodes, you check for whole bytes rather than a single bit within that byte. There are two opcodes which are suitable for this. We will use the example from the binary chapter: \`\`\` 10100000 ││└───── "Is daytime" flag │└───── "Is horizontal level" flag └───── "Is raining" flag \`\`\` Imagine this byte is stored in address $7E0095 for the examples to follow. ### The "AND" opcode \`AND\` is handled in the \[Bitwise Operations\](/assembly-for-the-snes/mathemathics-and-logic/logic.md) chapter. By using AND, you can basically "isolate" bits and check if any of them are set or cleared. For example, if we want to check if the "daytime" flag is set, you would load the address into A, then \`AND\` just that one bit: \`\`\` LDA $95 AND #%00100000 ; can also be written as #$20 BNE DayTimeIsSet ; branches when the daytime flag is set ... \`\`\` If that one bit in address $7E0095 is set, then the result of that \`AND\` will also be that A gets the value \`$20\`. Thus, the zero flag is cleared, and the branch is taken. If you want to check if either of the two flags are set (e.g. the daytime flag \*or\* the raining flag), you'd use \`AND\` to check two bits, rather than one: \`\`\` LDA $95 AND #%10100000 ; can also be written as #$A0 BNE IsRainingOrDaytime ; branches when both rain OR daytime flags are set ... \`\`\` If you want to check if both of the two flags are set (e.g. the daytime flag \*and\* the raining flag), you'd have to use \`AND\` and then a \`CMP\`. Then, you'd branch if the value resulting from the \`AND\` is equal to the flags you want set: \`\`\` LDA $95 AND #%10100000 ; First filter the bits CMP #%10100000 ; Then check if both bits are set BEQ IsRainingOrDaytime ; branches when both rain AND daytime flags are set ... \`\`\` ### The "BIT" opcode The \`BIT\` opcode, which is also handled in the \[Bitwise Operations\](/assembly-for-the-snes/mathemathics-and-logic/logic.md) chapter, is special because it can actually check if bits 7 and 6 (bits 15 and 14 in 16-bit mode) of an address' value are set, without having to modify A. Here's an example: \`\`\` BIT $95 BMI IsRaining ; Branches when bit 7 (negative flag) is set BVS IsHorizontalLevel ; Branches when bit 6 (overflow flag) is set ... \`\`\` --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/deep-dives/techniques.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches.md). # Comparing, branching, labels You can run certain pieces of code depending on certain conditions. For this, you'll have to make use of comparison and branching opcodes. Comparison opcodes compare the contents of A, X or Y with another value. A branching opcode then controls the program flow, depending on the comparison. ## Branches Branches are opcodes which control the flow of the code depending on the outcome of comparisons. Branches jump to a \*\*label\*\*. The branching opcodes have a range of -128 to 127 bytes. This means they can either jump 128 bytes backwards, or they can jump 127 bytes forward, relative to the program counter. One exception is BRL (Branch Long). BRL has a range of 32768 bytes (8000 in hex), which is a whole bank. If the branch goes out of range, the assembler gives an error. You'll have to find a way to put the destination label into the branch's reach. The "tips and tricks" chapter covers this. ## Labels Labels are basically text placed in code to locate an entry point of a jump or a "table". Labels are no opcodes or anything. It's basically an easier way to specify an offset/address, because the labels get turned into numbers by the assembler. It is good practice to give labels meaningful names, for your own sake. Examples codes in this chapter will make use of labels. ## CMP To make comparisons, you usually compare the contents of A with something else. The primary way for that is the opcode \`CMP\`. | Opcode | Full name | Explanation | | ------- | --------- | ------------------------------ | | \*\*CMP\*\* | Compare A | Compares A with something else | CMP takes whatever is in A, and compares it with a specified parameter. After using a CMP instruction, you need to use an opcode that will perform the type of "branch" that you wish to occur. It's also possible to compare 16-bit values. Just change \`CMP #$xx\` to \`CMP #$xxxx\`. ## BEQ and BNE There are branch opcodes which branch depending on if a value equals or doesn't equal. | Opcode | Full name | Explanation | | ------- | -------------------- | ---------------------------------------------------------------- | | \*\*BEQ\*\* | Branch if equals | Branches if the comparison equals with the compared value | | \*\*BNE\*\* | Branch if not equals | Branches if the comparison doesn't equal with the compared value | BEQ branches if the comparison is equal with the compared value. Here's an example: \`\`\` LDA $00 ; Loads the current value of RAM address $7E0000 into A CMP #$02 ; Compares A with the immediate value $02 BEQ Label1 ; If A = $02, go to the codes in Label1. NOTE: Case-Sensitive LDA #$01 ; \\ Else STA $1245 ; / Store value $01 into RAM $7E1245. RTS ; This instruction is used to end a routine. Label1: STZ $19 ; Store zero in $7E0019 RTS ; End. \`\`\` This code will store zero ($00) in $7E0019 when $7E0000 contains the value $02. If it doesn't have $02 as its value, the code will then store value $01 in $7E1245. As you can see, BEQ will "jump" to a portion of the code when compared values are equal, skipping certain code. In this case, the code jumps to the code located at the label "Label1". BNE branches if the comparison doesn't equal with the compared value. Here's an example: \`\`\` LDA $00 ; Loads the current value of RAM address $7E0000 into A CMP #$02 ; Compares A with $02 BNE Label1 ; A = NOT $02, finish the code, do nothing. LDA #$01 ; \\ Else STA $1245 ; / Store something in RAM $7E1245 Label1: ; RTS ; End. \`\`\` This code will store $01 to $7E1245, if $7E0000 has the value $02. If RAM address $7E0000 doesn't have the value $02, the code will instead do nothing and simply return. ## Comparing addresses You can also compare RAM addresses with each other. For example: \`\`\` LDA $00 ; Load $7E0000's value into A CMP $02 ; Compare A with $7E0002 BEQ Equal ; Branch if equal \`\`\` When RAM addresses $7E0000 and $7E0002 have the same values, the branch will be taken. ## CPX and CPY You can also compare values by using the registers X and Y. | Opcode | Full name | Explanation | | ------- | --------- | ------------------------------ | | \*\*CPX\*\* | Compare X | Compares X with something else | | \*\*CPY\*\* | Compare Y | Compares Y with something else | It's not just A which is capable of doing comparisons. For example, you can load a value into X or Y and compare it with something else. Here's an example using X: \`\`\` LDX $00 ; Load $7E0000's value into X CPX $02 ; Compare X with $7E0002 BEQ Equal ; Branch if equal \`\`\` It will have the same result as the example with comparing addresses. You can compare Y too by using CPY. However, you cannot mix registers. The the following is wrong: \`\`\` LDX $00 CMP $02 BEQ Equal \`\`\` CMP $02 would try to compare address $7E0002 with the register A, instead of X. This will cause unexpected results. ## BMI and BPL These are branch opcodes which branch depending on if a value is signed or unsigned. | Opcode | Full name | Explanation | | ------- | --------------- | --------------------------------------------------------------------------------------- | | \*\*BMI\*\* | Branch if minus | Branches if the last operation resulted in a negative value (thus, negative flag set) | | \*\*BPL\*\* | Branch if plus | Branches if the last operation resulted in a positive value (thus, negative flag clear) | BMI branches if the last operation is a minus/negative value. Minus values are the values $80-$FF. BPL branches if the last operation is not a minus value; it branches when the value is $00-$7F. ## BCS and BCC These are branch opcodes which branch depending on if a value is greater than or less than. | Opcode | Full name | Explanation | | ------- | --------------------- | ------------------------------------------------------------------------------------------------------------ | | \*\*BCS\*\* | Branch if carry set | Basically branches if the loaded value is greater than or equal to the compared value (thus, carry flag set) | | \*\*BCC\*\* | Branch if carry clear | Basically branches if the loaded value is less than the compared value (thus, carry flag clear) | BCS branches if the loaded value is equal or greater than the compared value. Alternatively, this also branches when the carry flag is set. BCC branches if the loaded value is lesser than the compared value. Alternatively, this also branches when the carry flag is clear. Please note that this BCC doesn't get taken if the compared value is equal, unlike BCS. ## BVS and BVC These are branch opcodes which branch depending on if a value results in a mathematical overflow or not, thus, when the overflow flag is set or clear. | Opcode | Full name | Explanation | | ------- | ------------------------ | -------------------------------------------------------------------------------------------- | | \*\*BVS\*\* | Branch if overflow set | Branches if the comparison causes a mathematical overflow (thus, overflow flag set) | | \*\*BVC\*\* | Branch if overflow clear | Branches if the comparison doesn't cause a mathematical overflow (thus, overflow flag clear) | The "overflow" flag is a processor flag, explained later in the tutorial. ## BRA and BRL These are unconditional branches which are always taken. | Opcode | Full name | Explanation | | ------- | ------------------ | --------------------------------------- | | \*\*BRA\*\* | Branch always | Always branches | | \*\*BRL\*\* | Branch always long | Always branches, but with greater reach | BRA will ALWAYS branch; it doesn't even check for conditions. BRL does the same, but it has a longer reach, enough to cover half a bank for each direction. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-basics/branches.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://ersanio.gitbook.io/assembly-for-the-snes/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers.md). # The SNES registers In SNES, there are several "registers" used for different purposes. They cannot be missed; they're one of the reasons why the SNES can function properly. Basically, registers are "global variables" which can be used to hold values, or can be used for math and logic and all those fancy stuffs! These registers can be accessed anytime. ## Accumulator The accumulator, also known as \*\*A\*\*, is used for general math, bit shifts, bitwise operations and loading indirect values. A can also hold general-purpose variables to store things to the memory and other registers. This register can hold either an 8-bit or 16-bit value The accumulator sometimes is referred to as \`B\` or \`C\` in some opcodes. B means the high byte of the accumulator, while C means the full 16-bit accumulator. {% hint style="warning" %} In reality, this register can be considered to be always 16-bit. When A is in 8-bit mode, you access the low byte of this register. When A is in 16-bit mode, you access both the high and low bytes of this register. The high byte doesn't get cleared when A enters 8-bit mode, even when new values are written to A, which is why the high byte can be considered to be "hidden". Also, certain instructions use both high and low bytes of the A register, regardless of whether A is in 8-bit or 16-bit mode. {% endhint %} ## Indexers The indexers are two registers, known as \*\*X\*\* and \*\*Y\*\*. Even though they are separate registers, they have exactly the same purposes and behave exactly the same. These registers are made for indexing, explained later in this tutorial. These registers can also be 8-bit or 16-bit. X and Y can also hold general-purpose variables to store things to the memory and other registers. X and Y are "paired" – they can be 8-bit or 16-bit mode only at the same time. One of them can't be 8-bit while the other one is 16-bit. {% hint style="warning" %} When X and Y leave 16-bit mode, their high bytes get cleared to the value $00, unlike the A register where the high byte remains intact. {% endhint %} ## Direct Page The direct page register is a 16-bit register, used for the direct page addressing mode (explained later in this tutorial). When you access a memory address by its direct page notation, the value in the direct page is added to that address. You can generally ignore this register if you're just beginning with assembly. ## Stack Pointer The stack pointer register is a 16-bit that holds the pointer to the stack in the RAM (explained later in this tutorial), relative to memory address $000000. The register dynamically changes, as you push and pull values to the stack (explained later in the tutorial). ## Processor Status The processor status register holds the current processor flags in 8-bit format. There are 8 processor flags, and they all occupy one bit. Changing this register would alter the SNES behaviour greatly. Processor flags are explained later in this tutorial. ## Data bank The data bank register holds the current data bank address as a single byte. When you access an address using the "absolute address" notation, the SNES will use this register to determine the bank of the address. ## Program bank The program bank register keeps track of the current bank of the currently executed instruction. So, if there is a code executed at address $018009, this register will hold the value $01. ## Program counter This register keeps track of the high and low bytes of the address of the currently executed instruction. So, if there is an instruction executed at $018009, this register will hold the value $8009. --- # Agent Instructions This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com. ## Querying This Documentation If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question. Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter: \`\`\` GET https://ersanio.gitbook.io/assembly-for-the-snes/the-fundamentals/registers.md?ask=&goal= \`\`\` \`ask\` is the immediate question: it should be specific, self-contained, and written in natural language. \`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation. Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections. ---