src/main.c

Thu, 02 Dec 2010 01:03:46 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Thu, 02 Dec 2010 01:03:46 +0000
changeset 28
70665b05cb10
parent 27
ceae676021ca
child 29
d73d07c1d492
permissions
-rw-r--r--

add SDL video mode set code

     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  ********************************************************/
    65 // read m68k memory
    66 uint32_t m68k_read_memory_32(uint32_t address)
    67 {
    68 	uint32_t data = 0xFFFFFFFF;
    70 	// If ROMLMAP is set, force system to access ROM
    71 	if (!state.romlmap)
    72 		address |= 0x800000;
    74 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
    75 		// ROM access
    76 		data = RD32(state.rom, address, ROM_SIZE - 1);
    77 	} else if (address <= (state.ram_size - 1)) {
    78 		// RAM access -- TODO: mapping
    79 		data = RD32(state.ram, address, state.ram_size - 1);
    80 	} else if ((address >= 0x420000) && (address <= 0x427FFF)) {
    81 		// VRAM access
    82 		data = RD32(state.vram, address, 0x7FFF);
    83 	} else if ((address >= 0x400000) && (address <= 0x4007FF)) {
    84 		// Map RAM access
    85 		data = RD32(state.map, address, 0x7FF);
    86 	} else {
    87 		// I/O register -- TODO
    88 		printf("RD32 0x%08X [unknown I/O register]\n", address);
    89 	}
    90 	return data;
    91 }
    93 uint32_t m68k_read_memory_16(uint32_t address)
    94 {
    95 	uint16_t data = 0xFFFF;
    97 	// If ROMLMAP is set, force system to access ROM
    98 	if (!state.romlmap)
    99 		address |= 0x800000;
   101 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
   102 		// ROM access
   103 		data = RD16(state.rom, address, ROM_SIZE - 1);
   104 	} else if (address <= (state.ram_size - 1)) {
   105 		// RAM access -- TODO: mapping
   106 		data = RD16(state.ram, address, state.ram_size - 1);
   107 	} else if ((address >= 0x420000) && (address <= 0x427FFF)) {
   108 		// VRAM access
   109 		data = RD16(state.vram, address, 0x7FFF);
   110 	} else if ((address >= 0x400000) && (address <= 0x4007FF)) {
   111 		// Map RAM access
   112 		data = RD16(state.map, address, 0x7FF);
   113 	} else {
   114 		// I/O register -- TODO
   115 		printf("RD16 0x%08X [unknown I/O register]\n", address);
   116 	}
   118 	return data;
   119 }
   121 uint32_t m68k_read_memory_8(uint32_t address)
   122 {
   123 	uint8_t data = 0xFF;
   125 	// If ROMLMAP is set, force system to access ROM
   126 	if (!state.romlmap)
   127 		address |= 0x800000;
   129 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
   130 		// ROM access
   131 		data = RD8(state.rom, address, ROM_SIZE - 1);
   132 	} else if (address <= (state.ram_size - 1)) {
   133 		// RAM access -- TODO: mapping
   134 		data = RD8(state.ram, address, state.ram_size - 1);
   135 	} else if ((address >= 0x420000) && (address <= 0x427FFF)) {
   136 		// VRAM access
   137 		data = RD8(state.vram, address, 0x7FFF);
   138 	} else if ((address >= 0x400000) && (address <= 0x4007FF)) {
   139 		// Map RAM access
   140 		data = RD8(state.map, address, 0x7FF);
   141 	} else {
   142 		// I/O register -- TODO
   143 		printf("RD08 0x%08X [unknown I/O register]\n", address);
   144 	}
   146 	return data;
   147 }
   149 // write m68k memory
   150 void m68k_write_memory_32(uint32_t address, uint32_t value)
   151 {
   152 	// If ROMLMAP is set, force system to access ROM
   153 	if (!state.romlmap)
   154 		address |= 0x800000;
   156 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
   157 		// ROM access
   158 		// TODO: bus error here? can't write to rom!
   159 	} else if (address <= (state.ram_size - 1)) {
   160 		// RAM -- TODO: mapping
   161 		WR32(state.ram, address, state.ram_size - 1, value);
   162 	} else if ((address >= 0x420000) && (address <= 0x427FFF)) {
   163 		// VRAM access
   164 		WR32(state.vram, address, 0x7fff, value);
   165 	} else if ((address >= 0x400000) && (address <= 0x4007FF)) {
   166 		// Map RAM access
   167 		WR32(state.map, address, 0x7FF, value);
   168 	} else {
   169 		switch (address) {
   170 			case 0xE43000:	state.romlmap = ((value & 0x8000) == 0x8000); break;	// GCR3: ROMLMAP
   171 			default:		printf("WR32 0x%08X ==> 0x%08X\n", address, value); break;
   172 		}
   173 	}
   174 }
   176 void m68k_write_memory_16(uint32_t address, uint32_t value)
   177 {
   178 	// If ROMLMAP is set, force system to access ROM
   179 	if (!state.romlmap)
   180 		address |= 0x800000;
   182 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
   183 		// ROM access
   184 		// TODO: bus error here? can't write to rom!
   185 	} else if (address <= (state.ram_size - 1)) {
   186 		// RAM access -- TODO: mapping
   187 		WR16(state.ram, address, state.ram_size - 1, value);
   188 	} else if ((address >= 0x420000) && (address <= 0x427FFF)) {
   189 		// VRAM access
   190 		WR16(state.vram, address, 0x7fff, value);
   191 	} else if ((address >= 0x400000) && (address <= 0x4007FF)) {
   192 		// Map RAM access
   193 		WR16(state.map, address, 0x7FF, value);
   194 	} else {
   195 		switch (address) {
   196 			case 0xE43000:	state.romlmap = ((value & 0x8000) == 0x8000); break;	// GCR3: ROMLMAP
   197 			default:		printf("WR16 0x%08X ==> 0x%04X\n", address, value); break;
   198 		}
   199 		if (address == 0x4A0000) {
   200 			printf("\tLED WRITE: %s %s %s %s\n",
   201 					value & 0x800 ? "-" : "R",
   202 					value & 0x400 ? "-" : "G",
   203 					value & 0x200 ? "-" : "Y",
   204 					value & 0x100 ? "-" : "R"
   205 					);
   206 		}
   207 	}
   208 }
   210 void m68k_write_memory_8(uint32_t address, uint32_t value)
   211 {
   212 	// If ROMLMAP is set, force system to access ROM
   213 	if (!state.romlmap)
   214 		address |= 0x800000;
   216 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
   217 		// ROM access
   218 		// TODO: bus error here? can't write to rom!
   219 	} else if (address <= (state.ram_size - 1)) {
   220 		// RAM access -- TODO: mapping
   221 		WR8(state.ram, address, state.ram_size - 1, value);
   222 	} else if ((address >= 0x420000) && (address <= 0x427FFF)) {
   223 		// VRAM access
   224 		WR8(state.vram, address, 0x7fff, value);
   225 	} else if ((address >= 0x400000) && (address <= 0x4007FF)) {
   226 		// Map RAM access
   227 		WR8(state.map, address, 0x7FF, value);
   228 	} else {
   229 		switch (address) {
   230 			case 0xE43000:	state.romlmap = ((value & 0x80) == 0x80); break;	// GCR3: ROMLMAP
   231 			default:		printf("WR08 0x%08X ==> 0x%02X\n", address, value); break;
   232 		}
   233 	}
   234 }
   236 // for the disassembler
   237 uint32_t m68k_read_disassembler_32(uint32_t addr) { return m68k_read_memory_32(addr); }
   238 uint32_t m68k_read_disassembler_16(uint32_t addr) { return m68k_read_memory_16(addr); }
   239 uint32_t m68k_read_disassembler_8 (uint32_t addr) { return m68k_read_memory_8 (addr); }
   242 /****************************
   243  * blessed be thy main()...
   244  ****************************/
   246 int main(void)
   247 {
   248 	// copyright banner
   249 	printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
   250 	printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
   251 	printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
   252 	printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
   253 	printf("Compiler: %s\n", VER_COMPILER);
   254 	printf("CFLAGS: %s\n", VER_CFLAGS);
   255 	printf("\n");
   257 	// set up system state
   258 	// 512K of RAM
   259 	state_init(512*1024);
   261 	// set up musashi and reset the CPU
   262 	m68k_set_cpu_type(M68K_CPU_TYPE_68010);
   263 	m68k_pulse_reset();
   265 	// Set up SDL
   266 	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
   267 		printf("Could not initialise SDL: %s.\n", SDL_GetError());
   268 		exit(EXIT_FAILURE);
   269 	}
   271 	// Make sure SDL cleans up after itself
   272 	atexit(SDL_Quit);
   274 	// Set up the video display
   275 	SDL_Surface *screen = NULL;
   276 	if ((screen = SDL_SetVideoMode(720, 384, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) {
   277 		printf("Could not find a suitable video mode: %s.\n", SDL_GetError());
   278 		exit(EXIT_FAILURE);
   279 	}
   280 	printf("Set %dx%d at %d bits-per-pixel mode\n", screen->w, screen->h, screen->format->BitsPerPixel);
   281 	SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee");
   283 	/***
   284 	 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
   285 	 * around 60Hz (???), with a 60Hz periodic interrupt.
   286 	 */
   287 	const uint32_t TIMESLOT_FREQUENCY = 240;	// Hz
   288 	const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
   289 	const uint32_t CLOCKS_PER_60HZ = (10e6 / 60);
   290 	uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
   291 	uint32_t clock_cycles = 0;
   292 	bool exitEmu = false;
   293 	for (;;) {
   294 		// Run the CPU for however many cycles we need to. CPU core clock is
   295 		// 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
   296 		// 41667 cycles per timeslot.
   297 		clock_cycles += m68k_execute(10e6/TIMESLOT_FREQUENCY);
   299 		// TODO: run DMA here
   301 		// Is it time to run the 60Hz periodic interrupt yet?
   302 		if (clock_cycles > CLOCKS_PER_60HZ) {
   303 			// TODO: refresh screen
   304 			// TODO: trigger periodic interrupt (if enabled)
   305 			// decrement clock cycle counter, we've handled the intr.
   306 			clock_cycles -= CLOCKS_PER_60HZ;
   307 		}
   309 		// make sure frame rate is equal to real time
   310 		uint32_t now = SDL_GetTicks();
   311 		if (now < next_timeslot) {
   312 			// timeslot finished early -- eat up some time
   313 			SDL_Delay(next_timeslot - now);
   314 		} else {
   315 			// timeslot finished late -- skip ahead to gain time
   316 			// TODO: if this happens a lot, we should let the user know
   317 			// that their PC might not be fast enough...
   318 			next_timeslot = now;
   319 		}
   320 		// advance to the next timeslot
   321 		next_timeslot += MILLISECS_PER_TIMESLOT;
   323 		// if we've been asked to exit the emulator, then do so.
   324 		if (exitEmu) break;
   325 	}
   327 	// shut down and exit
   328 	SDL_Quit();
   330 	return 0;
   331 }