--- tme/ic/m68k/m68k-insns-auto.sh 2018/04/24 16:39:03 1.1.1.2 +++ tme/ic/m68k/m68k-insns-auto.sh 2018/04/24 16:43:48 1.1.1.5 @@ -1,6 +1,6 @@ #! /bin/sh -# $Id: m68k-insns-auto.sh,v 1.1.1.2 2018/04/24 16:39:03 root Exp $ +# $Id: m68k-insns-auto.sh,v 1.1.1.5 2018/04/24 16:43:48 root Exp $ # ic/m68k/m68k-insns-auto.sh - automatically generates C code # for many m68k emulation instructions: @@ -48,7 +48,7 @@ done PROG=`basename $0` cat <_tme_m68k_ea_function_code = function_code;" - echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst);" - echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) += ireg_dst_adjust;" - echo " }" - echo " tme_m68k_read_memx${size}(ic);" - echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" - echo " ic->_tme_m68k_ea_function_code = function_code;" echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src);" echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) += ireg_src_adjust;" echo " }" echo " tme_m68k_read_mem${size}(ic, TME_M68K_IREG_MEMY${size});" + echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" + echo " ic->_tme_m68k_ea_function_code = function_code;" + echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst);" + echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) += ireg_dst_adjust;" + echo " }" + echo " tme_m68k_read_memx${size}(ic);" echo " ${dst} = ic->tme_m68k_ireg_memx${size};" echo " ${src} = ic->tme_m68k_ireg_memy${size};" ;; @@ -185,17 +185,17 @@ for size in 8 16 32; do echo " if (memory) {" echo " TME_M68K_INSN_CANFAULT;" echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" - echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust;" - echo " ic->_tme_m68k_ea_function_code = function_code;" - echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst);" - echo " }" - echo " tme_m68k_read_memx${size}(ic);" - echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) -= ireg_src_adjust;" echo " ic->_tme_m68k_ea_function_code = function_code;" echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src);" echo " }" echo " tme_m68k_read_mem${size}(ic, TME_M68K_IREG_MEMY${size});" + echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" + echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust;" + echo " ic->_tme_m68k_ea_function_code = function_code;" + echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst);" + echo " }" + echo " tme_m68k_read_memx${size}(ic);" echo " ${dst} = ic->tme_m68k_ireg_memx${size};" echo " ${src} = ic->tme_m68k_ireg_memy${size};" echo " }" @@ -341,6 +341,43 @@ for size in 8 16 32; do if test $name = cmpa; then size=16; fi done + # generate the wrapper functions for a move of an address register + # to a predecrement or postincrement EA with that same address + # register: + for name in pd pi; do + + # a move of an address registers are only word and long: + if test $size = 8; then continue; fi + + # if we're making the header, just emit a declaration: + if $header; then + echo "TME_M68K_INSN_DECL(tme_m68k_move_sr${name}${size});" + continue + fi + + echo "" + echo "/* a move of an address register to a predecrement or" + echo " postincrement EA with that same address register, must" + echo " store the original value of the address register. since the" + echo " predecrement and postincrement code in the executer updates" + echo " the address register before the move has happened, we wrap" + echo " the normal move function in this one, that gives an op1" + echo " argument that is the original value of the address register: */" + echo "TME_M68K_INSN(tme_m68k_move_sr${name}${size})" + echo "{" + if test ${name} = pd; then op='+'; else op='-'; fi + echo " /* NB: both this function and tme_m68k_move${size}()" + echo " get the source operand as _op1, and the destination" + echo " operand as _op0: */" + echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" + echo " *((tme_uint${size}_t *) _op0)" + echo " = (*((tme_uint${size}_t *) _op1)" + echo " ${op} sizeof(tme_uint${size}_t));" + echo " }" + echo " tme_m68k_move${size}(ic, _op0, _op0);" + echo "}" + done + # generate the address math functions: for name in suba adda movea; do @@ -447,7 +484,7 @@ for size in 8 16 32; do echo " generate the X, V, and C flags: */" echo " if (count > 0) {" case "${name}" in - [al]sr) + lsr) echo " if (63 > SHIFTMAX_INT${size}_T" echo " && count > ${size}) {" echo " res = 0;" @@ -458,6 +495,27 @@ for size in 8 16 32; do echo " flags |= (flags * TME_M68K_FLAG_X);" echo " res >>= 1;" ;; + asr) + echo " if (63 > SHIFTMAX_INT${size}_T" + echo " && count > ${size}) {" + echo " res = 0 - (res < 0);" + echo " }" + echo "#ifdef SHIFTSIGNED_INT${size}_T" + echo " res >>= (count - 1);" + echo "#else /* !SHIFTSIGNED_INT${size}_T */" + echo " for (; --count > 0; ) {" + echo " res = (res & ~((tme_${sign}int${size}_t) 1)) / 2;" + echo " }" + echo "#endif /* !SHIFTSIGNED_INT${size}_T */" + echo " flags = (res & 1);" + echo " flags *= TME_M68K_FLAG_C;" + echo " flags |= (flags * TME_M68K_FLAG_X);" + echo "#ifdef SHIFTSIGNED_INT${size}_T" + echo " res >>= 1;" + echo "#else /* !SHIFTSIGNED_INT${size}_T */" + echo " res = (res & ~((tme_${sign}int${size}_t) 1)) / 2;" + echo "#endif /* !SHIFTSIGNED_INT${size}_T */" + ;; [al]sl) if test ${name} = asl; then echo "" @@ -657,6 +715,20 @@ for size in 8 16 32; do echo " tme_uint16_t mask, bit;" echo " unsigned int ea_mode;" echo " tme_uint32_t addend;" + echo " tme_uint32_t total_size;" + + echo " /* get the register mask, and figure out the total size" + echo " of the transfer: */" + echo " mask = TME_M68K_INSN_SPECOP;" + echo " total_size = 0;" + echo " if (mask != 0) {" + echo " TME_M68K_INSN_CANFAULT;" + echo " bit = mask;" + echo " do {" + echo " total_size += sizeof(tme_uint${size}_t);" + echo " bit &= (bit - 1);" + echo " } while (bit != 0);" + echo " }" echo "" echo " /* figure out what direction to move in, and where to start from: */" @@ -668,6 +740,19 @@ for size in 8 16 32; do echo " direction = -1;" echo " ireg = TME_M68K_IREG_A7;" echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" + echo "" + echo " /* \"For the MC68020, MC68030, MC68040, and CPU32, if" + echo " the addressing register is also moved to memory, the" + echo " value written is the initial register value decremented " + echo " by the size of the operation. The MC68000 and MC68010 " + echo " write the initial register value (not decremented).\" */" + echo " if (ic->tme_m68k_type >= TME_M68K_M68020) {" + echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0" + echo " + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3))" + echo " = (ic->_tme_m68k_ea_address - total_size);" + echo " }" + echo "" + echo " /* predecrement the effective address for the first transfer: */" echo " ic->_tme_m68k_ea_address -= sizeof(tme_uint${size}_t);" echo " }" echo " }" @@ -676,10 +761,6 @@ for size in 8 16 32; do echo "" echo " /* do the transfer: */" - echo " mask = TME_M68K_INSN_SPECOP;" - echo " if (mask != 0) {" - echo " TME_M68K_INSN_CANFAULT;" - echo " }" echo " for (bit = 1; bit != 0; bit <<= 1) {" echo " if (mask & bit) {" if test $name = rm; then @@ -709,7 +790,13 @@ for size in 8 16 32; do # address register: if test $name = rm; then echo " /* if this is the predecrement mode, update the address register: */" - echo " if (ea_mode == 4) {" + echo " /* \"For the MC68020, MC68030, MC68040, and CPU32, if" + echo " the addressing register is also moved to memory, the" + echo " value written is the initial register value decremented " + echo " by the size of the operation. The MC68000 and MC68010 " + echo " write the initial register value (not decremented).\" */" + echo " if (ea_mode == 4" + echo " && ic->tme_m68k_type < TME_M68K_M68020) {" echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0" echo " + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3))" echo " = (ic->_tme_m68k_ea_address + sizeof(tme_uint${size}_t));" @@ -740,28 +827,109 @@ for size in 8 16 32; do echo "{" echo " if (*((tme_int${size}_t *) _op0) < 0) {" echo " ic->tme_m68k_ireg_ccr |= TME_M68K_FLAG_N;" + echo " ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc;" echo " ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next;" - echo " TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_GROUP2(6));" + echo " TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_CHK));" echo " }" echo " if (*((tme_int${size}_t *) _op0) > *((tme_int${size}_t *) _op1)) {" echo " ic->tme_m68k_ireg_ccr &= ~TME_M68K_FLAG_N;" + echo " ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc;" echo " ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next;" - echo " TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_GROUP2(6));" + echo " TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_CHK));" echo " }" echo " TME_M68K_INSN_OK;" echo "}" fi fi - # cas and cas2: - for name in cas cas2_; do - - # cas2 doesn't do byte operands: - buffers=x - if test $name = cas2_; then - if test $size = 8; then continue; fi - buffers="x y" - fi + # cas: + name=cas + if $header; then + echo "TME_M68K_INSN_DECL(tme_m68k_${name}${size});" + else + echo "" + echo "/* ${name}${size}: */" + echo "TME_M68K_INSN(tme_m68k_${name}${size})" + echo "{" + echo " struct tme_m68k_rmw rmw;" + echo " struct tme_m68k_tlb *tlb;" + echo " int ireg_dc, ireg_du;" + echo " tme_uint${size}_t value_dc, value_du, value_mem;" + echo "" + echo " /* start the read/modify/write cycle: */" + echo " rmw.tme_m68k_rmw_addresses[0] = ic->_tme_m68k_ea_address;" + echo " rmw.tme_m68k_rmw_address_count = 1;" + echo " rmw.tme_m68k_rmw_size = sizeof(tme_uint${size}_t);" + echo " if (tme_m68k_rmw_start(ic," + echo " &rmw)) {" + echo " TME_M68K_INSN_OK;" + echo " }" + echo "" + echo " /* get the compare and update registers: */" + echo " ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 3);" + echo " ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 6, 3);" + echo "" + echo " /* if we can do the fast compare-and-exchange: */" + echo " if (!rmw.tme_m68k_rmw_slow_reads[0]) {" + echo "" + echo " /* get the compare and update values in big-endian byte order: */" + echo " value_dc = ic->tme_m68k_ireg_uint${size}(ireg_dc${reg_size_shift});" + echo " value_du = ic->tme_m68k_ireg_uint${size}(ireg_du${reg_size_shift});" + if test ${size} != 8; then + echo " value_dc = tme_htobe_u${size}(value_dc);" + echo " value_du = tme_htobe_u${size}(value_du);" + fi + echo "" + echo " /* get this TLB entry: */" + echo " tlb = rmw.tme_m68k_rmw_tlbs[0];" + echo "" + echo " /* this TLB entry must allow fast reading and fast writing" + echo " to the same memory: */" + echo " assert (tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF" + echo " && tlb->tme_m68k_tlb_emulator_off_write == tlb->tme_m68k_tlb_emulator_off_read);" + echo "" + echo " /* do the compare-and-exchange: */" + echo " value_mem =" + echo " tme_memory_atomic_cx${size}(((tme_shared tme_uint${size}_t *)" + echo " (tlb->tme_m68k_tlb_emulator_off_read" + echo " + ic->_tme_m68k_ea_address))," + echo " value_dc," + echo " value_du," + echo " tlb->tme_m68k_tlb_bus_rwlock," + echo " sizeof(tme_uint8_t));" + echo -n " ic->tme_m68k_ireg_memx${size} = " + if test ${size} != 8; then echo -n "tme_betoh_u${size}"; fi + echo "(value_mem);" + echo " }" + echo "" + echo " /* compare the compare operand to the effective address operand: */" + echo " tme_m68k_cmp${size}(ic, &ic->tme_m68k_ireg_uint${size}(ireg_dc${reg_size_shift}), &ic->tme_m68k_ireg_memx${size});" + echo "" + echo " /* if the comparison succeeded: */" + echo " if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) {" + echo "" + echo " /* write the update operand to the effective address operand: */" + echo " ic->tme_m68k_ireg_memx${size} = ic->tme_m68k_ireg_uint${size}(ireg_du${reg_size_shift});" + echo " }" + echo "" + echo " /* otherwise, the comparison failed: */" + echo " else {" + echo "" + echo " /* write the effective address operand to the compare operand: */" + echo " ic->tme_m68k_ireg_uint${size}(ireg_dc${reg_size_shift}) = ic->tme_m68k_ireg_memx${size};" + echo " }" + echo "" + echo " /* finish the read/modify/write cycle: */" + echo " tme_m68k_rmw_finish(ic," + echo " &rmw," + echo " (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) != 0);" + echo " TME_M68K_INSN_OK;" + echo "}" + fi + + # cas2: + name=cas2_ + if test $size != 8; then if $header; then echo "TME_M68K_INSN_DECL(tme_m68k_${name}${size});" @@ -770,103 +938,65 @@ for size in 8 16 32; do echo "/* ${name}${size}: */" echo "TME_M68K_INSN(tme_m68k_${name}${size})" echo "{" - echo " struct tme_m68k_tlb *tlb;" - echo " int ireg_dc, ireg_du;" - echo " int do_write;" - echo " tme_uint16_t specopx = ic->_tme_m68k_insn_specop;" - if test $name = cas2_; then - echo " tme_uint16_t specopy = ic->_tme_m68k_insn_specop2;" - echo " tme_uint32_t addrx;" - echo " tme_uint32_t addry;" - echo "" - echo " /* get the function code and addresses we'll be dealing with: */" - echo " ic->_tme_m68k_ea_function_code = TME_M68K_FUNCTION_CODE_DATA(ic);" - echo " addrx = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0" - echo " + TME_FIELD_EXTRACTU(specopx, 12, 4));" - echo " addry = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0" - echo " + TME_FIELD_EXTRACTU(specopy, 12, 4));" - fi + echo " struct tme_m68k_rmw rmw;" + echo " int ireg_dcx, ireg_dux;" + echo " int ireg_dcy, ireg_duy;" + echo " const tme_uint16_t specopx = TME_M68K_INSN_SPECOP;" + echo " const tme_uint16_t specopy = TME_M68K_INSN_OP0(tme_uint16_t);" echo "" echo " /* start the read/modify/write cycle: */" - echo " tlb = tme_m68k_rmw_start(ic);" - echo " if (tlb == NULL) {" + echo " ic->_tme_m68k_ea_function_code = TME_M68K_FUNCTION_CODE_DATA(ic);" + echo " rmw.tme_m68k_rmw_addresses[0] = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0" + echo " + TME_FIELD_EXTRACTU(specopx, 12, 4));" + echo " rmw.tme_m68k_rmw_addresses[1] = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0" + echo " + TME_FIELD_EXTRACTU(specopy, 12, 4));" + echo " rmw.tme_m68k_rmw_address_count = 2;" + echo " rmw.tme_m68k_rmw_size = sizeof(tme_uint${size}_t);" + echo " if (tme_m68k_rmw_start(ic," + echo " &rmw)) {" echo " TME_M68K_INSN_OK;" echo " }" echo "" - echo " /* read: */" - for buffer in $buffers; do - if test $name = cas2_; then - echo " ic->_tme_m68k_ea_address = addr${buffer};" - fi - echo " tme_m68k_read${size}(ic, tlb," - echo " &ic->_tme_m68k_ea_function_code," - echo " &ic->_tme_m68k_ea_address," - echo " &ic->tme_m68k_ireg_mem${buffer}${size}," - echo " TME_M68K_BUS_CYCLE_RMW);" - done - - echo "" - echo " /* modify: */" - for buffer in $buffers; do - i= - if test $buffer = y; then - echo " if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) {" - i=" " - fi - echo "${i} ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specop${buffer}, 0, 3);" - echo "${i} tme_m68k_cmp${size}(ic, &ic->tme_m68k_ireg_uint${size}(ireg_dc), &ic->tme_m68k_ireg_mem${buffer}${size});" - if test $buffer = y; then - echo " }" - fi - done - + echo " /* do the comparisons: */" + echo " ireg_dcx = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3);" + echo " ireg_dcy = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 0, 3);" + echo " tme_m68k_cmp${size}(ic," + echo " &ic->tme_m68k_ireg_uint${size}(ireg_dcx${reg_size_shift})," + echo " &ic->tme_m68k_ireg_memx${size});" + echo " if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) {" + echo " tme_m68k_cmp${size}(ic," + echo " &ic->tme_m68k_ireg_uint${size}(ireg_dcy${reg_size_shift})," + echo " &ic->tme_m68k_ireg_memy${size});" + echo " }" echo "" - echo " /* write: */" + echo " /* if the comparisons succeeded: */" echo " if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) {" - for buffer in $buffers; do - if test $name = cas2_; then - echo " ic->_tme_m68k_ea_address = addr${buffer};" - fi - echo " ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specop${buffer}, 6, 3);" - echo " ic->tme_m68k_ireg_mem${buffer}${size} = ic->tme_m68k_ireg_uint${size}(ireg_du);" - echo " tme_m68k_write${size}(ic, tlb," - echo " &ic->_tme_m68k_ea_function_code," - echo " &ic->_tme_m68k_ea_address," - echo " &ic->tme_m68k_ireg_mem${buffer}${size}," - echo " TME_M68K_BUS_CYCLE_RMW);" - done + echo "" + echo " /* write the update operands to the effective address operands: */" + echo " ireg_dux = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 6, 3);" + echo " ireg_duy = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 6, 3);" + echo " ic->tme_m68k_ireg_memx${size} = ic->tme_m68k_ireg_uint${size}(ireg_dux${reg_size_shift});" + echo " ic->tme_m68k_ireg_memy${size} = ic->tme_m68k_ireg_uint${size}(ireg_duy${reg_size_shift});" echo " }" + echo "" + echo " /* otherwise, the comparisons failed: */" echo " else {" - echo " /* XXX the 68040 always does a write to finish its cycle: */" - echo " do_write = FALSE;" - for buffer in $buffers; do - echo " ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specop${buffer}, 0, 3);" - echo -n " if (do_write" - if test $name = cas2_; then - echo "" - echo " && ic->tme_m68k_ireg_mem${buffer}${size} != ic->tme_m68k_ireg_uint${size}(ireg_dc)) {" - echo " ic->_tme_m68k_ea_address = addr${buffer};" - else - echo ") {" - fi - echo " tme_m68k_write${size}(ic, tlb," - echo " &ic->_tme_m68k_ea_function_code," - echo " &ic->_tme_m68k_ea_address," - echo " &ic->tme_m68k_ireg_mem${buffer}${size}," - echo " TME_M68K_BUS_CYCLE_RMW);" - echo " do_write = FALSE;" - echo " }" - echo " ic->tme_m68k_ireg_uint${size}(ireg_dc) = ic->tme_m68k_ireg_mem${buffer}${size};" - done + echo "" + echo " /* write the effective address operands to the compare operands." + echo " \"If Dc1 and Dc2 specify the same data register and the comparison" + echo " fails, memory operand 1 is stored in the data register.\" */" + echo " ic->tme_m68k_ireg_uint${size}(ireg_dcy${reg_size_shift}) = ic->tme_m68k_ireg_memy${size};" + echo " ic->tme_m68k_ireg_uint${size}(ireg_dcx${reg_size_shift}) = ic->tme_m68k_ireg_memx${size};" echo " }" echo "" echo " /* finish the read/modify/write cycle: */" - echo " tme_m68k_rmw_finish(ic, tlb);" - echo "" + echo " tme_m68k_rmw_finish(ic," + echo " &rmw," + echo " (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) != 0);" echo " TME_M68K_INSN_OK;" echo "}" fi - done + fi # moves: if $header; then @@ -877,11 +1007,44 @@ for size in 8 16 32; do echo "TME_M68K_INSN(tme_m68k_moves${size})" echo "{" echo " int ireg;" + echo " tme_uint${size}_t ireg_value;" + echo " unsigned int ea_reg;" + echo " unsigned int increment;" + echo " TME_M68K_INSN_PRIV;" + echo " TME_M68K_INSN_CANFAULT;" echo " ireg = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 12, 4);" + echo "" + echo " /* in case we're storing the same address register used in a" + echo " postincrement or predecrement EA, save the current value" + echo " of the register now: */" + echo " ireg_value = ic->tme_m68k_ireg_uint${size}(ireg${reg_size_shift});" + echo "" + echo " /* we have to handle postincrement and predecrement ourselves: */" + echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" + echo " ea_reg = TME_M68K_IREG_A0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3);" + echo " increment = TME_M68K_SIZE_${size};" + echo " if (increment == TME_M68K_SIZE_8 && ea_reg == TME_M68K_IREG_A7) {" + echo " increment = TME_M68K_SIZE_16;" + echo " }" + echo " switch (TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3)) {" + echo " case 3: ic->tme_m68k_ireg_uint32(ea_reg) += increment; break;" + echo " case 4: ic->_tme_m68k_ea_address = (ic->tme_m68k_ireg_uint32(ea_reg) -= increment); break;" + echo " default: break;" + echo " }" + echo " }" + echo "" echo " if (TME_M68K_INSN_SPECOP & TME_BIT(11)) {" - echo " ic->tme_m68k_ireg_memx${size} = ic->tme_m68k_ireg_uint${size}(ireg${reg_size_shift});" + echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" + echo " ic->tme_m68k_ireg_memx${size} = ireg_value;" + echo " ic->_tme_m68k_ea_function_code = ic->tme_m68k_ireg_dfc;" + echo " }" + echo " tme_m68k_write_memx${size}(ic);" echo " }" echo " else {" + echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" + echo " ic->_tme_m68k_ea_function_code = ic->tme_m68k_ireg_sfc;" + echo " }" + echo " tme_m68k_read_memx${size}(ic);" if test ${size} != 32; then echo " if (ireg >= TME_M68K_IREG_A0) {" echo " ic->tme_m68k_ireg_uint32(ireg) = " @@ -906,10 +1069,8 @@ for size in 8 16 32 any; do for name in read write; do capname=`echo $name | tr a-z A-Z` if test $name = read; then - lockname="rd" from="from" else - lockname="wr" from="to" fi @@ -932,7 +1093,7 @@ for size in 8 16 32 any; do fc="" addr="" count="" - tlb="TME_M68K_TLB_ENTRY(ic, function_code, linear_address${_first})" + tlb="TME_M68K_DTLB_ENTRY(ic, bus_context, function_code, linear_address${_first})" flags="TME_M68K_BUS_CYCLE_NORMAL" case "${name}-${what}-${size}" in *-memx-8 | *-memx-16 | *-memx-32) @@ -948,6 +1109,7 @@ for size in 8 16 32 any; do args=", tme_uint8_t *buffer, unsigned int count" fcptr="&ic->_tme_m68k_ea_function_code" addrptr="&ic->_tme_m68k_ea_address" + _last= reg= regptr="buffer" ;; @@ -985,7 +1147,7 @@ for size in 8 16 32 any; do args="${args_proto} pc" fc="TME_M68K_FUNCTION_CODE_PROGRAM(ic)" addrptr="&pc" - tlb="TME_ATOMIC_READ(struct tme_m68k_tlb *, ic->_tme_m68k_itlb)"; + tlb="&ic->_tme_m68k_itlb" flags="TME_M68K_BUS_CYCLE_FETCH" ;; *) @@ -1007,6 +1169,7 @@ for size in 8 16 32 any; do echo "{" # our locals: + echo " tme_bus_context_t bus_context = ic->_tme_m68k_bus_context;" echo -n " unsigned int function_code = " if test "x${fc}" != x; then echo "${fc};" @@ -1032,14 +1195,27 @@ for size in 8 16 32 any; do echo " tme_uint32_t linear_address${_last} = linear_address_first + ${count} - 1;"; fi echo " struct tme_m68k_tlb *tlb = ${tlb};" + if test $size != any; then + memtype="tme_uint${size}_t" + echo " ${memtype} mem_value;" + memtype="tme_shared ${memtype} *" + if test $name = read; then memtype="const ${memtype}"; fi + echo " ${memtype}mem;" + fi case "$what" in inst) - echo " unsigned int insn_buffer_off = TME_ALIGN(ic->_tme_m68k_insn_buffer_off, sizeof(tme_uint${size}_t));" - regptr="((tme_uint${size}_t *) &ic->_tme_m68k_insn_buffer[insn_buffer_off])" + echo " unsigned int fetch_slow_next = ic->_tme_m68k_insn_fetch_slow_next;" + regptr="((tme_uint${size}_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next))" reg="*${regptr}" ;; esac + # track statistics: + echo "" + echo "#ifdef _TME_M68K_STATS" + echo " ic->tme_m68k_stats.tme_m68k_stats_memory_total++;" + echo "#endif /* _TME_M68K_STATS */" + # if this is a write, log the value written: if test $name = write; then echo "" @@ -1069,132 +1245,168 @@ for size in 8 16 32 any; do fi echo "" - echo " /* do the bus cycle(s) ourselves from emulator memory if we can." - echo " the emulator memory allocator and TLB filler must guarantee" - echo " that all tme_m68k_tlb_emulator_off_${name} pointers be 32-bit" - echo " aligned, so that a 16-bit-aligned linear address gets a" - echo " 16-bit-aligned emulator address: */" - echo " if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING" - if test $size != 8; then - echo " && !(linear_address${_first} & 1)" - fi - echo " && TME_M68K_TLB_OK_FAST_${capname}(tlb," - echo " function_code," - echo " linear_address${_first}," - echo " linear_address${_last}))) {" + echo " /* busy this TLB entry: */" + echo " tme_m68k_tlb_busy(tlb);" - memptr="(tlb->tme_m68k_tlb_emulator_off_${name} + linear_address${_first})" - mem="*((tme_uint${size}_t *) ${memptr})" - if test $name = read; then - simple="${reg} = tme_betoh_u${size}(${mem});" + # if this is an any-transfer: + # + if test $size = any; then + echo "" + echo " /* call the full ${name} function: */" + echo " tme_m68k_${name}(ic, tlb, ${fcptr}, ${addrptr}, ${regptr}, ${count}, TME_M68K_BUS_CYCLE_RAW);" + + # otherwise, this is not an any-transfer: + # else - simple="${mem} = tme_htobe_u${size}(${reg});" - fi - echo "" - - # if this is an 8-bit transfer: - if test $size = 8; then - echo " /* for an 8-bit transfer we can always do a simple " - echo " assignment. the ${lockname}lock is unnecessary, since we assume" - echo " that 8-bit accesses are always atomic: */" - if test $name = read; then - echo " ${reg} = ${mem};" - else - echo " ${mem} = ${reg};" + # dispatch on the what: + # + i= + case "$what" in + inst) + echo "" + echo " /* if this fetch was done by the fast executor: */" + echo " if (__tme_predict_true(fetch_slow_next < ic->_tme_m68k_insn_fetch_slow_count_fast)) {" + echo "" + echo " /* the entire fetch must be in the instruction buffer, and" + echo " we must be restarting: */" + echo " assert ((fetch_slow_next + sizeof(tme_uint${size}_t))" + echo " <= ic->_tme_m68k_insn_fetch_slow_count_fast);" + echo " assert (TME_M68K_SEQUENCE_RESTARTING);" + echo " mem_value = tme_memory_read${size}(${regptr}, sizeof(tme_uint16_t));" + echo " }" + echo "" + echo " /* otherwise, this fetch was not done by the fast executor: */" + echo " else {" + echo "" + echo " /* if we're restarting, but the offset in the instruction buffer" + echo " to fetch into is at the instruction buffer total, this must be" + echo " a fake fault caused by the fast executor. we confirm this by" + echo " checking that this transfer \"caused\" the fault, and that this" + echo " transfer will be the first slow one after any fast fetches." + echo " in this case, we can cancel the restart for now: */" + echo " if (TME_M68K_SEQUENCE_RESTARTING" + echo " && (fetch_slow_next" + echo " == ic->_tme_m68k_insn_fetch_slow_count_total)) {" + echo " assert ((ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next" + echo " == ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted)" + echo " && (fetch_slow_next" + echo " == ic->_tme_m68k_insn_fetch_slow_count_fast));" + echo " ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted--;" + echo " }" + echo "" + echo " /* if we're not restarting: */" + echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" + echo "" + echo " /* we advance the instruction buffer total *before* we do" + echo " what may be a slow fetch, because we may transfer a few" + echo " bytes and then fault. without this, those few bytes" + echo " would not get saved in the exception stack frame and" + echo " restored later before the continuation of the fetch: */" + echo " ic->_tme_m68k_insn_fetch_slow_count_total += sizeof(tme_uint${size}_t);" + echo " }" + echo "" + echo " /* make sure that if this is a new transfer or if this" + echo " transfer faulted, that we're fetching for the current" + echo " last positions in the instruction buffer: */" + echo " assert ((ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next" + echo " < ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted)" + echo " || ((fetch_slow_next + sizeof(tme_uint${size}_t))" + echo " == ic->_tme_m68k_insn_fetch_slow_count_total));" + i=" " + ;; + esac + + echo "" + echo "${i} /* if we aren't restarting, and this address is properly aligned," + echo "${i} and this TLB entry covers the operand and allows fast ${name}s: */" + echo "${i} if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING" + align_min="sizeof(tme_uint8_t)" + if test $size != 8; then + echo -n "${i} && (" + if test $what = inst; then + align_min="sizeof(tme_uint16_t)" + echo -n "(${align_min} - 1)" + else + echo -n "ic->_tme_m68k_bus_16bit" + fi + echo " & linear_address${_first}) == 0" fi - - # if this is a 16-bit transfer: - elif test $size = 16; then - echo " /* for a 16-bit transfer we can always do a simple" - echo " assignment - we tested that the linear address" - echo " is 16-bit aligned, which, since the TLB emulator" - echo " offset is guaranteed to be 32-bit aligned, guarantees" - echo " that the final emulator address is 16-bit aligned." + echo "${i} && tme_m68k_tlb_is_valid(tlb)" + echo "${i} && tlb->tme_m68k_tlb_bus_context == bus_context" + echo "${i} && (tlb->tme_m68k_tlb_function_codes_mask" + echo "${i} & TME_BIT(function_code))" + echo "${i} && linear_address${_first} >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first" + echo "${i} && linear_address${_last} <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last" + echo "${i} && tlb->tme_m68k_tlb_emulator_off_${name} != TME_EMULATOR_OFF_UNDEF)) {" + echo "" - echo " we need the ${lockname}lock if we're on an architecture" - echo " where an aligned access may not be atomic: */" - echo " tme_memory_aligned_${lockname}lock(tlb->tme_m68k_tlb_bus_rwlock);" - echo " ${simple}" - echo " tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock);" - - # if this is a 32-bit transfer: - elif test $size = 32; then - - echo " /* if the emulator host allows ${size}-bit quantities to be" - echo " transferred ${from} 16-bit aligned addresses, or if this" - echo " address is ${size}-bit aligned, do the transfer as a simple" - echo " assignment, otherwise transfer two 16-bit words." + echo "${i} /* make the emulator memory pointer: */" + echo "${i} mem = (${memtype}) (tlb->tme_m68k_tlb_emulator_off_${name} + linear_address${_first});" + + if test $name = write; then + if test $size = 8; then + echo "" + echo "${i} /* get the value to write: */" + echo "${i} mem_value = ${reg};" + else + echo "" + echo "${i} /* get the value to write, in big-endian byte order: */" + echo "${i} mem_value = tme_htobe_u${size}(${reg});" + fi + fi + echo "" - echo " we need the ${lockname}lock if we're on an architecture where" - echo " an aligned access may not be atomic, or if we're doing" - echo " an unaligned access on an architecture where they may" - echo " not be atomic: */" - misaligned="(linear_address${_first} & (sizeof(tme_uint${size}_t) - 1))" - echo "#if ALIGNOF_INT${size}_T <= ALIGNOF_INT16_T" - echo "#ifdef TME_UNALIGNED_ACCESS_ATOMIC" - echo " ${simple}" - echo "#else /* !TME_UNALIGNED_ACCESS_ATOMIC */" - echo " if (${misaligned}) {" - echo " tme_memory_unaligned_${lockname}lock(tlb->tme_m68k_tlb_bus_rwlock);" - echo " ${simple}" - echo " tme_memory_unaligned_unlock(tlb->tme_m68k_tlb_bus_rwlock);" - echo " }" - echo " else {" - echo " tme_memory_aligned_${lockname}lock(tlb->tme_m68k_tlb_bus_rwlock);" - echo " ${simple}" - echo " tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock);" - echo " }" - echo "#endif /* !TME_UNALIGNED_ACCESS_ATOMIC */" - echo "#else /* ALIGNOF_INT${size}_T > ALIGNOF_INT16_T */" - echo " if (TME_SEQUENCE_ACCESS_NOT_COSTLIER || ${misaligned}) {" - echo " tme_memory_sequence_${lockname}lock(tlb->tme_m68k_tlb_bus_rwlock); " + echo "${i} /* do the ${size}-bit bus ${name}: */" if test $name = read; then - echo "#ifdef WORDS_BIGENDIAN" - echo " ${reg} = (((tme_uint${size}_t) ((tme_uint16_t *) ${memptr})[0]) << 16) | ((tme_uint16_t *) ${memptr})[1];" - echo "#else /* !WORDS_BIGENDIAN */" - echo " ${reg} = tme_betoh_u32((((tme_uint${size}_t) ((tme_uint16_t *) ${memptr})[1]) << 16) | ((tme_uint16_t *) ${memptr})[0]);" - echo "#endif /* !WORDS_BIGENDIAN */" + echo -n "${i} mem_value = tme_memory_bus_${name}${size}(mem" else - echo " ((tme_uint16_t *) ${memptr})[0] = tme_htobe_u16(${reg} >> 16);" - echo " ((tme_uint16_t *) ${memptr})[1] = tme_htobe_u16(${reg} & 0xffff);" + echo -n "${i} tme_memory_bus_${name}${size}(mem, mem_value" fi - echo " tme_memory_sequence_unlock(tlb->tme_m68k_tlb_bus_rwlock); " - echo " }" - echo " else {" - echo " tme_memory_aligned_${lockname}lock(tlb->tme_m68k_tlb_bus_rwlock); " - echo " ${simple}" - echo " tme_memory_aligned_unlock(tlb->tme_m68k_tlb_bus_rwlock); " - echo " }" - echo "#endif /* ALIGNOF_INT${size}_T != 1 */" - - # if this is an any-transfer: - elif test $size = any; then - echo " tme_memory_sequence_${lockname}lock(tlb->tme_m68k_tlb_bus_rwlock);" + echo ", tlb->tme_m68k_tlb_bus_rwlock, ${align_min}, sizeof(tme_uint32_t));" + if test $name = read; then - echo " memcpy(${regptr}, ${memptr}, ${count});" - else - echo " memcpy(${memptr}, ${regptr}, ${count});" + if test $what = inst; then + echo "" + echo "${i} /* put the value read, in host byte order: */" + echo "${i} mem_value = tme_betoh_u${size}(mem_value);" + echo "${i} tme_memory_write${size}(${regptr}, mem_value, sizeof(tme_uint16_t));" + elif test $size = 8; then + echo "" + echo "${i} /* put the value read: */" + echo "${i} ${reg} = mem_value;" + else + echo "" + echo "${i} /* put the value read, in host byte order: */" + echo "${i} ${reg} = tme_betoh_u${size}(mem_value);" + fi fi - echo " tme_memory_sequence_unlock(tlb->tme_m68k_tlb_bus_rwlock);" + echo "" + echo "${i} /* step the transfer count: */" + echo "${i} TME_M68K_SEQUENCE_TRANSFER_STEP;" + echo "${i} }" + + echo "" + echo "${i} /* otherwise, do the bus cycles the slow way: */" + echo "${i} else {" + echo "${i} tme_m68k_${name}${size}(ic, tlb," + echo "${i} ${fcptr}," + echo "${i} ${addrptr}," + echo "${i} ${regptr}," + echo "${i} ${flags});" + if test ${what} = inst; then + echo "${i} mem_value = tme_memory_read${size}(${regptr}, sizeof(tme_uint16_t));" + fi + echo "${i} }" + fi + if test "x${i}" != x; then + echo " }" fi - echo " TME_M68K_SEQUENCE_TRANSFER_STEP;" - echo " }" echo "" - echo " /* otherwise, do the bus cycles the slow way: */" - echo " else {" - if test $size != any; then - echo " tme_m68k_${name}${size}(ic, tlb," - echo " ${fcptr}," - echo " ${addrptr}," - echo " ${regptr}," - echo " ${flags});" - else - echo " tme_m68k_${name}(ic, tlb, ${fcptr}, ${addrptr}, ${regptr}, ${count}, TME_M68K_BUS_CYCLE_RAW);" - fi - echo " }" + echo " /* unbusy this TLB entry: */" + echo " tme_m68k_tlb_unbusy(tlb);" # if this is a read, log the value read: if test $name = read; then @@ -1233,8 +1445,13 @@ for size in 8 16 32 any; do echo " }" ;; inst) - echo " ic->_tme_m68k_insn_buffer_off = insn_buffer_off + sizeof(tme_uint${size}_t);" - echo " return(${reg});" + echo "" + echo " /* advance the offset in the instruction buffer for the next slow fetch: */" + echo " fetch_slow_next += sizeof(tme_uint${size}_t);" + echo " ic->_tme_m68k_insn_fetch_slow_next = fetch_slow_next;" + echo "" + echo " /* return the fetched value: */" + echo " return(mem_value);" ;; esac @@ -1278,12 +1495,12 @@ for size in 8 16 32 any; do echo " struct tme_bus_cycle cycle;" echo " unsigned int transferred, resid, cycle_size;" echo " int exception;" - echo " tme_rwlock_t *rmw_rwlock;" echo " int err;" - echo "#ifndef WORDS_BIGENDIAN" echo " tme_uint8_t *reg_p;" echo " unsigned int buffer_i;" - echo "#endif /* !WORDS_BIGENDIAN */" + echo " tme_uint8_t reg_buffer[sizeof(tme_uint32_t) * 2];" + if test ${name} = read; then name_const_mem="const "; else name_const_mem= ; fi + echo " ${name_const_mem}tme_shared tme_uint8_t *mem;" echo "" echo " /* if we're not restarting, everything is fresh: */" @@ -1335,7 +1552,6 @@ for size in 8 16 32 any; do echo "" echo " /* do as many bus cycles as needed to complete the transfer: */" - echo " rmw_rwlock = tlb->tme_m68k_tlb_bus_rwlock;" echo " exception = TME_M68K_EXCEPTION_NONE;" echo " cycle_size = 0;" echo " for(; transferred < reg_size; ) {" @@ -1369,7 +1585,7 @@ for size in 8 16 32 any; do echo " /* only byte transfers can be unaligned: */" echo " if (resid > sizeof(tme_uint8_t)" echo " && (linear_address & 1)) {" - echo " exception = TME_M68K_EXCEPTION_GROUP0_AERR;" + echo " exception = TME_M68K_EXCEPTION_AERR;" echo " break;" echo " }" echo "" @@ -1388,7 +1604,7 @@ for size in 8 16 32 any; do echo " /* an instruction fetch must be aligned: */" echo " if (flags & TME_M68K_BUS_CYCLE_FETCH) {" echo " if (linear_address & 1) {" - echo " exception = TME_M68K_EXCEPTION_GROUP0_AERR;" + echo " exception = TME_M68K_EXCEPTION_AERR;" echo " break;" echo " }" echo " assert(!(resid & 1));" @@ -1404,32 +1620,104 @@ for size in 8 16 32 any; do echo " }" echo "" - echo " /* reload the TLB entry: */" - echo " if (!TME_M68K_TLB_OK_SLOW_${capname}(tlb, function_code, linear_address)) {" + echo " /* loop while this TLB entry is invalid or does not apply: */" + echo " for (; __tme_predict_false(tme_m68k_tlb_is_invalid(tlb)" + echo " || tlb->tme_m68k_tlb_bus_context != ic->_tme_m68k_bus_context" + echo " || (tlb->tme_m68k_tlb_function_codes_mask & TME_BIT(function_code)) == 0" + echo " || linear_address < (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first" + echo " || linear_address > (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last" + echo " || (tlb->tme_m68k_tlb_emulator_off_${name} == TME_EMULATOR_OFF_UNDEF" + echo " && (tlb->tme_m68k_tlb_cycles_ok & TME_BUS_CYCLE_${capname}) == 0)); ) {" + echo "" + echo " /* this must not be part of a read/modify/write cycle: */" + echo " assert(!(flags & TME_M68K_BUS_CYCLE_RMW));" + echo "" + echo " /* fill this TLB entry: */" echo " tme_m68k_tlb_fill(ic, tlb," echo " function_code," echo " linear_address," echo " TME_BUS_CYCLE_${capname});" echo " }" echo "" - echo " /* if this is a part of a read/modify/write cycle: */" - echo " if (flags & TME_M68K_BUS_CYCLE_RMW) {" - echo "" - echo " /* if this TLB entry doesn't support fast ${name}s, or" - echo " if the TLB lock has changed, that's a bus error." - echo " see the discussion in tme_m68k_rmw_start: */" - echo " if (!TME_M68K_TLB_OK_FAST_${capname}(tlb, function_code, linear_address, linear_address)" - echo " || (rmw_rwlock != NULL" - echo " && rmw_rwlock != tlb->tme_m68k_tlb_bus_rwlock)) {" - echo " exception = TME_M68K_EXCEPTION_GROUP0_BERR;" - echo " break;" + echo " /* if this TLB entry allows for fast ${name}s: */" + echo " mem = tlb->tme_m68k_tlb_emulator_off_${name};" + echo " if (__tme_predict_true(mem != TME_EMULATOR_OFF_UNDEF)) {" + echo "" + echo " /* make the emulator memory pointer: */" + echo " mem += linear_address;" + echo "" + echo " /* limit the cycle size to addresses covered by the TLB entry: */" + echo " if (__tme_predict_false((cycle_size - 1)" + echo " > (((tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last) - linear_address))) {" + echo " cycle_size = (((tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last) - linear_address) + 1;" echo " }" echo "" - echo " /* if we haven't locked this memory yet, do so: */" - echo " if (rmw_rwlock == NULL) {" - echo " rmw_rwlock = tlb->tme_m68k_tlb_bus_rwlock;" - echo " tme_rwlock_wrlock(rmw_rwlock);" + echo " /* if this is a little-endian host, and this isn't a raw ${name}: */" + echo " if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE" + echo " && (flags & TME_M68K_BUS_CYCLE_RAW) == 0) {" + if test ${name} = write; then + echo "" + echo " /* byteswap the data to write in the intermediate buffer: */" + echo " reg_p = cycle.tme_bus_cycle_buffer;" + echo " buffer_i = 0;" + echo " do {" + echo " reg_buffer[buffer_i] = *(reg_p--);" + echo " } while (++buffer_i != cycle_size);" + fi + echo "" + echo " /* use the intermediate buffer for the ${name}: */" + echo " cycle.tme_bus_cycle_buffer = ®_buffer[0];" echo " }" + echo "" + echo " /* do the bus ${name}: */" + echo " tme_memory_bus_${name}_buffer(mem," + echo " cycle.tme_bus_cycle_buffer," + echo " cycle_size," + echo " tlb->tme_m68k_tlb_bus_rwlock," + echo " sizeof(tme_uint8_t)," + echo " sizeof(tme_uint32_t));" + if test ${name} = read; then + echo "" + echo " /* if this is a little-endian host, and this isn't a raw ${name}: */" + echo " if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE" + echo " && (flags & TME_M68K_BUS_CYCLE_RAW) == 0) {" + echo "" + echo " /* byteswap the read data in the intermediate buffer: */" + echo " reg_p = reg + reg_size - (1 + transferred);" + echo " buffer_i = 0;" + echo " do {" + echo " *(reg_p--) = reg_buffer[buffer_i];" + echo " } while (++buffer_i != cycle_size);" + echo " }" + fi + echo "" + echo " /* update: */" + echo " linear_address += cycle_size;" + echo " transferred += cycle_size;" + echo " continue;" + echo " }" + echo "" + echo " /* otherwise, this TLB entry does not allow for fast ${name}s: */" + echo "" + echo " /* if this is a part of a read/modify/write cycle: */" + echo " if (flags & TME_M68K_BUS_CYCLE_RMW) {" + echo "" + if test ${name} = read; then + echo " /* if this is the first cycle in this read," + echo " we will establish the new lock, otherwise" + echo " we will continue using the existing lock: */" + else + echo " /* we will continue using the existing lock." + echo " the device will automatically unlock after" + echo " the last cycle of this write: */" + fi + echo " cycle.tme_bus_cycle_type" + echo " |= (TME_BUS_CYCLE_LOCK" + echo -n " | (" + if test ${name} = read; then + echo -n "transferred == 0 ? 0 : " + fi + echo "TME_BUS_CYCLE_UNLOCK));" echo " }" echo "" @@ -1446,22 +1734,33 @@ for size in 8 16 32 any; do echo "" echo " /* run the bus cycle: */" + echo " tme_m68k_tlb_unbusy(tlb);" + echo " tme_m68k_callout_unlock(ic);" echo " err = (*tlb->tme_m68k_tlb_bus_tlb.tme_bus_tlb_cycle)" echo " (tlb->tme_m68k_tlb_bus_tlb.tme_bus_tlb_cycle_private, &cycle);" + echo " tme_m68k_callout_relock(ic);" + echo " tme_m68k_tlb_busy(tlb);" + echo "" + echo " /* if the TLB entry was invalidated before the ${name}: */" + echo " if (err == EBADF" + echo " && tme_m68k_tlb_is_invalid(tlb)) {" + echo " cycle.tme_bus_cycle_size = 0;" + echo " }" echo "" - echo " /* if we deadlocked, we have no locks to release" - echo " ourselves, so sleep a while waiting for things" - echo " to clear up, then try again: */" - echo " if (err == TME_EDEADLK) {" - echo " TME_THREAD_DEADLOCK_SLEEP();" - echo " cycle.tme_bus_cycle_address = physical_address;" + echo " /* otherwise, if we didn't get a bus error, but some" + echo " synchronous event has happened: */" + echo " else if (err == TME_BUS_CYCLE_SYNCHRONOUS_EVENT) {" + echo "" + echo " /* after the currently executing instruction finishes, check" + echo " for external resets, halts, or interrupts: */" + echo " ic->_tme_m68k_instruction_burst_remaining = 0;" echo " }" echo "" echo " /* otherwise, any other error might be a bus error: */" echo " else if (err != TME_OK) {" echo " err = tme_bus_tlb_fault(&tlb->tme_m68k_tlb_bus_tlb, &cycle, err);" echo " if (err != TME_OK) {" - echo " exception = TME_M68K_EXCEPTION_GROUP0_BERR;" + echo " exception = TME_M68K_EXCEPTION_BERR;" echo " break;" echo " }" echo " }" @@ -1472,13 +1771,9 @@ for size in 8 16 32 any; do echo " }" echo "" - echo " /* if we got an exception and there is a locked" - echo " read/modify/write rwlock, unlock it: */" - echo " if (exception != TME_M68K_EXCEPTION_NONE" - echo " && (flags & TME_M68K_BUS_CYCLE_RMW)" - echo " && rmw_rwlock != NULL) {" - echo " tme_rwlock_unlock(rmw_rwlock);" - echo " }" + echo " /* NB: there is no need to explicitly unlock" + echo " a device. if a locked bus cycle to a device" + echo " faults, the lock must be automatically unlocked: */" echo "" echo " /* if we faulted, stash the information the fault stacker" @@ -1513,6 +1808,7 @@ for size in 8 16 32 any; do echo " }" echo " ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_faulted = "; echo " ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_next;" + echo " tme_m68k_tlb_unbusy(tlb);" echo " tme_m68k_exception(ic, exception);" echo " }" @@ -1549,20 +1845,25 @@ for name in abcd sbcd nbcd; do echo " int memory;" echo " int rx, ry, function_code;" echo "" - echo " TME_M68K_INSN_CANFAULT;" - echo "" echo " /* load the operands: */" echo " rx = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3);" echo " ry = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 9, 3);" echo " memory = (TME_M68K_INSN_OPCODE & TME_BIT(3)) != 0;" echo " function_code = TME_M68K_FUNCTION_CODE_DATA(ic);" echo " if (memory) {" + echo " TME_M68K_INSN_CANFAULT;" echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" + # the stack pointer must always be decremented by a multiple of two. + # assuming rx < 8, ((rx + 1) >> 3) == 1 iff rx == 7, meaning %a7: + echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + rx) -= sizeof(tme_uint8_t) + ((rx + 1) >> 3);" echo " ic->_tme_m68k_ea_function_code = function_code;" echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + rx);" echo " }" echo " tme_m68k_read_memx8(ic);" echo " if (!TME_M68K_SEQUENCE_RESTARTING) {" + # the stack pointer must always be incremented by a multiple of two. + # assuming rx < 8, ((rx + 1) >> 3) == 1 iff rx == 7, meaning %a7: + echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry) -= sizeof(tme_uint8_t) + ((ry + 1) >> 3);" echo " ic->_tme_m68k_ea_function_code = function_code;" echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry);" echo " }" @@ -1611,10 +1912,6 @@ for name in abcd sbcd nbcd; do echo " ic->tme_m68k_ireg_memx8 = res;" echo " ic->_tme_m68k_ea_function_code = function_code;" echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry);" - # the stack pointer must always be incremented by a multiple of two. - # assuming rx < 8, ((rx + 1) >> 3) == 1 iff rx == 7, meaning %a7: - echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + rx) += sizeof(tme_uint8_t) + ((rx + 1) >> 3);" - echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry) += sizeof(tme_uint8_t) + ((ry + 1) >> 3);" echo " ic->tme_m68k_ireg_ccr = flags;" echo " }" echo " tme_m68k_write_memx8(ic);" @@ -1802,8 +2099,9 @@ for _sign in u s; do echo " dividend = (tme_${sign}int${large}_t) ic->tme_m68k_ireg_${sign}int32(ireg_dq);" echo " divisor = TME_M68K_INSN_OP1(tme_${sign}int${small}_t);" echo " if (divisor == 0) {" + echo " ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc;" echo " ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next;" - echo " TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_GROUP2(5));" + echo " TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_DIV0));" echo " }" echo ""