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