// problems: // // LDS indicator??? // cpu name is m6809 // // references: // // [1] "MC6809/MC6809E Microprocessor Programming Manual" Motorola 1981 // [2] "HD63B09EP Technical Reference Guide" by Chet Simpson, additions by Alan DeKok cpu m6809 { reg { // clock clock T; // 8-bit A and B accumulators u8 A,B; // 16-bit D is A and B combined alias u16 D = A:B; // 16-bit X and Y index registers u16 X,Y; // 16-bit S and U stack pointers u16 S,U; // 8-bit direct page register u8 DP; // 16-bit program counter u16 PC; // 8-bit condition code register u8 CC { fields { u1 C, // carry = 1 u1 V, // overflow u1 Z, // zero u1 N, // negative u1 I, // inhibit IRQ u1 H, // half carry u1 F, // inhibit FIRQ u1 E // "entire register set pushed" flag = 128 } // check for IRQ just after changing these flags trap I(u1 rv) { if (rv && !I) call check_irq(); } trap F(u1 rv) { if (rv && !F) call check_irq(); } // predicates on CC flags predicate { RN = 0; // B + RN = BRN LS = Z | C; HI = !LS; CC = !C; CS = C; NE = !Z; EQ = Z; VC = !V; VS = V; PL = !N; MI = N; LT = N ^ V; GE = !LT; LE = (N ^ V) | Z; GT = !LE; RA = 1; // B + RA = BRA } // evaluators which work off the return-value alone Z(u8 rv) { return (rv == 0); } Z(u16 rv) { return (rv == 0); } N(u8 rv) { return rv[7]; } N(u16 rv) { return rv[15]; } C(u9 rv) { return rv[8]; } C(u17 rv) { return rv[16]; } // evaluator helpers for V and H // Vadd: set overflow according to result and arguments u1 Vadd(u8 rv, u8 a, u8 b) { return (rv[7] ^ a[7]) & (rv[7] ^ b[7]); } // Vsub: Vadd(rv,a,-b) works but this is faster u1 Vsub(u8 rv, u8 a, u8 b) { return (rv[7] ^ a[7]) & (a[7] ^ b[7]); } // H: generate half-carry u1 H(u8 rv, u8 a, u8 b) { return rv[4] ^ a[4] ^ b[4]; } } } // busses connect to the outside world bus { // 8-bit bus with 16 address lines 0:15 u8 bus1[0:15] master { // big-endian, with wrap at FFFF u16 bus1[N] = bus1[N]:bus1[N+1]; // bus semantics u8 bus1(u16 addr) { u8 val = read_bus1(addr); return val; } u8 bus1(u16 addr, u8 val) { write_bus1(addr, val); } } // active low strobes strobe u1 sIRQ = 1; strobe u1 sFIRQ = 1; strobe u1 sNMI = 1; // active high strobe strobe u1 sRESET = 0; trap sRESET { RESET(); } trap sIRQ { IRQ(); } trap sFIRQ { FIRQ(); } trap sNMI { NMI(); } } // vector table: constants VRSVD = 0xFFF0; VSWI3 = 0xFFF2; VSWI2 = 0xFFF4; VFIRQ = 0xFFF6; VIRQ = 0xFFF8; VSWI = 0xFFFA; VNMI = 0xFFFC; VRESET = 0xFFFE; operation : CC { u8 NEG(u8 a) : NZCV = * { u8 r = -a; V = Vsub(r,0,a); C = !r; return r; } u8 COM(u8 a) : NZ = *, V = 0, C = 1 { return ~a; } u8 LSR(u8 a) : ZC = *, N = 0 { C = a[0]; return 0:a[7:1]; } u8 ROR(u8 a) : NZC = * { u8 r = C:a[7:1]; C = a[0]; return r; } u8 ASR(u8 a) : NZC = * { C = a[0]; return 1:a[7:1]; } u8 ROL(u8 a) : NZCV = * { u8 r = a[6:0]:C; C = a[7]; V = Vadd(r,a,a); return r; } u8 DEC(u8 a) : NZV = * { u8 r = a - 1; V = Vsub(r,a,1); return r; } u8 INC(u8 a) : NZV = * { u8 r = a + 1; V = Vadd(r,a,1); return r; } void TST(u8 a) : NZ = *, V = 0 { return a; } u8 CLR(u8 a) : NZC = 0, Z = 1 { return 0; } // accurate since "EA is read during this operation" [1] u8 SUB(u8 a, u8 b) : NZCV = * { u9 r = a - b; V = Vsub(r,a,b); return r; } u8 CMP(u8 a, u8 b) : NZCV = * { u9 r = a - b; V = Vsub(r,a,b); return r; } u8 SBC(u8 a, u8 b) : NZCV = * { u9 r = a - b - C; V = Vsub(r,a,b); return r; } u8 ADD(u8 a, u8 b) : NZCVH = * { u9 r = a + b; V = Vadd(r,a,b); H = Hadd(r,a,b); return r; } u8 ADC(u8 a, u8 b) : NZCVH = * { u9 r = a + b + C; V = Vadd(r,a,b); H = Hadd(r,a,b); return r; } u16 SUB(u16 a, u16 b) : NZCV = * { u17 r = a - b; V = Vsub(r,a,b); return r; } u16 CMP(u16 a, u16 b) : NZCV = * { u17 r = a - b; V = Vsub(r,a,b); return r; } u16 SBC(u16 a, u16 b) : NZCV = * { u17 r = a - b - C; V = Vsub(r,a,b); return r; } u16 ADD(u16 a, u16 b) : NZCVH = * { u17 r = a + b; V = Vadd(r,a,b); H = Hadd(r,a,b); return r; } u16 ADC(u16 a, u16 b) : NZCVH = * { u17 r = a + b + C; V = Vadd(r,a,b); H = Hadd(r,a,b); return r; } void MUL() : ZC = * { D = A * B; C = D[7]; Z(D); } void SEX() : NZ = *, V = 0 { N(B); A = N ? 0xFF : 0x00; Z(B); } u8 AND(u8 a, u8 b) : NZ = *, V = 0 { return a & b; } u8 BIT(u8 a, u8 b) : NZ = *, V = 0 { u8 r = a & b; N(r); Z(r); return a; } u8 EOR(u8 a, u8 b) : NZ = *, V = 0 { return a ^ b; } u8 OR(u8 a, u8 b) : NZ = *, V = 0 { return a | b; } u16 AND(u16 a, u16 b) : NZ = *, V = 0 { return a & b; } u16 BIT(u16 a, u16 b) : NZ = *, V = 0 { u16 r = a & b; N(r); Z(r); return a; } u16 EOR(u16 a, u16 b) : NZ = *, V = 0 { return a ^ b; } u16 OR(u16 a, u16 b) : NZ = *, V = 0 { return a | b; } u8 LD(u8 a, u8 b) : NZ = *, V = 0 { return a; } u8 ST(u8 a, u8& b) : NZ = *, V = 0 { b = a; return a; } u16 LD(u16 a, u16 b) : NZ = *, V = 0 { return a; } u16 ST(u16 a, u16& b) : NZ = *, V = 0 { b = a; return a; } } operation { void JMP(bus1& a) { PC = &a; } void JSR(bus1& a) { PSHS(PC); PC = &a; } void RTS() { PULS(PC); } alias BRA = JMP; alias LBRA = JMP; alias BSR = JSR; alias LBSR = JSR; void NOP() { } void SYNC() { call sync(); } void ABX() { X += 0:B; } void DAA() { assert(0); } // complex! void EXG(reg a, reg b) { if ((a == ?) || (b == ?)) { if (b != ?) b = 0xFFFF; // [2] says it's FFFF if (a != ?) a = 0xFFFF; } else if (sizeof(a) == sizeof(b)) { ubig tmp = a; a = b; b = tmp; } else { a = 0xFFFF; b = 0xFFFF; } } void TFR(reg a, reg b) { if (a == ?) return; if ((b == ?) || (sizeof(a) != sizeof(b)) { a = 0xFFFF; } a = b; } void LEAX(bus1& a) : Z = * { X = &a; Z(X); } void LEAY(bus1& a) : Z = * { Y = &a; Z(Y); } void LEAS(bus1& a) { S = &a; call lds(); } void LEAU(bus1& a) { U = &a; } // PSH1 and PUL1 for 8- and 16-bit operands // flags are not affected, so we don't return a value // instead we take a reference argument // bus1[u16] uses the u16 accessor for bus1 void PSH(reg R, u8 a) { bus1[--R] = a; T[s]++; } void PSH(reg R, u16 a) { R -= 2; bus1[u16][R] = a; T[s] += 2; } void PSHS(reg a) { PSH(S, a); } void PUL(reg R, u8& a) { a = bus1[R++]; T[s]++; } void PUL(reg R, u16& a) { a = bus1[u16][R]; R += 2; T[s] += 2; } void PULS(reg a) { PUL(S, a); } void PSHSALL() { push(S, 0xFF); } void PULSALL() { pull(S, 0xFF); } // return from interrupt // pull CC, if E set pull everything but PC // finally pull PC void RTI() { if (E) { PULSALL(); } else { PULS(CC); PULS(PC); } } // CWAI // set E so RTI works, then stack everything // and call irq_state with argument CWAI to tell // it what's happening void CWAI(u8 op) { CC &= op; E = 1; PSHSALL; T += 8; call irq_state(CWAI); } void SWI() { E = 1; PSHSALL; T += 7; I = F = 1; PC = bus1[u16][VSWI]; } void SWIx(u16 vector) { E = 1; PSHSALL; T += 8; PC = bus1[u16][vector]; } void RESET() { I = F = 1; DP = 0; PC = bus1[u16][VRESET]; } void IRQ() { E = 1; PSHSALL; I = 1; PC = bus1[u16][VIRQ]; } void FIRQ() { E = 0; PSHS(PC); PSHS(CC); I = F = 1; PC = bus1[u16][VFIRQ]; } void NMI() { E = 1; PSHSALL; I = F = 1; PC = bus1[u16][VNMI]; } static reg stack[S][8] = { CC,A,B,DP,X,Y,U,PC }; static reg stack[U][8] = { CC,A,B,DP,X,Y,S,PC }; void push(reg R, u8 r) { for (ii = 7 to 0) { if (r[ii]) PSH(R, stack[R][ii]); } } string dasmstack(reg R, u8 r) { string res; for (ii = 0 to 7) { if (r[ii]) res += stack[R][ii] + ","; } res -= ","; return res; } void pull(reg R, u8 r) { for (ii = 0 to 7) { if (r[ii]) PUL(R, stack[R][ii]); } } } decoder { direct(func op, reg r = ?) { u8 DI = bus1[PC++]; u16 EA = DP:DI; void op(u8&) { op(bus1[EA]); } u8 op(u8&) { bus1[EA] = op(bus1[EA]); } u8 op(reg,u8&) { r = op(r,bus1[EA]); } u16 op(reg,u16&) { r = op(r,bus1[u16][EA]); } dasm("<$" + DI); } implicit(func op, reg r) { void op(u8&) { op(r); } u8 op(u8&) { r = op(r); } } regpair(void op(reg a, reg b)) { static reg list[0:3] = { D,X,Y,U,S,PC,?,?,A,B,CC,DP,?,?,?,? }; u8 IB = bus1[PC++]; reg a = list[IB[3:0]]; reg b = list[IB[7:4]]; op(a,b); dasm(a + "," + b); } extended(func op, reg r = ?) { u16 EA = bus1[u16][PC]; PC += 2; void op(u8&) { op(bus1[EA]); } u8 op(u8&) { bus1[EA] = op(bus1[EA]); } u8 op(reg,u8&) { r = op(r,bus1[EA]); } u16 op(reg,u16&) { r = op(r,bus1[u16][EA]); } dasm("$" + EA); } indexed(func op, reg r = ?) { u8 IB = bus1[PC++]; static reg list[0:1] = { X,Y,U,S }; u16 EA; reg R = list[IB[6:5]]; string d; if (!IB[7]) { // 0RRnnnnn = 5-bit offset EA = R + s16(IB[4:0]); d = s16(IB[4:0]) + "," + R; T++; } else { switch (IB[3:0]) { case 0b0000: // ,R+ EA = R++; d = "," + R + "+"; T += 2; break; case 0b0001: // ,R++ EA = R; R += 2; d = "," + R + "++"; T += 3; break; case 0b0010: // ,-R EA = --R; d = ",-" + R; T += 2; break; case 0b0011: // ,--R R -= 2; EA = R; d = ",--" + R; T += 3; break; case 0b0100: // ,R EA = R; d = "," + R; break; case 0b0101: // B,R EA = R + s16(B); d = "B," + R; T++; break; case 0b0110: // A,R EA = R + s16(A); d = "A," + R; T++; break; case 0b0111: // undefined undefined(); break; case 0b1000: // 8-bit,R s8 offset = s8(bus1[PC++]); EA = R + offset; d = offset + "," + R; T += 1; break; case 0b1001: // 16-bit,R s16 offset = s16(bus1[u16][PC++]); EA = R + offset; d = offset + "," + R; T += 4; break; case 0b1010: // undefined undefined(); break; case 0b1011: // D,R EA = R + D; d = "D," + R; T += 4; break; case 0b1100: // 8-bit PCR EA = PC + s8(bus1[PC++]); d = "$" + EA + ",PCR"; T++; break; case 0b1101: // 16-bit PCR EA = PC + bus1[u16][PC++]; d = "$" + EA + ",PCR"; T += 5; break; case 0b1110: // undefined undefined(); break; case 0b1111: // extended EA = bus1[u16][PC]; PC += 2; d = "$" + EA; T += 2; break; } // check for indirection if (IB[4]) { EA = bus1[u16][EA]; T += 3; d = "[" + d + "]" } } dasm(d); void op(u8&) { op(bus1[EA]); } u8 op(u8&) { bus1[EA] = op(bus1[EA]); } u8 op(reg,u8&) { r = op(r,bus1[EA]); } u16 op(reg,u16&) { r = op(r,bus1[u16][EA]); } } imm8(u8 op(reg,u8), reg r) { u8 IB = bus1[PC++]; r = op(r, IB); dasm("#$" + IB); } imm16(u16 op(reg,u16), reg r) { u16 IW = bus1[u16][PC]; PC += 2; r = op(r, IW); dasm("#$" + IW); } stack(void op(reg,u8), reg r) { u8 IB = bus1[PC++]; op(r, IB); dasm(dasmstack(r, IB)); } lbra(void op(bus1&), predicate pp = RA) { u16 offset = bus1[u16][PC]; PC += 2; u16 EA = PC + offset; T += 5; if ((pp != RA) && (pp != RN) && pp) T++; if (pp) op(bus1[EA]); dasm("$" + EA); } bra(void op(bus1&), predicate pp = RA) { s8 offset = s8(bus1[PC++]); u16 EA = PC + offset; if (pp) op(bus1[EA]); dasm("$" + EA); } } hex table op8(func op, clock dclk) : 00 { 00 NEG 0 03 COM 0 04 LSR 0 06 ROR 0 07 ASR 0 08 ASL 0 09 ROL 0 0A DEC 0 0C INC 0 0D TST 0 0E JMP -3 0F CLR 0 opcode([00]) = { direct(op); dT = 6 + dclk; dasm(op); } } hex table bra(predicate pp) : 20 { 20 RA 21 RN 22 HI 23 LS 24 CC 25 CS 26 NE 27 EQ 28 VC 29 VS 2A PL 2B MI 2C GE 2D LT 2E GT 2F LE opcode(10,[20]) = { lbra(LBRA, pp); dasm("LB" + pp); } opcode([20]) = { bra(BRA, pp); dasm("B" + pp); dT = 3; } } hex table op8x8(func op) : 80 { 80 SUB 81 CMP 82 SBC 84 AND 85 BIT 86 LD 87 ST 88 EOR 89 ADC 8A OR 8B ADD } hex table op16x16(func op, reg r, clock clks) : 80 { 83 SUB D 4 [10] 83 CMP D 5 [11] 83 CMP U 5 8C CMP X 4 [10] 8C CMP Y 5 [11] 8C CMP S 5 8D JSR ?? 5 8E LD X 3 [10] 8E LD Y 4 8F ST X 3 [10] 8F ST Y 4 C3 ADD D 4 CC LD D 3 CD ST D 3 CE LD U 3 [10] CE LD S 4 CF ST U 3 [10] CF ST S 4 } hex opcode { [00] op8 { direct(op); dT = 6 + dclk; dasm(op); } 10 [20] bra { lbra(LBRA, pp); dasm("LB" + pp); } // 10 20 does execute as LBRA, although it is "illegal" [2] 12 { NOP(); dT = 2; } 13 { SYNC(); dT = 2; } 16 { lbra(LBRA); dasm("LBRA"); } 17 { lbra(LBSR); dasm("LBSR"); } 19 { DAA(); dT = 2; } 1A { imm8(OR,CC); dasm("ORCC"); dT = 3; } 1C { imm8(AND,CC); dasm("ANDCC"); dT = 3; } 1D { SEX(); dT = 2; } 1E { regpair(EXG); dT = 8; } 1F { regpair(TFR); dT = 7; } [20] bra { bra(BRA, pp); dasm("B" + pp); dT = 3; } 30 { indexed(LEAX); dT += 4; } 31 { indexed(LEAY); dT += 4; } 32 { indexed(LEAS); dT += 4; } 33 { indexed(LEAU); dT += 4; } 34 { stack(push, S); dasm("PSHS"); dT += 5; } 35 { stack(pull, S); dasm("PULS"); dT += 5; } 36 { stack(push, U); dasm("PSHU"); dT += 5; } 37 { stack(pull, U); dasm("PULU"); dT += 5; } 39 { RTS(); dT = 5; } 3A { ABX(); dT = 3; } 3B { RTI(); dT = 3 + T[s]; } 3C { CWAI(bus1[PC++]); } 3D { MUL(); dT = 11; } 3F { SWI(); } 10 3F { SWIx(VSWI2); dasm("SWI2"); } 11 3F { SWIx(VSWI3); dasm("SWI3"); } [40] op8 - JMP { implicit(op, A); dasm(op + "A"); dT = 2; } [50] op8 - JMP { implicit(op, B); dasm(op + "B"); dT = 2; } [60] op8 { indexed(op); dT += 6 + dclk; } [70] op8 { extended(op); dT = 7 + dclk; } [80] op8x8 - ST { imm8(op, A); dasm(op + "A"); dT = 2; } [80] op16x16 - ST - JSR { imm16(op, r); dasm(op + r); dT = clks; } 8D { bra(BSR); dasm("BSR"); dT = 5; } [90] op8x8 { direct(op, A); dasm(op + "A"); dT = 4; } [90] op16x16 { direct(op, r); dasm(op + r); dT = 2 + clks; } [A0] op8x8 { indexed(op, A); dasm(op + "A"); dT += 4; } [A0] op16x16 { indexed(op, r); dasm(op + r); dT += 2 + clks; } [B0] op8x8 { extended(op, A); dasm(op + "A"); dT = 5; } [B0] op16x16 { extended(op, r); dasm(op + r); dT = 3 + clks; } [C0] op8x8 - ST { imm8(op, B); dasm(op + "B"); dT = 2; } [D0] op8x8 { direct(op, B); dasm(op + "B"); dT = 4; } [E0] op8x8 { indexed(op, B); dasm(op + "B"); dT += 4; } [F0] op8x8 { extended(op, B); dasm(op + "B"); dT = 5; } } ifetch { tabulate u8 val= bus1[PC++]; if ((val == 0x10) || (val == 0x11)) { tabulate u8 val2 = bus1[PC]; if (opcode(val, val2) == undefined) { // 6809 only swallows the 10 if undefined // e.g. 10 4F executes a CLRA [2] return; } PC++; opcode(val, val2); } else { opcode(val); } } undefined { } }