src/main.c

Thu, 02 Dec 2010 17:01:34 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Thu, 02 Dec 2010 17:01:34 +0000
changeset 32
a44afcf2354c
parent 30
3190629004b2
child 34
e8ebd433270a
permissions
-rw-r--r--

Implement memory mapping and access checking

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@30 60 /******************
philpem@30 61 * Memory mapping
philpem@30 62 ******************/
philpem@30 63
philpem@30 64 #define MAPRAM(addr) (((uint16_t)state.map[addr*2] << 8) + ((uint16_t)state.map[(addr*2)+1]))
philpem@30 65
philpem@30 66 uint32_t mapAddr(uint32_t addr, bool writing)
philpem@30 67 {
philpem@30 68 if (addr < 0x400000) {
philpem@30 69 // RAM access. Check against the Map RAM
philpem@30 70 // Start by getting the original page address
philpem@30 71 uint16_t page = (addr >> 12) & 0x3FF;
philpem@30 72
philpem@30 73 // Look it up in the map RAM and get the physical page address
philpem@30 74 uint32_t new_page_addr = MAPRAM(page) & 0x3FF;
philpem@30 75
philpem@30 76 // Update the Page Status bits
philpem@30 77 uint8_t pagebits = (MAPRAM(page) >> 13) & 0x03;
philpem@30 78 if (pagebits != 0) {
philpem@30 79 if (writing)
philpem@32 80 state.map[page*2] |= 0x60; // Page written to (dirty)
philpem@30 81 else
philpem@32 82 state.map[page*2] |= 0x40; // Page accessed but not written
philpem@30 83 }
philpem@30 84
philpem@30 85 // Return the address with the new physical page spliced in
philpem@30 86 return (new_page_addr << 12) + (addr & 0xFFF);
philpem@30 87 } else {
philpem@30 88 // I/O, VRAM or MapRAM space; no mapping is performed or required
philpem@30 89 // TODO: assert here?
philpem@30 90 return addr;
philpem@30 91 }
philpem@30 92 }
philpem@30 93
philpem@30 94 typedef enum {
philpem@30 95 MEM_ALLOWED = 0,
philpem@30 96 MEM_PAGEFAULT, // Page fault -- page not present
philpem@30 97 MEM_PAGE_NO_WE, // Page not write enabled
philpem@30 98 MEM_KERNEL, // User attempted to access kernel memory
philpem@30 99 MEM_UIE // User Nonmemory Location Access
philpem@30 100 } MEM_STATUS;
philpem@30 101
philpem@30 102 // check memory access permissions
philpem@30 103 MEM_STATUS checkMemoryAccess(uint32_t addr, bool writing)
philpem@30 104 {
philpem@30 105 // Are we in Supervisor mode?
philpem@30 106 if (m68k_get_reg(NULL, M68K_REG_SR) & 0x2000)
philpem@30 107 // Yes. We can do anything we like.
philpem@30 108 return MEM_ALLOWED;
philpem@30 109
philpem@30 110 // If we're here, then we must be in User mode.
philpem@30 111 // Check that the user didn't access memory outside of the RAM area
philpem@30 112 if (addr >= 0x400000)
philpem@30 113 return MEM_UIE;
philpem@30 114
philpem@30 115 // This leaves us with Page Fault checking. Get the page bits for this page.
philpem@30 116 uint16_t page = (addr >> 12) & 0x3FF;
philpem@30 117 uint8_t pagebits = (MAPRAM(page) >> 13) & 0x07;
philpem@30 118
philpem@30 119 // Check page is present
philpem@30 120 if ((pagebits & 0x03) == 0)
philpem@30 121 return MEM_PAGEFAULT;
philpem@30 122
philpem@30 123 // User attempt to access the kernel
philpem@30 124 // A19, A20, A21, A22 low (kernel access): RAM addr before paging; not in Supervisor mode
philpem@30 125 if (((addr >> 19) & 0x0F) == 0)
philpem@30 126 return MEM_KERNEL;
philpem@30 127
philpem@30 128 // Check page is write enabled
philpem@30 129 if ((pagebits & 0x04) == 0)
philpem@30 130 return MEM_PAGE_NO_WE;
philpem@30 131
philpem@30 132 // Page access allowed.
philpem@30 133 return MEM_ALLOWED;
philpem@30 134 }
philpem@30 135
philpem@30 136 #undef MAPRAM
philpem@26 137
philpem@26 138 /********************************************************
philpem@26 139 * m68k memory read/write support functions for Musashi
philpem@26 140 ********************************************************/
philpem@26 141
philpem@32 142 /**
philpem@32 143 * @brief Check memory access permissions for a write operation.
philpem@32 144 * @note This used to be a single macro (merged with ACCESS_CHECK_RD), but
philpem@32 145 * gcc throws warnings when you have a return-with-value in a void
philpem@32 146 * function, even if the return-with-value is completely unreachable.
philpem@32 147 * Similarly it doesn't like it if you have a return without a value
philpem@32 148 * in a non-void function, even if it's impossible to ever reach the
philpem@32 149 * return-with-no-value. UGH!
philpem@32 150 */
philpem@32 151 #define ACCESS_CHECK_WR() do { \
philpem@32 152 /* MEM_STATUS st; */ \
philpem@32 153 switch (checkMemoryAccess(address, true)) { \
philpem@32 154 case MEM_ALLOWED: \
philpem@32 155 /* Access allowed */ \
philpem@32 156 break; \
philpem@32 157 case MEM_PAGEFAULT: \
philpem@32 158 /* Page fault */ \
philpem@32 159 state.genstat = 0x8FFF; \
philpem@32 160 m68k_pulse_bus_error(); \
philpem@32 161 return; \
philpem@32 162 case MEM_UIE: \
philpem@32 163 /* User access to memory above 4MB */ \
philpem@32 164 state.genstat = 0x9EFF; \
philpem@32 165 m68k_pulse_bus_error(); \
philpem@32 166 return; \
philpem@32 167 case MEM_KERNEL: \
philpem@32 168 case MEM_PAGE_NO_WE: \
philpem@32 169 /* kernel access or page not write enabled */ \
philpem@32 170 /* TODO: which regs need setting? */ \
philpem@32 171 m68k_pulse_bus_error(); \
philpem@32 172 return; \
philpem@32 173 } \
philpem@32 174 } while (false)
philpem@32 175
philpem@32 176 /**
philpem@32 177 * @brief Check memory access permissions for a read operation.
philpem@32 178 * @note This used to be a single macro (merged with ACCESS_CHECK_WR), but
philpem@32 179 * gcc throws warnings when you have a return-with-value in a void
philpem@32 180 * function, even if the return-with-value is completely unreachable.
philpem@32 181 * Similarly it doesn't like it if you have a return without a value
philpem@32 182 * in a non-void function, even if it's impossible to ever reach the
philpem@32 183 * return-with-no-value. UGH!
philpem@32 184 */
philpem@32 185 #define ACCESS_CHECK_RD() do { \
philpem@32 186 /* MEM_STATUS st; */ \
philpem@32 187 switch (checkMemoryAccess(address, false)) { \
philpem@32 188 case MEM_ALLOWED: \
philpem@32 189 /* Access allowed */ \
philpem@32 190 break; \
philpem@32 191 case MEM_PAGEFAULT: \
philpem@32 192 /* Page fault */ \
philpem@32 193 state.genstat = 0xCFFF; \
philpem@32 194 m68k_pulse_bus_error(); \
philpem@32 195 return 0xFFFFFFFF; \
philpem@32 196 case MEM_UIE: \
philpem@32 197 /* User access to memory above 4MB */ \
philpem@32 198 state.genstat = 0xDEFF; \
philpem@32 199 m68k_pulse_bus_error(); \
philpem@32 200 return 0xFFFFFFFF; \
philpem@32 201 case MEM_KERNEL: \
philpem@32 202 case MEM_PAGE_NO_WE: \
philpem@32 203 /* kernel access or page not write enabled */ \
philpem@32 204 /* TODO: which regs need setting? */ \
philpem@32 205 m68k_pulse_bus_error(); \
philpem@32 206 return 0xFFFFFFFF; \
philpem@32 207 } \
philpem@32 208 } while (false)
philpem@32 209
philpem@32 210 /**
philpem@32 211 * @brief Read M68K memory, 32-bit
philpem@32 212 */
philpem@4 213 uint32_t m68k_read_memory_32(uint32_t address)
philpem@4 214 {
philpem@9 215 uint32_t data = 0xFFFFFFFF;
philpem@9 216
philpem@7 217 // If ROMLMAP is set, force system to access ROM
philpem@7 218 if (!state.romlmap)
philpem@7 219 address |= 0x800000;
philpem@7 220
philpem@32 221 // Check access permissions
philpem@32 222 ACCESS_CHECK_RD();
philpem@32 223
philpem@9 224 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@7 225 // ROM access
philpem@26 226 data = RD32(state.rom, address, ROM_SIZE - 1);
philpem@26 227 } else if (address <= (state.ram_size - 1)) {
philpem@32 228 // RAM access
philpem@32 229 data = RD32(state.ram, mapAddr(address, false), state.ram_size - 1);
philpem@24 230 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
philpem@27 231 // VRAM access
philpem@26 232 data = RD32(state.vram, address, 0x7FFF);
philpem@27 233 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
philpem@27 234 // Map RAM access
philpem@27 235 data = RD32(state.map, address, 0x7FF);
philpem@21 236 } else {
philpem@21 237 // I/O register -- TODO
philpem@29 238 printf("RD32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@7 239 }
philpem@9 240 return data;
philpem@4 241 }
philpem@4 242
philpem@32 243 /**
philpem@32 244 * @brief Read M68K memory, 16-bit
philpem@32 245 */
philpem@4 246 uint32_t m68k_read_memory_16(uint32_t address)
philpem@4 247 {
philpem@9 248 uint16_t data = 0xFFFF;
philpem@9 249
philpem@9 250 // If ROMLMAP is set, force system to access ROM
philpem@9 251 if (!state.romlmap)
philpem@9 252 address |= 0x800000;
philpem@9 253
philpem@32 254 // Check access permissions
philpem@32 255 ACCESS_CHECK_RD();
philpem@32 256
philpem@10 257 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@10 258 // ROM access
philpem@26 259 data = RD16(state.rom, address, ROM_SIZE - 1);
philpem@26 260 } else if (address <= (state.ram_size - 1)) {
philpem@32 261 // RAM access
philpem@32 262 data = RD16(state.ram, mapAddr(address, false), state.ram_size - 1);
philpem@24 263 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
philpem@27 264 // VRAM access
philpem@26 265 data = RD16(state.vram, address, 0x7FFF);
philpem@27 266 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
philpem@27 267 // Map RAM access
philpem@27 268 data = RD16(state.map, address, 0x7FF);
philpem@21 269 } else {
philpem@21 270 // I/O register -- TODO
philpem@29 271 printf("RD16 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@10 272 }
philpem@9 273
philpem@9 274 return data;
philpem@4 275 }
philpem@4 276
philpem@32 277 /**
philpem@32 278 * @brief Read M68K memory, 8-bit
philpem@32 279 */
philpem@4 280 uint32_t m68k_read_memory_8(uint32_t address)
philpem@4 281 {
philpem@9 282 uint8_t data = 0xFF;
philpem@9 283
philpem@7 284 // If ROMLMAP is set, force system to access ROM
philpem@7 285 if (!state.romlmap)
philpem@7 286 address |= 0x800000;
philpem@7 287
philpem@32 288 // Check access permissions
philpem@32 289 ACCESS_CHECK_RD();
philpem@32 290
philpem@10 291 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@10 292 // ROM access
philpem@26 293 data = RD8(state.rom, address, ROM_SIZE - 1);
philpem@26 294 } else if (address <= (state.ram_size - 1)) {
philpem@32 295 // RAM access
philpem@32 296 data = RD8(state.ram, mapAddr(address, false), state.ram_size - 1);
philpem@24 297 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
philpem@27 298 // VRAM access
philpem@26 299 data = RD8(state.vram, address, 0x7FFF);
philpem@27 300 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
philpem@27 301 // Map RAM access
philpem@27 302 data = RD8(state.map, address, 0x7FF);
philpem@21 303 } else {
philpem@21 304 // I/O register -- TODO
philpem@29 305 printf("RD08 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
philpem@10 306 }
philpem@9 307
philpem@9 308 return data;
philpem@4 309 }
philpem@4 310
philpem@32 311 /**
philpem@32 312 * @brief Write M68K memory, 32-bit
philpem@32 313 */
philpem@4 314 void m68k_write_memory_32(uint32_t address, uint32_t value)
philpem@4 315 {
philpem@7 316 // If ROMLMAP is set, force system to access ROM
philpem@7 317 if (!state.romlmap)
philpem@7 318 address |= 0x800000;
philpem@7 319
philpem@32 320 // Check access permissions
philpem@32 321 ACCESS_CHECK_WR();
philpem@32 322
philpem@9 323 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@7 324 // ROM access
philpem@32 325 // According to HwNote15 (John B. Milton), there is no write protection
philpem@32 326 // here. You can write to ROM, but nothing happens.
philpem@26 327 } else if (address <= (state.ram_size - 1)) {
philpem@32 328 // RAM access
philpem@32 329 WR32(state.ram, mapAddr(address, true), state.ram_size - 1, value);
philpem@24 330 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
philpem@24 331 // VRAM access
philpem@26 332 WR32(state.vram, address, 0x7fff, value);
philpem@27 333 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
philpem@27 334 // Map RAM access
philpem@27 335 WR32(state.map, address, 0x7FF, value);
philpem@9 336 } else {
philpem@9 337 switch (address) {
philpem@21 338 case 0xE43000: state.romlmap = ((value & 0x8000) == 0x8000); break; // GCR3: ROMLMAP
philpem@29 339 default: printf("WR32 0x%08X ==> 0x%08X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : ""); break;
philpem@9 340 }
philpem@7 341 }
philpem@4 342 }
philpem@4 343
philpem@32 344 /**
philpem@32 345 * @brief Write M68K memory, 16-bit
philpem@32 346 */
philpem@4 347 void m68k_write_memory_16(uint32_t address, uint32_t value)
philpem@4 348 {
philpem@7 349 // If ROMLMAP is set, force system to access ROM
philpem@7 350 if (!state.romlmap)
philpem@7 351 address |= 0x800000;
philpem@7 352
philpem@32 353 // Check access permissions
philpem@32 354 ACCESS_CHECK_WR();
philpem@32 355
philpem@9 356 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@7 357 // ROM access
philpem@32 358 // According to HwNote15 (John B. Milton), there is no write protection
philpem@32 359 // here. You can write to ROM, but nothing happens.
philpem@26 360 } else if (address <= (state.ram_size - 1)) {
philpem@32 361 // RAM access
philpem@32 362 WR16(state.ram, mapAddr(address, true), state.ram_size - 1, value);
philpem@24 363 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
philpem@24 364 // VRAM access
philpem@26 365 WR16(state.vram, address, 0x7fff, value);
philpem@27 366 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
philpem@27 367 // Map RAM access
philpem@27 368 WR16(state.map, address, 0x7FF, value);
philpem@9 369 } else {
philpem@9 370 switch (address) {
philpem@21 371 case 0xE43000: state.romlmap = ((value & 0x8000) == 0x8000); break; // GCR3: ROMLMAP
philpem@29 372 default: printf("WR16 0x%08X ==> 0x%04X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : ""); break;
philpem@9 373 }
philpem@25 374 if (address == 0x4A0000) {
philpem@25 375 printf("\tLED WRITE: %s %s %s %s\n",
philpem@25 376 value & 0x800 ? "-" : "R",
philpem@25 377 value & 0x400 ? "-" : "G",
philpem@25 378 value & 0x200 ? "-" : "Y",
philpem@25 379 value & 0x100 ? "-" : "R"
philpem@25 380 );
philpem@25 381 }
philpem@7 382 }
philpem@4 383 }
philpem@4 384
philpem@32 385 /**
philpem@32 386 * @brief Write M68K memory, 8-bit
philpem@32 387 */
philpem@4 388 void m68k_write_memory_8(uint32_t address, uint32_t value)
philpem@4 389 {
philpem@7 390 // If ROMLMAP is set, force system to access ROM
philpem@7 391 if (!state.romlmap)
philpem@7 392 address |= 0x800000;
philpem@7 393
philpem@32 394 // Check access permissions
philpem@32 395 ACCESS_CHECK_WR();
philpem@32 396
philpem@9 397 if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
philpem@7 398 // ROM access
philpem@32 399 // According to HwNote15 (John B. Milton), there is no write protection
philpem@32 400 // here. You can write to ROM, but nothing happens.
philpem@26 401 } else if (address <= (state.ram_size - 1)) {
philpem@32 402 // RAM access
philpem@32 403 WR8(state.ram, mapAddr(address, true), state.ram_size - 1, value);
philpem@24 404 } else if ((address >= 0x420000) && (address <= 0x427FFF)) {
philpem@24 405 // VRAM access
philpem@26 406 WR8(state.vram, address, 0x7fff, value);
philpem@27 407 } else if ((address >= 0x400000) && (address <= 0x4007FF)) {
philpem@27 408 // Map RAM access
philpem@27 409 WR8(state.map, address, 0x7FF, value);
philpem@9 410 } else {
philpem@9 411 switch (address) {
philpem@21 412 case 0xE43000: state.romlmap = ((value & 0x80) == 0x80); break; // GCR3: ROMLMAP
philpem@29 413 default: printf("WR08 0x%08X ==> 0x%02X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : ""); break;
philpem@9 414 }
philpem@7 415 }
philpem@4 416 }
philpem@4 417
philpem@10 418 // for the disassembler
philpem@9 419 uint32_t m68k_read_disassembler_32(uint32_t addr) { return m68k_read_memory_32(addr); }
philpem@9 420 uint32_t m68k_read_disassembler_16(uint32_t addr) { return m68k_read_memory_16(addr); }
philpem@9 421 uint32_t m68k_read_disassembler_8 (uint32_t addr) { return m68k_read_memory_8 (addr); }
philpem@9 422
philpem@27 423
philpem@27 424 /****************************
philpem@27 425 * blessed be thy main()...
philpem@27 426 ****************************/
philpem@27 427
philpem@0 428 int main(void)
philpem@0 429 {
philpem@7 430 // copyright banner
philpem@16 431 printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
philpem@17 432 printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
philpem@17 433 printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
philpem@16 434 printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
philpem@16 435 printf("Compiler: %s\n", VER_COMPILER);
philpem@16 436 printf("CFLAGS: %s\n", VER_CFLAGS);
philpem@17 437 printf("\n");
philpem@7 438
philpem@7 439 // set up system state
philpem@7 440 // 512K of RAM
philpem@18 441 state_init(512*1024);
philpem@7 442
philpem@20 443 // set up musashi and reset the CPU
philpem@7 444 m68k_set_cpu_type(M68K_CPU_TYPE_68010);
philpem@7 445 m68k_pulse_reset();
philpem@9 446
philpem@28 447 // Set up SDL
philpem@20 448 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
philpem@20 449 printf("Could not initialise SDL: %s.\n", SDL_GetError());
philpem@28 450 exit(EXIT_FAILURE);
philpem@20 451 }
philpem@7 452
philpem@28 453 // Make sure SDL cleans up after itself
philpem@28 454 atexit(SDL_Quit);
philpem@28 455
philpem@28 456 // Set up the video display
philpem@28 457 SDL_Surface *screen = NULL;
philpem@28 458 if ((screen = SDL_SetVideoMode(720, 384, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) {
philpem@28 459 printf("Could not find a suitable video mode: %s.\n", SDL_GetError());
philpem@28 460 exit(EXIT_FAILURE);
philpem@28 461 }
philpem@32 462 printf("Set %dx%d at %d bits-per-pixel mode\n\n", screen->w, screen->h, screen->format->BitsPerPixel);
philpem@28 463 SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee");
philpem@28 464
philpem@20 465 /***
philpem@20 466 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
philpem@20 467 * around 60Hz (???), with a 60Hz periodic interrupt.
philpem@20 468 */
philpem@20 469 const uint32_t TIMESLOT_FREQUENCY = 240; // Hz
philpem@20 470 const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
philpem@20 471 const uint32_t CLOCKS_PER_60HZ = (10e6 / 60);
philpem@20 472 uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
philpem@20 473 uint32_t clock_cycles = 0;
philpem@16 474 bool exitEmu = false;
philpem@16 475 for (;;) {
philpem@20 476 // Run the CPU for however many cycles we need to. CPU core clock is
philpem@20 477 // 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
philpem@20 478 // 41667 cycles per timeslot.
philpem@20 479 clock_cycles += m68k_execute(10e6/TIMESLOT_FREQUENCY);
philpem@20 480
philpem@20 481 // TODO: run DMA here
philpem@16 482
philpem@20 483 // Is it time to run the 60Hz periodic interrupt yet?
philpem@20 484 if (clock_cycles > CLOCKS_PER_60HZ) {
philpem@20 485 // TODO: refresh screen
philpem@20 486 // TODO: trigger periodic interrupt (if enabled)
philpem@20 487 // decrement clock cycle counter, we've handled the intr.
philpem@20 488 clock_cycles -= CLOCKS_PER_60HZ;
philpem@16 489 }
philpem@16 490
philpem@20 491 // make sure frame rate is equal to real time
philpem@20 492 uint32_t now = SDL_GetTicks();
philpem@20 493 if (now < next_timeslot) {
philpem@20 494 // timeslot finished early -- eat up some time
philpem@20 495 SDL_Delay(next_timeslot - now);
philpem@20 496 } else {
philpem@20 497 // timeslot finished late -- skip ahead to gain time
philpem@20 498 // TODO: if this happens a lot, we should let the user know
philpem@20 499 // that their PC might not be fast enough...
philpem@20 500 next_timeslot = now;
philpem@20 501 }
philpem@20 502 // advance to the next timeslot
philpem@20 503 next_timeslot += MILLISECS_PER_TIMESLOT;
philpem@20 504
philpem@20 505 // if we've been asked to exit the emulator, then do so.
philpem@16 506 if (exitEmu) break;
philpem@16 507 }
philpem@7 508
philpem@7 509 // shut down and exit
philpem@20 510 SDL_Quit();
philpem@7 511
philpem@0 512 return 0;
philpem@0 513 }