Programming Basics: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
m (1 revision: Rest of pages not related to reference)
 
m (interwiki and grammar)
 
(10 intermediate revisions by 4 users not shown)
Line 1: Line 1:
== Opcodes and their operands ==
NES runs on the [[wikipedia:MOS Technology 6502|6502]] microprocessor, an 8-bit microprocessor with a 16-bit address bus. The 6502 has powered systems like Commodore 64, Apple II, Atari 2600 and Nintendo Entertainment System. The 6502 has different mnemonics than what some assembly programmers might be used to and some useful instructions like <code>mul</code> (used for multiplication) are not available in the 6502 and hence present the need to program them on our own.


To be written.
== Stack ==
{{main|Stack}}
Stack is a data structure used to store data which is very simple and much faster than a heap-based memory structure. It runs by the principle of '''last-in-first-out''', where any new data that is to be entered, is put at the top of the stack (pushing data to the stack) and when removing data from the stack (popping data from the stack), the data that was entered last (the data that is at the top) will be removed first.


== Registers ==
== Instructions and Opcodes ==


To be written.
=== Instructions ===


== The stack ==
Instructions are actions that the processor performs. The 6502 has 56 of such instruction including instructions for operations such as addition, subtraction, AND, OR, ROR, etc. All the instructions are denoted by a 3-letter mnemonic and are then followed by their operands.


=== Overview ===
There are some instructions which perform operation on a specific register or need a specific register for its operation, such instructions contain the register mnemonic of the specific register in their instruction mnemonic only. For Example - <code>LDA</code> loads a byte of memory into the accumulator register denoted by <code>A</code>.


The stack on the 6502 is specific to page $01 of memory space (e.g. $0100 to $01FF).  The stack works "downwards", meaning as you push values on to the stack, the stack pointer (herein referred to as S) decreases.  The stack pointer is an 8-bit value, and should be initialised to $FF during program initialisation; the CPU will do this for you, but it's good practise to do it anyways.
=== Opcodes ===


As you push values on to the stack (using PHA or PHP), S will decrement by 1. As you pull values off the stack (using PLA or PLP), S will increment by 1.
Opcodes (abbreviated from operation codes) are the part of instruction in machine language which specifies the operation to be performed by the processor. Operands are the data on which the operation is performed. The 6502 processor has a total of 256 possible opcodes, but only 151 were used originally, arranged into 56 instructions which the NES used.


If you have trouble conceptualising how the 6502 stack works, imagine stacking a bunch of dinner plates on top of one another; you can't take a plate out from the middle of the stack, you have to take one off the very top each time.
== Registers ==
 
=== Pushing data on to the stack ===
 
Let's look at some example code:
<code>
_init:
  ldx #$ff ; Set the stack pointer to $FF
  txs ; (e.g. $01FF)
 
_pushstack:
  lda #$e0 ; Push value $e0 on to the stack.
  pha ; $01FF now contains $e0, and S is now $FE.
 
  ldy #$bb ; Push value $bb on to the stack.
  tya
  pha ; $01FE now contains $bb, and S is now $FD.
 
  txa
  pha ; Push value $ff (from the _init routine) on to the stack.
; $01FD now contains $ff, and S is now $FC.
</code>
 
At this point in our program, we have pushed 3 values on to the stack: $e0, $bb, and $ff.  Since $ff was the last thing we pushed onto the stack, it will be the first thing we pull off the stack.  We can't pull the $bb value until $ff has been pulled off, and so on -- hence the term "stack".


=== Pulling data off the stack ===
The 6502 processor has six 8-bit registers, with the exception of the Program Counter, which is 16-bit. The registers are as follows:


Using the above section (Pushing data on to the stack) as a preface, let's continue:
#Accumulator(A) - The accumulator can read and write to memory. It is used to store arithmetic and logic results such as addition and subtraction.
#X Index(X) - The x index is can read and write to memory. It is used primarily as a counter in loops, or for addressing memory, but can also temporarily store data like the accumulator.
#Y Index(Y) - Much like the x index, however they are not completely interchangeable. Some operations are only available for each register.
#Flag(P) - The register holds value of 7 different flags which can only have a value of 0 or 1 and hence can be represented in a single register. The bits represent the status of the processor.
#Stack Pointer(SP) - The stack pointer hold the address to the current location on the [https://www.nesdev.org/wiki/Stack Stack]. The stack is a way to store data by pushing or popping data to and from a section of memory.
#Program Counter(PC) - This is a 16-bit register unlike other registers which are only 8-bit in length, it indicates where the processor is in the program sequence.


