Wednesday, March 28, 2018

The Carry Flag


With Easter coming up this weekend, I have posted my Easter Egg Generator on Spelchan.com. The instructions for the processor were implemented in groups related to how the function worked with the order partially dictated on requirements, so instructions that were essential for writing code with other instructions were implemented first. The order I cover the instructions over the next few months (there is a lot to cover) are the actual order the instructions were implemented into my emulator. All the instructions are covered in detail so this can also be considered a crash course on 6502 assembly language for those not already familiar with it.

The carry flag is used in mathematical operations which indicate that the last add operation has carried a bit to the next byte. It is also used in subtraction to indicate that the last operation has borrowed from the higher byte. Some shift operations will also use this to This allows you to chain together multiple bytes to have numbers that are larger than a single byte. I will have an article explaining multi-byte numbers once we get to the point where we are implementing the instructions that are used for that.



Some instructions will work slightly differently based on whether the carry flag is set or not. For ADC you want to clear the carry flag before proceeding, while SBC must set the carry flag before use and uses carry to indicate that a borrow is required (an underflow if carry is clear).

The state of the carry flag can be controlled using the SEC and CLC instructions which we will implement now. These, as well as the rest of the flag setting and getting instructions, are very simple to implement. Setting a flag is simply using the logical or operation to set the appropriate bit. Clearing is a bit more complicated as you create an inverse of the byte that has all bits set except the desired bit and then use logical and to turn only that bit off leaving the other bits in the flag register alone. Inverting bits can be done by using 0xFF and exclusive or to toggle the flag bit off.

CLCClear Carry Flag
Address Mode
Decimal OPCode
Hexadecimal OpCode
Size
Cycles
Implied
24
$18
1
2
Flags affected: C
Usage: This sets the carry flag. It is used before using ADC or ASL, LSR when shifting in a zero
Test Code:
     SEC
     CLC
     BRK
Implementation:
m->run {
     m.state.flags = m.state.flags and (255 xor CARRY_FLAG)
}


SECSet Carry Flag
Address Mode
Decimal OPCode
Hexadecimal OpCode
Size
Cycles
Implied
56
$38
1
2
Flags affected: C
Usage: Before subtraction or shifting when you want the carry flag set (1)
Test Code:
     CLC
     SEC
     BRK
Implementation:
m->run {
                m.state.flags = m.state.flags or CARRY_FLAG
}

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.

Wednesday, March 14, 2018

Macro Directives???

Before I get to the final(ish) article in the creation of the assembler, let's quickly take a look at the state of my emulator as a pie chart. Why a pie chart? In Canada it is PI Day as I post this. I have started some work on the TIA emulation but have a ways to go, but as there are a few months worth of articles on writing the 6502 emumation this is not a huge deal. Work is slower than I would like as I am getting way too many things on my plate again and things are probably going to get worse so I may switch to a fortnight or even monthly release schedule for this blog once the 6502 emulator section has been posted depending on how far ahead I am at that point.




At this point I have everything necessary to write the assembly language tests for implementing the 6502 emulator. My assembler is also in a good enough state that it would be adequate for the 2600 portions of my emulator as well as Coffee Quest 2600. The only features that I can think of that my assembler doesn’t support are multiple files and macros. As I am planning on having this on a web page which wouldn’t necessarily have access to a file system (there are workarounds) this is not going to happen within the assembler. While macros are not a requirement of the assembler they are handy to have as they can save some work.

A macro would need to be recorded using .MSTART and .MEND directives and would be inserted back into the assembly language with the .MACRO directive. To make macros more useful, parameters would be desired so I am reserving P0 through P9 as parameter variables for the macro so that when you issue a macro it would let you specify parameters which would be replaced in the code so you could have something like the following program:

.MSTART DELAY 255
     LDX #P0
delayLoop:
     DEX
     BNE delayLoop
.MEND

.MSTART LONGDELAY 2 255
     LDY #P0
longDelayLoop:
     .MACRO DELAY P1
     DEY
     BNE longDelayLoop
.MEND

.MACRO DELAY 1
.MACRO DELAY 3
.MACRO LONGDELAY 10 100

This demo clearly shows how macros can be convenient but also demonstrates a problem with just copying and inserting the code. The delayLoop will end up being created 3 times and the address linkage will be totally incorrect for two of the three instances. This means that the macro will need to be smart enough to be able to track which labels are internal to it and change link labels to point to the updated version. This can be done simply by having a separate class that manages macros which searches for label declarations and tracks them in a list. When executing the macro, it then looks for any reference to that label (both the original declaration and any link labels) and changes them adding a prefix.

The AssemblerMacro class is a bit too long to cover in full so lets just look at the relevant parts. As you can see, the code for adding a macro line also handles the detection of the .MEND directive. The idea then is that when a macro is started, all further token lines are sent to the macro being created until the end is detected.

fun addLine(line:ArrayList<AssemblerToken>):Boolean {
     if (line.size < 1)
           return false
     // TODO do we want to do a more thorough check here? Probably should allow anywhere on line?
     if ((line[0].type == AssemblerTokenTypes.DIRECTIVE) and (line[0].contents == "MEND")) {
           return true
     }
     if (line[0].type == AssemblerTokenTypes.LABEL_DECLARATION)
           labels.add(line[0].contents)
     macroLines.add(line)
     return false
}

Executing the macro simply searches through the tokens replacing the labels that are internal links or parameters with the appropriate label, as demonstrated.

val token:AssemblerToken = adjustedLine[cntrToken]
if (token.type == AssemblerTokenTypes.LABEL_DECLARATION)
     adjustedLine[cntrToken] = AssemblerToken(AssemblerTokenTypes.LABEL_DECLARATION,
                prefix + token.contents, token.num)
