Saturday, May 26, 2018

6502 Basic Branching

My 6502 emulation portion of my 2600 emulator continues with branching this week.  Check previous posts for context.


When a computer must decide between two courses of action, it needs to either continue running code where it is or change the location of memory where it will start running the code at the new instruction pointer (IP) address.  This was given the term branching. There are many branching commands that the 6502 contains so it makes sense to have a special handing function for dealing with it. First, we need to know if the branch is going to be taken, this is the check which is simply the result of checking to see if the flag used for determining if we should branch is in the appropriate state for taking the branch. The offset parameter is simply the amount to adjust the instruction pointer.

    fun processBranch(check:Boolean, offset:Int) {
        if (check) {
            ++state.tick
            val adjOff = if (offset > 127) -( (offset xor 255)+1) else offset
            val target = state.ipNext + adjOff
            pageBoundsCheck(state.ipNext, target)
            state.ipNext = target
        }
    }

The zero flag happens to be handy to use for looping if you are counting down as it is set when you hit zero. It is also handy if you are incrementing a multiple byte number as you know when the byte has wrapped so you know it is time to increment the next byte. BNE takes the branch whenever the last zero-flag modifying command has a non-zero result while BEQ branches when the last result set the zero flag. Counting forward will also work if you are using a compare instruction which sets the zero flag if the comparison matches but the compare instructions won’t be implemented for a while yet.

The high order bit (128) is the negative bit and is used when you are dealing with signed numbers. We will be covering signed numbers next time when we get into the mathematical operations of the 6502 (adding and subtracting). If you are dealing with signed numbers or numbers less than 128 then this is handy as you know that if the negative flag is set then the result of the last action resulted in a value less than zero while if it is not set then the result is greater than or equal to zero. This is commonly used in conjunction with the zero flag checks to give you less than, equal, or greater than.

; operation being checked for here
BMI lessThan
BEQ equals
; code for greater than here

BMI is used to see if the negative bit is set, while BPL is used to see if the negative bit is clear.


BEQBranch if EQual to zero
Address Mode
Decimal OPCode
Hexadecimal OpCode
Size
Cycles
Relative
240
$F0
2
2-4
Flags affected: None
Cycle Notes: If branching will take an additional cycle (3). If branch crosses page boundaries then an additional cycle will be added on top of the cycle for branching (4).
Usage: Used to see if the last operation resulted in a zero result which is a very common test in things such as while loops.
Test Code:
; BEQ test
LDY #1
LDX #1
BEQ done
INY        ; Make y 2 if reached which should be
DEX
BEQ done
DEY        ; Make y 1 (or 0) if reached which fails test!
done: BRK
; expect y = 2
Implementation:
processBranch(state.flags and ZERO_FLAG == ZERO_FLAG, mem.read(state.ip+1) )


BNEBranch if Not Equal to zero
Address Mode
Decimal OPCode
Hexadecimal OpCode
Size
Cycles
Relative
208
$D0
2
2-4
Flags affected: None
Cycle Notes: If branching will take an additional cycle (3). If branch crosses page boundaries then an additional cycle will be added on top of the cycle for branching (4).
Usage: Used to see if last result was not zero. Often used for looping as by going from high value down to low value you can get the zero flag set for free saving a comparison instruction and the time and memory used for that instruction.
Test Code:
; BNE test 5 + 5 using iny and looping
     LDX #5
     LDY #5
add: INY
     DEX
     BNE add
     BRK
     ; expect y=10

Implementation:
processBranch(state.flags and ZERO_FLAG != ZERO_FLAG, mem.read(state.ip+1) )


BMIBranch on Minus (negative) result
Address Mode
Decimal OPCode
Hexadecimal OpCode
Size
Cycles
Relative
48
$30
2
2-4
Flags affected: None
Cycle Notes: If branching will take an additional cycle (3). If branch crosses page boundaries then an additional cycle will be added on top of the cycle for branching (4).
Usage: Check to see if a number is negative (high order bit set). Useful for comparison operations as this gives you the equivalent of a register being less than a number (or alternatively if the number compared is greater than the register)
Test Code:
; BMI -> ABS(-16) the hard way
     LDX #$F0   ; two's complement value for -16
     LDY #0
add: INY
     INX
     BMI add         ; repeat while X is negative
     BRK
     ; expect y=16

Implementation:
processBranch(state.flags and NEGATIVE_FLAG == NEGATIVE_FLAG, mem.read(state.ip+1) )

 BPL   Branch on Result Plus
BPLBranch on result PLus (positive)
Address Mode
Decimal OPCode
Hexadecimal OpCode
Size
Cycles
Relative
16
$10
2
2-4
Flags affected: None
Cycle Notes: If branching will take an additional cycle (3). If branch crosses page boundaries then an additional cycle will be added on top of the cycle for branching (4).
Usage: Checking to see if a number is 0 or positive (high order bit not set). Useful for comparison operations as this gives you the equivalent of the register being greater than or equal to the tested value (or the tested value being less than or equal to the register)
Test Code:
; BPL - Count to 28 the hard way
     LDX #100
     LDY #0
count: INY
     INX
     BPL count  ; will count from 100 to 127, quit when 128 hit
     BRK
     ; exect y=28

Implementation:
processBranch(state.flags and NEGATIVE_FLAG != NEGATIVE_FLAG, mem.read(state.ip+1) )

No comments:

Post a Comment