src/main.c

Thu, 02 Dec 2010 20:19:20 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Thu, 02 Dec 2010 20:19:20 +0000
changeset 35
391318413bb2
parent 34
e8ebd433270a
child 36
746fb8d0653e
permissions
-rw-r--r--

fix ROMLMAP handling

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