Compare commits

..

11 commits

Author SHA1 Message Date
431e905354 standalone patcher program 2025-03-27 11:42:43 -05:00
ce55830a1b disable via T2 irq for now
it broke more things than it solved
2025-03-22 12:32:53 -05:00
47b254f58e remove duplicate 'via_assess_irq' lines 2025-03-22 12:32:44 -05:00
697c8859c6 Merge remote-tracking branch 'origin/main' into soundemu 2025-03-22 10:24:07 -05:00
f8274158d4 debug prints must die 2025-03-22 10:13:26 -05:00
7b8b26f0e0 Start implementing via "timer 2"
This makes clicking "show notes" in the "Addresses" stack of
HyperCardBootSystem7.img succeed.

It also allows `play "Boing"` to work, but not `play "Boing" "a c e g"`.

It also makes running on RP2350 really, really slow.

It also makes glider come up complaining of a missing math co-processor(!?)
2025-03-22 10:04:44 -05:00
bc6529ea4b Turbocharge mouse movement
.. by storing directly into the vars used by the mac plus ROM,
rather than working through the IRQ & I/O subsystems.
2025-03-20 21:19:59 -05:00
e2a74064a0 Fix sound buffer address in BootBeep 2025-03-20 13:44:59 -05:00
4dea595da2 overlay audio data on screen 2025-03-20 13:09:07 -05:00
0be2290d4a audio: Make the last byte of the audio buffer a trap address
at that point, there should be 370 contiguous, sensible samples
available, so let the front end grab them.

Sadly this adds a test to the "store byte in RAM" fast path, but it seems
to be unavoidable.

In the unix frontend, vsync is now presented based on the SDL audio
callback when enabled, instead of gettimeofday.
2025-03-19 13:31:38 -05:00
c5ac98dffb Minimal audio implementation
This is good enough to play the system beep (which is at a multiple of
the refresh rate, so every buffer contains an integer number of cycles
of the tone)

However, because the audio buffer filling is not synchronized with
vertical retrace, it's probably not good enough for playing other
kinds of audio.
2025-03-19 11:14:10 -05:00
8 changed files with 443 additions and 221 deletions

View file

