src/memory.c

Tue, 15 Jan 2013 17:02:56 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Tue, 15 Jan 2013 17:02:56 +0000
changeset 121
15ae2788e848
parent 119
101fe02456ce
child 128
3246b74d96bc
child 129
8b24770dea79
permissions
-rw-r--r--

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