src/memory.c

Mon, 14 Jan 2013 09:22:12 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Mon, 14 Jan 2013 09:22:12 +0000
changeset 118
feee84e0b3bf
parent 117
73caf968b67b
child 119
101fe02456ce
permissions
-rw-r--r--

More bus error fixes for FreeBee

I have fixed two more bus error handling bugs in FreeBee. First, the CPU core was executing the instruction regardless of whether a bus error occurs when fetching the opcode (which caused it to execute a bogus instruction in such cases). The other one was related to one of my previous fixes - the jump to the bus error vector was at the beginning of the main loop, so it wouldn't be called immediately after the bus error occurred if the timeslot expired, causing the return address to be off.

With these fixes, Unix now runs enough to get into userspace and run the install script (it is also possible to break out and get a shell prompt). However, many commands segfault semi-randomly (or more specifically, it seems that some child processes forked by the shell might be segfaulting before they can exec the command program), so installing the system isn't possible yet. I am not sure exactly what the bug is, but it seems to be related to some function in the shell returning null when the code calling it is assuming that it won't. What the function is, or why it is returning null, I'm not sure (the shell is built without the shared libc and is stripped, making identifying the function harder). I suspect that the function might be in libc, but that is hard to tell.

Author: Andrew Warkentin <andreww591 gmail com>

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