src/main.c

Mon, 06 Dec 2010 01:43:04 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Mon, 06 Dec 2010 01:43:04 +0000
changeset 54
57c6ef81ae81
parent 53
e1693c4b8a0c
child 55
ba6b8e570062
child 76
2ef98ea1e944
permissions
-rw-r--r--

fix side-select bug in WDC FDC driver, was causing all reads to occur on side0... now the Loader boots!

Loader will boot, but immediately gives up on the floppy drive... Not sure why.

     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"
    13 #include "memory.h"
    15 void FAIL(char *err)
    16 {
    17 	state_done();
    18 	fprintf(stderr, "ERROR: %s\nExiting...\n", err);
    19 	exit(EXIT_FAILURE);
    20 }
    22 /**
    23  * @brief Set the pixel at (x, y) to the given value
    24  * @note The surface must be locked before calling this!
    25  * @param	surface		SDL surface upon which to draw
    26  * @param	x			X co-ordinate
    27  * @param	y			Y co-ordinate
    28  * @param	pixel		Pixel value (from SDL_MapRGB)
    29  */
    30 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
    31 {
    32 	int bpp = surface->format->BytesPerPixel;
    33 	/* Here p is the address to the pixel we want to set */
    34 	Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
    36 	switch (bpp) {
    37 		case 1:
    38 			*p = pixel;
    39 			break;
    41 		case 2:
    42 			*(Uint16 *)p = pixel;
    43 			break;
    45 		case 3:
    46 			if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
    47 				p[0] = (pixel >> 16) & 0xff;
    48 				p[1] = (pixel >> 8) & 0xff;
    49 				p[2] = pixel & 0xff;
    50 			}
    51 			else {
    52 				p[0] = pixel & 0xff;
    53 				p[1] = (pixel >> 8) & 0xff;
    54 				p[2] = (pixel >> 16) & 0xff;
    55 			}
    56 			break;
    58 		case 4:
    59 			*(Uint32 *)p = pixel;
    60 			break;
    62 		default:
    63 			break;           /* shouldn't happen, but avoids warnings */
    64 	} // switch
    65 }
    68 /**
    69  * @brief	Refresh the screen.
    70  * @param	surface		SDL surface upon which to draw.
    71  */
    72 void refreshScreen(SDL_Surface *s)
    73 {
    74 	// Lock the screen surface (if necessary)
    75 	if (SDL_MUSTLOCK(s)) {
    76 		if (SDL_LockSurface(s) < 0) {
    77 			fprintf(stderr, "ERROR: Unable to lock screen!\n");
    78 			exit(EXIT_FAILURE);
    79 		}
    80 	}
    82 	// Map the foreground and background colours
    83 	Uint32 fg = SDL_MapRGB(s->format, 0x00, 0xFF, 0x00);	// green foreground
    84 //	Uint32 fg = SDL_MapRGB(s->format, 0xFF, 0xC1, 0x06);	// amber foreground
    85 //	Uint32 fg = SDL_MapRGB(s->format, 0xFF, 0xFF, 0xFF);	// white foreground
    86 	Uint32 bg = SDL_MapRGB(s->format, 0x00, 0x00, 0x00);	// black background
    88 	// Refresh the 3B1 screen area first. TODO: only do this if VRAM has actually changed!
    89 	uint32_t vram_address = 0;
    90 	for (int y=0; y<348; y++) {
    91 		for (int x=0; x<720; x+=16) {	// 720 pixels, monochrome, packed into 16bit words
    92 			// Get the pixel
    93 			uint16_t val = RD16(state.vram, vram_address, sizeof(state.vram)-1);
    94 			vram_address += 2;
    95 			// Now copy it to the video buffer
    96 			for (int px=0; px<16; px++) {
    97 				if (val & 1)
    98 					putpixel(s, x+px, y, fg);
    99 				else
   100 					putpixel(s, x+px, y, bg);
   101 				val >>= 1;
   102 			}
   103 		}
   104 	}
   106 	// TODO: blit LEDs and status info
   108 	// Unlock the screen surface
   109 	if (SDL_MUSTLOCK(s)) {
   110 		SDL_UnlockSurface(s);
   111 	}
   113 	// Trigger a refresh -- TODO: partial refresh depending on whether we
   114 	// refreshed the screen area, status area, both, or none. Use SDL_UpdateRect() for this.
   115 	SDL_Flip(s);
   116 }
   118 /**
   119  * @brief	Handle events posted by SDL.
   120  */
   121 bool HandleSDLEvents(SDL_Surface *screen)
   122 {
   123 	SDL_Event event;
   124 	while (SDL_PollEvent(&event))
   125 	{
   126 		switch (event.type) {
   127 			case SDL_QUIT:
   128 				// Quit button tagged. Exit.
   129 				return true;
   130 			case SDL_KEYDOWN:
   131 				switch (event.key.keysym.sym) {
   132 					case SDLK_F12:
   133 						if (event.key.keysym.mod & (KMOD_LALT | KMOD_RALT))
   134 							// ALT-F12 pressed; exit emulator
   135 							return true;
   136 						break;
   137 					default:
   138 						break;
   139 				}
   140 				break;
   141 			default:
   142 				break;
   143 		}
   144 	}
   146 	return false;
   147 }
   150 /****************************
   151  * blessed be thy main()...
   152  ****************************/
   154 int main(void)
   155 {
   156 	// copyright banner
   157 	printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
   158 	printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
   159 	printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
   160 	printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
   161 	printf("Compiler: %s\n", VER_COMPILER);
   162 	printf("CFLAGS: %s\n", VER_CFLAGS);
   163 	printf("\n");
   165 	// set up system state
   166 	// 512K of RAM
   167 	state_init(512*1024);
   169 	// set up musashi and reset the CPU
   170 	m68k_set_cpu_type(M68K_CPU_TYPE_68010);
   171 	m68k_pulse_reset();
   173 	// Set up SDL
   174 	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
   175 		printf("Could not initialise SDL: %s.\n", SDL_GetError());
   176 		exit(EXIT_FAILURE);
   177 	}
   179 	// Make sure SDL cleans up after itself
   180 	atexit(SDL_Quit);
   182 	// Set up the video display
   183 	SDL_Surface *screen = NULL;
   184 	if ((screen = SDL_SetVideoMode(720, 384, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) {
   185 		printf("Could not find a suitable video mode: %s.\n", SDL_GetError());
   186 		exit(EXIT_FAILURE);
   187 	}
   188 	printf("Set %dx%d at %d bits-per-pixel mode\n\n", screen->w, screen->h, screen->format->BitsPerPixel);
   189 	SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee");
   191 	// Load a disc image
   192 	FILE *disc = fopen("discim", "rb");
   193 	wd2797_load(&state.fdc_ctx, disc, 512, 10, 2);
   195 	/***
   196 	 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
   197 	 * around 60Hz (???), with a 60Hz periodic interrupt.
   198 	 */
   199 	const uint32_t TIMESLOT_FREQUENCY = 240;	// Hz
   200 	const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
   201 	const uint32_t CLOCKS_PER_60HZ = (10e6 / 60);
   202 	uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
   203 	uint32_t clock_cycles = 0;
   204 	bool exitEmu = false;
   205 	for (;;) {
   206 		// Run the CPU for however many cycles we need to. CPU core clock is
   207 		// 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
   208 		// 41667 cycles per timeslot.
   209 		clock_cycles += m68k_execute(10e6/TIMESLOT_FREQUENCY);
   211 		// Run the DMA engine
   212 		//
   213 		if (state.dmaen) { //((state.dma_count < 0x3fff) && state.dmaen) {
   214 			printf("DMA: copy addr=%08X count=%08X idmarw=%d dmarw=%d\n", state.dma_address, state.dma_count, state.idmarw, state.dma_reading);
   215 			if (state.dmaenb) {
   216 				state.dmaenb = false;
   217 //				state.dma_address++;
   218 				state.dma_count++;
   219 			}
   220 			// DMA ready to go -- so do it.
   221 			size_t num = 0;
   222 			while (state.dma_count < 0x4000) {
   223 				uint16_t d = 0;
   225 				// num tells us how many words we've copied. If this is greater than the per-timeslot DMA maximum, bail out!
   226 				if (num > (1e6/TIMESLOT_FREQUENCY)) break;
   228 				// Evidently we have more words to copy. Copy them.
   229 				if (!wd2797_get_drq(&state.fdc_ctx)) {
   230 					printf("\tDMABAIL: no data! dmac=%04X dmaa=%04X\n", state.dma_count, state.dma_address);
   231 					// Bail out, no data available. Try again later.
   232 					// TODO: handle HDD controller too
   233 					break;
   234 				}
   236 				if (!state.dma_reading) {
   237 					// Data available. Get it from the FDC.
   238 					d = wd2797_read_reg(&state.fdc_ctx, WD2797_REG_DATA);
   239 					d <<= 8;
   240 					d += wd2797_read_reg(&state.fdc_ctx, WD2797_REG_DATA);
   242 					// TODO: FIXME: if we get a pagefault, it NEEDS to be tagged as 'peripheral sourced'... this is a HACK!
   243 					m68k_write_memory_16(state.dma_address << 1, d);
   244 				} else {
   245 					// Data write to FDC
   246 					// TODO: FIXME: if we get a pagefault, it NEEDS to be tagged as 'peripheral sourced'... this is a HACK!
   247 					d = m68k_read_memory_16(state.dma_address << 1);
   249 					wd2797_write_reg(&state.fdc_ctx, WD2797_REG_DATA, (d >> 8));
   250 					wd2797_write_reg(&state.fdc_ctx, WD2797_REG_DATA, (d & 0xff));
   251 				}
   253 				// Increment DMA address
   254 				state.dma_address++;
   255 				// Increment number of words transferred
   256 				num++; state.dma_count++;
   257 			}
   259 			// Turn off DMA engine if we finished this cycle
   260 			if (state.dma_count >= 0x4000) {
   261 				printf("\tDMATRAN: transfer complete! dmaa=%06X, dmac=%04X\n", state.dma_address, state.dma_count);
   262 				state.dma_count = 0;
   263 				state.dmaen = false;
   264 			}
   265 		}
   267 		// Any interrupts?
   268 		if (wd2797_get_irq(&state.fdc_ctx)) {
   269 			m68k_set_irq(2);
   270 		} else {
   271 			m68k_set_irq(0);
   272 		}
   274 		// Is it time to run the 60Hz periodic interrupt yet?
   275 		if (clock_cycles > CLOCKS_PER_60HZ) {
   276 			// Refresh the screen
   277 			refreshScreen(screen);
   278 			// TODO: trigger periodic interrupt (if enabled)
   279 			// decrement clock cycle counter, we've handled the intr.
   280 			clock_cycles -= CLOCKS_PER_60HZ;
   281 		}
   283 		// handle SDL events -- returns true if we need to exit
   284 		if (HandleSDLEvents(screen))
   285 			exitEmu = true;
   287 		// make sure frame rate is equal to real time
   288 		uint32_t now = SDL_GetTicks();
   289 		if (now < next_timeslot) {
   290 			// timeslot finished early -- eat up some time
   291 			SDL_Delay(next_timeslot - now);
   292 		} else {
   293 			// timeslot finished late -- skip ahead to gain time
   294 			// TODO: if this happens a lot, we should let the user know
   295 			// that their PC might not be fast enough...
   296 			next_timeslot = now;
   297 		}
   298 		// advance to the next timeslot
   299 		next_timeslot += MILLISECS_PER_TIMESLOT;
   301 		// if we've been asked to exit the emulator, then do so.
   302 		if (exitEmu) break;
   303 	}
   305 	// Release the disc image
   306 	wd2797_unload(&state.fdc_ctx);
   307 	fclose(disc);
   309 	return 0;
   310 }