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