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