--- tme/ic/m68k/m68k-insns-auto.c 2018/04/24 16:39:00 1.1.1.2 +++ tme/ic/m68k/m68k-insns-auto.c 2018/04/24 16:41:52 1.1.1.4 @@ -1,5 +1,5 @@ /* automatically generated by m68k-insns-auto.sh, do not edit! */ -_TME_RCSID("$Id: m68k-insns-auto.c,v 1.1.1.2 2018/04/24 16:39:00 root Exp $"); +_TME_RCSID("$Id: m68k-insns-auto.c,v 1.1.1.4 2018/04/24 16:41:52 root Exp $"); #include "m68k-impl.h" @@ -315,17 +315,17 @@ TME_M68K_INSN(tme_m68k_addx8) if (memory) { TME_M68K_INSN_CANFAULT; if (!TME_M68K_SEQUENCE_RESTARTING) { - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; - ic->_tme_m68k_ea_function_code = function_code; - ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); - } - tme_m68k_read_memx8(ic); - if (!TME_M68K_SEQUENCE_RESTARTING) { ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) -= ireg_src_adjust; ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src); } tme_m68k_read_mem8(ic, TME_M68K_IREG_MEMY8); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; + ic->_tme_m68k_ea_function_code = function_code; + ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); + } + tme_m68k_read_memx8(ic); op1 = ic->tme_m68k_ireg_memx8; op0 = ic->tme_m68k_ireg_memy8; } @@ -378,17 +378,17 @@ TME_M68K_INSN(tme_m68k_subx8) if (memory) { TME_M68K_INSN_CANFAULT; if (!TME_M68K_SEQUENCE_RESTARTING) { - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; - ic->_tme_m68k_ea_function_code = function_code; - ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); - } - tme_m68k_read_memx8(ic); - if (!TME_M68K_SEQUENCE_RESTARTING) { ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) -= ireg_src_adjust; ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src); } tme_m68k_read_mem8(ic, TME_M68K_IREG_MEMY8); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; + ic->_tme_m68k_ea_function_code = function_code; + ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); + } + tme_m68k_read_memx8(ic); op1 = ic->tme_m68k_ireg_memx8; op0 = ic->tme_m68k_ireg_memy8; } @@ -440,16 +440,16 @@ TME_M68K_INSN(tme_m68k_cmpm8) if (!TME_M68K_SEQUENCE_RESTARTING) { ic->_tme_m68k_ea_function_code = function_code; - ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) += ireg_dst_adjust; - } - tme_m68k_read_memx8(ic); - if (!TME_M68K_SEQUENCE_RESTARTING) { - ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src); ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) += ireg_src_adjust; } tme_m68k_read_mem8(ic, TME_M68K_IREG_MEMY8); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->_tme_m68k_ea_function_code = function_code; + ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) += ireg_dst_adjust; + } + tme_m68k_read_memx8(ic); op1 = ic->tme_m68k_ireg_memx8; op0 = ic->tme_m68k_ireg_memy8; @@ -635,13 +635,23 @@ TME_M68K_INSN(tme_m68k_asr8) if (count > 0) { if (63 > SHIFTMAX_INT8_T && count > 8) { - res = 0; + res = 0 - (res < 0); } +#ifdef SHIFTSIGNED_INT8_T res >>= (count - 1); +#else /* !SHIFTSIGNED_INT8_T */ + for (; --count > 0; ) { + res = (res & ~((tme_int8_t) 1)) / 2; + } +#endif /* !SHIFTSIGNED_INT8_T */ flags = (res & 1); flags *= TME_M68K_FLAG_C; flags |= (flags * TME_M68K_FLAG_X); +#ifdef SHIFTSIGNED_INT8_T res >>= 1; +#else /* !SHIFTSIGNED_INT8_T */ + res = (res & ~((tme_int8_t) 1)) / 2; +#endif /* !SHIFTSIGNED_INT8_T */ } /* store the result: */ @@ -939,56 +949,72 @@ TME_M68K_INSN(tme_m68k_roxr8) /* cas8: */ TME_M68K_INSN(tme_m68k_cas8) { + struct tme_m68k_rmw rmw; struct tme_m68k_tlb *tlb; int ireg_dc, ireg_du; - int do_write; - tme_uint16_t specopx = ic->_tme_m68k_insn_specop; + tme_uint8_t value_dc, value_du, value_mem; /* start the read/modify/write cycle: */ - tlb = tme_m68k_rmw_start(ic); - if (tlb == NULL) { + rmw.tme_m68k_rmw_addresses[0] = ic->_tme_m68k_ea_address; + rmw.tme_m68k_rmw_address_count = 1; + rmw.tme_m68k_rmw_size = sizeof(tme_uint8_t); + if (tme_m68k_rmw_start(ic, + &rmw)) { TME_M68K_INSN_OK; } - /* read: */ - tme_m68k_read8(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx8, - TME_M68K_BUS_CYCLE_RMW); - - /* modify: */ - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); - tme_m68k_cmp8(ic, &ic->tme_m68k_ireg_uint8(ireg_dc), &ic->tme_m68k_ireg_memx8); + /* get the compare and update registers: */ + ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 3); + ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 6, 3); + + /* if we can do the fast compare-and-exchange: */ + if (!rmw.tme_m68k_rmw_slow_reads[0]) { + + /* get the compare and update values in big-endian byte order: */ + value_dc = ic->tme_m68k_ireg_uint8(ireg_dc << 2); + value_du = ic->tme_m68k_ireg_uint8(ireg_du << 2); + + /* get this TLB entry: */ + tlb = rmw.tme_m68k_rmw_tlbs[0]; + + /* this TLB entry must allow fast reading and fast writing + to the same memory: */ + assert (tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF + && tlb->tme_m68k_tlb_emulator_off_write == tlb->tme_m68k_tlb_emulator_off_read); + + /* do the compare-and-exchange: */ + value_mem = + tme_memory_atomic_cx8(((tme_shared tme_uint8_t *) + (tlb->tme_m68k_tlb_emulator_off_read + + ic->_tme_m68k_ea_address)), + value_dc, + value_du, + tlb->tme_m68k_tlb_bus_rwlock, + sizeof(tme_uint8_t)); + ic->tme_m68k_ireg_memx8 = (value_mem); + } + + /* compare the compare operand to the effective address operand: */ + tme_m68k_cmp8(ic, &ic->tme_m68k_ireg_uint8(ireg_dc << 2), &ic->tme_m68k_ireg_memx8); - /* write: */ + /* if the comparison succeeded: */ if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) { - ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 6, 3); - ic->tme_m68k_ireg_memx8 = ic->tme_m68k_ireg_uint8(ireg_du); - tme_m68k_write8(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx8, - TME_M68K_BUS_CYCLE_RMW); - } - else { - /* XXX the 68040 always does a write to finish its cycle: */ - do_write = FALSE; - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); - if (do_write) { - tme_m68k_write8(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx8, - TME_M68K_BUS_CYCLE_RMW); - do_write = FALSE; - } - ic->tme_m68k_ireg_uint8(ireg_dc) = ic->tme_m68k_ireg_memx8; + + /* write the update operand to the effective address operand: */ + ic->tme_m68k_ireg_memx8 = ic->tme_m68k_ireg_uint8(ireg_du << 2); } - /* finish the read/modify/write cycle: */ - tme_m68k_rmw_finish(ic, tlb); + /* otherwise, the comparison failed: */ + else { + + /* write the effective address operand to the compare operand: */ + ic->tme_m68k_ireg_uint8(ireg_dc << 2) = ic->tme_m68k_ireg_memx8; + } + /* finish the read/modify/write cycle: */ + tme_m68k_rmw_finish(ic, + &rmw, + (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) != 0); TME_M68K_INSN_OK; } @@ -996,11 +1022,44 @@ TME_M68K_INSN(tme_m68k_cas8) TME_M68K_INSN(tme_m68k_moves8) { int ireg; + tme_uint8_t ireg_value; + unsigned int ea_reg; + unsigned int increment; + TME_M68K_INSN_PRIV; + TME_M68K_INSN_CANFAULT; ireg = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 12, 4); + + /* in case we're storing the same address register used in a + postincrement or predecrement EA, save the current value + of the register now: */ + ireg_value = ic->tme_m68k_ireg_uint8(ireg << 2); + + /* we have to handle postincrement and predecrement ourselves: */ + if (!TME_M68K_SEQUENCE_RESTARTING) { + ea_reg = TME_M68K_IREG_A0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3); + increment = TME_M68K_SIZE_8; + if (increment == TME_M68K_SIZE_8 && ea_reg == TME_M68K_IREG_A7) { + increment = TME_M68K_SIZE_16; + } + switch (TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3)) { + case 3: ic->tme_m68k_ireg_uint32(ea_reg) += increment; break; + case 4: ic->_tme_m68k_ea_address = (ic->tme_m68k_ireg_uint32(ea_reg) -= increment); break; + default: break; + } + } + if (TME_M68K_INSN_SPECOP & TME_BIT(11)) { - ic->tme_m68k_ireg_memx8 = ic->tme_m68k_ireg_uint8(ireg << 2); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_memx8 = ireg_value; + ic->_tme_m68k_ea_function_code = ic->tme_m68k_ireg_dfc; + } + tme_m68k_write_memx8(ic); } else { + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->_tme_m68k_ea_function_code = ic->tme_m68k_ireg_sfc; + } + tme_m68k_read_memx8(ic); if (ireg >= TME_M68K_IREG_A0) { ic->tme_m68k_ireg_uint32(ireg) = TME_EXT_S8_U32((tme_int8_t) ic->tme_m68k_ireg_memx8); @@ -1346,17 +1405,17 @@ TME_M68K_INSN(tme_m68k_addx16) if (memory) { TME_M68K_INSN_CANFAULT; if (!TME_M68K_SEQUENCE_RESTARTING) { - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; - ic->_tme_m68k_ea_function_code = function_code; - ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); - } - tme_m68k_read_memx16(ic); - if (!TME_M68K_SEQUENCE_RESTARTING) { ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) -= ireg_src_adjust; ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src); } tme_m68k_read_mem16(ic, TME_M68K_IREG_MEMY16); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; + ic->_tme_m68k_ea_function_code = function_code; + ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); + } + tme_m68k_read_memx16(ic); op1 = ic->tme_m68k_ireg_memx16; op0 = ic->tme_m68k_ireg_memy16; } @@ -1409,17 +1468,17 @@ TME_M68K_INSN(tme_m68k_subx16) if (memory) { TME_M68K_INSN_CANFAULT; if (!TME_M68K_SEQUENCE_RESTARTING) { - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; - ic->_tme_m68k_ea_function_code = function_code; - ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); - } - tme_m68k_read_memx16(ic); - if (!TME_M68K_SEQUENCE_RESTARTING) { ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) -= ireg_src_adjust; ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src); } tme_m68k_read_mem16(ic, TME_M68K_IREG_MEMY16); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; + ic->_tme_m68k_ea_function_code = function_code; + ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); + } + tme_m68k_read_memx16(ic); op1 = ic->tme_m68k_ireg_memx16; op0 = ic->tme_m68k_ireg_memy16; } @@ -1471,16 +1530,16 @@ TME_M68K_INSN(tme_m68k_cmpm16) if (!TME_M68K_SEQUENCE_RESTARTING) { ic->_tme_m68k_ea_function_code = function_code; - ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) += ireg_dst_adjust; - } - tme_m68k_read_memx16(ic); - if (!TME_M68K_SEQUENCE_RESTARTING) { - ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src); ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) += ireg_src_adjust; } tme_m68k_read_mem16(ic, TME_M68K_IREG_MEMY16); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->_tme_m68k_ea_function_code = function_code; + ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) += ireg_dst_adjust; + } + tme_m68k_read_memx16(ic); op1 = ic->tme_m68k_ireg_memx16; op0 = ic->tme_m68k_ireg_memy16; @@ -1498,6 +1557,46 @@ TME_M68K_INSN(tme_m68k_cmpm16) TME_M68K_INSN_OK; } +/* a move of an address register to a predecrement or + postincrement EA with that same address register, must + store the original value of the address register. since the + predecrement and postincrement code in the executer updates + the address register before the move has happened, we wrap + the normal move function in this one, that gives an op1 + argument that is the original value of the address register: */ +TME_M68K_INSN(tme_m68k_move_srpd16) +{ + /* NB: both this function and tme_m68k_move16() + get the source operand as _op1, and the destination + operand as _op0: */ + if (!TME_M68K_SEQUENCE_RESTARTING) { + *((tme_uint16_t *) _op0) + = (*((tme_uint16_t *) _op1) + + sizeof(tme_uint16_t)); + } + tme_m68k_move16(ic, _op0, _op0); +} + +/* a move of an address register to a predecrement or + postincrement EA with that same address register, must + store the original value of the address register. since the + predecrement and postincrement code in the executer updates + the address register before the move has happened, we wrap + the normal move function in this one, that gives an op1 + argument that is the original value of the address register: */ +TME_M68K_INSN(tme_m68k_move_srpi16) +{ + /* NB: both this function and tme_m68k_move16() + get the source operand as _op1, and the destination + operand as _op0: */ + if (!TME_M68K_SEQUENCE_RESTARTING) { + *((tme_uint16_t *) _op0) + = (*((tme_uint16_t *) _op1) + - sizeof(tme_uint16_t)); + } + tme_m68k_move16(ic, _op0, _op0); +} + /* the suba function on a 16-byte EA: */ TME_M68K_INSN(tme_m68k_suba16) { @@ -1624,13 +1723,23 @@ TME_M68K_INSN(tme_m68k_asr16) if (count > 0) { if (63 > SHIFTMAX_INT16_T && count > 16) { - res = 0; + res = 0 - (res < 0); } +#ifdef SHIFTSIGNED_INT16_T res >>= (count - 1); +#else /* !SHIFTSIGNED_INT16_T */ + for (; --count > 0; ) { + res = (res & ~((tme_int16_t) 1)) / 2; + } +#endif /* !SHIFTSIGNED_INT16_T */ flags = (res & 1); flags *= TME_M68K_FLAG_C; flags |= (flags * TME_M68K_FLAG_X); +#ifdef SHIFTSIGNED_INT16_T res >>= 1; +#else /* !SHIFTSIGNED_INT16_T */ + res = (res & ~((tme_int16_t) 1)) / 2; +#endif /* !SHIFTSIGNED_INT16_T */ } /* store the result: */ @@ -1964,6 +2073,19 @@ TME_M68K_INSN(tme_m68k_movem_rm16) tme_uint16_t mask, bit; unsigned int ea_mode; tme_uint32_t addend; + tme_uint32_t total_size; + /* get the register mask, and figure out the total size + of the transfer: */ + mask = TME_M68K_INSN_SPECOP; + total_size = 0; + if (mask != 0) { + TME_M68K_INSN_CANFAULT; + bit = mask; + do { + total_size += sizeof(tme_uint16_t); + bit &= (bit - 1); + } while (bit != 0); + } /* figure out what direction to move in, and where to start from: */ ea_mode = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3); @@ -1973,16 +2095,25 @@ TME_M68K_INSN(tme_m68k_movem_rm16) direction = -1; ireg = TME_M68K_IREG_A7; if (!TME_M68K_SEQUENCE_RESTARTING) { + + /* "For the MC68020, MC68030, MC68040, and CPU32, if + the addressing register is also moved to memory, the + value written is the initial register value decremented + by the size of the operation. The MC68000 and MC68010 + write the initial register value (not decremented)." */ + if (ic->tme_m68k_type >= TME_M68K_M68020) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3)) + = (ic->_tme_m68k_ea_address - total_size); + } + + /* predecrement the effective address for the first transfer: */ ic->_tme_m68k_ea_address -= sizeof(tme_uint16_t); } } addend = (tme_uint32_t) (direction * sizeof(tme_uint16_t)); /* do the transfer: */ - mask = TME_M68K_INSN_SPECOP; - if (mask != 0) { - TME_M68K_INSN_CANFAULT; - } for (bit = 1; bit != 0; bit <<= 1) { if (mask & bit) { if (!TME_M68K_SEQUENCE_RESTARTING) { @@ -1997,7 +2128,13 @@ TME_M68K_INSN(tme_m68k_movem_rm16) } /* if this is the predecrement mode, update the address register: */ - if (ea_mode == 4) { + /* "For the MC68020, MC68030, MC68040, and CPU32, if + the addressing register is also moved to memory, the + value written is the initial register value decremented + by the size of the operation. The MC68000 and MC68010 + write the initial register value (not decremented)." */ + if (ea_mode == 4 + && ic->tme_m68k_type < TME_M68K_M68020) { ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3)) = (ic->_tme_m68k_ea_address + sizeof(tme_uint16_t)); @@ -2046,6 +2183,19 @@ TME_M68K_INSN(tme_m68k_movem_mr16) tme_uint16_t mask, bit; unsigned int ea_mode; tme_uint32_t addend; + tme_uint32_t total_size; + /* get the register mask, and figure out the total size + of the transfer: */ + mask = TME_M68K_INSN_SPECOP; + total_size = 0; + if (mask != 0) { + TME_M68K_INSN_CANFAULT; + bit = mask; + do { + total_size += sizeof(tme_uint16_t); + bit &= (bit - 1); + } while (bit != 0); + } /* figure out what direction to move in, and where to start from: */ ea_mode = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3); @@ -2054,10 +2204,6 @@ TME_M68K_INSN(tme_m68k_movem_mr16) addend = (tme_uint32_t) (direction * sizeof(tme_uint16_t)); /* do the transfer: */ - mask = TME_M68K_INSN_SPECOP; - if (mask != 0) { - TME_M68K_INSN_CANFAULT; - } for (bit = 1; bit != 0; bit <<= 1) { if (mask & bit) { tme_m68k_read_memx16(ic); @@ -2083,13 +2229,15 @@ TME_M68K_INSN(tme_m68k_chk16) { if (*((tme_int16_t *) _op0) < 0) { ic->tme_m68k_ireg_ccr |= TME_M68K_FLAG_N; + ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc; ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next; - TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_GROUP2(6)); + TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_CHK)); } if (*((tme_int16_t *) _op0) > *((tme_int16_t *) _op1)) { ic->tme_m68k_ireg_ccr &= ~TME_M68K_FLAG_N; + ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc; ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next; - TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_GROUP2(6)); + TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_CHK)); } TME_M68K_INSN_OK; } @@ -2097,156 +2245,135 @@ TME_M68K_INSN(tme_m68k_chk16) /* cas16: */ TME_M68K_INSN(tme_m68k_cas16) { + struct tme_m68k_rmw rmw; struct tme_m68k_tlb *tlb; int ireg_dc, ireg_du; - int do_write; - tme_uint16_t specopx = ic->_tme_m68k_insn_specop; + tme_uint16_t value_dc, value_du, value_mem; /* start the read/modify/write cycle: */ - tlb = tme_m68k_rmw_start(ic); - if (tlb == NULL) { + rmw.tme_m68k_rmw_addresses[0] = ic->_tme_m68k_ea_address; + rmw.tme_m68k_rmw_address_count = 1; + rmw.tme_m68k_rmw_size = sizeof(tme_uint16_t); + if (tme_m68k_rmw_start(ic, + &rmw)) { TME_M68K_INSN_OK; } - /* read: */ - tme_m68k_read16(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx16, - TME_M68K_BUS_CYCLE_RMW); - - /* modify: */ - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); - tme_m68k_cmp16(ic, &ic->tme_m68k_ireg_uint16(ireg_dc), &ic->tme_m68k_ireg_memx16); + /* get the compare and update registers: */ + ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 3); + ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 6, 3); + + /* if we can do the fast compare-and-exchange: */ + if (!rmw.tme_m68k_rmw_slow_reads[0]) { + + /* get the compare and update values in big-endian byte order: */ + value_dc = ic->tme_m68k_ireg_uint16(ireg_dc << 1); + value_du = ic->tme_m68k_ireg_uint16(ireg_du << 1); + value_dc = tme_htobe_u16(value_dc); + value_du = tme_htobe_u16(value_du); + + /* get this TLB entry: */ + tlb = rmw.tme_m68k_rmw_tlbs[0]; + + /* this TLB entry must allow fast reading and fast writing + to the same memory: */ + assert (tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF + && tlb->tme_m68k_tlb_emulator_off_write == tlb->tme_m68k_tlb_emulator_off_read); + + /* do the compare-and-exchange: */ + value_mem = + tme_memory_atomic_cx16(((tme_shared tme_uint16_t *) + (tlb->tme_m68k_tlb_emulator_off_read + + ic->_tme_m68k_ea_address)), + value_dc, + value_du, + tlb->tme_m68k_tlb_bus_rwlock, + sizeof(tme_uint8_t)); + ic->tme_m68k_ireg_memx16 = tme_betoh_u16(value_mem); + } + + /* compare the compare operand to the effective address operand: */ + tme_m68k_cmp16(ic, &ic->tme_m68k_ireg_uint16(ireg_dc << 1), &ic->tme_m68k_ireg_memx16); - /* write: */ + /* if the comparison succeeded: */ if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) { - ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 6, 3); - ic->tme_m68k_ireg_memx16 = ic->tme_m68k_ireg_uint16(ireg_du); - tme_m68k_write16(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx16, - TME_M68K_BUS_CYCLE_RMW); - } - else { - /* XXX the 68040 always does a write to finish its cycle: */ - do_write = FALSE; - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); - if (do_write) { - tme_m68k_write16(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx16, - TME_M68K_BUS_CYCLE_RMW); - do_write = FALSE; - } - ic->tme_m68k_ireg_uint16(ireg_dc) = ic->tme_m68k_ireg_memx16; + + /* write the update operand to the effective address operand: */ + ic->tme_m68k_ireg_memx16 = ic->tme_m68k_ireg_uint16(ireg_du << 1); } - /* finish the read/modify/write cycle: */ - tme_m68k_rmw_finish(ic, tlb); + /* otherwise, the comparison failed: */ + else { + /* write the effective address operand to the compare operand: */ + ic->tme_m68k_ireg_uint16(ireg_dc << 1) = ic->tme_m68k_ireg_memx16; + } + + /* finish the read/modify/write cycle: */ + tme_m68k_rmw_finish(ic, + &rmw, + (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) != 0); TME_M68K_INSN_OK; } /* cas2_16: */ TME_M68K_INSN(tme_m68k_cas2_16) { - struct tme_m68k_tlb *tlb; - int ireg_dc, ireg_du; - int do_write; - tme_uint16_t specopx = ic->_tme_m68k_insn_specop; - tme_uint16_t specopy = ic->_tme_m68k_insn_specop2; - tme_uint32_t addrx; - tme_uint32_t addry; - - /* get the function code and addresses we'll be dealing with: */ - ic->_tme_m68k_ea_function_code = TME_M68K_FUNCTION_CODE_DATA(ic); - addrx = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0 - + TME_FIELD_EXTRACTU(specopx, 12, 4)); - addry = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0 - + TME_FIELD_EXTRACTU(specopy, 12, 4)); + struct tme_m68k_rmw rmw; + int ireg_dcx, ireg_dux; + int ireg_dcy, ireg_duy; + const tme_uint16_t specopx = TME_M68K_INSN_SPECOP; + const tme_uint16_t specopy = TME_M68K_INSN_OP0(tme_uint16_t); /* start the read/modify/write cycle: */ - tlb = tme_m68k_rmw_start(ic); - if (tlb == NULL) { + ic->_tme_m68k_ea_function_code = TME_M68K_FUNCTION_CODE_DATA(ic); + rmw.tme_m68k_rmw_addresses[0] = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0 + + TME_FIELD_EXTRACTU(specopx, 12, 4)); + rmw.tme_m68k_rmw_addresses[1] = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0 + + TME_FIELD_EXTRACTU(specopy, 12, 4)); + rmw.tme_m68k_rmw_address_count = 2; + rmw.tme_m68k_rmw_size = sizeof(tme_uint16_t); + if (tme_m68k_rmw_start(ic, + &rmw)) { TME_M68K_INSN_OK; } - /* read: */ - ic->_tme_m68k_ea_address = addrx; - tme_m68k_read16(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx16, - TME_M68K_BUS_CYCLE_RMW); - ic->_tme_m68k_ea_address = addry; - tme_m68k_read16(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memy16, - TME_M68K_BUS_CYCLE_RMW); - - /* modify: */ - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); - tme_m68k_cmp16(ic, &ic->tme_m68k_ireg_uint16(ireg_dc), &ic->tme_m68k_ireg_memx16); + /* do the comparisons: */ + ireg_dcx = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); + ireg_dcy = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 0, 3); + tme_m68k_cmp16(ic, + &ic->tme_m68k_ireg_uint16(ireg_dcx << 1), + &ic->tme_m68k_ireg_memx16); if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) { - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 0, 3); - tme_m68k_cmp16(ic, &ic->tme_m68k_ireg_uint16(ireg_dc), &ic->tme_m68k_ireg_memy16); + tme_m68k_cmp16(ic, + &ic->tme_m68k_ireg_uint16(ireg_dcy << 1), + &ic->tme_m68k_ireg_memy16); } - /* write: */ + /* if the comparisons succeeded: */ if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) { - ic->_tme_m68k_ea_address = addrx; - ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 6, 3); - ic->tme_m68k_ireg_memx16 = ic->tme_m68k_ireg_uint16(ireg_du); - tme_m68k_write16(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx16, - TME_M68K_BUS_CYCLE_RMW); - ic->_tme_m68k_ea_address = addry; - ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 6, 3); - ic->tme_m68k_ireg_memy16 = ic->tme_m68k_ireg_uint16(ireg_du); - tme_m68k_write16(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memy16, - TME_M68K_BUS_CYCLE_RMW); - } - else { - /* XXX the 68040 always does a write to finish its cycle: */ - do_write = FALSE; - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); - if (do_write - && ic->tme_m68k_ireg_memx16 != ic->tme_m68k_ireg_uint16(ireg_dc)) { - ic->_tme_m68k_ea_address = addrx; - tme_m68k_write16(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx16, - TME_M68K_BUS_CYCLE_RMW); - do_write = FALSE; - } - ic->tme_m68k_ireg_uint16(ireg_dc) = ic->tme_m68k_ireg_memx16; - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 0, 3); - if (do_write - && ic->tme_m68k_ireg_memy16 != ic->tme_m68k_ireg_uint16(ireg_dc)) { - ic->_tme_m68k_ea_address = addry; - tme_m68k_write16(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memy16, - TME_M68K_BUS_CYCLE_RMW); - do_write = FALSE; - } - ic->tme_m68k_ireg_uint16(ireg_dc) = ic->tme_m68k_ireg_memy16; + + /* write the update operands to the effective address operands: */ + ireg_dux = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 6, 3); + ireg_duy = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 6, 3); + ic->tme_m68k_ireg_memx16 = ic->tme_m68k_ireg_uint16(ireg_dux << 1); + ic->tme_m68k_ireg_memy16 = ic->tme_m68k_ireg_uint16(ireg_duy << 1); } - /* finish the read/modify/write cycle: */ - tme_m68k_rmw_finish(ic, tlb); + /* otherwise, the comparisons failed: */ + else { + + /* write the effective address operands to the compare operands. + "If Dc1 and Dc2 specify the same data register and the comparison + fails, memory operand 1 is stored in the data register." */ + ic->tme_m68k_ireg_uint16(ireg_dcy << 1) = ic->tme_m68k_ireg_memy16; + ic->tme_m68k_ireg_uint16(ireg_dcx << 1) = ic->tme_m68k_ireg_memx16; + } + /* finish the read/modify/write cycle: */ + tme_m68k_rmw_finish(ic, + &rmw, + (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) != 0); TME_M68K_INSN_OK; } @@ -2254,11 +2381,44 @@ TME_M68K_INSN(tme_m68k_cas2_16) TME_M68K_INSN(tme_m68k_moves16) { int ireg; + tme_uint16_t ireg_value; + unsigned int ea_reg; + unsigned int increment; + TME_M68K_INSN_PRIV; + TME_M68K_INSN_CANFAULT; ireg = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 12, 4); + + /* in case we're storing the same address register used in a + postincrement or predecrement EA, save the current value + of the register now: */ + ireg_value = ic->tme_m68k_ireg_uint16(ireg << 1); + + /* we have to handle postincrement and predecrement ourselves: */ + if (!TME_M68K_SEQUENCE_RESTARTING) { + ea_reg = TME_M68K_IREG_A0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3); + increment = TME_M68K_SIZE_16; + if (increment == TME_M68K_SIZE_8 && ea_reg == TME_M68K_IREG_A7) { + increment = TME_M68K_SIZE_16; + } + switch (TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3)) { + case 3: ic->tme_m68k_ireg_uint32(ea_reg) += increment; break; + case 4: ic->_tme_m68k_ea_address = (ic->tme_m68k_ireg_uint32(ea_reg) -= increment); break; + default: break; + } + } + if (TME_M68K_INSN_SPECOP & TME_BIT(11)) { - ic->tme_m68k_ireg_memx16 = ic->tme_m68k_ireg_uint16(ireg << 1); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_memx16 = ireg_value; + ic->_tme_m68k_ea_function_code = ic->tme_m68k_ireg_dfc; + } + tme_m68k_write_memx16(ic); } else { + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->_tme_m68k_ea_function_code = ic->tme_m68k_ireg_sfc; + } + tme_m68k_read_memx16(ic); if (ireg >= TME_M68K_IREG_A0) { ic->tme_m68k_ireg_uint32(ireg) = TME_EXT_S16_U32((tme_int16_t) ic->tme_m68k_ireg_memx16); @@ -2603,17 +2763,17 @@ TME_M68K_INSN(tme_m68k_addx32) if (memory) { TME_M68K_INSN_CANFAULT; if (!TME_M68K_SEQUENCE_RESTARTING) { - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; - ic->_tme_m68k_ea_function_code = function_code; - ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); - } - tme_m68k_read_memx32(ic); - if (!TME_M68K_SEQUENCE_RESTARTING) { ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) -= ireg_src_adjust; ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src); } tme_m68k_read_mem32(ic, TME_M68K_IREG_MEMY32); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; + ic->_tme_m68k_ea_function_code = function_code; + ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); + } + tme_m68k_read_memx32(ic); op1 = ic->tme_m68k_ireg_memx32; op0 = ic->tme_m68k_ireg_memy32; } @@ -2666,17 +2826,17 @@ TME_M68K_INSN(tme_m68k_subx32) if (memory) { TME_M68K_INSN_CANFAULT; if (!TME_M68K_SEQUENCE_RESTARTING) { - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; - ic->_tme_m68k_ea_function_code = function_code; - ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); - } - tme_m68k_read_memx32(ic); - if (!TME_M68K_SEQUENCE_RESTARTING) { ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) -= ireg_src_adjust; ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src); } tme_m68k_read_mem32(ic, TME_M68K_IREG_MEMY32); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust; + ic->_tme_m68k_ea_function_code = function_code; + ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); + } + tme_m68k_read_memx32(ic); op1 = ic->tme_m68k_ireg_memx32; op0 = ic->tme_m68k_ireg_memy32; } @@ -2728,16 +2888,16 @@ TME_M68K_INSN(tme_m68k_cmpm32) if (!TME_M68K_SEQUENCE_RESTARTING) { ic->_tme_m68k_ea_function_code = function_code; - ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) += ireg_dst_adjust; - } - tme_m68k_read_memx32(ic); - if (!TME_M68K_SEQUENCE_RESTARTING) { - ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src); ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) += ireg_src_adjust; } tme_m68k_read_mem32(ic, TME_M68K_IREG_MEMY32); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->_tme_m68k_ea_function_code = function_code; + ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst); + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) += ireg_dst_adjust; + } + tme_m68k_read_memx32(ic); op1 = ic->tme_m68k_ireg_memx32; op0 = ic->tme_m68k_ireg_memy32; @@ -2755,6 +2915,46 @@ TME_M68K_INSN(tme_m68k_cmpm32) TME_M68K_INSN_OK; } +/* a move of an address register to a predecrement or + postincrement EA with that same address register, must + store the original value of the address register. since the + predecrement and postincrement code in the executer updates + the address register before the move has happened, we wrap + the normal move function in this one, that gives an op1 + argument that is the original value of the address register: */ +TME_M68K_INSN(tme_m68k_move_srpd32) +{ + /* NB: both this function and tme_m68k_move32() + get the source operand as _op1, and the destination + operand as _op0: */ + if (!TME_M68K_SEQUENCE_RESTARTING) { + *((tme_uint32_t *) _op0) + = (*((tme_uint32_t *) _op1) + + sizeof(tme_uint32_t)); + } + tme_m68k_move32(ic, _op0, _op0); +} + +/* a move of an address register to a predecrement or + postincrement EA with that same address register, must + store the original value of the address register. since the + predecrement and postincrement code in the executer updates + the address register before the move has happened, we wrap + the normal move function in this one, that gives an op1 + argument that is the original value of the address register: */ +TME_M68K_INSN(tme_m68k_move_srpi32) +{ + /* NB: both this function and tme_m68k_move32() + get the source operand as _op1, and the destination + operand as _op0: */ + if (!TME_M68K_SEQUENCE_RESTARTING) { + *((tme_uint32_t *) _op0) + = (*((tme_uint32_t *) _op1) + - sizeof(tme_uint32_t)); + } + tme_m68k_move32(ic, _op0, _op0); +} + /* the suba function on a 32-byte EA: */ TME_M68K_INSN(tme_m68k_suba32) { @@ -2944,13 +3144,23 @@ TME_M68K_INSN(tme_m68k_asr32) if (count > 0) { if (63 > SHIFTMAX_INT32_T && count > 32) { - res = 0; + res = 0 - (res < 0); } +#ifdef SHIFTSIGNED_INT32_T res >>= (count - 1); +#else /* !SHIFTSIGNED_INT32_T */ + for (; --count > 0; ) { + res = (res & ~((tme_int32_t) 1)) / 2; + } +#endif /* !SHIFTSIGNED_INT32_T */ flags = (res & 1); flags *= TME_M68K_FLAG_C; flags |= (flags * TME_M68K_FLAG_X); +#ifdef SHIFTSIGNED_INT32_T res >>= 1; +#else /* !SHIFTSIGNED_INT32_T */ + res = (res & ~((tme_int32_t) 1)) / 2; +#endif /* !SHIFTSIGNED_INT32_T */ } /* store the result: */ @@ -3298,6 +3508,19 @@ TME_M68K_INSN(tme_m68k_movem_rm32) tme_uint16_t mask, bit; unsigned int ea_mode; tme_uint32_t addend; + tme_uint32_t total_size; + /* get the register mask, and figure out the total size + of the transfer: */ + mask = TME_M68K_INSN_SPECOP; + total_size = 0; + if (mask != 0) { + TME_M68K_INSN_CANFAULT; + bit = mask; + do { + total_size += sizeof(tme_uint32_t); + bit &= (bit - 1); + } while (bit != 0); + } /* figure out what direction to move in, and where to start from: */ ea_mode = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3); @@ -3307,16 +3530,25 @@ TME_M68K_INSN(tme_m68k_movem_rm32) direction = -1; ireg = TME_M68K_IREG_A7; if (!TME_M68K_SEQUENCE_RESTARTING) { + + /* "For the MC68020, MC68030, MC68040, and CPU32, if + the addressing register is also moved to memory, the + value written is the initial register value decremented + by the size of the operation. The MC68000 and MC68010 + write the initial register value (not decremented)." */ + if (ic->tme_m68k_type >= TME_M68K_M68020) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3)) + = (ic->_tme_m68k_ea_address - total_size); + } + + /* predecrement the effective address for the first transfer: */ ic->_tme_m68k_ea_address -= sizeof(tme_uint32_t); } } addend = (tme_uint32_t) (direction * sizeof(tme_uint32_t)); /* do the transfer: */ - mask = TME_M68K_INSN_SPECOP; - if (mask != 0) { - TME_M68K_INSN_CANFAULT; - } for (bit = 1; bit != 0; bit <<= 1) { if (mask & bit) { if (!TME_M68K_SEQUENCE_RESTARTING) { @@ -3331,7 +3563,13 @@ TME_M68K_INSN(tme_m68k_movem_rm32) } /* if this is the predecrement mode, update the address register: */ - if (ea_mode == 4) { + /* "For the MC68020, MC68030, MC68040, and CPU32, if + the addressing register is also moved to memory, the + value written is the initial register value decremented + by the size of the operation. The MC68000 and MC68010 + write the initial register value (not decremented)." */ + if (ea_mode == 4 + && ic->tme_m68k_type < TME_M68K_M68020) { ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3)) = (ic->_tme_m68k_ea_address + sizeof(tme_uint32_t)); @@ -3398,6 +3636,19 @@ TME_M68K_INSN(tme_m68k_movem_mr32) tme_uint16_t mask, bit; unsigned int ea_mode; tme_uint32_t addend; + tme_uint32_t total_size; + /* get the register mask, and figure out the total size + of the transfer: */ + mask = TME_M68K_INSN_SPECOP; + total_size = 0; + if (mask != 0) { + TME_M68K_INSN_CANFAULT; + bit = mask; + do { + total_size += sizeof(tme_uint32_t); + bit &= (bit - 1); + } while (bit != 0); + } /* figure out what direction to move in, and where to start from: */ ea_mode = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3); @@ -3406,10 +3657,6 @@ TME_M68K_INSN(tme_m68k_movem_mr32) addend = (tme_uint32_t) (direction * sizeof(tme_uint32_t)); /* do the transfer: */ - mask = TME_M68K_INSN_SPECOP; - if (mask != 0) { - TME_M68K_INSN_CANFAULT; - } for (bit = 1; bit != 0; bit <<= 1) { if (mask & bit) { tme_m68k_read_memx32(ic); @@ -3435,13 +3682,15 @@ TME_M68K_INSN(tme_m68k_chk32) { if (*((tme_int32_t *) _op0) < 0) { ic->tme_m68k_ireg_ccr |= TME_M68K_FLAG_N; + ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc; ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next; - TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_GROUP2(6)); + TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_CHK)); } if (*((tme_int32_t *) _op0) > *((tme_int32_t *) _op1)) { ic->tme_m68k_ireg_ccr &= ~TME_M68K_FLAG_N; + ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc; ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next; - TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_GROUP2(6)); + TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_CHK)); } TME_M68K_INSN_OK; } @@ -3449,156 +3698,135 @@ TME_M68K_INSN(tme_m68k_chk32) /* cas32: */ TME_M68K_INSN(tme_m68k_cas32) { + struct tme_m68k_rmw rmw; struct tme_m68k_tlb *tlb; int ireg_dc, ireg_du; - int do_write; - tme_uint16_t specopx = ic->_tme_m68k_insn_specop; + tme_uint32_t value_dc, value_du, value_mem; /* start the read/modify/write cycle: */ - tlb = tme_m68k_rmw_start(ic); - if (tlb == NULL) { + rmw.tme_m68k_rmw_addresses[0] = ic->_tme_m68k_ea_address; + rmw.tme_m68k_rmw_address_count = 1; + rmw.tme_m68k_rmw_size = sizeof(tme_uint32_t); + if (tme_m68k_rmw_start(ic, + &rmw)) { TME_M68K_INSN_OK; } - /* read: */ - tme_m68k_read32(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx32, - TME_M68K_BUS_CYCLE_RMW); + /* get the compare and update registers: */ + ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 3); + ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 6, 3); + + /* if we can do the fast compare-and-exchange: */ + if (!rmw.tme_m68k_rmw_slow_reads[0]) { + + /* get the compare and update values in big-endian byte order: */ + value_dc = ic->tme_m68k_ireg_uint32(ireg_dc); + value_du = ic->tme_m68k_ireg_uint32(ireg_du); + value_dc = tme_htobe_u32(value_dc); + value_du = tme_htobe_u32(value_du); + + /* get this TLB entry: */ + tlb = rmw.tme_m68k_rmw_tlbs[0]; + + /* this TLB entry must allow fast reading and fast writing + to the same memory: */ + assert (tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF + && tlb->tme_m68k_tlb_emulator_off_write == tlb->tme_m68k_tlb_emulator_off_read); + + /* do the compare-and-exchange: */ + value_mem = + tme_memory_atomic_cx32(((tme_shared tme_uint32_t *) + (tlb->tme_m68k_tlb_emulator_off_read + + ic->_tme_m68k_ea_address)), + value_dc, + value_du, + tlb->tme_m68k_tlb_bus_rwlock, + sizeof(tme_uint8_t)); + ic->tme_m68k_ireg_memx32 = tme_betoh_u32(value_mem); + } - /* modify: */ - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); + /* compare the compare operand to the effective address operand: */ tme_m68k_cmp32(ic, &ic->tme_m68k_ireg_uint32(ireg_dc), &ic->tme_m68k_ireg_memx32); - /* write: */ + /* if the comparison succeeded: */ if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) { - ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 6, 3); + + /* write the update operand to the effective address operand: */ ic->tme_m68k_ireg_memx32 = ic->tme_m68k_ireg_uint32(ireg_du); - tme_m68k_write32(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx32, - TME_M68K_BUS_CYCLE_RMW); - } - else { - /* XXX the 68040 always does a write to finish its cycle: */ - do_write = FALSE; - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); - if (do_write) { - tme_m68k_write32(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx32, - TME_M68K_BUS_CYCLE_RMW); - do_write = FALSE; - } + } + + /* otherwise, the comparison failed: */ + else { + + /* write the effective address operand to the compare operand: */ ic->tme_m68k_ireg_uint32(ireg_dc) = ic->tme_m68k_ireg_memx32; } /* finish the read/modify/write cycle: */ - tme_m68k_rmw_finish(ic, tlb); - + tme_m68k_rmw_finish(ic, + &rmw, + (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) != 0); TME_M68K_INSN_OK; } /* cas2_32: */ TME_M68K_INSN(tme_m68k_cas2_32) { - struct tme_m68k_tlb *tlb; - int ireg_dc, ireg_du; - int do_write; - tme_uint16_t specopx = ic->_tme_m68k_insn_specop; - tme_uint16_t specopy = ic->_tme_m68k_insn_specop2; - tme_uint32_t addrx; - tme_uint32_t addry; - - /* get the function code and addresses we'll be dealing with: */ - ic->_tme_m68k_ea_function_code = TME_M68K_FUNCTION_CODE_DATA(ic); - addrx = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0 - + TME_FIELD_EXTRACTU(specopx, 12, 4)); - addry = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0 - + TME_FIELD_EXTRACTU(specopy, 12, 4)); + struct tme_m68k_rmw rmw; + int ireg_dcx, ireg_dux; + int ireg_dcy, ireg_duy; + const tme_uint16_t specopx = TME_M68K_INSN_SPECOP; + const tme_uint16_t specopy = TME_M68K_INSN_OP0(tme_uint16_t); /* start the read/modify/write cycle: */ - tlb = tme_m68k_rmw_start(ic); - if (tlb == NULL) { + ic->_tme_m68k_ea_function_code = TME_M68K_FUNCTION_CODE_DATA(ic); + rmw.tme_m68k_rmw_addresses[0] = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0 + + TME_FIELD_EXTRACTU(specopx, 12, 4)); + rmw.tme_m68k_rmw_addresses[1] = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0 + + TME_FIELD_EXTRACTU(specopy, 12, 4)); + rmw.tme_m68k_rmw_address_count = 2; + rmw.tme_m68k_rmw_size = sizeof(tme_uint32_t); + if (tme_m68k_rmw_start(ic, + &rmw)) { TME_M68K_INSN_OK; } - /* read: */ - ic->_tme_m68k_ea_address = addrx; - tme_m68k_read32(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx32, - TME_M68K_BUS_CYCLE_RMW); - ic->_tme_m68k_ea_address = addry; - tme_m68k_read32(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memy32, - TME_M68K_BUS_CYCLE_RMW); - - /* modify: */ - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); - tme_m68k_cmp32(ic, &ic->tme_m68k_ireg_uint32(ireg_dc), &ic->tme_m68k_ireg_memx32); + /* do the comparisons: */ + ireg_dcx = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); + ireg_dcy = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 0, 3); + tme_m68k_cmp32(ic, + &ic->tme_m68k_ireg_uint32(ireg_dcx), + &ic->tme_m68k_ireg_memx32); if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) { - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 0, 3); - tme_m68k_cmp32(ic, &ic->tme_m68k_ireg_uint32(ireg_dc), &ic->tme_m68k_ireg_memy32); + tme_m68k_cmp32(ic, + &ic->tme_m68k_ireg_uint32(ireg_dcy), + &ic->tme_m68k_ireg_memy32); } - /* write: */ + /* if the comparisons succeeded: */ if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) { - ic->_tme_m68k_ea_address = addrx; - ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 6, 3); - ic->tme_m68k_ireg_memx32 = ic->tme_m68k_ireg_uint32(ireg_du); - tme_m68k_write32(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx32, - TME_M68K_BUS_CYCLE_RMW); - ic->_tme_m68k_ea_address = addry; - ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 6, 3); - ic->tme_m68k_ireg_memy32 = ic->tme_m68k_ireg_uint32(ireg_du); - tme_m68k_write32(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memy32, - TME_M68K_BUS_CYCLE_RMW); - } - else { - /* XXX the 68040 always does a write to finish its cycle: */ - do_write = FALSE; - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3); - if (do_write - && ic->tme_m68k_ireg_memx32 != ic->tme_m68k_ireg_uint32(ireg_dc)) { - ic->_tme_m68k_ea_address = addrx; - tme_m68k_write32(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memx32, - TME_M68K_BUS_CYCLE_RMW); - do_write = FALSE; - } - ic->tme_m68k_ireg_uint32(ireg_dc) = ic->tme_m68k_ireg_memx32; - ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 0, 3); - if (do_write - && ic->tme_m68k_ireg_memy32 != ic->tme_m68k_ireg_uint32(ireg_dc)) { - ic->_tme_m68k_ea_address = addry; - tme_m68k_write32(ic, tlb, - &ic->_tme_m68k_ea_function_code, - &ic->_tme_m68k_ea_address, - &ic->tme_m68k_ireg_memy32, - TME_M68K_BUS_CYCLE_RMW); - do_write = FALSE; - } - ic->tme_m68k_ireg_uint32(ireg_dc) = ic->tme_m68k_ireg_memy32; + + /* write the update operands to the effective address operands: */ + ireg_dux = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 6, 3); + ireg_duy = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 6, 3); + ic->tme_m68k_ireg_memx32 = ic->tme_m68k_ireg_uint32(ireg_dux); + ic->tme_m68k_ireg_memy32 = ic->tme_m68k_ireg_uint32(ireg_duy); } - /* finish the read/modify/write cycle: */ - tme_m68k_rmw_finish(ic, tlb); + /* otherwise, the comparisons failed: */ + else { + + /* write the effective address operands to the compare operands. + "If Dc1 and Dc2 specify the same data register and the comparison + fails, memory operand 1 is stored in the data register." */ + ic->tme_m68k_ireg_uint32(ireg_dcy) = ic->tme_m68k_ireg_memy32; + ic->tme_m68k_ireg_uint32(ireg_dcx) = ic->tme_m68k_ireg_memx32; + } + /* finish the read/modify/write cycle: */ + tme_m68k_rmw_finish(ic, + &rmw, + (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) != 0); TME_M68K_INSN_OK; } @@ -3606,11 +3834,44 @@ TME_M68K_INSN(tme_m68k_cas2_32) TME_M68K_INSN(tme_m68k_moves32) { int ireg; + tme_uint32_t ireg_value; + unsigned int ea_reg; + unsigned int increment; + TME_M68K_INSN_PRIV; + TME_M68K_INSN_CANFAULT; ireg = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 12, 4); + + /* in case we're storing the same address register used in a + postincrement or predecrement EA, save the current value + of the register now: */ + ireg_value = ic->tme_m68k_ireg_uint32(ireg); + + /* we have to handle postincrement and predecrement ourselves: */ + if (!TME_M68K_SEQUENCE_RESTARTING) { + ea_reg = TME_M68K_IREG_A0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3); + increment = TME_M68K_SIZE_32; + if (increment == TME_M68K_SIZE_8 && ea_reg == TME_M68K_IREG_A7) { + increment = TME_M68K_SIZE_16; + } + switch (TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3)) { + case 3: ic->tme_m68k_ireg_uint32(ea_reg) += increment; break; + case 4: ic->_tme_m68k_ea_address = (ic->tme_m68k_ireg_uint32(ea_reg) -= increment); break; + default: break; + } + } + if (TME_M68K_INSN_SPECOP & TME_BIT(11)) { - ic->tme_m68k_ireg_memx32 = ic->tme_m68k_ireg_uint32(ireg); + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_memx32 = ireg_value; + ic->_tme_m68k_ea_function_code = ic->tme_m68k_ireg_dfc; + } + tme_m68k_write_memx32(ic); } else { + if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->_tme_m68k_ea_function_code = ic->tme_m68k_ireg_sfc; + } + tme_m68k_read_memx32(ic); ic->tme_m68k_ireg_uint32(ireg) = ic->tme_m68k_ireg_memx32; } TME_M68K_INSN_OK; @@ -3623,22 +3884,34 @@ tme_m68k_read_memx8(struct tme_m68k *ic) unsigned int function_code = ic->_tme_m68k_ea_function_code; tme_uint32_t linear_address = ic->_tme_m68k_ea_address; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address); + tme_uint8_t mem_value; + const tme_shared tme_uint8_t *mem; - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_read pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ + + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast reads: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING && TME_M68K_TLB_OK_FAST_READ(tlb, function_code, linear_address, linear_address))) { - /* for an 8-bit transfer we can always do a simple - assignment. the rdlock is unnecessary, since we assume - that 8-bit accesses are always atomic: */ - ic->tme_m68k_ireg_memx8 = *((tme_uint8_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address)); + /* make the emulator memory pointer: */ + mem = (const tme_shared tme_uint8_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address); + + /* do the 8-bit bus read: */ + mem_value = tme_memory_bus_read8(mem, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* put the value read: */ + ic->tme_m68k_ireg_memx8 = mem_value; + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -3651,6 +3924,9 @@ tme_m68k_read_memx8(struct tme_m68k *ic) TME_M68K_BUS_CYCLE_NORMAL); } + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); + /* log the value read: */ tme_m68k_verify_mem8(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx8, TME_BUS_CYCLE_READ); tme_m68k_log(ic, 1000, TME_OK, @@ -3668,22 +3944,34 @@ tme_m68k_read_mem8(struct tme_m68k *ic, unsigned int function_code = ic->_tme_m68k_ea_function_code; tme_uint32_t linear_address = ic->_tme_m68k_ea_address; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address); + tme_uint8_t mem_value; + const tme_shared tme_uint8_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ + + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_read pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast reads: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING && TME_M68K_TLB_OK_FAST_READ(tlb, function_code, linear_address, linear_address))) { - /* for an 8-bit transfer we can always do a simple - assignment. the rdlock is unnecessary, since we assume - that 8-bit accesses are always atomic: */ - ic->tme_m68k_ireg_uint8(ireg) = *((tme_uint8_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address)); + /* make the emulator memory pointer: */ + mem = (const tme_shared tme_uint8_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address); + + /* do the 8-bit bus read: */ + mem_value = tme_memory_bus_read8(mem, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* put the value read: */ + ic->tme_m68k_ireg_uint8(ireg) = mem_value; + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -3696,6 +3984,9 @@ tme_m68k_read_mem8(struct tme_m68k *ic, TME_M68K_BUS_CYCLE_NORMAL); } + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); + /* log the value read: */ tme_m68k_verify_mem8(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint8(ireg), TME_BUS_CYCLE_READ); tme_m68k_log(ic, 1000, TME_OK, @@ -3713,6 +4004,12 @@ tme_m68k_write_memx8(struct tme_m68k *ic unsigned int function_code = ic->_tme_m68k_ea_function_code; tme_uint32_t linear_address = ic->_tme_m68k_ea_address; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address); + tme_uint8_t mem_value; + tme_shared tme_uint8_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ /* log the value written: */ tme_m68k_verify_mem8(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx8, TME_BUS_CYCLE_WRITE); @@ -3723,21 +4020,27 @@ tme_m68k_write_memx8(struct tme_m68k *ic ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx8)); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_write pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast writes: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING && TME_M68K_TLB_OK_FAST_WRITE(tlb, function_code, linear_address, linear_address))) { - /* for an 8-bit transfer we can always do a simple - assignment. the wrlock is unnecessary, since we assume - that 8-bit accesses are always atomic: */ - *((tme_uint8_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address)) = ic->tme_m68k_ireg_memx8; + /* make the emulator memory pointer: */ + mem = (tme_shared tme_uint8_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address); + + /* get the value to write: */ + mem_value = ic->tme_m68k_ireg_memx8; + + /* do the 8-bit bus write: */ + tme_memory_bus_write8(mem, mem_value, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -3749,6 +4052,9 @@ tme_m68k_write_memx8(struct tme_m68k *ic &ic->tme_m68k_ireg_memx8, TME_M68K_BUS_CYCLE_NORMAL); } + + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); } /* this writes a 8-bit mem value: */ @@ -3758,6 +4064,12 @@ tme_m68k_write_mem8(struct tme_m68k *ic, unsigned int function_code = ic->_tme_m68k_ea_function_code; tme_uint32_t linear_address = ic->_tme_m68k_ea_address; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address); + tme_uint8_t mem_value; + tme_shared tme_uint8_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ /* log the value written: */ tme_m68k_verify_mem8(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint8(ireg), TME_BUS_CYCLE_WRITE); @@ -3768,21 +4080,27 @@ tme_m68k_write_mem8(struct tme_m68k *ic, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint8(ireg))); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_write pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast writes: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING && TME_M68K_TLB_OK_FAST_WRITE(tlb, function_code, linear_address, linear_address))) { - /* for an 8-bit transfer we can always do a simple - assignment. the wrlock is unnecessary, since we assume - that 8-bit accesses are always atomic: */ - *((tme_uint8_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address)) = ic->tme_m68k_ireg_uint8(ireg); + /* make the emulator memory pointer: */ + mem = (tme_shared tme_uint8_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address); + + /* get the value to write: */ + mem_value = ic->tme_m68k_ireg_uint8(ireg); + + /* do the 8-bit bus write: */ + tme_memory_bus_write8(mem, mem_value, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -3794,6 +4112,9 @@ tme_m68k_write_mem8(struct tme_m68k *ic, &ic->tme_m68k_ireg_uint8(ireg), TME_M68K_BUS_CYCLE_NORMAL); } + + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); } /* this reads a 16-bit memx value: */ @@ -3804,30 +4125,35 @@ tme_m68k_read_memx16(struct tme_m68k *ic tme_uint32_t linear_address_first = ic->_tme_m68k_ea_address; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint16_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint16_t mem_value; + const tme_shared tme_uint16_t *mem; - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_read pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ + + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast reads: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_READ(tlb, function_code, linear_address_first, linear_address_last))) { - /* for a 16-bit transfer we can always do a simple - assignment - we tested that the linear address - is 16-bit aligned, which, since the TLB emulator - offset is guaranteed to be 32-bit aligned, guarantees - that the final emulator address is 16-bit aligned. - - we need the rdlock if we're on an architecture - where an aligned access may not be atomic: */ - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - ic->tme_m68k_ireg_memx16 = tme_betoh_u16(*((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); + /* make the emulator memory pointer: */ + mem = (const tme_shared tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first); + + /* do the 16-bit bus read: */ + mem_value = tme_memory_bus_read16(mem, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* put the value read, in host byte order: */ + ic->tme_m68k_ireg_memx16 = tme_betoh_u16(mem_value); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -3840,6 +4166,9 @@ tme_m68k_read_memx16(struct tme_m68k *ic TME_M68K_BUS_CYCLE_NORMAL); } + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); + /* log the value read: */ tme_m68k_verify_mem16(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx16, TME_BUS_CYCLE_READ); tme_m68k_log(ic, 1000, TME_OK, @@ -3858,30 +4187,35 @@ tme_m68k_read_mem16(struct tme_m68k *ic, tme_uint32_t linear_address_first = ic->_tme_m68k_ea_address; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint16_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint16_t mem_value; + const tme_shared tme_uint16_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ + + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_read pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast reads: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_READ(tlb, function_code, linear_address_first, linear_address_last))) { - /* for a 16-bit transfer we can always do a simple - assignment - we tested that the linear address - is 16-bit aligned, which, since the TLB emulator - offset is guaranteed to be 32-bit aligned, guarantees - that the final emulator address is 16-bit aligned. - - we need the rdlock if we're on an architecture - where an aligned access may not be atomic: */ - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - ic->tme_m68k_ireg_uint16(ireg) = tme_betoh_u16(*((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); + /* make the emulator memory pointer: */ + mem = (const tme_shared tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first); + + /* do the 16-bit bus read: */ + mem_value = tme_memory_bus_read16(mem, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* put the value read, in host byte order: */ + ic->tme_m68k_ireg_uint16(ireg) = tme_betoh_u16(mem_value); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -3894,6 +4228,9 @@ tme_m68k_read_mem16(struct tme_m68k *ic, TME_M68K_BUS_CYCLE_NORMAL); } + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); + /* log the value read: */ tme_m68k_verify_mem16(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint16(ireg), TME_BUS_CYCLE_READ); tme_m68k_log(ic, 1000, TME_OK, @@ -3911,54 +4248,119 @@ tme_m68k_fetch16(struct tme_m68k *ic, tm unsigned int function_code = TME_M68K_FUNCTION_CODE_PROGRAM(ic); tme_uint32_t linear_address_first = pc; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint16_t) - 1; - struct tme_m68k_tlb *tlb = TME_ATOMIC_READ(struct tme_m68k_tlb *, ic->_tme_m68k_itlb); - unsigned int insn_buffer_off = TME_ALIGN(ic->_tme_m68k_insn_buffer_off, sizeof(tme_uint16_t)); + struct tme_m68k_tlb *tlb = tme_memory_atomic_pointer_read(struct tme_m68k_tlb *, ic->_tme_m68k_itlb, &ic->_tme_m68k_tlbs_rwlock); + tme_uint16_t mem_value; + const tme_shared tme_uint16_t *mem; + unsigned int fetch_slow_next = ic->_tme_m68k_insn_fetch_slow_next; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ + + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if this fetch was done by the fast executor: */ + if (__tme_predict_true(fetch_slow_next < ic->_tme_m68k_insn_fetch_slow_count_fast)) { + + /* the entire fetch must be in the instruction buffer, and + we must be restarting: */ + assert ((fetch_slow_next + sizeof(tme_uint16_t)) + <= ic->_tme_m68k_insn_fetch_slow_count_fast); + assert (TME_M68K_SEQUENCE_RESTARTING); + mem_value = tme_memory_read16(((tme_uint16_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)), sizeof(tme_uint16_t)); + } + + /* otherwise, this fetch was not done by the fast executor: */ + else { + + /* if we're restarting, but the offset in the instruction buffer + to fetch into is at the instruction buffer total, this must be + a fake fault caused by the fast executor. we confirm this by + checking that this transfer "caused" the fault, and that this + transfer will be the first slow one after any fast fetches. + in this case, we can cancel the restart for now: */ + if (TME_M68K_SEQUENCE_RESTARTING + && (fetch_slow_next + == ic->_tme_m68k_insn_fetch_slow_count_total)) { + assert ((ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next + == ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted) + && (fetch_slow_next + == ic->_tme_m68k_insn_fetch_slow_count_fast)); + ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted--; + } - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_read pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ - if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) - && TME_M68K_TLB_OK_FAST_READ(tlb, - function_code, - linear_address_first, - linear_address_last))) { + /* if we're not restarting: */ + if (!TME_M68K_SEQUENCE_RESTARTING) { - /* for a 16-bit transfer we can always do a simple - assignment - we tested that the linear address - is 16-bit aligned, which, since the TLB emulator - offset is guaranteed to be 32-bit aligned, guarantees - that the final emulator address is 16-bit aligned. - - we need the rdlock if we're on an architecture - where an aligned access may not be atomic: */ - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint16_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]) = tme_betoh_u16(*((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - TME_M68K_SEQUENCE_TRANSFER_STEP; - } + /* we advance the instruction buffer total *before* we do + what may be a slow fetch, because we may transfer a few + bytes and then fault. without this, those few bytes + would not get saved in the exception stack frame and + restored later before the continuation of the fetch: */ + ic->_tme_m68k_insn_fetch_slow_count_total += sizeof(tme_uint16_t); + } + + /* make sure that if this is a new transfer or if this + transfer faulted, that we're fetching for the current + last positions in the instruction buffer: */ + assert ((ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next + < ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted) + || ((fetch_slow_next + sizeof(tme_uint16_t)) + == ic->_tme_m68k_insn_fetch_slow_count_total)); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast reads: */ + if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING + && ((sizeof(tme_uint16_t) - 1) & linear_address_first) == 0 + && TME_M68K_TLB_OK_FAST_READ(tlb, + function_code, + linear_address_first, + linear_address_last))) { + + /* make the emulator memory pointer: */ + mem = (const tme_shared tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first); + + /* do the 16-bit bus read: */ + mem_value = tme_memory_bus_read16(mem, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint16_t), sizeof(tme_uint32_t)); + + /* put the value read, in host byte order: */ + mem_value = tme_betoh_u16(mem_value); + tme_memory_write16(((tme_uint16_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)), mem_value, sizeof(tme_uint16_t)); - /* otherwise, do the bus cycles the slow way: */ - else { - tme_m68k_read16(ic, tlb, - &function_code, - &pc, - ((tme_uint16_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]), - TME_M68K_BUS_CYCLE_FETCH); + /* step the transfer count: */ + TME_M68K_SEQUENCE_TRANSFER_STEP; + } + + /* otherwise, do the bus cycles the slow way: */ + else { + tme_m68k_read16(ic, tlb, + &function_code, + &pc, + ((tme_uint16_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)), + TME_M68K_BUS_CYCLE_FETCH); + mem_value = tme_memory_read16(((tme_uint16_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)), sizeof(tme_uint16_t)); + } } + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); + /* log the value read: */ - tme_m68k_verify_mem16(ic, function_code, pc, *((tme_uint16_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]), TME_BUS_CYCLE_READ); + tme_m68k_verify_mem16(ic, function_code, pc, *((tme_uint16_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)), TME_BUS_CYCLE_READ); tme_m68k_log(ic, 1000, TME_OK, (TME_M68K_LOG_HANDLE(ic), _("fetch16\t%d:0x%08x:\t0x%04x"), function_code, pc, - *((tme_uint16_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]))); - ic->_tme_m68k_insn_buffer_off = insn_buffer_off + sizeof(tme_uint16_t); - return(*((tme_uint16_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off])); + *((tme_uint16_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)))); + + /* advance the offset in the instruction buffer for the next slow fetch: */ + fetch_slow_next += sizeof(tme_uint16_t); + ic->_tme_m68k_insn_fetch_slow_next = fetch_slow_next; + + /* return the fetched value: */ + return(mem_value); } /* this reads a 16-bit stack value: */ @@ -3969,30 +4371,35 @@ tme_m68k_pop16(struct tme_m68k *ic, tme_ tme_uint32_t linear_address_first = ic->tme_m68k_ireg_a7; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint16_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint16_t mem_value; + const tme_shared tme_uint16_t *mem; - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_read pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ + + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast reads: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_READ(tlb, function_code, linear_address_first, linear_address_last))) { - /* for a 16-bit transfer we can always do a simple - assignment - we tested that the linear address - is 16-bit aligned, which, since the TLB emulator - offset is guaranteed to be 32-bit aligned, guarantees - that the final emulator address is 16-bit aligned. - - we need the rdlock if we're on an architecture - where an aligned access may not be atomic: */ - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - *_value = tme_betoh_u16(*((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); + /* make the emulator memory pointer: */ + mem = (const tme_shared tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first); + + /* do the 16-bit bus read: */ + mem_value = tme_memory_bus_read16(mem, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* put the value read, in host byte order: */ + *_value = tme_betoh_u16(mem_value); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -4005,6 +4412,9 @@ tme_m68k_pop16(struct tme_m68k *ic, tme_ TME_M68K_BUS_CYCLE_NORMAL); } + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); + /* log the value read: */ tme_m68k_verify_mem16(ic, function_code, ic->tme_m68k_ireg_a7, *_value, TME_BUS_CYCLE_READ); tme_m68k_log(ic, 1000, TME_OK, @@ -4026,6 +4436,12 @@ tme_m68k_write_memx16(struct tme_m68k *i tme_uint32_t linear_address_first = ic->_tme_m68k_ea_address; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint16_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint16_t mem_value; + tme_shared tme_uint16_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ /* log the value written: */ tme_m68k_verify_mem16(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx16, TME_BUS_CYCLE_WRITE); @@ -4036,29 +4452,28 @@ tme_m68k_write_memx16(struct tme_m68k *i ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx16)); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_write pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast writes: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_WRITE(tlb, function_code, linear_address_first, linear_address_last))) { - /* for a 16-bit transfer we can always do a simple - assignment - we tested that the linear address - is 16-bit aligned, which, since the TLB emulator - offset is guaranteed to be 32-bit aligned, guarantees - that the final emulator address is 16-bit aligned. - - we need the wrlock if we're on an architecture - where an aligned access may not be atomic: */ - tme_memory_aligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u16(ic->tme_m68k_ireg_memx16); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); + /* make the emulator memory pointer: */ + mem = (tme_shared tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first); + + /* get the value to write, in big-endian byte order: */ + mem_value = tme_htobe_u16(ic->tme_m68k_ireg_memx16); + + /* do the 16-bit bus write: */ + tme_memory_bus_write16(mem, mem_value, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -4070,6 +4485,9 @@ tme_m68k_write_memx16(struct tme_m68k *i &ic->tme_m68k_ireg_memx16, TME_M68K_BUS_CYCLE_NORMAL); } + + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); } /* this writes a 16-bit mem value: */ @@ -4080,6 +4498,12 @@ tme_m68k_write_mem16(struct tme_m68k *ic tme_uint32_t linear_address_first = ic->_tme_m68k_ea_address; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint16_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint16_t mem_value; + tme_shared tme_uint16_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ /* log the value written: */ tme_m68k_verify_mem16(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint16(ireg), TME_BUS_CYCLE_WRITE); @@ -4090,29 +4514,28 @@ tme_m68k_write_mem16(struct tme_m68k *ic ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint16(ireg))); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_write pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast writes: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_WRITE(tlb, function_code, linear_address_first, linear_address_last))) { - /* for a 16-bit transfer we can always do a simple - assignment - we tested that the linear address - is 16-bit aligned, which, since the TLB emulator - offset is guaranteed to be 32-bit aligned, guarantees - that the final emulator address is 16-bit aligned. - - we need the wrlock if we're on an architecture - where an aligned access may not be atomic: */ - tme_memory_aligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u16(ic->tme_m68k_ireg_uint16(ireg)); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); + /* make the emulator memory pointer: */ + mem = (tme_shared tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first); + + /* get the value to write, in big-endian byte order: */ + mem_value = tme_htobe_u16(ic->tme_m68k_ireg_uint16(ireg)); + + /* do the 16-bit bus write: */ + tme_memory_bus_write16(mem, mem_value, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -4124,6 +4547,9 @@ tme_m68k_write_mem16(struct tme_m68k *ic &ic->tme_m68k_ireg_uint16(ireg), TME_M68K_BUS_CYCLE_NORMAL); } + + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); } /* this writes a 16-bit stack value: */ @@ -4134,6 +4560,12 @@ tme_m68k_push16(struct tme_m68k *ic, tme tme_uint32_t linear_address_first = ic->tme_m68k_ireg_a7 - sizeof(tme_uint16_t); tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint16_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint16_t mem_value; + tme_shared tme_uint16_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ /* log the value written: */ tme_m68k_verify_mem16(ic, function_code, linear_address_first, value, TME_BUS_CYCLE_WRITE); @@ -4144,29 +4576,28 @@ tme_m68k_push16(struct tme_m68k *ic, tme linear_address_first, value)); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_write pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast writes: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_WRITE(tlb, function_code, linear_address_first, linear_address_last))) { - /* for a 16-bit transfer we can always do a simple - assignment - we tested that the linear address - is 16-bit aligned, which, since the TLB emulator - offset is guaranteed to be 32-bit aligned, guarantees - that the final emulator address is 16-bit aligned. - - we need the wrlock if we're on an architecture - where an aligned access may not be atomic: */ - tme_memory_aligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u16(value); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); + /* make the emulator memory pointer: */ + mem = (tme_shared tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first); + + /* get the value to write, in big-endian byte order: */ + mem_value = tme_htobe_u16(value); + + /* do the 16-bit bus write: */ + tme_memory_bus_write16(mem, mem_value, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -4178,6 +4609,9 @@ tme_m68k_push16(struct tme_m68k *ic, tme &value, TME_M68K_BUS_CYCLE_NORMAL); } + + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); if (!TME_M68K_SEQUENCE_RESTARTING) { ic->tme_m68k_ireg_a7 -= sizeof(tme_uint16_t); } @@ -4191,59 +4625,35 @@ tme_m68k_read_memx32(struct tme_m68k *ic tme_uint32_t linear_address_first = ic->_tme_m68k_ea_address; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint32_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint32_t mem_value; + const tme_shared tme_uint32_t *mem; - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_read pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ + + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast reads: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_READ(tlb, function_code, linear_address_first, linear_address_last))) { - /* if the emulator host allows 32-bit quantities to be - transferred from 16-bit aligned addresses, or if this - address is 32-bit aligned, do the transfer as a simple - assignment, otherwise transfer two 16-bit words. - - we need the rdlock if we're on an architecture where - an aligned access may not be atomic, or if we're doing - an unaligned access on an architecture where they may - not be atomic: */ -#if ALIGNOF_INT32_T <= ALIGNOF_INT16_T -#ifdef TME_UNALIGNED_ACCESS_ATOMIC - ic->tme_m68k_ireg_memx32 = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); -#else /* !TME_UNALIGNED_ACCESS_ATOMIC */ - if ((linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_unaligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - ic->tme_m68k_ireg_memx32 = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_unaligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - ic->tme_m68k_ireg_memx32 = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* !TME_UNALIGNED_ACCESS_ATOMIC */ -#else /* ALIGNOF_INT32_T > ALIGNOF_INT16_T */ - if (TME_SEQUENCE_ACCESS_NOT_COSTLIER || (linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_sequence_rdlock(tlb->tme_m68k_tlb_bus_rwlock); -#ifdef WORDS_BIGENDIAN - ic->tme_m68k_ireg_memx32 = (((tme_uint32_t) ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[0]) << 16) | ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[1]; -#else /* !WORDS_BIGENDIAN */ - ic->tme_m68k_ireg_memx32 = tme_betoh_u32((((tme_uint32_t) ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[1]) << 16) | ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[0]); -#endif /* !WORDS_BIGENDIAN */ - tme_memory_sequence_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - ic->tme_m68k_ireg_memx32 = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* ALIGNOF_INT32_T != 1 */ + /* make the emulator memory pointer: */ + mem = (const tme_shared tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first); + + /* do the 32-bit bus read: */ + mem_value = tme_memory_bus_read32(mem, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* put the value read, in host byte order: */ + ic->tme_m68k_ireg_memx32 = tme_betoh_u32(mem_value); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -4256,6 +4666,9 @@ tme_m68k_read_memx32(struct tme_m68k *ic TME_M68K_BUS_CYCLE_NORMAL); } + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); + /* log the value read: */ tme_m68k_verify_mem32(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx32, TME_BUS_CYCLE_READ); tme_m68k_log(ic, 1000, TME_OK, @@ -4274,59 +4687,35 @@ tme_m68k_read_mem32(struct tme_m68k *ic, tme_uint32_t linear_address_first = ic->_tme_m68k_ea_address; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint32_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint32_t mem_value; + const tme_shared tme_uint32_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ + + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_read pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast reads: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_READ(tlb, function_code, linear_address_first, linear_address_last))) { - /* if the emulator host allows 32-bit quantities to be - transferred from 16-bit aligned addresses, or if this - address is 32-bit aligned, do the transfer as a simple - assignment, otherwise transfer two 16-bit words. - - we need the rdlock if we're on an architecture where - an aligned access may not be atomic, or if we're doing - an unaligned access on an architecture where they may - not be atomic: */ -#if ALIGNOF_INT32_T <= ALIGNOF_INT16_T -#ifdef TME_UNALIGNED_ACCESS_ATOMIC - ic->tme_m68k_ireg_uint32(ireg) = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); -#else /* !TME_UNALIGNED_ACCESS_ATOMIC */ - if ((linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_unaligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - ic->tme_m68k_ireg_uint32(ireg) = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_unaligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - ic->tme_m68k_ireg_uint32(ireg) = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* !TME_UNALIGNED_ACCESS_ATOMIC */ -#else /* ALIGNOF_INT32_T > ALIGNOF_INT16_T */ - if (TME_SEQUENCE_ACCESS_NOT_COSTLIER || (linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_sequence_rdlock(tlb->tme_m68k_tlb_bus_rwlock); -#ifdef WORDS_BIGENDIAN - ic->tme_m68k_ireg_uint32(ireg) = (((tme_uint32_t) ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[0]) << 16) | ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[1]; -#else /* !WORDS_BIGENDIAN */ - ic->tme_m68k_ireg_uint32(ireg) = tme_betoh_u32((((tme_uint32_t) ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[1]) << 16) | ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[0]); -#endif /* !WORDS_BIGENDIAN */ - tme_memory_sequence_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - ic->tme_m68k_ireg_uint32(ireg) = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* ALIGNOF_INT32_T != 1 */ + /* make the emulator memory pointer: */ + mem = (const tme_shared tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first); + + /* do the 32-bit bus read: */ + mem_value = tme_memory_bus_read32(mem, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* put the value read, in host byte order: */ + ic->tme_m68k_ireg_uint32(ireg) = tme_betoh_u32(mem_value); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -4339,6 +4728,9 @@ tme_m68k_read_mem32(struct tme_m68k *ic, TME_M68K_BUS_CYCLE_NORMAL); } + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); + /* log the value read: */ tme_m68k_verify_mem32(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint32(ireg), TME_BUS_CYCLE_READ); tme_m68k_log(ic, 1000, TME_OK, @@ -4356,83 +4748,119 @@ tme_m68k_fetch32(struct tme_m68k *ic, tm unsigned int function_code = TME_M68K_FUNCTION_CODE_PROGRAM(ic); tme_uint32_t linear_address_first = pc; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint32_t) - 1; - struct tme_m68k_tlb *tlb = TME_ATOMIC_READ(struct tme_m68k_tlb *, ic->_tme_m68k_itlb); - unsigned int insn_buffer_off = TME_ALIGN(ic->_tme_m68k_insn_buffer_off, sizeof(tme_uint32_t)); + struct tme_m68k_tlb *tlb = tme_memory_atomic_pointer_read(struct tme_m68k_tlb *, ic->_tme_m68k_itlb, &ic->_tme_m68k_tlbs_rwlock); + tme_uint32_t mem_value; + const tme_shared tme_uint32_t *mem; + unsigned int fetch_slow_next = ic->_tme_m68k_insn_fetch_slow_next; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ + + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if this fetch was done by the fast executor: */ + if (__tme_predict_true(fetch_slow_next < ic->_tme_m68k_insn_fetch_slow_count_fast)) { + + /* the entire fetch must be in the instruction buffer, and + we must be restarting: */ + assert ((fetch_slow_next + sizeof(tme_uint32_t)) + <= ic->_tme_m68k_insn_fetch_slow_count_fast); + assert (TME_M68K_SEQUENCE_RESTARTING); + mem_value = tme_memory_read32(((tme_uint32_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)), sizeof(tme_uint16_t)); + } + + /* otherwise, this fetch was not done by the fast executor: */ + else { + + /* if we're restarting, but the offset in the instruction buffer + to fetch into is at the instruction buffer total, this must be + a fake fault caused by the fast executor. we confirm this by + checking that this transfer "caused" the fault, and that this + transfer will be the first slow one after any fast fetches. + in this case, we can cancel the restart for now: */ + if (TME_M68K_SEQUENCE_RESTARTING + && (fetch_slow_next + == ic->_tme_m68k_insn_fetch_slow_count_total)) { + assert ((ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next + == ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted) + && (fetch_slow_next + == ic->_tme_m68k_insn_fetch_slow_count_fast)); + ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted--; + } - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_read pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ - if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) - && TME_M68K_TLB_OK_FAST_READ(tlb, - function_code, - linear_address_first, - linear_address_last))) { + /* if we're not restarting: */ + if (!TME_M68K_SEQUENCE_RESTARTING) { - /* if the emulator host allows 32-bit quantities to be - transferred from 16-bit aligned addresses, or if this - address is 32-bit aligned, do the transfer as a simple - assignment, otherwise transfer two 16-bit words. - - we need the rdlock if we're on an architecture where - an aligned access may not be atomic, or if we're doing - an unaligned access on an architecture where they may - not be atomic: */ -#if ALIGNOF_INT32_T <= ALIGNOF_INT16_T -#ifdef TME_UNALIGNED_ACCESS_ATOMIC - *((tme_uint32_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]) = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); -#else /* !TME_UNALIGNED_ACCESS_ATOMIC */ - if ((linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_unaligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]) = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_unaligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]) = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* !TME_UNALIGNED_ACCESS_ATOMIC */ -#else /* ALIGNOF_INT32_T > ALIGNOF_INT16_T */ - if (TME_SEQUENCE_ACCESS_NOT_COSTLIER || (linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_sequence_rdlock(tlb->tme_m68k_tlb_bus_rwlock); -#ifdef WORDS_BIGENDIAN - *((tme_uint32_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]) = (((tme_uint32_t) ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[0]) << 16) | ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[1]; -#else /* !WORDS_BIGENDIAN */ - *((tme_uint32_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]) = tme_betoh_u32((((tme_uint32_t) ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[1]) << 16) | ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[0]); -#endif /* !WORDS_BIGENDIAN */ - tme_memory_sequence_unlock(tlb->tme_m68k_tlb_bus_rwlock); + /* we advance the instruction buffer total *before* we do + what may be a slow fetch, because we may transfer a few + bytes and then fault. without this, those few bytes + would not get saved in the exception stack frame and + restored later before the continuation of the fetch: */ + ic->_tme_m68k_insn_fetch_slow_count_total += sizeof(tme_uint32_t); + } + + /* make sure that if this is a new transfer or if this + transfer faulted, that we're fetching for the current + last positions in the instruction buffer: */ + assert ((ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next + < ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted) + || ((fetch_slow_next + sizeof(tme_uint32_t)) + == ic->_tme_m68k_insn_fetch_slow_count_total)); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast reads: */ + if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING + && ((sizeof(tme_uint16_t) - 1) & linear_address_first) == 0 + && TME_M68K_TLB_OK_FAST_READ(tlb, + function_code, + linear_address_first, + linear_address_last))) { + + /* make the emulator memory pointer: */ + mem = (const tme_shared tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first); + + /* do the 32-bit bus read: */ + mem_value = tme_memory_bus_read32(mem, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint16_t), sizeof(tme_uint32_t)); + + /* put the value read, in host byte order: */ + mem_value = tme_betoh_u32(mem_value); + tme_memory_write32(((tme_uint32_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)), mem_value, sizeof(tme_uint16_t)); + + /* step the transfer count: */ + TME_M68K_SEQUENCE_TRANSFER_STEP; } + + /* otherwise, do the bus cycles the slow way: */ else { - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]) = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); + tme_m68k_read32(ic, tlb, + &function_code, + &pc, + ((tme_uint32_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)), + TME_M68K_BUS_CYCLE_FETCH); + mem_value = tme_memory_read32(((tme_uint32_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)), sizeof(tme_uint16_t)); } -#endif /* ALIGNOF_INT32_T != 1 */ - TME_M68K_SEQUENCE_TRANSFER_STEP; } - /* otherwise, do the bus cycles the slow way: */ - else { - tme_m68k_read32(ic, tlb, - &function_code, - &pc, - ((tme_uint32_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]), - TME_M68K_BUS_CYCLE_FETCH); - } + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); /* log the value read: */ - tme_m68k_verify_mem32(ic, function_code, pc, *((tme_uint32_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]), TME_BUS_CYCLE_READ); + tme_m68k_verify_mem32(ic, function_code, pc, *((tme_uint32_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)), TME_BUS_CYCLE_READ); tme_m68k_log(ic, 1000, TME_OK, (TME_M68K_LOG_HANDLE(ic), _("fetch32\t%d:0x%08x:\t0x%08x"), function_code, pc, - *((tme_uint32_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off]))); - ic->_tme_m68k_insn_buffer_off = insn_buffer_off + sizeof(tme_uint32_t); - return(*((tme_uint32_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off])); + *((tme_uint32_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next)))); + + /* advance the offset in the instruction buffer for the next slow fetch: */ + fetch_slow_next += sizeof(tme_uint32_t); + ic->_tme_m68k_insn_fetch_slow_next = fetch_slow_next; + + /* return the fetched value: */ + return(mem_value); } /* this reads a 32-bit stack value: */ @@ -4443,59 +4871,35 @@ tme_m68k_pop32(struct tme_m68k *ic, tme_ tme_uint32_t linear_address_first = ic->tme_m68k_ireg_a7; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint32_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint32_t mem_value; + const tme_shared tme_uint32_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ + + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_read pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast reads: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_READ(tlb, function_code, linear_address_first, linear_address_last))) { - /* if the emulator host allows 32-bit quantities to be - transferred from 16-bit aligned addresses, or if this - address is 32-bit aligned, do the transfer as a simple - assignment, otherwise transfer two 16-bit words. - - we need the rdlock if we're on an architecture where - an aligned access may not be atomic, or if we're doing - an unaligned access on an architecture where they may - not be atomic: */ -#if ALIGNOF_INT32_T <= ALIGNOF_INT16_T -#ifdef TME_UNALIGNED_ACCESS_ATOMIC - *_value = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); -#else /* !TME_UNALIGNED_ACCESS_ATOMIC */ - if ((linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_unaligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - *_value = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_unaligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - *_value = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* !TME_UNALIGNED_ACCESS_ATOMIC */ -#else /* ALIGNOF_INT32_T > ALIGNOF_INT16_T */ - if (TME_SEQUENCE_ACCESS_NOT_COSTLIER || (linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_sequence_rdlock(tlb->tme_m68k_tlb_bus_rwlock); -#ifdef WORDS_BIGENDIAN - *_value = (((tme_uint32_t) ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[0]) << 16) | ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[1]; -#else /* !WORDS_BIGENDIAN */ - *_value = tme_betoh_u32((((tme_uint32_t) ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[1]) << 16) | ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))[0]); -#endif /* !WORDS_BIGENDIAN */ - tme_memory_sequence_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - *_value = tme_betoh_u32(*((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first))); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* ALIGNOF_INT32_T != 1 */ + /* make the emulator memory pointer: */ + mem = (const tme_shared tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first); + + /* do the 32-bit bus read: */ + mem_value = tme_memory_bus_read32(mem, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* put the value read, in host byte order: */ + *_value = tme_betoh_u32(mem_value); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -4508,6 +4912,9 @@ tme_m68k_pop32(struct tme_m68k *ic, tme_ TME_M68K_BUS_CYCLE_NORMAL); } + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); + /* log the value read: */ tme_m68k_verify_mem32(ic, function_code, ic->tme_m68k_ireg_a7, *_value, TME_BUS_CYCLE_READ); tme_m68k_log(ic, 1000, TME_OK, @@ -4529,6 +4936,12 @@ tme_m68k_write_memx32(struct tme_m68k *i tme_uint32_t linear_address_first = ic->_tme_m68k_ea_address; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint32_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint32_t mem_value; + tme_shared tme_uint32_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ /* log the value written: */ tme_m68k_verify_mem32(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx32, TME_BUS_CYCLE_WRITE); @@ -4539,55 +4952,28 @@ tme_m68k_write_memx32(struct tme_m68k *i ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx32)); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_write pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast writes: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_WRITE(tlb, function_code, linear_address_first, linear_address_last))) { - /* if the emulator host allows 32-bit quantities to be - transferred to 16-bit aligned addresses, or if this - address is 32-bit aligned, do the transfer as a simple - assignment, otherwise transfer two 16-bit words. - - we need the wrlock if we're on an architecture where - an aligned access may not be atomic, or if we're doing - an unaligned access on an architecture where they may - not be atomic: */ -#if ALIGNOF_INT32_T <= ALIGNOF_INT16_T -#ifdef TME_UNALIGNED_ACCESS_ATOMIC - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(ic->tme_m68k_ireg_memx32); -#else /* !TME_UNALIGNED_ACCESS_ATOMIC */ - if ((linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_unaligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(ic->tme_m68k_ireg_memx32); - tme_memory_unaligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(ic->tme_m68k_ireg_memx32); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* !TME_UNALIGNED_ACCESS_ATOMIC */ -#else /* ALIGNOF_INT32_T > ALIGNOF_INT16_T */ - if (TME_SEQUENCE_ACCESS_NOT_COSTLIER || (linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_sequence_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first))[0] = tme_htobe_u16(ic->tme_m68k_ireg_memx32 >> 16); - ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first))[1] = tme_htobe_u16(ic->tme_m68k_ireg_memx32 & 0xffff); - tme_memory_sequence_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(ic->tme_m68k_ireg_memx32); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* ALIGNOF_INT32_T != 1 */ + /* make the emulator memory pointer: */ + mem = (tme_shared tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first); + + /* get the value to write, in big-endian byte order: */ + mem_value = tme_htobe_u32(ic->tme_m68k_ireg_memx32); + + /* do the 32-bit bus write: */ + tme_memory_bus_write32(mem, mem_value, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -4599,6 +4985,9 @@ tme_m68k_write_memx32(struct tme_m68k *i &ic->tme_m68k_ireg_memx32, TME_M68K_BUS_CYCLE_NORMAL); } + + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); } /* this writes a 32-bit mem value: */ @@ -4609,6 +4998,12 @@ tme_m68k_write_mem32(struct tme_m68k *ic tme_uint32_t linear_address_first = ic->_tme_m68k_ea_address; tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint32_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint32_t mem_value; + tme_shared tme_uint32_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ /* log the value written: */ tme_m68k_verify_mem32(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint32(ireg), TME_BUS_CYCLE_WRITE); @@ -4619,55 +5014,28 @@ tme_m68k_write_mem32(struct tme_m68k *ic ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint32(ireg))); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_write pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast writes: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_WRITE(tlb, function_code, linear_address_first, linear_address_last))) { - /* if the emulator host allows 32-bit quantities to be - transferred to 16-bit aligned addresses, or if this - address is 32-bit aligned, do the transfer as a simple - assignment, otherwise transfer two 16-bit words. - - we need the wrlock if we're on an architecture where - an aligned access may not be atomic, or if we're doing - an unaligned access on an architecture where they may - not be atomic: */ -#if ALIGNOF_INT32_T <= ALIGNOF_INT16_T -#ifdef TME_UNALIGNED_ACCESS_ATOMIC - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(ic->tme_m68k_ireg_uint32(ireg)); -#else /* !TME_UNALIGNED_ACCESS_ATOMIC */ - if ((linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_unaligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(ic->tme_m68k_ireg_uint32(ireg)); - tme_memory_unaligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(ic->tme_m68k_ireg_uint32(ireg)); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* !TME_UNALIGNED_ACCESS_ATOMIC */ -#else /* ALIGNOF_INT32_T > ALIGNOF_INT16_T */ - if (TME_SEQUENCE_ACCESS_NOT_COSTLIER || (linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_sequence_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first))[0] = tme_htobe_u16(ic->tme_m68k_ireg_uint32(ireg) >> 16); - ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first))[1] = tme_htobe_u16(ic->tme_m68k_ireg_uint32(ireg) & 0xffff); - tme_memory_sequence_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(ic->tme_m68k_ireg_uint32(ireg)); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* ALIGNOF_INT32_T != 1 */ + /* make the emulator memory pointer: */ + mem = (tme_shared tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first); + + /* get the value to write, in big-endian byte order: */ + mem_value = tme_htobe_u32(ic->tme_m68k_ireg_uint32(ireg)); + + /* do the 32-bit bus write: */ + tme_memory_bus_write32(mem, mem_value, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -4679,6 +5047,9 @@ tme_m68k_write_mem32(struct tme_m68k *ic &ic->tme_m68k_ireg_uint32(ireg), TME_M68K_BUS_CYCLE_NORMAL); } + + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); } /* this writes a 32-bit stack value: */ @@ -4689,6 +5060,12 @@ tme_m68k_push32(struct tme_m68k *ic, tme tme_uint32_t linear_address_first = ic->tme_m68k_ireg_a7 - sizeof(tme_uint32_t); tme_uint32_t linear_address_last = linear_address_first + sizeof(tme_uint32_t) - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + tme_uint32_t mem_value; + tme_shared tme_uint32_t *mem; + +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ /* log the value written: */ tme_m68k_verify_mem32(ic, function_code, linear_address_first, value, TME_BUS_CYCLE_WRITE); @@ -4699,55 +5076,28 @@ tme_m68k_push32(struct tme_m68k *ic, tme linear_address_first, value)); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_write pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* if we aren't restarting, and this address is properly aligned, + and this TLB entry covers the operand and allows fast writes: */ if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 && TME_M68K_TLB_OK_FAST_WRITE(tlb, function_code, linear_address_first, linear_address_last))) { - /* if the emulator host allows 32-bit quantities to be - transferred to 16-bit aligned addresses, or if this - address is 32-bit aligned, do the transfer as a simple - assignment, otherwise transfer two 16-bit words. - - we need the wrlock if we're on an architecture where - an aligned access may not be atomic, or if we're doing - an unaligned access on an architecture where they may - not be atomic: */ -#if ALIGNOF_INT32_T <= ALIGNOF_INT16_T -#ifdef TME_UNALIGNED_ACCESS_ATOMIC - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(value); -#else /* !TME_UNALIGNED_ACCESS_ATOMIC */ - if ((linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_unaligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(value); - tme_memory_unaligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(value); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* !TME_UNALIGNED_ACCESS_ATOMIC */ -#else /* ALIGNOF_INT32_T > ALIGNOF_INT16_T */ - if (TME_SEQUENCE_ACCESS_NOT_COSTLIER || (linear_address_first & (sizeof(tme_uint32_t) - 1))) { - tme_memory_sequence_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first))[0] = tme_htobe_u16(value >> 16); - ((tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first))[1] = tme_htobe_u16(value & 0xffff); - tme_memory_sequence_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } - else { - tme_memory_aligned_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - *((tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first)) = tme_htobe_u32(value); - tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); - } -#endif /* ALIGNOF_INT32_T != 1 */ + /* make the emulator memory pointer: */ + mem = (tme_shared tme_uint32_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first); + + /* get the value to write, in big-endian byte order: */ + mem_value = tme_htobe_u32(value); + + /* do the 32-bit bus write: */ + tme_memory_bus_write32(mem, mem_value, tlb->tme_m68k_tlb_bus_rwlock, sizeof(tme_uint8_t), sizeof(tme_uint32_t)); + + /* step the transfer count: */ TME_M68K_SEQUENCE_TRANSFER_STEP; } @@ -4759,6 +5109,9 @@ tme_m68k_push32(struct tme_m68k *ic, tme &value, TME_M68K_BUS_CYCLE_NORMAL); } + + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); if (!TME_M68K_SEQUENCE_RESTARTING) { ic->tme_m68k_ireg_a7 -= sizeof(tme_uint32_t); } @@ -4770,31 +5123,20 @@ tme_m68k_read_mem(struct tme_m68k *ic, t { unsigned int function_code = ic->_tme_m68k_ea_function_code; tme_uint32_t linear_address_first = ic->_tme_m68k_ea_address; - tme_uint32_t linear_address_last = linear_address_first + count - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_read pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ - if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) - && TME_M68K_TLB_OK_FAST_READ(tlb, - function_code, - linear_address_first, - linear_address_last))) { +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ - tme_memory_sequence_rdlock(tlb->tme_m68k_tlb_bus_rwlock); - memcpy(buffer, (tlb->tme_m68k_tlb_emulator_off_read + linear_address_first), count); - tme_memory_sequence_unlock(tlb->tme_m68k_tlb_bus_rwlock); - TME_M68K_SEQUENCE_TRANSFER_STEP; - } + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); - /* otherwise, do the bus cycles the slow way: */ - else { - tme_m68k_read(ic, tlb, &ic->_tme_m68k_ea_function_code, &ic->_tme_m68k_ea_address, buffer, count, TME_M68K_BUS_CYCLE_RAW); - } + /* call the full read function: */ + tme_m68k_read(ic, tlb, &ic->_tme_m68k_ea_function_code, &ic->_tme_m68k_ea_address, buffer, count, TME_M68K_BUS_CYCLE_RAW); + + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); /* log the value read: */ tme_m68k_verify_mem_any(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, buffer, count, TME_BUS_CYCLE_READ); @@ -4828,12 +5170,11 @@ tme_m68k_read(struct tme_m68k *ic, struct tme_bus_cycle cycle; unsigned int transferred, resid, cycle_size; int exception; - tme_rwlock_t *rmw_rwlock; int err; -#ifndef WORDS_BIGENDIAN tme_uint8_t *reg_p; unsigned int buffer_i; -#endif /* !WORDS_BIGENDIAN */ + tme_uint8_t reg_buffer[sizeof(tme_uint32_t) * 2]; + const tme_shared tme_uint8_t *mem; /* if we're not restarting, everything is fresh: */ if (!TME_M68K_SEQUENCE_RESTARTING) { @@ -4880,7 +5221,6 @@ tme_m68k_read(struct tme_m68k *ic, } /* do as many bus cycles as needed to complete the transfer: */ - rmw_rwlock = tlb->tme_m68k_tlb_bus_rwlock; exception = TME_M68K_EXCEPTION_NONE; cycle_size = 0; for(; transferred < reg_size; ) { @@ -4912,7 +5252,7 @@ tme_m68k_read(struct tme_m68k *ic, /* only byte transfers can be unaligned: */ if (resid > sizeof(tme_uint8_t) && (linear_address & 1)) { - exception = TME_M68K_EXCEPTION_GROUP0_AERR; + exception = TME_M68K_EXCEPTION_AERR; break; } @@ -4930,7 +5270,7 @@ tme_m68k_read(struct tme_m68k *ic, /* an instruction fetch must be aligned: */ if (flags & TME_M68K_BUS_CYCLE_FETCH) { if (linear_address & 1) { - exception = TME_M68K_EXCEPTION_GROUP0_AERR; + exception = TME_M68K_EXCEPTION_AERR; break; } assert(!(resid & 1)); @@ -4944,32 +5284,82 @@ tme_m68k_read(struct tme_m68k *ic, &tme_m68k_router_32[TME_M68K_BUS_ROUTER_INDEX(TME_BUS32_LOG2, cycle_size, linear_address)]; } - /* reload the TLB entry: */ - if (!TME_M68K_TLB_OK_SLOW_READ(tlb, function_code, linear_address)) { + /* loop while this TLB entry is invalid or does not apply: */ + for (; __tme_predict_false(tme_bus_tlb_is_invalid(&tlb->tme_m68k_tlb_bus_tlb) + || (tlb->tme_m68k_tlb_function_codes_mask & TME_BIT(function_code)) == 0 + || linear_address < tlb->tme_m68k_tlb_linear_first + || linear_address > tlb->tme_m68k_tlb_linear_last + || (tlb->tme_m68k_tlb_emulator_off_read == TME_EMULATOR_OFF_UNDEF + && (tlb->tme_m68k_tlb_cycles_ok & TME_BUS_CYCLE_READ) == 0)); ) { + + /* this must not be part of a read/modify/write cycle: */ + assert(!(flags & TME_M68K_BUS_CYCLE_RMW)); + + /* fill this TLB entry: */ tme_m68k_tlb_fill(ic, tlb, function_code, linear_address, TME_BUS_CYCLE_READ); } - /* if this is a part of a read/modify/write cycle: */ - if (flags & TME_M68K_BUS_CYCLE_RMW) { + /* if this TLB entry allows for fast reads: */ + mem = tlb->tme_m68k_tlb_emulator_off_read; + if (__tme_predict_true(mem != TME_EMULATOR_OFF_UNDEF)) { - /* if this TLB entry doesn't support fast reads, or - if the TLB lock has changed, that's a bus error. - see the discussion in tme_m68k_rmw_start: */ - if (!TME_M68K_TLB_OK_FAST_READ(tlb, function_code, linear_address, linear_address) - || (rmw_rwlock != NULL - && rmw_rwlock != tlb->tme_m68k_tlb_bus_rwlock)) { - exception = TME_M68K_EXCEPTION_GROUP0_BERR; - break; + /* make the emulator memory pointer: */ + mem += linear_address; + + /* limit the cycle size to addresses covered by the TLB entry: */ + if (__tme_predict_false((cycle_size - 1) + > (tlb->tme_m68k_tlb_linear_last - linear_address))) { + cycle_size = (tlb->tme_m68k_tlb_linear_last - linear_address) + 1; + } + + /* if this is a little-endian host, and this isn't a raw read: */ + if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE + && (flags & TME_M68K_BUS_CYCLE_RAW) == 0) { + + /* use the intermediate buffer for the read: */ + cycle.tme_bus_cycle_buffer = ®_buffer[0]; } - /* if we haven't locked this memory yet, do so: */ - if (rmw_rwlock == NULL) { - rmw_rwlock = tlb->tme_m68k_tlb_bus_rwlock; - tme_rwlock_wrlock(rmw_rwlock); + /* do the bus read: */ + tme_memory_bus_read_buffer(mem, + cycle.tme_bus_cycle_buffer, + cycle_size, + tlb->tme_m68k_tlb_bus_rwlock, + sizeof(tme_uint8_t), + sizeof(tme_uint32_t)); + + /* if this is a little-endian host, and this isn't a raw read: */ + if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE + && (flags & TME_M68K_BUS_CYCLE_RAW) == 0) { + + /* byteswap the read data in the intermediate buffer: */ + reg_p = reg + reg_size - (1 + transferred); + buffer_i = 0; + do { + *(reg_p--) = reg_buffer[buffer_i]; + } while (++buffer_i != cycle_size); } + + /* update: */ + linear_address += cycle_size; + transferred += cycle_size; + continue; + } + + /* otherwise, this TLB entry does not allow for fast reads: */ + + /* if this is a part of a read/modify/write cycle: */ + if (flags & TME_M68K_BUS_CYCLE_RMW) { + + /* if this is the first cycle in this read, + we will establish the new lock, otherwise + we will continue using the existing lock: */ + cycle.tme_bus_cycle_type + |= (TME_BUS_CYCLE_LOCK + | (transferred == 0 ? 0 : TME_BUS_CYCLE_UNLOCK)); } /* form the physical address for the bus cycle handler: */ @@ -4984,22 +5374,33 @@ tme_m68k_read(struct tme_m68k *ic, cycle.tme_bus_cycle_address = physical_address; /* run the bus cycle: */ + tme_m68k_tlb_unbusy(tlb); + tme_m68k_callout_unlock(ic); err = (*tlb->tme_m68k_tlb_bus_tlb.tme_bus_tlb_cycle) (tlb->tme_m68k_tlb_bus_tlb.tme_bus_tlb_cycle_private, &cycle); + tme_m68k_callout_relock(ic); + tme_m68k_tlb_busy(tlb); - /* if we deadlocked, we have no locks to release - ourselves, so sleep a while waiting for things - to clear up, then try again: */ - if (err == TME_EDEADLK) { - TME_THREAD_DEADLOCK_SLEEP(); - cycle.tme_bus_cycle_address = physical_address; + /* if the TLB entry was invalidated before the read: */ + if (err == EBADF + && tme_bus_tlb_is_invalid(&tlb->tme_m68k_tlb_bus_tlb)) { + cycle.tme_bus_cycle_size = 0; + } + + /* otherwise, if we didn't get a bus error, but some + synchronous event has happened: */ + else if (err == TME_BUS_CYCLE_SYNCHRONOUS_EVENT) { + + /* after the currently executing instruction finishes, check + for external resets, halts, or interrupts: */ + ic->_tme_m68k_instruction_burst_remaining = 0; } /* otherwise, any other error might be a bus error: */ else if (err != TME_OK) { err = tme_bus_tlb_fault(&tlb->tme_m68k_tlb_bus_tlb, &cycle, err); if (err != TME_OK) { - exception = TME_M68K_EXCEPTION_GROUP0_BERR; + exception = TME_M68K_EXCEPTION_BERR; break; } } @@ -5009,13 +5410,9 @@ tme_m68k_read(struct tme_m68k *ic, transferred += cycle.tme_bus_cycle_size; } - /* if we got an exception and there is a locked - read/modify/write rwlock, unlock it: */ - if (exception != TME_M68K_EXCEPTION_NONE - && (flags & TME_M68K_BUS_CYCLE_RMW) - && rmw_rwlock != NULL) { - tme_rwlock_unlock(rmw_rwlock); - } + /* NB: there is no need to explicitly unlock + a device. if a locked bus cycle to a device + faults, the lock must be automatically unlocked: */ /* if we faulted, stash the information the fault stacker will need and start exception processing: */ @@ -5031,6 +5428,7 @@ tme_m68k_read(struct tme_m68k *ic, } ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_faulted = ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_next; + tme_m68k_tlb_unbusy(tlb); tme_m68k_exception(ic, exception); } @@ -5044,9 +5442,12 @@ tme_m68k_write_mem(struct tme_m68k *ic, { unsigned int function_code = ic->_tme_m68k_ea_function_code; tme_uint32_t linear_address_first = ic->_tme_m68k_ea_address; - tme_uint32_t linear_address_last = linear_address_first + count - 1; struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ + /* log the value written: */ tme_m68k_verify_mem_any(ic, ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, buffer, count, TME_BUS_CYCLE_WRITE); tme_m68k_log_start(ic, 1000, TME_OK) { @@ -5061,28 +5462,14 @@ tme_m68k_write_mem(struct tme_m68k *ic, } } tme_m68k_log_finish(ic); - /* do the bus cycle(s) ourselves from emulator memory if we can. - the emulator memory allocator and TLB filler must guarantee - that all tme_m68k_tlb_emulator_off_write pointers be 32-bit - aligned, so that a 16-bit-aligned linear address gets a - 16-bit-aligned emulator address: */ - if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING - && !(linear_address_first & 1) - && TME_M68K_TLB_OK_FAST_WRITE(tlb, - function_code, - linear_address_first, - linear_address_last))) { + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); - tme_memory_sequence_wrlock(tlb->tme_m68k_tlb_bus_rwlock); - memcpy((tlb->tme_m68k_tlb_emulator_off_write + linear_address_first), buffer, count); - tme_memory_sequence_unlock(tlb->tme_m68k_tlb_bus_rwlock); - TME_M68K_SEQUENCE_TRANSFER_STEP; - } + /* call the full write function: */ + tme_m68k_write(ic, tlb, &ic->_tme_m68k_ea_function_code, &ic->_tme_m68k_ea_address, buffer, count, TME_M68K_BUS_CYCLE_RAW); - /* otherwise, do the bus cycles the slow way: */ - else { - tme_m68k_write(ic, tlb, &ic->_tme_m68k_ea_function_code, &ic->_tme_m68k_ea_address, buffer, count, TME_M68K_BUS_CYCLE_RAW); - } + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); } /* this writes a region of address space using actual bus cycles: */ @@ -5102,12 +5489,11 @@ tme_m68k_write(struct tme_m68k *ic, struct tme_bus_cycle cycle; unsigned int transferred, resid, cycle_size; int exception; - tme_rwlock_t *rmw_rwlock; int err; -#ifndef WORDS_BIGENDIAN tme_uint8_t *reg_p; unsigned int buffer_i; -#endif /* !WORDS_BIGENDIAN */ + tme_uint8_t reg_buffer[sizeof(tme_uint32_t) * 2]; + tme_shared tme_uint8_t *mem; /* if we're not restarting, everything is fresh: */ if (!TME_M68K_SEQUENCE_RESTARTING) { @@ -5154,7 +5540,6 @@ tme_m68k_write(struct tme_m68k *ic, } /* do as many bus cycles as needed to complete the transfer: */ - rmw_rwlock = tlb->tme_m68k_tlb_bus_rwlock; exception = TME_M68K_EXCEPTION_NONE; cycle_size = 0; for(; transferred < reg_size; ) { @@ -5186,7 +5571,7 @@ tme_m68k_write(struct tme_m68k *ic, /* only byte transfers can be unaligned: */ if (resid > sizeof(tme_uint8_t) && (linear_address & 1)) { - exception = TME_M68K_EXCEPTION_GROUP0_AERR; + exception = TME_M68K_EXCEPTION_AERR; break; } @@ -5209,32 +5594,77 @@ tme_m68k_write(struct tme_m68k *ic, &tme_m68k_router_32[TME_M68K_BUS_ROUTER_INDEX(TME_BUS32_LOG2, cycle_size, linear_address)]; } - /* reload the TLB entry: */ - if (!TME_M68K_TLB_OK_SLOW_WRITE(tlb, function_code, linear_address)) { + /* loop while this TLB entry is invalid or does not apply: */ + for (; __tme_predict_false(tme_bus_tlb_is_invalid(&tlb->tme_m68k_tlb_bus_tlb) + || (tlb->tme_m68k_tlb_function_codes_mask & TME_BIT(function_code)) == 0 + || linear_address < tlb->tme_m68k_tlb_linear_first + || linear_address > tlb->tme_m68k_tlb_linear_last + || (tlb->tme_m68k_tlb_emulator_off_write == TME_EMULATOR_OFF_UNDEF + && (tlb->tme_m68k_tlb_cycles_ok & TME_BUS_CYCLE_WRITE) == 0)); ) { + + /* this must not be part of a read/modify/write cycle: */ + assert(!(flags & TME_M68K_BUS_CYCLE_RMW)); + + /* fill this TLB entry: */ tme_m68k_tlb_fill(ic, tlb, function_code, linear_address, TME_BUS_CYCLE_WRITE); } - /* if this is a part of a read/modify/write cycle: */ - if (flags & TME_M68K_BUS_CYCLE_RMW) { + /* if this TLB entry allows for fast writes: */ + mem = tlb->tme_m68k_tlb_emulator_off_write; + if (__tme_predict_true(mem != TME_EMULATOR_OFF_UNDEF)) { - /* if this TLB entry doesn't support fast writes, or - if the TLB lock has changed, that's a bus error. - see the discussion in tme_m68k_rmw_start: */ - if (!TME_M68K_TLB_OK_FAST_WRITE(tlb, function_code, linear_address, linear_address) - || (rmw_rwlock != NULL - && rmw_rwlock != tlb->tme_m68k_tlb_bus_rwlock)) { - exception = TME_M68K_EXCEPTION_GROUP0_BERR; - break; + /* make the emulator memory pointer: */ + mem += linear_address; + + /* limit the cycle size to addresses covered by the TLB entry: */ + if (__tme_predict_false((cycle_size - 1) + > (tlb->tme_m68k_tlb_linear_last - linear_address))) { + cycle_size = (tlb->tme_m68k_tlb_linear_last - linear_address) + 1; } - /* if we haven't locked this memory yet, do so: */ - if (rmw_rwlock == NULL) { - rmw_rwlock = tlb->tme_m68k_tlb_bus_rwlock; - tme_rwlock_wrlock(rmw_rwlock); + /* if this is a little-endian host, and this isn't a raw write: */ + if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE + && (flags & TME_M68K_BUS_CYCLE_RAW) == 0) { + + /* byteswap the data to write in the intermediate buffer: */ + reg_p = cycle.tme_bus_cycle_buffer; + buffer_i = 0; + do { + reg_buffer[buffer_i] = *(reg_p--); + } while (++buffer_i != cycle_size); + + /* use the intermediate buffer for the write: */ + cycle.tme_bus_cycle_buffer = ®_buffer[0]; } + + /* do the bus write: */ + tme_memory_bus_write_buffer(mem, + cycle.tme_bus_cycle_buffer, + cycle_size, + tlb->tme_m68k_tlb_bus_rwlock, + sizeof(tme_uint8_t), + sizeof(tme_uint32_t)); + + /* update: */ + linear_address += cycle_size; + transferred += cycle_size; + continue; + } + + /* otherwise, this TLB entry does not allow for fast writes: */ + + /* if this is a part of a read/modify/write cycle: */ + if (flags & TME_M68K_BUS_CYCLE_RMW) { + + /* we will continue using the existing lock. + the device will automatically unlock after + the last cycle of this write: */ + cycle.tme_bus_cycle_type + |= (TME_BUS_CYCLE_LOCK + | (TME_BUS_CYCLE_UNLOCK)); } /* form the physical address for the bus cycle handler: */ @@ -5249,22 +5679,33 @@ tme_m68k_write(struct tme_m68k *ic, cycle.tme_bus_cycle_address = physical_address; /* run the bus cycle: */ + tme_m68k_tlb_unbusy(tlb); + tme_m68k_callout_unlock(ic); err = (*tlb->tme_m68k_tlb_bus_tlb.tme_bus_tlb_cycle) (tlb->tme_m68k_tlb_bus_tlb.tme_bus_tlb_cycle_private, &cycle); + tme_m68k_callout_relock(ic); + tme_m68k_tlb_busy(tlb); - /* if we deadlocked, we have no locks to release - ourselves, so sleep a while waiting for things - to clear up, then try again: */ - if (err == TME_EDEADLK) { - TME_THREAD_DEADLOCK_SLEEP(); - cycle.tme_bus_cycle_address = physical_address; + /* if the TLB entry was invalidated before the write: */ + if (err == EBADF + && tme_bus_tlb_is_invalid(&tlb->tme_m68k_tlb_bus_tlb)) { + cycle.tme_bus_cycle_size = 0; + } + + /* otherwise, if we didn't get a bus error, but some + synchronous event has happened: */ + else if (err == TME_BUS_CYCLE_SYNCHRONOUS_EVENT) { + + /* after the currently executing instruction finishes, check + for external resets, halts, or interrupts: */ + ic->_tme_m68k_instruction_burst_remaining = 0; } /* otherwise, any other error might be a bus error: */ else if (err != TME_OK) { err = tme_bus_tlb_fault(&tlb->tme_m68k_tlb_bus_tlb, &cycle, err); if (err != TME_OK) { - exception = TME_M68K_EXCEPTION_GROUP0_BERR; + exception = TME_M68K_EXCEPTION_BERR; break; } } @@ -5274,13 +5715,9 @@ tme_m68k_write(struct tme_m68k *ic, transferred += cycle.tme_bus_cycle_size; } - /* if we got an exception and there is a locked - read/modify/write rwlock, unlock it: */ - if (exception != TME_M68K_EXCEPTION_NONE - && (flags & TME_M68K_BUS_CYCLE_RMW) - && rmw_rwlock != NULL) { - tme_rwlock_unlock(rmw_rwlock); - } + /* NB: there is no need to explicitly unlock + a device. if a locked bus cycle to a device + faults, the lock must be automatically unlocked: */ /* if we faulted, stash the information the fault stacker will need and start exception processing: */ @@ -5308,6 +5745,7 @@ tme_m68k_write(struct tme_m68k *ic, } ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_faulted = ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_next; + tme_m68k_tlb_unbusy(tlb); tme_m68k_exception(ic, exception); } @@ -5324,20 +5762,21 @@ TME_M68K_INSN(tme_m68k_abcd) int memory; int rx, ry, function_code; - TME_M68K_INSN_CANFAULT; - /* load the operands: */ rx = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3); ry = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 9, 3); memory = (TME_M68K_INSN_OPCODE & TME_BIT(3)) != 0; function_code = TME_M68K_FUNCTION_CODE_DATA(ic); if (memory) { + TME_M68K_INSN_CANFAULT; if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + rx) -= sizeof(tme_uint8_t) + ((rx + 1) >> 3); ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + rx); } tme_m68k_read_memx8(ic); if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry) -= sizeof(tme_uint8_t) + ((ry + 1) >> 3); ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry); } @@ -5375,8 +5814,6 @@ TME_M68K_INSN(tme_m68k_abcd) ic->tme_m68k_ireg_memx8 = res; ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry); - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + rx) += sizeof(tme_uint8_t) + ((rx + 1) >> 3); - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry) += sizeof(tme_uint8_t) + ((ry + 1) >> 3); ic->tme_m68k_ireg_ccr = flags; } tme_m68k_write_memx8(ic); @@ -5398,20 +5835,21 @@ TME_M68K_INSN(tme_m68k_sbcd) int memory; int rx, ry, function_code; - TME_M68K_INSN_CANFAULT; - /* load the operands: */ rx = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3); ry = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 9, 3); memory = (TME_M68K_INSN_OPCODE & TME_BIT(3)) != 0; function_code = TME_M68K_FUNCTION_CODE_DATA(ic); if (memory) { + TME_M68K_INSN_CANFAULT; if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + rx) -= sizeof(tme_uint8_t) + ((rx + 1) >> 3); ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + rx); } tme_m68k_read_memx8(ic); if (!TME_M68K_SEQUENCE_RESTARTING) { + ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry) -= sizeof(tme_uint8_t) + ((ry + 1) >> 3); ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry); } @@ -5449,8 +5887,6 @@ TME_M68K_INSN(tme_m68k_sbcd) ic->tme_m68k_ireg_memx8 = res; ic->_tme_m68k_ea_function_code = function_code; ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry); - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + rx) += sizeof(tme_uint8_t) + ((rx + 1) >> 3); - ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry) += sizeof(tme_uint8_t) + ((ry + 1) >> 3); ic->tme_m68k_ireg_ccr = flags; } tme_m68k_write_memx8(ic); @@ -5606,8 +6042,9 @@ TME_M68K_INSN(tme_m68k_divu) dividend = (tme_uint32_t) ic->tme_m68k_ireg_uint32(ireg_dq); divisor = TME_M68K_INSN_OP1(tme_uint16_t); if (divisor == 0) { + ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc; ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next; - TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_GROUP2(5)); + TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_DIV0)); } /* do the division: */ @@ -5693,8 +6130,9 @@ TME_M68K_INSN(tme_m68k_divul) dividend = (tme_uint64_t) ic->tme_m68k_ireg_uint32(ireg_dq); divisor = TME_M68K_INSN_OP1(tme_uint32_t); if (divisor == 0) { + ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc; ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next; - TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_GROUP2(5)); + TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_DIV0)); } /* do the division: */ @@ -5759,8 +6197,9 @@ TME_M68K_INSN(tme_m68k_divs) dividend = (tme_int32_t) ic->tme_m68k_ireg_int32(ireg_dq); divisor = TME_M68K_INSN_OP1(tme_int16_t); if (divisor == 0) { + ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc; ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next; - TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_GROUP2(5)); + TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_DIV0)); } /* do the division: */ @@ -5846,8 +6285,9 @@ TME_M68K_INSN(tme_m68k_divsl) dividend = (tme_int64_t) ic->tme_m68k_ireg_int32(ireg_dq); divisor = TME_M68K_INSN_OP1(tme_int32_t); if (divisor == 0) { + ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc; ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next; - TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_GROUP2(5)); + TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_DIV0)); } /* do the division: */