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