<pre>
== Math Operations ==
_pullstack:
  pla ; Pull the value $ff off the stack, and put it into the accumulator.
  tax ; S now becomes $FD.


  pla ; Pull the next value ($bb) off the stack, and put it into the X register.
The 6502 processor only has instruction for addition and subtraction, it unfortunately doesn't have an instruction for multiplication or division and hence puts the need to implement them on our own.
  tay ; S now becomes $FE.
 
  pla ; Pull $e0 off the stack, and put it into the Y register.
; S now becomes $FF -- which is where we started!
</pre>
 
Pulling may be called "popping" by people who come from an 8080, Z80, or x86 background, where the instruction is called ''pop''.
 
=== Stack underflow and overflow ===
The terms "overflow" and "underflow" refer to situations where the program is either attempting to push more data on to the stack when S is already at $FF, or attempting to pull data off of the stack when S is already at $00.  Usually this implies a PHA vs. PLA mismatch of some sort.
 
Occasionally these two terms are reversed, depending upon who you ask.
 
== Math operations ==


=== Simple operations ===
=== Simple operations ===
==== Addition and subtraction ====


''To be written.''
#Addition: The 6502 processor has the instruction <code>ADC</code> for addition. It adds the value of an 8-bit number to the accumulator along with the carry bit.
#Subtraction: The <code>SBC</code> instruction is used to subtract a value to the accumulator together with the <code>NOT</code> of the carry bit.


==== Bitwise (factor of 2) multiplication and division ====
=== Complex operations ===


To multiply the value in A by two, use the instruction ASL A.
==== Multiplication ====


To divide the value in A by two, use the instruction LSR A.
As multiplication is repeated addition, one can implement a simple loop to add the value of multiplicand (the quantity to be multiplied) to itself times the value of multiplier (the value multiplicand is to be multiplied with). This is a valid approach but a more efficient solution would be to use left shifts and additions which can significantly reduce the number of operations.


''To be written.''
The following routine multiplies two unsigned 16-bit numbers, and returns an unsigned 32-bit value.


=== Complex operations ===
==== Multiplication of arbitrary numbers ====
The following routine multiplies two unsigned 16-bit numbers, and returns an unsigned 32-bit value.
<pre>
<pre>
mulplr = $c0 ; ZP location = $c0
mulplr = $c0 ; ZP location = $c0
Line 143: Line 107:
;    partial+1 = High byte of upper word (bits 24 through 31)
;    partial+1 = High byte of upper word (bits 24 through 31)
;
;
</pre>
==== Division of arbitrary numbers ====
To be written.
==== Floating-point numbers ====
To be written.
==== Gaming: keeping score ====
To be written.
If you keep score in a binary number, you must convert it to a sequence of digits before displaying it. The article [[16-bit BCD]] lists a subroutine to do this.
== Making simple sounds ==
To be written.
== Controller input ==
To be written.
== Graphics (should be covered elsewhere!) ==
== "Hello, world!" program ==
Since the NES can't easily do something like <code>printf()</code> (or <code>echo</code> for those familiar with scripting), one of the easiest ways to test code is to output some audio.  Something along the lines of...
<pre>
reset:
  lda #$01 ; square 1
  sta $4015
  lda #$08 ; period low
  sta $4002
  lda #$02 ; period high
  sta $4003
  lda #$bf ; volume
  sta $4000
forever:
  jmp forever
</pre>
</pre>

Latest revision as of 19:17, 10 December 2023

NES runs on the 6502 microprocessor, an 8-bit microprocessor with a 16-bit address bus. The 6502 has powered systems like Commodore 64, Apple II, Atari 2600 and Nintendo Entertainment System. The 6502 has different mnemonics than what some assembly programmers might be used to and some useful instructions like mul (used for multiplication) are not available in the 6502 and hence present the need to program them on our own.

Stack

Main article: Stack

Stack is a data structure used to store data which is very simple and much faster than a heap-based memory structure. It runs by the principle of last-in-first-out, where any new data that is to be entered, is put at the top of the stack (pushing data to the stack) and when removing data from the stack (popping data from the stack), the data that was entered last (the data that is at the top) will be removed first.

