src/main.c

Thu, 02 Dec 2010 19:30:46 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Thu, 02 Dec 2010 19:30:46 +0000
changeset 34
e8ebd433270a
parent 32
a44afcf2354c
child 35
391318413bb2
permissions
-rw-r--r--

rewrite memory access routines

     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 /***********************************
    22  * Array read/write utility macros
    23  * "Don't Repeat Yourself" :)
    24  ***********************************/
    26 /// Array read, 32-bit
    27 #define RD32(array, address, andmask)							\
    28 	(((uint32_t)array[(address + 0) & (andmask)] << 24) |		\
    29 	 ((uint32_t)array[(address + 1) & (andmask)] << 16) |		\
    30 	 ((uint32_t)array[(address + 2) & (andmask)] << 8)  |		\
    31 	 ((uint32_t)array[(address + 3) & (andmask)]))
    33 /// Array read, 16-bit
    34 #define RD16(array, address, andmask)							\
    35 	(((uint32_t)array[(address + 0) & (andmask)] << 8)  |		\
    36 	 ((uint32_t)array[(address + 1) & (andmask)]))
    38 /// Array read, 8-bit
    39 #define RD8(array, address, andmask)							\
    40 	((uint32_t)array[(address + 0) & (andmask)])
    42 /// Array write, 32-bit
    43 #define WR32(array, address, andmask, value) {					\
    44 	array[(address + 0) & (andmask)] = (value >> 24) & 0xff;	\
    45 	array[(address + 1) & (andmask)] = (value >> 16) & 0xff;	\
    46 	array[(address + 2) & (andmask)] = (value >> 8)  & 0xff;	\
    47 	array[(address + 3) & (andmask)] =  value        & 0xff;	\
    48 }
    50 /// Array write, 16-bit
    51 #define WR16(array, address, andmask, value) {					\
    52 	array[(address + 0) & (andmask)] = (value >> 8)  & 0xff;	\
    53 	array[(address + 1) & (andmask)] =  value        & 0xff;	\
    54 }
    56 /// Array write, 8-bit
    57 #define WR8(array, address, andmask, value)						\
    58 	array[(address + 0) & (andmask)] =  value        & 0xff;
    60 /******************
    61  * Memory mapping
    62  ******************/
    64 #define MAPRAM(addr) (((uint16_t)state.map[addr*2] << 8) + ((uint16_t)state.map[(addr*2)+1]))
    66 uint32_t mapAddr(uint32_t addr, bool writing)
    67 {
    68 	if (addr < 0x400000) {
    69 		// RAM access. Check against the Map RAM
    70 		// Start by getting the original page address
    71 		uint16_t page = (addr >> 12) & 0x3FF;
    73 		// Look it up in the map RAM and get the physical page address
    74 		uint32_t new_page_addr = MAPRAM(page) & 0x3FF;
    76 		// Update the Page Status bits
    77 		uint8_t pagebits = (MAPRAM(page) >> 13) & 0x03;
    78 		if (pagebits != 0) {
    79 			if (writing)
    80 				state.map[page*2] |= 0x60;		// Page written to (dirty)
    81 			else
    82 				state.map[page*2] |= 0x40;		// Page accessed but not written
    83 		}
    85 		// Return the address with the new physical page spliced in
    86 		return (new_page_addr << 12) + (addr & 0xFFF);
    87 	} else {
    88 		// I/O, VRAM or MapRAM space; no mapping is performed or required
    89 		// TODO: assert here?
    90 		return addr;
    91 	}
    92 }
    94 typedef enum {
    95 	MEM_ALLOWED = 0,
    96 	MEM_PAGEFAULT,		// Page fault -- page not present
    97 	MEM_PAGE_NO_WE,		// Page not write enabled
    98 	MEM_KERNEL,			// User attempted to access kernel memory
    99 	MEM_UIE				// User Nonmemory Location Access
   100 } MEM_STATUS;
   102 // check memory access permissions
   103 MEM_STATUS checkMemoryAccess(uint32_t addr, bool writing)
   104 {
   105 	// Are we in Supervisor mode?
   106 	if (m68k_get_reg(NULL, M68K_REG_SR) & 0x2000)
   107 		// Yes. We can do anything we like.
   108 		return MEM_ALLOWED;
   110 	// If we're here, then we must be in User mode.
   111 	// Check that the user didn't access memory outside of the RAM area
   112 	if (addr >= 0x400000)
   113 		return MEM_UIE;
   115 	// This leaves us with Page Fault checking. Get the page bits for this page.
   116 	uint16_t page = (addr >> 12) & 0x3FF;
   117 	uint8_t pagebits = (MAPRAM(page) >> 13) & 0x07;
   119 	// Check page is present
   120 	if ((pagebits & 0x03) == 0)
   121 		return MEM_PAGEFAULT;
   123 	// User attempt to access the kernel
   124 	// A19, A20, A21, A22 low (kernel access): RAM addr before paging; not in Supervisor mode
   125 	if (((addr >> 19) & 0x0F) == 0)
   126 		return MEM_KERNEL;
   128 	// Check page is write enabled
   129 	if ((pagebits & 0x04) == 0)
   130 		return MEM_PAGE_NO_WE;
   132 	// Page access allowed.
   133 	return MEM_ALLOWED;
   134 }
   136 #undef MAPRAM
   138 /********************************************************
   139  * m68k memory read/write support functions for Musashi
   140  ********************************************************/
   142 /**
   143  * @brief	Check memory access permissions for a write operation.
   144  * @note	This used to be a single macro (merged with ACCESS_CHECK_RD), but
   145  * 			gcc throws warnings when you have a return-with-value in a void
   146  * 			function, even if the return-with-value is completely unreachable.
   147  * 			Similarly it doesn't like it if you have a return without a value
   148  * 			in a non-void function, even if it's impossible to ever reach the
   149  * 			return-with-no-value. UGH!
   150  */
   151 #define ACCESS_CHECK_WR() do {									\
   152 		/* MEM_STATUS st; */									\
   153 		switch (checkMemoryAccess(address, true)) {				\
   154 			case MEM_ALLOWED:									\
   155 				/* Access allowed */							\
   156 				break;											\
   157 			case MEM_PAGEFAULT:									\
   158 				/* Page fault */								\
   159 				state.genstat = 0x8FFF;							\
   160 				m68k_pulse_bus_error();							\
   161 				return;											\
   162 			case MEM_UIE:										\
   163 				/* User access to memory above 4MB */			\
   164 				state.genstat = 0x9EFF;							\
   165 				m68k_pulse_bus_error();							\
   166 				return;											\
   167 			case MEM_KERNEL:									\
   168 			case MEM_PAGE_NO_WE:								\
   169 				/* kernel access or page not write enabled */	\
   170 				/* TODO: which regs need setting? */			\
   171 				m68k_pulse_bus_error();							\
   172 				return;											\
   173 		}														\
   174 	} while (false)
   176 /**
   177  * @brief Check memory access permissions for a read operation.
   178  * @note	This used to be a single macro (merged with ACCESS_CHECK_WR), but
   179  * 			gcc throws warnings when you have a return-with-value in a void
   180  * 			function, even if the return-with-value is completely unreachable.
   181  * 			Similarly it doesn't like it if you have a return without a value
   182  * 			in a non-void function, even if it's impossible to ever reach the
   183  * 			return-with-no-value. UGH!
   184  */
   185 #define ACCESS_CHECK_RD() do {									\
   186 		/* MEM_STATUS st; */									\
   187 		switch (checkMemoryAccess(address, false)) {			\
   188 			case MEM_ALLOWED:									\
   189 				/* Access allowed */							\
   190 				break;											\
   191 			case MEM_PAGEFAULT:									\
   192 				/* Page fault */								\
   193 				state.genstat = 0xCFFF;							\
   194 				m68k_pulse_bus_error();							\
   195 				return 0xFFFFFFFF;								\
   196 			case MEM_UIE:										\
   197 				/* User access to memory above 4MB */			\
   198 				state.genstat = 0xDEFF;							\
   199 				m68k_pulse_bus_error();							\
   200 				return 0xFFFFFFFF;								\
   201 			case MEM_KERNEL:									\
   202 			case MEM_PAGE_NO_WE:								\
   203 				/* kernel access or page not write enabled */	\
   204 				/* TODO: which regs need setting? */			\
   205 				m68k_pulse_bus_error();							\
   206 				return 0xFFFFFFFF;								\
   207 		}														\
   208 	} while (false)
   210 /**
   211  * @brief Read M68K memory, 32-bit
   212  */
   213 uint32_t m68k_read_memory_32(uint32_t address)
   214 {
   215 	uint32_t data = 0xFFFFFFFF;
   217 	// If ROMLMAP is set, force system to access ROM
   218 	if (!state.romlmap)
   219 		address |= 0x800000;
   221 	// Check access permissions
   222 	ACCESS_CHECK_RD();
   224 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
   225 		// ROM access
   226 		data = RD32(state.rom, address, ROM_SIZE - 1);
   227 	} else if (address <= (state.ram_size - 1)) {
   228 		// RAM access
   229 		data = RD32(state.ram, mapAddr(address, false), state.ram_size - 1);
   230 	} else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
   231 		// I/O register space, zone A
   232 		printf("RD32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   233 		switch (address & 0x0F0000) {
   234 			case 0x000000:				// Map RAM access
   235 				if (address > 0x4007FF) fprintf(stderr, "NOTE: RD32 from MapRAM mirror, addr=0x%08X\n", address);
   236 				data = RD32(state.map, address, 0x7FF);
   237 				break;
   238 			case 0x010000:				// General Status Register
   239 				data = ((uint32_t)state.genstat << 16) + (uint32_t)state.genstat;
   240 				break;
   241 			case 0x020000:				// Video RAM
   242 				if (address > 0x427FFF) fprintf(stderr, "NOTE: RD32 from VideoRAM mirror, addr=0x%08X\n", address);
   243 				data = RD32(state.vram, address, 0x7FFF);
   244 				break;
   245 			case 0x030000:				// Bus Status Register 0
   246 			case 0x040000:				// Bus Status Register 1
   247 			case 0x050000:				// Phone status
   248 			case 0x060000:				// DMA Count
   249 			case 0x070000:				// Line Printer Status Register
   250 			case 0x080000:				// Real Time Clock
   251 			case 0x090000:				// Phone registers
   252 				switch (address & 0x0FF000) {
   253 					case 0x090000:		// Handset relay
   254 					case 0x098000:
   255 					case 0x091000:		// Line select 2
   256 					case 0x099000:
   257 					case 0x092000:		// Hook relay 1
   258 					case 0x09A000:
   259 					case 0x093000:		// Hook relay 2
   260 					case 0x09B000:
   261 					case 0x094000:		// Line 1 hold
   262 					case 0x09C000:
   263 					case 0x095000:		// Line 2 hold
   264 					case 0x09D000:
   265 					case 0x096000:		// Line 1 A-lead
   266 					case 0x09E000:
   267 					case 0x097000:		// Line 2 A-lead
   268 					case 0x09F000:
   269 						break;
   270 				}
   271 				break;
   272 			case 0x0A0000:				// Miscellaneous Control Register
   273 			case 0x0B0000:				// TM/DIALWR
   274 			case 0x0C0000:				// CSR
   275 			case 0x0D0000:				// DMA Address Register
   276 			case 0x0E0000:				// Disk Control Register
   277 			case 0x0F0000:				// Line Printer Data Register
   278 				break;
   279 		}
   280 	} else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
   281 		// I/O register space, zone B
   282 		printf("RD32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   283 		switch (address & 0xF00000) {
   284 			case 0xC00000:				// Expansion slots
   285 			case 0xD00000:
   286 				switch (address & 0xFC0000) {
   287 					case 0xC00000:		// Expansion slot 0
   288 					case 0xC40000:		// Expansion slot 1
   289 					case 0xC80000:		// Expansion slot 2
   290 					case 0xCC0000:		// Expansion slot 3
   291 					case 0xD00000:		// Expansion slot 4
   292 					case 0xD40000:		// Expansion slot 5
   293 					case 0xD80000:		// Expansion slot 6
   294 					case 0xDC0000:		// Expansion slot 7
   295 						fprintf(stderr, "NOTE: RD32 from expansion card space, addr=0x%08X\n", address);
   296 						break;
   297 				}
   298 				break;
   299 			case 0xE00000:				// HDC, FDC, MCR2 and RTC data bits
   300 			case 0xF00000:
   301 				switch (address & 0x070000) {
   302 					case 0x000000:		// [ef][08]xxxx ==> WD1010 hard disc controller
   303 						break;
   304 					case 0x010000:		// [ef][19]xxxx ==> WD2797 floppy disc controller
   305 						break;
   306 					case 0x020000:		// [ef][2a]xxxx ==> Miscellaneous Control Register 2
   307 						break;
   308 					case 0x030000:		// [ef][3b]xxxx ==> Real Time Clock data bits
   309 						break;
   310 					default:
   311 						fprintf(stderr, "NOTE: RD32 from undefined E/F-block address 0x%08X", address);
   312 				}
   313 		}
   314 	}
   315 	return data;
   316 }
   318 /**
   319  * @brief Read M68K memory, 16-bit
   320  */
   321 uint32_t m68k_read_memory_16(uint32_t address)
   322 {
   323 	uint16_t data = 0xFFFF;
   325 	// If ROMLMAP is set, force system to access ROM
   326 	if (!state.romlmap)
   327 		address |= 0x800000;
   329 	// Check access permissions
   330 	ACCESS_CHECK_RD();
   332 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
   333 		// ROM access
   334 		data = RD16(state.rom, address, ROM_SIZE - 1);
   335 	} else if (address <= (state.ram_size - 1)) {
   336 		// RAM access
   337 		data = RD16(state.ram, mapAddr(address, false), state.ram_size - 1);
   338 	} else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
   339 		// I/O register space, zone A
   340 		printf("RD16 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   341 		switch (address & 0x0F0000) {
   342 			case 0x000000:				// Map RAM access
   343 				if (address > 0x4007FF) fprintf(stderr, "NOTE: RD16 from MapRAM mirror, addr=0x%08X\n", address);
   344 				data = RD16(state.map, address, 0x7FF);
   345 				break;
   346 			case 0x010000:				// General Status Register
   347 				data = state.genstat;
   348 				break;
   349 			case 0x020000:				// Video RAM
   350 				if (address > 0x427FFF) fprintf(stderr, "NOTE: RD16 from VideoRAM mirror, addr=0x%08X\n", address);
   351 				data = RD16(state.vram, address, 0x7FFF);
   352 				break;
   353 			case 0x030000:				// Bus Status Register 0
   354 			case 0x040000:				// Bus Status Register 1
   355 			case 0x050000:				// Phone status
   356 			case 0x060000:				// DMA Count
   357 			case 0x070000:				// Line Printer Status Register
   358 			case 0x080000:				// Real Time Clock
   359 			case 0x090000:				// Phone registers
   360 				switch (address & 0x0FF000) {
   361 					case 0x090000:		// Handset relay
   362 					case 0x098000:
   363 					case 0x091000:		// Line select 2
   364 					case 0x099000:
   365 					case 0x092000:		// Hook relay 1
   366 					case 0x09A000:
   367 					case 0x093000:		// Hook relay 2
   368 					case 0x09B000:
   369 					case 0x094000:		// Line 1 hold
   370 					case 0x09C000:
   371 					case 0x095000:		// Line 2 hold
   372 					case 0x09D000:
   373 					case 0x096000:		// Line 1 A-lead
   374 					case 0x09E000:
   375 					case 0x097000:		// Line 2 A-lead
   376 					case 0x09F000:
   377 						break;
   378 				}
   379 				break;
   380 			case 0x0A0000:				// Miscellaneous Control Register
   381 			case 0x0B0000:				// TM/DIALWR
   382 			case 0x0C0000:				// CSR
   383 			case 0x0D0000:				// DMA Address Register
   384 			case 0x0E0000:				// Disk Control Register
   385 			case 0x0F0000:				// Line Printer Data Register
   386 				break;
   387 		}
   388 	} else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
   389 		// I/O register space, zone B
   390 		printf("RD16 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   391 		switch (address & 0xF00000) {
   392 			case 0xC00000:				// Expansion slots
   393 			case 0xD00000:
   394 				switch (address & 0xFC0000) {
   395 					case 0xC00000:		// Expansion slot 0
   396 					case 0xC40000:		// Expansion slot 1
   397 					case 0xC80000:		// Expansion slot 2
   398 					case 0xCC0000:		// Expansion slot 3
   399 					case 0xD00000:		// Expansion slot 4
   400 					case 0xD40000:		// Expansion slot 5
   401 					case 0xD80000:		// Expansion slot 6
   402 					case 0xDC0000:		// Expansion slot 7
   403 						fprintf(stderr, "NOTE: RD16 from expansion card space, addr=0x%08X\n", address);
   404 						break;
   405 				}
   406 				break;
   407 			case 0xE00000:				// HDC, FDC, MCR2 and RTC data bits
   408 			case 0xF00000:
   409 				switch (address & 0x070000) {
   410 					case 0x000000:		// [ef][08]xxxx ==> WD1010 hard disc controller
   411 						break;
   412 					case 0x010000:		// [ef][19]xxxx ==> WD2797 floppy disc controller
   413 						break;
   414 					case 0x020000:		// [ef][2a]xxxx ==> Miscellaneous Control Register 2
   415 						break;
   416 					case 0x030000:		// [ef][3b]xxxx ==> Real Time Clock data bits
   417 						break;
   418 					default:
   419 						fprintf(stderr, "NOTE: RD16 to undefined E/F-block address 0x%08X", address);
   420 				}
   421 		}
   422 	}
   423 	return data;
   424 }
   426 /**
   427  * @brief Read M68K memory, 8-bit
   428  */
   429 uint32_t m68k_read_memory_8(uint32_t address)
   430 {
   431 	uint8_t data = 0xFF;
   433 	// If ROMLMAP is set, force system to access ROM
   434 	if (!state.romlmap)
   435 		address |= 0x800000;
   437 	// Check access permissions
   438 	ACCESS_CHECK_RD();
   440 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
   441 		// ROM access
   442 		data = RD8(state.rom, address, ROM_SIZE - 1);
   443 	} else if (address <= (state.ram_size - 1)) {
   444 		// RAM access
   445 		data = RD8(state.ram, mapAddr(address, false), state.ram_size - 1);
   446 	} else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
   447 		// I/O register space, zone A
   448 		printf("RD8 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   449 		switch (address & 0x0F0000) {
   450 			case 0x000000:				// Map RAM access
   451 				if (address > 0x4007FF) fprintf(stderr, "NOTE: RD8 from MapRAM mirror, addr=0x%08X\n", address);
   452 				data = RD8(state.map, address, 0x7FF);
   453 				break;
   454 			case 0x010000:				// General Status Register
   455 				if ((address & 1) == 0)
   456 					data = (state.genstat >> 8) & 0xff;
   457 				else
   458 					data = (state.genstat)      & 0xff;
   459 				break;
   460 			case 0x020000:				// Video RAM
   461 				if (address > 0x427FFF) fprintf(stderr, "NOTE: RD8 from VideoRAM mirror, addr=0x%08X\n", address);
   462 				data = RD8(state.vram, address, 0x7FFF);
   463 				break;
   464 			case 0x030000:				// Bus Status Register 0
   465 			case 0x040000:				// Bus Status Register 1
   466 			case 0x050000:				// Phone status
   467 			case 0x060000:				// DMA Count
   468 			case 0x070000:				// Line Printer Status Register
   469 			case 0x080000:				// Real Time Clock
   470 			case 0x090000:				// Phone registers
   471 				switch (address & 0x0FF000) {
   472 					case 0x090000:		// Handset relay
   473 					case 0x098000:
   474 					case 0x091000:		// Line select 2
   475 					case 0x099000:
   476 					case 0x092000:		// Hook relay 1
   477 					case 0x09A000:
   478 					case 0x093000:		// Hook relay 2
   479 					case 0x09B000:
   480 					case 0x094000:		// Line 1 hold
   481 					case 0x09C000:
   482 					case 0x095000:		// Line 2 hold
   483 					case 0x09D000:
   484 					case 0x096000:		// Line 1 A-lead
   485 					case 0x09E000:
   486 					case 0x097000:		// Line 2 A-lead
   487 					case 0x09F000:
   488 						break;
   489 				}
   490 				break;
   491 			case 0x0A0000:				// Miscellaneous Control Register
   492 			case 0x0B0000:				// TM/DIALWR
   493 			case 0x0C0000:				// CSR
   494 			case 0x0D0000:				// DMA Address Register
   495 			case 0x0E0000:				// Disk Control Register
   496 			case 0x0F0000:				// Line Printer Data Register
   497 				break;
   498 		}
   499 	} else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
   500 		// I/O register space, zone B
   501 		printf("RD32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   502 		switch (address & 0xF00000) {
   503 			case 0xC00000:				// Expansion slots
   504 			case 0xD00000:
   505 				switch (address & 0xFC0000) {
   506 					case 0xC00000:		// Expansion slot 0
   507 					case 0xC40000:		// Expansion slot 1
   508 					case 0xC80000:		// Expansion slot 2
   509 					case 0xCC0000:		// Expansion slot 3
   510 					case 0xD00000:		// Expansion slot 4
   511 					case 0xD40000:		// Expansion slot 5
   512 					case 0xD80000:		// Expansion slot 6
   513 					case 0xDC0000:		// Expansion slot 7
   514 						fprintf(stderr, "NOTE: RD8 from expansion card address 0x%08X\n", address);
   515 						break;
   516 				}
   517 				break;
   518 			case 0xE00000:				// HDC, FDC, MCR2 and RTC data bits
   519 			case 0xF00000:
   520 				switch (address & 0x070000) {
   521 					case 0x000000:		// [ef][08]xxxx ==> WD1010 hard disc controller
   522 						break;
   523 					case 0x010000:		// [ef][19]xxxx ==> WD2797 floppy disc controller
   524 						break;
   525 					case 0x020000:		// [ef][2a]xxxx ==> Miscellaneous Control Register 2
   526 						break;
   527 					case 0x030000:		// [ef][3b]xxxx ==> Real Time Clock data bits
   528 						break;
   529 					default:
   530 						fprintf(stderr, "NOTE: RD8 from undefined E/F-block address 0x%08X", address);
   531 				}
   532 		}
   533 	}
   534 	return data;
   535 }
   537 /**
   538  * @brief Write M68K memory, 32-bit
   539  */
   540 void m68k_write_memory_32(uint32_t address, uint32_t value)
   541 {
   542 	// If ROMLMAP is set, force system to access ROM
   543 	if (!state.romlmap)
   544 		address |= 0x800000;
   546 	// Check access permissions
   547 	ACCESS_CHECK_WR();
   549 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
   550 		// ROM access
   551 		WR32(state.rom, address, ROM_SIZE - 1, value);
   552 	} else if (address <= (state.ram_size - 1)) {
   553 		// RAM access
   554 		WR32(state.ram, mapAddr(address, false), state.ram_size - 1, value);
   555 	} else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
   556 		// I/O register space, zone A
   557 		printf("WR32 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   558 		switch (address & 0x0F0000) {
   559 			case 0x000000:				// Map RAM access
   560 				if (address > 0x4007FF) fprintf(stderr, "NOTE: WR32 to MapRAM mirror, addr=0x%08X, data=0x%08X\n", address, value);
   561 				WR32(state.map, address, 0x7FF, value);
   562 				break;
   563 			case 0x010000:				// General Status Register
   564 				state.genstat = (value & 0xffff);
   565 				break;
   566 			case 0x020000:				// Video RAM
   567 				if (address > 0x427FFF) fprintf(stderr, "NOTE: WR32 to VideoRAM mirror, addr=0x%08X, data=0x%08X\n", address, value);
   568 				WR32(state.vram, address, 0x7FFF, value);
   569 				break;
   570 			case 0x030000:				// Bus Status Register 0
   571 			case 0x040000:				// Bus Status Register 1
   572 			case 0x050000:				// Phone status
   573 			case 0x060000:				// DMA Count
   574 			case 0x070000:				// Line Printer Status Register
   575 			case 0x080000:				// Real Time Clock
   576 			case 0x090000:				// Phone registers
   577 				switch (address & 0x0FF000) {
   578 					case 0x090000:		// Handset relay
   579 					case 0x098000:
   580 					case 0x091000:		// Line select 2
   581 					case 0x099000:
   582 					case 0x092000:		// Hook relay 1
   583 					case 0x09A000:
   584 					case 0x093000:		// Hook relay 2
   585 					case 0x09B000:
   586 					case 0x094000:		// Line 1 hold
   587 					case 0x09C000:
   588 					case 0x095000:		// Line 2 hold
   589 					case 0x09D000:
   590 					case 0x096000:		// Line 1 A-lead
   591 					case 0x09E000:
   592 					case 0x097000:		// Line 2 A-lead
   593 					case 0x09F000:
   594 						break;
   595 				}
   596 				break;
   597 			case 0x0A0000:				// Miscellaneous Control Register
   598 			case 0x0B0000:				// TM/DIALWR
   599 			case 0x0C0000:				// CSR
   600 			case 0x0D0000:				// DMA Address Register
   601 			case 0x0E0000:				// Disk Control Register
   602 			case 0x0F0000:				// Line Printer Data Register
   603 				break;
   604 		}
   605 	} else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
   606 		// I/O register space, zone B
   607 		printf("WR32 0x%08X ==> 0x%08X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   608 		switch (address & 0xF00000) {
   609 			case 0xC00000:				// Expansion slots
   610 			case 0xD00000:
   611 				switch (address & 0xFC0000) {
   612 					case 0xC00000:		// Expansion slot 0
   613 					case 0xC40000:		// Expansion slot 1
   614 					case 0xC80000:		// Expansion slot 2
   615 					case 0xCC0000:		// Expansion slot 3
   616 					case 0xD00000:		// Expansion slot 4
   617 					case 0xD40000:		// Expansion slot 5
   618 					case 0xD80000:		// Expansion slot 6
   619 					case 0xDC0000:		// Expansion slot 7
   620 						fprintf(stderr, "NOTE: WR32 to expansion card space, addr=0x%08X, data=0x%08X\n", address, value);
   621 						break;
   622 				}
   623 				break;
   624 			case 0xE00000:				// HDC, FDC, MCR2 and RTC data bits
   625 			case 0xF00000:
   626 				switch (address & 0x070000) {
   627 					case 0x000000:		// [ef][08]xxxx ==> WD1010 hard disc controller
   628 						break;
   629 					case 0x010000:		// [ef][19]xxxx ==> WD2797 floppy disc controller
   630 						break;
   631 					case 0x020000:		// [ef][2a]xxxx ==> Miscellaneous Control Register 2
   632 						break;
   633 					case 0x030000:		// [ef][3b]xxxx ==> Real Time Clock data bits
   634 						break;
   635 					default:
   636 						fprintf(stderr, "NOTE: WR32 to undefined E/F-block space, addr=0x%08X, data=0x%08X\n", address, value);
   637 				}
   638 		}
   639 	}
   640 }
   642 /**
   643  * @brief Write M68K memory, 16-bit
   644  */
   645 void m68k_write_memory_16(uint32_t address, uint32_t value)
   646 {
   647 	// If ROMLMAP is set, force system to access ROM
   648 	if (!state.romlmap)
   649 		address |= 0x800000;
   651 	// Check access permissions
   652 	ACCESS_CHECK_WR();
   654 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
   655 		// ROM access
   656 		WR16(state.rom, address, ROM_SIZE - 1, value);
   657 	} else if (address <= (state.ram_size - 1)) {
   658 		// RAM access
   659 		WR16(state.ram, mapAddr(address, false), state.ram_size - 1, value);
   660 	} else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
   661 		// I/O register space, zone A
   662 		printf("WR16 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   663 		switch (address & 0x0F0000) {
   664 			case 0x000000:				// Map RAM access
   665 				if (address > 0x4007FF) fprintf(stderr, "NOTE: WR16 to MapRAM mirror, addr=0x%08X, data=0x%04X\n", address, value);
   666 				WR16(state.map, address, 0x7FF, value);
   667 				break;
   668 			case 0x010000:				// General Status Register
   669 				state.genstat = (value & 0xffff);
   670 				break;
   671 			case 0x020000:				// Video RAM
   672 				if (address > 0x427FFF) fprintf(stderr, "NOTE: WR16 to VideoRAM mirror, addr=0x%08X, data=0x%04X\n", address, value);
   673 				WR16(state.vram, address, 0x7FFF, value);
   674 				break;
   675 			case 0x030000:				// Bus Status Register 0
   676 			case 0x040000:				// Bus Status Register 1
   677 			case 0x050000:				// Phone status
   678 			case 0x060000:				// DMA Count
   679 			case 0x070000:				// Line Printer Status Register
   680 			case 0x080000:				// Real Time Clock
   681 			case 0x090000:				// Phone registers
   682 				switch (address & 0x0FF000) {
   683 					case 0x090000:		// Handset relay
   684 					case 0x098000:
   685 					case 0x091000:		// Line select 2
   686 					case 0x099000:
   687 					case 0x092000:		// Hook relay 1
   688 					case 0x09A000:
   689 					case 0x093000:		// Hook relay 2
   690 					case 0x09B000:
   691 					case 0x094000:		// Line 1 hold
   692 					case 0x09C000:
   693 					case 0x095000:		// Line 2 hold
   694 					case 0x09D000:
   695 					case 0x096000:		// Line 1 A-lead
   696 					case 0x09E000:
   697 					case 0x097000:		// Line 2 A-lead
   698 					case 0x09F000:
   699 						break;
   700 				}
   701 				break;
   702 			case 0x0A0000:				// Miscellaneous Control Register
   703 			case 0x0B0000:				// TM/DIALWR
   704 			case 0x0C0000:				// CSR
   705 			case 0x0D0000:				// DMA Address Register
   706 			case 0x0E0000:				// Disk Control Register
   707 			case 0x0F0000:				// Line Printer Data Register
   708 				break;
   709 		}
   710 	} else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
   711 		// I/O register space, zone B
   712 		printf("WR16 0x%08X ==> 0x%04X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   713 		switch (address & 0xF00000) {
   714 			case 0xC00000:				// Expansion slots
   715 			case 0xD00000:
   716 				switch (address & 0xFC0000) {
   717 					case 0xC00000:		// Expansion slot 0
   718 					case 0xC40000:		// Expansion slot 1
   719 					case 0xC80000:		// Expansion slot 2
   720 					case 0xCC0000:		// Expansion slot 3
   721 					case 0xD00000:		// Expansion slot 4
   722 					case 0xD40000:		// Expansion slot 5
   723 					case 0xD80000:		// Expansion slot 6
   724 					case 0xDC0000:		// Expansion slot 7
   725 						fprintf(stderr, "NOTE: WR16 to expansion card space, addr=0x%08X, data=0x%04X\n", address, value);
   726 						break;
   727 				}
   728 				break;
   729 			case 0xE00000:				// HDC, FDC, MCR2 and RTC data bits
   730 			case 0xF00000:
   731 				switch (address & 0x070000) {
   732 					case 0x000000:		// [ef][08]xxxx ==> WD1010 hard disc controller
   733 						break;
   734 					case 0x010000:		// [ef][19]xxxx ==> WD2797 floppy disc controller
   735 						break;
   736 					case 0x020000:		// [ef][2a]xxxx ==> Miscellaneous Control Register 2
   737 						break;
   738 					case 0x030000:		// [ef][3b]xxxx ==> Real Time Clock data bits
   739 						break;
   740 					default:
   741 						fprintf(stderr, "NOTE: WR16 to undefined E/F-block space, addr=0x%08X, data=0x%04X\n", address, value);
   742 				}
   743 		}
   744 	}
   745 }
   747 /**
   748  * @brief Write M68K memory, 8-bit
   749  */
   750 void m68k_write_memory_8(uint32_t address, uint32_t value)
   751 {
   752 	// If ROMLMAP is set, force system to access ROM
   753 	if (!state.romlmap)
   754 		address |= 0x800000;
   756 	// Check access permissions
   757 	ACCESS_CHECK_WR();
   759 	if ((address >= 0x800000) && (address <= 0xBFFFFF)) {
   760 		// ROM access
   761 		WR8(state.rom, address, ROM_SIZE - 1, value);
   762 	} else if (address <= (state.ram_size - 1)) {
   763 		// RAM access
   764 		WR8(state.ram, mapAddr(address, false), state.ram_size - 1, value);
   765 	} else if ((address >= 0x400000) && (address <= 0x7FFFFF)) {
   766 		// I/O register space, zone A
   767 		printf("WR8 0x%08X ==> ??? %s\n", address, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   768 		switch (address & 0x0F0000) {
   769 			case 0x000000:				// Map RAM access
   770 				if (address > 0x4007FF) fprintf(stderr, "NOTE: WR8 to MapRAM mirror, addr=%08X, data=%02X\n", address, value);
   771 				WR8(state.map, address, 0x7FF, value);
   772 				break;
   773 			case 0x010000:				// General Status Register
   774 				state.genstat = (value & 0xffff);
   775 				break;
   776 			case 0x020000:				// Video RAM
   777 				if (address > 0x427FFF) fprintf(stderr, "NOTE: WR8 to VideoRAM mirror, addr=%08X\n, data=0x%02X", address, value);
   778 				WR8(state.vram, address, 0x7FFF, value);
   779 				break;
   780 			case 0x030000:				// Bus Status Register 0
   781 			case 0x040000:				// Bus Status Register 1
   782 			case 0x050000:				// Phone status
   783 			case 0x060000:				// DMA Count
   784 			case 0x070000:				// Line Printer Status Register
   785 			case 0x080000:				// Real Time Clock
   786 			case 0x090000:				// Phone registers
   787 				switch (address & 0x0FF000) {
   788 					case 0x090000:		// Handset relay
   789 					case 0x098000:
   790 					case 0x091000:		// Line select 2
   791 					case 0x099000:
   792 					case 0x092000:		// Hook relay 1
   793 					case 0x09A000:
   794 					case 0x093000:		// Hook relay 2
   795 					case 0x09B000:
   796 					case 0x094000:		// Line 1 hold
   797 					case 0x09C000:
   798 					case 0x095000:		// Line 2 hold
   799 					case 0x09D000:
   800 					case 0x096000:		// Line 1 A-lead
   801 					case 0x09E000:
   802 					case 0x097000:		// Line 2 A-lead
   803 					case 0x09F000:
   804 						break;
   805 				}
   806 				break;
   807 			case 0x0A0000:				// Miscellaneous Control Register
   808 			case 0x0B0000:				// TM/DIALWR
   809 			case 0x0C0000:				// CSR
   810 			case 0x0D0000:				// DMA Address Register
   811 			case 0x0E0000:				// Disk Control Register
   812 			case 0x0F0000:				// Line Printer Data Register
   813 				break;
   814 		}
   815 	} else if ((address >= 0xC00000) && (address <= 0xFFFFFF)) {
   816 		// I/O register space, zone B
   817 		printf("WR8 0x%08X ==> 0x%08X %s\n", address, value, m68k_get_reg(NULL, M68K_REG_SR) & 0x2000 ? "[SV]" : "");
   818 		switch (address & 0xF00000) {
   819 			case 0xC00000:				// Expansion slots
   820 			case 0xD00000:
   821 				switch (address & 0xFC0000) {
   822 					case 0xC00000:		// Expansion slot 0
   823 					case 0xC40000:		// Expansion slot 1
   824 					case 0xC80000:		// Expansion slot 2
   825 					case 0xCC0000:		// Expansion slot 3
   826 					case 0xD00000:		// Expansion slot 4
   827 					case 0xD40000:		// Expansion slot 5
   828 					case 0xD80000:		// Expansion slot 6
   829 					case 0xDC0000:		// Expansion slot 7
   830 						fprintf(stderr, "NOTE: WR8 to expansion card space, addr=0x%08X, data=0x%08X\n", address, value);
   831 						break;
   832 				}
   833 				break;
   834 			case 0xE00000:				// HDC, FDC, MCR2 and RTC data bits
   835 			case 0xF00000:
   836 				switch (address & 0x070000) {
   837 					case 0x000000:		// [ef][08]xxxx ==> WD1010 hard disc controller
   838 						break;
   839 					case 0x010000:		// [ef][19]xxxx ==> WD2797 floppy disc controller
   840 						break;
   841 					case 0x020000:		// [ef][2a]xxxx ==> Miscellaneous Control Register 2
   842 						break;
   843 					case 0x030000:		// [ef][3b]xxxx ==> Real Time Clock data bits
   844 						break;
   845 					default:
   846 						fprintf(stderr, "NOTE: WR8 to undefined E/F-block space, addr=0x%08X, data=0x%08X\n", address, value);
   847 				}
   848 		}
   849 	}
   850 }
   853 // for the disassembler
   854 uint32_t m68k_read_disassembler_32(uint32_t addr) { return m68k_read_memory_32(addr); }
   855 uint32_t m68k_read_disassembler_16(uint32_t addr) { return m68k_read_memory_16(addr); }
   856 uint32_t m68k_read_disassembler_8 (uint32_t addr) { return m68k_read_memory_8 (addr); }
   859 /****************************
   860  * blessed be thy main()...
   861  ****************************/
   863 int main(void)
   864 {
   865 	// copyright banner
   866 	printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
   867 	printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
   868 	printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
   869 	printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
   870 	printf("Compiler: %s\n", VER_COMPILER);
   871 	printf("CFLAGS: %s\n", VER_CFLAGS);
   872 	printf("\n");
   874 	// set up system state
   875 	// 512K of RAM
   876 	state_init(512*1024);
   878 	// set up musashi and reset the CPU
   879 	m68k_set_cpu_type(M68K_CPU_TYPE_68010);
   880 	m68k_pulse_reset();
   882 	// Set up SDL
   883 	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
   884 		printf("Could not initialise SDL: %s.\n", SDL_GetError());
   885 		exit(EXIT_FAILURE);
   886 	}
   888 	// Make sure SDL cleans up after itself
   889 	atexit(SDL_Quit);
   891 	// Set up the video display
   892 	SDL_Surface *screen = NULL;
   893 	if ((screen = SDL_SetVideoMode(720, 384, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) {
   894 		printf("Could not find a suitable video mode: %s.\n", SDL_GetError());
   895 		exit(EXIT_FAILURE);
   896 	}
   897 	printf("Set %dx%d at %d bits-per-pixel mode\n\n", screen->w, screen->h, screen->format->BitsPerPixel);
   898 	SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee");
   900 	/***
   901 	 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
   902 	 * around 60Hz (???), with a 60Hz periodic interrupt.
   903 	 */
   904 	const uint32_t TIMESLOT_FREQUENCY = 240;	// Hz
   905 	const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
   906 	const uint32_t CLOCKS_PER_60HZ = (10e6 / 60);
   907 	uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
   908 	uint32_t clock_cycles = 0;
   909 	bool exitEmu = false;
   910 	for (;;) {
   911 		// Run the CPU for however many cycles we need to. CPU core clock is
   912 		// 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
   913 		// 41667 cycles per timeslot.
   914 		clock_cycles += m68k_execute(10e6/TIMESLOT_FREQUENCY);
   916 		// TODO: run DMA here
   918 		// Is it time to run the 60Hz periodic interrupt yet?
   919 		if (clock_cycles > CLOCKS_PER_60HZ) {
   920 			// TODO: refresh screen
   921 			// TODO: trigger periodic interrupt (if enabled)
   922 			// decrement clock cycle counter, we've handled the intr.
   923 			clock_cycles -= CLOCKS_PER_60HZ;
   924 		}
   926 		// make sure frame rate is equal to real time
   927 		uint32_t now = SDL_GetTicks();
   928 		if (now < next_timeslot) {
   929 			// timeslot finished early -- eat up some time
   930 			SDL_Delay(next_timeslot - now);
   931 		} else {
   932 			// timeslot finished late -- skip ahead to gain time
   933 			// TODO: if this happens a lot, we should let the user know
   934 			// that their PC might not be fast enough...
   935 			next_timeslot = now;
   936 		}
   937 		// advance to the next timeslot
   938 		next_timeslot += MILLISECS_PER_TIMESLOT;
   940 		// if we've been asked to exit the emulator, then do so.
   941 		if (exitEmu) break;
   942 	}
   944 	// shut down and exit
   945 	SDL_Quit();
   947 	return 0;
   948 }