src/main.c

Thu, 02 Dec 2010 22:27:43 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Thu, 02 Dec 2010 22:27:43 +0000
changeset 38
b948045ca964
parent 37
87662afa1d98
child 39
cab49f90c3b9
permissions
-rw-r--r--

add proper tracking of unhandled R/W operations

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@38 236 // Logging macros
philpem@38 237 #define LOG_NOT_HANDLED_R(bits) \
philpem@38 238 do { \
philpem@38 239 if (!handled) \
philpem@38 240 printf("unhandled read%02d, addr=0x%08X\n", bits, address); \
philpem@38 241 } while (0);
philpem@38 242
philpem@38 243 #define LOG_NOT_HANDLED_W(bits) \
philpem@38 244 do { \
philpem@38 245 if (!handled) \
philpem@38 246 printf("unhandled write%02d, addr=0x%08X, data=0x%08X\n", bits, address, value); \
philpem@38 247 } while (0);
philpem@37 248
philpem@32 249 /**
philpem@32 250 * @brief Read M68K memory, 32-bit
philpem@32 251 */
philpem@4 252 uint32_t m68k_read_memory_32(uint32_t address)
philpem@4 253 {
philpem@9 254 uint32_t data = 0xFFFFFFFF;
philpem@38 255 bool handled = false;
philpem@9 256
philpem@7 257 // If ROMLMAP is set, force system to access ROM
philpem@7 258 if (!state.romlmap)
philpem@7 259 address |= 0x800000;
philpem@7 260
philpem@32 261 // Check access permissions
philpem@37 262 ACCESS_CHECK_RD(address, 32);
philpem@32 263
philpem@9 264 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@7 265 // ROM access
philpem@26 266 data = RD32(state.rom, address, ROM_SIZE - 1);
philpem@38 267 handled = true;
philpem@26 268 } else if (address <= (state.ram_size - 1)) {
philpem@32 269 // RAM access
philpem@32 270 data = RD32(state.ram, mapAddr(address, false), state.ram_size - 1);
philpem@38 271 handled = true;
philpem@34 272 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@34 273 // I/O register space, zone A
philpem@36 274 // printf("RD32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@34 275 switch (address & 0x0F0000) {
philpem@34 276 case 0x000000: // Map RAM access
philpem@34 277 if (address > 0x4007FF) fprintf(stderr, "NOTE: RD32 from MapRAM mirror, addr=0x%08X\n", address);
philpem@34 278 data = RD32(state.map, address, 0x7FF);
philpem@38 279 handled = true;
philpem@34 280 break;
philpem@34 281 case 0x010000: // General Status Register
philpem@34 282 data = ((uint32_t)state.genstat << 16) + (uint32_t)state.genstat;
philpem@38 283 handled = true;
philpem@34 284 break;
philpem@34 285 case 0x020000: // Video RAM
philpem@34 286 if (address > 0x427FFF) fprintf(stderr, "NOTE: RD32 from VideoRAM mirror, addr=0x%08X\n", address);
philpem@34 287 data = RD32(state.vram, address, 0x7FFF);
philpem@38 288 handled = true;
philpem@34 289 break;
philpem@34 290 case 0x030000: // Bus Status Register 0
philpem@38 291 data = ((uint32_t)state.bsr0 << 16) + (uint32_t)state.bsr0;
philpem@38 292 handled = true;
philpem@35 293 break;
philpem@34 294 case 0x040000: // Bus Status Register 1
philpem@38 295 data = ((uint32_t)state.bsr1 << 16) + (uint32_t)state.bsr1;
philpem@38 296 handled = true;
philpem@35 297 break;
philpem@34 298 case 0x050000: // Phone status
philpem@35 299 break;
philpem@34 300 case 0x060000: // DMA Count
philpem@35 301 break;
philpem@34 302 case 0x070000: // Line Printer Status Register
philpem@35 303 break;
philpem@34 304 case 0x080000: // Real Time Clock
philpem@35 305 break;
philpem@34 306 case 0x090000: // Phone registers
philpem@34 307 switch (address & 0x0FF000) {
philpem@34 308 case 0x090000: // Handset relay
philpem@34 309 case 0x098000:
philpem@35 310 break;
philpem@34 311 case 0x091000: // Line select 2
philpem@34 312 case 0x099000:
philpem@35 313 break;
philpem@34 314 case 0x092000: // Hook relay 1
philpem@34 315 case 0x09A000:
philpem@35 316 break;
philpem@34 317 case 0x093000: // Hook relay 2
philpem@34 318 case 0x09B000:
philpem@35 319 break;
philpem@34 320 case 0x094000: // Line 1 hold
philpem@34 321 case 0x09C000:
philpem@35 322 break;
philpem@34 323 case 0x095000: // Line 2 hold
philpem@34 324 case 0x09D000:
philpem@35 325 break;
philpem@34 326 case 0x096000: // Line 1 A-lead
philpem@34 327 case 0x09E000:
philpem@35 328 break;
philpem@34 329 case 0x097000: // Line 2 A-lead
philpem@34 330 case 0x09F000:
philpem@34 331 break;
philpem@34 332 }
philpem@34 333 break;
philpem@34 334 case 0x0A0000: // Miscellaneous Control Register
philpem@35 335 break;
philpem@34 336 case 0x0B0000: // TM/DIALWR
philpem@35 337 break;
philpem@34 338 case 0x0C0000: // CSR
philpem@35 339 break;
philpem@34 340 case 0x0D0000: // DMA Address Register
philpem@35 341 break;
philpem@34 342 case 0x0E0000: // Disk Control Register
philpem@35 343 break;
philpem@34 344 case 0x0F0000: // Line Printer Data Register
philpem@34 345 break;
philpem@34 346 }
philpem@34 347 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@34 348 // I/O register space, zone B
philpem@34 349 switch (address & 0xF00000) {
philpem@34 350 case 0xC00000: // Expansion slots
philpem@34 351 case 0xD00000:
philpem@34 352 switch (address & 0xFC0000) {
philpem@34 353 case 0xC00000: // Expansion slot 0
philpem@34 354 case 0xC40000: // Expansion slot 1
philpem@34 355 case 0xC80000: // Expansion slot 2
philpem@34 356 case 0xCC0000: // Expansion slot 3
philpem@34 357 case 0xD00000: // Expansion slot 4
philpem@34 358 case 0xD40000: // Expansion slot 5
philpem@34 359 case 0xD80000: // Expansion slot 6
philpem@34 360 case 0xDC0000: // Expansion slot 7
philpem@34 361 fprintf(stderr, "NOTE: RD32 from expansion card space, addr=0x%08X\n", address);
philpem@34 362 break;
philpem@34 363 }
philpem@34 364 break;
philpem@34 365 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@34 366 case 0xF00000:
philpem@34 367 switch (address & 0x070000) {
philpem@34 368 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@34 369 break;
philpem@34 370 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@34 371 break;
philpem@34 372 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@34 373 break;
philpem@34 374 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@34 375 break;
philpem@35 376 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@35 377 switch (address & 0x077000) {
philpem@35 378 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@35 379 break;
philpem@35 380 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@35 381 break;
philpem@35 382 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@35 383 break;
philpem@35 384 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@35 385 break;
philpem@35 386 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@35 387 break;
philpem@35 388 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@35 389 break;
philpem@35 390 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@35 391 break;
philpem@35 392 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@35 393 break;
philpem@35 394 }
philpem@38 395 break;
philpem@35 396 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@38 397 break;
philpem@35 398 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@35 399 switch (address & 0x07F000) {
philpem@35 400 default:
philpem@35 401 break;
philpem@35 402 }
philpem@35 403 break;
philpem@35 404 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@38 405 break;
philpem@34 406 }
philpem@34 407 }
philpem@7 408 }
philpem@38 409
philpem@38 410 LOG_NOT_HANDLED_R(32);
philpem@9 411 return data;
philpem@4 412 }
philpem@4 413
philpem@32 414 /**
philpem@32 415 * @brief Read M68K memory, 16-bit
philpem@32 416 */
philpem@4 417 uint32_t m68k_read_memory_16(uint32_t address)
philpem@4 418 {
philpem@9 419 uint16_t data = 0xFFFF;
philpem@38 420 bool handled = false;
philpem@9 421
philpem@9 422 // If ROMLMAP is set, force system to access ROM
philpem@9 423 if (!state.romlmap)
philpem@9 424 address |= 0x800000;
philpem@9 425
philpem@32 426 // Check access permissions
philpem@37 427 ACCESS_CHECK_RD(address, 16);
philpem@32 428
philpem@10 429 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@10 430 // ROM access
philpem@26 431 data = RD16(state.rom, address, ROM_SIZE - 1);
philpem@38 432 handled = true;
philpem@26 433 } else if (address <= (state.ram_size - 1)) {
philpem@32 434 // RAM access
philpem@32 435 data = RD16(state.ram, mapAddr(address, false), state.ram_size - 1);
philpem@38 436 handled = true;
philpem@34 437 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@34 438 // I/O register space, zone A
philpem@34 439 switch (address & 0x0F0000) {
philpem@34 440 case 0x000000: // Map RAM access
philpem@34 441 if (address > 0x4007FF) fprintf(stderr, "NOTE: RD16 from MapRAM mirror, addr=0x%08X\n", address);
philpem@34 442 data = RD16(state.map, address, 0x7FF);
philpem@38 443 handled = true;
philpem@34 444 break;
philpem@34 445 case 0x010000: // General Status Register
philpem@34 446 data = state.genstat;
philpem@38 447 handled = true;
philpem@34 448 break;
philpem@34 449 case 0x020000: // Video RAM
philpem@34 450 if (address > 0x427FFF) fprintf(stderr, "NOTE: RD16 from VideoRAM mirror, addr=0x%08X\n", address);
philpem@34 451 data = RD16(state.vram, address, 0x7FFF);
philpem@38 452 handled = true;
philpem@34 453 break;
philpem@34 454 case 0x030000: // Bus Status Register 0
philpem@38 455 data = state.bsr0;
philpem@38 456 handled = true;
philpem@35 457 break;
philpem@34 458 case 0x040000: // Bus Status Register 1
philpem@38 459 data = state.bsr1;
philpem@38 460 handled = true;
philpem@35 461 break;
philpem@34 462 case 0x050000: // Phone status
philpem@35 463 break;
philpem@34 464 case 0x060000: // DMA Count
philpem@35 465 break;
philpem@34 466 case 0x070000: // Line Printer Status Register
philpem@35 467 break;
philpem@34 468 case 0x080000: // Real Time Clock
philpem@35 469 break;
philpem@34 470 case 0x090000: // Phone registers
philpem@34 471 switch (address & 0x0FF000) {
philpem@34 472 case 0x090000: // Handset relay
philpem@34 473 case 0x098000:
philpem@35 474 break;
philpem@34 475 case 0x091000: // Line select 2
philpem@34 476 case 0x099000:
philpem@35 477 break;
philpem@34 478 case 0x092000: // Hook relay 1
philpem@34 479 case 0x09A000:
philpem@35 480 break;
philpem@34 481 case 0x093000: // Hook relay 2
philpem@34 482 case 0x09B000:
philpem@35 483 break;
philpem@34 484 case 0x094000: // Line 1 hold
philpem@34 485 case 0x09C000:
philpem@35 486 break;
philpem@34 487 case 0x095000: // Line 2 hold
philpem@34 488 case 0x09D000:
philpem@35 489 break;
philpem@34 490 case 0x096000: // Line 1 A-lead
philpem@34 491 case 0x09E000:
philpem@35 492 break;
philpem@34 493 case 0x097000: // Line 2 A-lead
philpem@34 494 case 0x09F000:
philpem@34 495 break;
philpem@34 496 }
philpem@34 497 break;
philpem@34 498 case 0x0A0000: // Miscellaneous Control Register
philpem@35 499 break;
philpem@34 500 case 0x0B0000: // TM/DIALWR
philpem@35 501 break;
philpem@34 502 case 0x0C0000: // CSR
philpem@35 503 break;
philpem@34 504 case 0x0D0000: // DMA Address Register
philpem@35 505 break;
philpem@34 506 case 0x0E0000: // Disk Control Register
philpem@35 507 break;
philpem@34 508 case 0x0F0000: // Line Printer Data Register
philpem@34 509 break;
philpem@34 510 }
philpem@34 511 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@34 512 // I/O register space, zone B
philpem@34 513 switch (address & 0xF00000) {
philpem@34 514 case 0xC00000: // Expansion slots
philpem@34 515 case 0xD00000:
philpem@34 516 switch (address & 0xFC0000) {
philpem@34 517 case 0xC00000: // Expansion slot 0
philpem@34 518 case 0xC40000: // Expansion slot 1
philpem@34 519 case 0xC80000: // Expansion slot 2
philpem@34 520 case 0xCC0000: // Expansion slot 3
philpem@34 521 case 0xD00000: // Expansion slot 4
philpem@34 522 case 0xD40000: // Expansion slot 5
philpem@34 523 case 0xD80000: // Expansion slot 6
philpem@34 524 case 0xDC0000: // Expansion slot 7
philpem@34 525 fprintf(stderr, "NOTE: RD16 from expansion card space, addr=0x%08X\n", address);
philpem@34 526 break;
philpem@34 527 }
philpem@34 528 break;
philpem@34 529 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@34 530 case 0xF00000:
philpem@34 531 switch (address & 0x070000) {
philpem@34 532 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@34 533 break;
philpem@34 534 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@34 535 break;
philpem@34 536 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@34 537 break;
philpem@34 538 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@34 539 break;
philpem@35 540 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@35 541 switch (address & 0x077000) {
philpem@35 542 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@35 543 break;
philpem@35 544 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@35 545 break;
philpem@35 546 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@35 547 break;
philpem@35 548 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@35 549 break;
philpem@35 550 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@35 551 break;
philpem@35 552 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@35 553 break;
philpem@35 554 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@35 555 break;
philpem@35 556 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@35 557 break;
philpem@35 558 }
philpem@38 559 break;
philpem@35 560 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@38 561 break;
philpem@35 562 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@35 563 switch (address & 0x07F000) {
philpem@35 564 default:
philpem@35 565 break;
philpem@35 566 }
philpem@35 567 break;
philpem@35 568 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@38 569 break;
philpem@34 570 }
philpem@34 571 }
philpem@10 572 }
philpem@38 573
philpem@38 574 LOG_NOT_HANDLED_R(32);
philpem@9 575 return data;
philpem@4 576 }
philpem@4 577
philpem@32 578 /**
philpem@32 579 * @brief Read M68K memory, 8-bit
philpem@32 580 */
philpem@4 581 uint32_t m68k_read_memory_8(uint32_t address)
philpem@4 582 {
philpem@9 583 uint8_t data = 0xFF;
philpem@38 584 bool handled = false;
philpem@9 585
philpem@7 586 // If ROMLMAP is set, force system to access ROM
philpem@7 587 if (!state.romlmap)
philpem@7 588 address |= 0x800000;
philpem@7 589
philpem@32 590 // Check access permissions
philpem@37 591 ACCESS_CHECK_RD(address, 8);
philpem@32 592
philpem@10 593 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@10 594 // ROM access
philpem@26 595 data = RD8(state.rom, address, ROM_SIZE - 1);
philpem@38 596 handled = true;
philpem@26 597 } else if (address <= (state.ram_size - 1)) {
philpem@32 598 // RAM access
philpem@32 599 data = RD8(state.ram, mapAddr(address, false), state.ram_size - 1);
philpem@38 600 handled = true;
philpem@34 601 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@34 602 // I/O register space, zone A
philpem@34 603 switch (address & 0x0F0000) {
philpem@34 604 case 0x000000: // Map RAM access
philpem@34 605 if (address > 0x4007FF) fprintf(stderr, "NOTE: RD8 from MapRAM mirror, addr=0x%08X\n", address);
philpem@34 606 data = RD8(state.map, address, 0x7FF);
philpem@38 607 handled = true;
philpem@34 608 break;
philpem@34 609 case 0x010000: // General Status Register
philpem@34 610 if ((address & 1) == 0)
philpem@34 611 data = (state.genstat >> 8) & 0xff;
philpem@34 612 else
philpem@34 613 data = (state.genstat) & 0xff;
philpem@38 614 handled = true;
philpem@34 615 break;
philpem@34 616 case 0x020000: // Video RAM
philpem@34 617 if (address > 0x427FFF) fprintf(stderr, "NOTE: RD8 from VideoRAM mirror, addr=0x%08X\n", address);
philpem@34 618 data = RD8(state.vram, address, 0x7FFF);
philpem@38 619 handled = true;
philpem@38 620 break;
philpem@38 621 case 0x030000: // Bus Status Register 0
philpem@38 622 if ((address & 1) == 0)
philpem@38 623 data = (state.bsr0 >> 8) & 0xff;
philpem@38 624 else
philpem@38 625 data = (state.bsr0) & 0xff;
philpem@38 626 handled = true;
philpem@38 627 break;
philpem@38 628 case 0x040000: // Bus Status Register 1
philpem@38 629 if ((address & 1) == 0)
philpem@38 630 data = (state.bsr1 >> 8) & 0xff;
philpem@38 631 else
philpem@38 632 data = (state.bsr1) & 0xff;
philpem@38 633 handled = true;
philpem@38 634 break;
philpem@38 635 case 0x050000: // Phone status
philpem@38 636 break;
philpem@38 637 case 0x060000: // DMA Count
philpem@38 638 break;
philpem@38 639 case 0x070000: // Line Printer Status Register
philpem@38 640 break;
philpem@38 641 case 0x080000: // Real Time Clock
philpem@38 642 break;
philpem@38 643 case 0x090000: // Phone registers
philpem@38 644 switch (address & 0x0FF000) {
philpem@38 645 case 0x090000: // Handset relay
philpem@38 646 case 0x098000:
philpem@38 647 break;
philpem@38 648 case 0x091000: // Line select 2
philpem@38 649 case 0x099000:
philpem@38 650 break;
philpem@38 651 case 0x092000: // Hook relay 1
philpem@38 652 case 0x09A000:
philpem@38 653 break;
philpem@38 654 case 0x093000: // Hook relay 2
philpem@38 655 case 0x09B000:
philpem@38 656 break;
philpem@38 657 case 0x094000: // Line 1 hold
philpem@38 658 case 0x09C000:
philpem@38 659 break;
philpem@38 660 case 0x095000: // Line 2 hold
philpem@38 661 case 0x09D000:
philpem@38 662 break;
philpem@38 663 case 0x096000: // Line 1 A-lead
philpem@38 664 case 0x09E000:
philpem@38 665 break;
philpem@38 666 case 0x097000: // Line 2 A-lead
philpem@38 667 case 0x09F000:
philpem@38 668 break;
philpem@38 669 }
philpem@38 670 break;
philpem@38 671 case 0x0A0000: // Miscellaneous Control Register
philpem@38 672 break;
philpem@38 673 case 0x0B0000: // TM/DIALWR
philpem@38 674 break;
philpem@38 675 case 0x0C0000: // CSR
philpem@38 676 break;
philpem@38 677 case 0x0D0000: // DMA Address Register
philpem@38 678 break;
philpem@38 679 case 0x0E0000: // Disk Control Register
philpem@38 680 break;
philpem@38 681 case 0x0F0000: // Line Printer Data Register
philpem@38 682 break;
philpem@38 683 }
philpem@38 684 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@38 685 // I/O register space, zone B
philpem@38 686 switch (address & 0xF00000) {
philpem@38 687 case 0xC00000: // Expansion slots
philpem@38 688 case 0xD00000:
philpem@38 689 switch (address & 0xFC0000) {
philpem@38 690 case 0xC00000: // Expansion slot 0
philpem@38 691 case 0xC40000: // Expansion slot 1
philpem@38 692 case 0xC80000: // Expansion slot 2
philpem@38 693 case 0xCC0000: // Expansion slot 3
philpem@38 694 case 0xD00000: // Expansion slot 4
philpem@38 695 case 0xD40000: // Expansion slot 5
philpem@38 696 case 0xD80000: // Expansion slot 6
philpem@38 697 case 0xDC0000: // Expansion slot 7
philpem@38 698 fprintf(stderr, "NOTE: RD8 from expansion card space, addr=0x%08X\n", address);
philpem@38 699 break;
philpem@38 700 }
philpem@38 701 break;
philpem@38 702 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@38 703 case 0xF00000:
philpem@38 704 switch (address & 0x070000) {
philpem@38 705 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@38 706 break;
philpem@38 707 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@38 708 break;
philpem@38 709 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@38 710 break;
philpem@38 711 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@38 712 break;
philpem@38 713 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@38 714 switch (address & 0x077000) {
philpem@38 715 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@38 716 break;
philpem@38 717 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@38 718 break;
philpem@38 719 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@38 720 break;
philpem@38 721 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@38 722 break;
philpem@38 723 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@38 724 break;
philpem@38 725 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@38 726 break;
philpem@38 727 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@38 728 break;
philpem@38 729 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@38 730 break;
philpem@38 731 }
philpem@38 732 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@38 733 break;
philpem@38 734 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@38 735 switch (address & 0x07F000) {
philpem@38 736 default:
philpem@38 737 break;
philpem@38 738 }
philpem@38 739 break;
philpem@38 740 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@38 741 break;
philpem@38 742 }
philpem@38 743 }
philpem@38 744 }
philpem@38 745
philpem@38 746 LOG_NOT_HANDLED_R(8);
philpem@38 747
philpem@38 748 return data;
philpem@38 749 }
philpem@38 750
philpem@38 751 /**
philpem@38 752 * @brief Write M68K memory, 32-bit
philpem@38 753 */
philpem@38 754 void m68k_write_memory_32(uint32_t address, uint32_t value)
philpem@38 755 {
philpem@38 756 bool handled = false;
philpem@38 757
philpem@38 758 // If ROMLMAP is set, force system to access ROM
philpem@38 759 if (!state.romlmap)
philpem@38 760 address |= 0x800000;
philpem@38 761
philpem@38 762 // Check access permissions
philpem@38 763 ACCESS_CHECK_WR(address, 32);
philpem@38 764
philpem@38 765 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@38 766 // ROM access
philpem@38 767 handled = true;
philpem@38 768 } else if (address <= (state.ram_size - 1)) {
philpem@38 769 // RAM access
philpem@38 770 WR32(state.ram, mapAddr(address, false), state.ram_size - 1, value);
philpem@38 771 handled = true;
philpem@38 772 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@38 773 // I/O register space, zone A
philpem@38 774 switch (address & 0x0F0000) {
philpem@38 775 case 0x000000: // Map RAM access
philpem@38 776 if (address > 0x4007FF) fprintf(stderr, "NOTE: WR32 to MapRAM mirror, addr=0x%08X, data=0x%08X\n", address, value);
philpem@38 777 WR32(state.map, address, 0x7FF, value);
philpem@38 778 handled = true;
philpem@38 779 break;
philpem@38 780 case 0x010000: // General Status Register
philpem@38 781 state.genstat = (value & 0xffff);
philpem@38 782 handled = true;
philpem@38 783 break;
philpem@38 784 case 0x020000: // Video RAM
philpem@38 785 if (address > 0x427FFF) fprintf(stderr, "NOTE: WR32 to VideoRAM mirror, addr=0x%08X, data=0x%08X\n", address, value);
philpem@38 786 WR32(state.vram, address, 0x7FFF, value);
philpem@38 787 handled = true;
philpem@34 788 break;
philpem@34 789 case 0x030000: // Bus Status Register 0
philpem@35 790 break;
philpem@34 791 case 0x040000: // Bus Status Register 1
philpem@35 792 break;
philpem@34 793 case 0x050000: // Phone status
philpem@35 794 break;
philpem@34 795 case 0x060000: // DMA Count
philpem@35 796 break;
philpem@34 797 case 0x070000: // Line Printer Status Register
philpem@35 798 break;
philpem@34 799 case 0x080000: // Real Time Clock
philpem@35 800 break;
philpem@34 801 case 0x090000: // Phone registers
philpem@34 802 switch (address & 0x0FF000) {
philpem@34 803 case 0x090000: // Handset relay
philpem@34 804 case 0x098000:
philpem@35 805 break;
philpem@34 806 case 0x091000: // Line select 2
philpem@34 807 case 0x099000:
philpem@35 808 break;
philpem@34 809 case 0x092000: // Hook relay 1
philpem@34 810 case 0x09A000:
philpem@35 811 break;
philpem@34 812 case 0x093000: // Hook relay 2
philpem@34 813 case 0x09B000:
philpem@35 814 break;
philpem@34 815 case 0x094000: // Line 1 hold
philpem@34 816 case 0x09C000:
philpem@35 817 break;
philpem@34 818 case 0x095000: // Line 2 hold
philpem@34 819 case 0x09D000:
philpem@35 820 break;
philpem@34 821 case 0x096000: // Line 1 A-lead
philpem@34 822 case 0x09E000:
philpem@35 823 break;
philpem@34 824 case 0x097000: // Line 2 A-lead
philpem@34 825 case 0x09F000:
philpem@34 826 break;
philpem@34 827 }
philpem@34 828 break;
philpem@34 829 case 0x0A0000: // Miscellaneous Control Register
philpem@35 830 break;
philpem@34 831 case 0x0B0000: // TM/DIALWR
philpem@35 832 break;
philpem@34 833 case 0x0C0000: // CSR
philpem@35 834 break;
philpem@34 835 case 0x0D0000: // DMA Address Register
philpem@35 836 break;
philpem@34 837 case 0x0E0000: // Disk Control Register
philpem@35 838 break;
philpem@34 839 case 0x0F0000: // Line Printer Data Register
philpem@34 840 break;
philpem@34 841 }
philpem@34 842 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@34 843 // I/O register space, zone B
philpem@34 844 switch (address & 0xF00000) {
philpem@34 845 case 0xC00000: // Expansion slots
philpem@34 846 case 0xD00000:
philpem@34 847 switch (address & 0xFC0000) {
philpem@34 848 case 0xC00000: // Expansion slot 0
philpem@34 849 case 0xC40000: // Expansion slot 1
philpem@34 850 case 0xC80000: // Expansion slot 2
philpem@34 851 case 0xCC0000: // Expansion slot 3
philpem@34 852 case 0xD00000: // Expansion slot 4
philpem@34 853 case 0xD40000: // Expansion slot 5
philpem@34 854 case 0xD80000: // Expansion slot 6
philpem@34 855 case 0xDC0000: // Expansion slot 7
philpem@38 856 fprintf(stderr, "NOTE: WR32 to expansion card space, addr=0x%08X, data=0x%08X\n", address, value);
philpem@38 857 handled = true;
philpem@34 858 break;
philpem@34 859 }
philpem@34 860 break;
philpem@34 861 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@34 862 case 0xF00000:
philpem@34 863 switch (address & 0x070000) {
philpem@34 864 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@34 865 break;
philpem@34 866 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@34 867 break;
philpem@34 868 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@34 869 break;
philpem@34 870 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@34 871 break;
philpem@35 872 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@35 873 switch (address & 0x077000) {
philpem@35 874 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@35 875 break;
philpem@35 876 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@35 877 break;
philpem@35 878 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@35 879 break;
philpem@35 880 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@38 881 state.romlmap = ((value & 0x8000) == 0x8000);
philpem@35 882 break;
philpem@35 883 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@35 884 break;
philpem@35 885 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@35 886 break;
philpem@35 887 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@35 888 break;
philpem@35 889 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@35 890 break;
philpem@35 891 }
philpem@35 892 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@38 893 break;
philpem@35 894 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@35 895 switch (address & 0x07F000) {
philpem@35 896 default:
philpem@35 897 break;
philpem@35 898 }
philpem@35 899 break;
philpem@35 900 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@38 901 break;
philpem@34 902 }
philpem@34 903 }
philpem@10 904 }
philpem@38 905
philpem@38 906 LOG_NOT_HANDLED_W(32);
philpem@4 907 }
philpem@4 908
philpem@32 909 /**
philpem@38 910 * @brief Write M68K memory, 16-bit
philpem@32 911 */
philpem@38 912 void m68k_write_memory_16(uint32_t address, uint32_t value)
philpem@4 913 {
philpem@38 914 bool handled = false;
philpem@38 915
philpem@7 916 // If ROMLMAP is set, force system to access ROM
philpem@7 917 if (!state.romlmap)
philpem@7 918 address |= 0x800000;
philpem@7 919
philpem@32 920 // Check access permissions
philpem@38 921 ACCESS_CHECK_WR(address, 16);
philpem@32 922
philpem@9 923 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@7 924 // ROM access
philpem@38 925 handled = true;
philpem@26 926 } else if (address <= (state.ram_size - 1)) {
philpem@32 927 // RAM access
philpem@38 928 WR16(state.ram, mapAddr(address, false), state.ram_size - 1, value);
philpem@38 929 handled = true;
philpem@34 930 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@34 931 // I/O register space, zone A
philpem@34 932 switch (address & 0x0F0000) {
philpem@34 933 case 0x000000: // Map RAM access
philpem@38 934 if (address > 0x4007FF) fprintf(stderr, "NOTE: WR16 to MapRAM mirror, addr=0x%08X, data=0x%04X\n", address, value);
philpem@38 935 WR16(state.map, address, 0x7FF, value);
philpem@38 936 handled = true;
philpem@34 937 break;
philpem@38 938 case 0x010000: // General Status Register (read only)
philpem@38 939 handled = true;
philpem@34 940 break;
philpem@34 941 case 0x020000: // Video RAM
philpem@38 942 if (address > 0x427FFF) fprintf(stderr, "NOTE: WR16 to VideoRAM mirror, addr=0x%08X, data=0x%04X\n", address, value);
philpem@38 943 WR16(state.vram, address, 0x7FFF, value);
philpem@38 944 handled = true;
philpem@34 945 break;
philpem@38 946 case 0x030000: // Bus Status Register 0 (read only)
philpem@38 947 handled = true;
philpem@35 948 break;
philpem@38 949 case 0x040000: // Bus Status Register 1 (read only)
philpem@38 950 handled = true;
philpem@35 951 break;
philpem@34 952 case 0x050000: // Phone status
philpem@35 953 break;
philpem@34 954 case 0x060000: // DMA Count
philpem@35 955 break;
philpem@34 956 case 0x070000: // Line Printer Status Register
philpem@35 957 break;
philpem@34 958 case 0x080000: // Real Time Clock
philpem@35 959 break;
philpem@34 960 case 0x090000: // Phone registers
philpem@34 961 switch (address & 0x0FF000) {
philpem@34 962 case 0x090000: // Handset relay
philpem@34 963 case 0x098000:
philpem@35 964 break;
philpem@34 965 case 0x091000: // Line select 2
philpem@34 966 case 0x099000:
philpem@35 967 break;
philpem@34 968 case 0x092000: // Hook relay 1
philpem@34 969 case 0x09A000:
philpem@35 970 break;
philpem@34 971 case 0x093000: // Hook relay 2
philpem@34 972 case 0x09B000:
philpem@35 973 break;
philpem@34 974 case 0x094000: // Line 1 hold
philpem@34 975 case 0x09C000:
philpem@35 976 break;
philpem@34 977 case 0x095000: // Line 2 hold
philpem@34 978 case 0x09D000:
philpem@35 979 break;
philpem@34 980 case 0x096000: // Line 1 A-lead
philpem@34 981 case 0x09E000:
philpem@35 982 break;
philpem@34 983 case 0x097000: // Line 2 A-lead
philpem@34 984 case 0x09F000:
philpem@34 985 break;
philpem@34 986 }
philpem@34 987 break;
philpem@34 988 case 0x0A0000: // Miscellaneous Control Register
philpem@35 989 break;
philpem@34 990 case 0x0B0000: // TM/DIALWR
philpem@35 991 break;
philpem@34 992 case 0x0C0000: // CSR
philpem@35 993 break;
philpem@34 994 case 0x0D0000: // DMA Address Register
philpem@35 995 break;
philpem@34 996 case 0x0E0000: // Disk Control Register
philpem@35 997 break;
philpem@34 998 case 0x0F0000: // Line Printer Data Register
philpem@34 999 break;
philpem@34 1000 }
philpem@34 1001 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@34 1002 // I/O register space, zone B
philpem@34 1003 switch (address & 0xF00000) {
philpem@34 1004 case 0xC00000: // Expansion slots
philpem@34 1005 case 0xD00000:
philpem@34 1006 switch (address & 0xFC0000) {
philpem@34 1007 case 0xC00000: // Expansion slot 0
philpem@34 1008 case 0xC40000: // Expansion slot 1
philpem@34 1009 case 0xC80000: // Expansion slot 2
philpem@34 1010 case 0xCC0000: // Expansion slot 3
philpem@34 1011 case 0xD00000: // Expansion slot 4
philpem@34 1012 case 0xD40000: // Expansion slot 5
philpem@34 1013 case 0xD80000: // Expansion slot 6
philpem@34 1014 case 0xDC0000: // Expansion slot 7
philpem@38 1015 fprintf(stderr, "NOTE: WR16 to expansion card space, addr=0x%08X, data=0x%04X\n", address, value);
philpem@34 1016 break;
philpem@34 1017 }
philpem@34 1018 break;
philpem@34 1019 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@34 1020 case 0xF00000:
philpem@34 1021 switch (address & 0x070000) {
philpem@34 1022 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@34 1023 break;
philpem@34 1024 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@34 1025 break;
philpem@34 1026 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@34 1027 break;
philpem@34 1028 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@34 1029 break;
philpem@35 1030 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@35 1031 switch (address & 0x077000) {
philpem@35 1032 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@35 1033 break;
philpem@35 1034 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@35 1035 break;
philpem@35 1036 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@35 1037 break;
philpem@35 1038 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@35 1039 state.romlmap = ((value & 0x8000) == 0x8000);
philpem@38 1040 handled = true;
philpem@35 1041 break;
philpem@35 1042 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@35 1043 break;
philpem@35 1044 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@35 1045 break;
philpem@35 1046 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@35 1047 break;
philpem@35 1048 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@35 1049 break;
philpem@35 1050 }
philpem@35 1051 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@35 1052 break;
philpem@35 1053 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@35 1054 switch (address & 0x07F000) {
philpem@35 1055 default:
philpem@35 1056 break;
philpem@35 1057 }
philpem@35 1058 break;
philpem@35 1059 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@35 1060 break;
philpem@34 1061 }
philpem@9 1062 }
philpem@7 1063 }
philpem@38 1064
philpem@38 1065 LOG_NOT_HANDLED_W(16);
philpem@4 1066 }
philpem@4 1067
philpem@32 1068 /**
philpem@38 1069 * @brief Write M68K memory, 8-bit
philpem@32 1070 */
philpem@38 1071 void m68k_write_memory_8(uint32_t address, uint32_t value)
philpem@4 1072 {
philpem@38 1073 bool handled = false;
philpem@38 1074
philpem@7 1075 // If ROMLMAP is set, force system to access ROM
philpem@7 1076 if (!state.romlmap)
philpem@7 1077 address |= 0x800000;
philpem@7 1078
philpem@32 1079 // Check access permissions
philpem@38 1080 ACCESS_CHECK_WR(address, 8);
philpem@32 1081
philpem@9 1082 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@38 1083 // ROM access (read only!)
philpem@38 1084 handled = true;
philpem@26 1085 } else if (address <= (state.ram_size - 1)) {
philpem@32 1086 // RAM access
philpem@38 1087 WR8(state.ram, mapAddr(address, false), state.ram_size - 1, value);
philpem@38 1088 handled = true;
philpem@34 1089 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
philpem@34 1090 // I/O register space, zone A
philpem@34 1091 switch (address & 0x0F0000) {
philpem@34 1092 case 0x000000: // Map RAM access
philpem@38 1093 if (address > 0x4007FF) fprintf(stderr, "NOTE: WR8 to MapRAM mirror, addr=%08X, data=%02X\n", address, value);
philpem@38 1094 WR8(state.map, address, 0x7FF, value);
philpem@38 1095 handled = true;
philpem@34 1096 break;
philpem@34 1097 case 0x010000: // General Status Register
philpem@38 1098 handled = true;
philpem@34 1099 break;
philpem@34 1100 case 0x020000: // Video RAM
philpem@38 1101 if (address > 0x427FFF) fprintf(stderr, "NOTE: WR8 to VideoRAM mirror, addr=%08X\n, data=0x%02X", address, value);
philpem@38 1102 WR8(state.vram, address, 0x7FFF, value);
philpem@38 1103 handled = true;
philpem@34 1104 break;
philpem@34 1105 case 0x030000: // Bus Status Register 0
philpem@38 1106 handled = true;
philpem@35 1107 break;
philpem@34 1108 case 0x040000: // Bus Status Register 1
philpem@38 1109 handled = true;
philpem@35 1110 break;
philpem@34 1111 case 0x050000: // Phone status
philpem@35 1112 break;
philpem@34 1113 case 0x060000: // DMA Count
philpem@35 1114 break;
philpem@34 1115 case 0x070000: // Line Printer Status Register
philpem@35 1116 break;
philpem@34 1117 case 0x080000: // Real Time Clock
philpem@35 1118 break;
philpem@34 1119 case 0x090000: // Phone registers
philpem@34 1120 switch (address & 0x0FF000) {
philpem@34 1121 case 0x090000: // Handset relay
philpem@34 1122 case 0x098000:
philpem@35 1123 break;
philpem@34 1124 case 0x091000: // Line select 2
philpem@34 1125 case 0x099000:
philpem@35 1126 break;
philpem@34 1127 case 0x092000: // Hook relay 1
philpem@34 1128 case 0x09A000:
philpem@35 1129 break;
philpem@34 1130 case 0x093000: // Hook relay 2
philpem@34 1131 case 0x09B000:
philpem@35 1132 break;
philpem@34 1133 case 0x094000: // Line 1 hold
philpem@34 1134 case 0x09C000:
philpem@35 1135 break;
philpem@34 1136 case 0x095000: // Line 2 hold
philpem@34 1137 case 0x09D000:
philpem@35 1138 break;
philpem@34 1139 case 0x096000: // Line 1 A-lead
philpem@34 1140 case 0x09E000:
philpem@35 1141 break;
philpem@34 1142 case 0x097000: // Line 2 A-lead
philpem@34 1143 case 0x09F000:
philpem@34 1144 break;
philpem@34 1145 }
philpem@34 1146 break;
philpem@34 1147 case 0x0A0000: // Miscellaneous Control Register
philpem@35 1148 break;
philpem@34 1149 case 0x0B0000: // TM/DIALWR
philpem@35 1150 break;
philpem@34 1151 case 0x0C0000: // CSR
philpem@35 1152 break;
philpem@34 1153 case 0x0D0000: // DMA Address Register
philpem@35 1154 break;
philpem@34 1155 case 0x0E0000: // Disk Control Register
philpem@35 1156 break;
philpem@34 1157 case 0x0F0000: // Line Printer Data Register
philpem@34 1158 break;
philpem@9 1159 }
philpem@34 1160 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
philpem@34 1161 // I/O register space, zone B
philpem@34 1162 switch (address & 0xF00000) {
philpem@34 1163 case 0xC00000: // Expansion slots
philpem@34 1164 case 0xD00000:
philpem@34 1165 switch (address & 0xFC0000) {
philpem@34 1166 case 0xC00000: // Expansion slot 0
philpem@34 1167 case 0xC40000: // Expansion slot 1
philpem@34 1168 case 0xC80000: // Expansion slot 2
philpem@34 1169 case 0xCC0000: // Expansion slot 3
philpem@34 1170 case 0xD00000: // Expansion slot 4
philpem@34 1171 case 0xD40000: // Expansion slot 5
philpem@34 1172 case 0xD80000: // Expansion slot 6
philpem@34 1173 case 0xDC0000: // Expansion slot 7
philpem@34 1174 fprintf(stderr, "NOTE: WR8 to expansion card space, addr=0x%08X, data=0x%08X\n", address, value);
philpem@34 1175 break;
philpem@34 1176 }
philpem@34 1177 break;
philpem@34 1178 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
philpem@34 1179 case 0xF00000:
philpem@34 1180 switch (address & 0x070000) {
philpem@34 1181 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
philpem@34 1182 break;
philpem@34 1183 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
philpem@34 1184 break;
philpem@34 1185 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
philpem@34 1186 break;
philpem@34 1187 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
philpem@34 1188 break;
philpem@35 1189 case 0x040000: // [ef][4c]xxxx ==> General Control Register
philpem@35 1190 switch (address & 0x077000) {
philpem@35 1191 case 0x040000: // [ef][4c][08]xxx ==> EE
philpem@35 1192 break;
philpem@35 1193 case 0x041000: // [ef][4c][19]xxx ==> P1E
philpem@35 1194 break;
philpem@35 1195 case 0x042000: // [ef][4c][2A]xxx ==> BP
philpem@35 1196 break;
philpem@35 1197 case 0x043000: // [ef][4c][3B]xxx ==> ROMLMAP
philpem@35 1198 if ((address & 1) == 0)
philpem@38 1199 state.romlmap = ((value & 0x80) == 0x80);
philpem@38 1200 handled = true;
philpem@35 1201 break;
philpem@35 1202 case 0x044000: // [ef][4c][4C]xxx ==> L1 MODEM
philpem@35 1203 break;
philpem@35 1204 case 0x045000: // [ef][4c][5D]xxx ==> L2 MODEM
philpem@35 1205 break;
philpem@35 1206 case 0x046000: // [ef][4c][6E]xxx ==> D/N CONNECT
philpem@35 1207 break;
philpem@35 1208 case 0x047000: // [ef][4c][7F]xxx ==> Whole screen reverse video
philpem@35 1209 break;
philpem@35 1210 }
philpem@35 1211 case 0x050000: // [ef][5d]xxxx ==> 8274
philpem@35 1212 break;
philpem@35 1213 case 0x060000: // [ef][6e]xxxx ==> Control regs
philpem@35 1214 switch (address & 0x07F000) {
philpem@35 1215 default:
philpem@35 1216 break;
philpem@35 1217 }
philpem@35 1218 break;
philpem@35 1219 case 0x070000: // [ef][7f]xxxx ==> 6850 Keyboard Controller
philpem@35 1220 break;
philpem@34 1221 default:
philpem@34 1222 fprintf(stderr, "NOTE: WR8 to undefined E/F-block space, addr=0x%08X, data=0x%08X\n", address, value);
philpem@35 1223 break;
philpem@34 1224 }
philpem@9 1225 }
philpem@7 1226 }
philpem@38 1227
philpem@38 1228 LOG_NOT_HANDLED_W(8);
philpem@4 1229 }
philpem@4 1230
philpem@34 1231
philpem@10 1232 // for the disassembler
philpem@9 1233 uint32_t m68k_read_disassembler_32(uint32_t addr) { return m68k_read_memory_32(addr); }
philpem@9 1234 uint32_t m68k_read_disassembler_16(uint32_t addr) { return m68k_read_memory_16(addr); }
philpem@9 1235 uint32_t m68k_read_disassembler_8 (uint32_t addr) { return m68k_read_memory_8 (addr); }
philpem@9 1236
philpem@27 1237
philpem@27 1238 /****************************
philpem@27 1239 * blessed be thy main()...
philpem@27 1240 ****************************/
philpem@27 1241
philpem@0 1242 int main(void)
philpem@0 1243 {
philpem@7 1244 // copyright banner
philpem@16 1245 printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
philpem@17 1246 printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
philpem@17 1247 printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
philpem@16 1248 printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
philpem@16 1249 printf("Compiler: %s\n", VER_COMPILER);
philpem@16 1250 printf("CFLAGS: %s\n", VER_CFLAGS);
philpem@17 1251 printf("\n");
philpem@7 1252
philpem@7 1253 // set up system state
philpem@7 1254 // 512K of RAM
philpem@18 1255 state_init(512*1024);
philpem@7 1256
philpem@20 1257 // set up musashi and reset the CPU
philpem@7 1258 m68k_set_cpu_type(M68K_CPU_TYPE_68010);
philpem@7 1259 m68k_pulse_reset();
philpem@9 1260
philpem@28 1261 // Set up SDL
philpem@20 1262 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
philpem@20 1263 printf("Could not initialise SDL: %s.\n", SDL_GetError());
philpem@28 1264 exit(EXIT_FAILURE);
philpem@20 1265 }
philpem@7 1266
philpem@28 1267 // Make sure SDL cleans up after itself
philpem@28 1268 atexit(SDL_Quit);
philpem@28 1269
philpem@28 1270 // Set up the video display
philpem@28 1271 SDL_Surface *screen = NULL;
philpem@28 1272 if ((screen = SDL_SetVideoMode(720, 384, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) {
philpem@28 1273 printf("Could not find a suitable video mode: %s.\n", SDL_GetError());
philpem@28 1274 exit(EXIT_FAILURE);
philpem@28 1275 }
philpem@32 1276 printf("Set %dx%d at %d bits-per-pixel mode\n\n", screen->w, screen->h, screen->format->BitsPerPixel);
philpem@28 1277 SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee");
philpem@28 1278
philpem@20 1279 /***
philpem@20 1280 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
philpem@20 1281 * around 60Hz (???), with a 60Hz periodic interrupt.
philpem@20 1282 */
philpem@20 1283 const uint32_t TIMESLOT_FREQUENCY = 240; // Hz
philpem@20 1284 const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
philpem@20 1285 const uint32_t CLOCKS_PER_60HZ = (10e6 / 60);
philpem@20 1286 uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
philpem@20 1287 uint32_t clock_cycles = 0;
philpem@16 1288 bool exitEmu = false;
philpem@16 1289 for (;;) {
philpem@20 1290 // Run the CPU for however many cycles we need to. CPU core clock is
philpem@20 1291 // 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
philpem@20 1292 // 41667 cycles per timeslot.
philpem@20 1293 clock_cycles += m68k_execute(10e6/TIMESLOT_FREQUENCY);
philpem@20 1294
philpem@20 1295 // TODO: run DMA here
philpem@16 1296
philpem@20 1297 // Is it time to run the 60Hz periodic interrupt yet?
philpem@20 1298 if (clock_cycles > CLOCKS_PER_60HZ) {
philpem@20 1299 // TODO: refresh screen
philpem@20 1300 // TODO: trigger periodic interrupt (if enabled)
philpem@20 1301 // decrement clock cycle counter, we've handled the intr.
philpem@20 1302 clock_cycles -= CLOCKS_PER_60HZ;
philpem@16 1303 }
philpem@16 1304
philpem@20 1305 // make sure frame rate is equal to real time
philpem@20 1306 uint32_t now = SDL_GetTicks();
philpem@20 1307 if (now < next_timeslot) {
philpem@20 1308 // timeslot finished early -- eat up some time
philpem@20 1309 SDL_Delay(next_timeslot - now);
philpem@20 1310 } else {
philpem@20 1311 // timeslot finished late -- skip ahead to gain time
philpem@20 1312 // TODO: if this happens a lot, we should let the user know
philpem@20 1313 // that their PC might not be fast enough...
philpem@20 1314 next_timeslot = now;
philpem@20 1315 }
philpem@20 1316 // advance to the next timeslot
philpem@20 1317 next_timeslot += MILLISECS_PER_TIMESLOT;
philpem@20 1318
philpem@20 1319 // if we've been asked to exit the emulator, then do so.
philpem@16 1320 if (exitEmu) break;
philpem@16 1321 }
philpem@7 1322
philpem@7 1323 // shut down and exit
philpem@20 1324 SDL_Quit();
philpem@7 1325
philpem@0 1326 return 0;
philpem@0 1327 }