Intro To 65816 Assembly Programming

By Okibi 

Forward 

Intro To 65816 Assembly Programming is a book written by Okibi to help others who wish to become familiar with how the SNES works. It's set up into several chapters, each with sections that contain information on ways to write code for use on the SNES. You can use this information to code new SNES games, or to hack existing ones. I hope you enjoy reading this and would love to hear any suggestions you have. With that in mind, let's get started.

Chapter 1 - The Basics 

65816, or SNES, assembly can seem hard to learn to the average person, but in fact it is really easy once you get the basics down. There are a few guides out there that'll teach you to code for the 65816, but not many really show you how to get started or how things work. This guide will be as simple an intro that you can find. First, you'll need to know what is "under the hood", so to speak, of the SNES. 

SNES Specs 

The SNES features a 16 bit processor, 1 Mb of RAM, SPC700 sound co-processor, and the most advanced graphics chip of it's time. There are also two kinds of games for this. One is called HiRom and runs at 3.58 MHz and the other is LowRom and runs at 2.68 MHz. The SNES also doesn't understand ASCII code. ASCII (American Standard Code for Information Interchange) is a code for most symbols that you use in any word editor. Usually, text documents are referred to as ASCII. It doesn't matter if you know ASCII codes or not, just know that the SNES doesn't understand it. Also know that 65816 assembly usually doesn't work in base 10 but rather base 2. Base 2 and 10 will be discussed in the next section. If you are coding 65816 for use on the SNES, you'll need to know the SNES registers. These will be covered in another document, not this one. Everything else you need to know will be discussed in other sections. 

Base 2 & Base 10 

First let me say that these are methods of counting. Most people use Base 10. Base 10 is also called decimal and has 10 different digits (0,1,2,3,4,5,6,7,8,9). We use this method because we have 10 fingers and it makes it easy to count. Base 2 is how computers count. Base 2 is also called binary and has 2 different digits (0,1). This is where most people get confused. In base 10, when you count to 9, you will say 10 next because it's the next number. In the number 10, there are 2 digits, 1 and 0. Everyone knows that in the number 10 the 1 is in the ten's place and the 0 is in the one's place. So this number could be said as "1 ten and 0 ones", or simply 10. You would then proceed until you got to 20, and you have 2 in the ten's place. Base 2 is exactly like this, only with less digits. 0 is 0, 1 is 1, but now we are out of digits to use. We then proceed to the two's place. We now get 10, or "1 two and 0 ones". We proceed until we run out of digits again, then we will go to the next place. In base 10 this is called the hundred's place, but in base 2 it is called the four's place. You always get the next place by multiplying the name of the last place by the base you are in. 10 times 10 is 100, so we get the hundred's place in base 10, but in base 2 we get the four's place because 2 times 2 is 4. Let me make a pretty little graph for you:

Base 10                            Base 2

1 - one's place                 1 - one's place

10 - ten's place                10 - two's place

100 - hundred's place      100 - four's place

1000 - thousand's place   1000 - eight's place 

I hope that makes sense. Here are the first 12 numbers in base 10:

0,1,2,3,4,5,6,7,8,9,10,11

Here are the first 11 numbers in base 2:

0,1,10,11,100,101,110,111,1000,1001,1010,1011

That'll cover the basics of base 2 and base 10. 

The Accumulator

Now we get to jump into actually using the code. First, we must go over what the accumulator is and what it does. It can be referred to as the heart of the 65816. It's able to be added, subtracted, and shifted however you wish. It can be 8 or 16 bit and can access any part of the memory. There are many things to use this for, but first we'll go over things called instructions. Instructions are commands given to perform a specific task. Much like people respond to commands such as walk, talk, run, swim, jump, the 65816 responds to instructions such as LDA. LDA stands for LoaD Accumulator. This is the first command you'll want to know. When giving this command, you'll want to give some kind of data that you'll be loading. Saying "LDA #1" is like saying "eat bread". You give what you'll be doing with this command after giving the command. While LDA means load and is told #1, he'll load #1. Just like while eat means to eat and is told bread, he'll eat the bread. Hope that makes sense. You are probably asking yourself why I put a # before the 1. The # tells LDA that we'll be loading a number. This is much like saying wheat before saying bread. Like there are other breads, there are other data to be loaded. $ means it'll be in hexadecimal, which is another counting method we didn't go over, and won't yet. % means the number is in binary, which is base 2 and we did go over that. If you said #$80, but forgot the #, LDA would read that as $80 and that is an address that it'll read from. If there is a $ and no #, it is an address. Now you are probably asking yourself what I mean by address. Well, the address can be either a location in memory, or in the rom itself. Now, let's say you wanted to load the binary number 10 into the accumulator. You would do this with the following code:

LDA #%10

That will load the binary number 10 into the accumulator. Now let's say you wanted to load the decimal number 42. You would do this:

LDA #42

And an address like this:

LDA $2120

Now these commands don't store the value to the accumulator. You have to do that with the STA command. STA stands for STore Accumulator. This command is used to store the current value of the accumulator to any writable address. We'll get further into this command when we cover X and Y registers.  

Status Registers (NVMXDIZC) 

Status Registers are very important to know. They will do a lot more than you think. Let's start by covering what they are. They are n, v, m, x, d, i, z, and c. Each flag (letter) has a different bit. Here's a nice little model for you:

n = bit 7 = Negative

v = bit 6 = Overflow

m = bit 5 = Memory Size

x = bit 4 = X & Y Register Size

d = bit 3 = Base

i = bit 2 = IRQ

z = bit 1 = Zero

c = bit 0 = Carry

