src/main.c

Wed, 01 Dec 2010 22:11:06 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Wed, 01 Dec 2010 22:11:06 +0000
changeset 24
724d2f6deb37
parent 23
3d964a6aa59b
child 25
49526038b0fb
permissions
-rw-r--r--

add VRAM emulation

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