src/main.c

Thu, 02 Dec 2010 17:12:28 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Thu, 02 Dec 2010 17:12:28 +0000
changeset 33
ae97a3146978
parent 32
a44afcf2354c
child 34
e8ebd433270a
permissions
-rw-r--r--

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 }