/* * Copyright (c) 2013, Peter Rutenbar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "../core/shoebill.h" #ifdef __APPLE__ #include #define ntohll(x) OSSwapBigToHostInt64(x) #endif /* --- Physical_get jump table --- */ #pragma mark Physical_get jump table void _physical_get_ram (void) { uint64_t *addr; if (shoe.physical_addr < shoe.physical_mem_size) addr = (uint64_t*)&shoe.physical_mem_base[shoe.physical_addr]; else addr = (uint64_t*)&shoe.physical_mem_base[shoe.physical_addr % shoe.physical_mem_size]; /*if ((shoe.physical_addr >= 0x100) && (shoe.physical_addr < (0x4000+256))) { uint32_t i, val = 0; _Bool uninit = 0; for (i=0; i> bits; if ((shoe.physical_addr >= 256) && (shoe.physical_addr < 0x4000)) { printf("LOMEM get: *0x%08x = 0x%x\n", shoe.physical_addr, (uint32_t)shoe.physical_dat); } } void _physical_get_rom (void) { uint64_t *addr = (uint64_t*)&shoe.physical_rom_base[shoe.physical_addr & (shoe.physical_rom_size-1)]; const uint8_t bits = (8 - shoe.physical_size) * 8; shoe.physical_dat = ntohll(*addr) >> bits; } void _physical_get_io (void) { switch (shoe.physical_addr & 0x5003ffff) { case 0x50000000 ... 0x50003fff: // VIA1 + VIA2 via_read_raw(); return ; case 0x50004000 ... 0x50005fff: {// SCC //printf("physical_get: got read to SCC\n"); const uint32_t a = shoe.physical_addr & 0x1fff; if (a == 2 && shoe.physical_size==1) shoe.physical_dat = 0x4; // R0TXRDY return ; } case 0x50006000 ... 0x50007fff: // SCSI (pseudo-DMA with DRQ?) assert(shoe.logical_size == 4); shoe.physical_dat = scsi_dma_read_long(); return ; case 0x50010000 ... 0x50011fff: // SCSI (normal mode?) scsi_reg_read(); return ; case 0x50012000 ... 0x50013fff: // SCSI (pseudo-DMA with no DRQ?) assert(shoe.logical_size == 1); shoe.physical_dat = scsi_dma_read(); return ; case 0x50014000 ... 0x50015fff: // Sound //printf("physical_get: got read to sound\n"); printf("soundsound read : register 0x%04x sz=%u\n", shoe.physical_addr - 0x50014000, shoe.physical_size); shoe.physical_dat = 0; return ; case 0x50016000 ... 0x50017fff: // SWIM (IWM?) // printf("physical_get: got read to IWM\n"); shoe.physical_dat = iwm_dma_read(); return ; default: //printf("physical_get: got read to UNKNOWN IO ADDR %x\n", shoe.physical_addr); return ; } } void _physical_get_super_slot (void) { const uint32_t slot = shoe.physical_addr >> 28; if (shoe.slots[slot].connected) shoe.physical_dat = shoe.slots[slot].read_func(shoe.physical_addr, shoe.physical_size, slot); else shoe.abort = 1; // throw a bus error for reads to disconnected slots // XXX: Do super slot accesses raise bus errors? } void _physical_get_standard_slot (void) { const uint32_t slot = (shoe.physical_addr >> 24) & 0xf; if (shoe.slots[slot].connected) shoe.physical_dat = shoe.slots[slot].read_func(shoe.physical_addr, shoe.physical_size, slot); else shoe.abort = 1; // throw a bus error for reads to disconnected slots } const physical_get_ptr physical_get_jump_table[16] = { _physical_get_ram, // 0x0 _physical_get_ram, // 0x1 _physical_get_ram, // 0x2 _physical_get_ram, // 0x3 _physical_get_rom, // 0x4 _physical_get_io, // 0x5 _physical_get_super_slot, // 0x6 _physical_get_super_slot, // 0x7 _physical_get_super_slot, // 0x8 _physical_get_super_slot, // 0x9 _physical_get_super_slot, // 0xa _physical_get_super_slot, // 0xb _physical_get_super_slot, // 0xc _physical_get_super_slot, // 0xd _physical_get_super_slot, // 0xe _physical_get_standard_slot // 0xf }; /* --- Physical_set jump table --- */ #pragma mark Physical_set jump table void _physical_set_ram (void) { uint8_t *addr; if (shoe.physical_addr >= shoe.physical_mem_size) addr = &shoe.physical_mem_base[shoe.physical_addr % shoe.physical_mem_size]; else addr = &shoe.physical_mem_base[shoe.physical_addr]; if ((shoe.physical_addr >= 0x100) && (shoe.physical_addr < (0x8000))) { printf("LOMEM set: *0x%08x = 0x%x\n", shoe.physical_addr, (uint32_t)chop(shoe.physical_dat, shoe.physical_size)); } const uint32_t sz = shoe.physical_size; switch (sz) { case 1: *addr = (uint8_t)shoe.physical_dat; return ; case 2: *((uint16_t*)addr) = htons((uint16_t)shoe.physical_dat); return ; case 4: *((uint32_t*)addr) = htonl((uint32_t)shoe.physical_dat); return ; case 8: *(uint64_t*)addr = ntohll(shoe.physical_dat); return ; default: { uint64_t q = shoe.physical_dat; uint8_t i; for (i=1; i<=sz; i++) { addr[sz-i] = (uint8_t)q; q >>= 8; } return ; } } } void _physical_set_rom (void) { // nothing happens when you try to modify ROM } void _physical_set_io (void) { switch (shoe.physical_addr & 0x5003ffff) { case 0x50000000 ... 0x50003fff: // VIA1 + VIA2 via_write_raw(); return ; case 0x50004000 ... 0x50005fff: // SCC //printf("physical_set: got write to SCC\n"); return ; case 0x50006000 ... 0x50007fff: // SCSI (pseudo-DMA with DRQ?) assert(shoe.physical_size == 4); scsi_dma_write_long(shoe.physical_dat); return ; case 0x50010000 ... 0x50011fff: // SCSI (normal mode?) scsi_reg_write(); return ; case 0x50012000 ... 0x50013fff: // SCSI (pseudo-DMA with no DRQ?) assert(shoe.physical_size == 1); scsi_dma_write(shoe.physical_dat); return ; case 0x50014000 ... 0x50015fff: // Sound printf("soundsound write: register 0x%04x sz=%u dat=0x%x\n", shoe.physical_addr - 0x50014000, shoe.physical_size, (uint32_t)shoe.physical_dat); // printf("physical_set: got write to sound\n"); return ; case 0x50016000 ... 0x50017fff: // SWIM (IWM?) //printf("physical_set: got write to IWM\n"); iwm_dma_write(); return ; default: //printf("physical_set: got write to UNKNOWN IO ADDR %x\n", shoe.physical_addr); return ; } } void _physical_set_super_slot (void) { const uint32_t slot = shoe.physical_addr >> 28; if (shoe.slots[slot].connected) shoe.slots[slot].write_func(shoe.physical_addr, shoe.physical_size, shoe.physical_dat, slot); } void _physical_set_standard_slot (void) { const uint32_t slot = (shoe.physical_addr >> 24) & 0xf; if (shoe.slots[slot].connected) shoe.slots[slot].write_func(shoe.physical_addr, shoe.physical_size, shoe.physical_dat, slot); } const physical_set_ptr physical_set_jump_table[16] = { _physical_set_ram, // 0x0 _physical_set_ram, // 0x1 _physical_set_ram, // 0x2 _physical_set_ram, // 0x3 _physical_set_rom, // 0x4 _physical_set_io, // 0x5 _physical_set_super_slot, // 0x6 _physical_set_super_slot, // 0x7 _physical_set_super_slot, // 0x8 _physical_set_super_slot, // 0x9 _physical_set_super_slot, // 0xa _physical_set_super_slot, // 0xb _physical_set_super_slot, // 0xc _physical_set_super_slot, // 0xd _physical_set_super_slot, // 0xe _physical_set_standard_slot // 0xf }; /* --- PMMU logical address translation --- */ #pragma mark PMMU logical address translation #define PMMU_CACHE_CRP 0 #define PMMU_CACHE_SRP 1 /*typedef struct { uint32_t logical_value : 24; // At most the high 24 bits of the logical address uint32_t used_bits : 5; uint32_t wp : 1; // whether the page is write protected uint32_t modified : 1; // whether the page has been modified uint32_t unused1 : 1; uint32_t unused2 : 8; uint32_t physical_addr : 24; } pmmu_cache_entry; struct { pmmu_cache_entry entry[512]; uint8_t valid_map[512 / 8]; } pmmu_cache[2];*/ #define write_back_desc() { \ if (desc_addr < 0) { \ *rootp_ptr = desc; \ } else { \ pset((uint32_t)desc_addr, 4<= 4)); // logical addr [is]xxxxxxxxxxxx[ps] -> value xxxxxxxxxxxx const uint32_t value = (shoe.logical_addr << tc_is()) >> (tc_is() + tc_ps()); // value xxx[xxxxxxxxx] -> key xxxxxxxxx const uint32_t key = value & (PMMU_CACHE_SIZE-1); // low PMMU_CACHE_KEY_BITS bits const pmmu_cache_entry_t entry = shoe.pmmu_cache[use_srp].entry[key]; const _Bool is_set = (shoe.pmmu_cache[use_srp].valid_map[key/8] >> (key & 7)) & 1; const _Bool values_match = (entry.logical_value == value); const _Bool first_modify = !(shoe.logical_is_write && !entry.modified); const _Bool not_write_protected = !(shoe.logical_is_write && entry.wp); const uint32_t ps_mask = 0xffffffff >> entry.used_bits; const uint32_t v_mask = ~~ps_mask; shoe.physical_addr = ((entry.physical_addr<<8) & v_mask) | (shoe.logical_addr & ps_mask); return is_set && values_match && first_modify && not_write_protected; } static void translate_logical_addr() { const uint8_t use_srp = (tc_sre() && (shoe.logical_fc >= 4)); uint64_t *rootp_ptr = (use_srp ? (&shoe.srp) : (&shoe.crp)); const uint64_t rootp = *rootp_ptr; uint8_t desc_did_change = 0; uint8_t desc_level = 0; int64_t desc_addr = -1; // address of the descriptor (-1 -> register) uint8_t wp = 0; // Whether any descriptor in the search has wp (write protected) set uint8_t i; uint64_t desc = rootp; // Initial descriptor is the root pointer descriptor uint8_t desc_size = 1; // And the root pointer descriptor is always 8 bytes (1==8 bytes, 0==4 bytes) uint8_t used_bits = tc_is(); // Keep track of how many bits will be the effective "page size" // (If the table search terminates early (before used_bits == ts_ps()), // then (32 - used_bits) will be the effective page size. That is, the number of bits // we or into the physical addr from the virtual addr) desc_addr = -1; // address of the descriptor (-1 -> register) /* We'll keep shifting logical_addr such that its most significant bits are the next ti-index */ uint32_t logical_addr = shoe.logical_addr << used_bits; // TODO: Check limit here // If root descriptor is invalid, throw a bus error if (rp_dt(rootp) == 0) { throw_bus_error(shoe.logical_addr, shoe.logical_is_write); return ; } // desc is a page descriptor, skip right ahead to search_done: if (rp_dt(rootp) == 1) goto search_done; // for (i=0; i < 4; i++) { // (the condition is unnecessary - just leaving it in for clarity) for (i=0; 1; i++) { // desc must be a table descriptor here const uint8_t ti = tc_ti(i); used_bits += ti; // TODO: do the limit check here // Find the index into our current i-level table const uint32_t index = logical_addr >> (32-ti); logical_addr <<= ti; // load the child descriptor const uint32_t table_base_addr = desc_table_addr(desc); const uint8_t s = desc_dt(desc, desc_size) & 1; // desc = pget(table_base_addr + (4 << s)*index, (4 << s)); get_desc(table_base_addr + (4 << s)*index, (4 << s)); desc_size = s; // Desc may be a table descriptor, page descriptor, indirect descriptor, or invalid descriptor const uint8_t dt = desc_dt(desc, desc_size); // If this descriptor is invalid, throw a bus error if (dt == 0) { throw_bus_error(shoe.logical_addr, shoe.logical_is_write); return ; } if (dt == 1) { // TODO: do a limit check here goto search_done; // it's a page descriptor } else if ((i==3) || (tc_ti(i+1)==0)) { desc_size = desc_dt(desc, s) & 1; /* Otherwise, if this is a leaf in the tree, it's an indirect descriptor. The size of the page descriptor is indicated by DT in the indirect descriptor. (or is it???) */ //desc = pget(desc & 0xfffffff0, (4 << desc_size)); get_desc(desc & 0xfffffff0, (4 << desc_size)); // I think it's possible for an indirect descriptor to point to an invalid descriptor... if (desc_dt(desc, desc_size) == 0) { throw_bus_error(shoe.logical_addr, shoe.logical_is_write); return ; } goto search_done; } // Now it must be a table descriptor // TODO: set the U (used) bit in this table descriptor wp |= desc_wp(desc, desc_size); // or in the wp flag for this table descriptor } // never get here assert(!"translate_logical_addr: never get here"); search_done: // Desc must be a page descriptor // NOTE: The limit checks have been done already // (or would be, if they were implemented yet) // TODO: update U (used) bit wp |= desc_wp(desc, desc_size); // or in the wp flag for this page descriptor // And finally throw a bus error if (wp && shoe.logical_is_write) { throw_bus_error(shoe.logical_addr, shoe.logical_is_write); return ; } // If we're writing, set the modified flag if (shoe.logical_is_write && !(desc & ( desc_size ? desc_m_long : desc_m_short ))) { desc |= ( desc_size ? desc_m_long : desc_m_short ); write_back_desc(); } const uint32_t ps_mask = 0xffffffff >> used_bits; const uint32_t v_mask = ~~ps_mask; const uint32_t paddr = (desc_page_addr(desc) & v_mask) | (shoe.logical_addr & ps_mask); shoe.physical_addr = paddr; /* --- insert this translation into pmmu_cache --- */ // logical addr [is]xxxxxxxxxxxx[ps] -> value xxxxxxxxxxxx const uint32_t value = (shoe.logical_addr << tc_is()) >> (tc_is() + tc_ps()); // value xxx[xxxxxxxxx] -> key xxxxxxxxx const uint32_t key = value & (PMMU_CACHE_SIZE-1); // low PMMU_CACHE_KEY_BITS bits pmmu_cache_entry_t entry; shoe.pmmu_cache[use_srp].valid_map[key/8] |= (1 << (key & 7)); entry.logical_value = value; entry.physical_addr = (desc_page_addr(desc)) >> 8; entry.wp = wp; entry.modified = desc_m(desc, desc_size); entry.used_bits = used_bits; shoe.pmmu_cache[use_srp].entry[key] = entry; } void logical_get (void) { // If address translation isn't enabled, this is a physical address if (!tc_enable()) { shoe.physical_addr = shoe.logical_addr; shoe.physical_size = shoe.logical_size; physical_get(); if (shoe.abort) { shoe.abort = 0; throw_long_bus_error(shoe.logical_addr, 0); return ; } shoe.logical_dat = shoe.physical_dat; return ; } const uint32_t logical_size = shoe.logical_size; const uint32_t logical_addr = shoe.logical_addr; const uint16_t ps = tc_ps(); // log2 of the page size const uint32_t pagesize = 1 << ps; // the page size const uint32_t pagemask = pagesize-1; // a mask of the page bits const uint32_t pageoffset = logical_addr & pagemask; shoe.logical_is_write = 0; // Common case: the read is contained entirely within a page if (!((pageoffset + logical_size - 1) >> ps)) { if (!check_pmmu_cache()) { translate_logical_addr(); if (shoe.abort) return ; } if (shoe.physical_addr < shoe.physical_mem_size) { // Fast path shoe.logical_dat = ntohll(*(uint64_t*)&shoe.physical_mem_base[shoe.physical_addr]) >> ((8-logical_size)*8); } else { shoe.physical_size = logical_size; physical_get(); if (shoe.abort) { shoe.abort = 0; throw_long_bus_error(logical_addr, 0); return ; } shoe.logical_dat = shoe.physical_dat; } } else { // This read crosses a page boundary const uint32_t addr_a = logical_addr; const uint32_t size_b = (pageoffset + logical_size) & pagemask; const uint32_t size_a = logical_size - size_b; const uint32_t addr_b = addr_a + size_a; shoe.logical_addr = addr_a; shoe.logical_size = size_a; if (!check_pmmu_cache()) { translate_logical_addr(); if (shoe.abort) return ; } const uint32_t p_addr_a = shoe.physical_addr; shoe.logical_addr = addr_b; shoe.logical_size = size_b; if (!check_pmmu_cache()) { translate_logical_addr(); if (shoe.abort) return ; } const uint32_t p_addr_b = shoe.physical_addr; shoe.physical_size = size_b; physical_get(); if (shoe.abort) { shoe.abort = 0; throw_long_bus_error(shoe.logical_addr, 0); return ; } const uint64_t fetch_b = shoe.physical_dat; shoe.physical_addr = p_addr_a; shoe.physical_size = size_a; physical_get(); if (shoe.abort) { shoe.abort = 0; throw_long_bus_error(shoe.logical_addr, 0); return ; } shoe.logical_dat = (shoe.physical_dat << (size_b*8)) | fetch_b; } } void logical_set (void) { // If address translation isn't enabled, this is a physical address if (!tc_enable()) { shoe.physical_addr = shoe.logical_addr; shoe.physical_size = shoe.logical_size; shoe.physical_dat = shoe.logical_dat; physical_set(); return ; } const uint32_t logical_size = shoe.logical_size; const uint32_t logical_addr = shoe.logical_addr; const uint16_t ps = tc_ps(); // log2 of the page size const uint32_t pagesize = 1 << ps; // the page size const uint32_t pagemask = pagesize-1; // a mask of the page bits const uint32_t pageoffset = logical_addr & pagemask; // Make the translate function fail if the page is write-protected shoe.logical_is_write = 1; // Common case: this write is contained entirely in one page if (!((pageoffset + logical_size - 1) >> ps)) { // Common case: the write is contained entirely within a page if (!check_pmmu_cache()) { translate_logical_addr(); if (shoe.abort) return ; } shoe.physical_size = shoe.logical_size; shoe.physical_dat = shoe.logical_dat; physical_set(); } else { // This write crosses a page boundary const uint32_t addr_a = shoe.logical_addr; const uint32_t size_b = (pageoffset + logical_size) & pagemask; const uint32_t size_a = shoe.logical_size - size_b; const uint32_t addr_b = addr_a + size_a; const uint64_t data_a = shoe.logical_dat >> (size_b*8); const uint64_t data_b = bitchop_64(shoe.logical_dat, size_b*8); shoe.logical_addr = addr_a; shoe.logical_size = size_a; if (!check_pmmu_cache()) { translate_logical_addr(); if (shoe.abort) return ; } const uint32_t p_addr_a = shoe.physical_addr; shoe.logical_addr = addr_b; shoe.logical_size = size_b; if (!check_pmmu_cache()) { translate_logical_addr(); if (shoe.abort) return ; } const uint32_t p_addr_b = shoe.physical_addr; shoe.physical_addr = p_addr_a; shoe.physical_size = size_a; shoe.physical_dat = data_a; physical_set(); shoe.physical_addr = p_addr_b; shoe.physical_size = size_b; shoe.physical_dat = data_b; physical_set(); return ; } } /* --- EA routines --- */ #pragma mark EA routines #define nextword(pc) ({const uint16_t w=lget((pc),2);if (shoe.abort){return;}(pc)+=2; w;}) #define nextlong(pc) ({const uint32_t L=lget((pc),4);if (shoe.abort){return;}(pc)+=4; L;}) // ea_decode_extended() - find the EA for those hiddeous 68020 addr modes static void ea_decode_extended() { const uint32_t start_pc = shoe.pc; // the original PC uint32_t mypc = start_pc; // our local PC (don't modify up the real PC) const uint32_t ext_a = nextword(mypc); // the extension word ~decompose(ext_a, d rrr w ss F b i zz 0 III) // d == index register type // r == index register // w == word/long-word index size // s == scale factor // F == extension word format (0==brief, 1==full) // b == base register suppress // i == index suppress // z == base displacement size // I == index/indirect selection if (F == 0) { // If this is the brief extension word // use the sign-extended least significant byte in the extension word uint32_t base_disp = (int8_t)(ext_a & 0xff); // load the base_address uint32_t base_addr; if (~bmatch(shoe.mr, 00xx1xxx)) { // consult the MR, use the PC? base_addr = start_pc; // start at the beginning of the extension word } else { // otherwise, it's shoe.a[shoe.mr&7] base_addr = shoe.a[shoe.mr&7]; } // load the index value uint32_t index_val; if (w==0) { // use signed-extended lower word of the register if (d==0) // shoe.d[] index_val = (int16_t)(get_d(r, 2)); else // shoe.a[] index_val = (int16_t)(get_a(r, 2)); } else { // use entire register if (d==0) index_val = shoe.d[r]; else index_val = shoe.a[r]; } // Scale the index value index_val <<= s; // the brief extension word is implicitly preindexed shoe.extended_addr = base_addr + base_disp + index_val; shoe.extended_len = mypc - start_pc; //printf("I found address 0x%x\n", shoe.extended_addr); return ; } else { // If this is a full extension word, // first find the base address, which may be shoe.a[?] or shoe.pc uint32_t base_addr = 0; if (b == 0) { // only if it isn't suppressed if (~bmatch(shoe.mr, 00xx1xxx)) { // consult the MR, base_addr = start_pc; // start at the beginning of the extension word } else { // otherwise, it's shoe.a[shoe.mr&7] base_addr = shoe.a[shoe.mr&7]; } } // Find the index value uint32_t index_val = 0; if (i == 0) { // only if it isn't suppressed if (w==0) { // use signed-extended lower word of the register if (d==0) // shoe.d[] index_val = (int16_t)(get_d(r, 2)); else // shoe.a[] index_val = (int16_t)(get_a(r, 2)); } else { // use entire register if (d==0) index_val = shoe.d[r]; else index_val = shoe.a[r]; } // Scale the index value index_val <<= s; } // Find the base displacement uint32_t base_disp = 0; // ... but only if the size is > null if (z > 1) { if (z == 2) { // if word-length, fetch nextword() and sign-extend base_disp = (int16_t)(nextword(mypc)); } else { // otherwise, it's a longword base_disp = nextlong(mypc); } } // Find the outer displacement uint32_t outer_disp = 0; // based on the I/IS behavior switch ((i<<3)|I) { case 0b0010: case 0b0110: case 0b1010: // sign-extended word-length outer displacement outer_disp = (int16_t)nextword(mypc); break; case 0b0011: case 0b0111: case 0b1011: { // long word outer displacement outer_disp = nextlong(mypc); break ; } } //printf("D/A=%u, reg=%u, W/L=%u, Scale=%u, F=%u, BS=%u, IS=%u, BDSize=%u, I/IS=%u\n", //d, r, w, s, F, b, i, z, I); //printf("base_addr=%x, index_val=%x, base_disp=%x, outer_disp=%x\n", //base_addr, index_val, base_disp, outer_disp); // Now mash all these numbers together to get an EA switch ((i<<3)|I) { case 0b0001: case 0b0010: case 0b0011: case 0b1001: case 0b1010: case 0b1011: { // Indirect preindexed const uint32_t intermediate = lget(base_addr + base_disp + index_val, 4); if (shoe.abort) return ; shoe.extended_addr = intermediate + outer_disp; shoe.extended_len = mypc - start_pc; // printf("addr=0x%x len=%u\n", shoe.extended_addr, shoe.extended_len); return ; } case 0b0101: case 0b0110: case 0b0111: { // Indirect postindexed const uint32_t intermediate = lget(base_addr + base_disp, 4); if (shoe.abort) return ; shoe.extended_addr = intermediate + index_val + outer_disp; shoe.extended_len = mypc - start_pc; return ; } case 0b1000: case 0b0000: { // No memory indirect action // EA = base_addr + base_disp + index shoe.extended_addr = base_addr + base_disp + index_val; shoe.extended_len = mypc - start_pc; return ; } default: assert(!"ea_decode_extended: oh noes! invalid I/IS!\n"); // I think that 68040 *doesn't* throw an exception here... // FIXME: figure out what actually happens here break; } } } // Data register direct mode void _ea_000_read (void) { shoe.dat = get_d(shoe.mr & 7, shoe.sz); } void _ea_000_write (void) { set_d(shoe.mr & 7, shoe.dat, shoe.sz); } // address register direct mode void _ea_001_read (void) { shoe.dat = get_a(shoe.mr & 7, shoe.sz); } void _ea_001_write (void) { assert(shoe.sz==4); shoe.a[shoe.mr & 7] = shoe.dat; } // address register indirect mode void _ea_010_read (void) { shoe.dat = lget(shoe.a[shoe.mr & 7], shoe.sz); } void _ea_010_write (void) { lset(shoe.a[shoe.mr & 7], shoe.sz, shoe.dat); } void _ea_010_addr (void) { shoe.dat = shoe.a[shoe.mr & 7]; } // address register indirect with postincrement mode void _ea_011_read (void) { shoe.dat = lget(shoe.a[shoe.mr & 7], shoe.sz); } void _ea_011_read_commit (void) { const uint8_t reg = shoe.mr & 7; shoe.a[reg] += (((reg==7) && (shoe.sz==1)) ? 2 : shoe.sz); } void _ea_011_write (void) { const uint8_t reg = shoe.mr & 7; const uint8_t delta = ((reg==7) && (shoe.sz==1)) ? 2 : shoe.sz; lset(shoe.a[reg], shoe.sz, shoe.dat); if (!shoe.abort) shoe.a[reg] += delta; } // address register indirect with predecrement mode void _ea_100_read (void) { const uint8_t reg = shoe.mr & 7; const uint8_t delta = ((reg==7) && (shoe.sz==1))?2:shoe.sz; shoe.dat = lget(shoe.a[reg]-delta, shoe.sz); } void _ea_100_read_commit (void) { const uint8_t reg = shoe.mr & 7; shoe.a[reg] -= (((reg==7) && (shoe.sz==1)) ? 2 : shoe.sz); } void _ea_100_write (void) { const uint8_t reg = shoe.mr & 7; const uint8_t delta = ((reg==7) && (shoe.sz==1)) ? 2 : shoe.sz; lset(shoe.a[reg] - delta, shoe.sz, shoe.dat); if (!shoe.abort) shoe.a[reg] -= delta; } // address register indirect with displacement mode void _ea_101_read (void) { shoe.uncommitted_ea_read_pc = shoe.pc; const int16_t disp = nextword(shoe.uncommitted_ea_read_pc); shoe.dat = lget(shoe.a[shoe.mr & 7] + disp, shoe.sz); } void _ea_101_read_commit (void) { shoe.pc += 2; } void _ea_101_write (void) { const int16_t disp = nextword(shoe.pc); lset(shoe.a[shoe.mr & 7] + disp, shoe.sz, shoe.dat); } void _ea_101_addr (void) { const int16_t disp = nextword(shoe.pc); shoe.dat = shoe.a[shoe.mr & 7] + disp; } // memory/address register indirect with index void _ea_110_read (void) { ea_decode_extended(); if (!shoe.abort) shoe.dat = lget(shoe.extended_addr, shoe.sz); shoe.uncommitted_ea_read_pc = shoe.pc + shoe.extended_len; } void _ea_110_read_commit (void) { shoe.pc = shoe.uncommitted_ea_read_pc; } void _ea_110_write (void) { ea_decode_extended(); if (!shoe.abort) { lset(shoe.extended_addr, shoe.sz, shoe.dat); if (!shoe.abort) shoe.pc += shoe.extended_len; } } void _ea_110_addr (void) { ea_decode_extended(); if (!shoe.abort) { shoe.dat = shoe.extended_addr; shoe.pc += shoe.extended_len; } } // absolute short addressing mode void _ea_111_000_read (void) { shoe.uncommitted_ea_read_pc = shoe.pc; const int32_t addr = (int16_t)nextword(shoe.uncommitted_ea_read_pc); shoe.dat = lget((uint32_t)addr, shoe.sz); } void _ea_111_000_read_commit (void) { shoe.pc += 2; } void _ea_111_000_write (void) { const int32_t addr = (int16_t)nextword(shoe.pc); lset((uint32_t)addr, shoe.sz, shoe.dat); } void _ea_111_000_addr (void) { const int32_t addr = (int16_t)nextword(shoe.pc); shoe.dat = (uint32_t)addr; } // absolute long addressing mode void _ea_111_001_read (void) { shoe.uncommitted_ea_read_pc = shoe.pc; const uint32_t addr = nextlong(shoe.uncommitted_ea_read_pc); shoe.dat = lget(addr, shoe.sz); } void _ea_111_001_read_commit (void) { shoe.pc += 4; } void _ea_111_001_write (void) { const uint32_t addr = nextlong(shoe.pc); lset(addr, shoe.sz, shoe.dat); } void _ea_111_001_addr (void) { const uint32_t addr = nextlong(shoe.pc); shoe.dat = addr; } // program counter indirect with displacement mode void _ea_111_010_read (void) { const uint32_t base_pc = shoe.pc; shoe.uncommitted_ea_read_pc = base_pc; const int16_t disp = nextword(shoe.uncommitted_ea_read_pc); shoe.dat = lget(base_pc + disp, shoe.sz); } void _ea_111_010_read_commit (void) { shoe.pc += 2; } void _ea_111_010_addr (void) { const uint32_t oldpc = shoe.pc; const int16_t displacement = nextword(shoe.pc); shoe.dat = oldpc + displacement; } // (program counter ...) void _ea_111_011_read (void) { ea_decode_extended(); if (!shoe.abort) shoe.dat = lget(shoe.extended_addr, shoe.sz); shoe.uncommitted_ea_read_pc = shoe.pc + shoe.extended_len; } void _ea_111_011_read_commit (void) { shoe.pc = shoe.uncommitted_ea_read_pc; } void _ea_111_011_addr (void) { ea_decode_extended(); if (!shoe.abort) { shoe.dat = shoe.extended_addr; shoe.pc += shoe.extended_len; } } // immediate data void _ea_111_100_read (void) { if (shoe.sz == 1) { shoe.uncommitted_ea_read_pc = shoe.pc + 2; shoe.dat = lget(shoe.pc, 2) & 0xff; } else { shoe.uncommitted_ea_read_pc = shoe.pc + shoe.sz; shoe.dat = lget(shoe.pc, shoe.sz); } } void _ea_111_100_read_commit (void) { shoe.pc = shoe.uncommitted_ea_read_pc; } // illegal EA mode void _ea_illegal (void) { throw_illegal_instruction(); } // nothing to do void _ea_nop (void) { } const _ea_func ea_read_jump_table[64] = { // Data register direct mode _ea_000_read, _ea_000_read, _ea_000_read, _ea_000_read, _ea_000_read, _ea_000_read, _ea_000_read, _ea_000_read, // address register direct mode _ea_001_read, _ea_001_read, _ea_001_read, _ea_001_read, _ea_001_read, _ea_001_read, _ea_001_read, _ea_001_read, // address register indirect mode _ea_010_read, _ea_010_read, _ea_010_read, _ea_010_read, _ea_010_read, _ea_010_read, _ea_010_read, _ea_010_read, // address register indirect with postincrement mode _ea_011_read, _ea_011_read, _ea_011_read, _ea_011_read, _ea_011_read, _ea_011_read, _ea_011_read, _ea_011_read, // address register indirect with predecrement mode _ea_100_read, _ea_100_read, _ea_100_read, _ea_100_read, _ea_100_read, _ea_100_read, _ea_100_read, _ea_100_read, // address register indirect with displacement mode _ea_101_read, _ea_101_read, _ea_101_read, _ea_101_read, _ea_101_read, _ea_101_read, _ea_101_read, _ea_101_read, // memory/address register indirect with index _ea_110_read, _ea_110_read, _ea_110_read, _ea_110_read, _ea_110_read, _ea_110_read, _ea_110_read, _ea_110_read, // absolute short addressing mode _ea_111_000_read, // absolute long addressing mode _ea_111_001_read, // program counter indirect with displacement mode _ea_111_010_read, // program counter indirect with index _ea_111_011_read, // immediate _ea_111_100_read, // The rest are illegal EA modes _ea_illegal, _ea_illegal, _ea_illegal }; const _ea_func ea_read_commit_jump_table[64] = { // Data register direct mode _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, // address register direct mode _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, // address register indirect mode _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, _ea_nop, // address register indirect with postincrement mode _ea_011_read_commit, _ea_011_read_commit, _ea_011_read_commit, _ea_011_read_commit, _ea_011_read_commit, _ea_011_read_commit, _ea_011_read_commit, _ea_011_read_commit, // address register indirect with predecrement mode _ea_100_read_commit, _ea_100_read_commit, _ea_100_read_commit, _ea_100_read_commit, _ea_100_read_commit, _ea_100_read_commit, _ea_100_read_commit, _ea_100_read_commit, // address register indirect with displacement mode _ea_101_read_commit, _ea_101_read_commit, _ea_101_read_commit, _ea_101_read_commit, _ea_101_read_commit, _ea_101_read_commit, _ea_101_read_commit, _ea_101_read_commit, // memory/address register indirect with index _ea_110_read_commit, _ea_110_read_commit, _ea_110_read_commit, _ea_110_read_commit, _ea_110_read_commit, _ea_110_read_commit, _ea_110_read_commit, _ea_110_read_commit, // absolute short addressing mode _ea_111_000_read_commit, // absolute long addressing mode _ea_111_001_read_commit, // program counter indirect with displacement mode _ea_111_010_read_commit, // program counter indirect with index _ea_111_011_read_commit, // immediate _ea_111_100_read_commit, // The rest are illegal EA modes NULL, NULL, NULL }; const _ea_func ea_write_jump_table[64] = { // Data register direct mode _ea_000_write, _ea_000_write, _ea_000_write, _ea_000_write, _ea_000_write, _ea_000_write, _ea_000_write, _ea_000_write, // address register direct mode _ea_001_write, _ea_001_write, _ea_001_write, _ea_001_write, _ea_001_write, _ea_001_write, _ea_001_write, _ea_001_write, // address register indirect mode _ea_010_write, _ea_010_write, _ea_010_write, _ea_010_write, _ea_010_write, _ea_010_write, _ea_010_write, _ea_010_write, // address register indirect with postincrement mode _ea_011_write, _ea_011_write, _ea_011_write, _ea_011_write, _ea_011_write, _ea_011_write, _ea_011_write, _ea_011_write, // address register indirect with predecrement mode _ea_100_write, _ea_100_write, _ea_100_write, _ea_100_write, _ea_100_write, _ea_100_write, _ea_100_write, _ea_100_write, // address register indirect with displacement mode _ea_101_write, _ea_101_write, _ea_101_write, _ea_101_write, _ea_101_write, _ea_101_write, _ea_101_write, _ea_101_write, // memory/address register indirect with index _ea_110_write, _ea_110_write, _ea_110_write, _ea_110_write, _ea_110_write, _ea_110_write, _ea_110_write, _ea_110_write, // absolute short addressing mode _ea_111_000_write, // absolute long addressing mode _ea_111_001_write, // program counter indirect with displacement mode _ea_illegal, // program counter indirect with index _ea_illegal, // immediate _ea_illegal, // The rest are illegal EA modes _ea_illegal, _ea_illegal, _ea_illegal }; const _ea_func ea_addr_jump_table[64] = { // Data register direct mode _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, // address register direct mode _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, // address register indirect mode _ea_010_addr, _ea_010_addr, _ea_010_addr, _ea_010_addr, _ea_010_addr, _ea_010_addr, _ea_010_addr, _ea_010_addr, // address register indirect with postincrement mode _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, // address register indirect with predecrement mode _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, _ea_illegal, // address register indirect with displacement mode _ea_101_addr, _ea_101_addr, _ea_101_addr, _ea_101_addr, _ea_101_addr, _ea_101_addr, _ea_101_addr, _ea_101_addr, // memory/address register indirect with index _ea_110_addr, _ea_110_addr, _ea_110_addr, _ea_110_addr, _ea_110_addr, _ea_110_addr, _ea_110_addr, _ea_110_addr, // absolute short addressing mode _ea_111_000_addr, // absolute long addressing mode _ea_111_001_addr, // program counter indirect with displacement mode _ea_111_010_addr, // program counter indirect with index _ea_111_011_addr, // immediate _ea_illegal, // The rest are illegal EA modes _ea_illegal, _ea_illegal, _ea_illegal };