Instructions and Opcodes

Instructions

Instructions are actions that the processor performs. The 6502 has 56 of such instruction including instructions for operations such as addition, subtraction, AND, OR, ROR, etc. All the instructions are denoted by a 3-letter mnemonic and are then followed by their operands.

There are some instructions which perform operation on a specific register or need a specific register for its operation, such instructions contain the register mnemonic of the specific register in their instruction mnemonic only. For Example - LDA loads a byte of memory into the accumulator register denoted by A.

Opcodes

Opcodes (abbreviated from operation codes) are the part of instruction in machine language which specifies the operation to be performed by the processor. Operands are the data on which the operation is performed. The 6502 processor has a total of 256 possible opcodes, but only 151 were used originally, arranged into 56 instructions which the NES used.

Registers

The 6502 processor has six 8-bit registers, with the exception of the Program Counter, which is 16-bit. The registers are as follows:

  1. Accumulator(A) - The accumulator can read and write to memory. It is used to store arithmetic and logic results such as addition and subtraction.
  2. X Index(X) - The x index is can read and write to memory. It is used primarily as a counter in loops, or for addressing memory, but can also temporarily store data like the accumulator.
  3. Y Index(Y) - Much like the x index, however they are not completely interchangeable. Some operations are only available for each register.
  4. Flag(P) - The register holds value of 7 different flags which can only have a value of 0 or 1 and hence can be represented in a single register. The bits represent the status of the processor.
  5. Stack Pointer(SP) - The stack pointer hold the address to the current location on the Stack. The stack is a way to store data by pushing or popping data to and from a section of memory.
  6. Program Counter(PC) - This is a 16-bit register unlike other registers which are only 8-bit in length, it indicates where the processor is in the program sequence.

Math Operations

The 6502 processor only has instruction for addition and subtraction, it unfortunately doesn't have an instruction for multiplication or division and hence puts the need to implement them on our own.

Simple operations

  1. Addition: The 6502 processor has the instruction ADC for addition. It adds the value of an 8-bit number to the accumulator along with the carry bit.
  2. Subtraction: The SBC instruction is used to subtract a value to the accumulator together with the NOT of the carry bit.

Complex operations

Multiplication

As multiplication is repeated addition, one can implement a simple loop to add the value of multiplicand (the quantity to be multiplied) to itself times the value of multiplier (the value multiplicand is to be multiplied with). This is a valid approach but a more efficient solution would be to use left shifts and additions which can significantly reduce the number of operations.

The following routine multiplies two unsigned 16-bit numbers, and returns an unsigned 32-bit value.

mulplr	= $c0		; ZP location = $c0
partial	= mulplr+2	; ZP location = $c2
mulcnd	= partial+2	; ZP location = $c4

_usmul:
  pha
  tya
  pha

_usmul_1:
  ldy #$10	; Setup for 16-bit multiply
_usmul_2:
  lda mulplr	; Is low order bit set?
  lsr a
  bcc _usmul_4

  clc		; Low order bit set -- add mulcnd to partial product
  lda partial
  adc mulcnd
  sta partial
  lda partial+1
  adc mulcnd+1
  sta partial+1
;
; Shift result into mulplr and get the next bit of the multiplier into the low order bit of mulplr.
;
_usmul_4:
  ror partial+1
  ror partial
  ror mulplr+1
  ror mulplr
  dey
  bne _usmul_2
  pla
  tay
  pla
  rts

Here's an example of the above _usmul routine in action, which multiplies 340*268:

  lda #<340	; Low byte of 16-bit decimal value 340  (value: $54)
  sta mulplr
  lda #>340	; High byte of 16-bit decimal value 340 (value: $01) (makes $0154)
  sta mulplr+1
  lda #<268	; Low byte of 16-bit decimal value 268  (value: $0C)
  sta mulcnd
  lda #>268	; High byte of 16-bit decimal value 268 (value: $01) (makes $010C)
  sta mulcnd+1
  lda #0		; Must be set to zero (0)!
  sta partial
  sta partial+1
  jsr _usmul	; Perform multiplication
;
; RESULTS
;    mulplr    = Low byte of lower word  (bits 0 through 7)
;    mulplr+1  = High byte of lower word (bits 8 through 15)
;    partial   = Low byte of upper word  (bits 16 through 23)
;    partial+1 = High byte of upper word (bits 24 through 31)
;