--- tme/ic/m68k/m68k-insns-auto.c 2018/04/24 16:37:52 1.1.1.1 +++ tme/ic/m68k/m68k-insns-auto.c 2018/04/24 16:45:38 1.1.1.6 @@ -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.1 2018/04/24 16:37:52 root Exp $"); +_TME_RCSID("$Id: m68k-insns-auto.c,v 1.1.1.6 2018/04/24 16:45:38 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; @@ -534,7 +534,7 @@ TME_M68K_INSN(tme_m68k_bset8) TME_M68K_INSN(tme_m68k_asl8) { unsigned int count; - tme_uint8_t sign_bits; + tme_uint8_t sign_bits, sign_bits_mask; tme_uint8_t res; tme_uint8_t flags; @@ -553,27 +553,28 @@ TME_M68K_INSN(tme_m68k_asl8) shifting in order to generate V. in general, the idea is to get all of the bits that will ever - appear in the sign position into sign_bits; if sign_bits is - all-bits-one or all-bits zero, clear V, else set V. a good trick - is that ((sign_bits + 1) & sign_bits) is nonzero iff all of the - bits in sign_bits are the same. + appear in the sign position into sign_bits, with a mask in + sign_bits_mask. if (sign_bits & sign_bits_mask) is zero or + sign_bits_mask, clear V, else set V. - start by loading all of the operand into sign_bits. + start by loading the operand into sign_bits and setting + sign_bits_mask to all-bits-one. if the shift count is exactly 8 - 1, then all of the bits of the operand will appear in the sign position. if the shift count is less than 8 - 1, then some of the less significant bits of the operand will never appear in the - sign position, so we can shift them off of sign_bits now. + sign position, so we can shift sign_bits_mask to ignore them. if the shift count is greater than 8 - 1, then all of the bits in the operand, plus at least one zero bit, will appear in the sign position. the only way that the sign bit will never change during the shift is if the operand was zero to begin with. - we need to change sign_bits such that ((sign_bits + 1) & - sign_bits) will be zero iff the operand was zero to begin with. - the magic below does just that: */ + without any changes to sign_bits or sign_bits_mask, the final + test will always work, except when sign_bits is all-bits-one. + the magic below clears the least-significant bit of sign_bits + iff sign_bits is all-bits-one: */ sign_bits = res; if (63 > SHIFTMAX_INT8_T && count > 8) { @@ -584,16 +585,17 @@ TME_M68K_INSN(tme_m68k_asl8) flags *= TME_M68K_FLAG_C; flags |= (flags * TME_M68K_FLAG_X); res <<= 1; + sign_bits_mask = (tme_uint8_t) -1; if (count != 8 - 1) { if (count < 8) { - sign_bits >>= ((8 - 1) - count); + sign_bits_mask <<= ((8 - 1) - count); } else { - sign_bits |= (sign_bits << 1); - sign_bits &= -2; + sign_bits ^= !(sign_bits + 1); } } - if ((sign_bits + 1) & sign_bits) { + sign_bits &= sign_bits_mask; + if (sign_bits != 0 && sign_bits != sign_bits_mask) { flags |= TME_M68K_FLAG_V; } } @@ -633,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: */ @@ -937,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); + } - /* write: */ + /* 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); + + /* 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; } @@ -994,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); @@ -1344,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; } @@ -1407,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; } @@ -1469,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; @@ -1496,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) { @@ -1521,7 +1622,7 @@ TME_M68K_INSN(tme_m68k_movea16) TME_M68K_INSN(tme_m68k_asl16) { unsigned int count; - tme_uint16_t sign_bits; + tme_uint16_t sign_bits, sign_bits_mask; tme_uint16_t res; tme_uint8_t flags; @@ -1540,27 +1641,28 @@ TME_M68K_INSN(tme_m68k_asl16) shifting in order to generate V. in general, the idea is to get all of the bits that will ever - appear in the sign position into sign_bits; if sign_bits is - all-bits-one or all-bits zero, clear V, else set V. a good trick - is that ((sign_bits + 1) & sign_bits) is nonzero iff all of the - bits in sign_bits are the same. + appear in the sign position into sign_bits, with a mask in + sign_bits_mask. if (sign_bits & sign_bits_mask) is zero or + sign_bits_mask, clear V, else set V. - start by loading all of the operand into sign_bits. + start by loading the operand into sign_bits and setting + sign_bits_mask to all-bits-one. if the shift count is exactly 16 - 1, then all of the bits of the operand will appear in the sign position. if the shift count is less than 16 - 1, then some of the less significant bits of the operand will never appear in the - sign position, so we can shift them off of sign_bits now. + sign position, so we can shift sign_bits_mask to ignore them. if the shift count is greater than 16 - 1, then all of the bits in the operand, plus at least one zero bit, will appear in the sign position. the only way that the sign bit will never change during the shift is if the operand was zero to begin with. - we need to change sign_bits such that ((sign_bits + 1) & - sign_bits) will be zero iff the operand was zero to begin with. - the magic below does just that: */ + without any changes to sign_bits or sign_bits_mask, the final + test will always work, except when sign_bits is all-bits-one. + the magic below clears the least-significant bit of sign_bits + iff sign_bits is all-bits-one: */ sign_bits = res; if (63 > SHIFTMAX_INT16_T && count > 16) { @@ -1571,16 +1673,17 @@ TME_M68K_INSN(tme_m68k_asl16) flags *= TME_M68K_FLAG_C; flags |= (flags * TME_M68K_FLAG_X); res <<= 1; + sign_bits_mask = (tme_uint16_t) -1; if (count != 16 - 1) { if (count < 16) { - sign_bits >>= ((16 - 1) - count); + sign_bits_mask <<= ((16 - 1) - count); } else { - sign_bits |= (sign_bits << 1); - sign_bits &= -2; + sign_bits ^= !(sign_bits + 1); } } - if ((sign_bits + 1) & sign_bits) { + sign_bits &= sign_bits_mask; + if (sign_bits != 0 && sign_bits != sign_bits_mask) { flags |= TME_M68K_FLAG_V; } } @@ -1620,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: */ @@ -1960,8 +2073,19 @@ TME_M68K_INSN(tme_m68k_movem_rm16) tme_uint16_t mask, bit; unsigned int ea_mode; tme_uint32_t addend; - - TME_M68K_INSN_CANFAULT; + 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); @@ -1971,13 +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; for (bit = 1; bit != 0; bit <<= 1) { if (mask & bit) { if (!TME_M68K_SEQUENCE_RESTARTING) { @@ -1992,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)); @@ -2041,8 +2183,19 @@ TME_M68K_INSN(tme_m68k_movem_mr16) tme_uint16_t mask, bit; unsigned int ea_mode; tme_uint32_t addend; - - TME_M68K_INSN_CANFAULT; + 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); @@ -2051,7 +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; for (bit = 1; bit != 0; bit <<= 1) { if (mask & bit) { tme_m68k_read_memx16(ic); @@ -2077,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; } @@ -2091,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; } @@ -2248,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); @@ -2597,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; } @@ -2660,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; } @@ -2722,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; @@ -2749,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) { @@ -2837,7 +3043,7 @@ TME_M68K_INSN(tme_m68k_bset32) TME_M68K_INSN(tme_m68k_asl32) { unsigned int count; - tme_uint32_t sign_bits; + tme_uint32_t sign_bits, sign_bits_mask; tme_uint32_t res; tme_uint8_t flags; @@ -2856,27 +3062,28 @@ TME_M68K_INSN(tme_m68k_asl32) shifting in order to generate V. in general, the idea is to get all of the bits that will ever - appear in the sign position into sign_bits; if sign_bits is - all-bits-one or all-bits zero, clear V, else set V. a good trick - is that ((sign_bits + 1) & sign_bits) is nonzero iff all of the - bits in sign_bits are the same. + appear in the sign position into sign_bits, with a mask in + sign_bits_mask. if (sign_bits & sign_bits_mask) is zero or + sign_bits_mask, clear V, else set V. - start by loading all of the operand into sign_bits. + start by loading the operand into sign_bits and setting + sign_bits_mask to all-bits-one. if the shift count is exactly 32 - 1, then all of the bits of the operand will appear in the sign position. if the shift count is less than 32 - 1, then some of the less significant bits of the operand will never appear in the - sign position, so we can shift them off of sign_bits now. + sign position, so we can shift sign_bits_mask to ignore them. if the shift count is greater than 32 - 1, then all of the bits in the operand, plus at least one zero bit, will appear in the sign position. the only way that the sign bit will never change during the shift is if the operand was zero to begin with. - we need to change sign_bits such that ((sign_bits + 1) & - sign_bits) will be zero iff the operand was zero to begin with. - the magic below does just that: */ + without any changes to sign_bits or sign_bits_mask, the final + test will always work, except when sign_bits is all-bits-one. + the magic below clears the least-significant bit of sign_bits + iff sign_bits is all-bits-one: */ sign_bits = res; if (63 > SHIFTMAX_INT32_T && count > 32) { @@ -2887,16 +3094,17 @@ TME_M68K_INSN(tme_m68k_asl32) flags *= TME_M68K_FLAG_C; flags |= (flags * TME_M68K_FLAG_X); res <<= 1; + sign_bits_mask = (tme_uint32_t) -1; if (count != 32 - 1) { if (count < 32) { - sign_bits >>= ((32 - 1) - count); + sign_bits_mask <<= ((32 - 1) - count); } else { - sign_bits |= (sign_bits << 1); - sign_bits &= -2; + sign_bits ^= !(sign_bits + 1); } } - if ((sign_bits + 1) & sign_bits) { + sign_bits &= sign_bits_mask; + if (sign_bits != 0 && sign_bits != sign_bits_mask) { flags |= TME_M68K_FLAG_V; } } @@ -2936,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: */ @@ -3290,8 +3508,19 @@ TME_M68K_INSN(tme_m68k_movem_rm32) tme_uint16_t mask, bit; unsigned int ea_mode; tme_uint32_t addend; - - TME_M68K_INSN_CANFAULT; + 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); @@ -3301,13 +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; for (bit = 1; bit != 0; bit <<= 1) { if (mask & bit) { if (!TME_M68K_SEQUENCE_RESTARTING) { @@ -3322,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)); @@ -3389,8 +3636,19 @@ TME_M68K_INSN(tme_m68k_movem_mr32) tme_uint16_t mask, bit; unsigned int ea_mode; tme_uint32_t addend; - - TME_M68K_INSN_CANFAULT; + 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); @@ -3399,7 +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; for (bit = 1; bit != 0; bit <<= 1) { if (mask & bit) { tme_m68k_read_memx32(ic); @@ -3425,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; } @@ -3439,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; } @@ -3596,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; @@ -3610,25 +3881,41 @@ TME_M68K_INSN(tme_m68k_moves32) void tme_m68k_read_memx8(struct tme_m68k *ic) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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_memx8 = *((tme_uint8_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address)); + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -3641,11 +3928,14 @@ 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, (TME_M68K_LOG_HANDLE(ic), - _("read_memx8\t%d:0x%08x:\t0x%02x"), + _("read_memx8 %d:0x%08x: 0x%02x"), ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx8)); @@ -3655,25 +3945,41 @@ tme_m68k_read_memx8(struct tme_m68k *ic) void tme_m68k_read_mem8(struct tme_m68k *ic, int ireg) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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 */ - /* 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: */ + /* 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_uint8(ireg) = *((tme_uint8_t *) (tlb->tme_m68k_tlb_emulator_off_read + linear_address)); + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -3686,11 +3992,14 @@ 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, (TME_M68K_LOG_HANDLE(ic), - _("read_mem8\t%d:0x%08x:\t0x%02x"), + _("read_mem8 %d:0x%08x: 0x%02x"), ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint8(ireg))); @@ -3700,34 +4009,50 @@ tme_m68k_read_mem8(struct tme_m68k *ic, void tme_m68k_write_memx8(struct tme_m68k *ic) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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); tme_m68k_log(ic, 1000, TME_OK, (TME_M68K_LOG_HANDLE(ic), - _("write_memx8\t%d:0x%08x:\t0x%02x"), + _("write_memx8 %d:0x%08x: 0x%02x"), ic->_tme_m68k_ea_function_code, 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; + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_write != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -3739,40 +4064,59 @@ 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: */ void tme_m68k_write_mem8(struct tme_m68k *ic, int ireg) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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); tme_m68k_log(ic, 1000, TME_OK, (TME_M68K_LOG_HANDLE(ic), - _("write_mem8\t%d:0x%08x:\t0x%02x"), + _("write_mem8 %d:0x%08x: 0x%02x"), ic->_tme_m68k_ea_function_code, 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); + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_write != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -3784,40 +4128,52 @@ 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: */ void tme_m68k_read_memx16(struct tme_m68k *ic) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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 + sizeof(tme_uint16_t) - 1; - struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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 */ - /* 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: */ + /* 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) - && 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); + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -3830,11 +4186,14 @@ 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, (TME_M68K_LOG_HANDLE(ic), - _("read_memx16\t%d:0x%08x:\t0x%04x"), + _("read_memx16 %d:0x%08x: 0x%04x"), ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx16)); @@ -3844,34 +4203,43 @@ tme_m68k_read_memx16(struct tme_m68k *ic void tme_m68k_read_mem16(struct tme_m68k *ic, int ireg) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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 + sizeof(tme_uint16_t) - 1; - struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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) - && 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); + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -3884,11 +4252,14 @@ 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, (TME_M68K_LOG_HANDLE(ic), - _("read_mem16\t%d:0x%08x:\t0x%04x"), + _("read_mem16 %d:0x%08x: 0x%04x"), ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint16(ireg))); @@ -3898,91 +4269,169 @@ tme_m68k_read_mem16(struct tme_m68k *ic, tme_uint16_t tme_m68k_fetch16(struct tme_m68k *ic, tme_uint32_t pc) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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 = &ic->_tme_m68k_itlb; + 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))) { - - /* 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; - } + /* if we're not restarting: */ + if (!TME_M68K_SEQUENCE_RESTARTING) { - /* 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); + /* 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_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF)) { + + /* 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)); + + /* 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"), + _("fetch16 %d:0x%08x: 0x%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: */ void tme_m68k_pop16(struct tme_m68k *ic, tme_uint16_t *_value) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; unsigned int function_code = TME_M68K_FUNCTION_CODE_DATA(ic); 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); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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) - && 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); + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -3995,11 +4444,14 @@ 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, (TME_M68K_LOG_HANDLE(ic), - _("pop16\t%d:0x%08x:\t0x%04x"), + _("pop16 %d:0x%08x: 0x%04x"), function_code, ic->tme_m68k_ireg_a7, *_value)); @@ -4012,97 +4464,137 @@ tme_m68k_pop16(struct tme_m68k *ic, tme_ void tme_m68k_write_memx16(struct tme_m68k *ic) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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 + sizeof(tme_uint16_t) - 1; - struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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); tme_m68k_log(ic, 1000, TME_OK, (TME_M68K_LOG_HANDLE(ic), - _("write_memx16\t%d:0x%08x:\t0x%04x"), + _("write_memx16 %d:0x%08x: 0x%04x"), ic->_tme_m68k_ea_function_code, 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) - && 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); + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_write != TME_EMULATOR_OFF_UNDEF)) { + + /* 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); + +#if 1 + { + extern int printf(const char *format, ...); + if ((int)ic->tme_m68k_ireg_pc == 0x3c3bc) { + printf("tme_m68k_write_memx16 #1; mem %x, val %x\n", (int)mem, (int)mem_value); + } + } +#endif + + /* 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; } /* otherwise, do the bus cycles the slow way: */ else { +#if 1 + { + extern int printf(const char *format, ...); + if ((int)ic->tme_m68k_ireg_pc == 0x3c3bc) { + mem = (tme_shared tme_uint16_t *) (tlb->tme_m68k_tlb_emulator_off_write + linear_address_first); + mem_value = tme_htobe_u16(ic->tme_m68k_ireg_memx16); + printf("tme_m68k_write_memx16 #2; mem %x, val %x\n", (int)mem, (int)mem_value); + } + } +#endif 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_NORMAL); } + + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); } /* this writes a 16-bit mem value: */ void tme_m68k_write_mem16(struct tme_m68k *ic, int ireg) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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 + sizeof(tme_uint16_t) - 1; - struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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); tme_m68k_log(ic, 1000, TME_OK, (TME_M68K_LOG_HANDLE(ic), - _("write_mem16\t%d:0x%08x:\t0x%04x"), + _("write_mem16 %d:0x%08x: 0x%04x"), ic->_tme_m68k_ea_function_code, 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) - && 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); + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_write != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -4114,49 +4606,61 @@ 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: */ void tme_m68k_push16(struct tme_m68k *ic, tme_uint16_t value) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; unsigned int function_code = TME_M68K_FUNCTION_CODE_DATA(ic); 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); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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); tme_m68k_log(ic, 1000, TME_OK, (TME_M68K_LOG_HANDLE(ic), - _("push16\t%d:0x%08x:\t0x%04x"), + _("push16 %d:0x%08x: 0x%04x"), function_code, 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) - && 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); + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_write != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -4168,6 +4672,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); } @@ -4177,63 +4684,43 @@ tme_m68k_push16(struct tme_m68k *ic, tme void tme_m68k_read_memx32(struct tme_m68k *ic) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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 + sizeof(tme_uint32_t) - 1; - struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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) - && 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 */ + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -4246,11 +4733,14 @@ 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, (TME_M68K_LOG_HANDLE(ic), - _("read_memx32\t%d:0x%08x:\t0x%08x"), + _("read_memx32 %d:0x%08x: 0x%08x"), ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_memx32)); @@ -4260,63 +4750,43 @@ tme_m68k_read_memx32(struct tme_m68k *ic void tme_m68k_read_mem32(struct tme_m68k *ic, int ireg) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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 + sizeof(tme_uint32_t) - 1; - struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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) - && 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 */ + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -4329,11 +4799,14 @@ 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, (TME_M68K_LOG_HANDLE(ic), - _("read_mem32\t%d:0x%08x:\t0x%08x"), + _("read_mem32 %d:0x%08x: 0x%08x"), ic->_tme_m68k_ea_function_code, ic->_tme_m68k_ea_address, ic->tme_m68k_ireg_uint32(ireg))); @@ -4343,149 +4816,169 @@ tme_m68k_read_mem32(struct tme_m68k *ic, tme_uint32_t tme_m68k_fetch32(struct tme_m68k *ic, tme_uint32_t pc) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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)); - - /* 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 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); + struct tme_m68k_tlb *tlb = &ic->_tme_m68k_itlb; + 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--; } - 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); + + /* if we're not restarting: */ + if (!TME_M68K_SEQUENCE_RESTARTING) { + + /* 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_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF)) { + + /* 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"), + _("fetch32 %d:0x%08x: 0x%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: */ void tme_m68k_pop32(struct tme_m68k *ic, tme_uint32_t *_value) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; unsigned int function_code = TME_M68K_FUNCTION_CODE_DATA(ic); 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); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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) - && 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 */ + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -4498,11 +4991,14 @@ 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, (TME_M68K_LOG_HANDLE(ic), - _("pop32\t%d:0x%08x:\t0x%08x"), + _("pop32 %d:0x%08x: 0x%08x"), function_code, ic->tme_m68k_ireg_a7, *_value)); @@ -4515,69 +5011,52 @@ tme_m68k_pop32(struct tme_m68k *ic, tme_ void tme_m68k_write_memx32(struct tme_m68k *ic) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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 + sizeof(tme_uint32_t) - 1; - struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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); tme_m68k_log(ic, 1000, TME_OK, (TME_M68K_LOG_HANDLE(ic), - _("write_memx32\t%d:0x%08x:\t0x%08x"), + _("write_memx32 %d:0x%08x: 0x%08x"), ic->_tme_m68k_ea_function_code, 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) - && 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 */ + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_write != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -4589,75 +5068,61 @@ 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: */ void tme_m68k_write_mem32(struct tme_m68k *ic, int ireg) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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 + sizeof(tme_uint32_t) - 1; - struct tme_m68k_tlb *tlb = TME_M68K_TLB_ENTRY(ic, function_code, linear_address_first); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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); tme_m68k_log(ic, 1000, TME_OK, (TME_M68K_LOG_HANDLE(ic), - _("write_mem32\t%d:0x%08x:\t0x%08x"), + _("write_mem32 %d:0x%08x: 0x%08x"), ic->_tme_m68k_ea_function_code, 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) - && 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 */ + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_write != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -4669,75 +5134,61 @@ 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: */ void tme_m68k_push32(struct tme_m68k *ic, tme_uint32_t value) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; unsigned int function_code = TME_M68K_FUNCTION_CODE_DATA(ic); 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); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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); tme_m68k_log(ic, 1000, TME_OK, (TME_M68K_LOG_HANDLE(ic), - _("push32\t%d:0x%08x:\t0x%08x"), + _("push32 %d:0x%08x: 0x%08x"), function_code, 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) - && 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 */ + && (ic->_tme_m68k_bus_16bit & linear_address_first) == 0 + && tme_m68k_tlb_is_valid(tlb) + && tlb->tme_m68k_tlb_bus_context == bus_context + && (tlb->tme_m68k_tlb_function_codes_mask + & TME_BIT(function_code)) + && linear_address_first >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + && linear_address_last <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last + && tlb->tme_m68k_tlb_emulator_off_write != TME_EMULATOR_OFF_UNDEF)) { + + /* 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; } @@ -4749,6 +5200,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); } @@ -4758,33 +5212,23 @@ tme_m68k_push32(struct tme_m68k *ic, tme void tme_m68k_read_mem(struct tme_m68k *ic, tme_uint8_t *buffer, unsigned int count) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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))) { - - 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; - } +#ifdef _TME_M68K_STATS + ic->tme_m68k_stats.tme_m68k_stats_memory_total++; +#endif /* _TME_M68K_STATS */ - /* 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); - } + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); + + /* 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); @@ -4818,12 +5262,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) { @@ -4870,7 +5313,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; ) { @@ -4902,7 +5344,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; } @@ -4920,7 +5362,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)); @@ -4934,32 +5376,83 @@ 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_m68k_tlb_is_invalid(tlb) + || tlb->tme_m68k_tlb_bus_context != ic->_tme_m68k_bus_context + || (tlb->tme_m68k_tlb_function_codes_mask & TME_BIT(function_code)) == 0 + || linear_address < (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + || linear_address > (tme_bus_addr32_t) 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) + > (((tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last) - linear_address))) { + cycle_size = (((tme_bus_addr32_t) 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 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]; } + + /* 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: */ @@ -4974,22 +5467,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_m68k_tlb_is_invalid(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; } } @@ -4999,13 +5503,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: */ @@ -5021,6 +5521,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); } @@ -5032,10 +5533,14 @@ tme_m68k_read(struct tme_m68k *ic, void tme_m68k_write_mem(struct tme_m68k *ic, tme_uint8_t *buffer, unsigned int count) { + tme_bus_context_t bus_context = ic->_tme_m68k_bus_context; 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); + struct tme_m68k_tlb *tlb = TME_M68K_DTLB_ENTRY(ic, bus_context, 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); @@ -5051,28 +5556,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))) { - - 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; - } + /* busy this TLB entry: */ + tme_m68k_tlb_busy(tlb); - /* 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); - } + /* 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); + + /* unbusy this TLB entry: */ + tme_m68k_tlb_unbusy(tlb); } /* this writes a region of address space using actual bus cycles: */ @@ -5092,12 +5583,25 @@ 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 1 + int should_fault = 0; + { + extern int printf(const char *format, ...); + if ((int)ic->tme_m68k_ireg_pc == 0x3c3bc) { + printf("tme_m68k_write; *_linear_address %x\n", (int)*_linear_address); + if ((int)*_linear_address == 0x509000) { + should_fault = 1; + } + printf("tme_m68k_write; should_fault %d\n", should_fault); + } + } +#endif /* if we're not restarting, everything is fresh: */ if (!TME_M68K_SEQUENCE_RESTARTING) { @@ -5140,14 +5644,29 @@ tme_m68k_write(struct tme_m68k *ic, transfer number and return: */ else { TME_M68K_SEQUENCE_TRANSFER_STEP; +#if 0 + { + extern int printf(const char *format, ...); + if ((int)ic->tme_m68k_ireg_pc == 0x3c3bc) { + printf("tme_m68k_write; returning\n"); + } + } +#endif return; } /* 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; ) { +#if 0 + { + extern int printf(const char *format, ...); + if ((int)ic->tme_m68k_ireg_pc == 0x3c3bc) { + printf("tme_m68k_write; loop %x\n", (int)linear_address); + } + } +#endif resid = reg_size - transferred; /* start the bus cycle structure: */ @@ -5176,7 +5695,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; } @@ -5199,32 +5718,78 @@ 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_m68k_tlb_is_invalid(tlb) + || tlb->tme_m68k_tlb_bus_context != ic->_tme_m68k_bus_context + || (tlb->tme_m68k_tlb_function_codes_mask & TME_BIT(function_code)) == 0 + || linear_address < (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first + || linear_address > (tme_bus_addr32_t) 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) + > (((tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last) - linear_address))) { + cycle_size = (((tme_bus_addr32_t) 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: */ @@ -5239,22 +5804,43 @@ 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 0 + { + extern int printf(const char *format, ...); + if ((int)ic->tme_m68k_ireg_pc == 0x3c3bc) { + printf("tme_m68k_write; old %x\n", (int)err); + err = ENOENT; + } + } +#endif + + /* if the TLB entry was invalidated before the write: */ + if (err == EBADF + && tme_m68k_tlb_is_invalid(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; } } @@ -5264,13 +5850,21 @@ 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); - } +#if 1 + { + extern int printf(const char *format, ...); + if ((int)ic->tme_m68k_ireg_pc == 0x3c3bc) { + if (0) printf("tme_m68k_write; not returning\n"); + if (should_fault) { + printf("tme_m68k_write; faulting\n"); + exception = TME_M68K_EXCEPTION_BERR; + } + } + } +#endif + /* 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: */ @@ -5298,11 +5892,21 @@ 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); } /* otherwise, this transfer has now completed: */ TME_M68K_SEQUENCE_TRANSFER_STEP; + +#if 0 + { + extern int printf(const char *format, ...); + if ((int)ic->tme_m68k_ireg_pc == 0x3c3bc) { + printf("tme_m68k_write; end\n"); + } + } +#endif } TME_M68K_INSN(tme_m68k_abcd) @@ -5314,20 +5918,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); } @@ -5365,8 +5970,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); @@ -5388,20 +5991,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); } @@ -5439,8 +6043,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); @@ -5596,8 +6198,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: */ @@ -5622,9 +6225,9 @@ TME_M68K_INSN(tme_m68k_divu) TME_M68K_INSN(tme_m68k_mulul) { -#ifndef HAVE_UINT64_T +#ifndef TME_HAVE_INT64_T abort(); -#else /* HAVE_UINT64_T */ +#else /* TME_HAVE_INT64_T */ unsigned int flag_v; int ireg_dh; int ireg_dl; @@ -5651,18 +6254,18 @@ TME_M68K_INSN(tme_m68k_mulul) flags = ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X; if (((tme_int64_t) res) < 0) flags |= TME_M68K_FLAG_N; if (res == 0) flags |= TME_M68K_FLAG_Z; - if (res > 0xffffffff) flags |= flag_v; + if (res > 0xffffffffUL) flags |= flag_v; ic->tme_m68k_ireg_ccr = flags; TME_M68K_INSN_OK; -#endif /* HAVE_UINT64_T */ +#endif /* TME_HAVE_INT64_T */ } TME_M68K_INSN(tme_m68k_divul) { -#ifndef HAVE_UINT64_T +#ifndef TME_HAVE_INT64_T abort(); -#else /* HAVE_UINT64_T */ +#else /* TME_HAVE_INT64_T */ int ireg_dr; int ireg_dq; tme_uint64_t dividend, quotient; @@ -5683,8 +6286,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: */ @@ -5693,7 +6297,7 @@ TME_M68K_INSN(tme_m68k_divul) /* set the flags and return the quotient and remainder: */ flags = ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X; - if (quotient > 0xffffffff) { + if (quotient > 0xffffffffUL) { flags |= TME_M68K_FLAG_V; } else { @@ -5707,7 +6311,7 @@ TME_M68K_INSN(tme_m68k_divul) ic->tme_m68k_ireg_ccr = flags; TME_M68K_INSN_OK; -#endif /* HAVE_UINT64_T */ +#endif /* TME_HAVE_INT64_T */ } TME_M68K_INSN(tme_m68k_muls) @@ -5749,8 +6353,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: */ @@ -5759,7 +6364,7 @@ TME_M68K_INSN(tme_m68k_divs) /* set the flags and return the quotient and remainder: */ flags = ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X; - if (quotient > 0xffff || quotient < -32768) { + if (quotient > 0x7fff || quotient < -32768) { flags |= TME_M68K_FLAG_V; } else { @@ -5775,9 +6380,9 @@ TME_M68K_INSN(tme_m68k_divs) TME_M68K_INSN(tme_m68k_mulsl) { -#ifndef HAVE_UINT64_T +#ifndef TME_HAVE_INT64_T abort(); -#else /* HAVE_UINT64_T */ +#else /* TME_HAVE_INT64_T */ unsigned int flag_v; int ireg_dh; int ireg_dl; @@ -5804,18 +6409,18 @@ TME_M68K_INSN(tme_m68k_mulsl) flags = ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X; if (((tme_int64_t) res) < 0) flags |= TME_M68K_FLAG_N; if (res == 0) flags |= TME_M68K_FLAG_Z; - if (res > 0xffffffff || res < -2147483648) flags |= flag_v; + if (res > 0x7fffffffL || res < ((0L - 0x7fffffffL) - 1L)) flags |= flag_v; ic->tme_m68k_ireg_ccr = flags; TME_M68K_INSN_OK; -#endif /* HAVE_UINT64_T */ +#endif /* TME_HAVE_INT64_T */ } TME_M68K_INSN(tme_m68k_divsl) { -#ifndef HAVE_UINT64_T +#ifndef TME_HAVE_INT64_T abort(); -#else /* HAVE_UINT64_T */ +#else /* TME_HAVE_INT64_T */ int ireg_dr; int ireg_dq; tme_int64_t dividend, quotient; @@ -5836,8 +6441,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: */ @@ -5846,7 +6452,7 @@ TME_M68K_INSN(tme_m68k_divsl) /* set the flags and return the quotient and remainder: */ flags = ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X; - if (quotient > 0xffffffff || quotient < -2147483648) { + if (quotient > 0x7fffffffL || quotient < ((0L - 0x7fffffffL) - 1L)) { flags |= TME_M68K_FLAG_V; } else { @@ -5860,7 +6466,7 @@ TME_M68K_INSN(tme_m68k_divsl) ic->tme_m68k_ireg_ccr = flags; TME_M68K_INSN_OK; -#endif /* HAVE_UINT64_T */ +#endif /* TME_HAVE_INT64_T */ } /* automatically generated by m68k-misc-auto.sh, do not edit! */