Apr 13, 2015

Arithmetic Operations in 8-bit ASM

This is mostly notes for my own sake, so I don't waste time trying to wrap my head around a piece of code, Google the operation I think is being undertaken, then discover I was way off base.

If you're not familiar with math operations in 8-bit and want to hack 8-bit games (or even 16-bit, as the rules are similar), then you may find this page useful. Some of these you may yourself already know, but if you're like me, you're learning everything from Ground Zero.

When you look through ASM, it's confusing enough already. When you stumble across something that is purely arithmetic and goes beyond basic 8-bit addition/subtraction, it can be daunting to make sense of what you see. It doesn't help that each programmer has his own style of coding the same operations. This reference article should help you at least recognize common arithmetic operations; your trusty old calculator and deductive reasoning will help with the rest.

As always, make sure your calculator is set to work in 32-bit HEX.


Adding
8bit  $01+$02=$03
  1. CLC ; always clear Carry
  2. LDA $01 
  3. ADC $02 
  4. STA $03
8+16bit $01+$03$02=$05$04

  1. CLC ; always clear Carry before smaller value
  2. LDA $01
  3. ADC $02
  4. STA $04
  5. LDA $03
  6. ADC #00
  7. STA $05
16bit  $02$01+$04$03=$06$05
  1. CLC ; always clear Carry
  2. LDA $01
  3. ADC $03
  4. STA $05
  5. LDA $02
  6. ADC $04
  7. STA $06

Subtraction
8bit  $01-$02=$03
  1. SEC ; always set Carry
  2. LDA $01
  3. SBC $02
  4. STA $03
Alternate 8bit (Adding -1)
  1. CLC ; always clear Carry for addition
  2. LDA $01
  3. ADC #FF
  4. STA $02
16-8bit  $02$01-$03=$05$04
  1. SEC ; always set Carry before larger value
  2. LDA $01
  3. SBC $03
  4. STA $04
  5. LDA $02
  6. SBC #00
  7. STA $05
Alternate 16bit (Adding -1)
  1. SEC ; always set Carry before larger value
  2. LDA $01
  3. SBC $03
  4. STA $04
  5. LDA $02
  6. ADC #FF
  7. STA $05

Multiplication
8bit*3  $01*3=$02
  1. LDA $01
  2. ASL
  3. CLC
  4. ADC $01
  5. STA $02
8bit*10  $01*10=$02
  1. ASL $01
  2. STA $02 ; save the value
  3. ASL $01
  4. ASL $01
  5. CLC
  6. ADC $02
  7. STA $02
16bit*2  $02$01*2=$02$01
  1. ASL $01
  2. ROL $02
8bit*N (where N is 8bit)
I have seen a couple variations on this, so I will just try to explain what I have seen happen. This is what I witnessed in Castlevania III:

You take your first number and save it as a 16bit value (so across two bytes). You save how many bits beyond the first byte you want to go (typically register X or Y). Save your low byte and clear the next two subsequent bytes so you're working with, up to 24 bits. Here's where it can be confusing - you shift and rotate the values right when multiplying. You would think left, but nope. If the Carry flag is set after shifting the lowest byte (i.e., the lowest byte was odd), you add the other number to your upper two bytes; either way, you then rotate the other two bytes and decrease the bit counter until it zeroes.

To illustrate, here's the CV3 code abridged $01*$04=$01$02
  1. LDY #08
  2. LDA #00
  3. STA $02
  4. STA $03
  5. STA $05
  6. LSR $01
  7. BCC >loop ; >enter
  8. CLC
  9. LDA $04
  10. ADC $02
  11. STA $02
  12. LDA $05
  13. ADC $03
  14. STA $03
  15. ROR $03 ; >loop
  16. ROR $02
  17. ROR $01
  18. DEY
  19. BNE >enter
  20. RTS


Division
Division is essentially the same as multiplication, except you switch ASL with LSR and ROL with ROR. You also start your operations with the higher byte.

Consider the 16bit*2 code. The lower byte $01 is shifted left and then the upper byte $02 is rotated left. Now look at the 8bit*N code. The upper byte is shifted (rotated in that situation) right and then the lower byte is rotated right.


Modulo
I have seen modulo handled in two different ways. I've even seen both inside Castlevania III. One method to calculate 8bit mod N is to subtract N from a value until the result is less than N. If all you care about is the modulo, this is the simplest route. Another method is to perform division (see above), which will usually result in one of the variables (or the accumulator) holding the remainder.

Further Reading
The following link has some articles on various arithmetic operations on 8bit machines. Currently, I will admit, it's beyond my tired brain. It's easier for me to see a code in a running game and figure out what the code does using hard numbers instead of ambiguous variables. If you're interested, by all means read through the articles. There's even one or two by Apple co-founder Steve Wozniak.
http://codebase64.org/doku.php?id=base:6502_6510_maths

    No comments:

    Post a Comment