src/main.c

Wed, 01 Dec 2010 22:34:15 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Wed, 01 Dec 2010 22:34:15 +0000
changeset 26
fef12817c5e8
parent 25
49526038b0fb
child 27
ceae676021ca
permissions
-rw-r--r--

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 }