Wednesday, March 21, 2018

6502 Flags and Registers


Processors need to be able to manipulate things very quickly but he amount of space on a processor is limited, so memory is stored outside of the processor. To work with memory then, a processor needs to load it into its own memory, manipulate it, then store it back in the computer’s memory. In modern computers, memory is slow taking many cycles to get from RAM into the processor where it can be used which is why they have multiple levels of caches to speed things up. Back in the early days processors ran at near the same speed as memory so this was not as much of a problem. Still, the processor needs to hold some information to work with, which it stores in registers.

The number and type of registers that a processor has varies between models of processors. The 6502 has 3 main registers which are used by the programmer. It also has three special registers which are indirectly manipulated by the programmer. The following diagram shows the registers.


The three main registers are the accumulator, index register x (x), and index register y (y). The x and y registers can be used for storing temporary information but are generally used as indexes for several addressing modes. The accumulator is where the bulk of the work is done and is the only register that many math related commands will work with.

The special registers are where things get interesting. The stack index register is an index register like x and y but is used for holding the stack index. We will be covering the stack in a later section but essentially you add and remove things from the stack and the stack index controls where in stack memory that item will be written to or retrieved from. It can directly be changed with the TXS instruction and is indirectly changed with the PHA, PHP. PLA, and PLP instructions.

The IP register is a 16 bit register and holds the address of the current instruction that is being executed. Every time an instruction is executed it is updated to the next instruction, which can be changed through branching instructions.

The most interesting special register is the processor status register which holds the flags. This is an 8 bit register but there are only 6 flags used by programmers. One of the additional bits is always 1 and the other bit is for the break instruction. These flags are Negative, Overflow, Decimal, Interrupt, Zero and Carry. The purpose of each of these flags will be discussed in the section that is related to the commands that implement them. The flags are set up as constants within the a2600Dragons.m6502 package as follows:

const val CARRY_FLAG = 1
const val ZERO_FLAG = 2
const val INTERRUPT_FLAG = 4
const val DECIMAL_FLAG = 8
const val BREAK_FLAG = 16
const val OVERFLOW_FLAG = 64
const val NEGATIVE_FLAG = 128

While I could have had the processor state right within the M6502 class, I opted to keep the processor state outside of the processor class to make it easy to make snapshots of the processor state for the eventual debugger that I will be developing as well as for the testing routines that I will be writing. Here is the processor state class.

data class ProcessorState(var acc:Int, var x:Int, var y:Int, var ip:Int, var flags:Int, var sp:Int, var tick:Long) {
    var ipNext = ip

    fun checkState(regmem:String, expected:Int, mem:MemoryManager? = null):Boolean {
        var reg = regmem.toUpperCase()
        var addr = 0
        if (reg.startsWith("M")) {
            addr = reg.substring(1).toInt(16)
            reg = "M"
        }

        return when (reg) {
            "A", "ACC" -> acc == expected
            "X" -> x == expected
            "Y" -> y == expected
            "IP" -> ip == expected
            "FLAGS" -> flags == expected
            "C" -> ((flags and CARRY_FLAG) > 0) == (expected > 0)
            "Z" -> ((flags and ZERO_FLAG) > 0) == (expected > 0)
            "I"-> ((flags and INTERRUPT_FLAG) > 0) == (expected > 0)
            "D"-> ((flags and DECIMAL_FLAG) > 0) == (expected > 0)
            "B"-> ((flags and BREAK_FLAG) > 0) == (expected > 0)
            "V"-> ((flags and OVERFLOW_FLAG) > 0) == (expected > 0)
            "N" -> ((flags and NEGATIVE_FLAG) > 0) == (expected > 0)
            "M" -> if (mem != null) mem.read(addr) == expected else false
            else -> {
                println("Error: unknown register or memory request $regmem.")
                false
            }
        }
    }
}

Note the checkState function which is used to quickly check that a register, flag, or memory address is a specific value.  This is used in testing where I simply have a bit of test code to test the instruction followed by the list of expected valued for key registers and memory locations. This makes writing small snippets of test code to test each of the instructions much easier.

No comments:

Post a Comment