@ -27,6 +27,7 @@
DEBUG ?= 0 DEBUG ?= 0
MEMSIZE ?= 128 MEMSIZE ?= 128
ENABLE_AUDIO ?= 1
SOURCES = $(wildcard src/*.c) SOURCES = $(wildcard src/*.c)
@ -54,9 +55,12 @@ endif
# Basic support for changing screen res (with the MacPlusV3 ROM) # Basic support for changing screen res (with the MacPlusV3 ROM)
DISP_WIDTH ?= 512 DISP_WIDTH ?= 512
DISP_HEIGHT ?= 342 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 $(MUSASHI_SRC): $(MUSASHI)/m68kops.h
@ -74,7 +78,7 @@ main: $(OBJS)
clean: clean:
make -C $(MUSASHI) clean make -C $(MUSASHI) clean
rm -f $(MY_OBJS) main rm -f $(MY_OBJS) main patcher
################################################################################ ################################################################################
# Mac driver sources (no need to generally rebuild # Mac driver sources (no need to generally rebuild

View file

@ -81,6 +81,8 @@ extern int overlay;
* But, that should never happen post-boot. * But, that should never happen post-boot.
*/ */
#define CLAMP_RAM_ADDR(x) ((x) >= RAM_SIZE ? (x) % RAM_SIZE : (x)) #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_VIA(x) ((ADR24(x) & 0xe80000) == 0xe80000)
#define IS_IWM(x) ((ADR24(x) >= 0xdfe1ff) && (ADR24(x) < (0xdfe1ff + 0x2000))) #define IS_IWM(x) ((ADR24(x) >= 0xdfe1ff) && (ADR24(x) < (0xdfe1ff + 0x2000)))

View file

@ -53,4 +53,15 @@ static inline unsigned int umac_get_fb_offset(void)
return RAM_SIZE - ((DISP_WIDTH * DISP_HEIGHT / 8) + 0x380); 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 #endif

View file

@ -40,7 +40,8 @@ struct via_cb {
void via_init(struct via_cb *cb); void via_init(struct via_cb *cb);
void via_write(unsigned int address, uint8_t data); void via_write(unsigned int address, uint8_t data);
uint8_t via_read(unsigned int address); 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: */ /* Trigger an event on CA1 or CA2: */
void via_caX_event(int ca); void via_caX_event(int ca);
void via_sr_rx(uint8_t val); void via_sr_rx(uint8_t val);

View file

@ -40,6 +40,7 @@
#include <errno.h> #include <errno.h>
#include <setjmp.h> #include <setjmp.h>
#include "umac.h"
#include "machw.h" #include "machw.h"
#include "m68k.h" #include "m68k.h"
#include "via.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 *_ram_base;
uint8_t *_rom_base; uint8_t *_rom_base;
#if ENABLE_AUDIO
static int umac_volume, umac_sndres;
#endif
int overlay = 1; 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 int sim_done = 0;
static jmp_buf main_loop_jb; static jmp_buf main_loop_jb;
@ -154,8 +158,14 @@ static void via_ra_changed(uint8_t val)
MDBG("OVERLAY CHANGING\n"); MDBG("OVERLAY CHANGING\n");
update_overlay_layout(); 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; oldval = val;
#endif
} }
static void via_rb_changed(uint8_t val) 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) // 4 = mouse4 (in, mouse X2)
// 3 = mouse7 (in, 0 = button pressed) // 3 = mouse7 (in, 0 = button pressed)
// [2:0] = RTC controls // [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) 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) void FAST_FUNC(cpu_write_byte)(unsigned int address, unsigned int value)
{ {
if (IS_RAM(address)) { 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; return;
} }
@ -592,94 +614,32 @@ void umac_opt_disassemble(int enable)
disassemble = enable; disassemble = enable;
} }
#define MOUSE_MAX_PENDING_PIX 30
static int pending_mouse_deltax = 0;
static int pending_mouse_deltay = 0;
/* Provide mouse input (movement, button) data. /* Provide mouse input (movement, button) data.
* *
* X is positive going right; Y is positive going upwards. * X is positive going right; Y is positive going upwards.
*/ */
void umac_mouse(int deltax, int deltay, int button) void umac_mouse(int deltax, int deltay, int button)
{ {
pending_mouse_deltax += deltax; #define MTemp_h 0x82a
pending_mouse_deltay += deltay; #define MTemp_v 0x828
#define CrsrNew 0x8ce
/* Clamp if the UI has flooded with lots and lots of steps! #define CrsrCouple 0x8cf
*/
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) { if(deltax) {
dcd_a = !dcd_a; int16_t temp_h = RAM_RD16(MTemp_h) + deltax;
qb = (qb & ~0x10) | ((deltax < 0) == dcd_a ? 0x10 : 0); RAM_WR16(MTemp_h, temp_h);
pending_mouse_deltax += (deltax > 0) ? -1 : 1;
MDBG(" px %d, oldpx %d", pending_mouse_deltax, deltax);
} }
if (deltay) { if (deltay) {
dcd_b = !dcd_b; int16_t temp_v = RAM_RD16(MTemp_v) - deltay;
qb = (qb & ~0x20) | ((deltay < 0) == dcd_b ? 0x20 : 0); RAM_WR16(MTemp_v, temp_v);
pending_mouse_deltay += (deltay > 0) ? -1 : 1;
MDBG(" py %d, oldpy %d", pending_mouse_deltay, deltay);
} }
MDBG("\n");
via_quadbits = qb; if(deltax || deltay) {
old_dcd_a = dcd_a; RAM_WR8(CrsrNew, RAM_RD8(CrsrCouple));
old_dcd_b = dcd_b; }
scc_set_dcd(dcd_a, dcd_b);
via_mouse_pressed = button;
} }
void umac_reset(void) void umac_reset(void)
@ -705,13 +665,15 @@ int umac_loop(void)
{ {
setjmp(main_loop_jb); setjmp(main_loop_jb);
const int us = UMAC_EXECLOOP_QUANTUM; int cycles = UMAC_EXECLOOP_QUANTUM * 8;
m68k_execute(us*8); cycles = via_limit_cycles(cycles);
global_time_us += us; 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 // Device polling
via_tick(global_time_us); via_tick(used_cycles);
mouse_tick();
kbd_check_work(); kbd_check_work();
return sim_done; return sim_done;

208
src/rom.c
View file

@ -80,7 +80,7 @@ static uint32_t rom_get_version(uint8_t *rom_base)
} while (0) } 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! /* Inspired by patches in BasiliskII!
*/ */
@ -100,13 +100,13 @@ static void rom_patch_plusv3(uint8_t *rom_base)
* - No IWM init * - No IWM init
* - new Sound? * - new Sound?
*/ */
#if UMAC_MEMSIZE > 128 && UMAC_MEMSIZE < 512 if (ram_size > 128*1024 && ram_size < 512*1024) {
/* Hack to change memtop: try out a 256K Mac :) */ /* Hack to change memtop: try out a 256K Mac :) */
for (int i = 0x376; i < 0x37e; i += 2) for (int i = 0x376; i < 0x37e; i += 2)
ROM_WR16(i, M68K_INST_NOP); ROM_WR16(i, M68K_INST_NOP);
ROM_WR16(0x376, 0x2a7c); // moveal #RAM_SIZE, A5 ROM_WR16(0x376, 0x2a7c); // moveal #ram_size, A5
ROM_WR16(0x378, RAM_SIZE >> 16); ROM_WR16(0x378, ram_size >> 16);
ROM_WR16(0x37a, RAM_SIZE & 0xffff); ROM_WR16(0x37a, ram_size & 0xffff);
/* That overrides the probed memory size, but /* That overrides the probed memory size, but
* P_ChecksumRomAndTestMemory returns a failure code for * P_ChecksumRomAndTestMemory returns a failure code for
* things that aren't 128/512. Skip that: * things that aren't 128/512. Skip that:
@ -116,17 +116,21 @@ static void rom_patch_plusv3(uint8_t *rom_base)
* allowing the ROM checksum to fail (it returns failure, then * allowing the ROM checksum to fail (it returns failure, then
* we carry on). This avoids wild RAM addrs being accessed. * we carry on). This avoids wild RAM addrs being accessed.
*/ */
#endif
#if DISP_WIDTH != 512 || DISP_HEIGHT != 342 /* Fix up the sound buffer as used by BootBeep */
#define SCREEN_SIZE (DISP_WIDTH*DISP_HEIGHT/8) ROM_WR32(0x292, ram_size - 768);
#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" if(disp_width != 512 || disp_height != 342) {
#endif int screen_size = (disp_width*disp_height/8);
#define SCREEN_BASE (0x400000-SCREEN_DISTANCE_FROM_TOP) int screen_distance_from_top = screen_size + 0x380;
#define SCREEN_BASE_L16 (SCREEN_BASE & 0xffff) if (screen_distance_from_top >= 65536) {
#define SBCOORD(x, y) (SCREEN_BASE + ((DISP_WIDTH/8)*(y)) + ((x)/8)) 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))
/* Changing video res: /* Changing video res:
* *
@ -134,7 +138,7 @@ static void rom_patch_plusv3(uint8_t *rom_base)
* buffer lands underneath sound/other buffers at top of mem, * buffer lands underneath sound/other buffers at top of mem,
* i,e, 0x3fa700 = 0x400000-0x5580-0x380. So any new buffer * i,e, 0x3fa700 = 0x400000-0x5580-0x380. So any new buffer
* will be placed (and read out from for the GUI) at * will be placed (and read out from for the GUI) at
* MEM_TOP-0x380-SCREEN_SIZE. * MEM_TOP-0x380-screen_size.
* *
* For VGA, size is 0x9600 bytes (0x2580 words) * For VGA, size is 0x9600 bytes (0x2580 words)
*/ */
@ -149,7 +153,7 @@ static void rom_patch_plusv3(uint8_t *rom_base)
unsigned int patch_0 = 0x46; unsigned int patch_0 = 0x46;
ROM_WR16(patch_0 + 0, 0x9bfc); /* suba.l #imm32, A5 */ ROM_WR16(patch_0 + 0, 0x9bfc); /* suba.l #imm32, A5 */
ROM_WR16(patch_0 + 2, 0); /* (Could add more here) */ ROM_WR16(patch_0 + 2, 0); /* (Could add more here) */
ROM_WR16(patch_0 + 4, SCREEN_DISTANCE_FROM_TOP); ROM_WR16(patch_0 + 4, screen_distance_from_top);
ROM_WR16(patch_0 + 6, 0x6000); /* bra */ ROM_WR16(patch_0 + 6, 0x6000); /* bra */
ROM_WR16(patch_0 + 8, 0x3a4 - (patch_0 + 8)); /* Return to 3a4 */ ROM_WR16(patch_0 + 8, 0x3a4 - (patch_0 + 8)); /* Return to 3a4 */
@ -185,19 +189,19 @@ static void rom_patch_plusv3(uint8_t *rom_base)
* 1e6e : x * 1e6e : x
* 1e82 : y * 1e82 : y
*/ */
ROM_WR16(0x8c, SCREEN_BASE_L16); ROM_WR16(0x8c, screen_base_l16);
ROM_WR16(0x148, SCREEN_BASE_L16); ROM_WR16(0x148, screen_base_l16);
ROM_WR32(0x164, SBCOORD(DISP_WIDTH/2 - (48/2), DISP_HEIGHT/2 + 8)); ROM_WR32(0x164, SBCOORD(disp_width/2 - (48/2), disp_height/2 + 8));
ROM_WR16(0x188, DISP_WIDTH/8); ROM_WR16(0x188, disp_width/8);
ROM_WR16(0x194, DISP_WIDTH/8); ROM_WR16(0x194, disp_width/8);
ROM_WR16(0x19c, (6*DISP_WIDTH/8)-1); ROM_WR16(0x19c, (6*disp_width/8)-1);
ROM_WR32(0x1a4, SBCOORD(DISP_WIDTH/2 - 8, DISP_HEIGHT/2 + 8 + 8)); ROM_WR32(0x1a4, SBCOORD(disp_width/2 - 8, disp_height/2 + 8 + 8));
ROM_WR16(0x1ee, (SCREEN_SIZE/4)-1); ROM_WR16(0x1ee, (screen_size/4)-1);
ROM_WR32(0xf0c, SBCOORD(DISP_WIDTH/2 - 16, DISP_HEIGHT/2 - 26)); 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(0xf18, SBCOORD(disp_width/2 - 8, disp_height/2 - 20));
ROM_WR32(0x7e0, SBCOORD(DISP_WIDTH/2 - 16, DISP_HEIGHT/2 - 26)); 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_WR32(0x7f2, SBCOORD(disp_width/2 - 8, disp_height/2 - 11));
/* Patch "SubA #$5900, A5" to subtract 0x9880. /* Patch "SubA #$5900, A5" to subtract 0x9880.
* However... can't just patch the int16 immediate, as that's * However... can't just patch the int16 immediate, as that's
@ -209,29 +213,30 @@ static void rom_patch_plusv3(uint8_t *rom_base)
ROM_WR16(0x3a0, 0x6000); /* bra */ ROM_WR16(0x3a0, 0x6000); /* bra */
ROM_WR16(0x3a2, patch_0 - 0x3a2); /* ...to patch0, returns at 0x3a4 */ ROM_WR16(0x3a2, patch_0 - 0x3a2); /* ...to patch0, returns at 0x3a4 */
ROM_WR16(0x474, DISP_WIDTH/8); ROM_WR16(0x474, disp_width/8);
ROM_WR16(0x494, DISP_HEIGHT); ROM_WR16(0x494, disp_height);
ROM_WR16(0x498, DISP_WIDTH); ROM_WR16(0x498, disp_width);
ROM_WR16(0xa0e, DISP_HEIGHT); /* copybits? */ ROM_WR16(0xa0e, disp_height); /* copybits? */
ROM_WR16(0xa10, DISP_WIDTH); ROM_WR16(0xa10, disp_width);
ROM_WR16(0xee2, (DISP_WIDTH/8)-4); /* tPutIcon bytes per row, minus 4 */ 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(0xef2, disp_width/8); /* tPutIcon bytes per row */
ROM_WR16(0xf36, (DISP_WIDTH/8)-2); /* tPutIcon bytes per row, minus 2 */ ROM_WR16(0xf36, (disp_width/8)-2); /* tPutIcon bytes per row, minus 2 */
ROM_WR8(0x1cd1, DISP_WIDTH/8); /* hidecursor */ ROM_WR8(0x1cd1, disp_width/8); /* hidecursor */
ROM_WR16(0x1d48, DISP_WIDTH-32); /* 1d46+2 was originally 512-32 rite? */ 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(0x1d4e, disp_width-32); /* 1d4c+2 is 480, same */
ROM_WR16(0x1d6e, DISP_HEIGHT-16); /* showcursor (YESS fixed Y crash bug!) */ ROM_WR16(0x1d6e, disp_height-16); /* showcursor (YESS fixed Y crash bug!) */
ROM_WR16(0x1d74, DISP_HEIGHT); /* showcursor */ ROM_WR16(0x1d74, disp_height); /* showcursor */
ROM_WR8(0x1d93, DISP_WIDTH/8); /* showcursor */ ROM_WR8(0x1d93, disp_width/8); /* showcursor */
ROM_WR16(0x1e68, DISP_HEIGHT); /* mScrnSize */ ROM_WR16(0x1e68, disp_height); /* mScrnSize */
ROM_WR16(0x1e6e, DISP_WIDTH); /* mScrnSize */ ROM_WR16(0x1e6e, disp_width); /* mScrnSize */
ROM_WR16(0x1e82, DISP_HEIGHT); /* tScrnBitMap */ ROM_WR16(0x1e82, disp_height); /* tScrnBitMap */
/* FIXME: Welcome To Macintosh is drawn at the wrong position. Find where that's done. */
#endif
} }
int rom_patch(uint8_t *rom_base) /* FIXME: Welcome To Macintosh is drawn at the wrong position. Find where that's done. */
return 0;
}
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); uint32_t v = rom_get_version(rom_base);
int r = -1; int r = -1;
@ -239,8 +244,7 @@ int rom_patch(uint8_t *rom_base)
*/ */
switch(v) { switch(v) {
case ROM_PLUSv3_VERSION: case ROM_PLUSv3_VERSION:
rom_patch_plusv3(rom_base); r = rom_patch_plusv3(rom_base, disp_width, disp_height, mem_size);
r = 0;
break; break;
default: default:
@ -250,3 +254,103 @@ int rom_patch(uint8_t *rom_base)
return r; return r;
} }
#if defined(UMAC_STANDALONE_PATCHER)
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
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

