Thu, 02 Dec 2010 19:30:46 +0000
rewrite memory access routines
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 case 0x040000: // Bus Status Register 1
247 case 0x050000: // Phone status
248 case 0x060000: // DMA Count
249 case 0x070000: // Line Printer Status Register
250 case 0x080000: // Real Time Clock
251 case 0x090000: // Phone registers
252 switch (address & 0x0FF000) {
253 case 0x090000: // Handset relay
254 case 0x098000:
255 case 0x091000: // Line select 2
256 case 0x099000:
257 case 0x092000: // Hook relay 1
258 case 0x09A000:
259 case 0x093000: // Hook relay 2
260 case 0x09B000:
261 case 0x094000: // Line 1 hold
262 case 0x09C000:
263 case 0x095000: // Line 2 hold
264 case 0x09D000:
265 case 0x096000: // Line 1 A-lead
266 case 0x09E000:
267 case 0x097000: // Line 2 A-lead
268 case 0x09F000:
269 break;
270 }
271 break;
272 case 0x0A0000: // Miscellaneous Control Register
273 case 0x0B0000: // TM/DIALWR
274 case 0x0C0000: // CSR
275 case 0x0D0000: // DMA Address Register
276 case 0x0E0000: // Disk Control Register
277 case 0x0F0000: // Line Printer Data Register
278 break;
279 }
280 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
281 // I/O register space, zone B
282 printf("RD32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
283 switch (address & 0xF00000) {
284 case 0xC00000: // Expansion slots
285 case 0xD00000:
286 switch (address & 0xFC0000) {
287 case 0xC00000: // Expansion slot 0
288 case 0xC40000: // Expansion slot 1
289 case 0xC80000: // Expansion slot 2
290 case 0xCC0000: // Expansion slot 3
291 case 0xD00000: // Expansion slot 4
292 case 0xD40000: // Expansion slot 5
293 case 0xD80000: // Expansion slot 6
294 case 0xDC0000: // Expansion slot 7
295 fprintf(stderr, "NOTE: RD32 from expansion card space, addr=0x%08X\n", address);
296 break;
297 }
298 break;
299 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
300 case 0xF00000:
301 switch (address & 0x070000) {
302 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
303 break;
304 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
305 break;
306 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
307 break;
308 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
309 break;
310 default:
311 fprintf(stderr, "NOTE: RD32 from undefined E/F-block address 0x%08X", address);
312 }
313 }
314 }
315 return data;
316 }
318 /**
319 * @brief Read M68K memory, 16-bit
320 */
321 uint32_t m68k_read_memory_16(uint32_t address)
322 {
323 uint16_t data = 0xFFFF;
325 // If ROMLMAP is set, force system to access ROM
326 if (!state.romlmap)
327 address |= 0x800000;
329 // Check access permissions
330 ACCESS_CHECK_RD();
332 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
333 // ROM access
334 data = RD16(state.rom, address, ROM_SIZE - 1);
335 } else if (address <= (state.ram_size - 1)) {
336 // RAM access
337 data = RD16(state.ram, mapAddr(address, false), state.ram_size - 1);
338 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
339 // I/O register space, zone A
340 printf("RD16 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
341 switch (address & 0x0F0000) {
342 case 0x000000: // Map RAM access
343 if (address > 0x4007FF) fprintf(stderr, "NOTE: RD16 from MapRAM mirror, addr=0x%08X\n", address);
344 data = RD16(state.map, address, 0x7FF);
345 break;
346 case 0x010000: // General Status Register
347 data = state.genstat;
348 break;
349 case 0x020000: // Video RAM
350 if (address > 0x427FFF) fprintf(stderr, "NOTE: RD16 from VideoRAM mirror, addr=0x%08X\n", address);
351 data = RD16(state.vram, address, 0x7FFF);
352 break;
353 case 0x030000: // Bus Status Register 0
354 case 0x040000: // Bus Status Register 1
355 case 0x050000: // Phone status
356 case 0x060000: // DMA Count
357 case 0x070000: // Line Printer Status Register
358 case 0x080000: // Real Time Clock
359 case 0x090000: // Phone registers
360 switch (address & 0x0FF000) {
361 case 0x090000: // Handset relay
362 case 0x098000:
363 case 0x091000: // Line select 2
364 case 0x099000:
365 case 0x092000: // Hook relay 1
366 case 0x09A000:
367 case 0x093000: // Hook relay 2
368 case 0x09B000:
369 case 0x094000: // Line 1 hold
370 case 0x09C000:
371 case 0x095000: // Line 2 hold
372 case 0x09D000:
373 case 0x096000: // Line 1 A-lead
374 case 0x09E000:
375 case 0x097000: // Line 2 A-lead
376 case 0x09F000:
377 break;
378 }
379 break;
380 case 0x0A0000: // Miscellaneous Control Register
381 case 0x0B0000: // TM/DIALWR
382 case 0x0C0000: // CSR
383 case 0x0D0000: // DMA Address Register
384 case 0x0E0000: // Disk Control Register
385 case 0x0F0000: // Line Printer Data Register
386 break;
387 }
388 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
389 // I/O register space, zone B
390 printf("RD16 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
391 switch (address & 0xF00000) {
392 case 0xC00000: // Expansion slots
393 case 0xD00000:
394 switch (address & 0xFC0000) {
395 case 0xC00000: // Expansion slot 0
396 case 0xC40000: // Expansion slot 1
397 case 0xC80000: // Expansion slot 2
398 case 0xCC0000: // Expansion slot 3
399 case 0xD00000: // Expansion slot 4
400 case 0xD40000: // Expansion slot 5
401 case 0xD80000: // Expansion slot 6
402 case 0xDC0000: // Expansion slot 7
403 fprintf(stderr, "NOTE: RD16 from expansion card space, addr=0x%08X\n", address);
404 break;
405 }
406 break;
407 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
408 case 0xF00000:
409 switch (address & 0x070000) {
410 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
411 break;
412 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
413 break;
414 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
415 break;
416 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
417 break;
418 default:
419 fprintf(stderr, "NOTE: RD16 to undefined E/F-block address 0x%08X", address);
420 }
421 }
422 }
423 return data;
424 }
426 /**
427 * @brief Read M68K memory, 8-bit
428 */
429 uint32_t m68k_read_memory_8(uint32_t address)
430 {
431 uint8_t data = 0xFF;
433 // If ROMLMAP is set, force system to access ROM
434 if (!state.romlmap)
435 address |= 0x800000;
437 // Check access permissions
438 ACCESS_CHECK_RD();
440 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
441 // ROM access
442 data = RD8(state.rom, address, ROM_SIZE - 1);
443 } else if (address <= (state.ram_size - 1)) {
444 // RAM access
445 data = RD8(state.ram, mapAddr(address, false), state.ram_size - 1);
446 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
447 // I/O register space, zone A
448 printf("RD8 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
449 switch (address & 0x0F0000) {
450 case 0x000000: // Map RAM access
451 if (address > 0x4007FF) fprintf(stderr, "NOTE: RD8 from MapRAM mirror, addr=0x%08X\n", address);
452 data = RD8(state.map, address, 0x7FF);
453 break;
454 case 0x010000: // General Status Register
455 if ((address & 1) == 0)
456 data = (state.genstat >> 8) & 0xff;
457 else
458 data = (state.genstat) & 0xff;
459 break;
460 case 0x020000: // Video RAM
461 if (address > 0x427FFF) fprintf(stderr, "NOTE: RD8 from VideoRAM mirror, addr=0x%08X\n", address);
462 data = RD8(state.vram, address, 0x7FFF);
463 break;
464 case 0x030000: // Bus Status Register 0
465 case 0x040000: // Bus Status Register 1
466 case 0x050000: // Phone status
467 case 0x060000: // DMA Count
468 case 0x070000: // Line Printer Status Register
469 case 0x080000: // Real Time Clock
470 case 0x090000: // Phone registers
471 switch (address & 0x0FF000) {
472 case 0x090000: // Handset relay
473 case 0x098000:
474 case 0x091000: // Line select 2
475 case 0x099000:
476 case 0x092000: // Hook relay 1
477 case 0x09A000:
478 case 0x093000: // Hook relay 2
479 case 0x09B000:
480 case 0x094000: // Line 1 hold
481 case 0x09C000:
482 case 0x095000: // Line 2 hold
483 case 0x09D000:
484 case 0x096000: // Line 1 A-lead
485 case 0x09E000:
486 case 0x097000: // Line 2 A-lead
487 case 0x09F000:
488 break;
489 }
490 break;
491 case 0x0A0000: // Miscellaneous Control Register
492 case 0x0B0000: // TM/DIALWR
493 case 0x0C0000: // CSR
494 case 0x0D0000: // DMA Address Register
495 case 0x0E0000: // Disk Control Register
496 case 0x0F0000: // Line Printer Data Register
497 break;
498 }
499 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
500 // I/O register space, zone B
501 printf("RD32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
502 switch (address & 0xF00000) {
503 case 0xC00000: // Expansion slots
504 case 0xD00000:
505 switch (address & 0xFC0000) {
506 case 0xC00000: // Expansion slot 0
507 case 0xC40000: // Expansion slot 1
508 case 0xC80000: // Expansion slot 2
509 case 0xCC0000: // Expansion slot 3
510 case 0xD00000: // Expansion slot 4
511 case 0xD40000: // Expansion slot 5
512 case 0xD80000: // Expansion slot 6
513 case 0xDC0000: // Expansion slot 7
514 fprintf(stderr, "NOTE: RD8 from expansion card address 0x%08X\n", address);
515 break;
516 }
517 break;
518 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
519 case 0xF00000:
520 switch (address & 0x070000) {
521 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
522 break;
523 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
524 break;
525 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
526 break;
527 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
528 break;
529 default:
530 fprintf(stderr, "NOTE: RD8 from undefined E/F-block address 0x%08X", address);
531 }
532 }
533 }
534 return data;
535 }
537 /**
538 * @brief Write M68K memory, 32-bit
539 */
540 void m68k_write_memory_32(uint32_t address, uint32_t value)
541 {
542 // If ROMLMAP is set, force system to access ROM
543 if (!state.romlmap)
544 address |= 0x800000;
546 // Check access permissions
547 ACCESS_CHECK_WR();
549 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
550 // ROM access
551 WR32(state.rom, address, ROM_SIZE - 1, value);
552 } else if (address <= (state.ram_size - 1)) {
553 // RAM access
554 WR32(state.ram, mapAddr(address, false), state.ram_size - 1, value);
555 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
556 // I/O register space, zone A
557 printf("WR32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
558 switch (address & 0x0F0000) {
559 case 0x000000: // Map RAM access
560 if (address > 0x4007FF) fprintf(stderr, "NOTE: WR32 to MapRAM mirror, addr=0x%08X, data=0x%08X\n", address, value);
561 WR32(state.map, address, 0x7FF, value);
562 break;
563 case 0x010000: // General Status Register
564 state.genstat = (value & 0xffff);
565 break;
566 case 0x020000: // Video RAM
567 if (address > 0x427FFF) fprintf(stderr, "NOTE: WR32 to VideoRAM mirror, addr=0x%08X, data=0x%08X\n", address, value);
568 WR32(state.vram, address, 0x7FFF, value);
569 break;
570 case 0x030000: // Bus Status Register 0
571 case 0x040000: // Bus Status Register 1
572 case 0x050000: // Phone status
573 case 0x060000: // DMA Count
574 case 0x070000: // Line Printer Status Register
575 case 0x080000: // Real Time Clock
576 case 0x090000: // Phone registers
577 switch (address & 0x0FF000) {
578 case 0x090000: // Handset relay
579 case 0x098000:
580 case 0x091000: // Line select 2
581 case 0x099000:
582 case 0x092000: // Hook relay 1
583 case 0x09A000:
584 case 0x093000: // Hook relay 2
585 case 0x09B000:
586 case 0x094000: // Line 1 hold
587 case 0x09C000:
588 case 0x095000: // Line 2 hold
589 case 0x09D000:
590 case 0x096000: // Line 1 A-lead
591 case 0x09E000:
592 case 0x097000: // Line 2 A-lead
593 case 0x09F000:
594 break;
595 }
596 break;
597 case 0x0A0000: // Miscellaneous Control Register
598 case 0x0B0000: // TM/DIALWR
599 case 0x0C0000: // CSR
600 case 0x0D0000: // DMA Address Register
601 case 0x0E0000: // Disk Control Register
602 case 0x0F0000: // Line Printer Data Register
603 break;
604 }
605 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
606 // I/O register space, zone B
607 printf("WR32 0x%08X ==> 0x%08X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
608 switch (address & 0xF00000) {
609 case 0xC00000: // Expansion slots
610 case 0xD00000:
611 switch (address & 0xFC0000) {
612 case 0xC00000: // Expansion slot 0
613 case 0xC40000: // Expansion slot 1
614 case 0xC80000: // Expansion slot 2
615 case 0xCC0000: // Expansion slot 3
616 case 0xD00000: // Expansion slot 4
617 case 0xD40000: // Expansion slot 5
618 case 0xD80000: // Expansion slot 6
619 case 0xDC0000: // Expansion slot 7
620 fprintf(stderr, "NOTE: WR32 to expansion card space, addr=0x%08X, data=0x%08X\n", address, value);
621 break;
622 }
623 break;
624 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
625 case 0xF00000:
626 switch (address & 0x070000) {
627 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
628 break;
629 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
630 break;
631 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
632 break;
633 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
634 break;
635 default:
636 fprintf(stderr, "NOTE: WR32 to undefined E/F-block space, addr=0x%08X, data=0x%08X\n", address, value);
637 }
638 }
639 }
640 }
642 /**
643 * @brief Write M68K memory, 16-bit
644 */
645 void m68k_write_memory_16(uint32_t address, uint32_t value)
646 {
647 // If ROMLMAP is set, force system to access ROM
648 if (!state.romlmap)
649 address |= 0x800000;
651 // Check access permissions
652 ACCESS_CHECK_WR();
654 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
655 // ROM access
656 WR16(state.rom, address, ROM_SIZE - 1, value);
657 } else if (address <= (state.ram_size - 1)) {
658 // RAM access
659 WR16(state.ram, mapAddr(address, false), state.ram_size - 1, value);
660 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
661 // I/O register space, zone A
662 printf("WR16 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
663 switch (address & 0x0F0000) {
664 case 0x000000: // Map RAM access
665 if (address > 0x4007FF) fprintf(stderr, "NOTE: WR16 to MapRAM mirror, addr=0x%08X, data=0x%04X\n", address, value);
666 WR16(state.map, address, 0x7FF, value);
667 break;
668 case 0x010000: // General Status Register
669 state.genstat = (value & 0xffff);
670 break;
671 case 0x020000: // Video RAM
672 if (address > 0x427FFF) fprintf(stderr, "NOTE: WR16 to VideoRAM mirror, addr=0x%08X, data=0x%04X\n", address, value);
673 WR16(state.vram, address, 0x7FFF, value);
674 break;
675 case 0x030000: // Bus Status Register 0
676 case 0x040000: // Bus Status Register 1
677 case 0x050000: // Phone status
678 case 0x060000: // DMA Count
679 case 0x070000: // Line Printer Status Register
680 case 0x080000: // Real Time Clock
681 case 0x090000: // Phone registers
682 switch (address & 0x0FF000) {
683 case 0x090000: // Handset relay
684 case 0x098000:
685 case 0x091000: // Line select 2
686 case 0x099000:
687 case 0x092000: // Hook relay 1
688 case 0x09A000:
689 case 0x093000: // Hook relay 2
690 case 0x09B000:
691 case 0x094000: // Line 1 hold
692 case 0x09C000:
693 case 0x095000: // Line 2 hold
694 case 0x09D000:
695 case 0x096000: // Line 1 A-lead
696 case 0x09E000:
697 case 0x097000: // Line 2 A-lead
698 case 0x09F000:
699 break;
700 }
701 break;
702 case 0x0A0000: // Miscellaneous Control Register
703 case 0x0B0000: // TM/DIALWR
704 case 0x0C0000: // CSR
705 case 0x0D0000: // DMA Address Register
706 case 0x0E0000: // Disk Control Register
707 case 0x0F0000: // Line Printer Data Register
708 break;
709 }
710 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
711 // I/O register space, zone B
712 printf("WR16 0x%08X ==> 0x%04X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
713 switch (address & 0xF00000) {
714 case 0xC00000: // Expansion slots
715 case 0xD00000:
716 switch (address & 0xFC0000) {
717 case 0xC00000: // Expansion slot 0
718 case 0xC40000: // Expansion slot 1
719 case 0xC80000: // Expansion slot 2
720 case 0xCC0000: // Expansion slot 3
721 case 0xD00000: // Expansion slot 4
722 case 0xD40000: // Expansion slot 5
723 case 0xD80000: // Expansion slot 6
724 case 0xDC0000: // Expansion slot 7
725 fprintf(stderr, "NOTE: WR16 to expansion card space, addr=0x%08X, data=0x%04X\n", address, value);
726 break;
727 }
728 break;
729 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
730 case 0xF00000:
731 switch (address & 0x070000) {
732 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
733 break;
734 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
735 break;
736 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
737 break;
738 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
739 break;
740 default:
741 fprintf(stderr, "NOTE: WR16 to undefined E/F-block space, addr=0x%08X, data=0x%04X\n", address, value);
742 }
743 }
744 }
745 }
747 /**
748 * @brief Write M68K memory, 8-bit
749 */
750 void m68k_write_memory_8(uint32_t address, uint32_t value)
751 {
752 // If ROMLMAP is set, force system to access ROM
753 if (!state.romlmap)
754 address |= 0x800000;
756 // Check access permissions
757 ACCESS_CHECK_WR();
759 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
760 // ROM access
761 WR8(state.rom, address, ROM_SIZE - 1, value);
762 } else if (address <= (state.ram_size - 1)) {
763 // RAM access
764 WR8(state.ram, mapAddr(address, false), state.ram_size - 1, value);
765 } else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
766 // I/O register space, zone A
767 printf("WR8 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
768 switch (address & 0x0F0000) {
769 case 0x000000: // Map RAM access
770 if (address > 0x4007FF) fprintf(stderr, "NOTE: WR8 to MapRAM mirror, addr=%08X, data=%02X\n", address, value);
771 WR8(state.map, address, 0x7FF, value);
772 break;
773 case 0x010000: // General Status Register
774 state.genstat = (value & 0xffff);
775 break;
776 case 0x020000: // Video RAM
777 if (address > 0x427FFF) fprintf(stderr, "NOTE: WR8 to VideoRAM mirror, addr=%08X\n, data=0x%02X", address, value);
778 WR8(state.vram, address, 0x7FFF, value);
779 break;
780 case 0x030000: // Bus Status Register 0
781 case 0x040000: // Bus Status Register 1
782 case 0x050000: // Phone status
783 case 0x060000: // DMA Count
784 case 0x070000: // Line Printer Status Register
785 case 0x080000: // Real Time Clock
786 case 0x090000: // Phone registers
787 switch (address & 0x0FF000) {
788 case 0x090000: // Handset relay
789 case 0x098000:
790 case 0x091000: // Line select 2
791 case 0x099000:
792 case 0x092000: // Hook relay 1
793 case 0x09A000:
794 case 0x093000: // Hook relay 2
795 case 0x09B000:
796 case 0x094000: // Line 1 hold
797 case 0x09C000:
798 case 0x095000: // Line 2 hold
799 case 0x09D000:
800 case 0x096000: // Line 1 A-lead
801 case 0x09E000:
802 case 0x097000: // Line 2 A-lead
803 case 0x09F000:
804 break;
805 }
806 break;
807 case 0x0A0000: // Miscellaneous Control Register
808 case 0x0B0000: // TM/DIALWR
809 case 0x0C0000: // CSR
810 case 0x0D0000: // DMA Address Register
811 case 0x0E0000: // Disk Control Register
812 case 0x0F0000: // Line Printer Data Register
813 break;
814 }
815 } else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
816 // I/O register space, zone B
817 printf("WR8 0x%08X ==> 0x%08X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
818 switch (address & 0xF00000) {
819 case 0xC00000: // Expansion slots
820 case 0xD00000:
821 switch (address & 0xFC0000) {
822 case 0xC00000: // Expansion slot 0
823 case 0xC40000: // Expansion slot 1
824 case 0xC80000: // Expansion slot 2
825 case 0xCC0000: // Expansion slot 3
826 case 0xD00000: // Expansion slot 4
827 case 0xD40000: // Expansion slot 5
828 case 0xD80000: // Expansion slot 6
829 case 0xDC0000: // Expansion slot 7
830 fprintf(stderr, "NOTE: WR8 to expansion card space, addr=0x%08X, data=0x%08X\n", address, value);
831 break;
832 }
833 break;
834 case 0xE00000: // HDC, FDC, MCR2 and RTC data bits
835 case 0xF00000:
836 switch (address & 0x070000) {
837 case 0x000000: // [ef][08]xxxx ==> WD1010 hard disc controller
838 break;
839 case 0x010000: // [ef][19]xxxx ==> WD2797 floppy disc controller
840 break;
841 case 0x020000: // [ef][2a]xxxx ==> Miscellaneous Control Register 2
842 break;
843 case 0x030000: // [ef][3b]xxxx ==> Real Time Clock data bits
844 break;
845 default:
846 fprintf(stderr, "NOTE: WR8 to undefined E/F-block space, addr=0x%08X, data=0x%08X\n", address, value);
847 }
848 }
849 }
850 }
853 // for the disassembler
854 uint32_t m68k_read_disassembler_32(uint32_t addr) { return m68k_read_memory_32(addr); }
855 uint32_t m68k_read_disassembler_16(uint32_t addr) { return m68k_read_memory_16(addr); }
856 uint32_t m68k_read_disassembler_8 (uint32_t addr) { return m68k_read_memory_8 (addr); }
859 /****************************
860 * blessed be thy main()...
861 ****************************/
863 int main(void)
864 {
865 // copyright banner
866 printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
867 printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
868 printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
869 printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
870 printf("Compiler: %s\n", VER_COMPILER);
871 printf("CFLAGS: %s\n", VER_CFLAGS);
872 printf("\n");
874 // set up system state
875 // 512K of RAM
876 state_init(512*1024);
878 // set up musashi and reset the CPU
879 m68k_set_cpu_type(M68K_CPU_TYPE_68010);
880 m68k_pulse_reset();
882 // Set up SDL
883 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
884 printf("Could not initialise SDL: %s.\n", SDL_GetError());
885 exit(EXIT_FAILURE);
886 }
888 // Make sure SDL cleans up after itself
889 atexit(SDL_Quit);
891 // Set up the video display
892 SDL_Surface *screen = NULL;
893 if ((screen = SDL_SetVideoMode(720, 384, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) {
894 printf("Could not find a suitable video mode: %s.\n", SDL_GetError());
895 exit(EXIT_FAILURE);
896 }
897 printf("Set %dx%d at %d bits-per-pixel mode\n\n", screen->w, screen->h, screen->format->BitsPerPixel);
898 SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee");
900 /***
901 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
902 * around 60Hz (???), with a 60Hz periodic interrupt.
903 */
904 const uint32_t TIMESLOT_FREQUENCY = 240; // Hz
905 const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
906 const uint32_t CLOCKS_PER_60HZ = (10e6 / 60);
907 uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
908 uint32_t clock_cycles = 0;
909 bool exitEmu = false;
910 for (;;) {
911 // Run the CPU for however many cycles we need to. CPU core clock is
912 // 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
913 // 41667 cycles per timeslot.
914 clock_cycles += m68k_execute(10e6/TIMESLOT_FREQUENCY);
916 // TODO: run DMA here
918 // Is it time to run the 60Hz periodic interrupt yet?
919 if (clock_cycles > CLOCKS_PER_60HZ) {
920 // TODO: refresh screen
921 // TODO: trigger periodic interrupt (if enabled)
922 // decrement clock cycle counter, we've handled the intr.
923 clock_cycles -= CLOCKS_PER_60HZ;
924 }
926 // make sure frame rate is equal to real time
927 uint32_t now = SDL_GetTicks();
928 if (now < next_timeslot) {
929 // timeslot finished early -- eat up some time
930 SDL_Delay(next_timeslot - now);
931 } else {
932 // timeslot finished late -- skip ahead to gain time
933 // TODO: if this happens a lot, we should let the user know
934 // that their PC might not be fast enough...
935 next_timeslot = now;
936 }
937 // advance to the next timeslot
938 next_timeslot += MILLISECS_PER_TIMESLOT;
940 // if we've been asked to exit the emulator, then do so.
941 if (exitEmu) break;
942 }
944 // shut down and exit
945 SDL_Quit();
947 return 0;
948 }