Difference between revisions of "6502 Beginner Tips"

From 8BitDev.org - Atari 7800 Development Wiki
Jump to: navigation, search
(Created page with "==6502 Beginner Tips== This entry assumes you're new but familiar with 6502 assembly. If that isn't true, you may wish to check out ===6502 if...then equivalents=== If yo...")
 
(16-bit increment without full 16-bit addition)
 
(8 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
==6502 Beginner Tips==
 
==6502 Beginner Tips==
  
This entry assumes you're new but familiar with 6502 assembly. If that isn't true, you may wish to check out  
+
This entry assumes you're new but generally familiar with 6502 assembly, and are just looking for some tips. If you need something more fundamental, check out the [https://skilldrick.github.io/easy6502/ "Easy 6502" tutorial by Nick Morgan].
  
  
Line 39: Line 39:
  
 
Similar tests can be made for X or Y instead of A, substituting CPX or CPY for CMP.
 
Similar tests can be made for X or Y instead of A, substituting CPX or CPY for CMP.
 +
 +
 +
===quickly testing bit 6 or 7 without affecting A, X, or Y===
 +
 +
The BIT $MEM instruction will put bit 7 of MEM in the N flag, and bit 6 of MEM in the V flag. This doesn't use/invalidate the registers.
  
  
Line 59: Line 64:
  
 
The alternative approach to above, is creating a look-up table for multiplication by the desired value. While this is much faster, it can use a lot of ROM. You'll need to figure out whether the trade-off is worthwhile in your application.
 
The alternative approach to above, is creating a look-up table for multiplication by the desired value. While this is much faster, it can use a lot of ROM. You'll need to figure out whether the trade-off is worthwhile in your application.
 +
  
 
===lookup tables instead of complex math===
 
===lookup tables instead of complex math===
  
 +
The 6502 isn't great at arbitrary multiplication, let alone more complex operations. The usual way around this is replacing the complicated operation using a look-up table in your code. Create the table using a program on a modern platform using a modern language, spreadsheet program, or calculator.
 +
 +
GetSquare
 +
  ;called with value to square in A
 +
  ;return value in A
 +
  TAY
 +
  LDA squaretable,y
 +
  RTS
 +
squaretable
 +
  .byte 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 169, 196, 225
 +
 +
 +
===2's complement representation===
  
 +
The 6502 uses [https://en.wikipedia.org/wiki/Two%27s_complement 2's Complement] representation for negative numbers. In a nutshell, this means that very large numbers can also be treated as negative, depending on the operation.
  
===2's complement===
+
For example, 5 + (-1) is 4. 5 + 255 is also 4, due to the byte overflowing. 255 is the 2's complement representation of -1.
negating numbers
+
 
dividing by 2 while preserving sign
+
To negate a number using 2's complement...
 +
  EOR #255
 +
  CLC
 +
  ADC #1
 +
 
 +
To divide a signed 7-bit value by 2...
 +
  CMP #$80
 +
  ROR
 +
 
 +
To multiply a signed 7-bit number by 2...
 +
  CLC
 +
  LDA number
 +
  ADC number
 +
  ; caution - if the value in "number" is 64 or more, you'll overflow the sign bit
  
 
===avoid double jsr===
 
===avoid double jsr===
Line 73: Line 106:
  
 
  MySubRoutine
 
  MySubRoutine
  LDA PlayerX
+
  LDA PlayerX
  JSR MultiplyBy2
+
  JSR MultiplyBy2
  RTS
+
  RTS
 
  MultiplyBy2
 
  MultiplyBy2
  ASL
+
  ASL
  RTS
+
  RTS
  
 
Instead you can save stack and cycle overhead by changing the first JSR into a JMP...
 
Instead you can save stack and cycle overhead by changing the first JSR into a JMP...
  
 
  MySubRoutine
 
  MySubRoutine
  LDA PlayerX
+
  LDA PlayerX
  JMP MultiplyBy2
+
  JMP MultiplyBy2
 
  MultiplyBy2
 
  MultiplyBy2
  ASL
+
  ASL
  RTS
+
  RTS
 +
 
 +
 
 +
===16-bit data tables===
 +
 
 +
store 16-bit data (including addresses) in 2 separate tables with high and low values, rather than 1 table with 16-bit words. This allows you to increment over the values with a singe register increment/decrement, and increases the maximum table size to 256 entries.
 +
 
 +
 
 +
===16-bit increment without full 16-bit addition===
  
 +
Instead of a 16-bit addition by 1, you can quickly increment 16-bit words like this...
  
===16-bit data===
+
  INC VariableLo
store in 2 tables
+
  BNE skipHiInc
 +
  INC VariableHi
 +
skipHiInc
  
===16-bit increment without 16-bit addition===
+
We check if the first INC causes the low byte to overflow and become zero again, and increment the high byte if that happens.

Latest revision as of 15:11, 8 March 2022

6502 Beginner Tips

This entry assumes you're new but generally familiar with 6502 assembly, and are just looking for some tips. If you need something more fundamental, check out the "Easy 6502" tutorial by Nick Morgan.


6502 if...then equivalents

If you're learning 6502 assembly with a background in a higher language, the following table presents typical logic comparisons in a more familiar format.

6502 if...then equivalents
test desired comparison branch
A = VALUE CMP #VALUE BEQ
A <> VALUE CMP #VALUE BNE
A > VALUE CMP #VALUE BEQ and then BCS
A >= VALUE CMP #VALUE BCS
A < VALUE CMP #VALUE BCC
A <= VALUE CMP #VALUE BEQ and then BCC
A = $ADDR CMP $ADDR BEQ
A <> $ADDR CMP $ADDR BNE
A > $ADDR CMP $ADDR BEQ and then BCS
A >= $ADDR CMP $ADDR BCS
A < $ADDR CMP $ADDR BCC
A <= $ADDR CMP $ADDR BEQ and then BCC

Similar tests can be made for X or Y instead of A, substituting CPX or CPY for CMP.


quickly testing bit 6 or 7 without affecting A, X, or Y

The BIT $MEM instruction will put bit 7 of MEM in the N flag, and bit 6 of MEM in the V flag. This doesn't use/invalidate the registers.


divide/multiply by powers of 2

On the 6502, the easiest way to multiply a value by 2 is to shift the bits of the value one place to the left. e.g. "ASL" or "ASL VALUE"

Similarly, you can divide by 2 by shifting the bits of the value one place to the right. e.g. "LSR" or "LSR VALUE"

Repeated shifts allow for operations with other powers of 2. e.g. 2 shifts for a divide/multiply by 4, 3 shifts for a divide/multiply by 8, etc.

Often you can design games to take advantage of powers of 2 divide/multiply, rather than using other values that are less easily manipulated by the 6502.


multiply by near powers of 2

In a pinch, you can multiply by 3 by performing a left shift (multiply by 2) followed by an addition of the original value. Perform another left shift for a multiply by 6.

Similarly you can multiply by 5 by performing two left shifts (multiply by 4) and an addition of the original value.

The alternative approach to above, is creating a look-up table for multiplication by the desired value. While this is much faster, it can use a lot of ROM. You'll need to figure out whether the trade-off is worthwhile in your application.


lookup tables instead of complex math

The 6502 isn't great at arbitrary multiplication, let alone more complex operations. The usual way around this is replacing the complicated operation using a look-up table in your code. Create the table using a program on a modern platform using a modern language, spreadsheet program, or calculator.

GetSquare
  ;called with value to square in A
  ;return value in A
  TAY
  LDA squaretable,y
  RTS
squaretable
  .byte 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 169, 196, 225


2's complement representation

The 6502 uses 2's Complement representation for negative numbers. In a nutshell, this means that very large numbers can also be treated as negative, depending on the operation.

For example, 5 + (-1) is 4. 5 + 255 is also 4, due to the byte overflowing. 255 is the 2's complement representation of -1.

To negate a number using 2's complement...

  EOR #255
  CLC
  ADC #1

To divide a signed 7-bit value by 2...

  CMP #$80
  ROR

To multiply a signed 7-bit number by 2...

  CLC
  LDA number
  ADC number
  ; caution - if the value in "number" is 64 or more, you'll overflow the sign bit

avoid double jsr

You should avoid double JSRs followed by RTSs, like the following...

MySubRoutine
  LDA PlayerX
  JSR MultiplyBy2
  RTS
MultiplyBy2
  ASL
  RTS

Instead you can save stack and cycle overhead by changing the first JSR into a JMP...

MySubRoutine
  LDA PlayerX
  JMP MultiplyBy2
MultiplyBy2
  ASL
  RTS


16-bit data tables

store 16-bit data (including addresses) in 2 separate tables with high and low values, rather than 1 table with 16-bit words. This allows you to increment over the values with a singe register increment/decrement, and increases the maximum table size to 256 entries.


16-bit increment without full 16-bit addition

Instead of a 16-bit addition by 1, you can quickly increment 16-bit words like this...

  INC VariableLo
  BNE skipHiInc
  INC VariableHi
skipHiInc

We check if the first INC causes the low byte to overflow and become zero again, and increment the high byte if that happens.