diff --git a/src/upi42.c b/src/upi42.c index e98af5496..216f87c73 100644 --- a/src/upi42.c +++ b/src/upi42.c @@ -18,41 +18,52 @@ #include #include #include -#define fatal printf -#define pclog printf -enum { - UPI42_8042 = 0, - UPI42_80C42 -}; +#ifdef UPI42_STANDALONE +# define fatal(...) \ + upi42_log(__VA_ARGS__); \ + abort(); +# define upi42_log printf +#endif + +#define UPI42_REG(upi42, r, op) ((upi42->psw & 0x10) ? (upi42->ram[24 + ((r) &7)] op) : (upi42->ram[(r) &7] op)) + +#define UPI42_ROM_SHIFT 0 /* actually the mask */ +#define UPI42_RAM_SHIFT 16 /* actually the mask */ +#define UPI42_TYPE_MCS (0 << 24) +#define UPI42_TYPE_UPI (1 << 24) +#define UPI42_EXT_C42 (1 << 25) + +#define UPI42_8048 ((1023 << UPI42_ROM_SHIFT) | (63 << UPI42_RAM_SHIFT) | UPI42_TYPE_MCS) +#define UPI42_8049 ((2047 << UPI42_ROM_SHIFT) | (127 << UPI42_RAM_SHIFT) | UPI42_TYPE_MCS) +#define UPI42_8041 ((1023 << UPI42_ROM_SHIFT) | (127 << UPI42_RAM_SHIFT) | UPI42_TYPE_UPI) +#define UPI42_8042 ((2047 << UPI42_ROM_SHIFT) | (255 << UPI42_RAM_SHIFT) | UPI42_TYPE_UPI) +#define UPI42_80C42 ((4095 << UPI42_ROM_SHIFT) | (255 << UPI42_RAM_SHIFT) | UPI42_TYPE_UPI | UPI42_EXT_C42) typedef struct _upi42_ { int (*ops[256])(struct _upi42_ *upi42, uint32_t fetchdat); - uint8_t ram[256], rom[4096], /* memory */ - ports[7], /* I/O ports */ - dbb_in, dbb_out; /* UPI-42 data buffer */ + uint32_t type; + uint8_t ram[256], *rom, /* memory */ + ports_in[8], ports_out[8], /* I/O ports */ + dbb_in, dbb_out; /* UPI-42 data buffer */ - uint8_t rammask, - a, /* accumulator */ - t, /* timer counter */ - psw, /* program status word */ - sts; /* UPI-42 status */ + uint8_t rammask, /* RAM mask */ + a, /* accumulator */ + t, /* timer counter */ + psw, /* program status word */ + sts; /* UPI-42 status */ - uint16_t pc; /* program counter */ + uint16_t pc, rommask; /* program counter and ROM mask */ - unsigned int prescaler : 5, tf : 1, tcnti : 1, run_timer : 1, run_counter : 1, skip_timer_inc : 1, /* timer/counter */ - i : 1, i_asserted : 1, tcnti_asserted : 1, irq_mask : 1, /* interrupts */ - dbf : 1, /* ROM bank */ - t0 : 1, t1 : 1, /* T0/T1 signals */ - flags : 1, /* buffer flag pins */ - suspend : 1; /* 80C42 suspend flag */ + unsigned int prescaler : 5, tf : 1, skip_timer_inc : 1, /* timer/counter */ + run_timer : 1, run_counter : 1, tcnti : 1, /* timer/counter enables */ + i : 1, i_raise : 1, tcnti_raise : 1, irq_mask : 1, /* interrupts */ + t0 : 1, t1 : 1, /* T0/T1 signals */ + flags : 1, dbf : 1, suspend : 1; /* UPI-42 flags */ int cycs; /* cycle counter */ } upi42_t; -#define UPI42_REG_READ(upi42, r) ((upi42->psw & 0x10) ? (upi42->ram[24 + ((r) &7)]) : (upi42->ram[(r) &7])) -#define UPI42_REG_WRITE(upi42, r, op) ((upi42->psw & 0x10) ? (upi42->ram[24 + ((r) &7)] op) : (upi42->ram[(r) &7] op)) - static inline void upi42_mirror_f0(upi42_t *upi42) { @@ -63,14 +74,14 @@ upi42_mirror_f0(upi42_t *upi42) static int upi42_op_MOV_A_Rr(upi42_t *upi42, uint32_t fetchdat) { - upi42->a = UPI42_REG_READ(upi42, fetchdat); + upi42->a = UPI42_REG(upi42, fetchdat, ); return 1; } static int upi42_op_MOV_Rr_A(upi42_t *upi42, uint32_t fetchdat) { - UPI42_REG_WRITE(upi42, fetchdat, = upi42->a); + UPI42_REG(upi42, fetchdat, = upi42->a); return 1; } @@ -91,7 +102,7 @@ upi42_op_MOV_indRr_A(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_MOV_Rr_imm(upi42_t *upi42, uint32_t fetchdat) { - UPI42_REG_WRITE(upi42, fetchdat, = fetchdat >> 8); + UPI42_REG(upi42, fetchdat, = fetchdat >> 8); upi42->cycs--; return 2; } @@ -169,8 +180,8 @@ static int upi42_op_XCH_A_Rr(upi42_t *upi42, uint32_t fetchdat) { uint8_t temp = upi42->a; - upi42->a = UPI42_REG_READ(upi42, fetchdat); - UPI42_REG_WRITE(upi42, fetchdat, = temp); + upi42->a = UPI42_REG(upi42, fetchdat, ); + UPI42_REG(upi42, fetchdat, = temp); return 1; } @@ -202,7 +213,8 @@ upi42_op_SWAP_A(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_IN_A_Pp(upi42_t *upi42, uint32_t fetchdat) { - upi42->a = upi42->ports[fetchdat & 3]; + int port = fetchdat & 3; + upi42->a = upi42->ports_in[port] & upi42->ports_out[port]; upi42->cycs--; return 1; } @@ -211,13 +223,14 @@ static int upi42_op_IN_A_DBB(upi42_t *upi42, uint32_t fetchdat) { upi42->a = upi42->dbb_in; + upi42->sts &= ~0x02; /* clear IBF */ return 1; } static int upi42_op_OUTL_Pp_A(upi42_t *upi42, uint32_t fetchdat) { - upi42->ports[fetchdat & 3] = upi42->a; + upi42->ports_out[fetchdat & 3] = upi42->a; upi42->cycs--; return 1; } @@ -226,14 +239,15 @@ static int upi42_op_OUT_DBB_A(upi42_t *upi42, uint32_t fetchdat) { upi42->dbb_out = upi42->a; - upi42->sts |= 0x01; + upi42->sts |= 0x01; /* set OBF */ return 1; } static int upi42_op_MOVD_A_Pp(upi42_t *upi42, uint32_t fetchdat) { - upi42->a = upi42->ports[4 | (fetchdat & 3)] & 0x0f; + int port = 4 | (fetchdat & 3); + upi42->a = (upi42->ports_in[port] & upi42->ports_out[port]) & 0x0f; upi42->cycs--; return 1; } @@ -241,7 +255,7 @@ upi42_op_MOVD_A_Pp(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_MOVD_Pp_A(upi42_t *upi42, uint32_t fetchdat) { - upi42->ports[4 | (fetchdat & 3)] = upi42->a & 0x0f; + upi42->ports_out[4 | (fetchdat & 3)] = upi42->a & 0x0f; upi42->cycs--; return 1; } @@ -249,21 +263,21 @@ upi42_op_MOVD_Pp_A(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_ANL_A_Rr(upi42_t *upi42, uint32_t fetchdat) { - upi42->a &= UPI42_REG_READ(upi42, fetchdat); + upi42->a &= UPI42_REG(upi42, fetchdat, ); return 1; } static int upi42_op_ORL_A_Rr(upi42_t *upi42, uint32_t fetchdat) { - upi42->a |= UPI42_REG_READ(upi42, fetchdat); + upi42->a |= UPI42_REG(upi42, fetchdat, ); return 1; } static int upi42_op_XRL_A_Rr(upi42_t *upi42, uint32_t fetchdat) { - upi42->a ^= UPI42_REG_READ(upi42, fetchdat); + upi42->a ^= UPI42_REG(upi42, fetchdat, ); return 1; } @@ -315,7 +329,7 @@ upi42_op_XRL_A_imm(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_ANL_Pp_imm(upi42_t *upi42, uint32_t fetchdat) { - upi42->ports[fetchdat & 3] &= fetchdat >> 8; + upi42->ports_out[fetchdat & 3] &= fetchdat >> 8; upi42->cycs--; return 2; } @@ -323,7 +337,7 @@ upi42_op_ANL_Pp_imm(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_ORL_Pp_imm(upi42_t *upi42, uint32_t fetchdat) { - upi42->ports[fetchdat & 3] |= fetchdat >> 8; + upi42->ports_out[fetchdat & 3] |= fetchdat >> 8; upi42->cycs--; return 2; } @@ -331,7 +345,7 @@ upi42_op_ORL_Pp_imm(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_ANLD_Pp_A(upi42_t *upi42, uint32_t fetchdat) { - upi42->ports[4 | (fetchdat & 3)] &= upi42->a; + upi42->ports_out[4 | (fetchdat & 3)] &= upi42->a; upi42->cycs--; return 1; } @@ -339,7 +353,7 @@ upi42_op_ANLD_Pp_A(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_ORLD_Pp_A(upi42_t *upi42, uint32_t fetchdat) { - upi42->ports[4 | (fetchdat & 3)] |= upi42->a; + upi42->ports_out[4 | (fetchdat & 3)] |= upi42->a; upi42->cycs--; return 1; } @@ -386,7 +400,7 @@ upi42_op_INC_A(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_INC_Rr(upi42_t *upi42, uint32_t fetchdat) { - UPI42_REG_WRITE(upi42, fetchdat, ++); + UPI42_REG(upi42, fetchdat, ++); return 1; } @@ -407,15 +421,16 @@ upi42_op_DEC_A(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_DEC_Rr(upi42_t *upi42, uint32_t fetchdat) { - UPI42_REG_WRITE(upi42, fetchdat, --); + UPI42_REG(upi42, fetchdat, --); return 1; } static int upi42_op_DJNZ_Rr_imm(upi42_t *upi42, uint32_t fetchdat) { - UPI42_REG_WRITE(upi42, fetchdat, --); - if (UPI42_REG_READ(upi42, fetchdat)) { + upi42->cycs--; + UPI42_REG(upi42, fetchdat, --); + if (UPI42_REG(upi42, fetchdat, )) { upi42->pc = (upi42->pc & 0xff00) | ((fetchdat >> 8) & 0xff); return 0; } else { @@ -426,7 +441,7 @@ upi42_op_DJNZ_Rr_imm(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_ADD_A_Rr(upi42_t *upi42, uint32_t fetchdat) { - int res = upi42->a + UPI42_REG_READ(upi42, fetchdat); + int res = upi42->a + UPI42_REG(upi42, fetchdat, ); upi42->a = res; upi42->psw = ((res >> 1) & 0x80) | (upi42->psw & ~0x80); return 1; @@ -435,7 +450,7 @@ upi42_op_ADD_A_Rr(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_ADDC_A_Rr(upi42_t *upi42, uint32_t fetchdat) { - int res = upi42->a + (upi42->psw >> 7) + UPI42_REG_READ(upi42, fetchdat); + int res = upi42->a + (upi42->psw >> 7) + UPI42_REG(upi42, fetchdat, ); upi42->a = res; upi42->psw = ((res >> 1) & 0x80) | (upi42->psw & ~0x80); return 1; @@ -444,7 +459,7 @@ upi42_op_ADDC_A_Rr(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_ADD_A_indRr(upi42_t *upi42, uint32_t fetchdat) { - int res = upi42->a + upi42->ram[UPI42_REG_READ(upi42, fetchdat) & upi42->rammask]; + int res = upi42->a + upi42->ram[UPI42_REG(upi42, fetchdat, ) & upi42->rammask]; upi42->a = res; upi42->psw = ((res >> 1) & 0x80) | (upi42->psw & ~0x80); return 1; @@ -453,7 +468,7 @@ upi42_op_ADD_A_indRr(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_ADDC_A_indRr(upi42_t *upi42, uint32_t fetchdat) { - int res = upi42->a + (upi42->psw >> 7) + upi42->ram[UPI42_REG_READ(upi42, fetchdat) & upi42->rammask]; + int res = upi42->a + (upi42->psw >> 7) + upi42->ram[UPI42_REG(upi42, fetchdat, ) & upi42->rammask]; upi42->a = res; upi42->psw = ((res >> 1) & 0x80) | (upi42->psw & ~0x80); return 1; @@ -576,7 +591,7 @@ upi42_op_EN_TCNTI(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_DIS_TCNTI(upi42_t *upi42, uint32_t fetchdat) { - upi42->tcnti = upi42->tcnti_asserted = 0; + upi42->tcnti = upi42->tcnti_raise = 0; return 1; } @@ -644,16 +659,14 @@ upi42_op_CALL_imm(upi42_t *upi42, uint32_t fetchdat) { /* Push new frame onto stack. */ uint8_t sp = (upi42->psw & 0x07) << 1; - upi42->ram[8 + sp++] = upi42->pc; /* stack frame format is undocumented! */ + upi42->ram[8 + sp++] = upi42->pc + 2; /* stack frame format is undocumented! */ upi42->ram[8 + sp++] = (upi42->psw & 0xf0) | ((upi42->pc >> 8) & 0x07); upi42->psw = (upi42->psw & 0xf8) | (sp >> 1); /* Load new program counter. */ upi42->pc = (upi42->dbf << 11) | ((fetchdat << 3) & 0x0700) | ((fetchdat >> 8) & 0x00ff); - /* Don't decrease cycle counter if this is an interrupt call. */ - if (fetchdat & 0xff) - upi42->cycs--; + upi42->cycs--; return 0; } @@ -703,24 +716,24 @@ upi42_op_JMPP_indA(upi42_t *upi42, uint32_t fetchdat) { \ if (cond) \ upi42->pc = (upi42->pc & 0xff00) | ((fetchdat >> 8) & 0x00ff); \ - post \ - upi42->cycs--; \ + post; \ + upi42->cycs--; \ return 2 * !(cond); \ } -UPI42_COND_JMP_IMM(JC, upi42->psw & 0x80, ;) -UPI42_COND_JMP_IMM(JNC, !(upi42->psw & 0x80), ;) -UPI42_COND_JMP_IMM(JZ, !upi42->a, ;) -UPI42_COND_JMP_IMM(JNZ, upi42->a, ;) -UPI42_COND_JMP_IMM(JT0, upi42->t0, ;) -UPI42_COND_JMP_IMM(JNT0, !upi42->t0, ;) -UPI42_COND_JMP_IMM(JT1, upi42->t1, ;) -UPI42_COND_JMP_IMM(JNT1, !upi42->t1, ;) -UPI42_COND_JMP_IMM(JF0, upi42->psw & 0x20, ;) -UPI42_COND_JMP_IMM(JF1, upi42->sts & 0x08, ;) -UPI42_COND_JMP_IMM(JTF, !upi42->tf, upi42->tf = 0;) -UPI42_COND_JMP_IMM(JBb, upi42->a &(1 << ((fetchdat >> 5) & 7)), ;) -UPI42_COND_JMP_IMM(JNIBF, !(upi42->sts & 0x02), ;) -UPI42_COND_JMP_IMM(JOBF, upi42->sts & 0x01, ;) +UPI42_COND_JMP_IMM(JC, upi42->psw & 0x80, ) +UPI42_COND_JMP_IMM(JNC, !(upi42->psw & 0x80), ) +UPI42_COND_JMP_IMM(JZ, !upi42->a, ) +UPI42_COND_JMP_IMM(JNZ, upi42->a, ) +UPI42_COND_JMP_IMM(JT0, upi42->t0, ) +UPI42_COND_JMP_IMM(JNT0, !upi42->t0, ) +UPI42_COND_JMP_IMM(JT1, upi42->t1, ) +UPI42_COND_JMP_IMM(JNT1, !upi42->t1, ) +UPI42_COND_JMP_IMM(JF0, upi42->psw & 0x20, ) +UPI42_COND_JMP_IMM(JF1, upi42->sts & 0x08, ) +UPI42_COND_JMP_IMM(JTF, !upi42->tf, upi42->tf = 0) +UPI42_COND_JMP_IMM(JBb, upi42->a &(1 << ((fetchdat >> 5) & 7)), ) +UPI42_COND_JMP_IMM(JNIBF, !(upi42->sts & 0x02), ) +UPI42_COND_JMP_IMM(JOBF, upi42->sts & 0x01, ) static int upi42_op_EN_A20(upi42_t *upi42, uint32_t fetchdat) @@ -738,6 +751,7 @@ upi42_op_EN_DMA(upi42_t *upi42, uint32_t fetchdat) static int upi42_op_EN_FLAGS(upi42_t *upi42, uint32_t fetchdat) { + upi42->flags = 1; return 1; } @@ -749,65 +763,6 @@ upi42_op_SUSPEND(upi42_t *upi42, uint32_t fetchdatr) return 1; } -static void -upi42_exec(void *priv) -{ - upi42_t *upi42 = (upi42_t *) priv; - - /* Skip interrupt handling and code execution if we're suspended or in a multi-cycle instruction. */ - if (upi42->suspend || ++upi42->cycs < 0) - return; - - /* Trigger interrupt if requested. */ - if (upi42->irq_mask) { - /* Masked, we're currently in an ISR. */ - } else if (upi42->i_asserted) { - /* External interrupt. Higher priority than the timer interrupt. */ - upi42->irq_mask = 1; - upi42->i_asserted = 0; - upi42_op_CALL_imm(upi42, 3 << 8); - return; - } else if (upi42->tcnti_asserted) { - /* Timer interrupt. */ - upi42->irq_mask = 1; - upi42->tcnti_asserted = 0; - upi42_op_CALL_imm(upi42, 7 << 8); - return; - } - - /* Fetch instruction. */ - uint32_t fetchdat = *((uint32_t *) &upi42->rom[upi42->pc]); - pclog("%04X @ %04X R0=%02X", fetchdat & 0xffff, upi42->pc, upi42->ram[0]); - - /* Decode instruction. */ - uint8_t insn = fetchdat & 0xff; - if (upi42->ops[insn]) { - /* Execute instruction and increment program counter. */ - upi42->pc += upi42->ops[insn](upi42, fetchdat); - - /* Decrement cycle counter. Multi-cycle instructions also decrement within their code. */ - upi42->cycs--; - } else { - fatal("UPI42: Unknown opcode %02X (%08X)\n", insn, fetchdat); - return; - } - - /* Some instructions don't increment the timer. */ - if (upi42->skip_timer_inc) { - upi42->skip_timer_inc = 0; - } else { - /* Increment counter once the prescaler overflows, - and set timer flag once the main value overflows. */ - if ((++upi42->prescaler == 0) && (++upi42->t == 0)) { - upi42->tf = 1; - - /* Fire counter interrupt if enabled. */ - if (upi42->tcnti) - upi42->tcnti_asserted = 1; - } - } -} - static const int (*ops_80c42[256])(upi42_t *upi42, uint32_t fetchdat) = { // clang-format off /* 0 / 8 */ /* 1 / 9 */ /* 2 / a */ /* 3 / b */ /* 4 / c */ /* 5 / d */ /* 6 / e */ /* 7 / f */ @@ -823,9 +778,9 @@ static const int (*ops_80c42[256])(upi42_t *upi42, uint32_t fetchdat) = { /* 48 */ upi42_op_ORL_A_Rr, upi42_op_ORL_A_Rr, upi42_op_ORL_A_Rr, upi42_op_ORL_A_Rr, upi42_op_ORL_A_Rr, upi42_op_ORL_A_Rr, upi42_op_ORL_A_Rr, upi42_op_ORL_A_Rr, /* 50 */ upi42_op_ANL_A_indRr, upi42_op_ANL_A_indRr, upi42_op_JBb_imm, upi42_op_ANL_A_imm, upi42_op_CALL_imm, upi42_op_STRT_T, upi42_op_JT1_imm, upi42_op_DA_A, /* 58 */ upi42_op_ANL_A_Rr, upi42_op_ANL_A_Rr, upi42_op_ANL_A_Rr, upi42_op_ANL_A_Rr, upi42_op_ANL_A_Rr, upi42_op_ANL_A_Rr, upi42_op_ANL_A_Rr, upi42_op_ANL_A_Rr, - /* 60 */ upi42_op_ADD_A_indRr, upi42_op_ADD_A_indRr, upi42_op_MOV_T_A, NULL, upi42_op_JMP_imm, upi42_op_STOP_TCNT, NULL, upi42_op_RRC_A, + /* 60 */ upi42_op_ADD_A_indRr, upi42_op_ADD_A_indRr, upi42_op_MOV_T_A, upi42_op_SEL_PMB0, upi42_op_JMP_imm, upi42_op_STOP_TCNT, NULL, upi42_op_RRC_A, /* 68 */ upi42_op_ADD_A_Rr, upi42_op_ADD_A_Rr, upi42_op_ADD_A_Rr, upi42_op_ADD_A_Rr, upi42_op_ADD_A_Rr, upi42_op_ADD_A_Rr, upi42_op_ADD_A_Rr, upi42_op_ADD_A_Rr, - /* 70 */ upi42_op_ADDC_A_indRr, upi42_op_ADDC_A_indRr, upi42_op_JBb_imm, NULL, upi42_op_CALL_imm, NULL, upi42_op_JF1_imm, upi42_op_RR_A, + /* 70 */ upi42_op_ADDC_A_indRr, upi42_op_ADDC_A_indRr, upi42_op_JBb_imm, upi42_op_SEL_PMB1, upi42_op_CALL_imm, NULL, upi42_op_JF1_imm, upi42_op_RR_A, /* 78 */ upi42_op_ADDC_A_Rr, upi42_op_ADDC_A_Rr, upi42_op_ADDC_A_Rr, upi42_op_ADDC_A_Rr, upi42_op_ADDC_A_Rr, upi42_op_ADDC_A_Rr, upi42_op_ADDC_A_Rr, upi42_op_ADDC_A_Rr, /* 80 */ NULL, NULL, upi42_op_SUSPEND, upi42_op_RET, upi42_op_JMP_imm, upi42_op_CLR_F0, upi42_op_JOBF_imm, NULL, /* 88 */ upi42_op_ORL_Pp_imm, upi42_op_ORL_Pp_imm, upi42_op_ORL_Pp_imm, upi42_op_ORL_Pp_imm, upi42_op_ORLD_Pp_A, upi42_op_ORLD_Pp_A, upi42_op_ORLD_Pp_A, upi42_op_ORLD_Pp_A, @@ -847,27 +802,184 @@ static const int (*ops_80c42[256])(upi42_t *upi42, uint32_t fetchdat) = { }; static void -upi42_reset(upi42_t *upi42) +upi42_exec(void *priv) { - upi42->pc = 0; /* program counter */ - upi42->psw = 0; /* stack pointer, register bank and F0 */ - upi42->dbf = 0; /* memory bank */ - upi42->i = upi42->tcnti = 0; /* both interrupts */ - upi42->tf = 0; /* timer flag */ - upi42->sts = 0; /* F1 */ - upi42->suspend = 0; /* 80C42 suspend flag */ + upi42_t *upi42 = (upi42_t *) priv; + + /* Skip everything if we're suspended, or just process timer if we're in a multi-cycle instruction. */ + if (upi42->suspend) + return; + else if (++upi42->cycs < 0) + goto timer; + + /* Trigger interrupt if requested. */ + if (upi42->irq_mask) { + /* Masked, we're currently in an ISR. */ + } else if (upi42->i_raise) { + /* External interrupt. Higher priority than the timer interrupt. */ + upi42->irq_mask = 1; + upi42->i_raise = 0; + + upi42->pc -= 2; + upi42->cycs++; + upi42_op_CALL_imm(upi42, 3 << 8); + return; + } else if (upi42->tcnti_raise) { + /* Timer interrupt. */ + upi42->irq_mask = 1; + upi42->tcnti_raise = 0; + + upi42->pc -= 2; + upi42->cycs++; + upi42_op_CALL_imm(upi42, 7 << 8); + return; + } + + /* Fetch instruction. */ + uint32_t fetchdat = *((uint32_t *) &upi42->rom[upi42->pc]); + + /* Decode instruction. */ + uint8_t insn = fetchdat & 0xff; + if (upi42->ops[insn]) { + /* Execute instruction. */ + int pc_inc = upi42->ops[insn](upi42, fetchdat); + + /* Increment lower 11 bits of the program counter. */ + upi42->pc = (upi42->pc & 0xf800) | ((upi42->pc + pc_inc) & 0x07ff); + + /* Decrement cycle counter. Multi-cycle instructions also decrement within their code. */ + upi42->cycs--; + } else { + fatal("UPI42: Unknown opcode %02X (%08X)\n", insn, fetchdat); + return; + } + +timer: + /* Process timer. */ + if (!upi42->run_timer) { + /* Timer disabled. */ + } else if (upi42->skip_timer_inc) { + /* Some instructions don't increment the timer. */ + upi42->skip_timer_inc = 0; + } else { + /* Increment counter once the prescaler overflows, + and set timer flag once the main value overflows. */ + if ((++upi42->prescaler == 0) && (++upi42->t == 0)) { + upi42->tf = 1; + + /* Fire counter interrupt if enabled. */ + if (upi42->tcnti) + upi42->tcnti_raise = 1; + } + } } -static upi42_t * -upi42_init(int type) +uint8_t +upi42_port_read(void *priv, int port) +{ + upi42_t *upi42 = (upi42_t *) priv; + + /* Read base port value. */ + port &= 7; + uint8_t ret = upi42->ports_in[port] & upi42->ports_out[port]; + + /* Apply special meanings. */ + switch (port) { + } + + upi42_log("UPI42: port_read(%d) = %02X\n", port, ret); + return ret; +} + +void +upi42_port_write(void *priv, int port, uint8_t val) +{ + upi42_t *upi42 = (upi42_t *) priv; + + port &= 7; + upi42_log("UPI42: port_write(%d, %02X)\n", port, val); + + /* Set input level. */ + upi42->ports_in[port] = val; +} + +/* NOTE: The dbb/sts/cmd functions use I/O handler signatures; port is ignored. */ + +uint8_t +upi42_dbb_read(uint16_t port, void *priv) +{ + upi42_t *upi42 = (upi42_t *) priv; + + uint8_t ret = upi42->dbb_out; + upi42_log("UPI42: dbb_read(%04X) = %02X\n", port, ret); + upi42->sts &= ~0x01; /* clear OBF */ + return ret; +} + +void +upi42_dbb_write(uint16_t port, uint8_t val, void *priv) +{ + upi42_t *upi42 = (upi42_t *) priv; + + upi42_log("UPI42: dbb_write(%04X, %02X)\n", port, val); + upi42->dbb_in = val; + upi42->sts = (upi42->sts & ~0x08) | 0x02; /* clear F1 and set IBF */ + if (upi42->i) /* fire IBF interrupt if enabled */ + upi42->i_raise = 1; +} + +uint8_t +upi42_sts_read(uint16_t port, void *priv) +{ + upi42_t *upi42 = (upi42_t *) priv; + + uint8_t ret = upi42->sts; + upi42_log("UPI42: sts_read(%04X) = %02X\n", port, ret); + return ret; +} + +void +upi42_cmd_write(uint16_t port, uint8_t val, void *priv) +{ + upi42_t *upi42 = (upi42_t *) priv; + + upi42_log("UPI42: cmd_write(%04X, %02X)\n", port, val); + upi42->dbb_in = val; + upi42->sts |= 0x0a; /* set F1 and IBF */ + if (upi42->i) /* fire IBF interrupt if enabled */ + upi42->i_raise = 1; +} + +void +upi42_reset(upi42_t *upi42) +{ + upi42->pc = 0; /* program counter */ + upi42->psw = 0; /* stack pointer, register bank and F0 */ + upi42->dbf = 0; /* ROM bank */ + upi42->i = 0; /* external interrupt */ + upi42->tcnti = 0; /* timer/counter interrupt */ + upi42->tf = 0; /* timer flag */ + upi42->sts = 0; /* F1 */ + upi42->flags = 0; /* UPI-42 buffer interrupts */ + upi42->suspend = 0; /* 80C42 suspend flag */ +} + +void * +upi42_init(uint32_t type, uint8_t *rom) { /* Allocate state structure. */ upi42_t *upi42 = (upi42_t *) malloc(sizeof(upi42_t)); memset(upi42, 0, sizeof(upi42_t)); + upi42->rom = rom; + + /* Set chip type. */ + upi42->type = type; + upi42->rommask = type >> UPI42_ROM_SHIFT; + upi42->rammask = type >> UPI42_RAM_SHIFT; /* Build instruction table. */ memcpy(upi42->ops, ops_80c42, sizeof(ops_80c42)); - if (type < UPI42_80C42) { + if (!(type & UPI42_EXT_C42)) { /* Remove 80C42-only instructions. */ upi42->ops[0x33] = NULL; /* EN A20 */ upi42->ops[0x63] = NULL; /* SEL PMB0 */ @@ -879,25 +991,133 @@ upi42_init(int type) return upi42; } +#ifdef UPI42_STANDALONE +static const char *flags_8042[] = { "OBF", "IBF", "F0", "F1", "ST4", "ST5", "ST6", "ST7" }; + int main(int argc, char **argv) { - upi42_t *upi42 = upi42_init(UPI42_8042); + /* Check arguments. */ + if (argc < 2) { + upi42_log("Specify a ROM file to execute.\n"); + return 1; + } /* Load ROM. */ - FILE *f = fopen("1503033.bin", "rb"); - fread(upi42->rom, 1, sizeof(upi42->rom), f); + uint8_t rom[4096] = { 0 }; + FILE *f = fopen(argv[1], "rb"); + if (!f) { + upi42_log("Could not read ROM file.\n"); + return 2; + } + size_t rom_size = fread(rom, sizeof(rom[0]), sizeof(rom), f); fclose(f); - /* Start execution. */ - char buf[256]; - while (1) { - upi42->sts |= 0x02; - upi42->sts |= 0x08; - upi42->dbb_in = 0xaa; - upi42->cycs = 0; + /* Determine chip type from ROM. */ + upi42_log("%d-byte ROM, ", rom_size); + uint32_t type; + switch (rom_size) { + case 0 ... 1024: + upi42_log("emulating 8041"); + type = UPI42_8041; + break; - upi42_exec(upi42); - fgets(buf, 256, stdin); + case 1025 ... 2048: + upi42_log("emulating 8042"); + type = UPI42_8042; + break; + + case 2049 ... 4096: + upi42_log("emulating 80C42"); + type = UPI42_80C42; + break; + + default: + upi42_log("unknown!\n"); + return 3; } + upi42_log(".\n"); + + /* Initialize emulator. */ + upi42_t *upi42 = (upi42_t *) upi42_init(type, rom); + + /* Start execution. */ + char cmd, cmd_buf[256]; + int val, go_until = -1; + while (1) { + /* Output status. */ + upi42_log("PC=%04X I=%02X(%02X) A=%02X", upi42->pc, upi42->rom[upi42->pc], upi42->rom[upi42->pc + 1], upi42->a); + for (val = 0; val < 8; val++) + upi42_log(" R%d=%02X", val, UPI42_REG(upi42, val, )); + upi42_log(" T=%02X PSW=%02X TF=%d I=%d TCNTI=%d", upi42->t, upi42->psw, upi42->tf, upi42->i, upi42->tcnti); + if (type & UPI42_TYPE_UPI) { + upi42_log(" STS=%02X", upi42->sts); + for (val = 0; val < 8; val++) { + if (upi42->sts & (1 << val)) + upi42_log(" [%s]", flags_8042[val]); + else + upi42_log(" %s ", flags_8042[val]); + } + } + upi42_log("\n"); + + /* Break for command only if stepping. */ + if ((go_until < 0) || (upi42->pc == go_until)) { +retry: + go_until = -1; + upi42_log("> "); + + /* Read command. */ + cmd = '\0'; + scanf("%c", &cmd); + + /* Execute command. */ + switch (cmd) { + case 'c': /* write command */ + if (scanf("%X%*c", &val, &cmd_buf)) + upi42_cmd_write(0, val, upi42); + goto retry; + + case 'd': /* write data */ + if (scanf("%X%*c", &val, &cmd_buf)) + upi42_dbb_write(0, val, upi42); + goto retry; + + case 'g': /* go until */ + if (!scanf("%X%*c", &go_until, &cmd_buf)) + go_until = -1; + break; + + case 'r': /* read data */ + upi42_dbb_read(0, upi42); /* return value will be logged */ + goto skip_and_retry; + + case 'q': /* exit */ + return 0; + + case '\r': /* step */ + case '\n': + case '\0': + break; + + default: + upi42_log("Monitor commands:\n"); + upi42_log("- Return (no command) - Step execution\n"); + upi42_log("- q (or Ctrl+C) - Exit\n"); + upi42_log("- gXXXX - Execute until PC is hex value XXXX\n"); + upi42_log("- dXX - Write hex value XX to data port\n"); + upi42_log("- cXX - Write hex value XX to command port\n"); + upi42_log("- r - Read from data port and reset OBF\n"); +skip_and_retry: + scanf("%*c", &cmd_buf); + goto retry; + } + } + + /* Execute a cycle. */ + upi42_exec(upi42); + } + + return 0; } +#endif