The 6502 rotation operations
assume a byte’s highest bit is leftmost and lowest bit is rightmost. The ASL
command shifts the bits to the left. Shifting simply moves what value was in
the highest bit into the carry flag, then moves the bits to the right by 1 and
places a 0 in the lowest bit. In other words, 10101010 becomes 01010101 with a
carry of 1.
Related to the ASL
command is the ROL command which rotates the bits to the left. Rotating is the
same as shifting with the exception that the value in the carry flag is shifted
into the high bit. This effectively means that if you rotate a byte 9 times (8
bits plus 1 carry bit) you will end up with the number that you started with.
The following diagram illustrates both of these instructions
This is all
fascinating, but why would you ever want to do this? The two most common
reasons is to reposition bits if you have bitpacked values and for
multiplying.
As mentioned earlier,
due to low amount of memory early machines had, every bit counts. Lets say you
wanted a playing card to be represented in a byte. You may have a bit to
representing if the card was revealed, 4 bits to hold the face value, and 2
bits to hold the suit. This would give us a packed byte in the format 0RFFFFSS.
Here is how you would pack the 3 bytes into a single byte.
LDA cardShowing
ASL
ASL
ASL
ASL
ORA cardFace
ASL
ASL
ORA cardSuit
The 6502 does not
have any multiplication or division instructions so those operations need to be
done manually. This is where rotation really comes in handy. Shifting left is
the equivalent to multiplying by 2. Shifting twice is the same as multiplying by
4, and so on for the powers of 2. Combining the shifting multiplications with
additions lets you multiply by any value. For instance, multiplying by 10 can
be done by multiplying by 8 then adding to that value the original value
multiplied by 2.
Multibyte shifting
or multiplication is done as follows:
LDA lowByte
ASL
STA lowByte
LDA highByte
ROL
STA highByte
The idea here is that
the ASL puts the high bit into the carry flag and the ROL uses the carry from
the previous byte.
ROL – ROtate Left by one bit
Address Mode

Decimal OPCode

Hexadecimal OpCode

Size

Cycles

Accumulator

42

$2A

1

2

Zero Page

38

$26

2

5

Zero Page,X

54

$36

2

6

Absolute

46

$2E

3

6

Absolute,X

62

$3E

3

7

Flags affected: CNZ
Usage: Rotates bits left using the carry bit
to fill in the missing bit and putting the sign bit into the carry flag. Used
primarily for multibyte multiplication by powers of 2 and reading serial data
though can be used to reposition bits.
Test Code:
; ROL accumulator
CLC
LDA
#128
ROL
A
BRK
;
expect A=0, Z=1, C=1, N=0
; ROL with memory
LDX
#1
ROL
254
ROL
254,X
ROL
256
ROL
256,X
BRK
.ORG 254
.BYTE 128 127 $77 $77
;expect
MFE=0 MFF=255 M100=$EE M101=$EE N=1, Z=0, C=0
Implementation:
// Accumulator
state.acc =
performLeftBitShift(state.acc, true)
// Zero Page
val address =
mem.read(state.ip+1)
mem.write(address,
performLeftBitShift(mem.read(address), true))
// Zero Page, X
val address =
mem.read(state.ip+1)+state.x
mem.write(address,
performLeftBitShift(mem.read(address), true))
// Absolute
val address =
findAbsoluteAddress(state.ip)
mem.write(address,
performLeftBitShift(mem.read(address), true))
// Absolute, X
val address =
findAbsoluteAddress(state.ip)+state.x
mem.write(address,
performLeftBitShift(mem.read(address), true))
ASL – Arithmetic Shift Left by one
bit
Address Mode

Decimal OPCode

Hexadecimal OpCode

Size

Cycles

Accumulator

10

$0A

1

2

Zero Page

6

$06

2

5

Zero Page,X

22

$16

2

6

Absolute

14

$0E

3

6

Absolute,X

30

$1E

3

7

Flags affected: CNZ
Usage: Rotates bits left using the carry bit
to fill in the missing bit and putting the sign bit into the carry flag. Used
primarily for single byte multiplication by powers of 2 and reading serial data
though can be used to reposition bits.
Test Code:
; ASL accumulator
LDA
#128
SEC
ASL
A
BRK
;
expect A=0, Z=1, C=1, N=0
; ASL with memory
LDX
#1
ASL
254
ASL
254,X
ASL
256
ASL
256,X
BRK
.ORG 254
.BYTE 128 127 $F7 $77
;expect
MFE=0 MFF=254 M100=$EE M101=$EE N=1, Z=0, C=0
Implementation:
// Accumulator
state.acc =
performLeftBitShift(state.acc, false)
// Zero Page
val address =
mem.read(state.ip+1)
mem.write(address,
performLeftBitShift(mem.read(address), false))
// Zero Page, X
val address =
mem.read(state.ip+1)+state.x
mem.write(address,
performLeftBitShift(mem.read(address), false))
// Absolute
val address =
findAbsoluteAddress(state.ip)
mem.write(address,
performLeftBitShift(mem.read(address), false))
// Absolute, X
val address =
findAbsoluteAddress(state.ip)+state.x
mem.write(address,
performLeftBitShift(mem.read(address), false))
No comments:
Post a Comment