Mercurial > repos > blastem
view upd78k2_util.c @ 2723:4f88822e6a5d default tip
Fix regression in CPU DSL implementation impacting new 68K core
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 17 Jul 2025 07:38:33 -0700 |
parents | 8ce5d1a7ef54 |
children |
line wrap: on
line source
#include <string.h> //#define FETCH_DEBUG void upd78k2_read_8(upd78k2_context *upd) { #ifdef FETCH_DEBUG uint32_t tmp = upd->scratch1; #endif upd->scratch1 = read_byte(upd->scratch1, (void **)upd->mem_pointers, &upd->opts->gen, upd); #ifdef FETCH_DEBUG if (tmp == upd->pc) { printf("uPD78K/II fetch %04X: %02X, AX=%02X%02X BC=%02X%02X DE=%02X%02X HL=%02X%02X SP=%04X\n", tmp, upd->scratch1, upd->main[1], upd->main[0], upd->main[3], upd->main[2], upd->main[5], upd->main[4], upd->main[7], upd->main[6], upd->sp); } #endif //FIXME: cycle count upd->cycles += 2 * upd->opts->gen.clock_divider; } void upd78k2_write_8(upd78k2_context *upd) { write_byte(upd->scratch2, upd->scratch1, (void **)upd->mem_pointers, &upd->opts->gen, upd); //FIXME: cycle count upd->cycles += 2 * upd->opts->gen.clock_divider; } #define CE0 0x08 #define CE1 0x08 #define CIF00 0x0010 #define CIF01 0x0020 #define CIF10 0x0040 #define CIF11 0x0080 void upd78k2_update_timer0(upd78k2_context *upd) { uint32_t diff = (upd->cycles - upd->tm0_cycle) / upd->opts->gen.clock_divider; upd->tm0_cycle += (diff & ~7) * upd->opts->gen.clock_divider; diff >>= 3; if (upd->tmc0 & CE0) { uint32_t tmp = upd->tm0 + diff; //TODO: the rest of the CR00/CR01 stuff if (upd->tm0 < upd->cr00 && tmp >= upd->cr00) { upd->if0 |= CIF00; } if (upd->tm0 < upd->cr01 && tmp >= upd->cr01) { upd->if0 |= CIF01; if (upd->crc0 & 8) { //CR01 clear is enabled if (upd->cr01) { while (tmp >= upd->cr01) { tmp -= upd->cr01; } } else { tmp = 0; } } } if (tmp > 0xFFFF) { upd->tmc0 |= 4; } upd->tm0 = tmp; } } uint8_t upd78k2_tm1_scale(upd78k2_context *upd) { uint8_t scale = upd->prm1 & 3; if (scale < 2) { scale = 2; } scale++; return scale; } void upd78k2_update_timer1(upd78k2_context *upd) { uint8_t scale = upd78k2_tm1_scale(upd); uint32_t diff = (upd->cycles - upd->tm1_cycle) / upd->opts->gen.clock_divider; upd->tm1_cycle += (diff & ~((1 << scale) - 1)) * upd->opts->gen.clock_divider; diff >>= scale; if (upd->tmc1 & CE1) { //tm1 count enabled uint32_t tmp = upd->tm1 + diff; if (upd->tm1 < upd->cr10 && tmp >= upd->cr10) { upd->if0 |= CIF10; } if (upd->tm1 < upd->cr11 && tmp >= upd->cr11) { upd->if0 |= CIF11; } uint8_t do_clr11 = 0; if (upd->crc1 & 2) { //clr10 enabled uint8_t do_clr10 = 1; if ((upd->crc1 & 0xC) == 8) { //clr11 also enabled if (upd->cr11 < upd->cr10) { do_clr10 = 0; do_clr11 = 1; } } if (do_clr10) { if (upd->cr10) { while (tmp >= upd->cr10) { tmp -= upd->cr10; } } else { tmp = 0; } } } else if ((upd->crc1 & 0xC) == 8) { do_clr11 = 1; } if (do_clr11) { if (upd->cr11) { while (tmp >= upd->cr11) { tmp -= upd->cr11; } } else { tmp = 0; } } if (tmp > 0xFF) { upd->tmc1 |= 4; } upd->tm1 = tmp; } } #define CMK00 CIF00 #define CMK01 CIF01 #define CMK10 CIF10 #define CMK11 CIF11 void upd78k2_calc_next_int(upd78k2_context *upd) { uint32_t next_int = 0xFFFFFFFF; if (!upd->int_enable) { //maskable interrupts disabled //TODO: NMIs upd->int_cycle = next_int; return; } if (upd->if0 & (~upd->mk0)) { //unmasked interrupt is pending upd->int_cycle = upd->cycles; return; } uint32_t cycle; if (!(upd->mk0 & CMK00) && (upd->tmc0 & CE0)) { //TODO: account for clear function cycle = ((uint16_t)(upd->cr00 - upd->tm0)) << 3; cycle *= upd->opts->gen.clock_divider; cycle += upd->tm0_cycle; if (cycle < next_int) { next_int = cycle; } } if (!(upd->mk0 & CMK01) && (upd->tmc0 & CE0)) { //TODO: account for clear function cycle = ((uint16_t)(upd->cr01 - upd->tm0)) << 3; cycle *= upd->opts->gen.clock_divider; cycle += upd->tm0_cycle; if (cycle < next_int) { next_int = cycle; } } uint8_t scale = upd78k2_tm1_scale(upd); if (!(upd->mk0 & CMK10) && (upd->tmc1 & CE1)) { //TODO: account for clear function cycle = ((uint8_t)(upd->cr10 - upd->tm1)) << scale; cycle *= upd->opts->gen.clock_divider; cycle += upd->tm1_cycle; if (cycle < next_int) { next_int = cycle; } } if (!(upd->mk0 & CMK11) && (upd->tmc1 & CE1)) { //TODO: account for clear function cycle = ((uint8_t)(upd->cr11 - upd->tm1)) << scale; cycle *= upd->opts->gen.clock_divider; cycle += upd->tm1_cycle; if (cycle < next_int) { next_int = cycle; } } #ifdef FETCH_DEBUG if (next_int != upd->int_cycle) { printf("UPD78K/II int cycle: %u, cur cycle %u\n", next_int, upd->cycles); } #endif upd->int_cycle = next_int; } uint8_t upd78237_sfr_read(uint32_t address, void *context) { upd78k2_context *upd = context; switch (address) { case 0x00: case 0x04: case 0x05: case 0x06: return upd->port_data[address]; case 0x02: case 0x07: //input only if (upd->io_read) { upd->io_read(upd, address); } return upd->port_input[address]; break; case 0x01: case 0x03: if (upd->io_read) { upd->io_read(upd, address); } return (upd->port_input[address] & upd->port_mode[address]) | (upd->port_data[address] & ~upd->port_mode[address]); case 0x10: return upd->cr00; case 0x11: return upd->cr00 >> 8; case 0x12: return upd->cr01; case 0x13: return upd->cr01 >> 8; case 0x14: return upd->cr10; case 0x1C: return upd->cr11; case 0x21: case 0x26: return upd->port_mode[address & 0x7]; case 0x5D: upd78k2_update_timer0(upd); printf("TMC0 Read: %02X\n", upd->tmc0); return upd->tmc0; case 0x5F: upd78k2_update_timer1(upd); printf("TMC1 Read: %02X\n", upd->tmc1); return upd->tmc1; case 0xC4: return upd->mm; case 0xE0: return upd->if0; case 0xE1: return upd->if0 >> 8; case 0xE4: return upd->mk0; case 0xE5: return upd->mk0 >> 8; case 0xE8: return upd->pr0; case 0xE9: return upd->pr0 >> 8; case 0xEC: return upd->ism0; case 0xED: return upd->ism0 >> 8; case 0xF4: return upd->intm0; case 0xF5: return upd->intm1; case 0xF8: return upd->ist; default: fprintf(stderr, "Unhandled uPD78237 SFR read %02X\n", address); return 0xFF; } } void *upd78237_sfr_write(uint32_t address, void *context, uint8_t value) { upd78k2_context *upd = context; if (address < 8 && address != 2 && address != 7) { upd->port_data[address] = value; } else { switch (address) { case 0x00: case 0x01: case 0x03: case 0x04: case 0x05: case 0x06: printf("P%X: %02X\n", address & 7, value); upd->port_data[address & 7] = value; if (upd->io_write) { upd->io_write(upd, address); } break; case 0x10: upd78k2_update_timer0(upd); upd->cr00 &= 0xFF00; upd->cr00 |= value; printf("CR00: %04X\n", upd->cr00); upd78k2_calc_next_int(upd); break; case 0x11: upd78k2_update_timer0(upd); upd->cr00 &= 0xFF; upd->cr00 |= value << 8; printf("CR00: %04X\n", upd->cr00); upd78k2_calc_next_int(upd); break; case 0x12: upd78k2_update_timer0(upd); upd->cr01 &= 0xFF00; upd->cr01 |= value; printf("CR01: %04X\n", upd->cr01); upd78k2_calc_next_int(upd); break; case 0x13: upd78k2_update_timer0(upd); upd->cr01 &= 0xFF; upd->cr01 |= value << 8; printf("CR01: %04X\n", upd->cr01); upd78k2_calc_next_int(upd); break; case 0x14: upd78k2_update_timer1(upd); upd->cr10 = value; printf("CR10: %02X\n", value); upd78k2_calc_next_int(upd); break; case 0x1C: upd78k2_update_timer1(upd); upd->cr11 = value; printf("CR11: %02X\n", value); upd78k2_calc_next_int(upd); break; case 0x20: case 0x21: case 0x23: case 0x25: case 0x26: printf("PM%X: %02X\n", address & 0x7, value); upd->port_mode[address & 7] = value; break; case 0x30: upd78k2_update_timer0(upd); upd->crc0 = value; printf("CRC0 CLR01: %X, MOD: %X, Other: %X\n", value >> 3 & 1, value >> 6, value & 0x37); upd78k2_calc_next_int(upd); break; case 0x32: upd78k2_update_timer1(upd); upd->crc1 = value; printf("CRC1 CLR11: %X, CM: %X, CLR10: %X\n", value >> 3 & 1, value >> 2 & 1, value >> 1 & 1); upd78k2_calc_next_int(upd); break; case 0x40: upd->puo = value; printf("PUO: %02X\n", value); break; case 0x43: upd->pmc3 = value; printf("PMC3 TO: %X, SO: %X, SCK: %X, TxD: %X, RxD: %X\n", value >> 4, value >> 3 & 1, value >> 2 & 1, value >> 1 & 1, value & 1); break; case 0x5D: upd78k2_update_timer0(upd); upd->tmc0 = value; printf("TMC0 CE0: %X, OVF0: %X - TM3 CE3: %X\n", value >> 3 & 1, value >> 2 & 1, value >> 7 & 1); if (!(value & 0x8)) { upd->tm0 = 0; } upd78k2_calc_next_int(upd); break; case 0x5E: upd78k2_update_timer1(upd); upd->prm1 = value; printf("PRM1: %02X\n", value); upd78k2_calc_next_int(upd); break; case 0x5F: upd78k2_update_timer1(upd); upd->tmc1 = value; printf("TMC1 CE2: %X, OVF2: %X, CMD2: %X, CE1: %X, OVF1: %X\n", value >> 7, value >> 6 & 1, value >> 5 & 1, value >> 3 & 1, value >> 2 & 1); upd78k2_calc_next_int(upd); break; case 0xC4: upd->mm = value; break; case 0xE0: upd->if0 &= 0xFF00; upd->if0 |= value; upd78k2_calc_next_int(upd); break; case 0xE1: upd->if0 &= 0xFF; upd->if0 |= value << 8; upd78k2_calc_next_int(upd); break; case 0xE4: upd->mk0 &= 0xFF00; upd->mk0 |= value; printf("MK0: %04X (low: %02X)\n", upd->mk0, value); upd78k2_sync_cycle(upd, upd->sync_cycle); break; case 0xE5: upd->mk0 &= 0xFF; upd->mk0 |= value << 8; printf("MK0: %04X (hi: %02X)\n", upd->mk0, value); upd78k2_sync_cycle(upd, upd->sync_cycle); break; case 0xE8: upd->pr0 &= 0xFF00; upd->pr0 |= value; printf("PR0: %04X\n", upd->pr0); upd78k2_sync_cycle(upd, upd->sync_cycle); break; case 0xE9: upd->pr0 &= 0xFF; upd->pr0 |= value << 8; printf("PR0: %04X\n", upd->pr0); upd78k2_sync_cycle(upd, upd->sync_cycle); break; case 0xEC: upd->ism0 &= 0xFF00; upd->ism0 |= value; printf("ISM0: %04X\n", upd->ism0); break; case 0xED: upd->ism0 &= 0xFF; upd->ism0 |= value << 8; printf("ISM0: %04X\n", upd->ism0); break; case 0xF4: printf("INTM0: %02X\n", value); upd->intm0 = value; break; case 0xF5: printf("INTM1: %02X\n", value); upd->intm1 = value; break; case 0xF8: upd->ist = value; break; default: fprintf(stderr, "Unhandled uPD78237 SFR write %02X: %02X\n", address, value); break; } } return context; } void init_upd78k2_opts(upd78k2_options *opts, memmap_chunk const *chunks, uint32_t num_chunks) { memset(opts, 0, sizeof(*opts)); opts->gen.memmap = chunks; opts->gen.memmap_chunks = num_chunks; opts->gen.address_mask = 0xFFFFF; opts->gen.max_address = 0xFFFFF; opts->gen.clock_divider = 1; } upd78k2_context *init_upd78k2_context(upd78k2_options *opts) { upd78k2_context *context = calloc(1, sizeof(upd78k2_context)); context->opts = opts; memset(context->port_mode, 0xFF, sizeof(context->port_mode)); context->crc0 = 0x10; context->mm = 0x20; context->mk0 = 0xFFFF; context->pr0 = 0xFFFF; return context; } void upd78k2_sync_cycle(upd78k2_context *upd, uint32_t target_cycle) { upd78k2_update_timer0(upd); upd78k2_update_timer1(upd); upd->sync_cycle = target_cycle; upd78k2_calc_next_int(upd); } void upd78k2_calc_vector(upd78k2_context *upd) { uint32_t pending_enabled = upd->scratch1; uint32_t vector = 0x6; uint32_t bit = 1; while (pending_enabled) { if (pending_enabled & 1) { upd->if0 &= ~bit; upd->scratch1 = vector; return; } bit <<= 1; pending_enabled >>= 1; vector += 2; if (vector == 0xE) { vector = 0x14; } else if (vector == 0x20) { vector = 0xE; } else if (vector == 0x14) { vector = 0x20; } } fatal_error("upd78k2_calc_vector: %X\n", upd->scratch1); } void upd78k2_adjust_cycles(upd78k2_context *upd, uint32_t deduction) { upd78k2_update_timer0(upd); upd78k2_update_timer1(upd); if (upd->cycles <= deduction) { upd->cycles = 0; } else { upd->cycles -= deduction; } if (upd->tm0_cycle <= deduction) { upd->tm0_cycle = 0; } else { upd->tm0_cycle -= deduction; } if (upd->tm1_cycle <= deduction) { upd->tm1_cycle = 0; } else { upd->tm1_cycle -= deduction; } }