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