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