// Big Mess o' Wires microcode source // // Each opcode consists of up to 16 phases. The microcode for a phase may depend on // the ALUFLAG input, in which two lines will be provided for that phase: one for // ALUFLAG == 0 and one for ALUFLAG == 1. An ALUFLAG of "*" means the same microcode // should be executed for that phase regardless of ALUFLAG. // HALT // Stops the machine. Cycles through all 16 phases until the phase counter wraps around. // Note that the PC will point to the next byte after the HALT instruction. opcode: FF *: *: *: *: *: *: *: *: *: *: *: *: *: *: *: *: // NOP // Do nothing. Proceed to next instruction. opcode: EA *: nextop // JMP absolute // Interpret the next two bytes of the program as the lo,hi bytes of the destination // address. opcode: 4C *: T <- MEM(PC); PC++ *: PCHI <- MEM(PC) *: PCLO <- T *: nextop // JMP indirect // Interpret the next two bytes of the program as the lo,hi bytes of a location in memory // that contains the destination address. opcode: 6C *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: PCLO <- MEM(AR); AR++ *: PCHI <- MEM(AR) *: nextop // LDA immediate // Load the Accumulator with the next byte of the program. opcode: A9 *: A <- MEM(PC); PC++ *: nextop // LDA absolute // Interpret the next two bytes of the program as the lo,hi bytes of a location in memory; // and load a byte from that location into the Accumulator. opcode: AD *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: A <- MEM(AR) *: nextop // LDA absolute,X // Interpret the next two bytes of the program as the lo,hi bytes of a location in memory; // and load a byte from the location obtained by adding X to that address into the Accumulator. opcode: BD *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: A <- MEM(AR) c=0: A <- MEM(AR) c=1: nextop *: nextop // LDX immediate // Load X with the next byte of the program. opcode: A2 *: X <- MEM(PC); PC++ *: nextop // LDX absolute // Interpret the next two bytes of the program as the lo,hi bytes of a location in memory; // and load a byte from that location into the Accumulator. opcode: AE *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: X <- MEM(AR) *: nextop // TAX // Transfer Accumulator to X, latch CCs opcode: AA *: X <- A; latchcc; nextop // TXA // Transfer X to Accumulator, latch CCs opcode: 8A *: A <- X; latchcc; nextop // PHA // Push Accumulator onto the stack opcode: 48 *: MEM(SP) <- A; SP-- *: nextop // PLA // Pull Accumulator from the stack opcode: 68 *: SP++ *: A <- MEM(SP) *: nextop // STA absolute // Interpret the next two bytes of the program as the lo,hi bytes of a location in memory; // and store the Accumulator at that location. opcode: 8D *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: MEM(AR) <- A *: nextop // STA absolute,X // Interpret the next two bytes of the program as the lo,hi bytes of a location in memory; // and store the Accumulator at the location obtained by adding X to that address. opcode: 9D *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: MEM(AR) <- A c=0: MEM(AR) <- A c=1: nextop *: nextop // STX absolute // Interpret the next two bytes of the program as the lo,hi bytes of a location in memory; // and store X at that location. opcode: 8E *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: MEM(AR) <- X *: nextop // JSR // Push return address onto the stack; then interpret the next two bytes of the program // as the lo;hi bytes of the destination address, and jump to the destination. // Note this actually pushes return address - 2 onto the stack. RTS must compensate. opcode: 20 *: T <- PCLO; SP-- *: MEM(SP) <- T; SP++ *: T <- PCHI *: MEM(SP) <- T; SP-- *: T <- MEM(PC); SP--; PC++ *: PCHI <- MEM(PC); PC++ *: PCLO <- T *: nextop // RTS // Return from subroutine, using the return address on the stack opcode: 60 *: SP++ *: PCLO <- MEM(SP); SP++ *: PCHI <- MEM(SP) *: PC++ *: PC++ *: nextop // INX // Increment X by one, latch CC's. opcode: E8 *: X <- X + 1; latchCC; nextop // DEX // Decrement X by one, latch CC's. opcode: CA *: X <- X - 1; latchCC; nextop // DEC // Decrement contents of absolute memory address opcode: CE *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- MEM(AR) *: T <- 0 - T; latchCC *: MEM(AR) <- 0 - T - 1 c=0: T <- 0 + 1; latchCC; nextop // invert carry, blecch c=1: T <- ALU(0,T,F,0,0); latchCC; nextop // ALU(left, right, function, carry_in, mode) // DEC // Decrement contents of absolute memory address, X-indexed opcode: DE *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: ARHI <- 0 + ARHI // nop *: T <- MEM(AR) *: T <- 0 - T; latchCC *: MEM(AR) <- 0 - T - 1 c=0: T <- 0 + 1; latchCC; nextop // invert carry, blecch c=1: T <- ALU(0,T,F,0,0); latchCC; nextop // ALU(left, right, function, carry_in, mode) // INC // Increment contents of absolute memory address opcode: EE *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- MEM(AR) *: T <- 0 + T + 1; latchCC *: MEM(AR) <- T *: nextop // INC // Increment contents of absolute memory address, X-indexed opcode: FE *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: ARHI <- 0 + ARHI // nop *: T <- MEM(AR) *: T <- 0 + T + 1; latchCC *: MEM(AR) <- T *: nextop // BEQ // Branch if result is zero. If Z bit of the condition codes is 0, then interpret the // next byte of the program as a signed offset from the next instruction's PC, and jump to that // address. opcode: F0 z=0: T <- X // brach taken z=1: pc++ // branch not taken z=0: X <- MEM(PC); PC++ z=1: nextop *: PCLO <- X + PCLO; latchCC // add delta to low byte of PC; latch carry //if no X7 available: // *: X <- X; latchCC //n=0: X <- 0 //n=1: X <- 0 - 1 //c=0: PCHI <- X + PCHI + 1 //c=1: PCHI <- X + PCHI c=0: PCHI <- X7 + PCHI + 1 // carry flag == 0 means there was a carry c=1: PCHI <- X7 + PCHI *: X <- T; nextop // BNE // Branch if result is non-zero. If Z bit of the condition codes is 1, then interpret the // next byte of the program as a signed offset from the next instruction's PC, and jump to that // address. opcode: D0 z=1: T <- X // brach taken z=0: pc++ // branch not taken z=1: X <- MEM(PC); PC++ z=0: nextop *: PCLO <- X + PCLO; latchCC // add delta to low byte of PC; latch carry c=0: PCHI <- X7 + PCHI + 1 // carry flag == 0 means there was a carry c=1: PCHI <- X7 + PCHI *: X <- T; nextop // BCS // Branch if carry set. If C bit of the condition codes is 0, then interpret the // next byte of the program as a signed offset from the next instruction's PC, and jump to that // address. opcode: B0 c=0: T <- X // brach taken c=1: pc++ // branch not taken c=0: X <- MEM(PC); PC++ c=1: nextop *: PCLO <- X + PCLO; latchCC // add delta to low byte of PC; latch carry c=0: PCHI <- X7 + PCHI + 1 // carry flag == 0 means there was a carry c=1: PCHI <- X7 + PCHI *: X <- T; nextop // BCC // Branch if carry clear. If C bit of the condition codes is 1, then interpret the // next byte of the program as a signed offset from the next instruction's PC, and jump to that // address. opcode: 90 c=1: T <- X // brach taken c=0: pc++ // branch not taken c=1: X <- MEM(PC); PC++ c=0: nextop *: PCLO <- X + PCLO; latchCC // add delta to low byte of PC; latch carry c=0: PCHI <- X7 + PCHI + 1 // carry flag == 0 means there was a carry c=1: PCHI <- X7 + PCHI *: X <- T; nextop // BVS // Branch if overflow set. If V bit of the condition codes is 1, then interpret the // next byte of the program as a signed offset from the next instruction's PC, and jump to that // address. opcode: 70 v=0: pc++ // branch not taken v=1: T <- X // brach taken v=0: nextop v=1: X <- MEM(PC); PC++ *: PCLO <- X + PCLO; latchCC // add delta to low byte of PC; latch carry c=0: PCHI <- X7 + PCHI + 1 // carry flag == 0 means there was a carry c=1: PCHI <- X7 + PCHI *: X <- T; nextop // BVC // Branch if overflow clear. If V bit of the condition codes is 0, then interpret the // next byte of the program as a signed offset from the next instruction's PC, and jump to that // address. opcode: 50 v=1: pc++ // branch not taken v=0: T <- X // brach taken v=1: nextop v=0: X <- MEM(PC); PC++ *: PCLO <- X + PCLO; latchCC // add delta to low byte of PC; latch carry c=0: PCHI <- X7 + PCHI + 1 // carry flag == 0 means there was a carry c=1: PCHI <- X7 + PCHI *: X <- T; nextop // BMI // Branch if minus. If N bit of the condition codes is 1, then interpret the // next byte of the program as a signed offset from the next instruction's PC, and jump to that // address. opcode: 30 n=0: pc++ // branch not taken n=1: T <- X // brach taken n=0: nextop n=1: X <- MEM(PC); PC++ *: PCLO <- X + PCLO; latchCC // add delta to low byte of PC; latch carry c=0: PCHI <- X7 + PCHI + 1 // carry flag == 0 means there was a carry c=1: PCHI <- X7 + PCHI *: X <- T; nextop // BPL // Branch if plus. If N bit of the condition codes is 0 and Z bit is 1, then interpret the // next byte of the program as a signed offset from the next instruction's PC, and jump to that // address. opcode: 10 n=1,z=0: pc++ // branch not taken n=1,z=1: pc++ n=0,z=0: pc++ n=0,z=1: T <- X // brach taken n=1,z=0: nextop n=1,z=1: nextop n=0,z=0: nextop n=0,z=1: X <- MEM(PC); PC++ *: PCLO <- X + PCLO; latchCC // add delta to low byte of PC; latch carry c=0: PCHI <- X7 + PCHI + 1 // carry flag == 0 means there was a carry c=1: PCHI <- X7 + PCHI *: X <- T; nextop // ASL Accumulator // Arithmetic shift left Accumulator one bit. Shifted-out high bit goes to carry flag, inverted. // C <- [76543210] <- 0 // Maybe this should shift the pre-existing carry bit into the low end, to facilitate multi-byte shifts? opcode: 0A *: A <- A + A; latchCC; nextop // ASL // Arithmetic shift left contents of an absolute memory address one bit. Shifted-out high bit goes to carry flag, inverted. // C <- [76543210] <- 0 // Maybe this should shift the pre-existing carry bit into the low end, to facilitate multi-byte shifts? opcode: 0E *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- A *: A <- MEM(AR) *: MEM(AR) <- A + A; latchCC *: A <- T; nextop // ASL // Arithmetic shift left contents of an absolute memory address, X-indexed, one bit. Shifted-out high bit goes to carry flag, inverted. // C <- [76543210] <- 0 // Maybe this should shift the pre-existing carry bit into the low end, to facilitate multi-byte shifts? opcode: 1E *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: ARHI <- 0 + ARHI // nop *: T <- A *: A <- MEM(AR) *: MEM(AR) <- A + A; latchCC *: A <- T; nextop // LSR Accumulator // Logical shift right Accumulator one bit. Shifted-out low bit goes to carry flag, inverted. // 0 -> [76543210] -> C // This is pretty slow. If there's space in ROM, maybe just implement this as a 256-entry lookup. // Maybe this should shift the pre-existing carry bit into the high end, to facilitate multi-byte shifts? opcode: 4A *: A <- A + A; latchCC // A BCDEFGH0 c=0: A <- A + A + 1; latchCC // B CDEFGH0A c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // C DEFGH0AB c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // D EFGH0ABC c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // E FGH0ABCD c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // F GH0ABCDE c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // G H0ABCDEF c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC; nextop // H 0ABCDEFG c=1: A <- A + A; latchCC; nextop // LSR // Logical shift right contents of an absolute memory address one bit. Shifted-out low bit goes to carry flag, inverted. // 0 -> [76543210] -> C // This is pretty slow. If there's space in ROM, maybe just implement this as a 256-entry lookup. // Maybe this should shift the pre-existing carry bit into the high end, to facilitate multi-byte shifts? opcode: 4E *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- A *: A <- MEM(AR) *: A <- A + A; latchCC // A BCDEFGH0 c=0: A <- A + A + 1; latchCC // B CDEFGH0A c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // C DEFGH0AB c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // D EFGH0ABC c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // E FGH0ABCD c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // F GH0ABCDE c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // G H0ABCDEF c=1: A <- A + A; latchCC c=0: MEM(AR) <- A + A + 1; latchCC // H 0ABCDEFG c=1: MEM(AR) <- A + A; latchCC *: A <- T; nextop // LSR // Logical shift right contents of an absolute memory address, X-indexed, one bit. Shifted-out low bit goes to carry flag, inverted. // 0 -> [76543210] -> C // This is pretty slow. If there's space in ROM, maybe just implement this as a 256-entry lookup. // Maybe this should shift the pre-existing carry bit into the high end, to facilitate multi-byte shifts? opcode: 5E *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: ARHI <- 0 + ARHI // nop *: T <- A *: A <- MEM(AR) *: A <- A + A; latchCC // A BCDEFGH0 c=0: A <- A + A + 1; latchCC // B CDEFGH0A c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // C DEFGH0AB c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // D EFGH0ABC c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // E FGH0ABCD c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // F GH0ABCDE c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // G H0ABCDEF c=1: A <- A + A; latchCC c=0: MEM(AR) <- A + A + 1; latchCC // H 0ABCDEFG c=1: MEM(AR) <- A + A; latchCC *: A <- T; nextop // ROL Accumulator // C <- [76543210] <- C opcode: 2A c=0: A <- A + A + 1; latchCC; nextop // /7 6543210C c=1: A <- A + A; latchCC; nextop // ROL contents of absolute memory address // C <- [76543210] <- C opcode: 2E *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- A *: A <- MEM(AR) c=0: MEM(AR) <- A + A + 1; latchCC // /7 6543210C c=1: MEM(AR) <- A + A; latchCC *: A <- T; nextop // ROL contents of absolute memory address, X-indexed // C <- [76543210] <- C // This is pretty slow, due to the need to save and restore the original carry flag. Maybe add a separate "last carry" flag? opcode: 3E *: T <- A c=0: A <- 0 // fill A with carry flag c=1: A <- 0 - 1 *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: ARHI <- 0 + ARHI // nop *: A <- MEM(AR); shiftCC // restore original carry flag into overflow flag v=0: MEM(AR) <- A + A + 1; latchCC // /7 6543210C v=1: MEM(AR) <- A + A; latchCC *: A <- T; nextop // ROR Accumulator // C -> [76543210] -> C // This is pretty slow. If there's space in ROM, maybe just implement this as a 256-entry lookup. // Maybe this should shift the pre-existing carry bit into the high end, to facilitate multi-byte shifts? opcode: 6A c=0: A <- A + A + 1; latchCC // /7 6543210/C c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /6 543210/C7 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /5 43210/C76 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /4 3210/C765 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /3 210/C7654 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /2 10/C76543 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /1 0/C765432 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC; nextop // /0 /C7654321 c=1: A <- A + A; latchCC; nextop // ROR contents of absolute memory address // C -> [76543210] -> C // This is pretty slow. If there's space in ROM, maybe just implement this as a 256-entry lookup. // Maybe this should shift the pre-existing carry bit into the high end, to facilitate multi-byte shifts? opcode: 6E *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- A *: A <- MEM(AR) c=0: A <- A + A + 1; latchCC // /7 6543210/C c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /6 543210/C7 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /5 43210/C76 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /4 3210/C765 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /3 210/C7654 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /2 10/C76543 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /1 0/C765432 c=1: A <- A + A; latchCC c=0: MEM(AR) <- A + A + 1; latchCC // /0 /C7654321 c=1: MEM(AR) <- A + A; latchCC *: A <- T; nextop // ROR contents of absolute memory address, X-indexed // C -> [76543210] -> C // This is pretty slow. If there's space in ROM, maybe just implement this as a 256-entry lookup. // Maybe this should shift the pre-existing carry bit into the high end, to facilitate multi-byte shifts? // This is extra slow, due to the need to save and restore the original carry flag. Maybe add a separate "last carry" flag? opcode: 7E *: T <- A c=0: A <- 0 // fill A with carry flag c=1: A <- 0 - 1 *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: ARHI <- 0 + ARHI // nop *: A <- MEM(AR); shiftCC // restore original carry flag into overflow flag v=0: A <- A + A + 1; latchCC // /7 6543210/C v=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /6 543210/C7 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /5 43210/C76 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /4 3210/C765 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /3 210/C7654 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /2 10/C76543 c=1: A <- A + A; latchCC c=0: A <- A + A + 1; latchCC // /1 0/C765432 c=1: A <- A + A; latchCC c=0: MEM(AR) <- A + A + 1; latchCC // /0 /C7654321 c=1: MEM(AR) <- A + A; latchCC *: A <- T; nextop // ASR Accumulator // Arithmetic shift right Accumulator one bit. Shifted-out low bit goes to carry flag, inverted. // 7 -> [76543210] -> C // This is pretty slow. If there's space in ROM, maybe just implement this as a 256-entry lookup. //opcode: ?? // *: T <- A + A; latchCC //c=0: A <- A + A + 1; latchCC // A BCDEFGHA //c=1: A <- A + A; latchCC //c=0: A <- A + A + 1; latchCC // B CDEFGHAA //c=1: A <- A + A; latchCC //c=0: A <- A + A + 1; latchCC // C DEFGHAAB //c=1: A <- A + A; latchCC //c=0: A <- A + A + 1; latchCC // D EFGHAABC //c=1: A <- A + A; latchCC //c=0: A <- A + A + 1; latchCC // E FGHAABCD //c=1: A <- A + A; latchCC //c=0: A <- A + A + 1; latchCC // F GHAABCDE //c=1: A <- A + A; latchCC //c=0: A <- A + A + 1; latchCC // G HAABCDEF //c=1: A <- A + A; latchCC //c=0: A <- A + A + 1; latchCC; nextop // H AABCDEFG //c=1: A <- A + A; latchCC; nextop // SPA // Swap the contents of the processor status register (condition codes) with the Accumulator. // Staus register format is reversed: 0000CNZV opcode: EF c=0: A <- A + A; shiftCC c=1: A <- A + A + 1; shiftCC c=0: A <- A + A; shiftCC c=1: A <- A + A + 1; shiftCC c=0: A <- A + A; shiftCC c=1: A <- A + A + 1; shiftCC c=0: A <- A + A; shiftCC; nextop c=1: A <- A + A + 1; shiftCC; nextop // SCC // Set all the condition codes to the logical TRUE state. opcode: DF *: T <- A *: A <- 0 + 1 // A = 00000001 *: A <- A + A // A = 00000010 *: A <- A + A + 1 // A - 00000101 *: A <- A + A; shiftCC *: A <- A + A; shiftCC *: A <- A + A; shiftCC *: A <- T; shiftCC; nextop // CCC // Set all the condition codes to the logical FALSE state. opcode: CF *: T <- A *: A <- 0 + 1 // A = 00000001 *: A <- A + A // A = 00000010 *: A <- A + A + 1 // A - 00000101 *: A <- A + A // A = 00001010 *: A <- A + A; shiftCC *: A <- A + A; shiftCC *: A <- A + A; shiftCC *: A <- T; shiftCC; nextop // PHP // Push the contents of the processor status register onto the stack. // Staus register format is reversed: 0000CNZV opcode: 08 *: T <- A c=0: A <- 0; shiftCC c=1: A <- 0 + 1; shiftCC c=0: A <- A + A; shiftCC c=1: A <- A + A + 1; shiftCC c=0: A <- A + A; shiftCC c=1: A <- A + A + 1; shiftCC c=0: A <- A + A; shiftCC c=1: A <- A + A + 1; shiftCC *: MEM(SP) <- A; SP-- *: A <- T; nextop // PLP // Pull the contents of the processor status register from the stack. // Staus register format is reversed: 0000CNZV opcode: 28 *: T <- A; SP++ *: A <- MEM(SP) *: A <- A + A; shiftCC *: A <- A + A; shiftCC *: A <- A + A; shiftCC *: A <- T; shiftCC; nextop // BRK // Disable interrupts, push A, push P, push PC, and jump to interrupt service routine // whose address is stored at $FFFE. Normally BRK is inserted into the instruction // stream by an IRQ. If encountered in the program data, the program will endlessly // execute the ISR, producing an effect similar to HALT. // // 6502's treats BRK differently if encountered in the program data. It sets the B flag, // so the ISR can test if the interrupt was caused by software or hardware. It also // pushes PC+2 on the stack, so the ISR will return to the next instruction+1, rather // than repeating the BRK. // // Maybe I should make my hardware interrupt handling opcode named something different, // with an opcode other than 00, and make BRK behave like the 6502. A software interrupt // might be useful for setting "breakpoints" that would invoke a simple monitor program. // Without a B bit, this would probably require a separate interrupt vector. // // Or maybe "software interrupts" should just be JSR $NNNN instead of BRK. // opcode: 00 *: MEM(SP) <- A; SP-- // push A c=0: A <- 0; shiftCC c=1: A <- 0 + 1; shiftCC c=0: A <- A + A; shiftCC c=1: A <- A + A + 1; shiftCC c=0: A <- A + A; shiftCC c=1: A <- A + A + 1; shiftCC c=0: A <- A + A; shiftCC c=1: A <- A + A + 1; shiftCC *: MEM(SP) <- A; SP-- // push P (in low nibble, high nibble is ignored) *: A <- PCLO; SP-- *: MEM(SP) <- A - 1; latchCC; SP++ *: A <- PCHI; disableInt c=0: MEM(SP) <- A; SP-- c=1: MEM(SP) <- A - 1; SP-- *: A <- 0 - 1 *: ARHI <- A // ARHI = $FF *: ARLO <- A - 1 // ARLO = $FE *: PCLO <- MEM(AR); AR++ *: PCHI <- MEM(AR) *: nextop // RTI // Enable interrupts, pop PC, pop P, pop A. opcode: 40 *: SP++ *: PCLO <- MEM(SP); SP++ *: PCHI <- MEM(SP); SP++ *: A <- MEM(SP); SP++ // pop P *: A <- A + A; shiftCC *: A <- A + A; shiftCC *: A <- A + A; shiftCC *: A <- A + A; shiftCC *: A <- MEM(SP); enableInt // pop A *: nextop // CMP // Compare immediate value with accumulator opcode: C9 *: T <- MEM(PC); PC++ *: T <- A - T; latchCC; nextop // CMP // Compare value at absolute address with accumulator opcode: CD *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- MEM(AR) *: T <- A - T; latchCC; nextop // CMP // Compare value at absolute address, X indexed with accumulator opcode: DD *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: T <- MEM(AR) c=0: T <- MEM(AR) c=1: T <- A - T; latchCC; nextop *: T <- A - T; latchCC; nextop // CPX // Compare immediate value with X opcode: E0 *: T <- MEM(PC); PC++ *: T <- X - T; latchCC; nextop // CLI // Clear interrupt disable (actually sets interrupt enable) opcode: 58 *: enableInt *: nextop // SEI // Set interrupt disable (actually clears interrupt enable) opcode: 78 *: disableInt *: nextop // ORA imm // Perform a bitwise OR of the accumulator with an immediate value, // latching the condition codes and storing the result in the accumulator. opcode: 09 *: T <- MEM(PC); PC++ *: A <- ALU(A,T,E,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) // ORA abs // Perform a bitwise OR of the accumulator with a byte at an absolute memory address, // latching the condition codes and storing the result in the accumulator. opcode: 0D *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- MEM(AR) *: A <- ALU(A,T,E,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) // ORA abs,X // Perform a bitwise OR of the accumulator with at an absolute memory address offset by X, // latching the condition codes and storing the result in the accumulator. opcode: 1D *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: T <- MEM(AR) c=0: T <- MEM(AR) c=1: A <- ALU(A,T,E,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) c=0: A <- ALU(A,T,E,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) c=1: nextop; // control will never get here // AND // Perform a bitwise AND of the accumulator with an immediate value, // latching the condition codes and storing the result in the accumulator. opcode: 29 *: T <- MEM(PC); PC++ *: A <- ALU(A,T,B,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) // AND abs // Perform a bitwise OR of the accumulator with a byte at an absolute memory address, // latching the condition codes and storing the result in the accumulator. opcode: 2D *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- MEM(AR) *: A <- ALU(A,T,B,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) // AND abs,X // Perform a bitwise OR of the accumulator with at an absolute memory address offset by X, // latching the condition codes and storing the result in the accumulator. opcode: 3D *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: T <- MEM(AR) c=0: T <- MEM(AR) c=1: A <- ALU(A,T,B,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) c=0: A <- ALU(A,T,B,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) c=1: nextop; // control will never get here // EOR // Perform a bitwise EXCLUSIVE OR of the accumulator with an immediate value, // latching the condition codes and storing the result in the accumulator. opcode: 49 *: T <- MEM(PC); PC++ *: A <- ALU(A,T,6,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) // EOR abs // Perform a bitwise OR of the accumulator with a byte at an absolute memory address, // latching the condition codes and storing the result in the accumulator. opcode: 4D *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- MEM(AR) *: A <- ALU(A,T,6,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) // EOR abs,X // Perform a bitwise OR of the accumulator with at an absolute memory address offset by X, // latching the condition codes and storing the result in the accumulator. opcode: 5D *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: T <- MEM(AR) c=0: T <- MEM(AR) c=1: A <- ALU(A,T,6,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) c=0: A <- ALU(A,T,6,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode) c=1: nextop; // control will never get here // ADC // Add an immediate value to the accumulator, including the current value of the carry flag. // A <- A + #imm + C opcode: 69 *: T <- MEM(PC); PC++ c=0: A <- A + T + 1; latchCC; nextop c=1: A <- A + T; latchCC; nextop // ADC // Add a byte at an absolute memory address to the accumulator, including the current value of the carry flag. // A <- A + mem + C opcode: 6D *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- MEM(AR) c=0: A <- A + T + 1; latchCC; nextop c=1: A <- A + T; latchCC; nextop // ADC // Add a byte at an absolute memory address offset by X to the accumulator, including the current value of the carry flag. // A <- A + mem,X + C // This is pretty slow, due to the need to save and restore the original carry flag. Maybe add a separate "last carry" flag? // That would cut the cycle count from 9 to 5/6. opcode: 7D *: MEM(SP) <- A // save A (SP is not adjusted) c=0: A <- 0 // fill A with carry flag c=1: A <- 0 - 1 *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: ARHI <- 0 + ARHI // nop *: T <- MEM(AR) *: A <- MEM(SP); shiftCC // restore A, shift original carry flag into overflow v=0: A <- A + T + 1; latchCC; nextop // this makes me want to puke v=1: A <- A + T; latchCC; nextop // SBC // Subtract an immediate value from the accumulator, including the current value of the carry flag. // A <- A - #imm - C opcode: E9 *: T <- MEM(PC); PC++ c=0: A <- A - T; latchCC; nextop c=1: A <- A - T - 1; latchCC; nextop // SBC // Subtract a value at an absolute address from the accumulator, including the current value of the carry flag. // A <- A - mem - C opcode: ED *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- MEM(AR) c=0: A <- A - T; latchCC; nextop c=1: A <- A - T - 1; latchCC; nextop // SBC // Subtract a value at an absolute address, X-indexed from the accumulator, including the current value of the carry flag. // A <- A - mem,x - C // This is pretty slow, due to the need to save and restore the original carry flag. Maybe add a separate "last carry" flag? // That would cut the cycle count from 9 to 5/6. opcode: FD *: MEM(SP) <- A // save A (SP is not adjusted) c=0: A <- 0 // fill A with carry flag c=1: A <- 0 - 1 *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: ARLO <- X + ARLO; latchCC c=0: ARHI <- 0 + ARHI + 1 c=1: ARHI <- 0 + ARHI // nop *: T <- MEM(AR) *: A <- MEM(SP); shiftCC // restore A, shift original carry flag into overflow v=0: A <- A - T; latchCC; nextop // this makes me want to puke v=1: A <- A - T - 1; latchCC; nextop // BIT // Perform a bitwise AND of the accumulator with an byte stored at an absolute address, // latching the condition codes. The accumulator is not modified. // Note this differs slighting from the 6502 BIT instruction. opcode: 2C *: ARLO <- MEM(PC); PC++ *: ARHI <- MEM(PC); PC++ *: T <- MEM(AR) *: T <- ALU(A,T,B,1,1); latchCC; nextop // ALU(left, right, function, carry_in, mode)