src/main.c

Wed, 01 Dec 2010 21:29:44 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Wed, 01 Dec 2010 21:29:44 +0000
changeset 20
bea459bc22a8
parent 18
320dc6206f52
child 21
e31d2ede6c6b
permissions
-rw-r--r--

add main emulation loop with preliminary timing

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