Fri, 12 Apr 2013 16:26:25 +0100
Don't set PS1 if there is a level-7 interrupt or bus error
PS1 should only be set if the page was originally present (PS1 or PS0 set). If
PS0 and PS1 are clear (page not present) then do NOT set PS1.
Once again the TRM is blatantly and spectacularly wrong...
philpem@40 | 1 | #include <stdio.h> |
philpem@40 | 2 | #include <stdlib.h> |
philpem@40 | 3 | #include <stdint.h> |
philpem@40 | 4 | #include <stdbool.h> |
philpem@59 | 5 | #include <assert.h> |
philpem@40 | 6 | #include "musashi/m68k.h" |
philpem@40 | 7 | #include "state.h" |
philpem@100 | 8 | #include "utils.h" |
philpem@40 | 9 | #include "memory.h" |
philpem@40 | 10 | |
philpem@119 | 11 | // The value which will be returned if the CPU attempts to read from empty memory |
philpem@119 | 12 | // TODO (FIXME?) - need to figure out if R/W ops wrap around. This seems to appease the UNIX kernel and P4TEST. |
philpem@119 | 13 | #define EMPTY 0xFFFFFFFFUL |
philpem@129 | 14 | //#define EMPTY 0x55555555UL |
philpem@129 | 15 | //#define EMPTY 0x00000000UL |
philpem@119 | 16 | |
philpem@40 | 17 | /****************** |
philpem@40 | 18 | * Memory mapping |
philpem@40 | 19 | ******************/ |
philpem@40 | 20 | |
philpem@128 | 21 | /// Set a page bit |
philpem@133 | 22 | #define MAP_SET_PAGEBIT(addr, bit) state.map[(MAP_ADDR_TO_PAGE(addr))*2] |= ((uint8_t)bit << 2) |
philpem@128 | 23 | /// Clear a page bit |
philpem@133 | 24 | #define MAP_CLR_PAGEBIT(addr, bit) state.map[(MAP_ADDR_TO_PAGE(addr))*2] &= ~((uint8_t)bit << 2) |
philpem@40 | 25 | |
philpem@40 | 26 | |
philpem@40 | 27 | /******************************************************** |
philpem@40 | 28 | * m68k memory read/write support functions for Musashi |
philpem@40 | 29 | ********************************************************/ |
philpem@40 | 30 | |
philpem@40 | 31 | /** |
philpem@40 | 32 | * @brief Check memory access permissions for a write operation. |
philpem@40 | 33 | * @note This used to be a single macro (merged with ACCESS_CHECK_RD), but |
philpem@40 | 34 | * gcc throws warnings when you have a return-with-value in a void |
philpem@40 | 35 | * function, even if the return-with-value is completely unreachable. |
philpem@40 | 36 | * Similarly it doesn't like it if you have a return without a value |
philpem@40 | 37 | * in a non-void function, even if it's impossible to ever reach the |
philpem@40 | 38 | * return-with-no-value. UGH! |
philpem@40 | 39 | */ |
philpem@59 | 40 | /*{{{ macro: ACCESS_CHECK_WR(address, bits)*/ |
philpem@59 | 41 | #define ACCESS_CHECK_WR(address, bits) \ |
philpem@59 | 42 | do { \ |
philpem@128 | 43 | if (access_check_cpu(address, bits, true)) { \ |
philpem@40 | 44 | return; \ |
philpem@40 | 45 | } \ |
philpem@70 | 46 | } while (0) |
philpem@59 | 47 | /*}}}*/ |
philpem@40 | 48 | |
philpem@40 | 49 | /** |
philpem@40 | 50 | * @brief Check memory access permissions for a read operation. |
philpem@40 | 51 | * @note This used to be a single macro (merged with ACCESS_CHECK_WR), but |
philpem@40 | 52 | * gcc throws warnings when you have a return-with-value in a void |
philpem@40 | 53 | * function, even if the return-with-value is completely unreachable. |
philpem@40 | 54 | * Similarly it doesn't like it if you have a return without a value |
philpem@40 | 55 | * in a non-void function, even if it's impossible to ever reach the |
philpem@40 | 56 | * return-with-no-value. UGH! |
philpem@40 | 57 | */ |
philpem@59 | 58 | /*{{{ macro: ACCESS_CHECK_RD(address, bits)*/ |
philpem@59 | 59 | #define ACCESS_CHECK_RD(address, bits) \ |
philpem@59 | 60 | do { \ |
philpem@128 | 61 | if (access_check_cpu(address, bits, false)) { \ |
philpem@128 | 62 | if (bits == 32) \ |
philpem@128 | 63 | return EMPTY & 0xFFFFFFFF; \ |
philpem@40 | 64 | else \ |
philpem@128 | 65 | return EMPTY & ((1UL << bits)-1); \ |
philpem@40 | 66 | } \ |
philpem@70 | 67 | } while (0) |
philpem@59 | 68 | /*}}}*/ |
philpem@40 | 69 | |
philpem@128 | 70 | |
philpem@128 | 71 | /** |
philpem@128 | 72 | * Update the page bits for a given memory address |
philpem@128 | 73 | * |
philpem@128 | 74 | * @param addr Memory address being accessed |
philpem@128 | 75 | * @param l7intr Set to <i>true</i> if a level-seven interrupt has been |
philpem@128 | 76 | * signalled (even if <b>ENABLE ERROR</b> isn't set). |
philpem@128 | 77 | * @param write Set to <i>true</i> if the address is being written to. |
philpem@128 | 78 | */ |
philpem@128 | 79 | static void update_page_bits(uint32_t addr, bool l7intr, bool write) |
philpem@112 | 80 | { |
philpem@128 | 81 | bool ps0_state = false; |
philpem@128 | 82 | |
philpem@128 | 83 | // Don't try and update pagebits for non-RAM addresses |
philpem@128 | 84 | if (addr > 0x3FFFFF) |
philpem@128 | 85 | return; |
philpem@128 | 86 | |
philpem@128 | 87 | if (l7intr) { |
philpem@128 | 88 | // if (!(MAP_PAGEBITS(addr) & PAGE_BIT_PS0)) { |
philpem@128 | 89 | // FIXME FUCKUP The ruddy TRM is wrong AGAIN! If above line is uncommented, Really Bad Things Happen. |
philpem@128 | 90 | if ((MAP_PAGEBITS(addr) & PAGE_BIT_PS0)) { |
philpem@144 | 91 | // Level 7 interrupt, PS0 set, PS1 don't-care. Set PS0. |
philpem@128 | 92 | ps0_state = true; |
philpem@128 | 93 | } |
philpem@128 | 94 | } else { |
philpem@128 | 95 | // No L7 interrupt |
philpem@128 | 96 | if ((write && !(MAP_PAGEBITS(addr) & PAGE_BIT_PS1) && (MAP_PAGEBITS(addr) & PAGE_BIT_PS0)) || |
philpem@140 | 97 | (write && (MAP_PAGEBITS(addr) & PAGE_BIT_PS1) && !(MAP_PAGEBITS(addr) & PAGE_BIT_PS0)) || |
philpem@140 | 98 | ( (MAP_PAGEBITS(addr) & PAGE_BIT_PS1) && (MAP_PAGEBITS(addr) & PAGE_BIT_PS0))) /* NOTE -- Once again, this case was missing from the PAL equations in the TRM... */ |
philpem@128 | 99 | { |
philpem@128 | 100 | // No L7 interrupt, PS[1:0] = 0b01, write |
philpem@128 | 101 | // No L7 interrupt, PS[1:0] = 0b10, write |
philpem@128 | 102 | ps0_state = true; |
philpem@128 | 103 | } |
philpem@128 | 104 | } |
philpem@112 | 105 | |
philpem@128 | 106 | #ifdef MAPRAM_BIT_TEST |
philpem@128 | 107 | LOG("Starting Mapram Bit Test"); |
philpem@128 | 108 | state.map[0] = state.map[1] = 0; |
philpem@128 | 109 | LOG("Start = %04X %02X", MAPRAM_ADDR(0), MAP_PAGEBITS(0)); |
philpem@128 | 110 | MAP_SET_PAGEBIT(0, PAGE_BIT_WE); |
philpem@128 | 111 | LOG("Set WE = %04X %02X", MAPRAM_ADDR(0), MAP_PAGEBITS(0)); |
philpem@128 | 112 | MAP_SET_PAGEBIT(0, PAGE_BIT_PS1); |
philpem@128 | 113 | LOG("Set PS1 = %04X %02X", MAPRAM_ADDR(0), MAP_PAGEBITS(0)); |
philpem@128 | 114 | MAP_SET_PAGEBIT(0, PAGE_BIT_PS0); |
philpem@128 | 115 | LOG("Set PS0 = %04X %02X", MAPRAM_ADDR(0), MAP_PAGEBITS(0)); |
philpem@128 | 116 | |
philpem@128 | 117 | MAP_CLR_PAGEBIT(0, PAGE_BIT_WE); |
philpem@128 | 118 | LOG("Clr WE = %04X %02X", MAPRAM_ADDR(0), MAP_PAGEBITS(0)); |
philpem@128 | 119 | MAP_CLR_PAGEBIT(0, PAGE_BIT_PS1); |
philpem@128 | 120 | LOG("Clr PS1 = %04X %02X", MAPRAM_ADDR(0), MAP_PAGEBITS(0)); |
philpem@128 | 121 | MAP_CLR_PAGEBIT(0, PAGE_BIT_PS0); |
philpem@128 | 122 | LOG("Clr PS0 = %04X %02X", MAPRAM_ADDR(0), MAP_PAGEBITS(0)); |
philpem@128 | 123 | exit(-1); |
philpem@128 | 124 | #endif |
philpem@128 | 125 | |
philpem@144 | 126 | if (!l7intr) { |
philpem@144 | 127 | // PS1 is always set on access if no fault |
philpem@144 | 128 | MAP_SET_PAGEBIT(addr, PAGE_BIT_PS1); |
philpem@128 | 129 | |
philpem@144 | 130 | // Update PS0 |
philpem@144 | 131 | if (ps0_state) { |
philpem@144 | 132 | MAP_SET_PAGEBIT(addr, PAGE_BIT_PS0); |
philpem@144 | 133 | } else { |
philpem@144 | 134 | MAP_CLR_PAGEBIT(addr, PAGE_BIT_PS0); |
philpem@144 | 135 | } |
philpem@128 | 136 | } |
philpem@112 | 137 | |
philpem@141 | 138 | #ifdef MAPRAM_DEBUG_MESSAGES |
philpem@128 | 139 | uint16_t new_pagebit2 = MAP_PAGEBITS(addr); |
philpem@128 | 140 | switch (addr) { |
philpem@128 | 141 | case 0x000000: |
philpem@128 | 142 | case 0x001000: |
philpem@128 | 143 | case 0x002000: |
philpem@128 | 144 | case 0x003000: |
philpem@128 | 145 | case 0x004000: |
philpem@128 | 146 | case 0x033000: |
philpem@128 | 147 | case 0x034000: |
philpem@128 | 148 | case 0x035000: |
philpem@141 | 149 | LOG("Addr %08X %s MapNew %04X Page %04X -- Pagebit update -- ps0 %d, old %s%s => %s%s => %s%s", |
philpem@141 | 150 | addr, |
philpem@141 | 151 | write ? "Wr" : "Rd", |
philpem@141 | 152 | MAPRAM_ADDR(addr), |
philpem@141 | 153 | MAP_ADDR_TO_PAGE(addr), |
philpem@141 | 154 | ps0_state, |
philpem@141 | 155 | old_pagebits & PAGE_BIT_PS0 ? "PS0" : "", |
philpem@141 | 156 | old_pagebits & PAGE_BIT_PS1 ? "PS1" : "", |
philpem@141 | 157 | new_pagebit1 & PAGE_BIT_PS0 ? "PS0" : "", |
philpem@141 | 158 | new_pagebit1 & PAGE_BIT_PS1 ? "PS1" : "", |
philpem@141 | 159 | new_pagebit2 & PAGE_BIT_PS0 ? "PS0" : "", |
philpem@141 | 160 | new_pagebit2 & PAGE_BIT_PS1 ? "PS1" : "" |
philpem@141 | 161 | ); |
philpem@128 | 162 | default: |
philpem@112 | 163 | break; |
philpem@112 | 164 | } |
philpem@141 | 165 | #endif // MAPRAM_DEBUG_MESSAGES |
philpem@128 | 166 | } |
philpem@128 | 167 | |
philpem@128 | 168 | bool access_check_dma(void) |
philpem@128 | 169 | { |
philpem@128 | 170 | // TODO FIXME BUGBUG Sanity check - Make sure DMAC is only accessing RAM addresses |
philpem@128 | 171 | |
philpem@128 | 172 | // DMA access check -- make sure the page is mapped in |
philpem@128 | 173 | if (!(MAP_PAGEBITS(state.dma_address) & PAGE_BIT_PS0) && !(MAP_PAGEBITS(state.dma_address) & PAGE_BIT_PS1)) { |
philpem@128 | 174 | // DMA access to page which is not mapped in. |
philpem@128 | 175 | // Level 7 interrupt, page fault, DMA invoked |
philpem@128 | 176 | state.genstat = 0xABFF |
philpem@128 | 177 | | (state.dma_reading ? 0x4000 : 0) |
philpem@128 | 178 | | (state.pie ? 0x0400 : 0); |
philpem@128 | 179 | |
philpem@128 | 180 | // XXX: Check all this stuff. |
philpem@112 | 181 | state.bsr0 = 0x3C00; |
philpem@112 | 182 | state.bsr0 |= (state.dma_address >> 16); |
philpem@112 | 183 | state.bsr1 = state.dma_address & 0xffff; |
philpem@128 | 184 | |
philpem@128 | 185 | // Update page bits for this transfer |
philpem@128 | 186 | update_page_bits(state.dma_address, true, !state.dma_reading); |
philpem@128 | 187 | |
philpem@128 | 188 | // XXX: is this right? |
philpem@128 | 189 | // Fire a Level 7 interrupt |
philpem@128 | 190 | /*if (state.ee)*/ m68k_set_irq(7); |
philpem@128 | 191 | |
philpem@128 | 192 | LOG("BUS ERROR FROM DMA: genstat=%04X, bsr0=%04X, bsr1=%04X\n", state.genstat, state.bsr0, state.bsr1); |
philpem@128 | 193 | return false; |
philpem@128 | 194 | } else { |
philpem@128 | 195 | // No errors. Just update the page bits. |
philpem@128 | 196 | update_page_bits(state.dma_address, false, !state.dma_reading); |
philpem@128 | 197 | return true; |
philpem@112 | 198 | } |
philpem@128 | 199 | } |
philpem@128 | 200 | |
philpem@128 | 201 | /** |
philpem@128 | 202 | * Check memory access permissions for a CPU memory access. |
philpem@128 | 203 | * |
philpem@128 | 204 | * @param addr Virtual memory address being accessed (from CPU address bus). |
philpem@128 | 205 | * @param bits Word size of this transfer (8, 16 or 32 bits). |
philpem@128 | 206 | * @param write <i>true</i> if this is a write operation, <i>false</i> if it is a read operation. |
philpem@128 | 207 | * @return <i>true</i> if the access was denied and a level-7 interrupt and/or bus error raised. |
philpem@128 | 208 | * <i>false</i> if the access was allowed. |
philpem@128 | 209 | */ |
philpem@128 | 210 | bool access_check_cpu(uint32_t addr, int bits, bool write) |
philpem@128 | 211 | { |
philpem@128 | 212 | bool supervisor = (m68k_get_reg(NULL, M68K_REG_SR) & 0x2000); |
philpem@128 | 213 | bool fault = false; |
philpem@128 | 214 | |
philpem@128 | 215 | // TODO FIXME BUGBUG? Do we need to check for supervisor access here? |
philpem@128 | 216 | if ((addr >= 0x000000) && (addr <= 0x3FFFFF) && !(MAP_PAGEBITS(addr) & PAGE_BIT_PS1) && !(MAP_PAGEBITS(addr) & PAGE_BIT_PS0)) { |
philpem@128 | 217 | // (A) Page Fault -- user access to page which is not mapped in |
philpem@128 | 218 | // Level 7 Interrupt, Bus Error, regs=PAGEFAULT |
philpem@128 | 219 | if (write) { |
philpem@128 | 220 | state.genstat = 0x8BFF | (state.pie ? 0x0400 : 0); |
philpem@128 | 221 | } else { |
philpem@128 | 222 | state.genstat = 0xCBFF | (state.pie ? 0x0400 : 0); |
philpem@128 | 223 | } |
philpem@128 | 224 | fault = true; |
philpem@128 | 225 | } else if (!supervisor && (addr >= 0x000000) && (addr <= 0x07FFFF)) { |
philpem@128 | 226 | // (B) User attempted to access the kernel |
philpem@128 | 227 | // Level 7 Interrupt, Bus Error, regs=KERNEL |
philpem@128 | 228 | if (write) { |
philpem@128 | 229 | // XXX: BUGBUG? Is this correct? |
philpem@128 | 230 | state.genstat = 0x9BFF | (state.pie ? 0x0400 : 0); |
philpem@128 | 231 | } else { |
philpem@128 | 232 | state.genstat = 0xDBFF | (state.pie ? 0x0400 : 0); |
philpem@128 | 233 | } |
philpem@128 | 234 | fault = true; |
philpem@128 | 235 | } else if (!supervisor && write && (addr >= 0x000000) && (addr <= 0x3FFFFF) && !(MAP_PAGEBITS(addr) & PAGE_BIT_WE)) { |
philpem@128 | 236 | // (C) User attempted to write to a page which is not write enabled |
philpem@128 | 237 | // Level 7 Interrupt, Bus Error, regs=WRITE_EN |
philpem@128 | 238 | if (write) { |
philpem@128 | 239 | // XXX: BUGBUG? Is this correct? |
philpem@128 | 240 | state.genstat = 0x9BFF | (state.pie ? 0x0400 : 0); |
philpem@128 | 241 | } else { |
philpem@128 | 242 | state.genstat = 0xDBFF | (state.pie ? 0x0400 : 0); |
philpem@128 | 243 | } |
philpem@128 | 244 | fault = true; |
philpem@128 | 245 | } else if (!supervisor && (addr >= 0x400000) && (addr <= 0xFFFFFF)) { |
philpem@128 | 246 | // (D) UIE - user I/O exception |
philpem@128 | 247 | // Bus Error only, regs=UIE |
philpem@128 | 248 | if (write) { |
philpem@128 | 249 | state.genstat = 0x9AFF | (state.pie ? 0x0400 : 0); |
philpem@128 | 250 | } else { |
philpem@128 | 251 | state.genstat = 0xDAFF | (state.pie ? 0x0400 : 0); |
philpem@128 | 252 | } |
philpem@128 | 253 | fault = true; |
philpem@128 | 254 | } |
philpem@128 | 255 | |
philpem@144 | 256 | unsigned char pagebits_preup = MAP_PAGEBITS(addr & 0x3fffff); |
philpem@144 | 257 | |
philpem@128 | 258 | // Update the page bits first |
philpem@128 | 259 | update_page_bits(addr, fault, write); |
philpem@128 | 260 | |
philpem@128 | 261 | if (fault) { |
philpem@128 | 262 | if (bits >= 16) |
philpem@128 | 263 | state.bsr0 = 0x7C00; |
philpem@128 | 264 | else |
philpem@128 | 265 | state.bsr0 = (addr & 1) ? 0x7E00 : 0x7D00; |
philpem@128 | 266 | // FIXME? Physical or virtual address here? |
philpem@128 | 267 | state.bsr0 |= (addr >> 16); |
philpem@128 | 268 | state.bsr1 = addr & 0xffff; |
philpem@128 | 269 | |
philpem@144 | 270 | LOG("CPU Bus Error or L7Intr while %s, vaddr %08X, map %08X, pagebits 0x%02X=>0x%02X bsr0=%04X bsr1=%04X genstat=%04X", |
philpem@128 | 271 | write ? "writing" : "reading", addr, |
philpem@128 | 272 | MAPRAM_ADDR(addr & 0x3fffff), |
philpem@144 | 273 | pagebits_preup, |
philpem@128 | 274 | MAP_PAGEBITS(addr & 0x3fffff), |
philpem@128 | 275 | state.bsr0, state.bsr1, state.genstat); |
philpem@128 | 276 | |
philpem@128 | 277 | // FIXME? BUGBUG? Does EE disable one or both of these? |
philpem@128 | 278 | // /*if (state.ee)*/ m68k_set_irq(7); |
philpem@128 | 279 | /*if (state.ee)*/ m68k_pulse_bus_error(); |
philpem@128 | 280 | } |
philpem@128 | 281 | |
philpem@128 | 282 | return fault; |
philpem@112 | 283 | } |
philpem@112 | 284 | |
philpem@40 | 285 | // Logging macros |
philpem@59 | 286 | #define LOG_NOT_HANDLED_R(bits) \ |
philpem@128 | 287 | if (!handled) fprintf(stderr, "unhandled read%02d, addr=0x%08X\n", bits, address); |
philpem@40 | 288 | |
philpem@59 | 289 | #define LOG_NOT_HANDLED_W(bits) \ |
philpem@128 | 290 | if (!handled) fprintf(stderr, "unhandled write%02d, addr=0x%08X, data=0x%08X\n", bits, address, data); |
philpem@59 | 291 | |
philpem@59 | 292 | /******************************************************** |
philpem@59 | 293 | * I/O read/write functions |
philpem@59 | 294 | ********************************************************/ |
philpem@40 | 295 | |
philpem@40 | 296 | /** |
philpem@59 | 297 | * Issue a warning if a read operation is made with an invalid size |
philpem@40 | 298 | */ |
philpem@66 | 299 | inline static void ENFORCE_SIZE(int bits, uint32_t address, bool read, int allowed, char *regname) |
philpem@40 | 300 | { |
philpem@59 | 301 | assert((bits == 8) || (bits == 16) || (bits == 32)); |
philpem@59 | 302 | if ((bits & allowed) == 0) { |
philpem@128 | 303 | LOG("WARNING: %s 0x%08X (%s) with invalid size %d!\n", read ? "read from" : "write to", address, regname, bits); |
philpem@59 | 304 | } |
philpem@59 | 305 | } |
philpem@59 | 306 | |
philpem@66 | 307 | inline static void ENFORCE_SIZE_R(int bits, uint32_t address, int allowed, char *regname) |
philpem@40 | 308 | { |
philpem@66 | 309 | ENFORCE_SIZE(bits, address, true, allowed, regname); |
philpem@66 | 310 | } |
philpem@66 | 311 | |
philpem@66 | 312 | inline static void ENFORCE_SIZE_W(int bits, uint32_t address, int allowed, char *regname) |
philpem@66 | 313 | { |
philpem@66 | 314 | ENFORCE_SIZE(bits, address, false, allowed, regname); |
philpem@66 | 315 | } |
philpem@66 | 316 | |
philpem@59 | 317 | void IoWrite(uint32_t address, uint32_t data, int bits)/*{{{*/ |
philpem@59 | 318 | { |
philpem@40 | 319 | bool handled = false; |
philpem@40 | 320 | |
philpem@59 | 321 | if ((address >= 0x400000) && (address <= 0x7FFFFF)) { |
philpem@40 | 322 | // I/O register space, zone A |
philpem@40 | 323 | switch (address & 0x0F0000) { |
philpem@40 | 324 | case 0x010000: // General Status Register |
philpem@59 | 325 | if (bits == 16) |
philpem@59 | 326 | state.genstat = (data & 0xffff); |
philpem@59 | 327 | else if (bits == 8) { |
philpem@59 | 328 | if (address & 0) |
philpem@59 | 329 | state.genstat = data; |
philpem@59 | 330 | else |
philpem@59 | 331 | state.genstat = data << 8; |
philpem@59 | 332 | } |
philpem@40 | 333 | handled = true; |
philpem@40 | 334 | break; |
philpem@40 | 335 | case 0x030000: // Bus Status Register 0 |
philpem@40 | 336 | break; |
philpem@40 | 337 | case 0x040000: // Bus Status Register 1 |
philpem@40 | 338 | break; |
philpem@40 | 339 | case 0x050000: // Phone status |
philpem@40 | 340 | break; |
philpem@40 | 341 | case 0x060000: // DMA Count |
philpem@66 | 342 | ENFORCE_SIZE_W(bits, address, 16, "DMACOUNT"); |
philpem@59 | 343 | state.dma_count = (data & 0x3FFF); |
philpem@59 | 344 | state.idmarw = ((data & 0x4000) == 0x4000); |
philpem@59 | 345 | state.dmaen = ((data & 0x8000) == 0x8000); |
philpem@59 | 346 | // This handles the "dummy DMA transfer" mentioned in the docs |
philpem@112 | 347 | // disabled because it causes the floppy test to fail |
philpem@112 | 348 | #if 0 |
philpem@112 | 349 | if (!state.idmarw){ |
philpem@112 | 350 | if (access_check_dma(true)){ |
philpem@112 | 351 | uint32_t newAddr = mapAddr(state.dma_address, true); |
philpem@112 | 352 | // RAM access |
philpem@112 | 353 | if (newAddr <= 0x1fffff) |
philpem@112 | 354 | WR16(state.base_ram, newAddr, state.base_ram_size - 1, 0xFF); |
philpem@112 | 355 | else if (address <= 0x3FFFFF) |
philpem@112 | 356 | WR16(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1, 0xFF); |
philpem@112 | 357 | } |
philpem@112 | 358 | } |
philpem@112 | 359 | #endif |
philpem@59 | 360 | state.dma_count++; |
philpem@53 | 361 | handled = true; |
philpem@40 | 362 | break; |
philpem@40 | 363 | case 0x070000: // Line Printer Status Register |
philpem@40 | 364 | break; |
philpem@40 | 365 | case 0x080000: // Real Time Clock |
philpem@128 | 366 | LOGS("REAL TIME CLOCK WRITE"); |
philpem@40 | 367 | break; |
philpem@40 | 368 | case 0x090000: // Phone registers |
philpem@40 | 369 | switch (address & 0x0FF000) { |
philpem@40 | 370 | case 0x090000: // Handset relay |
philpem@40 | 371 | case 0x098000: |
philpem@40 | 372 | break; |
philpem@40 | 373 | case 0x091000: // Line select 2 |
philpem@40 | 374 | case 0x099000: |
philpem@40 | 375 | break; |
philpem@40 | 376 | case 0x092000: // Hook relay 1 |
philpem@40 | 377 | case 0x09A000: |
philpem@40 | 378 | break; |
philpem@40 | 379 | case 0x093000: // Hook relay 2 |
philpem@40 | 380 | case 0x09B000: |
philpem@40 | 381 | break; |
philpem@40 | 382 | case 0x094000: // Line 1 hold |
philpem@40 | 383 | case 0x09C000: |
philpem@40 | 384 | break; |
philpem@40 | 385 | case 0x095000: // Line 2 hold |
philpem@40 | 386 | case 0x09D000: |
philpem@40 | 387 | break; |
philpem@40 | 388 | case 0x096000: // Line 1 A-lead |
philpem@40 | 389 | case 0x09E000: |
philpem@40 | 390 | break; |
philpem@40 | 391 | case 0x097000: // Line 2 A-lead |
philpem@40 | 392 | case 0x09F000: |
philpem@40 | 393 | break; |
philpem@40 | 394 | } |
philpem@40 | 395 | break; |
philpem@59 | 396 | case 0x0A0000: // Miscellaneous Control Register |
philpem@66 | 397 | ENFORCE_SIZE_W(bits, address, 16, "MISCCON"); |
philpem@59 | 398 | // TODO: handle the ctrl bits properly |
philpem@97 | 399 | if (data & 0x8000){ |
philpem@97 | 400 | state.timer_enabled = 1; |
philpem@97 | 401 | }else{ |
philpem@97 | 402 | state.timer_enabled = 0; |
philpem@97 | 403 | state.timer_asserted = 0; |
philpem@97 | 404 | } |
philpem@59 | 405 | state.dma_reading = (data & 0x4000); |
philpem@72 | 406 | if (state.leds != ((~data & 0xF00) >> 8)) { |
philpem@72 | 407 | state.leds = (~data & 0xF00) >> 8; |
philpem@117 | 408 | #ifdef SHOW_LEDS |
philpem@72 | 409 | printf("LEDs: %s %s %s %s\n", |
philpem@72 | 410 | (state.leds & 8) ? "R" : "-", |
philpem@72 | 411 | (state.leds & 4) ? "G" : "-", |
philpem@72 | 412 | (state.leds & 2) ? "Y" : "-", |
philpem@72 | 413 | (state.leds & 1) ? "R" : "-"); |
philpem@117 | 414 | #endif |
philpem@72 | 415 | } |
philpem@46 | 416 | handled = true; |
philpem@40 | 417 | break; |
philpem@40 | 418 | case 0x0B0000: // TM/DIALWR |
philpem@40 | 419 | break; |
philpem@59 | 420 | case 0x0C0000: // Clear Status Register |
philpem@59 | 421 | state.genstat = 0xFFFF; |
philpem@59 | 422 | state.bsr0 = 0xFFFF; |
philpem@59 | 423 | state.bsr1 = 0xFFFF; |
philpem@43 | 424 | handled = true; |
philpem@40 | 425 | break; |
philpem@40 | 426 | case 0x0D0000: // DMA Address Register |
philpem@59 | 427 | if (address & 0x004000) { |
philpem@59 | 428 | // A14 high -- set most significant bits |
philpem@59 | 429 | state.dma_address = (state.dma_address & 0x1fe) | ((address & 0x3ffe) << 8); |
philpem@59 | 430 | } else { |
philpem@59 | 431 | // A14 low -- set least significant bits |
philpem@59 | 432 | state.dma_address = (state.dma_address & 0x3ffe00) | (address & 0x1fe); |
philpem@59 | 433 | } |
philpem@59 | 434 | handled = true; |
philpem@40 | 435 | break; |
philpem@40 | 436 | case 0x0E0000: // Disk Control Register |
philpem@112 | 437 | { |
philpem@112 | 438 | bool fd_selected; |
philpem@112 | 439 | bool hd_selected; |
philpem@112 | 440 | ENFORCE_SIZE_W(bits, address, 16, "DISKCON"); |
philpem@112 | 441 | // B7 = FDD controller reset |
philpem@112 | 442 | if ((data & 0x80) == 0) wd2797_reset(&state.fdc_ctx); |
philpem@112 | 443 | // B6 = drive 0 select |
philpem@112 | 444 | fd_selected = (data & 0x40) != 0; |
philpem@112 | 445 | // B5 = motor enable -- TODO |
philpem@112 | 446 | // B4 = HDD controller reset |
philpem@112 | 447 | if ((data & 0x10) == 0) wd2010_reset(&state.hdc_ctx); |
philpem@112 | 448 | // B3 = HDD0 select |
philpem@112 | 449 | hd_selected = (data & 0x08) != 0; |
philpem@112 | 450 | // B2,1,0 = HDD0 head select -- TODO? |
philpem@112 | 451 | if (hd_selected && !state.hd_selected){ |
philpem@112 | 452 | state.fd_selected = false; |
philpem@112 | 453 | state.hd_selected = true; |
philpem@112 | 454 | }else if (fd_selected && !state.fd_selected){ |
philpem@112 | 455 | state.hd_selected = false; |
philpem@112 | 456 | state.fd_selected = true; |
philpem@112 | 457 | } |
philpem@112 | 458 | handled = true; |
philpem@112 | 459 | break; |
philpem@112 | 460 | } |
philpem@40 | 461 | case 0x0F0000: // Line Printer Data Register |
philpem@40 | 462 | break; |
philpem@40 | 463 | } |
philpem@40 | 464 | } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) { |
philpem@40 | 465 | // I/O register space, zone B |
philpem@40 | 466 | switch (address & 0xF00000) { |
philpem@40 | 467 | case 0xC00000: // Expansion slots |
philpem@40 | 468 | case 0xD00000: |
philpem@40 | 469 | switch (address & 0xFC0000) { |
philpem@40 | 470 | case 0xC00000: // Expansion slot 0 |
philpem@40 | 471 | case 0xC40000: // Expansion slot 1 |
philpem@40 | 472 | case 0xC80000: // Expansion slot 2 |
philpem@40 | 473 | case 0xCC0000: // Expansion slot 3 |
philpem@40 | 474 | case 0xD00000: // Expansion slot 4 |
philpem@40 | 475 | case 0xD40000: // Expansion slot 5 |
philpem@40 | 476 | case 0xD80000: // Expansion slot 6 |
philpem@40 | 477 | case 0xDC0000: // Expansion slot 7 |
philpem@59 | 478 | fprintf(stderr, "NOTE: WR%d to expansion card space, addr=0x%08X, data=0x%08X\n", bits, address, data); |
philpem@59 | 479 | handled = true; |
philpem@40 | 480 | break; |
philpem@40 | 481 | } |
philpem@40 | 482 | break; |
philpem@40 | 483 | case 0xE00000: // HDC, FDC, MCR2 and RTC data bits |
philpem@40 | 484 | case 0xF00000: |
philpem@40 | 485 | switch (address & 0x070000) { |
philpem@112 | 486 | case 0x000000: // [ef][08]xxxx ==> WD2010 hard disc controller |
philpem@112 | 487 | wd2010_write_reg(&state.hdc_ctx, (address >> 1) & 7, data); |
philpem@112 | 488 | handled = true; |
philpem@40 | 489 | break; |
philpem@40 | 490 | case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller |
philpem@112 | 491 | /*ENFORCE_SIZE_W(bits, address, 16, "FDC REGISTERS");*/ |
philpem@59 | 492 | wd2797_write_reg(&state.fdc_ctx, (address >> 1) & 3, data); |
philpem@52 | 493 | handled = true; |
philpem@40 | 494 | break; |
philpem@40 | 495 | case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2 |
philpem@116 | 496 | // MCR2 - UNIX PC Rev. P5.1 HDD head select b3 and potential HDD#2 select |
philpem@116 | 497 | wd2010_write_reg(&state.hdc_ctx, UNIXPC_REG_MCR2, data); |
philpem@116 | 498 | handled = true; |
philpem@40 | 499 | break; |
philpem@40 | 500 | case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits |
philpem@128 | 501 | LOGS("REAL TIME CLOCK DATA WRITE"); |
philpem@40 | 502 | break; |
philpem@40 | 503 | case 0x040000: // [ef][4c]xxxx ==> General Control Register |
philpem@40 | 504 | switch (address & 0x077000) { |
philpem@40 | 505 | case 0x040000: // [ef][4c][08]xxx ==> EE |
philpem@102 | 506 | // Error Enable. If =0, Level7 intrs and bus errors are masked. |
philpem@102 | 507 | ENFORCE_SIZE_W(bits, address, 16, "EE"); |
philpem@102 | 508 | state.ee = ((data & 0x8000) == 0x8000); |
philpem@102 | 509 | handled = true; |
philpem@59 | 510 | break; |
philpem@44 | 511 | case 0x041000: // [ef][4c][19]xxx ==> PIE |
philpem@66 | 512 | ENFORCE_SIZE_W(bits, address, 16, "PIE"); |
philpem@59 | 513 | state.pie = ((data & 0x8000) == 0x8000); |
philpem@59 | 514 | handled = true; |
philpem@59 | 515 | break; |
philpem@40 | 516 | case 0x042000: // [ef][4c][2A]xxx ==> BP |
philpem@59 | 517 | break; |
philpem@40 | 518 | case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP |
philpem@66 | 519 | ENFORCE_SIZE_W(bits, address, 16, "ROMLMAP"); |
philpem@59 | 520 | state.romlmap = ((data & 0x8000) == 0x8000); |
philpem@44 | 521 | handled = true; |
philpem@40 | 522 | break; |
philpem@59 | 523 | case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM |
philpem@66 | 524 | ENFORCE_SIZE_W(bits, address, 16, "L1 MODEM"); |
philpem@59 | 525 | break; |
philpem@59 | 526 | case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM |
philpem@66 | 527 | ENFORCE_SIZE_W(bits, address, 16, "L2 MODEM"); |
philpem@59 | 528 | break; |
philpem@59 | 529 | case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT |
philpem@66 | 530 | ENFORCE_SIZE_W(bits, address, 16, "D/N CONNECT"); |
philpem@59 | 531 | break; |
philpem@59 | 532 | case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video |
philpem@66 | 533 | ENFORCE_SIZE_W(bits, address, 16, "WHOLE SCREEN REVERSE VIDEO"); |
philpem@40 | 534 | break; |
philpem@40 | 535 | } |
philpem@40 | 536 | case 0x050000: // [ef][5d]xxxx ==> 8274 |
philpem@40 | 537 | break; |
philpem@40 | 538 | case 0x060000: // [ef][6e]xxxx ==> Control regs |
philpem@40 | 539 | switch (address & 0x07F000) { |
philpem@40 | 540 | default: |
philpem@40 | 541 | break; |
philpem@40 | 542 | } |
philpem@40 | 543 | break; |
philpem@40 | 544 | case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller |
philpem@84 | 545 | // TODO: figure out which sizes are valid (probably just 8 and 16) |
philpem@84 | 546 | // ENFORCE_SIZE_W(bits, address, 16, "KEYBOARD CONTROLLER"); |
philpem@93 | 547 | if (bits == 8) { |
philpem@128 | 548 | #ifdef LOG_KEYBOARD_WRITES |
philpem@128 | 549 | LOG("KBD WR %02X => %02X\n", (address >> 1) & 3, data); |
philpem@128 | 550 | #endif |
philpem@93 | 551 | keyboard_write(&state.kbd, (address >> 1) & 3, data); |
philpem@93 | 552 | handled = true; |
philpem@93 | 553 | } else if (bits == 16) { |
philpem@128 | 554 | #ifdef LOG_KEYBOARD_WRITES |
philpem@128 | 555 | LOG("KBD WR %02X => %04X\n", (address >> 1) & 3, data); |
philpem@128 | 556 | #endif |
philpem@93 | 557 | keyboard_write(&state.kbd, (address >> 1) & 3, data >> 8); |
philpem@93 | 558 | handled = true; |
philpem@93 | 559 | } |
philpem@40 | 560 | break; |
philpem@40 | 561 | } |
philpem@40 | 562 | } |
philpem@40 | 563 | } |
philpem@40 | 564 | |
philpem@64 | 565 | LOG_NOT_HANDLED_W(bits); |
philpem@59 | 566 | }/*}}}*/ |
philpem@40 | 567 | |
philpem@59 | 568 | uint32_t IoRead(uint32_t address, int bits)/*{{{*/ |
philpem@59 | 569 | { |
philpem@59 | 570 | bool handled = false; |
philpem@119 | 571 | uint32_t data = EMPTY & 0xFFFFFFFF; |
philpem@40 | 572 | |
philpem@59 | 573 | if ((address >= 0x400000) && (address <= 0x7FFFFF)) { |
philpem@40 | 574 | // I/O register space, zone A |
philpem@40 | 575 | switch (address & 0x0F0000) { |
philpem@40 | 576 | case 0x010000: // General Status Register |
philpem@116 | 577 | /* ENFORCE_SIZE_R(bits, address, 16, "GENSTAT"); */ |
philpem@116 | 578 | if (bits == 32) { |
philpem@116 | 579 | return ((uint32_t)state.genstat << 16) + (uint32_t)state.genstat; |
philpem@116 | 580 | } else if (bits == 16) { |
philpem@116 | 581 | return (uint16_t)state.genstat; |
philpem@116 | 582 | } else { |
philpem@116 | 583 | return (uint8_t)(state.genstat & 0xff); |
philpem@116 | 584 | } |
philpem@40 | 585 | break; |
philpem@40 | 586 | case 0x030000: // Bus Status Register 0 |
philpem@66 | 587 | ENFORCE_SIZE_R(bits, address, 16, "BSR0"); |
philpem@59 | 588 | return ((uint32_t)state.bsr0 << 16) + (uint32_t)state.bsr0; |
philpem@40 | 589 | break; |
philpem@40 | 590 | case 0x040000: // Bus Status Register 1 |
philpem@66 | 591 | ENFORCE_SIZE_R(bits, address, 16, "BSR1"); |
philpem@59 | 592 | return ((uint32_t)state.bsr1 << 16) + (uint32_t)state.bsr1; |
philpem@40 | 593 | break; |
philpem@40 | 594 | case 0x050000: // Phone status |
philpem@66 | 595 | ENFORCE_SIZE_R(bits, address, 8 | 16, "PHONE STATUS"); |
philpem@40 | 596 | break; |
philpem@40 | 597 | case 0x060000: // DMA Count |
philpem@55 | 598 | // TODO: U/OERR- is always inactive (bit set)... or should it be = DMAEN+? |
philpem@55 | 599 | // Bit 14 is always unused, so leave it set |
philpem@66 | 600 | ENFORCE_SIZE_R(bits, address, 16, "DMACOUNT"); |
philpem@59 | 601 | return (state.dma_count & 0x3fff) | 0xC000; |
philpem@40 | 602 | break; |
philpem@40 | 603 | case 0x070000: // Line Printer Status Register |
philpem@53 | 604 | data = 0x00120012; // no parity error, no line printer error, no irqs from FDD or HDD |
philpem@78 | 605 | data |= wd2797_get_irq(&state.fdc_ctx) ? 0x00080008 : 0; |
philpem@112 | 606 | data |= wd2010_get_irq(&state.hdc_ctx) ? 0x00040004 : 0; |
philpem@59 | 607 | return data; |
philpem@40 | 608 | break; |
philpem@40 | 609 | case 0x080000: // Real Time Clock |
philpem@128 | 610 | LOGS("REAL TIME CLOCK READ"); |
philpem@40 | 611 | break; |
philpem@40 | 612 | case 0x090000: // Phone registers |
philpem@40 | 613 | switch (address & 0x0FF000) { |
philpem@40 | 614 | case 0x090000: // Handset relay |
philpem@40 | 615 | case 0x098000: |
philpem@40 | 616 | break; |
philpem@40 | 617 | case 0x091000: // Line select 2 |
philpem@40 | 618 | case 0x099000: |
philpem@40 | 619 | break; |
philpem@40 | 620 | case 0x092000: // Hook relay 1 |
philpem@40 | 621 | case 0x09A000: |
philpem@40 | 622 | break; |
philpem@40 | 623 | case 0x093000: // Hook relay 2 |
philpem@40 | 624 | case 0x09B000: |
philpem@40 | 625 | break; |
philpem@40 | 626 | case 0x094000: // Line 1 hold |
philpem@40 | 627 | case 0x09C000: |
philpem@40 | 628 | break; |
philpem@40 | 629 | case 0x095000: // Line 2 hold |
philpem@40 | 630 | case 0x09D000: |
philpem@40 | 631 | break; |
philpem@40 | 632 | case 0x096000: // Line 1 A-lead |
philpem@40 | 633 | case 0x09E000: |
philpem@40 | 634 | break; |
philpem@40 | 635 | case 0x097000: // Line 2 A-lead |
philpem@40 | 636 | case 0x09F000: |
philpem@40 | 637 | break; |
philpem@40 | 638 | } |
philpem@40 | 639 | break; |
philpem@46 | 640 | case 0x0A0000: // Miscellaneous Control Register -- write only! |
philpem@46 | 641 | handled = true; |
philpem@40 | 642 | break; |
philpem@40 | 643 | case 0x0B0000: // TM/DIALWR |
philpem@40 | 644 | break; |
philpem@46 | 645 | case 0x0C0000: // Clear Status Register -- write only! |
philpem@43 | 646 | handled = true; |
philpem@40 | 647 | break; |
philpem@40 | 648 | case 0x0D0000: // DMA Address Register |
philpem@40 | 649 | break; |
philpem@40 | 650 | case 0x0E0000: // Disk Control Register |
philpem@40 | 651 | break; |
philpem@40 | 652 | case 0x0F0000: // Line Printer Data Register |
philpem@40 | 653 | break; |
philpem@40 | 654 | } |
philpem@40 | 655 | } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) { |
philpem@40 | 656 | // I/O register space, zone B |
philpem@40 | 657 | switch (address & 0xF00000) { |
philpem@40 | 658 | case 0xC00000: // Expansion slots |
philpem@40 | 659 | case 0xD00000: |
philpem@40 | 660 | switch (address & 0xFC0000) { |
philpem@40 | 661 | case 0xC00000: // Expansion slot 0 |
philpem@40 | 662 | case 0xC40000: // Expansion slot 1 |
philpem@40 | 663 | case 0xC80000: // Expansion slot 2 |
philpem@40 | 664 | case 0xCC0000: // Expansion slot 3 |
philpem@40 | 665 | case 0xD00000: // Expansion slot 4 |
philpem@40 | 666 | case 0xD40000: // Expansion slot 5 |
philpem@40 | 667 | case 0xD80000: // Expansion slot 6 |
philpem@40 | 668 | case 0xDC0000: // Expansion slot 7 |
philpem@65 | 669 | fprintf(stderr, "NOTE: RD%d from expansion card space, addr=0x%08X\n", bits, address); |
philpem@65 | 670 | handled = true; |
philpem@40 | 671 | break; |
philpem@40 | 672 | } |
philpem@40 | 673 | break; |
philpem@40 | 674 | case 0xE00000: // HDC, FDC, MCR2 and RTC data bits |
philpem@40 | 675 | case 0xF00000: |
philpem@40 | 676 | switch (address & 0x070000) { |
philpem@40 | 677 | case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller |
philpem@112 | 678 | return (wd2010_read_reg(&state.hdc_ctx, (address >> 1) & 7)); |
philpem@112 | 679 | |
philpem@40 | 680 | break; |
philpem@40 | 681 | case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller |
philpem@112 | 682 | /*ENFORCE_SIZE_R(bits, address, 16, "FDC REGISTERS");*/ |
philpem@59 | 683 | return wd2797_read_reg(&state.fdc_ctx, (address >> 1) & 3); |
philpem@40 | 684 | break; |
philpem@40 | 685 | case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2 |
philpem@40 | 686 | break; |
philpem@40 | 687 | case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits |
philpem@128 | 688 | LOGS("REAL TIME CLOCK DATA READ"); |
philpem@40 | 689 | break; |
philpem@40 | 690 | case 0x040000: // [ef][4c]xxxx ==> General Control Register |
philpem@40 | 691 | switch (address & 0x077000) { |
philpem@40 | 692 | case 0x040000: // [ef][4c][08]xxx ==> EE |
philpem@44 | 693 | case 0x041000: // [ef][4c][19]xxx ==> PIE |
philpem@40 | 694 | case 0x042000: // [ef][4c][2A]xxx ==> BP |
philpem@40 | 695 | case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP |
philpem@40 | 696 | case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM |
philpem@40 | 697 | case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM |
philpem@40 | 698 | case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT |
philpem@44 | 699 | // All write-only registers... TODO: bus error? |
philpem@44 | 700 | handled = true; |
philpem@40 | 701 | break; |
philpem@44 | 702 | case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video [FIXME: not in TRM] |
philpem@40 | 703 | break; |
philpem@40 | 704 | } |
philpem@40 | 705 | break; |
philpem@40 | 706 | case 0x050000: // [ef][5d]xxxx ==> 8274 |
philpem@40 | 707 | break; |
philpem@40 | 708 | case 0x060000: // [ef][6e]xxxx ==> Control regs |
philpem@40 | 709 | switch (address & 0x07F000) { |
philpem@40 | 710 | default: |
philpem@40 | 711 | break; |
philpem@40 | 712 | } |
philpem@40 | 713 | break; |
philpem@40 | 714 | case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller |
philpem@84 | 715 | // TODO: figure out which sizes are valid (probably just 8 and 16) |
philpem@84 | 716 | //ENFORCE_SIZE_R(bits, address, 16, "KEYBOARD CONTROLLER"); |
philpem@84 | 717 | { |
philpem@93 | 718 | if (bits == 8) { |
philpem@93 | 719 | return keyboard_read(&state.kbd, (address >> 1) & 3); |
philpem@93 | 720 | } else { |
philpem@93 | 721 | return keyboard_read(&state.kbd, (address >> 1) & 3) << 8; |
philpem@93 | 722 | } |
philpem@84 | 723 | return data; |
philpem@84 | 724 | } |
philpem@40 | 725 | break; |
philpem@40 | 726 | } |
philpem@40 | 727 | } |
philpem@40 | 728 | } |
philpem@40 | 729 | |
philpem@64 | 730 | LOG_NOT_HANDLED_R(bits); |
philpem@64 | 731 | |
philpem@59 | 732 | return data; |
philpem@59 | 733 | }/*}}}*/ |
philpem@40 | 734 | |
philpem@59 | 735 | |
philpem@59 | 736 | /******************************************************** |
philpem@59 | 737 | * m68k memory read/write support functions for Musashi |
philpem@59 | 738 | ********************************************************/ |
philpem@59 | 739 | |
philpem@59 | 740 | /** |
philpem@59 | 741 | * @brief Read M68K memory, 32-bit |
philpem@59 | 742 | */ |
philpem@59 | 743 | uint32_t m68k_read_memory_32(uint32_t address)/*{{{*/ |
philpem@59 | 744 | { |
philpem@119 | 745 | uint32_t data = EMPTY & 0xFFFFFFFF; |
philpem@59 | 746 | |
philpem@59 | 747 | // If ROMLMAP is set, force system to access ROM |
philpem@59 | 748 | if (!state.romlmap) |
philpem@59 | 749 | address |= 0x800000; |
philpem@59 | 750 | |
philpem@59 | 751 | // Check access permissions |
philpem@59 | 752 | ACCESS_CHECK_RD(address, 32); |
philpem@59 | 753 | |
philpem@59 | 754 | if ((address >= 0x800000) && (address <= 0xBFFFFF)) { |
philpem@59 | 755 | // ROM access |
philpem@60 | 756 | return RD32(state.rom, address, ROM_SIZE - 1); |
philpem@60 | 757 | } else if (address <= 0x3fffff) { |
philpem@59 | 758 | // RAM access |
philpem@128 | 759 | uint32_t newAddr = MAP_ADDR(address); |
philpem@128 | 760 | |
philpem@63 | 761 | if (newAddr <= 0x1fffff) { |
philpem@129 | 762 | // Base memory wraps around |
philpem@129 | 763 | return RD32(state.base_ram, newAddr, state.base_ram_size - 1); |
philpem@63 | 764 | } else { |
philpem@119 | 765 | if ((newAddr <= (state.exp_ram_size + 0x200000 - 1)) && (newAddr >= 0x200000)) |
philpem@63 | 766 | return RD32(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1); |
philpem@63 | 767 | else |
philpem@119 | 768 | return EMPTY & 0xffffffff; |
philpem@63 | 769 | } |
philpem@59 | 770 | } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) { |
philpem@59 | 771 | // I/O register space, zone A |
philpem@59 | 772 | switch (address & 0x0F0000) { |
philpem@59 | 773 | case 0x000000: // Map RAM access |
philpem@59 | 774 | if (address > 0x4007FF) fprintf(stderr, "NOTE: RD32 from MapRAM mirror, addr=0x%08X\n", address); |
philpem@60 | 775 | return RD32(state.map, address, 0x7FF); |
philpem@59 | 776 | break; |
philpem@59 | 777 | case 0x020000: // Video RAM |
philpem@59 | 778 | if (address > 0x427FFF) fprintf(stderr, "NOTE: RD32 from VideoRAM mirror, addr=0x%08X\n", address); |
philpem@60 | 779 | return RD32(state.vram, address, 0x7FFF); |
philpem@59 | 780 | break; |
philpem@59 | 781 | default: |
philpem@60 | 782 | return IoRead(address, 32); |
philpem@59 | 783 | } |
philpem@59 | 784 | } else { |
philpem@60 | 785 | return IoRead(address, 32); |
philpem@59 | 786 | } |
philpem@59 | 787 | |
philpem@40 | 788 | return data; |
philpem@59 | 789 | }/*}}}*/ |
philpem@40 | 790 | |
philpem@40 | 791 | /** |
philpem@40 | 792 | * @brief Read M68K memory, 16-bit |
philpem@40 | 793 | */ |
philpem@59 | 794 | uint32_t m68k_read_memory_16(uint32_t address)/*{{{*/ |
philpem@40 | 795 | { |
philpem@119 | 796 | uint16_t data = EMPTY & 0xFFFF; |
philpem@40 | 797 | |
philpem@40 | 798 | // If ROMLMAP is set, force system to access ROM |
philpem@40 | 799 | if (!state.romlmap) |
philpem@40 | 800 | address |= 0x800000; |
philpem@40 | 801 | |
philpem@40 | 802 | // Check access permissions |
philpem@40 | 803 | ACCESS_CHECK_RD(address, 16); |
philpem@40 | 804 | |
philpem@40 | 805 | if ((address >= 0x800000) && (address <= 0xBFFFFF)) { |
philpem@40 | 806 | // ROM access |
philpem@40 | 807 | data = RD16(state.rom, address, ROM_SIZE - 1); |
philpem@60 | 808 | } else if (address <= 0x3fffff) { |
philpem@40 | 809 | // RAM access |
philpem@128 | 810 | uint32_t newAddr = MAP_ADDR(address); |
philpem@128 | 811 | |
philpem@63 | 812 | if (newAddr <= 0x1fffff) { |
philpem@129 | 813 | // Base memory wraps around |
philpem@129 | 814 | return RD16(state.base_ram, newAddr, state.base_ram_size - 1); |
philpem@63 | 815 | } else { |
philpem@119 | 816 | if ((newAddr <= (state.exp_ram_size + 0x200000 - 1)) && (newAddr >= 0x200000)) |
philpem@63 | 817 | return RD16(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1); |
philpem@63 | 818 | else |
philpem@119 | 819 | return EMPTY & 0xffff; |
philpem@63 | 820 | } |
philpem@40 | 821 | } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) { |
philpem@40 | 822 | // I/O register space, zone A |
philpem@40 | 823 | switch (address & 0x0F0000) { |
philpem@40 | 824 | case 0x000000: // Map RAM access |
philpem@40 | 825 | if (address > 0x4007FF) fprintf(stderr, "NOTE: RD16 from MapRAM mirror, addr=0x%08X\n", address); |
philpem@40 | 826 | data = RD16(state.map, address, 0x7FF); |
philpem@40 | 827 | break; |
philpem@40 | 828 | case 0x020000: // Video RAM |
philpem@40 | 829 | if (address > 0x427FFF) fprintf(stderr, "NOTE: RD16 from VideoRAM mirror, addr=0x%08X\n", address); |
philpem@40 | 830 | data = RD16(state.vram, address, 0x7FFF); |
philpem@40 | 831 | break; |
philpem@59 | 832 | default: |
philpem@59 | 833 | data = IoRead(address, 16); |
philpem@40 | 834 | } |
philpem@59 | 835 | } else { |
philpem@59 | 836 | data = IoRead(address, 16); |
philpem@40 | 837 | } |
philpem@40 | 838 | |
philpem@40 | 839 | return data; |
philpem@59 | 840 | }/*}}}*/ |
philpem@40 | 841 | |
philpem@40 | 842 | /** |
philpem@40 | 843 | * @brief Read M68K memory, 8-bit |
philpem@40 | 844 | */ |
philpem@59 | 845 | uint32_t m68k_read_memory_8(uint32_t address)/*{{{*/ |
philpem@40 | 846 | { |
philpem@119 | 847 | uint8_t data = EMPTY & 0xFF; |
philpem@40 | 848 | |
philpem@40 | 849 | // If ROMLMAP is set, force system to access ROM |
philpem@40 | 850 | if (!state.romlmap) |
philpem@40 | 851 | address |= 0x800000; |
philpem@40 | 852 | |
philpem@40 | 853 | // Check access permissions |
philpem@40 | 854 | ACCESS_CHECK_RD(address, 8); |
philpem@40 | 855 | |
philpem@40 | 856 | if ((address >= 0x800000) && (address <= 0xBFFFFF)) { |
philpem@40 | 857 | // ROM access |
philpem@40 | 858 | data = RD8(state.rom, address, ROM_SIZE - 1); |
philpem@60 | 859 | } else if (address <= 0x3fffff) { |
philpem@40 | 860 | // RAM access |
philpem@128 | 861 | uint32_t newAddr = MAP_ADDR(address); |
philpem@128 | 862 | |
philpem@63 | 863 | if (newAddr <= 0x1fffff) { |
philpem@129 | 864 | // Base memory wraps around |
philpem@129 | 865 | return RD8(state.base_ram, newAddr, state.base_ram_size - 1); |
philpem@63 | 866 | } else { |
philpem@119 | 867 | if ((newAddr <= (state.exp_ram_size + 0x200000 - 1)) && (newAddr >= 0x200000)) |
philpem@63 | 868 | return RD8(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1); |
philpem@63 | 869 | else |
philpem@119 | 870 | return EMPTY & 0xff; |
philpem@63 | 871 | } |
philpem@40 | 872 | } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) { |
philpem@40 | 873 | // I/O register space, zone A |
philpem@40 | 874 | switch (address & 0x0F0000) { |
philpem@40 | 875 | case 0x000000: // Map RAM access |
philpem@40 | 876 | if (address > 0x4007FF) fprintf(stderr, "NOTE: RD8 from MapRAM mirror, addr=0x%08X\n", address); |
philpem@40 | 877 | data = RD8(state.map, address, 0x7FF); |
philpem@40 | 878 | break; |
philpem@40 | 879 | case 0x020000: // Video RAM |
philpem@40 | 880 | if (address > 0x427FFF) fprintf(stderr, "NOTE: RD8 from VideoRAM mirror, addr=0x%08X\n", address); |
philpem@40 | 881 | data = RD8(state.vram, address, 0x7FFF); |
philpem@40 | 882 | break; |
philpem@59 | 883 | default: |
philpem@59 | 884 | data = IoRead(address, 8); |
philpem@40 | 885 | } |
philpem@59 | 886 | } else { |
philpem@59 | 887 | data = IoRead(address, 8); |
philpem@40 | 888 | } |
philpem@40 | 889 | |
philpem@40 | 890 | return data; |
philpem@59 | 891 | }/*}}}*/ |
philpem@40 | 892 | |
philpem@40 | 893 | /** |
philpem@40 | 894 | * @brief Write M68K memory, 32-bit |
philpem@40 | 895 | */ |
philpem@59 | 896 | void m68k_write_memory_32(uint32_t address, uint32_t value)/*{{{*/ |
philpem@40 | 897 | { |
philpem@40 | 898 | // If ROMLMAP is set, force system to access ROM |
philpem@40 | 899 | if (!state.romlmap) |
philpem@40 | 900 | address |= 0x800000; |
philpem@40 | 901 | |
philpem@40 | 902 | // Check access permissions |
philpem@40 | 903 | ACCESS_CHECK_WR(address, 32); |
philpem@40 | 904 | |
philpem@40 | 905 | if ((address >= 0x800000) && (address <= 0xBFFFFF)) { |
philpem@40 | 906 | // ROM access |
philpem@60 | 907 | } else if (address <= 0x3FFFFF) { |
philpem@40 | 908 | // RAM access |
philpem@128 | 909 | uint32_t newAddr = MAP_ADDR(address); |
philpem@128 | 910 | |
philpem@119 | 911 | if (newAddr <= 0x1fffff) { |
philpem@119 | 912 | if (newAddr < state.base_ram_size) { |
philpem@119 | 913 | WR32(state.base_ram, newAddr, state.base_ram_size - 1, value); |
philpem@119 | 914 | } |
philpem@119 | 915 | } else { |
philpem@119 | 916 | if ((newAddr - 0x200000) < state.exp_ram_size) { |
philpem@119 | 917 | WR32(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1, value); |
philpem@119 | 918 | } |
philpem@119 | 919 | } |
philpem@40 | 920 | } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) { |
philpem@40 | 921 | // I/O register space, zone A |
philpem@40 | 922 | switch (address & 0x0F0000) { |
philpem@40 | 923 | case 0x000000: // Map RAM access |
philpem@105 | 924 | if (address > 0x4007FF) fprintf(stderr, "NOTE: WR32 to MapRAM mirror, addr=0x%08X\n", address); |
philpem@40 | 925 | WR32(state.map, address, 0x7FF, value); |
philpem@40 | 926 | break; |
philpem@40 | 927 | case 0x020000: // Video RAM |
philpem@105 | 928 | if (address > 0x427FFF) fprintf(stderr, "NOTE: WR32 to VideoRAM mirror, addr=0x%08X\n", address); |
philpem@40 | 929 | WR32(state.vram, address, 0x7FFF, value); |
philpem@40 | 930 | break; |
philpem@59 | 931 | default: |
philpem@59 | 932 | IoWrite(address, value, 32); |
philpem@40 | 933 | } |
philpem@59 | 934 | } else { |
philpem@59 | 935 | IoWrite(address, value, 32); |
philpem@40 | 936 | } |
philpem@59 | 937 | }/*}}}*/ |
philpem@40 | 938 | |
philpem@40 | 939 | /** |
philpem@40 | 940 | * @brief Write M68K memory, 16-bit |
philpem@40 | 941 | */ |
philpem@59 | 942 | void m68k_write_memory_16(uint32_t address, uint32_t value)/*{{{*/ |
philpem@40 | 943 | { |
philpem@40 | 944 | // If ROMLMAP is set, force system to access ROM |
philpem@40 | 945 | if (!state.romlmap) |
philpem@40 | 946 | address |= 0x800000; |
philpem@40 | 947 | |
philpem@40 | 948 | // Check access permissions |
philpem@40 | 949 | ACCESS_CHECK_WR(address, 16); |
philpem@40 | 950 | |
philpem@40 | 951 | if ((address >= 0x800000) && (address <= 0xBFFFFF)) { |
philpem@40 | 952 | // ROM access |
philpem@60 | 953 | } else if (address <= 0x3FFFFF) { |
philpem@40 | 954 | // RAM access |
philpem@128 | 955 | uint32_t newAddr = MAP_ADDR(address); |
philpem@112 | 956 | |
philpem@119 | 957 | if (newAddr <= 0x1fffff) { |
philpem@119 | 958 | if (newAddr < state.base_ram_size) { |
philpem@119 | 959 | WR16(state.base_ram, newAddr, state.base_ram_size - 1, value); |
philpem@119 | 960 | } |
philpem@119 | 961 | } else { |
philpem@119 | 962 | if ((newAddr - 0x200000) < state.exp_ram_size) { |
philpem@119 | 963 | WR16(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1, value); |
philpem@119 | 964 | } |
philpem@119 | 965 | } |
philpem@40 | 966 | } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) { |
philpem@40 | 967 | // I/O register space, zone A |
philpem@40 | 968 | switch (address & 0x0F0000) { |
philpem@40 | 969 | case 0x000000: // Map RAM access |
philpem@40 | 970 | if (address > 0x4007FF) fprintf(stderr, "NOTE: WR16 to MapRAM mirror, addr=0x%08X, data=0x%04X\n", address, value); |
philpem@40 | 971 | WR16(state.map, address, 0x7FF, value); |
philpem@40 | 972 | break; |
philpem@40 | 973 | case 0x020000: // Video RAM |
philpem@40 | 974 | if (address > 0x427FFF) fprintf(stderr, "NOTE: WR16 to VideoRAM mirror, addr=0x%08X, data=0x%04X\n", address, value); |
philpem@40 | 975 | WR16(state.vram, address, 0x7FFF, value); |
philpem@40 | 976 | break; |
philpem@59 | 977 | default: |
philpem@59 | 978 | IoWrite(address, value, 16); |
philpem@40 | 979 | } |
philpem@59 | 980 | } else { |
philpem@59 | 981 | IoWrite(address, value, 16); |
philpem@40 | 982 | } |
philpem@59 | 983 | }/*}}}*/ |
philpem@40 | 984 | |
philpem@40 | 985 | /** |
philpem@40 | 986 | * @brief Write M68K memory, 8-bit |
philpem@40 | 987 | */ |
philpem@59 | 988 | void m68k_write_memory_8(uint32_t address, uint32_t value)/*{{{*/ |
philpem@40 | 989 | { |
philpem@40 | 990 | // If ROMLMAP is set, force system to access ROM |
philpem@40 | 991 | if (!state.romlmap) |
philpem@40 | 992 | address |= 0x800000; |
philpem@40 | 993 | |
philpem@40 | 994 | // Check access permissions |
philpem@40 | 995 | ACCESS_CHECK_WR(address, 8); |
philpem@40 | 996 | |
philpem@40 | 997 | if ((address >= 0x800000) && (address <= 0xBFFFFF)) { |
philpem@40 | 998 | // ROM access (read only!) |
philpem@60 | 999 | } else if (address <= 0x3FFFFF) { |
philpem@40 | 1000 | // RAM access |
philpem@128 | 1001 | uint32_t newAddr = MAP_ADDR(address); |
philpem@128 | 1002 | |
philpem@119 | 1003 | if (newAddr <= 0x1fffff) { |
philpem@119 | 1004 | if (newAddr < state.base_ram_size) { |
philpem@119 | 1005 | WR8(state.base_ram, newAddr, state.base_ram_size - 1, value); |
philpem@119 | 1006 | } |
philpem@119 | 1007 | } else { |
philpem@119 | 1008 | if ((newAddr - 0x200000) < state.exp_ram_size) { |
philpem@119 | 1009 | WR8(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1, value); |
philpem@119 | 1010 | } |
philpem@119 | 1011 | } |
philpem@40 | 1012 | } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) { |
philpem@40 | 1013 | // I/O register space, zone A |
philpem@40 | 1014 | switch (address & 0x0F0000) { |
philpem@40 | 1015 | case 0x000000: // Map RAM access |
philpem@59 | 1016 | if (address > 0x4007FF) fprintf(stderr, "NOTE: WR8 to MapRAM mirror, addr=0x%08X, data=0x%04X\n", address, value); |
philpem@40 | 1017 | WR8(state.map, address, 0x7FF, value); |
philpem@40 | 1018 | break; |
philpem@40 | 1019 | case 0x020000: // Video RAM |
philpem@59 | 1020 | if (address > 0x427FFF) fprintf(stderr, "NOTE: WR8 to VideoRAM mirror, addr=0x%08X, data=0x%04X\n", address, value); |
philpem@40 | 1021 | WR8(state.vram, address, 0x7FFF, value); |
philpem@40 | 1022 | break; |
philpem@59 | 1023 | default: |
philpem@59 | 1024 | IoWrite(address, value, 8); |
philpem@40 | 1025 | } |
philpem@59 | 1026 | } else { |
philpem@59 | 1027 | IoWrite(address, value, 8); |
philpem@40 | 1028 | } |
philpem@59 | 1029 | }/*}}}*/ |
philpem@40 | 1030 | |
philpem@40 | 1031 | |
philpem@40 | 1032 | // for the disassembler |
philpem@121 | 1033 | uint32_t m68k_read_disassembler_32(uint32_t addr) |
philpem@121 | 1034 | { |
philpem@121 | 1035 | if (addr < 0x400000) { |
philpem@128 | 1036 | // XXX FIXME BUGBUG update this to use the new mapper macros! |
philpem@121 | 1037 | uint16_t page = (addr >> 12) & 0x3FF; |
philpem@121 | 1038 | uint32_t new_page_addr = MAPRAM(page) & 0x3FF; |
philpem@121 | 1039 | uint32_t newAddr = (new_page_addr << 12) + (addr & 0xFFF); |
philpem@121 | 1040 | if (newAddr <= 0x1fffff) { |
philpem@121 | 1041 | if (newAddr >= state.base_ram_size) |
philpem@121 | 1042 | return EMPTY; |
philpem@121 | 1043 | else |
philpem@121 | 1044 | return RD32(state.base_ram, newAddr, state.base_ram_size - 1); |
philpem@121 | 1045 | } else { |
philpem@121 | 1046 | if ((newAddr <= (state.exp_ram_size + 0x200000 - 1)) && (newAddr >= 0x200000)) |
philpem@121 | 1047 | return RD32(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1); |
philpem@121 | 1048 | else |
philpem@121 | 1049 | return EMPTY; |
philpem@121 | 1050 | } |
philpem@121 | 1051 | } else { |
philpem@128 | 1052 | LOG("WARNING: Disassembler RD32 out of range 0x%08X\n", addr); |
philpem@121 | 1053 | return EMPTY; |
philpem@121 | 1054 | } |
philpem@121 | 1055 | } |
philpem@40 | 1056 | |
philpem@121 | 1057 | uint32_t m68k_read_disassembler_16(uint32_t addr) |
philpem@121 | 1058 | { |
philpem@121 | 1059 | if (addr < 0x400000) { |
philpem@121 | 1060 | uint16_t page = (addr >> 12) & 0x3FF; |
philpem@121 | 1061 | uint32_t new_page_addr = MAPRAM(page) & 0x3FF; |
philpem@121 | 1062 | uint32_t newAddr = (new_page_addr << 12) + (addr & 0xFFF); |
philpem@121 | 1063 | if (newAddr <= 0x1fffff) { |
philpem@121 | 1064 | if (newAddr >= state.base_ram_size) |
philpem@121 | 1065 | return EMPTY & 0xffff; |
philpem@121 | 1066 | else |
philpem@121 | 1067 | return RD16(state.base_ram, newAddr, state.base_ram_size - 1); |
philpem@121 | 1068 | } else { |
philpem@121 | 1069 | if ((newAddr <= (state.exp_ram_size + 0x200000 - 1)) && (newAddr >= 0x200000)) |
philpem@121 | 1070 | return RD16(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1); |
philpem@121 | 1071 | else |
philpem@121 | 1072 | return EMPTY & 0xffff; |
philpem@121 | 1073 | } |
philpem@121 | 1074 | } else { |
philpem@128 | 1075 | LOG("WARNING: Disassembler RD16 out of range 0x%08X\n", addr); |
philpem@121 | 1076 | return EMPTY & 0xffff; |
philpem@121 | 1077 | } |
philpem@121 | 1078 | } |
philpem@121 | 1079 | |
philpem@121 | 1080 | uint32_t m68k_read_disassembler_8 (uint32_t addr) |
philpem@121 | 1081 | { |
philpem@121 | 1082 | if (addr < 0x400000) { |
philpem@121 | 1083 | uint16_t page = (addr >> 12) & 0x3FF; |
philpem@121 | 1084 | uint32_t new_page_addr = MAPRAM(page) & 0x3FF; |
philpem@121 | 1085 | uint32_t newAddr = (new_page_addr << 12) + (addr & 0xFFF); |
philpem@121 | 1086 | if (newAddr <= 0x1fffff) { |
philpem@121 | 1087 | if (newAddr >= state.base_ram_size) |
philpem@121 | 1088 | return EMPTY & 0xff; |
philpem@121 | 1089 | else |
philpem@121 | 1090 | return RD8(state.base_ram, newAddr, state.base_ram_size - 1); |
philpem@121 | 1091 | } else { |
philpem@121 | 1092 | if ((newAddr <= (state.exp_ram_size + 0x200000 - 1)) && (newAddr >= 0x200000)) |
philpem@121 | 1093 | return RD8(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1); |
philpem@121 | 1094 | else |
philpem@121 | 1095 | return EMPTY & 0xff; |
philpem@121 | 1096 | } |
philpem@121 | 1097 | } else { |
philpem@128 | 1098 | LOG("WARNING: Disassembler RD8 out of range 0x%08X\n", addr); |
philpem@121 | 1099 | return EMPTY & 0xff; |
philpem@121 | 1100 | } |
philpem@121 | 1101 | } |
philpem@121 | 1102 |