We will cover most of these later, but I want to cover the ones you'll use first now. Let's start by going over how to enable and disable them. We do this with the SEP and REP commands. SEP stands for SEt Processor and REP stands for REset Processor. All the bits get set in binary. First, you must realize that these have two settings, 0 and 1. You set them like this:

SEP #%00000000

and reset them like this:

REP #%00000000

You see those zeros? Those are the bits. They are numbered like this:

76543210

You may be asking why I put all zeros there. It's because you can set them to 0 or 1, depending on which setting you need. So say we wanted to set base to decimal and also set the X & Y Registers to 8 bit. You do that like this:

SEP #%00011000

X & Y Register Size is the 4th bit and is set to 1 because 8 bit = 1 and 16 bit = 0. Base is the 3rd bit and is set to 1 because 1 = decimal and 0 = binary. You can almost think of these as on/off switches. Or Up/Down buttons on an elevator. Now let's go over some of the commands we'll use to modify the status registers. Here's a list to set the flags:

SEC (SEt Carry flag)

SED (SEt Decimal flag)

SEI (SEt Interrupt flag)

And here's a list that clears the flags:

CLC (CLear Carry flag)

CLD (CLear Decimal flag)

CLI (CLear Interrupt flag)

We'll cover these and other commands later. 

Adding and Comparing Values 

Firstly, let's look at comparing values. We can do this with the CMP command. CMP stands for CoMPare and will compare a number with the value in the accumulator. We could do this:

LDA number

CMP #67

This will compare whatever "number" is with 67. The result goes nowhere because we aren't sending it anywhere. LDA, as you should know from earlier, is saying load and what is it loading? It's loading number. If you follow it with a CMP, it will compare number with whatever you tell CMP, in this case 67. Just know that this is a good way to compare information. Now let's look at adding.

Let's go over the ADC command. ADC stands for ADd with Carry. This will add a number to the accumulator. We use this command to perform addition that would normally be too much for the CPU to handle. With this command, if the result is greater than the accumulator's limit to hold, then carry is set. Also, if the carry bit is set, an extra one will get added to the accumulator and then the carry bit will be cleared. Let's look at how we would add two numbers and store them as number.

CLC

LDA #42

ADC #8

STA number

What this does is use CLC to clear the carry flag. We usually need to do this before doing any multiplication. Next, we loaded 42 into the accumulator with LDA. Then we added 8 to 42 with ADC. Finally, we stored our new value to the variable number. This is important for the test program you will be running later. 

Commenting 

Let me say that commenting is not necessary for your code to work. It gets put next to the code to help you understand what the code does. You have to add your comments, the compiler won't do it for you. After you code something, it has to be compiled to be able to run. You can think of this like preparing a sandwich before you eat it. Without preparing it, there is just bits and pieces scattered everywhere that are of no use to you. When you compile the program, it skips comments like it doesn't see them. When text on a line is after a ; it is called commented. Like this:

LDA #$4A    ;load $4A into the accumulator

The compiler sees this as just:

LDA #$4A

You can use this on long programs to remind yourself what is going on in the code. But remember, if the line of text extends to the next line, you need another ; before it, like this:

LDA #$4A      ;load $4A into the accumulator so we can
STA number    ;store it to a variable and use it later

This can come in handy later when you program on your own. If you ever write code be sure to use comments to keep track of what's going on, even if you think you can remember it all. Odds are, until you get good, you'll forget what the commands do. 

X & Y Registers 

X and Y registers have three main purposes; To act as a pointer when accessing memory, to temporarily store data, and to hold the counter in a loop. Keep in mind that they don't have to do all these at once. These registers are usually 16 bit but unlike the accumulator, they can't access every part of the memory. Now we'll look at the LDX command. LDX stands for LoaD X register and is used in much the same way as LDA. Also, while LDA used CMP to compare values, LDX uses the CPX command. CPX stands for ComPare X register. This is pretty much the same as you did earlier, but I'll show you again anyways.

LDX number    ;this loads the variable number

CPX #56       ;and this compares it to 56

We can also do the exact same thing with the Y register like this:

LDY number    ;this loads the variable number

CPY #56       ;and this compares it to 56

Now you can see that the Y register gets treated the same as the X register. While LDX stands for LoaD X register, LDY stands for LoaD Y register and the compare command works like that as well. Let's use this information in a loop. A loop is when something continues until a requirement is met. We'll also use the BNE command. This is one of many branch commands and it stands for Branch if Not Equal. It means if you were to compare the too values, like in the previous code 56 to number, and they weren't equal, BNE would send it back to the beginning or where you told it to go. We also will use the WAI command. WAI stands for WAit for Interrupt. It says to not go on until an interrupt is ready to process something. You will also see the INX command. INX stands for INcrease X register and it does just that. It increases the value of the X register by one every time you use this command. Let me show you:

LDX number

loop:

WAI

INX

CPX #56

BNE loop

First, we used LDX to load number. Then, we used loop as a variable. When you see a word followed by a : that usually indicates a variable. It also acts as a position to branch to. Next we have the WAI command which just means wait until the next command can run then run it. Our next command is INX which will increase number by 1. If number was 42 it will now be 43. CPX compares number to 56. BNE says if the number does not equal 56, go to loop. We set loop in the second command in that code, so it goes there, and it runs again. This time when it gets to INX, it will increase number to 44, because it was just 43. It will go on and on like this until number reaches 56, then BNE will have no effect so the code will move on. Hope that makes sense. Now, there are other commands to work with the X and Y registers. Two you may be interested in are DEX and DEY. They stand for DEcrease X register and DEcrease Y register respectively. They work in the same way as INX and INY, only the decrease the value. We'll cover more on the X and Y registers later. 

This ends chapter 1. The next chapter will be released shortly, but this should be enough to keep you busy until then.

Download This Document

Back To Rated O