Merge pull request #1 from jepler/fix-mouse-movement-crash

Fix boot failure if mouse is moved too early.
This commit is contained in:
Liz 2025-08-05 10:41:24 -04:00 committed by GitHub
commit c265fb4e1e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 495 additions and 224 deletions

View file

@ -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

View file

@ -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)))

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -40,6 +40,7 @@
#include <errno.h>
#include <setjmp.h>
#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 (!scc_get_mie()) return;
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);
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;

208
src/rom.c
View file

@ -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,13 +100,13 @@ static void rom_patch_plusv3(uint8_t *rom_base)
* - No IWM init
* - 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 :) */
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);
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:
@ -116,17 +116,21 @@ static void rom_patch_plusv3(uint8_t *rom_base)
* allowing the ROM checksum to fail (it returns failure, then
* we carry on). This avoids wild RAM addrs being accessed.
*/
#endif
#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);
}
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))
/* 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,
* 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.
* MEM_TOP-0x380-screen_size.
*
* 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;
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 + 4, screen_distance_from_top);
ROM_WR16(patch_0 + 6, 0x6000); /* bra */
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
* 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);
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);
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_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));
/* Patch "SubA #$5900, A5" to subtract 0x9880.
* 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(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
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 */
}
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);
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 <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

@ -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)
{

View file

@ -27,6 +27,7 @@
* SOFTWARE.
*/
#include <assert.h>
#include <stdio.h>
#include <fcntl.h>
#include <getopt.h>
@ -35,6 +36,9 @@
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#if ENABLE_AUDIO
#include <stdatomic.h>
#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;
}
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;
}
}
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<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,
DISP_WIDTH * sizeof (Uint32));
/* Scales texture up to window size */

View file

@ -29,6 +29,7 @@
#include <inttypes.h>
#include <stdio.h>
#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.....!
}