/* * mii.c * * Copyright (C) 2023 Michel Pollet * * SPDX-License-Identifier: MIT */ #include #include #include #include #include "mii_rom_iiee.h" #include "mii.h" #include "mii_bank.h" #include "mii_video.h" #include "mii_sw.h" #include "mii_65c02.h" #include "minipt.h" mii_slot_drv_t * mii_slot_drv_list = NULL; static const mii_bank_t _mii_banks_init[MII_BANK_COUNT] = { [MII_BANK_MAIN] = { .name = "MAIN", .base = 0x0000, .size = 0xc0, }, [MII_BANK_BSR] = { .name = "BSR", .base = 0xd000, .size = 64, .mem_offset = 0xd000, .no_alloc = 1, }, [MII_BANK_BSR_P2] = { .name = "BSR P2", .base = 0xd000, .size = 16, .mem_offset = 0xc000, .no_alloc = 1, }, [MII_BANK_AUX_BASE] = { .name = "AUX_BASE", .base = 0x0000, .size = 0xd0, // 208 pages, 48KB .no_alloc = 1, }, [MII_BANK_AUX] = { .name = "AUX", .base = 0x0000, .size = 0xd0, // 208 pages, 48KB .no_alloc = 1, }, [MII_BANK_AUX_BSR] = { .name = "AUX BSR", .base = 0xd000, .size = 64, .mem_offset = 0xd000, .no_alloc = 1, }, [MII_BANK_AUX_BSR_P2] = { .name = "AUX BSR P2", .base = 0xd000, .size = 16, .mem_offset = 0xc000, .no_alloc = 1, }, [MII_BANK_ROM] = { .name = "ROM", .base = 0xc000, .size = 0x40, // 64 pages, 16KB .ro = 1, }, [MII_BANK_CARD_ROM] = { .name = "CARD ROM", .base = 0xc100, // c100-cfff = 15 pages // 7 * 2KB for extended ROMs for cards (not addressable directly) // Car roms are 'banked' as well, so we don't need to copy them around .size = 15,// + (7 * 8), .ro = 1, }, [MII_BANK_SW] = { .name = "SW", .base = 0xc000, .size = 0x1, }, }; #include "mii_65c02_ops.h" #include "mii_65c02_disasm.h" void mii_dump_trace_state( mii_t *mii) { mii_cpu_t * cpu = &mii->cpu; mii_cpu_state_t s = mii->cpu_state; printf("PC:%04X A:%02X X:%02X Y:%02X S:%02x #%d %c AD:%04X D:%02x %c ", cpu->PC, cpu->A, cpu->X, cpu->Y, cpu->S, cpu->cycle, s.sync ? 'I' : ' ', s.addr, s.data, s.w ? 'W' : 'R'); // display the S flags static const char *s_flags = "CZIDBRVN"; for (int i = 0; i < 8; i++) printf("%c", MII_GET_P_BIT(cpu, i) ? s_flags[i] : tolower(s_flags[i])); if (s.sync) { uint8_t op[16]; for (int i = 0; i < 4; i++) { mii_mem_access(mii, mii->cpu.PC + i, op + i, false, false); } mii_op_t d = mii_cpu_op[op[0]]; printf(" "); char dis[32]; mii_cpu_disasm_one(op, cpu->PC, dis, sizeof(dis), MII_DUMP_DIS_DUMP_HEX); printf(": %s", dis); if (d.desc.branch) { if (MII_GET_P_BIT(cpu, d.desc.s_bit) == d.desc.s_bit_value) printf(" ; taken"); } printf("\n"); } else printf("\n"); } void mii_dump_run_trace( mii_t *mii) { // walk all the previous PC values in mii->trace, and display a line // of disassembly for all of them for (int li = 0; li < MII_PC_LOG_SIZE; li++) { int idx = (mii->trace.idx + li) & (MII_PC_LOG_SIZE - 1); uint16_t pc = mii->trace.log[idx]; uint8_t op[16]; for (int i = 0; i < 4; i++) mii_mem_access(mii, pc + i, op + i, false, false); // mii_op_t d = mii_cpu_op[op[0]]; char dis[64]; mii_cpu_disasm_one(op, pc, dis, sizeof(dis), MII_DUMP_DIS_PC | MII_DUMP_DIS_DUMP_HEX); printf("%s\n", dis); } } #define _SAME 0xf static inline void mii_page_set( mii_t * mii, uint8_t read, uint8_t write, uint8_t bank, uint8_t end ) { for (int i = bank; i <= end; i++) { if (read != _SAME) mii->mem[i].read = read; if (write != _SAME) mii->mem[i].write = write; } } static inline uint8_t mii_sw( mii_t *mii, uint16_t sw) { return mii_bank_peek(&mii->bank[MII_BANK_SW], sw); } static void mii_page_table_update( mii_t *mii) { if (likely(!mii->mem_dirty)) return; mii->mem_dirty = 0; bool altzp = SW_GETSTATE(mii, SWALTPZ); bool page2 = SW_GETSTATE(mii, SWPAGE2); bool store80 = SW_GETSTATE(mii, SW80STORE); bool hires = SW_GETSTATE(mii, SWHIRES); bool ramrd = SW_GETSTATE(mii, SWRAMRD); bool ramwrt = SW_GETSTATE(mii, SWRAMWRT); bool intcxrom = SW_GETSTATE(mii, SWINTCXROM); bool slotc3rom = SW_GETSTATE(mii, SWSLOTC3ROM); bool intc8rom = SW_GETSTATE(mii, INTC8ROM); if (unlikely(mii->trace_cpu)) printf("%04x: MEM update altzp:%d page2:%d store80:%d " "hires:%d ramrd:%d ramwrt:%d intcxrom:%d " "slotc3rom:%d\n", mii->cpu.PC, altzp, page2, store80, hires, ramrd, ramwrt, intcxrom, slotc3rom); // clean slate mii_page_set(mii, MII_BANK_MAIN, MII_BANK_MAIN, 0x00, 0xbf); mii_page_set(mii, MII_BANK_SW, MII_BANK_SW, 0xc0, 0xc0); mii_page_set(mii, MII_BANK_ROM, MII_BANK_ROM, 0xc1, 0xff); if (altzp) mii_page_set(mii, MII_BANK_AUX, MII_BANK_AUX, 0x00, 0x01); mii_page_set(mii, ramrd ? MII_BANK_AUX : MII_BANK_MAIN, ramwrt ? MII_BANK_AUX : MII_BANK_MAIN, 0x02, 0xbf); if (store80) { mii_page_set(mii, page2 ? MII_BANK_AUX : MII_BANK_MAIN, page2 ? MII_BANK_AUX : MII_BANK_MAIN, 0x04, 0x07); if (hires) mii_page_set(mii, page2 ? MII_BANK_AUX : MII_BANK_MAIN, page2 ? MII_BANK_AUX : MII_BANK_MAIN, 0x20, 0x3f); } // c1-cf are at ROM state when we arrive here if (!intcxrom) { mii_page_set(mii, MII_BANK_CARD_ROM, _SAME, 0xc1, 0xcf); if (!slotc3rom) mii_page_set(mii, MII_BANK_ROM, _SAME, 0xc3, 0xc3); if (intc8rom) mii_page_set(mii, MII_BANK_ROM, _SAME, 0xc8, 0xcf); } bool bsrread = SW_GETSTATE(mii, BSRREAD); bool bsrwrite = SW_GETSTATE(mii, BSRWRITE); bool bsrpage2 = SW_GETSTATE(mii, BSRPAGE2); mii_page_set(mii, bsrread ? altzp ? MII_BANK_AUX_BSR : MII_BANK_BSR : MII_BANK_ROM, bsrwrite ? altzp ? MII_BANK_AUX_BSR : MII_BANK_BSR : MII_BANK_ROM, 0xd0, 0xff); // BSR P2 mii_page_set(mii, bsrread ? (altzp ? MII_BANK_AUX_BSR : MII_BANK_BSR) + bsrpage2 : MII_BANK_ROM, bsrwrite ? (altzp ? MII_BANK_AUX_BSR : MII_BANK_BSR) + bsrpage2 : MII_BANK_ROM, 0xd0, 0xdf); } static void mii_bank_update_ramworks( mii_t *mii, uint8_t bank) { if (bank > 127 || !(mii->ramworks.avail & ((unsigned __int128)1ULL << bank))) bank = 0; if (!mii->ramworks.bank[bank]) { mii->ramworks.bank[bank] = malloc(0x10000); int c = 0, a = 0; for (int i = 0; i < 128; i++ ) { if (mii->ramworks.bank[i]) c++; if (mii->ramworks.avail & ((unsigned __int128)1ULL << i)) a++; } printf("%s: RAMWORKS alloc bank %2d (%dKB / %dKB)\n", __func__, bank, c * 64, a * 64); } mii->bank[MII_BANK_AUX_BASE].mem = mii->ramworks.bank[0]; mii->bank[MII_BANK_AUX].mem = mii->ramworks.bank[bank]; mii->bank[MII_BANK_AUX_BSR].mem = mii->ramworks.bank[bank]; mii->bank[MII_BANK_AUX_BSR_P2].mem = mii->ramworks.bank[bank]; } void mii_set_sw_override( mii_t *mii, uint16_t sw_addr, mii_bank_access_cb cb, void *param) { if (!mii->soft_switches_override) mii->soft_switches_override = calloc(256, sizeof(*mii->soft_switches_override)); sw_addr &= 0xff; mii->soft_switches_override[sw_addr].cb = cb; mii->soft_switches_override[sw_addr].param = param; } /* * This watches for any write to 0xcfff -- if a card had it's aux rom * selected, it will deselect it. */ static bool _mii_deselect_cXrom( mii_t * mii, uint16_t addr, uint8_t * byte, bool write) { if (addr != 0xcfff) return false; if (!SW_GETSTATE(mii, INTC8ROM)) return false; for (int i = 0; i < 7; i++) { mii_slot_t * slot = &mii->slot[i]; if (slot->aux_rom_selected) { printf("%s %d: %s\n", __func__, i, slot->drv ? slot->drv->name : "(none?)"); slot->aux_rom_selected = false; } } SW_SETSTATE(mii, INTC8ROM, 0); mii->mem_dirty = true; mii_page_table_update(mii); return false; } static bool _mii_select_c3introm( struct mii_bank_t *bank, void *param, uint16_t addr, uint8_t * byte, bool write) { mii_t * mii = param; if (!SW_GETSTATE(mii, SWSLOTC3ROM) && !SW_GETSTATE(mii, INTC8ROM)) { SW_SETSTATE(mii, INTC8ROM, 1); mii->mem_dirty = true; mii_page_table_update(mii); } return false; } static bool mii_access_soft_switches( mii_t *mii, uint16_t addr, uint8_t * byte, bool write) { if (!(addr >= 0xc000 && addr <= 0xc0ff)) return false; bool res = false; uint8_t on = 0; mii_bank_t * sw = &mii->bank[MII_BANK_SW]; /* * This allows driver (titan accelerator etc) to have their own * soft switches, and override/supplement any default ones. */ if (mii->soft_switches_override && mii->soft_switches_override[addr & 0xff].cb) { res = mii->soft_switches_override[addr & 0xff].cb( sw, mii->soft_switches_override[addr & 0xff].param, addr, byte, write); if (res) return res; } switch (addr) { case 0xc090 ... 0xc0ff: { res = true; int slot = ((addr >> 4) & 7) - 1; #if 0 printf("SLOT %d addr %04x write %d %02x drv %s\n", slot, addr, write, *byte, mii->slot[slot].drv ? mii->slot[slot].drv->name : "none"); #endif if (mii->slot[slot].drv) { on = mii->slot[slot].drv->access(mii, &mii->slot[slot], addr, *byte, write); if (!write) *byte = on; } } break; /* SATHER-SATHER-SATHER-SATHER-SATHER-SATHER-SATHER-SATHER-SATHER-SATHER Writing to high RAM is enabled when the HRAMWRT' soft switch is reset. The controlling MPU program must set the PRE-WRITE soft switch before it can reset HRAMWRT'. PRE-WRITE is set in the by odd read access ub the C08X range. It is reset by even read access or any write access in the $C08X range. HRAMWRT' is reset by odd read access in the $C08X range when PRE-WRITE is set. It is set by even acce.ss in the $C08X range. Any other type of access causes HRAMWRT' to hold its current state. */ case 0xc080 ... 0xc08f: { res = true; const int offSwitch = addr & 0x02; if (addr & 0x01) { // Write switch // 0xC081, 0xC083 if (!write) { if (SW_GETSTATE(mii, BSRPREWRITE)) { SW_SETSTATE(mii, BSRWRITE, 1); } } SW_SETSTATE(mii, BSRPREWRITE, !write); // 0xC08B SW_SETSTATE(mii, BSRREAD, offSwitch); } else { // 0xC080, 0xC082 SW_SETSTATE(mii, BSRWRITE, 0); SW_SETSTATE(mii, BSRPREWRITE, 0); // 0xC082 SW_SETSTATE(mii, BSRREAD, !offSwitch); } SW_SETSTATE(mii, BSRPAGE2, !(addr & 0x08)); mii->mem_dirty = 1; } break; case SWPAGE2OFF: case SWPAGE2ON: res = true; SW_SETSTATE(mii, SWPAGE2, addr & 1); mii_bank_poke(sw, SWPAGE2, (addr & 1) << 7); mii->mem_dirty = 1; break; case SWHIRESOFF: case SWHIRESON: res = true; SW_SETSTATE(mii, SWHIRES, addr & 1); mii_bank_poke(sw, SWHIRES, (addr & 1) << 7); mii->mem_dirty = 1; // printf("HIRES %s\n", (addr & 1) ? "ON" : "OFF"); break; case SWSPEAKER: res = true; mii_speaker_click(&mii->speaker); break; case 0xc064 ... 0xc067: // Joystick, buttons case 0xc070: // Analog reset res = true; mii_analog_access(mii, &mii->analog, addr, byte, write); break; case 0xc068: res = true; // IIgs register, read by prodos tho break; } if (res && !mii->mem_dirty) return res; if (write) { switch (addr) { case SW80STOREOFF: case SW80STOREON: res = true; SW_SETSTATE(mii, SW80STORE, addr & 1); mii_bank_poke(sw, SW80STORE, (addr & 1) << 7); mii->mem_dirty = 1; break; case SWRAMRDOFF: case SWRAMRDON: res = true; SW_SETSTATE(mii, SWRAMRD, addr & 1); mii_bank_poke(sw, SWRAMRD, (addr & 1) << 7); mii->mem_dirty = 1; break; case SWRAMWRTOFF: case SWRAMWRTON: res = true; SW_SETSTATE(mii, SWRAMWRT, addr & 1); mii_bank_poke(sw, SWRAMWRT, (addr & 1) << 7); mii->mem_dirty = 1; break; case SWALTPZOFF: case SWALTPZON: res = true; SW_SETSTATE(mii, SWALTPZ, addr & 1); mii_bank_poke(sw, SWALTPZ, (addr & 1) << 7); mii->mem_dirty = 1; break; case SWINTCXROMOFF: case SWINTCXROMON: res = true; SW_SETSTATE(mii, SWINTCXROM, addr & 1); mii_bank_poke(sw, SWINTCXROM, (addr & 1) << 7); mii->mem_dirty = 1; break; case SWSLOTC3ROMOFF: case SWSLOTC3ROMON: res = true; SW_SETSTATE(mii, SWSLOTC3ROM, addr & 1); mii_bank_poke(sw, SWSLOTC3ROM, (addr & 1) << 7); mii->mem_dirty = 1; break; case SWRAMWORKS_BANK: mii_bank_poke(sw, SWRAMWORKS_BANK, *byte); mii_bank_update_ramworks(mii, *byte); break; } } else { switch (addr) { case SWBSRBANK2: *byte = SW_GETSTATE(mii, BSRPAGE2) ? 0x80 : 0; res = true; break; case SWBSRREADRAM: *byte = SW_GETSTATE(mii, BSRREAD) ? 0x80 : 0; res = true; break; case SWRAMRD: case SWRAMWRT: case SW80STORE: case SWINTCXROM: case SWALTPZ: case SWSLOTC3ROM: res = true; *byte = mii_bank_peek(sw, addr); break; case 0xc020: // toggle TAPE output ?!?! res = true; break; case 0xc068: res = true; // IIgs register, read by prodos tho break; default: res = true; /* * this is moderately important, return some random value * as it is supposed to represent what's on the bus at the time, * typically video being decoded etc. */ *byte = mii->random[mii->random_index++]; mii->random_index &= 0xff; break; } } if (!res) { // printf("%s addr %04x write %d %02x\n", __func__, addr, write, *byte); // mii->state = MII_STOPPED; } mii_page_table_update(mii); return res; } static bool mii_access_keyboard( mii_t *mii, uint16_t addr, uint8_t * byte, bool write) { bool res = false; mii_bank_t * sw = &mii->bank[MII_BANK_SW]; switch (addr) { case SWKBD: if (!write) { res = true; *byte = mii_bank_peek(sw, SWKBD); } break; case SWAKD: { res = true; uint8_t r = mii_bank_peek(sw, SWAKD); if (!write) *byte = r; r &= 0x7f; mii_bank_poke(sw, SWAKD, r); mii_bank_poke(sw, SWKBD, r); } break; case 0xc061 ... 0xc063: // Push Button 0, 1, 2 (Apple Keys) res = true; if (!write) *byte = mii_bank_peek(sw, addr); break; } return res; } void mii_keypress( mii_t *mii, uint8_t key) { mii_bank_t * sw = &mii->bank[MII_BANK_SW]; key |= 0x80; mii_bank_poke(sw, SWAKD, key); mii_bank_poke(sw, SWKBD, key); } #define B(x) ((unsigned __int128)1ULL << (x)) static const unsigned __int128 _mii_ramworks3_config[] = { B(0x00)|B(0x01)|B(0x02)|B(0x03), B(0x04)|B(0x05)|B(0x06)|B(0x07), B(0x08)|B(0x09)|B(0x0a)|B(0x0b), B(0x0c)|B(0x0d)|B(0x0e)|B(0x0f), // 512K Expander B(0x10)|B(0x11)|B(0x12)|B(0x13), B(0x14)|B(0x15)|B(0x16)|B(0x17), // 2MB Expander A to one meg B(0x30),B(0x31),B(0x32),B(0x33), B(0x34),B(0x35),B(0x36),B(0x37), // 2MB Expander B B(0x50),B(0x51),B(0x52),B(0x53), B(0x54),B(0x55),B(0x56),B(0x57), B(0x70),B(0x71),B(0x72),B(0x73), B(0x74),B(0x75),B(0x76),B(0x77), }; #undef B void mii_init( mii_t *mii ) { memset(mii, 0, sizeof(*mii)); mii->speed = MII_SPEED_NTSC; mii->timer.map = 0; for (int i = 0; i < MII_BANK_COUNT; i++) mii->bank[i] = _mii_banks_init[i]; mii->bank[MII_BANK_ROM].mem = (uint8_t*)&iie_enhanced_rom_bin[0]; for (int i = 0; i < MII_BANK_COUNT; i++) mii_bank_init(&mii->bank[i]); uint8_t *mem = realloc(mii->bank[MII_BANK_MAIN].mem, 0x10000); mii->bank[MII_BANK_MAIN].mem = mem; mii->bank[MII_BANK_BSR].mem = mem; mii->bank[MII_BANK_BSR_P2].mem = mem; mii->ramworks.avail = 0; mii_bank_update_ramworks(mii, 0); mii->cpu.trap = MII_TRAP; // these are called once, regardless of reset mii_dd_system_init(mii, &mii->dd); mii_analog_init(mii, &mii->analog); mii_video_init(mii); mii_speaker_init(mii, &mii->speaker); mii_reset(mii, true); mii->cpu_state = mii_cpu_init(&mii->cpu); for (int i = 0; i < 7; i++) mii->slot[i].id = i; // srandom(time(NULL)); for (int i = 0; i < 256; i++) mii->random[i] = random(); mii_bank_install_access_cb(&mii->bank[MII_BANK_ROM], _mii_select_c3introm, mii, 0xc3, 0xc3); } void mii_prepare( mii_t *mii, uint32_t flags ) { int banks = (flags >> MII_INIT_RAMWORKS_BIT) & 0xf; banks = 12; if (banks > 12) banks = 12; for (int i = 0; i < banks; i++) // add available banks mii->ramworks.avail |= _mii_ramworks3_config[i]; mii_slot_drv_t * drv = mii_slot_drv_list; while (drv) { printf("%s driver: %s\n", __func__, drv->name); if (drv->probe && drv->probe(mii, flags)) { // printf("%s %s probe done\n", __func__, drv->name); } drv = drv->next; } } void mii_dispose( mii_t *mii ) { for (int i = 0; i < 7; i++) { if (mii->slot[i].drv && mii->slot[i].drv->dispose) mii->slot[i].drv->dispose(mii, &mii->slot[i]); } for (int i = 0; i < MII_BANK_COUNT; i++) mii_bank_dispose(&mii->bank[i]); for (int i = 0; i < 128; i++ ) { if (mii->ramworks.bank[i]) { free(mii->ramworks.bank[i]); mii->ramworks.bank[i] = NULL; } } mii_speaker_dispose(&mii->speaker); mii_dd_system_dispose(&mii->dd); mii->state = MII_INIT; } void mii_reset( mii_t *mii, bool cold) { // printf("%s cold %d\n", __func__, cold); mii->state = MII_RUNNING; mii->cpu_state.reset = 1; mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; mii_bank_t * sw = &mii->bank[MII_BANK_SW]; mii->sw_state = M_BSRWRITE | M_BSRPAGE2; mii_bank_poke(sw, SWSLOTC3ROM, 0); mii_bank_poke(sw, SWRAMRD, 0); mii_bank_poke(sw, SWRAMWRT, 0); mii_bank_poke(sw, SWALTPZ, 0); mii_bank_poke(sw, SW80STORE, 0); mii_bank_poke(sw, SW80COL, 0); mii_bank_poke(sw, SWRAMWORKS_BANK, 0); mii->mem_dirty = 1; if (cold) { /* these HAS to be reset in that state somehow */ mii_bank_poke(sw, SWINTCXROM, 0); uint8_t z[2] = {0x55,0x55}; mii_bank_write(main, 0x3f2, z, 2); } mii->mem_dirty = 1; mii_page_table_update(mii); for (int i = 0; i < 7; i++) { if (mii->slot[i].drv && mii->slot[i].drv->reset) mii->slot[i].drv->reset(mii, &mii->slot[i]); } } void mii_mem_access( mii_t *mii, uint16_t addr, uint8_t * d, bool wr, bool do_sw) { if (!do_sw && addr >= 0xc000 && addr <= 0xc0ff && addr != 0xcfff) return; uint8_t done = _mii_deselect_cXrom(mii, addr, d, wr) || mii_access_keyboard(mii, addr, d, wr) || mii_access_video(mii, addr, d, wr) || mii_access_soft_switches(mii, addr, d, wr); if (done) return; uint8_t page = addr >> 8; if (wr) { uint8_t m = mii->mem[page].write; mii_bank_t * b = &mii->bank[m]; if (b->ro) { // printf("%s write to RO bank %s %04x:%02x\n", // __func__, b->name, addr, *d); } else mii_bank_write(b, addr, d, 1); } else { uint8_t m = mii->mem[page].read; mii_bank_t * b = &mii->bank[m]; *d = mii_bank_peek(b, addr); } } static void _mii_handle_trap( mii_t *mii) { // printf("%s TRAP hit PC: %04x\n", __func__, mii->cpu.PC); mii->cpu_state.sync = 1; mii->cpu_state.trap = 0; mii->cpu.state = NULL; uint8_t trap = mii_read_one(mii, mii->cpu.PC); mii->cpu.PC += 1; // printf("%s TRAP %02x return PC %04x\n", __func__, trap, mii->cpu.PC); if (mii->trap.map & (1 << trap)) { if (mii->trap.trap[trap].cb) mii->trap.trap[trap].cb(mii, trap); } else { printf("%s TRAP %02x not handled\n", __func__, trap); mii->state = MII_STOPPED; } // mii->state = MII_STOPPED; } uint8_t mii_register_trap( mii_t *mii, mii_trap_handler_cb cb) { if (mii->trap.map == 0xffff) { printf("%s no more traps!!\n", __func__); return 0xff; } for (int i = 0; i < (int)sizeof(mii->trap.map) * 8; i++) { if (!(mii->trap.map & (1 << i))) { mii->trap.map |= 1 << i; mii->trap.trap[i].cb = cb; return i; } } return 0xff; } uint8_t mii_timer_register( mii_t *mii, mii_timer_p cb, void *param, int64_t when, const char *name) { if (mii->timer.map == (uint64_t)-1) { printf("%s no more timers!!\n", __func__); return 0xff; } int i = ffsll(~mii->timer.map) - 1; mii->timer.map |= 1ull << i; mii->timer.timers[i].cb = cb; mii->timer.timers[i].param = param; mii->timer.timers[i].when = when; mii->timer.timers[i].name = name; return i; } int64_t mii_timer_get( mii_t *mii, uint8_t timer_id) { if (timer_id >= (int)sizeof(mii->timer.map) * 8) return 0; return mii->timer.timers[timer_id].when; } int mii_timer_set( mii_t *mii, uint8_t timer_id, int64_t when) { if (timer_id >= (int)sizeof(mii->timer.map) * 8) return -1; mii->timer.timers[timer_id].when = when; return 0; } static void mii_timer_run( mii_t *mii, uint64_t cycles) { uint64_t timer = mii->timer.map; while (timer) { int i = ffsll(timer) - 1; timer &= ~(1ull << i); if (mii->timer.timers[i].when > 0) { mii->timer.timers[i].when -= cycles; if (mii->timer.timers[i].when <= 0) { if (mii->timer.timers[i].cb) mii->timer.timers[i].when += mii->timer.timers[i].cb(mii, mii->timer.timers[i].param); } } } } void mii_run( mii_t *mii) { /* this runs all cycles for one instruction */ uint16_t cycle = mii->cpu.cycle; do { if (unlikely(mii->trace_cpu > 1)) mii_dump_trace_state(mii); mii->cpu_state = mii_cpu_run(&mii->cpu, mii->cpu_state); mii_timer_run(mii, mii->cpu.cycle > cycle ? mii->cpu.cycle - cycle : mii->cpu.cycle); cycle = mii->cpu.cycle; // extract 16-bit address from pin mask const uint16_t addr = mii->cpu_state.addr; // const uint8_t data = mii->cpu_state.data; int wr = mii->cpu_state.w; // uint8_t d = data; if (unlikely(mii->debug.bp_map)) { for (int i = 0; i < (int)sizeof(mii->debug.bp_map) * 8; i++) { if (!(mii->debug.bp_map & (1 << i))) continue; if (addr >= mii->debug.bp[i].addr && addr < mii->debug.bp[i].addr + mii->debug.bp[i].size) { if (((mii->debug.bp[i].kind & MII_BP_R) && !wr) || ((mii->debug.bp[i].kind & MII_BP_W) && wr)) { if (1 || !mii->debug.bp[i].silent) { printf("BREAKPOINT %d at %04x PC:%04x\n", i, addr, mii->cpu.PC); mii_dump_run_trace(mii); mii_dump_trace_state(mii); mii->state = MII_STOPPED; } } if (!(mii->debug.bp[i].kind & MII_BP_STICKY)) mii->debug.bp_map &= ~(1 << i); mii->debug.bp[i].kind |= MII_BP_HIT; } } } mii_mem_access(mii, addr, &mii->cpu_state.data, wr, true); if (unlikely(mii->cpu_state.trap)) { _mii_handle_trap(mii); } } while (!(mii->cpu_state.sync)); // log PC for the running disassembler display mii->trace.log[mii->trace.idx] = mii->cpu.PC; mii->trace.idx = (mii->trace.idx + 1) & (MII_PC_LOG_SIZE - 1); for (int i = 0; i < 7; i++) { if (mii->slot[i].drv && mii->slot[i].drv->run) mii->slot[i].drv->run(mii, &mii->slot[i]); } } //! Read one byte from and addres, using the current memory mapping uint8_t mii_read_one( mii_t *mii, uint16_t addr) { uint8_t d = 0; mii_mem_access(mii, addr, &d, 0, false); return d; } //! Read a word from addr, using current memory mapping (little endian) uint16_t mii_read_word( mii_t *mii, uint16_t addr) { uint8_t d = 0; uint16_t res = 0; mii_mem_access(mii, addr, &d, 0, false); res = d; mii_mem_access(mii, addr + 1, &d, 0, false); res |= d << 8; return res; } /* same accessors, for write */ void mii_write_one( mii_t *mii, uint16_t addr, uint8_t d) { mii_mem_access(mii, addr, &d, 1, false); } void mii_write_word( mii_t *mii, uint16_t addr, uint16_t w) { uint8_t d = w; mii_mem_access(mii, addr, &d, 1, false); d = w >> 8; mii_mem_access(mii, addr + 1, &d, 1, false); }