src/main.c

Thu, 02 Dec 2010 20:58:12 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Thu, 02 Dec 2010 20:58:12 +0000
changeset 37
87662afa1d98
parent 36
746fb8d0653e
child 38
b948045ca964
permissions
-rw-r--r--

rework address-check logic

philpem@0 1 #include <stdio.h>
philpem@7 2 #include <stdlib.h>
philpem@4 3 #include <stdint.h>
philpem@7 4 #include <stdbool.h>
philpem@7 5 #include <malloc.h>
philpem@7 6 #include <string.h>
philpem@18 7
philpem@20 8 #include "SDL.h"
philpem@20 9
philpem@4 10 #include "musashi/m68k.h"
philpem@7 11 #include "version.h"
philpem@18 12 #include "state.h"
philpem@7 13
philpem@7 14 void FAIL(char *err)
philpem@7 15 {
philpem@7 16 state_done();
philpem@7 17 fprintf(stderr, "ERROR: %s\nExiting...\n", err);
philpem@7 18 exit(EXIT_FAILURE);
philpem@7 19 }
philpem@7 20
philpem@26 21 /***********************************
philpem@26 22 * Array read/write utility macros
philpem@26 23 * "Don't Repeat Yourself" :)
philpem@26 24 ***********************************/
philpem@26 25
philpem@26 26 /// Array read, 32-bit
philpem@26 27 #define RD32(array, address, andmask) \
philpem@26 28 (((uint32_t)array[(address + 0) & (andmask)] << 24) | \
philpem@26 29 ((uint32_t)array[(address + 1) & (andmask)] << 16) | \
philpem@26 30 ((uint32_t)array[(address + 2) & (andmask)] << 8) | \
philpem@26 31 ((uint32_t)array[(address + 3) & (andmask)]))
philpem@26 32
philpem@26 33 /// Array read, 16-bit
philpem@26 34 #define RD16(array, address, andmask) \
philpem@26 35 (((uint32_t)array[(address + 0) & (andmask)] << 8) | \
philpem@26 36 ((uint32_t)array[(address + 1) & (andmask)]))
philpem@26 37
philpem@26 38 /// Array read, 8-bit
philpem@26 39 #define RD8(array, address, andmask) \
philpem@26 40 ((uint32_t)array[(address + 0) & (andmask)])
philpem@26 41
philpem@26 42 /// Array write, 32-bit
philpem@26 43 #define WR32(array, address, andmask, value) { \
philpem@26 44 array[(address + 0) & (andmask)] = (value >> 24) & 0xff; \
philpem@26 45 array[(address + 1) & (andmask)] = (value >> 16) & 0xff; \
philpem@26 46 array[(address + 2) & (andmask)] = (value >> 8) & 0xff; \
philpem@26 47 array[(address + 3) & (andmask)] = value & 0xff; \
philpem@26 48 }
philpem@26 49
philpem@26 50 /// Array write, 16-bit
philpem@26 51 #define WR16(array, address, andmask, value) { \
philpem@26 52 array[(address + 0) & (andmask)] = (value >> 8) & 0xff; \
philpem@26 53 array[(address + 1) & (andmask)] = value & 0xff; \
philpem@26 54 }
philpem@26 55
philpem@26 56 /// Array write, 8-bit
philpem@26 57 #define WR8(array, address, andmask, value) \
philpem@26 58 array[(address + 0) & (andmask)] = value & 0xff;
philpem@26 59
philpem@30 60 /******************
philpem@30 61 * Memory mapping
philpem@30 62 ******************/
philpem@30 63
philpem@30 64 #define MAPRAM(addr) (((uint16_t)state.map[addr*2] << 8) + ((uint16_t)state.map[(addr*2)+1]))
philpem@30 65
philpem@30 66 uint32_t mapAddr(uint32_t addr, bool writing)
philpem@30 67 {
philpem@30 68 if (addr < 0x400000) {
philpem@30 69 // RAM access. Check against the Map RAM
philpem@30 70 // Start by getting the original page address
philpem@30 71 uint16_t page = (addr >> 12) & 0x3FF;
philpem@30 72
philpem@30 73 // Look it up in the map RAM and get the physical page address
philpem@30 74 uint32_t new_page_addr = MAPRAM(page) & 0x3FF;
philpem@30 75
philpem@30 76 // Update the Page Status bits
philpem@30 77 uint8_t pagebits = (MAPRAM(page) >> 13) & 0x03;
philpem@30 78 if (pagebits != 0) {
philpem@30 79 if (writing)
philpem@32 80 state.map[page*2] |= 0x60; // Page written to (dirty)
philpem@30 81 else
philpem@32 82 state.map[page*2] |= 0x40; // Page accessed but not written
philpem@30 83 }
philpem@30 84
philpem@30 85 // Return the address with the new physical page spliced in
philpem@30 86 return (new_page_addr << 12) + (addr & 0xFFF);
philpem@30 87 } else {
philpem@30 88 // I/O, VRAM or MapRAM space; no mapping is performed or required
philpem@30 89 // TODO: assert here?
philpem@30 90 return addr;
philpem@30 91 }
philpem@30 92 }
philpem@30 93
philpem@30 94 typedef enum {
philpem@30 95 MEM_ALLOWED = 0,
philpem@30 96 MEM_PAGEFAULT, // Page fault -- page not present
philpem@30 97 MEM_PAGE_NO_WE, // Page not write enabled
philpem@30 98 MEM_KERNEL, // User attempted to access kernel memory
philpem@30 99 MEM_UIE // User Nonmemory Location Access
philpem@30 100 } MEM_STATUS;
philpem@30 101
philpem@30 102 // check memory access permissions
philpem@30 103 MEM_STATUS checkMemoryAccess(uint32_t addr, bool writing)
philpem@30 104 {
philpem@30 105 // Are we in Supervisor mode?
philpem@30 106 if (m68k_get_reg(NULL, M68K_REG_SR) & 0x2000)
philpem@30 107 // Yes. We can do anything we like.
philpem@30 108 return MEM_ALLOWED;
philpem@30 109
philpem@30 110 // If we're here, then we must be in User mode.
philpem@30 111 // Check that the user didn't access memory outside of the RAM area
philpem@30 112 if (addr >= 0x400000)
philpem@30 113 return MEM_UIE;
philpem@30 114
philpem@30 115 // This leaves us with Page Fault checking. Get the page bits for this page.
philpem@30 116 uint16_t page = (addr >> 12) & 0x3FF;
philpem@30 117 uint8_t pagebits = (MAPRAM(page) >> 13) & 0x07;
philpem@30 118
philpem@30 119 // Check page is present
philpem@30 120 if ((pagebits & 0x03) == 0)
philpem@30 121 return MEM_PAGEFAULT;
philpem@30 122
philpem@30 123 // User attempt to access the kernel
philpem@30 124 // A19, A20, A21, A22 low (kernel access): RAM addr before paging; not in Supervisor mode
philpem@30 125 if (((addr >> 19) & 0x0F) == 0)
philpem@30 126 return MEM_KERNEL;
philpem@30 127
philpem@30 128 // Check page is write enabled
philpem@30 129 if ((pagebits & 0x04) == 0)
philpem@30 130 return MEM_PAGE_NO_WE;
philpem@30 131
philpem@30 132 // Page access allowed.
philpem@30 133 return MEM_ALLOWED;
philpem@30 134 }
philpem@30 135
philpem@30 136 #undef MAPRAM
philpem@26 137
philpem@26 138 /********************************************************
philpem@26 139 * m68k memory read/write support functions for Musashi
philpem@26 140 ********************************************************/
philpem@26 141
philpem@32 142 /**
philpem@32 143 * @brief Check memory access permissions for a write operation.
philpem@32 144 * @note This used to be a single macro (merged with ACCESS_CHECK_RD), but
philpem@32 145 * gcc throws warnings when you have a return-with-value in a void
philpem@32 146 * function, even if the return-with-value is completely unreachable.
philpem@32 147 * Similarly it doesn't like it if you have a return without a value
philpem@32 148 * in a non-void function, even if it's impossible to ever reach the
philpem@32 149 * return-with-no-value. UGH!
philpem@32 150 */
philpem@37 151 #define ACCESS_CHECK_WR(address, bits) do { \
philpem@37 152 bool fault = false; \
philpem@37 153 /* MEM_STATUS st; */ \
philpem@37 154 switch (checkMemoryAccess(address, true)) { \
philpem@37 155 case MEM_ALLOWED: \
philpem@37 156 /* Access allowed */ \
philpem@37 157 break; \
philpem@37 158 case MEM_PAGEFAULT: \
philpem@37 159 /* Page fault */ \
philpem@37 160 state.genstat = 0x8FFF; \
philpem@37 161 fault = true; \
philpem@37 162 break; \
philpem@37 163 case MEM_UIE: \
philpem@37 164 /* User access to memory above 4MB */ \
philpem@37 165 state.genstat = 0x9EFF; \
philpem@37 166 fault = true; \
philpem@37 167 break; \
philpem@37 168 case MEM_KERNEL: \
philpem@37 169 case MEM_PAGE_NO_WE: \
philpem@37 170 /* kernel access or page not write enabled */ \
philpem@37 171 /* TODO: which regs need setting? */ \
philpem@37 172 fault = true; \
philpem@37 173 break; \
philpem@37 174 } \
philpem@37 175 \
philpem@37 176 if (fault) { \
philpem@37 177 if (bits >= 16) \
philpem@37 178 state.bsr0 = 0x7F00; \
philpem@37 179 else \
philpem@37 180 state.bsr0 = (address & 1) ? 0x7D00 : 0x7E00; \
philpem@37 181 state.bsr0 |= (address >> 16); \
philpem@37 182 state.bsr1 = address & 0xffff; \
philpem@37 183 printf("ERR: BusError WR\n"); \
philpem@37 184 m68k_pulse_bus_error(); \
philpem@37 185 return; \
philpem@37 186 } \
philpem@32 187 } while (false)
philpem@32 188
philpem@32 189 /**
philpem@32 190 * @brief Check memory access permissions for a read operation.
philpem@32 191 * @note This used to be a single macro (merged with ACCESS_CHECK_WR), but
philpem@32 192 * gcc throws warnings when you have a return-with-value in a void
philpem@32 193 * function, even if the return-with-value is completely unreachable.
philpem@32 194 * Similarly it doesn't like it if you have a return without a value
philpem@32 195 * in a non-void function, even if it's impossible to ever reach the
philpem@32 196 * return-with-no-value. UGH!
philpem@32 197 */
philpem@37 198 #define ACCESS_CHECK_RD(address, bits) do { \
philpem@37 199 bool fault = false; \
philpem@37 200 /* MEM_STATUS st; */ \
philpem@37 201 switch (checkMemoryAccess(address, false)) { \
philpem@37 202 case MEM_ALLOWED: \
philpem@37 203 /* Access allowed */ \
philpem@37 204 break; \
philpem@37 205 case MEM_PAGEFAULT: \
philpem@37 206 /* Page fault */ \
philpem@37 207 state.genstat = 0x8FFF; \
philpem@37 208 fault = true; \
philpem@37 209 break; \
philpem@37 210 case MEM_UIE: \
philpem@37 211 /* User access to memory above 4MB */ \
philpem@37 212 state.genstat = 0x9EFF; \
philpem@37 213 fault = true; \
philpem@37 214 break; \
philpem@37 215 case MEM_KERNEL: \
philpem@37 216 case MEM_PAGE_NO_WE: \
philpem@37 217 /* kernel access or page not write enabled */ \
philpem@37 218 /* TODO: which regs need setting? */ \
philpem@37 219 fault = true; \
philpem@37 220 break; \
philpem@37 221 } \
philpem@37 222 \
philpem@37 223 if (fault) { \
philpem@37 224 if (bits >= 16) \
philpem@37 225 state.bsr0 = 0x7F00; \
philpem@37 226 else \
philpem@37 227 state.bsr0 = (address & 1) ? 0x7D00 : 0x7E00; \
philpem@37 228 state.bsr0 |= (address >> 16); \
philpem@37 229 state.bsr1 = address & 0xffff; \
philpem@37 230 printf("ERR: BusError RD\n"); \
philpem@37 231 m68k_pulse_bus_error(); \
philpem@37 232 return 0xFFFFFFFF; \
philpem@37 233 } \
philpem@32 234 } while (false)
philpem@32 235
philpem@37 236
philpem@32 237 /**
philpem@32 238 * @brief Read M68K memory, 32-bit
philpem@32 239 */
philpem@4 240 uint32_t m68k_read_memory_32(uint32_t address)
philpem@4 241 {
philpem@9 242 uint32_t data = 0xFFFFFFFF;
philpem@9 243
philpem@7 244 // If ROMLMAP is set, force system to access ROM
philpem@7 245 if (!state.romlmap)
philpem@7 246 address |= 0x800000;
philpem@7 247
philpem@32 248 // Check access permissions
philpem@37 249 ACCESS_CHECK_RD(address, 32);
philpem@32 250
philpem@9 251 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@7 252 // ROM access
philpem@26 253 data = RD32(state.rom, address, ROM_SIZE - 1);
philpem@26 254 } else if (address <= (state.ram_size - 1)) {
philpem@32 255 // RAM access
philpem@32 256 data = RD32(state.ram, mapAddr(address, false), state.ram_size - 1);
philpem@34 257 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@34 258 // I/O register space, zone A
philpem@36 259 // printf("RD32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 260 switch (address & 0x0F0000) {
philpem@34 261 case 0x000000: // Map RAM access
philpem@34 262 if (address > 0x4007FF) fprintf(stderr, "NOTE: RD32 from MapRAM mirror, addr=0x%08X\n", address);
philpem@34 263 data = RD32(state.map, address, 0x7FF);
philpem@34 264 break;
philpem@34 265 case 0x010000: // General Status Register
philpem@34 266 data = ((uint32_t)state.genstat << 16) + (uint32_t)state.genstat;
philpem@34 267 break;
philpem@34 268 case 0x020000: // Video RAM
philpem@34 269 if (address > 0x427FFF) fprintf(stderr, "NOTE: RD32 from VideoRAM mirror, addr=0x%08X\n", address);
philpem@34 270 data = RD32(state.vram, address, 0x7FFF);
philpem@34 271 break;
philpem@34 272 case 0x030000: // Bus Status Register 0
philpem@35 273 break;
philpem@34 274 case 0x040000: // Bus Status Register 1
philpem@35 275 break;
philpem@34 276 case 0x050000: // Phone status
philpem@35 277 break;
philpem@34 278 case 0x060000: // DMA Count
philpem@35 279 break;
philpem@34 280 case 0x070000: // Line Printer Status Register
philpem@35 281 break;
philpem@34 282 case 0x080000: // Real Time Clock
philpem@35 283 break;
philpem@34 284 case 0x090000: // Phone registers
philpem@34 285 switch (address & 0x0FF000) {
philpem@34 286 case 0x090000: // Handset relay
philpem@34 287 case 0x098000:
philpem@35 288 break;
philpem@34 289 case 0x091000: // Line select 2
philpem@34 290 case 0x099000:
philpem@35 291 break;
philpem@34 292 case 0x092000: // Hook relay 1
philpem@34 293 case 0x09A000:
philpem@35 294 break;
philpem@34 295 case 0x093000: // Hook relay 2
philpem@34 296 case 0x09B000:
philpem@35 297 break;
philpem@34 298 case 0x094000: // Line 1 hold
philpem@34 299 case 0x09C000:
philpem@35 300 break;
philpem@34 301 case 0x095000: // Line 2 hold
philpem@34 302 case 0x09D000:
philpem@35 303 break;
philpem@34 304 case 0x096000: // Line 1 A-lead
philpem@34 305 case 0x09E000:
philpem@35 306 break;
philpem@34 307 case 0x097000: // Line 2 A-lead
philpem@34 308 case 0x09F000:
philpem@34 309 break;
philpem@34 310 }
philpem@34 311 break;
philpem@34 312 case 0x0A0000: // Miscellaneous Control Register
philpem@35 313 break;
philpem@34 314 case 0x0B0000: // TM/DIALWR
philpem@35 315 break;
philpem@34 316 case 0x0C0000: // CSR
philpem@35 317 break;
philpem@34 318 case 0x0D0000: // DMA Address Register
philpem@35 319 break;
philpem@34 320 case 0x0E0000: // Disk Control Register
philpem@35 321 break;
philpem@34 322 case 0x0F0000: // Line Printer Data Register
philpem@34 323 break;
philpem@34 324 }
philpem@34 325 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@34 326 // I/O register space, zone B
philpem@36 327 // printf("RD32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 328 switch (address & 0xF00000) {
philpem@34 329 case 0xC00000: // Expansion slots
philpem@34 330 case 0xD00000:
philpem@34 331 switch (address & 0xFC0000) {
philpem@34 332 case 0xC00000: // Expansion slot 0
philpem@34 333 case 0xC40000: // Expansion slot 1
philpem@34 334 case 0xC80000: // Expansion slot 2
philpem@34 335 case 0xCC0000: // Expansion slot 3
philpem@34 336 case 0xD00000: // Expansion slot 4
philpem@34 337 case 0xD40000: // Expansion slot 5
philpem@34 338 case 0xD80000: // Expansion slot 6
philpem@34 339 case 0xDC0000: // Expansion slot 7
philpem@34 340 fprintf(stderr, "NOTE: RD32 from expansion card space, addr=0x%08X\n", address);
philpem@34 341 break;
philpem@34 342 }
philpem@34 343 break;
philpem@34 344 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@34 345 case 0xF00000:
philpem@34 346 switch (address & 0x070000) {
philpem@34 347 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@34 348 break;
philpem@34 349 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@34 350 break;
philpem@34 351 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@34 352 break;
philpem@34 353 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@34 354 break;
philpem@35 355 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@35 356 switch (address & 0x077000) {
philpem@35 357 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@35 358 break;
philpem@35 359 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@35 360 break;
philpem@35 361 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@35 362 break;
philpem@35 363 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@35 364 break;
philpem@35 365 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@35 366 break;
philpem@35 367 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@35 368 break;
philpem@35 369 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@35 370 break;
philpem@35 371 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@35 372 break;
philpem@35 373 }
philpem@35 374 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@35 375 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@35 376 switch (address & 0x07F000) {
philpem@35 377 default:
philpem@35 378 break;
philpem@35 379 }
philpem@35 380 break;
philpem@35 381 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@34 382 default:
philpem@34 383 fprintf(stderr, "NOTE: RD32 from undefined E/F-block address 0x%08X", address);
philpem@34 384 }
philpem@34 385 }
philpem@7 386 }
philpem@9 387 return data;
philpem@4 388 }
philpem@4 389
philpem@32 390 /**
philpem@32 391 * @brief Read M68K memory, 16-bit
philpem@32 392 */
philpem@4 393 uint32_t m68k_read_memory_16(uint32_t address)
philpem@4 394 {
philpem@9 395 uint16_t data = 0xFFFF;
philpem@9 396
philpem@9 397 // If ROMLMAP is set, force system to access ROM
philpem@9 398 if (!state.romlmap)
philpem@9 399 address |= 0x800000;
philpem@9 400
philpem@32 401 // Check access permissions
philpem@37 402 ACCESS_CHECK_RD(address, 16);
philpem@32 403
philpem@10 404 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@10 405 // ROM access
philpem@26 406 data = RD16(state.rom, address, ROM_SIZE - 1);
philpem@26 407 } else if (address <= (state.ram_size - 1)) {
philpem@32 408 // RAM access
philpem@32 409 data = RD16(state.ram, mapAddr(address, false), state.ram_size - 1);
philpem@34 410 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@34 411 // I/O register space, zone A
philpem@36 412 // printf("RD16 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 413 switch (address & 0x0F0000) {
philpem@34 414 case 0x000000: // Map RAM access
philpem@34 415 if (address > 0x4007FF) fprintf(stderr, "NOTE: RD16 from MapRAM mirror, addr=0x%08X\n", address);
philpem@34 416 data = RD16(state.map, address, 0x7FF);
philpem@34 417 break;
philpem@34 418 case 0x010000: // General Status Register
philpem@34 419 data = state.genstat;
philpem@34 420 break;
philpem@34 421 case 0x020000: // Video RAM
philpem@34 422 if (address > 0x427FFF) fprintf(stderr, "NOTE: RD16 from VideoRAM mirror, addr=0x%08X\n", address);
philpem@34 423 data = RD16(state.vram, address, 0x7FFF);
philpem@34 424 break;
philpem@34 425 case 0x030000: // Bus Status Register 0
philpem@35 426 break;
philpem@34 427 case 0x040000: // Bus Status Register 1
philpem@35 428 break;
philpem@34 429 case 0x050000: // Phone status
philpem@35 430 break;
philpem@34 431 case 0x060000: // DMA Count
philpem@35 432 break;
philpem@34 433 case 0x070000: // Line Printer Status Register
philpem@35 434 break;
philpem@34 435 case 0x080000: // Real Time Clock
philpem@35 436 break;
philpem@34 437 case 0x090000: // Phone registers
philpem@34 438 switch (address & 0x0FF000) {
philpem@34 439 case 0x090000: // Handset relay
philpem@34 440 case 0x098000:
philpem@35 441 break;
philpem@34 442 case 0x091000: // Line select 2
philpem@34 443 case 0x099000:
philpem@35 444 break;
philpem@34 445 case 0x092000: // Hook relay 1
philpem@34 446 case 0x09A000:
philpem@35 447 break;
philpem@34 448 case 0x093000: // Hook relay 2
philpem@34 449 case 0x09B000:
philpem@35 450 break;
philpem@34 451 case 0x094000: // Line 1 hold
philpem@34 452 case 0x09C000:
philpem@35 453 break;
philpem@34 454 case 0x095000: // Line 2 hold
philpem@34 455 case 0x09D000:
philpem@35 456 break;
philpem@34 457 case 0x096000: // Line 1 A-lead
philpem@34 458 case 0x09E000:
philpem@35 459 break;
philpem@34 460 case 0x097000: // Line 2 A-lead
philpem@34 461 case 0x09F000:
philpem@34 462 break;
philpem@34 463 }
philpem@34 464 break;
philpem@34 465 case 0x0A0000: // Miscellaneous Control Register
philpem@35 466 break;
philpem@34 467 case 0x0B0000: // TM/DIALWR
philpem@35 468 break;
philpem@34 469 case 0x0C0000: // CSR
philpem@35 470 break;
philpem@34 471 case 0x0D0000: // DMA Address Register
philpem@35 472 break;
philpem@34 473 case 0x0E0000: // Disk Control Register
philpem@35 474 break;
philpem@34 475 case 0x0F0000: // Line Printer Data Register
philpem@34 476 break;
philpem@34 477 }
philpem@34 478 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@34 479 // I/O register space, zone B
philpem@36 480 // printf("RD16 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 481 switch (address & 0xF00000) {
philpem@34 482 case 0xC00000: // Expansion slots
philpem@34 483 case 0xD00000:
philpem@34 484 switch (address & 0xFC0000) {
philpem@34 485 case 0xC00000: // Expansion slot 0
philpem@34 486 case 0xC40000: // Expansion slot 1
philpem@34 487 case 0xC80000: // Expansion slot 2
philpem@34 488 case 0xCC0000: // Expansion slot 3
philpem@34 489 case 0xD00000: // Expansion slot 4
philpem@34 490 case 0xD40000: // Expansion slot 5
philpem@34 491 case 0xD80000: // Expansion slot 6
philpem@34 492 case 0xDC0000: // Expansion slot 7
philpem@34 493 fprintf(stderr, "NOTE: RD16 from expansion card space, addr=0x%08X\n", address);
philpem@34 494 break;
philpem@34 495 }
philpem@34 496 break;
philpem@34 497 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@34 498 case 0xF00000:
philpem@34 499 switch (address & 0x070000) {
philpem@34 500 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@34 501 break;
philpem@34 502 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@34 503 break;
philpem@34 504 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@34 505 break;
philpem@34 506 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@34 507 break;
philpem@35 508 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@35 509 switch (address & 0x077000) {
philpem@35 510 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@35 511 break;
philpem@35 512 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@35 513 break;
philpem@35 514 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@35 515 break;
philpem@35 516 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@35 517 break;
philpem@35 518 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@35 519 break;
philpem@35 520 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@35 521 break;
philpem@35 522 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@35 523 break;
philpem@35 524 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@35 525 break;
philpem@35 526 }
philpem@35 527 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@35 528 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@35 529 switch (address & 0x07F000) {
philpem@35 530 default:
philpem@35 531 break;
philpem@35 532 }
philpem@35 533 break;
philpem@35 534 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@34 535 default:
philpem@35 536 fprintf(stderr, "NOTE: RD16 from undefined E/F-block address 0x%08X", address);
philpem@34 537 }
philpem@34 538 }
philpem@10 539 }
philpem@9 540 return data;
philpem@4 541 }
philpem@4 542
philpem@32 543 /**
philpem@32 544 * @brief Read M68K memory, 8-bit
philpem@32 545 */
philpem@4 546 uint32_t m68k_read_memory_8(uint32_t address)
philpem@4 547 {
philpem@9 548 uint8_t data = 0xFF;
philpem@9 549
philpem@7 550 // If ROMLMAP is set, force system to access ROM
philpem@7 551 if (!state.romlmap)
philpem@7 552 address |= 0x800000;
philpem@7 553
philpem@32 554 // Check access permissions
philpem@37 555 ACCESS_CHECK_RD(address, 8);
philpem@32 556
philpem@10 557 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@10 558 // ROM access
philpem@26 559 data = RD8(state.rom, address, ROM_SIZE - 1);
philpem@26 560 } else if (address <= (state.ram_size - 1)) {
philpem@32 561 // RAM access
philpem@32 562 data = RD8(state.ram, mapAddr(address, false), state.ram_size - 1);
philpem@34 563 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@34 564 // I/O register space, zone A
philpem@36 565 // printf("RD8 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 566 switch (address & 0x0F0000) {
philpem@34 567 case 0x000000: // Map RAM access
philpem@34 568 if (address > 0x4007FF) fprintf(stderr, "NOTE: RD8 from MapRAM mirror, addr=0x%08X\n", address);
philpem@34 569 data = RD8(state.map, address, 0x7FF);
philpem@34 570 break;
philpem@34 571 case 0x010000: // General Status Register
philpem@34 572 if ((address & 1) == 0)
philpem@34 573 data = (state.genstat >> 8) & 0xff;
philpem@34 574 else
philpem@34 575 data = (state.genstat) & 0xff;
philpem@34 576 break;
philpem@34 577 case 0x020000: // Video RAM
philpem@34 578 if (address > 0x427FFF) fprintf(stderr, "NOTE: RD8 from VideoRAM mirror, addr=0x%08X\n", address);
philpem@34 579 data = RD8(state.vram, address, 0x7FFF);
philpem@34 580 break;
philpem@34 581 case 0x030000: // Bus Status Register 0
philpem@35 582 break;
philpem@34 583 case 0x040000: // Bus Status Register 1
philpem@35 584 break;
philpem@34 585 case 0x050000: // Phone status
philpem@35 586 break;
philpem@34 587 case 0x060000: // DMA Count
philpem@35 588 break;
philpem@34 589 case 0x070000: // Line Printer Status Register
philpem@35 590 break;
philpem@34 591 case 0x080000: // Real Time Clock
philpem@35 592 break;
philpem@34 593 case 0x090000: // Phone registers
philpem@34 594 switch (address & 0x0FF000) {
philpem@34 595 case 0x090000: // Handset relay
philpem@34 596 case 0x098000:
philpem@35 597 break;
philpem@34 598 case 0x091000: // Line select 2
philpem@34 599 case 0x099000:
philpem@35 600 break;
philpem@34 601 case 0x092000: // Hook relay 1
philpem@34 602 case 0x09A000:
philpem@35 603 break;
philpem@34 604 case 0x093000: // Hook relay 2
philpem@34 605 case 0x09B000:
philpem@35 606 break;
philpem@34 607 case 0x094000: // Line 1 hold
philpem@34 608 case 0x09C000:
philpem@35 609 break;
philpem@34 610 case 0x095000: // Line 2 hold
philpem@34 611 case 0x09D000:
philpem@35 612 break;
philpem@34 613 case 0x096000: // Line 1 A-lead
philpem@34 614 case 0x09E000:
philpem@35 615 break;
philpem@34 616 case 0x097000: // Line 2 A-lead
philpem@34 617 case 0x09F000:
philpem@34 618 break;
philpem@34 619 }
philpem@34 620 break;
philpem@34 621 case 0x0A0000: // Miscellaneous Control Register
philpem@35 622 break;
philpem@34 623 case 0x0B0000: // TM/DIALWR
philpem@35 624 break;
philpem@34 625 case 0x0C0000: // CSR
philpem@35 626 break;
philpem@34 627 case 0x0D0000: // DMA Address Register
philpem@35 628 break;
philpem@34 629 case 0x0E0000: // Disk Control Register
philpem@35 630 break;
philpem@34 631 case 0x0F0000: // Line Printer Data Register
philpem@34 632 break;
philpem@34 633 }
philpem@34 634 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@34 635 // I/O register space, zone B
philpem@36 636 // printf("RD8 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 637 switch (address & 0xF00000) {
philpem@34 638 case 0xC00000: // Expansion slots
philpem@34 639 case 0xD00000:
philpem@34 640 switch (address & 0xFC0000) {
philpem@34 641 case 0xC00000: // Expansion slot 0
philpem@34 642 case 0xC40000: // Expansion slot 1
philpem@34 643 case 0xC80000: // Expansion slot 2
philpem@34 644 case 0xCC0000: // Expansion slot 3
philpem@34 645 case 0xD00000: // Expansion slot 4
philpem@34 646 case 0xD40000: // Expansion slot 5
philpem@34 647 case 0xD80000: // Expansion slot 6
philpem@34 648 case 0xDC0000: // Expansion slot 7
philpem@35 649 fprintf(stderr, "NOTE: RD8 from expansion card space, addr=0x%08X\n", address);
philpem@34 650 break;
philpem@34 651 }
philpem@34 652 break;
philpem@34 653 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@34 654 case 0xF00000:
philpem@34 655 switch (address & 0x070000) {
philpem@34 656 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@34 657 break;
philpem@34 658 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@34 659 break;
philpem@34 660 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@34 661 break;
philpem@34 662 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@34 663 break;
philpem@35 664 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@35 665 switch (address & 0x077000) {
philpem@35 666 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@35 667 break;
philpem@35 668 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@35 669 break;
philpem@35 670 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@35 671 break;
philpem@35 672 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@35 673 break;
philpem@35 674 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@35 675 break;
philpem@35 676 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@35 677 break;
philpem@35 678 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@35 679 break;
philpem@35 680 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@35 681 break;
philpem@35 682 }
philpem@35 683 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@35 684 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@35 685 switch (address & 0x07F000) {
philpem@35 686 default:
philpem@35 687 break;
philpem@35 688 }
philpem@35 689 break;
philpem@35 690 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@34 691 default:
philpem@34 692 fprintf(stderr, "NOTE: RD8 from undefined E/F-block address 0x%08X", address);
philpem@34 693 }
philpem@34 694 }
philpem@10 695 }
philpem@9 696 return data;
philpem@4 697 }
philpem@4 698
philpem@32 699 /**
philpem@32 700 * @brief Write M68K memory, 32-bit
philpem@32 701 */
philpem@4 702 void m68k_write_memory_32(uint32_t address, uint32_t value)
philpem@4 703 {
philpem@7 704 // If ROMLMAP is set, force system to access ROM
philpem@7 705 if (!state.romlmap)
philpem@7 706 address |= 0x800000;
philpem@7 707
philpem@32 708 // Check access permissions
philpem@37 709 ACCESS_CHECK_WR(address, 32);
philpem@32 710
philpem@9 711 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@7 712 // ROM access
philpem@34 713 WR32(state.rom, address, ROM_SIZE - 1, value);
philpem@26 714 } else if (address <= (state.ram_size - 1)) {
philpem@32 715 // RAM access
philpem@34 716 WR32(state.ram, mapAddr(address, false), state.ram_size - 1, value);
philpem@34 717 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@34 718 // I/O register space, zone A
philpem@36 719 // printf("WR32 0x%08X ==> 0x%08X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 720 switch (address & 0x0F0000) {
philpem@34 721 case 0x000000: // Map RAM access
philpem@34 722 if (address > 0x4007FF) fprintf(stderr, "NOTE: WR32 to MapRAM mirror, addr=0x%08X, data=0x%08X\n", address, value);
philpem@34 723 WR32(state.map, address, 0x7FF, value);
philpem@34 724 break;
philpem@34 725 case 0x010000: // General Status Register
philpem@34 726 state.genstat = (value & 0xffff);
philpem@34 727 break;
philpem@34 728 case 0x020000: // Video RAM
philpem@34 729 if (address > 0x427FFF) fprintf(stderr, "NOTE: WR32 to VideoRAM mirror, addr=0x%08X, data=0x%08X\n", address, value);
philpem@34 730 WR32(state.vram, address, 0x7FFF, value);
philpem@34 731 break;
philpem@34 732 case 0x030000: // Bus Status Register 0
philpem@35 733 break;
philpem@34 734 case 0x040000: // Bus Status Register 1
philpem@35 735 break;
philpem@34 736 case 0x050000: // Phone status
philpem@35 737 break;
philpem@34 738 case 0x060000: // DMA Count
philpem@35 739 break;
philpem@34 740 case 0x070000: // Line Printer Status Register
philpem@35 741 break;
philpem@34 742 case 0x080000: // Real Time Clock
philpem@35 743 break;
philpem@34 744 case 0x090000: // Phone registers
philpem@34 745 switch (address & 0x0FF000) {
philpem@34 746 case 0x090000: // Handset relay
philpem@34 747 case 0x098000:
philpem@35 748 break;
philpem@34 749 case 0x091000: // Line select 2
philpem@34 750 case 0x099000:
philpem@35 751 break;
philpem@34 752 case 0x092000: // Hook relay 1
philpem@34 753 case 0x09A000:
philpem@35 754 break;
philpem@34 755 case 0x093000: // Hook relay 2
philpem@34 756 case 0x09B000:
philpem@35 757 break;
philpem@34 758 case 0x094000: // Line 1 hold
philpem@34 759 case 0x09C000:
philpem@35 760 break;
philpem@34 761 case 0x095000: // Line 2 hold
philpem@34 762 case 0x09D000:
philpem@35 763 break;
philpem@34 764 case 0x096000: // Line 1 A-lead
philpem@34 765 case 0x09E000:
philpem@35 766 break;
philpem@34 767 case 0x097000: // Line 2 A-lead
philpem@34 768 case 0x09F000:
philpem@34 769 break;
philpem@34 770 }
philpem@34 771 break;
philpem@34 772 case 0x0A0000: // Miscellaneous Control Register
philpem@35 773 break;
philpem@34 774 case 0x0B0000: // TM/DIALWR
philpem@35 775 break;
philpem@34 776 case 0x0C0000: // CSR
philpem@35 777 break;
philpem@34 778 case 0x0D0000: // DMA Address Register
philpem@35 779 break;
philpem@34 780 case 0x0E0000: // Disk Control Register
philpem@35 781 break;
philpem@34 782 case 0x0F0000: // Line Printer Data Register
philpem@34 783 break;
philpem@34 784 }
philpem@34 785 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@34 786 // I/O register space, zone B
philpem@36 787 // printf("WR32 0x%08X ==> 0x%08X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 788 switch (address & 0xF00000) {
philpem@34 789 case 0xC00000: // Expansion slots
philpem@34 790 case 0xD00000:
philpem@34 791 switch (address & 0xFC0000) {
philpem@34 792 case 0xC00000: // Expansion slot 0
philpem@34 793 case 0xC40000: // Expansion slot 1
philpem@34 794 case 0xC80000: // Expansion slot 2
philpem@34 795 case 0xCC0000: // Expansion slot 3
philpem@34 796 case 0xD00000: // Expansion slot 4
philpem@34 797 case 0xD40000: // Expansion slot 5
philpem@34 798 case 0xD80000: // Expansion slot 6
philpem@34 799 case 0xDC0000: // Expansion slot 7
philpem@34 800 fprintf(stderr, "NOTE: WR32 to expansion card space, addr=0x%08X, data=0x%08X\n", address, value);
philpem@34 801 break;
philpem@34 802 }
philpem@34 803 break;
philpem@34 804 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@34 805 case 0xF00000:
philpem@34 806 switch (address & 0x070000) {
philpem@34 807 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@34 808 break;
philpem@34 809 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@34 810 break;
philpem@34 811 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@34 812 break;
philpem@34 813 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@34 814 break;
philpem@35 815 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@35 816 switch (address & 0x077000) {
philpem@35 817 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@35 818 break;
philpem@35 819 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@35 820 break;
philpem@35 821 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@35 822 break;
philpem@35 823 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@35 824 state.romlmap = ((value & 0x8000) == 0x8000);
philpem@35 825 break;
philpem@35 826 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@35 827 break;
philpem@35 828 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@35 829 break;
philpem@35 830 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@35 831 break;
philpem@35 832 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@35 833 break;
philpem@35 834 }
philpem@35 835 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@35 836 break;
philpem@35 837 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@35 838 switch (address & 0x07F000) {
philpem@35 839 default:
philpem@35 840 break;
philpem@35 841 }
philpem@35 842 break;
philpem@35 843 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@35 844 break;
philpem@34 845 default:
philpem@34 846 fprintf(stderr, "NOTE: WR32 to undefined E/F-block space, addr=0x%08X, data=0x%08X\n", address, value);
philpem@34 847 }
philpem@9 848 }
philpem@7 849 }
philpem@4 850 }
philpem@4 851
philpem@32 852 /**
philpem@32 853 * @brief Write M68K memory, 16-bit
philpem@32 854 */
philpem@4 855 void m68k_write_memory_16(uint32_t address, uint32_t value)
philpem@4 856 {
philpem@7 857 // If ROMLMAP is set, force system to access ROM
philpem@7 858 if (!state.romlmap)
philpem@7 859 address |= 0x800000;
philpem@7 860
philpem@32 861 // Check access permissions
philpem@37 862 ACCESS_CHECK_WR(address, 16);
philpem@32 863
philpem@9 864 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@7 865 // ROM access
philpem@34 866 WR16(state.rom, address, ROM_SIZE - 1, value);
philpem@26 867 } else if (address <= (state.ram_size - 1)) {
philpem@32 868 // RAM access
philpem@34 869 WR16(state.ram, mapAddr(address, false), state.ram_size - 1, value);
philpem@34 870 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@34 871 // I/O register space, zone A
philpem@36 872 // printf("WR16 0x%08X ==> 0x%04X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 873 switch (address & 0x0F0000) {
philpem@34 874 case 0x000000: // Map RAM access
philpem@34 875 if (address > 0x4007FF) fprintf(stderr, "NOTE: WR16 to MapRAM mirror, addr=0x%08X, data=0x%04X\n", address, value);
philpem@34 876 WR16(state.map, address, 0x7FF, value);
philpem@34 877 break;
philpem@34 878 case 0x010000: // General Status Register
philpem@34 879 state.genstat = (value & 0xffff);
philpem@34 880 break;
philpem@34 881 case 0x020000: // Video RAM
philpem@34 882 if (address > 0x427FFF) fprintf(stderr, "NOTE: WR16 to VideoRAM mirror, addr=0x%08X, data=0x%04X\n", address, value);
philpem@34 883 WR16(state.vram, address, 0x7FFF, value);
philpem@34 884 break;
philpem@34 885 case 0x030000: // Bus Status Register 0
philpem@35 886 break;
philpem@34 887 case 0x040000: // Bus Status Register 1
philpem@35 888 break;
philpem@34 889 case 0x050000: // Phone status
philpem@35 890 break;
philpem@34 891 case 0x060000: // DMA Count
philpem@35 892 break;
philpem@34 893 case 0x070000: // Line Printer Status Register
philpem@35 894 break;
philpem@34 895 case 0x080000: // Real Time Clock
philpem@35 896 break;
philpem@34 897 case 0x090000: // Phone registers
philpem@34 898 switch (address & 0x0FF000) {
philpem@34 899 case 0x090000: // Handset relay
philpem@34 900 case 0x098000:
philpem@35 901 break;
philpem@34 902 case 0x091000: // Line select 2
philpem@34 903 case 0x099000:
philpem@35 904 break;
philpem@34 905 case 0x092000: // Hook relay 1
philpem@34 906 case 0x09A000:
philpem@35 907 break;
philpem@34 908 case 0x093000: // Hook relay 2
philpem@34 909 case 0x09B000:
philpem@35 910 break;
philpem@34 911 case 0x094000: // Line 1 hold
philpem@34 912 case 0x09C000:
philpem@35 913 break;
philpem@34 914 case 0x095000: // Line 2 hold
philpem@34 915 case 0x09D000:
philpem@35 916 break;
philpem@34 917 case 0x096000: // Line 1 A-lead
philpem@34 918 case 0x09E000:
philpem@35 919 break;
philpem@34 920 case 0x097000: // Line 2 A-lead
philpem@34 921 case 0x09F000:
philpem@34 922 break;
philpem@34 923 }
philpem@34 924 break;
philpem@34 925 case 0x0A0000: // Miscellaneous Control Register
philpem@35 926 break;
philpem@34 927 case 0x0B0000: // TM/DIALWR
philpem@35 928 break;
philpem@34 929 case 0x0C0000: // CSR
philpem@35 930 break;
philpem@34 931 case 0x0D0000: // DMA Address Register
philpem@35 932 break;
philpem@34 933 case 0x0E0000: // Disk Control Register
philpem@35 934 break;
philpem@34 935 case 0x0F0000: // Line Printer Data Register
philpem@34 936 break;
philpem@9 937 }
philpem@34 938 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@34 939 // I/O register space, zone B
philpem@36 940 // printf("WR16 0x%08X ==> 0x%04X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 941 switch (address & 0xF00000) {
philpem@34 942 case 0xC00000: // Expansion slots
philpem@34 943 case 0xD00000:
philpem@34 944 switch (address & 0xFC0000) {
philpem@34 945 case 0xC00000: // Expansion slot 0
philpem@34 946 case 0xC40000: // Expansion slot 1
philpem@34 947 case 0xC80000: // Expansion slot 2
philpem@34 948 case 0xCC0000: // Expansion slot 3
philpem@34 949 case 0xD00000: // Expansion slot 4
philpem@34 950 case 0xD40000: // Expansion slot 5
philpem@34 951 case 0xD80000: // Expansion slot 6
philpem@34 952 case 0xDC0000: // Expansion slot 7
philpem@34 953 fprintf(stderr, "NOTE: WR16 to expansion card space, addr=0x%08X, data=0x%04X\n", address, value);
philpem@34 954 break;
philpem@34 955 }
philpem@34 956 break;
philpem@34 957 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@34 958 case 0xF00000:
philpem@34 959 switch (address & 0x070000) {
philpem@34 960 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@34 961 break;
philpem@34 962 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@34 963 break;
philpem@34 964 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@34 965 break;
philpem@34 966 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@34 967 break;
philpem@35 968 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@35 969 switch (address & 0x077000) {
philpem@35 970 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@35 971 break;
philpem@35 972 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@35 973 break;
philpem@35 974 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@35 975 break;
philpem@35 976 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@35 977 state.romlmap = ((value & 0x8000) == 0x8000);
philpem@35 978 break;
philpem@35 979 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@35 980 break;
philpem@35 981 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@35 982 break;
philpem@35 983 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@35 984 break;
philpem@35 985 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@35 986 break;
philpem@35 987 }
philpem@35 988 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@35 989 break;
philpem@35 990 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@35 991 switch (address & 0x07F000) {
philpem@35 992 default:
philpem@35 993 break;
philpem@35 994 }
philpem@35 995 break;
philpem@35 996 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@35 997 break;
philpem@34 998 default:
philpem@35 999 fprintf(stderr, "NOTE: WR32 to undefined E/F-block space, addr=0x%08X, data=0x%08X\n", address, value);
philpem@34 1000 }
philpem@25 1001 }
philpem@7 1002 }
philpem@4 1003 }
philpem@4 1004
philpem@32 1005 /**
philpem@32 1006 * @brief Write M68K memory, 8-bit
philpem@32 1007 */
philpem@4 1008 void m68k_write_memory_8(uint32_t address, uint32_t value)
philpem@4 1009 {
philpem@7 1010 // If ROMLMAP is set, force system to access ROM
philpem@7 1011 if (!state.romlmap)
philpem@7 1012 address |= 0x800000;
philpem@7 1013
philpem@32 1014 // Check access permissions
philpem@37 1015 ACCESS_CHECK_WR(address, 8);
philpem@32 1016
philpem@9 1017 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@7 1018 // ROM access
philpem@34 1019 WR8(state.rom, address, ROM_SIZE - 1, value);
philpem@26 1020 } else if (address <= (state.ram_size - 1)) {
philpem@32 1021 // RAM access
philpem@34 1022 WR8(state.ram, mapAddr(address, false), state.ram_size - 1, value);
philpem@34 1023 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@34 1024 // I/O register space, zone A
philpem@36 1025 // printf("WR8 0x%08X ==> %02X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 1026 switch (address & 0x0F0000) {
philpem@34 1027 case 0x000000: // Map RAM access
philpem@34 1028 if (address > 0x4007FF) fprintf(stderr, "NOTE: WR8 to MapRAM mirror, addr=%08X, data=%02X\n", address, value);
philpem@34 1029 WR8(state.map, address, 0x7FF, value);
philpem@34 1030 break;
philpem@34 1031 case 0x010000: // General Status Register
philpem@34 1032 state.genstat = (value & 0xffff);
philpem@34 1033 break;
philpem@34 1034 case 0x020000: // Video RAM
philpem@34 1035 if (address > 0x427FFF) fprintf(stderr, "NOTE: WR8 to VideoRAM mirror, addr=%08X\n, data=0x%02X", address, value);
philpem@34 1036 WR8(state.vram, address, 0x7FFF, value);
philpem@34 1037 break;
philpem@34 1038 case 0x030000: // Bus Status Register 0
philpem@35 1039 break;
philpem@34 1040 case 0x040000: // Bus Status Register 1
philpem@35 1041 break;
philpem@34 1042 case 0x050000: // Phone status
philpem@35 1043 break;
philpem@34 1044 case 0x060000: // DMA Count
philpem@35 1045 break;
philpem@34 1046 case 0x070000: // Line Printer Status Register
philpem@35 1047 break;
philpem@34 1048 case 0x080000: // Real Time Clock
philpem@35 1049 break;
philpem@34 1050 case 0x090000: // Phone registers
philpem@34 1051 switch (address & 0x0FF000) {
philpem@34 1052 case 0x090000: // Handset relay
philpem@34 1053 case 0x098000:
philpem@35 1054 break;
philpem@34 1055 case 0x091000: // Line select 2
philpem@34 1056 case 0x099000:
philpem@35 1057 break;
philpem@34 1058 case 0x092000: // Hook relay 1
philpem@34 1059 case 0x09A000:
philpem@35 1060 break;
philpem@34 1061 case 0x093000: // Hook relay 2
philpem@34 1062 case 0x09B000:
philpem@35 1063 break;
philpem@34 1064 case 0x094000: // Line 1 hold
philpem@34 1065 case 0x09C000:
philpem@35 1066 break;
philpem@34 1067 case 0x095000: // Line 2 hold
philpem@34 1068 case 0x09D000:
philpem@35 1069 break;
philpem@34 1070 case 0x096000: // Line 1 A-lead
philpem@34 1071 case 0x09E000:
philpem@35 1072 break;
philpem@34 1073 case 0x097000: // Line 2 A-lead
philpem@34 1074 case 0x09F000:
philpem@34 1075 break;
philpem@34 1076 }
philpem@34 1077 break;
philpem@34 1078 case 0x0A0000: // Miscellaneous Control Register
philpem@35 1079 break;
philpem@34 1080 case 0x0B0000: // TM/DIALWR
philpem@35 1081 break;
philpem@34 1082 case 0x0C0000: // CSR
philpem@35 1083 break;
philpem@34 1084 case 0x0D0000: // DMA Address Register
philpem@35 1085 break;
philpem@34 1086 case 0x0E0000: // Disk Control Register
philpem@35 1087 break;
philpem@34 1088 case 0x0F0000: // Line Printer Data Register
philpem@34 1089 break;
philpem@34 1090 }
philpem@34 1091 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@34 1092 // I/O register space, zone B
philpem@36 1093 // printf("WR8 0x%08X ==> 0x%08X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 1094 switch (address & 0xF00000) {
philpem@34 1095 case 0xC00000: // Expansion slots
philpem@34 1096 case 0xD00000:
philpem@34 1097 switch (address & 0xFC0000) {
philpem@34 1098 case 0xC00000: // Expansion slot 0
philpem@34 1099 case 0xC40000: // Expansion slot 1
philpem@34 1100 case 0xC80000: // Expansion slot 2
philpem@34 1101 case 0xCC0000: // Expansion slot 3
philpem@34 1102 case 0xD00000: // Expansion slot 4
philpem@34 1103 case 0xD40000: // Expansion slot 5
philpem@34 1104 case 0xD80000: // Expansion slot 6
philpem@34 1105 case 0xDC0000: // Expansion slot 7
philpem@34 1106 fprintf(stderr, "NOTE: WR8 to expansion card space, addr=0x%08X, data=0x%08X\n", address, value);
philpem@34 1107 break;
philpem@34 1108 }
philpem@34 1109 break;
philpem@34 1110 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@34 1111 case 0xF00000:
philpem@34 1112 switch (address & 0x070000) {
philpem@34 1113 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@34 1114 break;
philpem@34 1115 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@34 1116 break;
philpem@34 1117 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@34 1118 break;
philpem@34 1119 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@34 1120 break;
philpem@35 1121 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@35 1122 switch (address & 0x077000) {
philpem@35 1123 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@35 1124 break;
philpem@35 1125 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@35 1126 break;
philpem@35 1127 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@35 1128 break;
philpem@35 1129 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@35 1130 if ((address & 1) == 0)
philpem@35 1131 state.romlmap = ((value & 0x8000) == 0x8000);
philpem@35 1132 break;
philpem@35 1133 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@35 1134 break;
philpem@35 1135 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@35 1136 break;
philpem@35 1137 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@35 1138 break;
philpem@35 1139 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@35 1140 break;
philpem@35 1141 }
philpem@35 1142 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@35 1143 break;
philpem@35 1144 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@35 1145 switch (address & 0x07F000) {
philpem@35 1146 default:
philpem@35 1147 break;
philpem@35 1148 }
philpem@35 1149 break;
philpem@35 1150 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@35 1151 break;
philpem@34 1152 default:
philpem@34 1153 fprintf(stderr, "NOTE: WR8 to undefined E/F-block space, addr=0x%08X, data=0x%08X\n", address, value);
philpem@35 1154 break;
philpem@34 1155 }
philpem@9 1156 }
philpem@7 1157 }
philpem@4 1158 }
philpem@4 1159
philpem@34 1160
philpem@10 1161 // for the disassembler
philpem@9 1162 uint32_t m68k_read_disassembler_32(uint32_t addr) { return m68k_read_memory_32(addr); }
philpem@9 1163 uint32_t m68k_read_disassembler_16(uint32_t addr) { return m68k_read_memory_16(addr); }
philpem@9 1164 uint32_t m68k_read_disassembler_8 (uint32_t addr) { return m68k_read_memory_8 (addr); }
philpem@9 1165
philpem@27 1166
philpem@27 1167 /****************************
philpem@27 1168 * blessed be thy main()...
philpem@27 1169 ****************************/
philpem@27 1170
philpem@0 1171 int main(void)
philpem@0 1172 {
philpem@7 1173 // copyright banner
philpem@16 1174 printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
philpem@17 1175 printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
philpem@17 1176 printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
philpem@16 1177 printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
philpem@16 1178 printf("Compiler: %s\n", VER_COMPILER);
philpem@16 1179 printf("CFLAGS: %s\n", VER_CFLAGS);
philpem@17 1180 printf("\n");
philpem@7 1181
philpem@7 1182 // set up system state
philpem@7 1183 // 512K of RAM
philpem@18 1184 state_init(512*1024);
philpem@7 1185
philpem@20 1186 // set up musashi and reset the CPU
philpem@7 1187 m68k_set_cpu_type(M68K_CPU_TYPE_68010);
philpem@7 1188 m68k_pulse_reset();
philpem@9 1189
philpem@28 1190 // Set up SDL
philpem@20 1191 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
philpem@20 1192 printf("Could not initialise SDL: %s.\n", SDL_GetError());
philpem@28 1193 exit(EXIT_FAILURE);
philpem@20 1194 }
philpem@7 1195
philpem@28 1196 // Make sure SDL cleans up after itself
philpem@28 1197 atexit(SDL_Quit);
philpem@28 1198
philpem@28 1199 // Set up the video display
philpem@28 1200 SDL_Surface *screen = NULL;
philpem@28 1201 if ((screen = SDL_SetVideoMode(720, 384, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) {
philpem@28 1202 printf("Could not find a suitable video mode: %s.\n", SDL_GetError());
philpem@28 1203 exit(EXIT_FAILURE);
philpem@28 1204 }
philpem@32 1205 printf("Set %dx%d at %d bits-per-pixel mode\n\n", screen->w, screen->h, screen->format->BitsPerPixel);
philpem@28 1206 SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee");
philpem@28 1207
philpem@20 1208 /***
philpem@20 1209 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
philpem@20 1210 * around 60Hz (???), with a 60Hz periodic interrupt.
philpem@20 1211 */
philpem@20 1212 const uint32_t TIMESLOT_FREQUENCY = 240; // Hz
philpem@20 1213 const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
philpem@20 1214 const uint32_t CLOCKS_PER_60HZ = (10e6 / 60);
philpem@20 1215 uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
philpem@20 1216 uint32_t clock_cycles = 0;
philpem@16 1217 bool exitEmu = false;
philpem@16 1218 for (;;) {
philpem@20 1219 // Run the CPU for however many cycles we need to. CPU core clock is
philpem@20 1220 // 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
philpem@20 1221 // 41667 cycles per timeslot.
philpem@20 1222 clock_cycles += m68k_execute(10e6/TIMESLOT_FREQUENCY);
philpem@20 1223
philpem@20 1224 // TODO: run DMA here
philpem@16 1225
philpem@20 1226 // Is it time to run the 60Hz periodic interrupt yet?
philpem@20 1227 if (clock_cycles > CLOCKS_PER_60HZ) {
philpem@20 1228 // TODO: refresh screen
philpem@20 1229 // TODO: trigger periodic interrupt (if enabled)
philpem@20 1230 // decrement clock cycle counter, we've handled the intr.
philpem@20 1231 clock_cycles -= CLOCKS_PER_60HZ;
philpem@16 1232 }
philpem@16 1233
philpem@20 1234 // make sure frame rate is equal to real time
philpem@20 1235 uint32_t now = SDL_GetTicks();
philpem@20 1236 if (now < next_timeslot) {
philpem@20 1237 // timeslot finished early -- eat up some time
philpem@20 1238 SDL_Delay(next_timeslot - now);
philpem@20 1239 } else {
philpem@20 1240 // timeslot finished late -- skip ahead to gain time
philpem@20 1241 // TODO: if this happens a lot, we should let the user know
philpem@20 1242 // that their PC might not be fast enough...
philpem@20 1243 next_timeslot = now;
philpem@20 1244 }
philpem@20 1245 // advance to the next timeslot
philpem@20 1246 next_timeslot += MILLISECS_PER_TIMESLOT;
philpem@20 1247
philpem@20 1248 // if we've been asked to exit the emulator, then do so.
philpem@16 1249 if (exitEmu) break;
philpem@16 1250 }
philpem@7 1251
philpem@7 1252 // shut down and exit
philpem@20 1253 SDL_Quit();
philpem@7 1254
philpem@0 1255 return 0;
philpem@0 1256 }