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

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