diff --git a/Makefile b/Makefile index 9e10490..51af3c4 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ DEBUG ?= 0 MEMSIZE ?= 128 +ENABLE_AUDIO ?= 1 SOURCES = $(wildcard src/*.c) @@ -54,9 +55,12 @@ endif # Basic support for changing screen res (with the MacPlusV3 ROM) DISP_WIDTH ?= 512 DISP_HEIGHT ?= 342 -CFLAGS_CFG = -DDISP_WIDTH=$(DISP_WIDTH) -DDISP_HEIGHT=$(DISP_HEIGHT) +CFLAGS_CFG = -DDISP_WIDTH=$(DISP_WIDTH) -DDISP_HEIGHT=$(DISP_HEIGHT) -DENABLE_AUDIO=$(ENABLE_AUDIO) -all: main +all: main patcher + +patcher: src/rom.c + $(CC) $(CFLAGS) -DUMAC_STANDALONE_PATCHER -o $@ $< $(MUSASHI_SRC): $(MUSASHI)/m68kops.h @@ -74,7 +78,7 @@ main: $(OBJS) clean: make -C $(MUSASHI) clean - rm -f $(MY_OBJS) main + rm -f $(MY_OBJS) main patcher ################################################################################ # Mac driver sources (no need to generally rebuild diff --git a/include/machw.h b/include/machw.h index 658e788..694c603 100644 --- a/include/machw.h +++ b/include/machw.h @@ -81,6 +81,8 @@ extern int overlay; * But, that should never happen post-boot. */ #define CLAMP_RAM_ADDR(x) ((x) >= RAM_SIZE ? (x) % RAM_SIZE : (x)) +/* Is the address (previously clamped with CLAMP_RAM_ADDR) the audio trap address? (last byte of audio data) */ +#define IS_RAM_AUDIO_TRAP(x) (x == umac_get_audio_offset() + 2 * 369) #define IS_VIA(x) ((ADR24(x) & 0xe80000) == 0xe80000) #define IS_IWM(x) ((ADR24(x) >= 0xdfe1ff) && (ADR24(x) < (0xdfe1ff + 0x2000))) diff --git a/include/scc.h b/include/scc.h index 1817d4f..c78fe08 100644 --- a/include/scc.h +++ b/include/scc.h @@ -35,6 +35,8 @@ void scc_write(unsigned int address, uint8_t data); uint8_t scc_read(unsigned int address); /* Set a new state for the DCD pins: */ void scc_set_dcd(int a, int b); +/* check if scc master interrupt is enabled */ +int scc_get_mie(); #endif diff --git a/include/umac.h b/include/umac.h index 024a174..e7798cd 100644 --- a/include/umac.h +++ b/include/umac.h @@ -34,6 +34,7 @@ int umac_loop(void); void umac_reset(void); void umac_opt_disassemble(int enable); void umac_mouse(int deltax, int deltay, int button); +void umac_absmouse(int x, int y, int button); void umac_kbd_event(uint8_t scancode, int down); static inline void umac_vsync_event(void) @@ -53,4 +54,15 @@ static inline unsigned int umac_get_fb_offset(void) return RAM_SIZE - ((DISP_WIDTH * DISP_HEIGHT / 8) + 0x380); } +#if ENABLE_AUDIO +#define umac_get_audio_offset() (RAM_SIZE - 768) +#define umac_get_audio_offset_end() (RAM_SIZE - 768 + 2 * 370) +extern unsigned first_audio_sample; +#define umac_get_first_audio_sample() (first_audio_sample) +#define umac_reset_first_audio_sample() (first_audio_sample = 0, (void)0) + +void umac_audio_trap(); +void umac_audio_cfg(int volume, int sndres); +#endif + #endif diff --git a/include/via.h b/include/via.h index 1633c14..e43ebf0 100644 --- a/include/via.h +++ b/include/via.h @@ -40,7 +40,8 @@ struct via_cb { void via_init(struct via_cb *cb); void via_write(unsigned int address, uint8_t data); uint8_t via_read(unsigned int address); -void via_tick(uint64_t time); +void via_tick(int cycles); +int via_limit_cycles(int cycles); /* Trigger an event on CA1 or CA2: */ void via_caX_event(int ca); void via_sr_rx(uint8_t val); diff --git a/src/main.c b/src/main.c index 1c68b26..23840aa 100644 --- a/src/main.c +++ b/src/main.c @@ -40,6 +40,7 @@ #include #include +#include "umac.h" #include "machw.h" #include "m68k.h" #include "via.h" @@ -70,8 +71,11 @@ static unsigned int g_int_controller_highest_int = 0; /* Highest pending interr uint8_t *_ram_base; uint8_t *_rom_base; +#if ENABLE_AUDIO +static int umac_volume, umac_sndres; +#endif int overlay = 1; -static uint64_t global_time_us = 0; +static uint64_t global_time_us = 0, global_cycles = 0; static int sim_done = 0; static jmp_buf main_loop_jb; @@ -154,8 +158,14 @@ static void via_ra_changed(uint8_t val) MDBG("OVERLAY CHANGING\n"); update_overlay_layout(); } - +#if ENABLE_AUDIO + uint8_t vol = val & 7; + if (vol != umac_volume) { + umac_volume = val & 7; + umac_audio_cfg(umac_volume, umac_sndres); + } oldval = val; +#endif } static void via_rb_changed(uint8_t val) @@ -166,7 +176,13 @@ static void via_rb_changed(uint8_t val) // 4 = mouse4 (in, mouse X2) // 3 = mouse7 (in, 0 = button pressed) // [2:0] = RTC controls - (void)val; +#if ENABLE_AUDIO + uint8_t sndres = val >> 7; + if(sndres != umac_sndres) { + umac_sndres = sndres; + umac_audio_cfg(umac_volume, umac_sndres); + } +#endif } static uint8_t via_ra_in(void) @@ -437,7 +453,13 @@ unsigned int cpu_read_long_dasm(unsigned int address) void FAST_FUNC(cpu_write_byte)(unsigned int address, unsigned int value) { if (IS_RAM(address)) { - RAM_WR8(CLAMP_RAM_ADDR(address), value); + address = CLAMP_RAM_ADDR(address); + RAM_WR8(address, value); +#if ENABLE_AUDIO + if(IS_RAM_AUDIO_TRAP(address)) { + umac_audio_trap(); + } +#endif return; } @@ -592,10 +614,35 @@ void umac_opt_disassemble(int enable) disassemble = enable; } -#define MOUSE_MAX_PENDING_PIX 30 +/* Provide mouse input (movement, button) data. + * + * X is positive going right; Y is positive going upwards. + */ +void umac_absmouse(int x, int y, int button) +{ + if (!scc_get_mie()) return; +#define MTemp_h 0x82a +#define MTemp_v 0x828 +#define CrsrNew 0x8ce +#define CrsrCouple 0x8cf -static int pending_mouse_deltax = 0; -static int pending_mouse_deltay = 0; + int oldx = RAM_RD16(MTemp_h); + int oldy = RAM_RD16(MTemp_v); + + if(x != oldx) { + RAM_WR16(MTemp_h, x); + } + + if (y != oldy) { + RAM_WR16(MTemp_v, y); + } + + if(x != oldx || y != oldy) { + RAM_WR8(CrsrNew, RAM_RD8(CrsrCouple)); + } + + via_mouse_pressed = button; +} /* Provide mouse input (movement, button) data. * @@ -603,83 +650,22 @@ static int pending_mouse_deltay = 0; */ void umac_mouse(int deltax, int deltay, int button) { - pending_mouse_deltax += deltax; - pending_mouse_deltay += deltay; - - /* Clamp if the UI has flooded with lots and lots of steps! - */ - if (pending_mouse_deltax > MOUSE_MAX_PENDING_PIX) - pending_mouse_deltax = MOUSE_MAX_PENDING_PIX; - if (pending_mouse_deltax < -MOUSE_MAX_PENDING_PIX) - pending_mouse_deltax = -MOUSE_MAX_PENDING_PIX; - if (pending_mouse_deltay > MOUSE_MAX_PENDING_PIX) - pending_mouse_deltay = MOUSE_MAX_PENDING_PIX; - if (pending_mouse_deltay < -MOUSE_MAX_PENDING_PIX) - pending_mouse_deltay = -MOUSE_MAX_PENDING_PIX; - - /* FIXME: The movement might take a little time, but this - * posts the button status immediately. Probably OK, but the - * mismatch might be perceptible. - */ - via_mouse_pressed = button; -} - -static void mouse_tick(void) -{ - /* Periodically, check if the mouse X/Y deltas are non-zero. - * If a movement is required, encode one step in X and/or Y - * and deduct from the pending delta. - * - * The step ultimately posts an SCC IRQ, so we _don't_ try to - * make any more steps while an IRQ is currently pending. - * (Currently, that means a previous step's DCD IRQ event - * hasn't yet been consumed by the OS handler. In future, if - * SCC is extended with other IRQ types, then just checking - * the IRQ status is technically too crude, but should still - * be fine given the timeframes.) - */ - if (pending_mouse_deltax == 0 && pending_mouse_deltay == 0) - return; - - if (scc_irq_state == 1) - return; - - static int old_dcd_a = 0; - static int old_dcd_b = 0; - - /* Mouse X/Y quadrature signals are wired to: - * VIA Port B[4] & SCC DCD_A for X - * VIA Port B[5] & SCC DCD_B for Y - * - * As VIA mouse signals aren't sampled until IRQ, can do this - * in one step, toggling existing DCD states and setting VIA - * either equal or opposite to DCD: - */ - int dcd_a = old_dcd_a; - int dcd_b = old_dcd_b; - int deltax = pending_mouse_deltax; - int deltay = pending_mouse_deltay; - uint8_t qb = via_quadbits; - - if (deltax) { - dcd_a = !dcd_a; - qb = (qb & ~0x10) | ((deltax < 0) == dcd_a ? 0x10 : 0); - pending_mouse_deltax += (deltax > 0) ? -1 : 1; - MDBG(" px %d, oldpx %d", pending_mouse_deltax, deltax); + if (!scc_get_mie()) return; + if(deltax) { + int16_t temp_h = RAM_RD16(MTemp_h) + deltax; + RAM_WR16(MTemp_h, temp_h); } if (deltay) { - dcd_b = !dcd_b; - qb = (qb & ~0x20) | ((deltay < 0) == dcd_b ? 0x20 : 0); - pending_mouse_deltay += (deltay > 0) ? -1 : 1; - MDBG(" py %d, oldpy %d", pending_mouse_deltay, deltay); + int16_t temp_v = RAM_RD16(MTemp_v) - deltay; + RAM_WR16(MTemp_v, temp_v); } - MDBG("\n"); - via_quadbits = qb; - old_dcd_a = dcd_a; - old_dcd_b = dcd_b; - scc_set_dcd(dcd_a, dcd_b); + if(deltax || deltay) { + RAM_WR8(CrsrNew, RAM_RD8(CrsrCouple)); + } + + via_mouse_pressed = button; } void umac_reset(void) @@ -705,13 +691,15 @@ int umac_loop(void) { setjmp(main_loop_jb); - const int us = UMAC_EXECLOOP_QUANTUM; - m68k_execute(us*8); - global_time_us += us; + int cycles = UMAC_EXECLOOP_QUANTUM * 8; + cycles = via_limit_cycles(cycles); + int used_cycles = m68k_execute(cycles); + MDBG("Asked to execute %d cycles, actual %d cycles\n", cycles, used_cycles); + global_cycles += used_cycles; + global_time_us = global_cycles / 8; // Device polling - via_tick(global_time_us); - mouse_tick(); + via_tick(used_cycles); kbd_check_work(); return sim_done; diff --git a/src/rom.c b/src/rom.c index 556c5ee..1305619 100644 --- a/src/rom.c +++ b/src/rom.c @@ -80,7 +80,7 @@ static uint32_t rom_get_version(uint8_t *rom_base) } while (0) -static void rom_patch_plusv3(uint8_t *rom_base) +static int rom_patch_plusv3(uint8_t *rom_base, int disp_width, int disp_height, int ram_size) { /* Inspired by patches in BasiliskII! */ @@ -100,138 +100,143 @@ static void rom_patch_plusv3(uint8_t *rom_base) * - No IWM init * - new Sound? */ -#if UMAC_MEMSIZE > 128 && UMAC_MEMSIZE < 512 - /* Hack to change memtop: try out a 256K Mac :) */ - for (int i = 0x376; i < 0x37e; i += 2) - ROM_WR16(i, M68K_INST_NOP); - ROM_WR16(0x376, 0x2a7c); // moveal #RAM_SIZE, A5 - ROM_WR16(0x378, RAM_SIZE >> 16); - ROM_WR16(0x37a, RAM_SIZE & 0xffff); - /* That overrides the probed memory size, but - * P_ChecksumRomAndTestMemory returns a failure code for - * things that aren't 128/512. Skip that: - */ - ROM_WR16(0x132, 0x6000); // Bra (was BEQ) - /* FIXME: We should also remove the memory probe routine, by - * allowing the ROM checksum to fail (it returns failure, then - * we carry on). This avoids wild RAM addrs being accessed. - */ -#endif + if (ram_size > 128*1024 && ram_size < 512*1024) { + /* Hack to change memtop: try out a 256K Mac :) */ + for (int i = 0x376; i < 0x37e; i += 2) + ROM_WR16(i, M68K_INST_NOP); + ROM_WR16(0x376, 0x2a7c); // moveal #ram_size, A5 + ROM_WR16(0x378, ram_size >> 16); + ROM_WR16(0x37a, ram_size & 0xffff); + /* That overrides the probed memory size, but + * P_ChecksumRomAndTestMemory returns a failure code for + * things that aren't 128/512. Skip that: + */ + ROM_WR16(0x132, 0x6000); // Bra (was BEQ) + /* FIXME: We should also remove the memory probe routine, by + * allowing the ROM checksum to fail (it returns failure, then + * we carry on). This avoids wild RAM addrs being accessed. + */ -#if DISP_WIDTH != 512 || DISP_HEIGHT != 342 -#define SCREEN_SIZE (DISP_WIDTH*DISP_HEIGHT/8) -#define SCREEN_DISTANCE_FROM_TOP (SCREEN_SIZE + 0x380) -#if (SCREEN_DISTANCE_FROM_TOP >= 65536) -#error "rom.c: Screen res patching maths won't work for a screen this large" -#endif -#define SCREEN_BASE (0x400000-SCREEN_DISTANCE_FROM_TOP) -#define SCREEN_BASE_L16 (SCREEN_BASE & 0xffff) -#define SBCOORD(x, y) (SCREEN_BASE + ((DISP_WIDTH/8)*(y)) + ((x)/8)) + /* Fix up the sound buffer as used by BootBeep */ + ROM_WR32(0x292, ram_size - 768); + } - /* Changing video res: - * - * Original 512*342 framebuffer is 0x5580 bytes; the screen - * buffer lands underneath sound/other buffers at top of mem, - * i,e, 0x3fa700 = 0x400000-0x5580-0x380. So any new buffer - * will be placed (and read out from for the GUI) at - * MEM_TOP-0x380-SCREEN_SIZE. - * - * For VGA, size is 0x9600 bytes (0x2580 words) - */ + if(disp_width != 512 || disp_height != 342) { + int screen_size = (disp_width*disp_height/8); + int screen_distance_from_top = screen_size + 0x380; + if (screen_distance_from_top >= 65536) { + RERR("rom.c: screen res patching maths won't work for a screen this large"); + return -1; + } + int screen_base = 0x400000-screen_distance_from_top; + int screen_base_l16 = screen_base & 0xffff; +#define SBCOORD(x, y) (screen_base + ((disp_width/8)*(y)) + ((x)/8)) - /* We need some space, low down, to create jump-out-and-patch - * routines where a patch is too large to put inline. The - * TestSoftware check at 0x42 isn't used: - */ - ROM_WR16(0x42, 0x6000); /* bra */ - ROM_WR16(0x44, 0x62-0x44); /* offset */ - /* Now 0x46-0x57 can be used */ - unsigned int patch_0 = 0x46; - ROM_WR16(patch_0 + 0, 0x9bfc); /* suba.l #imm32, A5 */ - ROM_WR16(patch_0 + 2, 0); /* (Could add more here) */ - ROM_WR16(patch_0 + 4, SCREEN_DISTANCE_FROM_TOP); - ROM_WR16(patch_0 + 6, 0x6000); /* bra */ - ROM_WR16(patch_0 + 8, 0x3a4 - (patch_0 + 8)); /* Return to 3a4 */ + /* Changing video res: + * + * Original 512*342 framebuffer is 0x5580 bytes; the screen + * buffer lands underneath sound/other buffers at top of mem, + * i,e, 0x3fa700 = 0x400000-0x5580-0x380. So any new buffer + * will be placed (and read out from for the GUI) at + * MEM_TOP-0x380-screen_size. + * + * For VGA, size is 0x9600 bytes (0x2580 words) + */ - /* Magic screen-related locations in Mac Plus ROM 4d1f8172: - * - * 8c : screen base addr (usually 3fa700, now 3f6680) - * 148 : screen base addr again - * 164 : u32 screen address of crash Mac/critErr hex numbers - * 188 : u16 bytes per row (critErr) - * 194 : u16 bytes per row (critErr) - * 19c : u16 (bytes per row * 6)-1 (critErr) - * 1a4 : u32 screen address of critErr twiddly pattern - * 1ee : u16 screen sie in words minus one - * 3a2 : u16 screen size in bytes (BUT can't patch immediate) - * 474 : u16 bytes per row - * 494 : u16 screen y - * 498 : u16 screen x - * a0e : y - * a10 : x - * ee2 : u16 bytes per row minus 4 (tPutIcon) - * ef2 : u16 bytes per row (tPutIcon) - * 7e0 : u32 screen address of disk icon (240, 145) - * 7f2 : u32 screen address of disk icon's symbol (248, 160) - * f0c : u32 screen address of Mac icon (240, 145) - * f18 : u32 screen address of Mac icon's face (248, 151) - * f36 : u16 bytes per row minus 2 (mPutSymbol) - * 1cd1 : hidecursor's bytes per line - * 1d48 : xres minus 32 (for cursor rect clipping) - * 1d4e : xres minus 32 - * 1d74 : y - * 1d93 : bytes per line (showcursor) - * 1e68 : y - * 1e6e : x - * 1e82 : y - */ - ROM_WR16(0x8c, SCREEN_BASE_L16); - ROM_WR16(0x148, SCREEN_BASE_L16); - ROM_WR32(0x164, SBCOORD(DISP_WIDTH/2 - (48/2), DISP_HEIGHT/2 + 8)); - ROM_WR16(0x188, DISP_WIDTH/8); - ROM_WR16(0x194, DISP_WIDTH/8); - ROM_WR16(0x19c, (6*DISP_WIDTH/8)-1); - ROM_WR32(0x1a4, SBCOORD(DISP_WIDTH/2 - 8, DISP_HEIGHT/2 + 8 + 8)); - ROM_WR16(0x1ee, (SCREEN_SIZE/4)-1); + /* We need some space, low down, to create jump-out-and-patch + * routines where a patch is too large to put inline. The + * TestSoftware check at 0x42 isn't used: + */ + ROM_WR16(0x42, 0x6000); /* bra */ + ROM_WR16(0x44, 0x62-0x44); /* offset */ + /* Now 0x46-0x57 can be used */ + unsigned int patch_0 = 0x46; + ROM_WR16(patch_0 + 0, 0x9bfc); /* suba.l #imm32, A5 */ + ROM_WR16(patch_0 + 2, 0); /* (Could add more here) */ + ROM_WR16(patch_0 + 4, screen_distance_from_top); + ROM_WR16(patch_0 + 6, 0x6000); /* bra */ + ROM_WR16(patch_0 + 8, 0x3a4 - (patch_0 + 8)); /* Return to 3a4 */ - ROM_WR32(0xf0c, SBCOORD(DISP_WIDTH/2 - 16, DISP_HEIGHT/2 - 26)); - ROM_WR32(0xf18, SBCOORD(DISP_WIDTH/2 - 8, DISP_HEIGHT/2 - 20)); - ROM_WR32(0x7e0, SBCOORD(DISP_WIDTH/2 - 16, DISP_HEIGHT/2 - 26)); - ROM_WR32(0x7f2, SBCOORD(DISP_WIDTH/2 - 8, DISP_HEIGHT/2 - 11)); + /* Magic screen-related locations in Mac Plus ROM 4d1f8172: + * + * 8c : screen base addr (usually 3fa700, now 3f6680) + * 148 : screen base addr again + * 164 : u32 screen address of crash Mac/critErr hex numbers + * 188 : u16 bytes per row (critErr) + * 194 : u16 bytes per row (critErr) + * 19c : u16 (bytes per row * 6)-1 (critErr) + * 1a4 : u32 screen address of critErr twiddly pattern + * 1ee : u16 screen sie in words minus one + * 3a2 : u16 screen size in bytes (BUT can't patch immediate) + * 474 : u16 bytes per row + * 494 : u16 screen y + * 498 : u16 screen x + * a0e : y + * a10 : x + * ee2 : u16 bytes per row minus 4 (tPutIcon) + * ef2 : u16 bytes per row (tPutIcon) + * 7e0 : u32 screen address of disk icon (240, 145) + * 7f2 : u32 screen address of disk icon's symbol (248, 160) + * f0c : u32 screen address of Mac icon (240, 145) + * f18 : u32 screen address of Mac icon's face (248, 151) + * f36 : u16 bytes per row minus 2 (mPutSymbol) + * 1cd1 : hidecursor's bytes per line + * 1d48 : xres minus 32 (for cursor rect clipping) + * 1d4e : xres minus 32 + * 1d74 : y + * 1d93 : bytes per line (showcursor) + * 1e68 : y + * 1e6e : x + * 1e82 : y + */ + ROM_WR16(0x8c, screen_base_l16); + ROM_WR16(0x148, screen_base_l16); + ROM_WR32(0x164, SBCOORD(disp_width/2 - (48/2), disp_height/2 + 8)); + ROM_WR16(0x188, disp_width/8); + ROM_WR16(0x194, disp_width/8); + ROM_WR16(0x19c, (6*disp_width/8)-1); + ROM_WR32(0x1a4, SBCOORD(disp_width/2 - 8, disp_height/2 + 8 + 8)); + ROM_WR16(0x1ee, (screen_size/4)-1); - /* Patch "SubA #$5900, A5" to subtract 0x9880. - * However... can't just patch the int16 immediate, as that's - * sign-extended (and we end up with a subtract-negative, - * i.e. an add). There isn't space here to turn it into sub.l - * so add some rigamarole to branch to some bytes stolen at - * patch_0 up above. - */ - ROM_WR16(0x3a0, 0x6000); /* bra */ - ROM_WR16(0x3a2, patch_0 - 0x3a2); /* ...to patch0, returns at 0x3a4 */ + ROM_WR32(0xf0c, SBCOORD(disp_width/2 - 16, disp_height/2 - 26)); + ROM_WR32(0xf18, SBCOORD(disp_width/2 - 8, disp_height/2 - 20)); + ROM_WR32(0x7e0, SBCOORD(disp_width/2 - 16, disp_height/2 - 26)); + ROM_WR32(0x7f2, SBCOORD(disp_width/2 - 8, disp_height/2 - 11)); - ROM_WR16(0x474, DISP_WIDTH/8); - ROM_WR16(0x494, DISP_HEIGHT); - ROM_WR16(0x498, DISP_WIDTH); - ROM_WR16(0xa0e, DISP_HEIGHT); /* copybits? */ - ROM_WR16(0xa10, DISP_WIDTH); - ROM_WR16(0xee2, (DISP_WIDTH/8)-4); /* tPutIcon bytes per row, minus 4 */ - ROM_WR16(0xef2, DISP_WIDTH/8); /* tPutIcon bytes per row */ - ROM_WR16(0xf36, (DISP_WIDTH/8)-2); /* tPutIcon bytes per row, minus 2 */ - ROM_WR8(0x1cd1, DISP_WIDTH/8); /* hidecursor */ - ROM_WR16(0x1d48, DISP_WIDTH-32); /* 1d46+2 was originally 512-32 rite? */ - ROM_WR16(0x1d4e, DISP_WIDTH-32); /* 1d4c+2 is 480, same */ - ROM_WR16(0x1d6e, DISP_HEIGHT-16); /* showcursor (YESS fixed Y crash bug!) */ - ROM_WR16(0x1d74, DISP_HEIGHT); /* showcursor */ - ROM_WR8(0x1d93, DISP_WIDTH/8); /* showcursor */ - ROM_WR16(0x1e68, DISP_HEIGHT); /* mScrnSize */ - ROM_WR16(0x1e6e, DISP_WIDTH); /* mScrnSize */ - ROM_WR16(0x1e82, DISP_HEIGHT); /* tScrnBitMap */ + /* Patch "SubA #$5900, A5" to subtract 0x9880. + * However... can't just patch the int16 immediate, as that's + * sign-extended (and we end up with a subtract-negative, + * i.e. an add). There isn't space here to turn it into sub.l + * so add some rigamarole to branch to some bytes stolen at + * patch_0 up above. + */ + ROM_WR16(0x3a0, 0x6000); /* bra */ + ROM_WR16(0x3a2, patch_0 - 0x3a2); /* ...to patch0, returns at 0x3a4 */ + + ROM_WR16(0x474, disp_width/8); + ROM_WR16(0x494, disp_height); + ROM_WR16(0x498, disp_width); + ROM_WR16(0xa0e, disp_height); /* copybits? */ + ROM_WR16(0xa10, disp_width); + ROM_WR16(0xee2, (disp_width/8)-4); /* tPutIcon bytes per row, minus 4 */ + ROM_WR16(0xef2, disp_width/8); /* tPutIcon bytes per row */ + ROM_WR16(0xf36, (disp_width/8)-2); /* tPutIcon bytes per row, minus 2 */ + ROM_WR8(0x1cd1, disp_width/8); /* hidecursor */ + ROM_WR16(0x1d48, disp_width-32); /* 1d46+2 was originally 512-32 rite? */ + ROM_WR16(0x1d4e, disp_width-32); /* 1d4c+2 is 480, same */ + ROM_WR16(0x1d6e, disp_height-16); /* showcursor (YESS fixed Y crash bug!) */ + ROM_WR16(0x1d74, disp_height); /* showcursor */ + ROM_WR8(0x1d93, disp_width/8); /* showcursor */ + ROM_WR16(0x1e68, disp_height); /* mScrnSize */ + ROM_WR16(0x1e6e, disp_width); /* mScrnSize */ + ROM_WR16(0x1e82, disp_height); /* tScrnBitMap */ + } /* FIXME: Welcome To Macintosh is drawn at the wrong position. Find where that's done. */ -#endif + return 0; } -int rom_patch(uint8_t *rom_base) +static int rom_patch1(uint8_t *rom_base, int disp_width, int disp_height, int mem_size) { uint32_t v = rom_get_version(rom_base); int r = -1; @@ -239,8 +244,7 @@ int rom_patch(uint8_t *rom_base) */ switch(v) { case ROM_PLUSv3_VERSION: - rom_patch_plusv3(rom_base); - r = 0; + r = rom_patch_plusv3(rom_base, disp_width, disp_height, mem_size); break; default: @@ -250,3 +254,103 @@ int rom_patch(uint8_t *rom_base) return r; } +#if defined(UMAC_STANDALONE_PATCHER) +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + uint8_t *rom_base; + char *rom_filename = "4D1F8172 - MacPlus v3.ROM"; + char *rom_dump_filename = NULL; // Raw bytes + char *rom_header_filename = NULL; // C header + int ch; + int disp_width = 512; + int disp_height = 342; + int ram_size = 128; + + while ((ch = getopt(argc, argv, "vm:r:w:W:")) != -1) { + switch (ch) { + case 'v': + disp_width = 640; + disp_height = 480; + break; + case 'm': + ram_size = atoi(optarg); + break; + case 'r': + rom_filename = strdup(optarg); + break; + case 'W': + rom_dump_filename = strdup(optarg); + break; + case 'w': + rom_header_filename = strdup(optarg); + break; + case '?': + abort(); + } + } + if (!rom_dump_filename && !rom_header_filename) { + printf("Must specify either a -W (binary) or -w (C header) output file"); + abort(); + } + printf("Opening ROM '%s'\n", rom_filename); + int ofd = open(rom_filename, O_RDONLY); + if (ofd < 0) { + perror("ROM"); + return 1; + } + + struct stat sb; + fstat(ofd, &sb); + off_t _rom_size = sb.st_size; + rom_base = mmap(0, _rom_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, ofd, 0); + if (rom_base == MAP_FAILED) { + printf("Can't mmap ROM!\n"); + return 1; + } + if (rom_patch1(rom_base, disp_width, disp_height, ram_size*1024)) { + printf("Failed to patch ROM\n"); + return 1; + } + if (rom_dump_filename) { + int rfd = open(rom_dump_filename, O_CREAT | O_TRUNC | O_RDWR, 0655); + if (rfd < 0) { + perror("ROM dump"); + return 1; + } + ssize_t written = write(rfd, rom_base, _rom_size); + if (written < 0) { + perror("ROM dump write"); + return 1; + } + if (written < _rom_size) { + printf("*** WARNING: Short write to %s!\n", + rom_dump_filename); + } else { + printf("Dumped ROM to %s\n", rom_dump_filename); + } + close(rfd); + } + if (rom_header_filename) { + FILE *ofd = fopen(rom_header_filename, "w"); + if (!ofd) { perror("fopen"); abort(); } + for(off_t i=0; i<_rom_size; i++) { + fprintf(ofd, "%d,", rom_base[i]); + if(i % 16 == 15) fprintf(ofd, "\n"); + } + fprintf(ofd, "\n"); + printf("Dumped ROM to %s as header\n", rom_header_filename); + fclose(ofd); + } +} +#else +int rom_patch(uint8_t *rom_base) { + return rom_patch1(rom_base, DISP_WIDTH, DISP_HEIGHT, UMAC_MEMSIZE * 1024); +} +#endif diff --git a/src/scc.c b/src/scc.c index a3ec367..6680b3e 100644 --- a/src/scc.c +++ b/src/scc.c @@ -126,6 +126,8 @@ static void scc_wr3(int AnB, uint8_t data) } } +int scc_get_mie() { return scc_mie; } + // WR9: Master Interrupt control and reset commands static void scc_wr9(uint8_t data) { diff --git a/src/unix_main.c b/src/unix_main.c index 588dba4..6ecdf05 100644 --- a/src/unix_main.c +++ b/src/unix_main.c @@ -27,6 +27,7 @@ * SOFTWARE. */ +#include #include #include #include @@ -35,6 +36,9 @@ #include #include #include +#if ENABLE_AUDIO +#include +#endif #include "SDL.h" #include "rom.h" @@ -78,6 +82,42 @@ static void copy_fb(uint32_t *fb_out, uint8_t *fb_in) } } +#if ENABLE_AUDIO +// audio_callback is called from a thread, so pending_v_retrace can't be a regular variable +atomic_int pending_v_retrace; +static int volscale; +uint8_t *audio_base; + +int16_t audio[370]; + +void umac_audio_cfg(int umac_volume, int umac_sndres) { + volscale = umac_sndres ? 0 : 65536 * umac_volume / 7; +} + +void umac_audio_trap() { + int32_t offset = 128; + uint16_t *audiodata = (uint16_t*)audio_base; + int scale = volscale; + if (!scale) { + memset(audio, 0, sizeof(audio)); + return; + } + int16_t *stream = audio; + for(int i=0; i<370; i++) { + int32_t a = (*audiodata++ & 0xff) - offset; + a = (a * scale) >> 8; + *stream++ = a; + } +} + +static void audio_callback(void *userdata, Uint8 *stream_in, int len_bytes) { + (void) userdata; + assert(len_bytes == sizeof(audio)); + atomic_store(&pending_v_retrace, 1); + memcpy(stream_in, audio, sizeof(audio)); +} +#endif + /**********************************************************************/ /* The emulator core expects to be given ROM and RAM pointers, @@ -232,7 +272,11 @@ int main(int argc, char *argv[]) SDL_Renderer *renderer; SDL_Texture *texture; - SDL_Init(SDL_INIT_VIDEO); + SDL_Init(SDL_INIT_VIDEO +#if ENABLE_AUDIO + | SDL_INIT_AUDIO +#endif +); SDL_Window *window = SDL_CreateWindow("umac", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, @@ -243,8 +287,14 @@ int main(int argc, char *argv[]) perror("SDL window"); return 1; } - SDL_SetWindowGrab(window, SDL_TRUE); - SDL_SetRelativeMouseMode(SDL_TRUE); + + static const int absmouse = 1; + if (!absmouse) { + SDL_SetWindowGrab(window, SDL_TRUE); + SDL_SetRelativeMouseMode(SDL_TRUE); + } else { + SDL_ShowCursor(SDL_DISABLE); + } SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); @@ -264,25 +314,55 @@ int main(int argc, char *argv[]) return 1; } +#if ENABLE_AUDIO + SDL_AudioSpec desired, obtained; + + audio_base = (uint8_t*)ram_base + umac_get_audio_offset(); + + SDL_zero(desired); + desired.freq = 22256; // wat + desired.channels = 1; + desired.samples = 370; + desired.userdata = (uint8_t*)ram_base + umac_get_audio_offset(); + desired.callback = audio_callback; + desired.format = AUDIO_S16; + + SDL_AudioDeviceID audio_device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0); + if (!audio_device) { + char buf[500]; + SDL_GetErrorMsg(buf, sizeof(buf)); + printf("SDL audio_deviceSDL_GetError() -> %s\n", buf); + return 1; + } +#endif + //////////////////////////////////////////////////////////////////////// // Emulator init umac_init(ram_base, rom_base, discs); umac_opt_disassemble(opt_disassemble); +#if ENABLE_AUDIO + // Default state is paused, this unpauses it + SDL_PauseAudioDevice(audio_device, 0); +#endif + //////////////////////////////////////////////////////////////////////// // Main loop int done = 0; int mouse_button = 0; +#if !ENABLE_AUDIO uint64_t last_vsync = 0; +#endif uint64_t last_1hz = 0; do { struct timeval tv_now; SDL_Event event; int mousex = 0; int mousey = 0; - + int send_mouse = 0; + static int absmousex, absmousey; if (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: @@ -299,21 +379,32 @@ int main(int argc, char *argv[]) } break; case SDL_MOUSEMOTION: + send_mouse = 1; + absmousex = event.motion.x / DISP_SCALE; + absmousey = event.motion.y / DISP_SCALE; mousex = event.motion.xrel; mousey = -event.motion.yrel; break; case SDL_MOUSEBUTTONDOWN: + send_mouse = 1; mouse_button = 1; break; case SDL_MOUSEBUTTONUP: + send_mouse = 1; mouse_button = 0; break; } } - umac_mouse(mousex, mousey, mouse_button); + if (send_mouse) { + if(absmouse) { + umac_absmouse(absmousex, absmousey, mouse_button); + } else { + umac_mouse(mousex, mousey, mouse_button); + } + } done |= umac_loop(); @@ -321,12 +412,27 @@ int main(int argc, char *argv[]) uint64_t now_usec = (tv_now.tv_sec * 1000000) + tv_now.tv_usec; /* Passage of time: */ - if ((now_usec - last_vsync) >= 16667) { +#if ENABLE_AUDIO + int do_v_retrace = atomic_exchange(&pending_v_retrace, 0); +#else + int do_v_retrace = (now_usec - last_vsync) >= 16667; +#endif + if (do_v_retrace) { umac_vsync_event(); - last_vsync = now_usec; - /* Cheapo framerate limiting: */ copy_fb(framebuffer, ram_get_base() + umac_get_fb_offset()); + + uint16_t *audioptr = (uint16_t*)((uint8_t*)ram_base + umac_get_audio_offset()); + for(int i=0; i #include +#include "m68kcpu.h" #include "via.h" #ifdef DEBUG @@ -47,7 +48,8 @@ #define VIA_T1CH 5 #define VIA_T1LL 6 #define VIA_T1LH 7 -#define VIA_T2CL 8 +#define VIA_T2CL 8 // write latch +#define VIA_T2LL 8 // read counter #define VIA_T2CH 9 #define VIA_SR 10 #define VIA_ACR 11 @@ -56,6 +58,7 @@ #define VIA_IRQ_CA 0x01 #define VIA_IRQ_CB 0x02 #define VIA_IRQ_SR 0x04 +#define VIA_IRQ_T2 0x20 #define VIA_IER 14 #define VIA_RA_ALT 15 // No-handshake version @@ -68,7 +71,7 @@ static const char *dbg_regnames[] = { "VIA_T1CH", "VIA_T1LL", "VIA_T1LH", - "VIA_T2CL", + "VIA_T2xL", "VIA_T2CH", "VIA_SR", "VIA_ACR", @@ -84,6 +87,8 @@ static int irq_status = 0; static uint8_t irq_active = 0; static uint8_t irq_enable = 0; +static uint16_t via_t2c; + void via_init(struct via_cb *cb) { for (int i = 0; i < 16; i++) @@ -224,6 +229,24 @@ void via_write(unsigned int address, uint8_t data) case VIA_PCR: VDBG("VIA PCR %02x\n", data); break; + case VIA_ACR: + if(data & 0xe0) + VDBG("VIA ACR %02x\n", data); + break; + case VIA_T2CH: + VDBG("VIA T2CH %02x [ACR=%02x]\n", data, via_regs[VIA_ACR]); + // writing T2CH loads the low 8 bits from the latch + via_t2c = via_regs[VIA_T2CL] | (data << 8); + VDBG("VIA Loaded timer 2 with %d cycles\n", via_t2c); + if(via_t2c > 0 && GET_CYCLES() > via_t2c) { + SET_CYCLES(via_t2c); + } + // writing T2CH clears associated IRQ flag + irq_active &= ~VIA_IRQ_T2; + break; + case VIA_T2CL: + VDBG("VIA T2CL %02x [ACR=%02x]\n", data, via_regs[VIA_ACR]); + break; default: VDBG("[VIA: unhandled WR %02x to %s (reg 0x%x)]\n", data, rname, r); } @@ -282,6 +305,14 @@ uint8_t via_read(unsigned int address) case VIA_IFR: data = via_read_ifr(); break; + + case VIA_T2LL: + // not right, should be reduced according to cycles since + // quantum began + data = via_t2c & 0xff; + // reading T2LL clears associated IRQ flag + irq_active &= ~VIA_IRQ_T2; + break; default: VDBG("[VIA: unhandled RD of %s (reg 0x%x)]\n", rname, r); } @@ -290,10 +321,29 @@ uint8_t via_read(unsigned int address) return data; } +int via_limit_cycles(int cycles_in) { + if (via_t2c > 0 && via_t2c < cycles_in) { + return via_t2c; + } + return cycles_in; +} + /* Time param in us */ -void via_tick(uint64_t time) +void via_tick(int cycles) { - (void)time; + int i = via_t2c; + if (i) { + int old = i; + i -= cycles; + VDBG("Timer count down %d -> %d\n", old, i); + if (i <= 0) { + i = 0; + VDBG("[VIA T2 reached zero, IRQ pending]\n"); + // irq_active |= VIA_IRQ_T2; + via_assess_irq(); + } + via_t2c = i; + } // FIXME: support actual timers.....! }