Date: 2019-03-15 23:33:31

CocoJEM Progress Report 2: Addressing Modes

CocoJEM progress report 2, brings us the letters L, D, and A.

LDA immediate addressing mode is now implemented. It was a little confusing wrapping my head around the addressing modes and getting the effective address calculated correctly. As such I moved that logic into its own method. I currently support immediate and direct:

public int getEffectiveAddress(AddressMode mode) {
    int effectiveAddr = 0;
    switch(mode) {
        case Immediate  : effectiveAddr = regPC.getValue(); break;
        case Direct     : effectiveAddr = memory[regDP.getValue()]*0x100+memory[regPC.getValue()]; break;
    }
    return effectiveAddr;
}

I haven't taken into consideration indexed addressing mode yet. LDA looks like this:

public void lda() {
    int operand = getOperand8bit(mnemonic.getMode());
    regD.setValue(operand,regD.getLSB());
    regCC.setZero(operand==0).setNegative((operand&0x80)==0x80).setOverflow(false);
    regPC.inc();
}
protected int getOperand8bit(AddressMode mode) {
    int operand = memory[getEffectiveAddress(mode)];
    return operand;
}

The last bit of code I'd like to share is how the CPU will read an instruction, figure out what it is, and calling the appropriate routine to do the processing.

public void exec(long now) {
    int opcode = memory[regPC.getValue()];
    regPC.inc();
    if(opcode == 0x10 || opcode == 0x11) {
        opcode = opcode*0x100+memory[regPC.getValue()];
        regPC.inc();
    }
    mnemonic = MnemonicEnum6809.lookupMnemonic(opcode);

    switch(mnemonic) {
        case ABX_I : abx(); break;
        case CLRA_I: clra(); break;
        case CLRB_I: clrb(); break;
        case LDA_M : lda(); break;
    }
}

exec() will be called every time the emulator goes through the main loop. We grab a byte of data from where the PC register points, checks to see if it is a multibyte instruction and grabs the next byte if needed. Then we lookup what the instruction is, and call the method.

Why do I look it up instead of just do a switch off the byte(s) we read? I think it's going to keep the code cleaner. MnemonicEnum6809.lookupMnemonic() returns an enum, and this enum contains more information then just the byte value you would get with a #define in C/C++. For example it has the cycle count and addressing mode of the instruction. If you look back at the lda() method, you'll notice that I don't even have to think about the addressing mode. I just do a getOperand8bit() call, and then just work with the operand data.

Very happy with my unit test coverage. I have tests for the CPU instructions, tests for calculating effective address based on the addressing mode, and tests to make sure the registers behave correctly. The CPU unit tests will come in handy in later development, as I will inevitably make a mistake and not set a flag or something. Running a unit test allows me to test EVERY instruction, every time I make a change to anything. It is also a double check that I understood the instruction correctly. So if I mess something up, I don't have to wait till I am running NitrOS9 and it freezes for some unknown reason.

I had to play around a little to figure out a good way to setup the CPU, memory, and regs, then have a good way to check the state after. I ended up with a test that looks like this:

@Test
public void ldaImmediate_8bit_NotNegative_Nonzero() {
    int[] mem = {MnemonicEnum6809.LDA_M.getOpcode(),0x01};
    //                     pc     x      y      u      s      dp   d      cc                EFHINZVC
    int[] regs          = {0x0000,0x0000,0x0000,0x0000,0x0000,0x00,0xAABB,Integer.parseInt("00001011",2)};
    Cpu6809 cpu = new Cpu6809(regs);
    cpu.setMemoryRange(0,mem);
    cpu.exec(System.nanoTime());
    //                     pc     x      y      u      s      dp   d      cc                EFHINZVC
    int[] regsResult    = {0x0002,0x0000,0x0000,0x0000,0x0000,0x00,0x01BB,Integer.parseInt("00000001",2)};
    verifyRegisters(cpu,regsResult);
}

I'm not sure if I'm happy with the way I check the CC reg yet, but with the comment line above it, it's pretty understandable.

Next up I plan to implement the other addressing modes, and the rest of the LD instructions. Then ST instructions. At that point I plan to integrate the CPU with the emulator, and make a tiny program that will load a register and store it to video memory. But let's not get too far ahead of ourself.

Thanks for reading. Hit that Like button if you had a fun read, or if not there is that dislike button too.



Copyright © 2019, Lee Patterson