Thu, 02 Dec 2010 17:12:28 +0000
Fix mallocing issue with ram array
RAM storage array was not being correctly allocated at startup. This caused memory access issues... Spotted with Valgrind, fixed.
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 >= 0x420000) && (address <= 0x427FFF)) {
231 // VRAM access
232 data = RD32(state.vram, address, 0x7FFF);
233 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
234 // Map RAM access
235 data = RD32(state.map, address, 0x7FF);
236 } else {
237 // I/O register -- TODO
238 printf("RD32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
239 }
240 return data;
241 }
243 /**
244 * @brief Read M68K memory, 16-bit
245 */
246 uint32_t m68k_read_memory_16(uint32_t address)
247 {
248 uint16_t data = 0xFFFF;
250 // If ROMLMAP is set, force system to access ROM
251 if (!state.romlmap)
252 address |= 0x800000;
254 // Check access permissions
255 ACCESS_CHECK_RD();
257 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
258 // ROM access
259 data = RD16(state.rom, address, ROM_SIZE - 1);
260 } else if (address <= (state.ram_size - 1)) {
261 // RAM access
262 data = RD16(state.ram, mapAddr(address, false), state.ram_size - 1);
263 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
264 // VRAM access
265 data = RD16(state.vram, address, 0x7FFF);
266 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
267 // Map RAM access
268 data = RD16(state.map, address, 0x7FF);
269 } else {
270 // I/O register -- TODO
271 printf("RD16 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
272 }
274 return data;
275 }
277 /**
278 * @brief Read M68K memory, 8-bit
279 */
280 uint32_t m68k_read_memory_8(uint32_t address)
281 {
282 uint8_t data = 0xFF;
284 // If ROMLMAP is set, force system to access ROM
285 if (!state.romlmap)
286 address |= 0x800000;
288 // Check access permissions
289 ACCESS_CHECK_RD();
291 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
292 // ROM access
293 data = RD8(state.rom, address, ROM_SIZE - 1);
294 } else if (address <= (state.ram_size - 1)) {
295 // RAM access
296 data = RD8(state.ram, mapAddr(address, false), state.ram_size - 1);
297 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
298 // VRAM access
299 data = RD8(state.vram, address, 0x7FFF);
300 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
301 // Map RAM access
302 data = RD8(state.map, address, 0x7FF);
303 } else {
304 // I/O register -- TODO
305 printf("RD08 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
306 }
308 return data;
309 }
311 /**
312 * @brief Write M68K memory, 32-bit
313 */
314 void m68k_write_memory_32(uint32_t address, uint32_t value)
315 {
316 // If ROMLMAP is set, force system to access ROM
317 if (!state.romlmap)
318 address |= 0x800000;
320 // Check access permissions
321 ACCESS_CHECK_WR();
323 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
324 // ROM access
325 // According to HwNote15 (John B. Milton), there is no write protection
326 // here. You can write to ROM, but nothing happens.
327 } else if (address <= (state.ram_size - 1)) {
328 // RAM access
329 WR32(state.ram, mapAddr(address, true), state.ram_size - 1, value);
330 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
331 // VRAM access
332 WR32(state.vram, address, 0x7fff, value);
333 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
334 // Map RAM access
335 WR32(state.map, address, 0x7FF, value);
336 } else {
337 switch (address) {
338 case 0xE43000: state.romlmap = ((value & 0x8000) == 0x8000); break; // GCR3: ROMLMAP
339 default: printf("WR32 0x%08X ==> 0x%08X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : ""); break;
340 }
341 }
342 }
344 /**
345 * @brief Write M68K memory, 16-bit
346 */
347 void m68k_write_memory_16(uint32_t address, uint32_t value)
348 {
349 // If ROMLMAP is set, force system to access ROM
350 if (!state.romlmap)
351 address |= 0x800000;
353 // Check access permissions
354 ACCESS_CHECK_WR();
356 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
357 // ROM access
358 // According to HwNote15 (John B. Milton), there is no write protection
359 // here. You can write to ROM, but nothing happens.
360 } else if (address <= (state.ram_size - 1)) {
361 // RAM access
362 WR16(state.ram, mapAddr(address, true), state.ram_size - 1, value);
363 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
364 // VRAM access
365 WR16(state.vram, address, 0x7fff, value);
366 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
367 // Map RAM access
368 WR16(state.map, address, 0x7FF, value);
369 } else {
370 switch (address) {
371 case 0xE43000: state.romlmap = ((value & 0x8000) == 0x8000); break; // GCR3: ROMLMAP
372 default: printf("WR16 0x%08X ==> 0x%04X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : ""); break;
373 }
374 if (address == 0x4A0000) {
375 printf("\tLED WRITE: %s %s %s %s\n",
376 value & 0x800 ? "-" : "R",
377 value & 0x400 ? "-" : "G",
378 value & 0x200 ? "-" : "Y",
379 value & 0x100 ? "-" : "R"
380 );
381 }
382 }
383 }
385 /**
386 * @brief Write M68K memory, 8-bit
387 */
388 void m68k_write_memory_8(uint32_t address, uint32_t value)
389 {
390 // If ROMLMAP is set, force system to access ROM
391 if (!state.romlmap)
392 address |= 0x800000;
394 // Check access permissions
395 ACCESS_CHECK_WR();
397 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
398 // ROM access
399 // According to HwNote15 (John B. Milton), there is no write protection
400 // here. You can write to ROM, but nothing happens.
401 } else if (address <= (state.ram_size - 1)) {
402 // RAM access
403 WR8(state.ram, mapAddr(address, true), state.ram_size - 1, value);
404 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
405 // VRAM access
406 WR8(state.vram, address, 0x7fff, value);
407 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
408 // Map RAM access
409 WR8(state.map, address, 0x7FF, value);
410 } else {
411 switch (address) {
412 case 0xE43000: state.romlmap = ((value & 0x80) == 0x80); break; // GCR3: ROMLMAP
413 default: printf("WR08 0x%08X ==> 0x%02X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : ""); break;
414 }
415 }
416 }
418 // for the disassembler
419 uint32_t m68k_read_disassembler_32(uint32_t addr) { return m68k_read_memory_32(addr); }
420 uint32_t m68k_read_disassembler_16(uint32_t addr) { return m68k_read_memory_16(addr); }
421 uint32_t m68k_read_disassembler_8 (uint32_t addr) { return m68k_read_memory_8 (addr); }
424 /****************************
425 * blessed be thy main()...
426 ****************************/
428 int main(void)
429 {
430 // copyright banner
431 printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
432 printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
433 printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
434 printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
435 printf("Compiler: %s\n", VER_COMPILER);
436 printf("CFLAGS: %s\n", VER_CFLAGS);
437 printf("\n");
439 // set up system state
440 // 512K of RAM
441 state_init(512*1024);
443 // set up musashi and reset the CPU
444 m68k_set_cpu_type(M68K_CPU_TYPE_68010);
445 m68k_pulse_reset();
447 // Set up SDL
448 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
449 printf("Could not initialise SDL: %s.\n", SDL_GetError());
450 exit(EXIT_FAILURE);
451 }
453 // Make sure SDL cleans up after itself
454 atexit(SDL_Quit);
456 // Set up the video display
457 SDL_Surface *screen = NULL;
458 if ((screen = SDL_SetVideoMode(720, 384, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) {
459 printf("Could not find a suitable video mode: %s.\n", SDL_GetError());
460 exit(EXIT_FAILURE);
461 }
462 printf("Set %dx%d at %d bits-per-pixel mode\n\n", screen->w, screen->h, screen->format->BitsPerPixel);
463 SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee");
465 /***
466 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
467 * around 60Hz (???), with a 60Hz periodic interrupt.
468 */
469 const uint32_t TIMESLOT_FREQUENCY = 240; // Hz
470 const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
471 const uint32_t CLOCKS_PER_60HZ = (10e6 / 60);
472 uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
473 uint32_t clock_cycles = 0;
474 bool exitEmu = false;
475 for (;;) {
476 // Run the CPU for however many cycles we need to. CPU core clock is
477 // 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
478 // 41667 cycles per timeslot.
479 clock_cycles += m68k_execute(10e6/TIMESLOT_FREQUENCY);
481 // TODO: run DMA here
483 // Is it time to run the 60Hz periodic interrupt yet?
484 if (clock_cycles > CLOCKS_PER_60HZ) {
485 // TODO: refresh screen
486 // TODO: trigger periodic interrupt (if enabled)
487 // decrement clock cycle counter, we've handled the intr.
488 clock_cycles -= CLOCKS_PER_60HZ;
489 }
491 // make sure frame rate is equal to real time
492 uint32_t now = SDL_GetTicks();
493 if (now < next_timeslot) {
494 // timeslot finished early -- eat up some time
495 SDL_Delay(next_timeslot - now);
496 } else {
497 // timeslot finished late -- skip ahead to gain time
498 // TODO: if this happens a lot, we should let the user know
499 // that their PC might not be fast enough...
500 next_timeslot = now;
501 }
502 // advance to the next timeslot
503 next_timeslot += MILLISECS_PER_TIMESLOT;
505 // if we've been asked to exit the emulator, then do so.
506 if (exitEmu) break;
507 }
509 // shut down and exit
510 SDL_Quit();
512 return 0;
513 }