Thu, 02 Dec 2010 02:43:49 +0000
add routines for memory access checking
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[addr*2] |= 0x60; // Page written to (dirty)
81 else
82 state.map[addr*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 // read m68k memory
143 uint32_t m68k_read_memory_32(uint32_t address)
144 {
145 uint32_t data = 0xFFFFFFFF;
147 // If ROMLMAP is set, force system to access ROM
148 if (!state.romlmap)
149 address |= 0x800000;
151 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
152 // ROM access
153 data = RD32(state.rom, address, ROM_SIZE - 1);
154 } else if (address <= (state.ram_size - 1)) {
155 // RAM access -- TODO: mapping
156 data = RD32(state.ram, address, state.ram_size - 1);
157 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
158 // VRAM access
159 data = RD32(state.vram, address, 0x7FFF);
160 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
161 // Map RAM access
162 data = RD32(state.map, address, 0x7FF);
163 } else {
164 // I/O register -- TODO
165 printf("RD32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
166 }
167 return data;
168 }
170 uint32_t m68k_read_memory_16(uint32_t address)
171 {
172 uint16_t data = 0xFFFF;
174 // If ROMLMAP is set, force system to access ROM
175 if (!state.romlmap)
176 address |= 0x800000;
178 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
179 // ROM access
180 data = RD16(state.rom, address, ROM_SIZE - 1);
181 } else if (address <= (state.ram_size - 1)) {
182 // RAM access -- TODO: mapping
183 data = RD16(state.ram, address, state.ram_size - 1);
184 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
185 // VRAM access
186 data = RD16(state.vram, address, 0x7FFF);
187 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
188 // Map RAM access
189 data = RD16(state.map, address, 0x7FF);
190 } else {
191 // I/O register -- TODO
192 printf("RD16 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
193 }
195 return data;
196 }
198 uint32_t m68k_read_memory_8(uint32_t address)
199 {
200 uint8_t data = 0xFF;
202 // If ROMLMAP is set, force system to access ROM
203 if (!state.romlmap)
204 address |= 0x800000;
206 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
207 // ROM access
208 data = RD8(state.rom, address, ROM_SIZE - 1);
209 } else if (address <= (state.ram_size - 1)) {
210 // RAM access -- TODO: mapping
211 data = RD8(state.ram, address, state.ram_size - 1);
212 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
213 // VRAM access
214 data = RD8(state.vram, address, 0x7FFF);
215 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
216 // Map RAM access
217 data = RD8(state.map, address, 0x7FF);
218 } else {
219 // I/O register -- TODO
220 printf("RD08 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
221 }
223 return data;
224 }
226 // write m68k memory
227 void m68k_write_memory_32(uint32_t address, uint32_t value)
228 {
229 // If ROMLMAP is set, force system to access ROM
230 if (!state.romlmap)
231 address |= 0x800000;
233 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
234 // ROM access
235 // TODO: bus error here? can't write to rom!
236 } else if (address <= (state.ram_size - 1)) {
237 // RAM -- TODO: mapping
238 WR32(state.ram, address, state.ram_size - 1, value);
239 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
240 // VRAM access
241 WR32(state.vram, address, 0x7fff, value);
242 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
243 // Map RAM access
244 WR32(state.map, address, 0x7FF, value);
245 } else {
246 switch (address) {
247 case 0xE43000: state.romlmap = ((value & 0x8000) == 0x8000); break; // GCR3: ROMLMAP
248 default: printf("WR32 0x%08X ==> 0x%08X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : ""); break;
249 }
250 }
251 }
253 void m68k_write_memory_16(uint32_t address, uint32_t value)
254 {
255 // If ROMLMAP is set, force system to access ROM
256 if (!state.romlmap)
257 address |= 0x800000;
259 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
260 // ROM access
261 // TODO: bus error here? can't write to rom!
262 } else if (address <= (state.ram_size - 1)) {
263 // RAM access -- TODO: mapping
264 WR16(state.ram, address, state.ram_size - 1, value);
265 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
266 // VRAM access
267 WR16(state.vram, address, 0x7fff, value);
268 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
269 // Map RAM access
270 WR16(state.map, address, 0x7FF, value);
271 } else {
272 switch (address) {
273 case 0xE43000: state.romlmap = ((value & 0x8000) == 0x8000); break; // GCR3: ROMLMAP
274 default: printf("WR16 0x%08X ==> 0x%04X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : ""); break;
275 }
276 if (address == 0x4A0000) {
277 printf("\tLED WRITE: %s %s %s %s\n",
278 value & 0x800 ? "-" : "R",
279 value & 0x400 ? "-" : "G",
280 value & 0x200 ? "-" : "Y",
281 value & 0x100 ? "-" : "R"
282 );
283 }
284 }
285 }
287 void m68k_write_memory_8(uint32_t address, uint32_t value)
288 {
289 // If ROMLMAP is set, force system to access ROM
290 if (!state.romlmap)
291 address |= 0x800000;
293 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
294 // ROM access
295 // TODO: bus error here? can't write to rom!
296 } else if (address <= (state.ram_size - 1)) {
297 // RAM access -- TODO: mapping
298 WR8(state.ram, address, state.ram_size - 1, value);
299 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
300 // VRAM access
301 WR8(state.vram, address, 0x7fff, value);
302 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
303 // Map RAM access
304 WR8(state.map, address, 0x7FF, value);
305 } else {
306 switch (address) {
307 case 0xE43000: state.romlmap = ((value & 0x80) == 0x80); break; // GCR3: ROMLMAP
308 default: printf("WR08 0x%08X ==> 0x%02X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : ""); break;
309 }
310 }
311 }
313 // for the disassembler
314 uint32_t m68k_read_disassembler_32(uint32_t addr) { return m68k_read_memory_32(addr); }
315 uint32_t m68k_read_disassembler_16(uint32_t addr) { return m68k_read_memory_16(addr); }
316 uint32_t m68k_read_disassembler_8 (uint32_t addr) { return m68k_read_memory_8 (addr); }
319 /****************************
320 * blessed be thy main()...
321 ****************************/
323 int main(void)
324 {
325 // copyright banner
326 printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
327 printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
328 printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
329 printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
330 printf("Compiler: %s\n", VER_COMPILER);
331 printf("CFLAGS: %s\n", VER_CFLAGS);
332 printf("\n");
334 // set up system state
335 // 512K of RAM
336 state_init(512*1024);
338 // set up musashi and reset the CPU
339 m68k_set_cpu_type(M68K_CPU_TYPE_68010);
340 m68k_pulse_reset();
342 // Set up SDL
343 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
344 printf("Could not initialise SDL: %s.\n", SDL_GetError());
345 exit(EXIT_FAILURE);
346 }
348 // Make sure SDL cleans up after itself
349 atexit(SDL_Quit);
351 // Set up the video display
352 SDL_Surface *screen = NULL;
353 if ((screen = SDL_SetVideoMode(720, 384, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) {
354 printf("Could not find a suitable video mode: %s.\n", SDL_GetError());
355 exit(EXIT_FAILURE);
356 }
357 printf("Set %dx%d at %d bits-per-pixel mode\n", screen->w, screen->h, screen->format->BitsPerPixel);
358 SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee");
360 /***
361 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
362 * around 60Hz (???), with a 60Hz periodic interrupt.
363 */
364 const uint32_t TIMESLOT_FREQUENCY = 240; // Hz
365 const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
366 const uint32_t CLOCKS_PER_60HZ = (10e6 / 60);
367 uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
368 uint32_t clock_cycles = 0;
369 bool exitEmu = false;
370 for (;;) {
371 // Run the CPU for however many cycles we need to. CPU core clock is
372 // 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
373 // 41667 cycles per timeslot.
374 clock_cycles += m68k_execute(10e6/TIMESLOT_FREQUENCY);
376 // TODO: run DMA here
378 // Is it time to run the 60Hz periodic interrupt yet?
379 if (clock_cycles > CLOCKS_PER_60HZ) {
380 // TODO: refresh screen
381 // TODO: trigger periodic interrupt (if enabled)
382 // decrement clock cycle counter, we've handled the intr.
383 clock_cycles -= CLOCKS_PER_60HZ;
384 }
386 // make sure frame rate is equal to real time
387 uint32_t now = SDL_GetTicks();
388 if (now < next_timeslot) {
389 // timeslot finished early -- eat up some time
390 SDL_Delay(next_timeslot - now);
391 } else {
392 // timeslot finished late -- skip ahead to gain time
393 // TODO: if this happens a lot, we should let the user know
394 // that their PC might not be fast enough...
395 next_timeslot = now;
396 }
397 // advance to the next timeslot
398 next_timeslot += MILLISECS_PER_TIMESLOT;
400 // if we've been asked to exit the emulator, then do so.
401 if (exitEmu) break;
402 }
404 // shut down and exit
405 SDL_Quit();
407 return 0;
408 }