Thu, 02 Dec 2010 23:37:49 +0000
add a few colour variants
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 }
102 }
103 }
105 // TODO: blit LEDs and status info
107 // Unlock the screen surface
108 if (SDL_MUSTLOCK(s)) {
109 SDL_UnlockSurface(s);
110 }
112 // Trigger a refresh -- TODO: partial refresh depending on whether we
113 // refreshed the screen area, status area, both, or none. Use SDL_UpdateRect() for this.
114 SDL_Flip(s);
115 }
117 /****************************
118 * blessed be thy main()...
119 ****************************/
121 int main(void)
122 {
123 // copyright banner
124 printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
125 printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
126 printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
127 printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
128 printf("Compiler: %s\n", VER_COMPILER);
129 printf("CFLAGS: %s\n", VER_CFLAGS);
130 printf("\n");
132 // set up system state
133 // 512K of RAM
134 state_init(512*1024);
136 // set up musashi and reset the CPU
137 m68k_set_cpu_type(M68K_CPU_TYPE_68010);
138 m68k_pulse_reset();
140 // Set up SDL
141 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
142 printf("Could not initialise SDL: %s.\n", SDL_GetError());
143 exit(EXIT_FAILURE);
144 }
146 // Make sure SDL cleans up after itself
147 atexit(SDL_Quit);
149 // Set up the video display
150 SDL_Surface *screen = NULL;
151 if ((screen = SDL_SetVideoMode(720, 384, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) {
152 printf("Could not find a suitable video mode: %s.\n", SDL_GetError());
153 exit(EXIT_FAILURE);
154 }
155 printf("Set %dx%d at %d bits-per-pixel mode\n\n", screen->w, screen->h, screen->format->BitsPerPixel);
156 SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee");
158 /***
159 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
160 * around 60Hz (???), with a 60Hz periodic interrupt.
161 */
162 const uint32_t TIMESLOT_FREQUENCY = 240; // Hz
163 const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
164 const uint32_t CLOCKS_PER_60HZ = (10e6 / 60);
165 uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
166 uint32_t clock_cycles = 0;
167 bool exitEmu = false;
168 for (;;) {
169 // Run the CPU for however many cycles we need to. CPU core clock is
170 // 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
171 // 41667 cycles per timeslot.
172 clock_cycles += m68k_execute(10e6/TIMESLOT_FREQUENCY);
174 // TODO: run DMA here
176 // Is it time to run the 60Hz periodic interrupt yet?
177 if (clock_cycles > CLOCKS_PER_60HZ) {
178 // Refresh the screen
179 refreshScreen(screen);
180 // TODO: trigger periodic interrupt (if enabled)
181 // decrement clock cycle counter, we've handled the intr.
182 clock_cycles -= CLOCKS_PER_60HZ;
183 }
185 // make sure frame rate is equal to real time
186 uint32_t now = SDL_GetTicks();
187 if (now < next_timeslot) {
188 // timeslot finished early -- eat up some time
189 SDL_Delay(next_timeslot - now);
190 } else {
191 // timeslot finished late -- skip ahead to gain time
192 // TODO: if this happens a lot, we should let the user know
193 // that their PC might not be fast enough...
194 next_timeslot = now;
195 }
196 // advance to the next timeslot
197 next_timeslot += MILLISECS_PER_TIMESLOT;
199 // if we've been asked to exit the emulator, then do so.
200 if (exitEmu) break;
201 }
203 // shut down and exit
204 SDL_Quit();
206 return 0;
207 }