View file

@ -27,6 +27,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <fcntl.h> #include <fcntl.h>
#include <getopt.h> #include <getopt.h>
@ -35,6 +36,9 @@
#include <sys/time.h> #include <sys/time.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#if ENABLE_AUDIO
#include <stdatomic.h>
#endif
#include "SDL.h" #include "SDL.h"
#include "rom.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, /* 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_Renderer *renderer;
SDL_Texture *texture; 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_Window *window = SDL_CreateWindow("umac",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
@ -264,18 +308,47 @@ int main(int argc, char *argv[])
return 1; 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 // Emulator init
umac_init(ram_base, rom_base, discs); umac_init(ram_base, rom_base, discs);
umac_opt_disassemble(opt_disassemble); umac_opt_disassemble(opt_disassemble);
#if ENABLE_AUDIO
// Default state is paused, this unpauses it
SDL_PauseAudioDevice(audio_device, 0);
#endif
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Main loop // Main loop
int done = 0; int done = 0;
int mouse_button = 0; int mouse_button = 0;
#if !ENABLE_AUDIO
uint64_t last_vsync = 0; uint64_t last_vsync = 0;
#endif
uint64_t last_1hz = 0; uint64_t last_1hz = 0;
do { do {
struct timeval tv_now; struct timeval tv_now;
@ -321,12 +394,27 @@ int main(int argc, char *argv[])
uint64_t now_usec = (tv_now.tv_sec * 1000000) + tv_now.tv_usec; uint64_t now_usec = (tv_now.tv_sec * 1000000) + tv_now.tv_usec;
/* Passage of time: */ /* 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(); umac_vsync_event();
last_vsync = now_usec;
/* Cheapo framerate limiting: */
copy_fb(framebuffer, ram_get_base() + umac_get_fb_offset()); 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<DISP_HEIGHT; i++) {
int d = *audioptr++ & 0xff;
for(int j=0; j<8; j++) {
if (d & (1 << j)) {
uint32_t fbdata = framebuffer[j + i * DISP_WIDTH];
fbdata = (fbdata & ~0xff) | ((d & (1 << j)) ? 0xff : 0);
framebuffer[j + i * DISP_WIDTH] = fbdata;
}
}
}
SDL_UpdateTexture(texture, NULL, framebuffer, SDL_UpdateTexture(texture, NULL, framebuffer,
DISP_WIDTH * sizeof (Uint32)); DISP_WIDTH * sizeof (Uint32));
/* Scales texture up to window size */ /* Scales texture up to window size */

View file

@ -29,6 +29,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include "m68kcpu.h"
#include "via.h" #include "via.h"
#ifdef DEBUG #ifdef DEBUG
@ -47,7 +48,8 @@
#define VIA_T1CH 5 #define VIA_T1CH 5
#define VIA_T1LL 6 #define VIA_T1LL 6
#define VIA_T1LH 7 #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_T2CH 9
#define VIA_SR 10 #define VIA_SR 10
#define VIA_ACR 11 #define VIA_ACR 11
@ -56,6 +58,7 @@
#define VIA_IRQ_CA 0x01 #define VIA_IRQ_CA 0x01
#define VIA_IRQ_CB 0x02 #define VIA_IRQ_CB 0x02
#define VIA_IRQ_SR 0x04 #define VIA_IRQ_SR 0x04
#define VIA_IRQ_T2 0x20
#define VIA_IER 14 #define VIA_IER 14
#define VIA_RA_ALT 15 // No-handshake version #define VIA_RA_ALT 15 // No-handshake version
@ -68,7 +71,7 @@ static const char *dbg_regnames[] = {
"VIA_T1CH", "VIA_T1CH",
"VIA_T1LL", "VIA_T1LL",
"VIA_T1LH", "VIA_T1LH",
"VIA_T2CL", "VIA_T2xL",
"VIA_T2CH", "VIA_T2CH",
"VIA_SR", "VIA_SR",
"VIA_ACR", "VIA_ACR",
@ -84,6 +87,8 @@ static int irq_status = 0;
static uint8_t irq_active = 0; static uint8_t irq_active = 0;
static uint8_t irq_enable = 0; static uint8_t irq_enable = 0;
static uint16_t via_t2c;
void via_init(struct via_cb *cb) void via_init(struct via_cb *cb)
{ {
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
@ -224,6 +229,24 @@ void via_write(unsigned int address, uint8_t data)
case VIA_PCR: case VIA_PCR:
VDBG("VIA PCR %02x\n", data); VDBG("VIA PCR %02x\n", data);
break; 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: default:
VDBG("[VIA: unhandled WR %02x to %s (reg 0x%x)]\n", data, rname, r); 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: case VIA_IFR:
data = via_read_ifr(); data = via_read_ifr();
break; 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: default:
VDBG("[VIA: unhandled RD of %s (reg 0x%x)]\n", rname, r); 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; 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 */ /* 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.....! // FIXME: support actual timers.....!
} }