Wed, 01 Dec 2010 22:34:15 +0000
move repeated R/W bit-shifting stuff into macros and fix RAM addressing issue
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;
61 /********************************************************
62 * m68k memory read/write support functions for Musashi
63 ********************************************************/
66 // read m68k memory
67 uint32_t m68k_read_memory_32(uint32_t address)
68 {
69 uint32_t data = 0xFFFFFFFF;
71 // If ROMLMAP is set, force system to access ROM
72 if (!state.romlmap)
73 address |= 0x800000;
75 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
76 // ROM access
77 data = RD32(state.rom, address, ROM_SIZE - 1);
78 } else if (address <= (state.ram_size - 1)) {
79 // RAM
80 data = RD32(state.ram, address, state.ram_size - 1);
81 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
82 // VRAM
83 data = RD32(state.vram, address, 0x7FFF);
84 } else {
85 // I/O register -- TODO
86 printf("RD32 0x%08X [unknown I/O register]\n", address);
87 }
88 return data;
89 }
91 uint32_t m68k_read_memory_16(uint32_t address)
92 {
93 uint16_t data = 0xFFFF;
95 // If ROMLMAP is set, force system to access ROM
96 if (!state.romlmap)
97 address |= 0x800000;
99 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
100 // ROM access
101 data = RD16(state.rom, address, ROM_SIZE - 1);
102 } else if (address <= (state.ram_size - 1)) {
103 // RAM
104 data = RD16(state.ram, address, state.ram_size - 1);
105 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
106 // VRAM
107 data = RD16(state.vram, address, 0x7FFF);
108 } else {
109 // I/O register -- TODO
110 printf("RD16 0x%08X [unknown I/O register]\n", address);
111 }
113 return data;
114 }
116 uint32_t m68k_read_memory_8(uint32_t address)
117 {
118 uint8_t data = 0xFF;
120 // If ROMLMAP is set, force system to access ROM
121 if (!state.romlmap)
122 address |= 0x800000;
124 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
125 // ROM access
126 data = RD8(state.rom, address, ROM_SIZE - 1);
127 } else if (address <= (state.ram_size - 1)) {
128 // RAM
129 data = RD8(state.ram, address, state.ram_size - 1);
130 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
131 // VRAM
132 data = RD8(state.vram, address, 0x7FFF);
133 } else {
134 // I/O register -- TODO
135 printf("RD08 0x%08X [unknown I/O register]\n", address);
136 }
138 return data;
139 }
141 // write m68k memory
142 void m68k_write_memory_32(uint32_t address, uint32_t value)
143 {
144 // If ROMLMAP is set, force system to access ROM
145 if (!state.romlmap)
146 address |= 0x800000;
148 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
149 // ROM access
150 // TODO: bus error here? can't write to rom!
151 } else if (address <= (state.ram_size - 1)) {
152 // RAM access
153 WR32(state.ram, address, state.ram_size - 1, value);
154 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
155 // VRAM access
156 WR32(state.vram, address, 0x7fff, value);
157 } else {
158 switch (address) {
159 case 0xE43000: state.romlmap = ((value & 0x8000) == 0x8000); break; // GCR3: ROMLMAP
160 default: printf("WR32 0x%08X ==> 0x%08X\n", address, value); break;
161 }
162 }
163 }
165 void m68k_write_memory_16(uint32_t address, uint32_t value)
166 {
167 // If ROMLMAP is set, force system to access ROM
168 if (!state.romlmap)
169 address |= 0x800000;
171 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
172 // ROM access
173 // TODO: bus error here? can't write to rom!
174 } else if (address <= (state.ram_size - 1)) {
175 // RAM access
176 WR16(state.ram, address, state.ram_size - 1, value);
177 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
178 // VRAM access
179 WR16(state.vram, address, 0x7fff, value);
180 } else {
181 switch (address) {
182 case 0xE43000: state.romlmap = ((value & 0x8000) == 0x8000); break; // GCR3: ROMLMAP
183 default: printf("WR16 0x%08X ==> 0x%04X\n", address, value); break;
184 }
185 if (address == 0x4A0000) {
186 printf("\tLED WRITE: %s %s %s %s\n",
187 value & 0x800 ? "-" : "R",
188 value & 0x400 ? "-" : "G",
189 value & 0x200 ? "-" : "Y",
190 value & 0x100 ? "-" : "R"
191 );
192 }
193 }
194 }
196 void m68k_write_memory_8(uint32_t address, uint32_t value)
197 {
198 // If ROMLMAP is set, force system to access ROM
199 if (!state.romlmap)
200 address |= 0x800000;
202 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
203 // ROM access
204 // TODO: bus error here? can't write to rom!
205 } else if (address <= (state.ram_size - 1)) {
206 // RAM access
207 WR8(state.ram, address, state.ram_size - 1, value);
208 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
209 // VRAM access
210 WR8(state.vram, address, 0x7fff, value);
211 } else {
212 switch (address) {
213 case 0xE43000: state.romlmap = ((value & 0x80) == 0x80); break; // GCR3: ROMLMAP
214 default: printf("WR08 0x%08X ==> 0x%02X\n", address, value); break;
215 }
216 }
217 }
219 // for the disassembler
220 uint32_t m68k_read_disassembler_32(uint32_t addr) { return m68k_read_memory_32(addr); }
221 uint32_t m68k_read_disassembler_16(uint32_t addr) { return m68k_read_memory_16(addr); }
222 uint32_t m68k_read_disassembler_8 (uint32_t addr) { return m68k_read_memory_8 (addr); }
224 int main(void)
225 {
226 // copyright banner
227 printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
228 printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
229 printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
230 printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
231 printf("Compiler: %s\n", VER_COMPILER);
232 printf("CFLAGS: %s\n", VER_CFLAGS);
233 printf("\n");
235 // set up system state
236 // 512K of RAM
237 state_init(512*1024);
239 // set up musashi and reset the CPU
240 m68k_set_cpu_type(M68K_CPU_TYPE_68010);
241 m68k_pulse_reset();
242 /*
243 size_t i = 0x80001a;
244 size_t len;
245 do {
246 char dasm[512];
247 len = m68k_disassemble(dasm, i, M68K_CPU_TYPE_68010);
248 printf("%06X: %s\n", i, dasm);
249 i += len;
250 } while (i < 0x8000ff);
251 */
253 // set up SDL
254 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
255 printf("Could not initialise SDL: %s.\n", SDL_GetError());
256 return -1;
257 }
259 /***
260 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
261 * around 60Hz (???), with a 60Hz periodic interrupt.
262 */
263 const uint32_t TIMESLOT_FREQUENCY = 240; // Hz
264 const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
265 const uint32_t CLOCKS_PER_60HZ = (10e6 / 60);
266 uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
267 uint32_t clock_cycles = 0;
268 bool exitEmu = false;
269 for (;;) {
270 // Run the CPU for however many cycles we need to. CPU core clock is
271 // 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
272 // 41667 cycles per timeslot.
273 clock_cycles += m68k_execute(10e6/TIMESLOT_FREQUENCY);
275 // TODO: run DMA here
277 // Is it time to run the 60Hz periodic interrupt yet?
278 if (clock_cycles > CLOCKS_PER_60HZ) {
279 // TODO: refresh screen
280 // TODO: trigger periodic interrupt (if enabled)
281 // decrement clock cycle counter, we've handled the intr.
282 clock_cycles -= CLOCKS_PER_60HZ;
283 }
285 // make sure frame rate is equal to real time
286 uint32_t now = SDL_GetTicks();
287 if (now < next_timeslot) {
288 // timeslot finished early -- eat up some time
289 SDL_Delay(next_timeslot - now);
290 } else {
291 // timeslot finished late -- skip ahead to gain time
292 // TODO: if this happens a lot, we should let the user know
293 // that their PC might not be fast enough...
294 next_timeslot = now;
295 }
296 // advance to the next timeslot
297 next_timeslot += MILLISECS_PER_TIMESLOT;
299 // if we've been asked to exit the emulator, then do so.
300 if (exitEmu) break;
301 }
303 // shut down and exit
304 SDL_Quit();
306 return 0;
307 }