else if (token.type == AssemblerTokenTypes.LABEL_LINK) {
     if  (labels.contains(token.contents))
           adjustedLine[cntrToken] = AssemblerToken(AssemblerTokenTypes.LABEL_LINK,
                     prefix + token.contents, token.num)
     else if (paramNames.contains(token.contents))
           adjustedLine[cntrToken] = params[paramNames.indexOf(token.contents)]
}

The prefix string is simply a count of how many times the macro has been executed. Once the tokens have been adjusted so they have the correct labels and parameters we simply pass that array of tokens to the assembler’s parse method as if it were sent from the assembler after tokenizing.

.MSTART works by creating an instance of the AssemblerMacro class and passing in any default parameters that were set in the call. There is a macroInProgress variable which is a nullable variable so when it is not null we know a macro is being recorded and the parse method calls the add method of that instance to add any additional lines of tokens until the add method returns false at which point the macroInProgress variable is added to the list of macros and macroInProgress is set again to be null.

if (macroInProgress != null) {
     if (macroInProgress!!.addLine(tokens)) {
           macroList.put(macroInProgress!!.label, macroInProgress!!)
           macroInProgress = null
     }
     return arrayOf()
}

The .MACRO function simply looks to see if there is a macro using the indicated name. If there is then it copies over the rest of the tokens on the line as parameters to the macro then calls the macro’s execute method which “plays” back the recorded tokens using the indicated parameters.

There are all sorts of additional features that you may want to have with a macro system but for my emulator project, and probably even for my Coffee Quest 2600 project, this is more than enough of an assembler for my needs and it is built right into my emulator! If the assembler was going to be for production use, I would want to go through a few more hardening passes to make sure that it is robust and stupid mistakes won’t crash it. I would probably also want to add some advanced features such as mathematics support. I wouldn’t mind going over the code to do a bit of clean up and refactoring to make things smoother and more efficient. This is meant to be an internal tool for making the test code for the emulators (6502 and TIA) easier.

So finally, we can start on the actual 6502 emulation, which will be next week. As there are 56 instructions this will take a while to do. My plan is to break the instructions down into groups of related inststructions, cover the theory then implement as many instructions per week as space permits trying to keep each post down to a few pages.

Wednesday, March 7, 2018

Storage Directives


One of the things that writing an assembler clearly gets into a person’s head is how machine language is just bytes. Manipulating bytes is the job of machine language and to do that it needs bytes to work with. I was going to create three directives for setting up data for a program: .BYTE, .JUMPTABLE, .WORD but I realized that if I allow for labels to be used on a .WORD statement then there is no need to have a separate declarative for .JUMPTABLE so have decided to go with .BYTE and .WORD with a jumptable simply being a special case of the word directive.

The format for the .Byte directive is:
 .BYTE byte [byte…]

The bytes after the declaration should be numbers or variables. I was undecided if labels should also be supported, so opted to allow labels but only use the low order byte of the label. For the Atari 2600 this would actually be useful as all of it’s RAM is located in zero page.

The format for .WORD is the same but with words instead of bytes. The numbers are stored with a low byte followed by a high byte so will always take up two bytes. Labels are natural for this as are numbers and variables. The test for implementing this is as follows:

.BANK 0 $1000 1024
.EQU two 2
.EQU fivesix $0605
     LDA bytes  ; make sure labels to .byte data works
     LDA words  ; make sure labels to .word data works
bytes: .BYTE 1 two 3 4
words: .WORD fivesix $0807 $0A09
     BRK  ; make sure can continue assembly generation after data

The implementation for both directives are similar so I am only showing the word directive here. The idea is to simply loop over all the tokens after the declaration until we come to a token that is not a number or label obviously stopping when we run out of tokens.

"WORD" -> {
     var validEntry = true
     while ((indx < tokens.size) and (validEntry)) {

As explained last time, variables use labels but replace them immediately instead of linking them in later. This means that we need to replace labels with variable token if they are variables, which is just a map lookup.

           if (tokens[indx].type == AssemblerTokenTypes.LABEL_LINK) {
                val varToken = variableList.get(tokens[indx].contents)
                if (varToken != null)
                     tokens[indx] = varToken
           }

We take advantage of Kotlin to get a value for the word we are going to add. If it is a number (or variable that was converted to a number token above) then we just use that number. If it is a label, we need to add a label link to the appropriate address and use 0 as a placeholder until the half-pass of the assembly occurs. While the way Kotlin returns values for if and when statements looks strange at first, it is actually a really nice feature. There is an extra else statement that handles the cases where there is a symbol that is not used at which point we stop processing the directive.

           val wordToAdd = if (tokens[indx].type == AssemblerTokenTypes.NUMBER) tokens[indx].num
           else if (tokens[indx].type == AssemblerTokenTypes.LABEL_LINK) {
                addLabel(AssemblerLabel(tokens[indx].contents, AssemblerLabelTypes.ADDRESS, currentBank.curAddress, currentBank.number))
                0
           } else {
                validEntry = false
                0
           }

At this point we make sure we have a valid number to add to the generated machine language and if not do nothing for now. It might be an idea to add some type of warning here because it is bad form to have the data followed by something else. Converting the number to a high and low byte is something that has already been covered so should be a familiar procedure at this point.

           if (validEntry) {
                currentBank.writeNextByte(wordToAdd and 255)
                currentBank.writeNextByte((wordToAdd / 256) and 255)
                tokens.removeAt(indx)
           }
     }
}

And now we have data! If you wanted to use unsupported codes, this is how you would add them to your assembly source file, though our emulator probably won’t be supporting those.  We now have everything that we need our assembler to do, and then some! Macros, however, would be nice but are they worth the effort to add? Find out next time!