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

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