diff --git a/CHANGELOG.md b/CHANGELOG.md index 135d9dd..8d34328 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,24 @@

# MII Version Changelog +## 1.96 +Bug fix release + * Mouse driver was revised completely. Now works in all program that I could try, + including Blazing Paddles; all the desktop apps, Dazzle Draw, MousePaint, etc. + * Also fixed the Super Serial Card driver, change the way data is processed, + and changed a lot of the way the IRQs are handled. + * In the process of this, I realized that I needed an IRQ MUX, as a program + that uses the SSC and the Mouse at the same time would not work. Typically, + I only know of one (french language VersionTel minitel emulator) that uses + both at the same time, but it's a good test. + * Also fixed a bug that prevented the 'debugger' to work properly. Doh. + +
+ VersionTel +
+
VersionTel is a rare program that uses 2 IRQs (SSC and Mouse)! +
+ ## 1.95 This is an intermediate release, mostly to fix a few bugs and add a few features that were requested. * Now have support for *writing* both DSK (PO and DO) as well as NIB files. As it @@ -17,7 +35,7 @@ This is an intermediate release, mostly to fix a few bugs and add a few features an IWM, but that'll come. * Changed the SmartPort driver to continue booting if there are no Disks. This seems to solve a problem people had with booting from a floppy when there was - no hard drive -- MII would stop at the basic prompt; not it continues to boot + no hard drive -- MII would stop at the basic prompt; now it continues to boot from the next slot down the line (with a message). * Audio processing was redone. There is now a notion of an audio 'sink' with an (now 32 bit float) FIFO. The speaker code (and soon, perhaps, the mockingboard) writes to it's own sink, and there an audio mixer that reads from all the sinks and mixes them together. This allows for a more flexible audio system, and also allows for a 'master volume' control. * Also now use 'sokol_audio' for the audio output, which gives me the possibility of a multi-platform audio output, if ever needed. I also added support for "miniaudio"; but it does a bit too much for my need at the minute, and slows down the compilation considerably, so it's not the default. @@ -36,7 +54,7 @@ This is an intermediate release, mostly to fix a few bugs and add a few features | ![NTSC](docs/screen/v19ntsc.png) | ![Mega2](docs/screen/v19mega2.png) | |--------------------------------------|--------------------------------------| * Added AVX/SSE/generic vectorial versions of some code paths, for speed. - * Added some more 'artifacts' color frindges to the HIRES rendering as well. + * Added some more 'artifacts' color fringes to the HIRES rendering as well.

Artifacts @@ -71,7 +89,7 @@ This is an intermediate release, mostly to fix a few bugs and add a few features split the 'XORG/GLX' code from the 'Pure GL' code from the 'MII UI' code, so it should be a lot easier to port to other platforms. * Redid the *DHRES rendering*, it's now a lot more accurate, has the correct artifacts. It's not as optimized as the previous version, but it looks better. - * Now remap the *joystick* coodinates to a 'square' -- my current 8bitdo joystick + * Now remap the *joystick* coordinates to a 'square' -- my current 8bitdo joystick has a circular deadzone, and it was a bit annoying to use. I might make that a setting, but for now, it's hardwired. * *Emulator now passes a2audit*. There is only one kludge to do it, regarding the diff --git a/docs/screen/v196versiontel.png b/docs/screen/v196versiontel.png new file mode 100644 index 0000000..6181218 Binary files /dev/null and b/docs/screen/v196versiontel.png differ diff --git a/libmui/fonts/Charcoal_mui.sfd b/libmui/fonts/Charcoal_mui.sfd index 791237a..b602581 100644 --- a/libmui/fonts/Charcoal_mui.sfd +++ b/libmui/fonts/Charcoal_mui.sfd @@ -22,7 +22,7 @@ OS2Version: 0 OS2_WeightWidthSlopeOnly: 0 OS2_UseTypoMetrics: 0 CreationTime: 830956166 -ModificationTime: 1711450964 +ModificationTime: 1724911886 PfmFamily: 81 TTFWeight: 400 TTFWidth: 5 @@ -800,10 +800,10 @@ NameList: AGL For New Fonts DisplaySize: -96 AntiAlias: 1 FitToEm: 1 -WinInfo: 230 23 12 +WinInfo: 207 23 12 BeginPrivate: 0 EndPrivate -BeginChars: 489 471 +BeginChars: 491 473 StartChar: .notdef Encoding: 409 -1 0 @@ -857,7 +857,7 @@ EndChar StartChar: .null Encoding: 5 8 1 -AltUni2: 002400.ffffffff.0 00001d.ffffffff.0 002400.ffffffff.0 00001d.ffffffff.0 +AltUni2: 00001d.ffffffff.0 002400.ffffffff.0 00001d.ffffffff.0 002400.ffffffff.0 Width: 0 GlyphClass: 1 Flags: W @@ -2429,7 +2429,7 @@ EndChar StartChar: hyphen Encoding: 36 45 16 -AltUni2: 002010.ffffffff.0 0000ad.ffffffff.0 002010.ffffffff.0 0000ad.ffffffff.0 +AltUni2: 0000ad.ffffffff.0 002010.ffffffff.0 0000ad.ffffffff.0 002010.ffffffff.0 Width: 1190 GlyphClass: 1 Flags: W @@ -31696,14 +31696,14 @@ SplineSet -84 -111 l 5,29,-1 505.200195312 1611 l 5,26,-1 EndSplineSet -Validated: 524289 +Validated: 1 EndChar StartChar: triagdn Encoding: 488 9660 470 Width: 1467 VWidth: 1000 -Flags: WO +Flags: W LayerCount: 2 Fore SplineSet @@ -31718,7 +31718,87 @@ SplineSet 731.5 206 l 1,12,13 331.900390625 616.400390625 331.900390625 616.400390625 65.5 890 c 0,0,1 EndSplineSet -Validated: 524289 +Validated: 1 +EndChar + +StartChar: clickbox +Encoding: 489 9746 471 +Width: 1881 +Flags: W +LayerCount: 2 +Fore +SplineSet +136 1304 m 1,0,-1 + 1744 1304 l 1,1,-1 + 1744 -304 l 1,2,-1 + 136 -304 l 1,3,-1 + 136 1304 l 1,0,-1 +0 1448 m 1,4,-1 + 0 -432 l 1,5,-1 + 1881 -432 l 1,6,-1 + 1881 1448 l 1,7,-1 + 0 1448 l 1,4,-1 +1016.33984375 114.719726562 m 1,8,-1 + 1016.33984375 -163.740234375 l 1,9,-1 + 860.73046875 -163.740234375 l 1,10,-1 + 860.73046875 114.719726562 l 1,11,-1 + 1016.33984375 114.719726562 l 1,8,-1 +1489.54003906 45.5595703125 m 1,12,-1 + 1380.33984375 -63.6396484375 l 1,13,-1 + 1159.20996094 156.580078125 l 1,14,-1 + 1269.3203125 266.690429688 l 1,15,-1 + 1489.54003906 45.5595703125 l 1,12,-1 +1016.33984375 862.740234375 m 1,16,-1 + 860.73046875 862.740234375 l 1,17,-1 + 860.73046875 1141.20019531 l 1,18,-1 + 1016.33984375 1141.20019531 l 1,19,-1 + 1016.33984375 862.740234375 l 1,16,-1 +1489.54003906 931.900390625 m 1,20,-1 + 1269.3203125 710.76953125 l 1,21,-1 + 1159.20996094 820.879882812 l 1,22,-1 + 1380.33984375 1041.09960938 l 1,23,-1 + 1489.54003906 931.900390625 l 1,20,-1 +733.330078125 805.41015625 m 1,24,-1 + 623.219726562 695.299804688 l 1,25,-1 + 427.5703125 890.950195312 l 1,26,-1 + 537.6796875 1001.05957031 l 1,27,-1 + 733.330078125 805.41015625 l 1,24,-1 +1638.78027344 417.75 m 1,28,-1 + 1327.55957031 417.75 l 1,29,-1 + 1327.55957031 573.360351562 l 1,30,-1 + 1638.78027344 573.360351562 l 1,31,-1 + 1638.78027344 417.75 l 1,28,-1 +549.509765625 417.75 m 1,32,-1 + 238.290039062 417.75 l 1,33,-1 + 238.290039062 573.360351562 l 1,34,-1 + 549.509765625 573.360351562 l 1,35,-1 + 549.509765625 417.75 l 1,32,-1 +719.6796875 161.129882812 m 1,36,-1 + 498.549804688 -59.08984375 l 1,37,-1 + 389.349609375 50.1103515625 l 1,38,-1 + 610.48046875 271.240234375 l 1,39,-1 + 719.6796875 161.129882812 l 1,36,-1 +EndSplineSet +EndChar + +StartChar: closebox +Encoding: 490 9744 472 +Width: 1881 +Flags: W +LayerCount: 2 +Fore +SplineSet +136 1304 m 1,0,-1 + 1744 1304 l 1,1,-1 + 1744 -304 l 1,2,-1 + 136 -304 l 1,3,-1 + 136 1304 l 1,0,-1 +0 1448 m 1,4,-1 + 0 -432 l 1,5,-1 + 1881 -432 l 1,6,-1 + 1881 1448 l 1,7,-1 + 0 1448 l 1,4,-1 +EndSplineSet EndChar EndChars EndSplineFont diff --git a/libmui/src/control/listbox.h b/libmui/src/control/listbox.h index d317134..d51cb02 100644 --- a/libmui/src/control/listbox.h +++ b/libmui/src/control/listbox.h @@ -8,7 +8,6 @@ #pragma once - #include #include @@ -16,24 +15,42 @@ struct mui_control_t; struct mui_window_t; struct mui_listbox_elem_t; -/* This is currently unused */ -typedef void (*mui_ldef_p)( - struct mui_control_t * c, - uint32_t elem_index, - struct mui_listbox_elem_t * elem); - typedef struct mui_listbox_elem_t { uint32_t disabled : 1; // currently this is a UTF8 string using the 'icons' font char icon[8]; // UTF8 icon - // default 'LDEF' is to draw the 'elem' string + // default 'LDEF' is to draw the 'elem' string using normal font void * elem; // char * or... ? } mui_listbox_elem_t; DECLARE_C_ARRAY(mui_listbox_elem_t, mui_listbox_elems, 2); IMPLEMENT_C_ARRAY(mui_listbox_elems); +enum mui_ldef_op_e { + /* + * Initialize the LDEF, the 'ldef_param' is passed in, and the + */ + MUI_LDEF_OP_INIT, + /* + * Allow the LDEF to dispose of ldef_param, if applicable + */ + MUI_LDEF_OP_DISPOSE, + MUI_LDEF_OP_DRAW, + MUI_LDEF_OP_GET_TEXT, + MUI_LDEF_OP_EVENT, + MUI_LDEF_OP_COUNT, +}; + +/* This is currently unused */ +typedef void (*mui_ldef_p)( + mui_control_t * c, + enum mui_ldef_op_e op, + void * ldef_param, + uint32_t elem_index, + mui_listbox_elem_t * elem, + void * io_result ); + mui_control_t * mui_listbox_new( @@ -46,3 +63,8 @@ mui_listbox_prepare( mui_listbox_elems_t * mui_listbox_get_elems( mui_control_t * c); +void +mui_listbox_set_ldef( + mui_control_t * c, + mui_ldef_p ldef, + void * ldef_param); diff --git a/libmui/src/control/popup.h b/libmui/src/control/popup.h index a993bb2..60af1ab 100644 --- a/libmui/src/control/popup.h +++ b/libmui/src/control/popup.h @@ -12,10 +12,13 @@ #include #include -/* Popup menu control. +/* + * Popup menu control. + * * flags are MUI_TEXT_ALIGN_* -- however this corresponds to the margins * of the popup control itself when placed into it's 'frame' -- the - * popup will be placed left,right,center of the frame rectangle depending. + * popup will be placed left,right,center of the frame rectangle depending + * on these flags. */ mui_control_t * mui_popupmenu_new( diff --git a/libmui/src/control/scrollbar.h b/libmui/src/control/scrollbar.h index 200276a..caa0c88 100644 --- a/libmui/src/control/scrollbar.h +++ b/libmui/src/control/scrollbar.h @@ -11,11 +11,11 @@ #include #include - - -/* Page step and line step are optional, they default to '30' pixels and - * the 'visible' area of the scrollbar, respectively. - * If you want to for example have a scrollbar that scrolls by 5 when you +/* + * Page step and line step are optional, they default to '30' pixels and the + * 'visible' area of the scrollbar, respectively. + * + * If you want to -for example- have a scrollbar that scrolls by 5 when you * click the arrows, and by 20 when you click the bar, you would set the * line_step to 5, and the page_step to 20. */ diff --git a/libmui/src/mui_cdef_listbox.c b/libmui/src/mui_cdef_listbox.c index dfea9ca..a38cc27 100644 --- a/libmui/src/mui_cdef_listbox.c +++ b/libmui/src/mui_cdef_listbox.c @@ -24,6 +24,7 @@ typedef struct mui_listbox_control_t { uint8_t elem_height; mui_listbox_elems_t elems; mui_ldef_p ldef; + void * ldef_param; // returned from MUI_LDEF_OP_INIT // to handle double-click mui_time_t last_click; // typehead search related @@ -37,6 +38,56 @@ typedef struct mui_listbox_control_t { extern const mui_control_color_t mui_control_color[MUI_CONTROL_STATE_COUNT]; +typedef struct mui_ldef_default_t { + mui_font_t * main; + mui_font_t * icons; +} mui_ldef_default_t; + +/* + * CURRENTLY NOT IN USE + */ +static void +mui_ldef_default( + mui_control_t * c, + enum mui_ldef_op_e op, + void * ldef_param, + uint32_t elem_index, + mui_listbox_elem_t * elem, + void * io_result ) +{ + switch (op) { + case MUI_LDEF_OP_INIT: { + mui_ldef_default_t * d = calloc(1, sizeof(*d)); + d->main = mui_font_find(c->win->ui, "main"); + d->icons = mui_font_find(c->win->ui, "icons"); + *((mui_ldef_default_t **)io_result) = d; + } break; + case MUI_LDEF_OP_DISPOSE: + if (ldef_param) + free(ldef_param); + break; + case MUI_LDEF_OP_DRAW: { + mui_drawable_t * dr = io_result; + mui_font_t * main = mui_font_find(c->win->ui, "main"); + c2_rect_t f = c->frame; + c2_rect_offset(&f, c->win->content.l, c->win->content.t); + f.t += elem_index * ((mui_listbox_control_t *)c)->elem_height; + f.b = f.t + ((mui_listbox_control_t *)c)->elem_height; + mui_font_text_draw(main, dr, f.tl, elem->elem, 0, + mui_control_color[elem->disabled ? + MUI_CONTROL_STATE_DISABLED : 0].text); + } break; + case MUI_LDEF_OP_GET_TEXT: + *((char **)io_result) = elem->elem; + break; + case MUI_LDEF_OP_EVENT: + break; + default: + break; + } +} + + static void mui_listbox_draw( mui_window_t * win, @@ -52,7 +103,7 @@ mui_listbox_draw( cg_rectangle(cg, f.l, f.t, c2_rect_width(&f), c2_rect_height(&f)); cg_stroke(cg); - { + { // this clips the frame so we don't draw over the border c2_rect_t clip = f; c2_rect_inset(&clip, 1, 1); mui_drawable_clip_push(dr, &clip); @@ -403,3 +454,18 @@ mui_listbox_prepare( mui_control_inval(lb->scrollbar); mui_control_inval(c); } + +void +mui_listbox_set_ldef( + mui_control_t * c, + mui_ldef_p ldef, + void * ldef_param) +{ + mui_listbox_control_t *lb = (mui_listbox_control_t *)c; + if (lb->ldef) + lb->ldef(c, MUI_LDEF_OP_DISPOSE, lb->ldef_param, 0, NULL, NULL); + lb->ldef = ldef; + lb->ldef_param = ldef_param; + if (lb->ldef) + lb->ldef(c, MUI_LDEF_OP_INIT, ldef_param, 0, NULL, &lb->ldef_param); +} diff --git a/libmui/src/mui_window.c b/libmui/src/mui_window.c index 1322a67..9a03e44 100644 --- a/libmui/src/mui_window.c +++ b/libmui/src/mui_window.c @@ -31,7 +31,7 @@ mui_window_update_rects( memset(parts, 0, sizeof(parts[0]) * MUI_WINDOW_PART_COUNT); parts[MUI_WINDOW_PART_CONTENT] = win->frame; c2_rect_inset(&parts[MUI_WINDOW_PART_CONTENT], 4, 4); - parts[MUI_WINDOW_PART_CONTENT].t += title_height - 1; + parts[MUI_WINDOW_PART_CONTENT].t += title_height - 2; stb_ttc_measure m = {}; if (win->title) { @@ -98,11 +98,16 @@ mui_titled_window_draw( if (isFront) { const int lrMargin = 6; const int steps = 6; + struct cg_matrix_t m = cg->state->matrix; cg_set_line_width(cg, 2); for (int i = 1; i < (title_height + 4) / steps; i++) { cg_move_to(cg, win->frame.l + lrMargin, win->frame.t + i * steps); cg_line_to(cg, win->frame.r - lrMargin, win->frame.t + i * steps); } + cg_translate(cg, 0.5, 0.5); + cg_set_source_color(cg, &CG_COLOR(contentFill)); + cg_stroke_preserve(cg); + cg_set_matrix(cg, &m); cg_set_source_color(cg, &CG_COLOR(decoColor)); cg_stroke(cg); } @@ -131,7 +136,7 @@ mui_titled_window_draw( c2_rect_t title = parts[MUI_WINDOW_PART_TITLE]; if (isFront) { c2_rect_t titleBack = parts[MUI_WINDOW_PART_TITLE]; - c2_rect_inset(&titleBack, -6, 0); + c2_rect_inset(&titleBack, -8, 0); cg_round_rectangle(cg, titleBack.l, titleBack.t, c2_rect_width(&titleBack), c2_rect_height(&titleBack), 12, 12); cg_set_source_color(cg, &CG_COLOR(frameFill)); diff --git a/libmui/src/stb_truetype.h b/libmui/src/stb_truetype.h index bbf2284..90a5c2e 100644 --- a/libmui/src/stb_truetype.h +++ b/libmui/src/stb_truetype.h @@ -54,7 +54,7 @@ // Hou Qiming Derek Vinyard // Rob Loach Cort Stratton // Kenney Phillis Jr. Brian Costabile -// Ken Voskuil (kaesve) +// Ken Voskuil (kaesve) Yakov Galka // // VERSION HISTORY // @@ -4604,6 +4604,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc scale_y = -scale_y; { + // distance from singular values (in the same units as the pixel grid) + const float eps = 1./1024, eps2 = eps*eps; int x,y,i,j; float *precompute; stbtt_vertex *verts; @@ -4616,15 +4618,15 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); - precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + precompute[i] = (dist < eps) ? 0.0f : 1.0f / dist; } else if (verts[i].type == STBTT_vcurve) { float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; float len2 = bx*bx + by*by; - if (len2 != 0.0f) - precompute[i] = 1.0f / (bx*bx + by*by); + if (len2 >= eps2) + precompute[i] = 1.0f / len2; else precompute[i] = 0.0f; } else @@ -4689,8 +4691,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc float a = 3*(ax*bx + ay*by); float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); float c = mx*ax+my*ay; - if (a == 0.0) { // if a is 0, it's linear - if (b != 0.0) { + if (STBTT_fabs(a) < eps2) { // if a is 0, it's linear + if (STBTT_fabs(b) >= eps2) { res[num++] = -c/b; } } else { diff --git a/src/drivers/mii_mb.c b/src/drivers/mii_mb.c index 5e94cd2..8e78d69 100644 --- a/src/drivers/mii_mb.c +++ b/src/drivers/mii_mb.c @@ -12,6 +12,7 @@ typedef struct mii_mb_t { uint8_t init; // init sequence bits bool init_done; uint8_t timer; + uint8_t irq_num; struct mb_t * mb; mii_audio_source_t source; uint64_t flush_cycle_count; @@ -66,8 +67,6 @@ mii_mb_start( printf("MB Start\n"); mb->init = 0; mb->init_done = true; - mb->timer = mii_timer_register(mb->mii, _mii_mb_timer, mb, 1000, __func__); - mb->flush_cycle_count = 512 * mb->mii->audio.clk_per_sample; mii_audio_add_source(&mb->mii->audio, &mb->source); } @@ -151,6 +150,13 @@ _mii_mb_init( .ts = mii->cpu.total_cycle, }; mb_io_reset(mb->mb, &clock); + + char name[32]; + snprintf(name, sizeof(name), "MB %d", slot->id+1); + + mb->timer = mii_timer_register(mb->mii, _mii_mb_timer, mb, 1000, name); + mb->irq_num = mii_irq_register(mb->mii, name); + uint16_t addr = 0xc100 + (slot->id * 0x100); mii_mb_start(mb); mii_bank_install_access_cb(&mii->bank[MII_BANK_CARD_ROM], diff --git a/src/drivers/mii_mouse.c b/src/drivers/mii_mouse.c index 54cd8c0..91b6a81 100644 --- a/src/drivers/mii_mouse.c +++ b/src/drivers/mii_mouse.c @@ -64,6 +64,22 @@ * `----------------- Reserved */ +enum { + MOUSE_STATUS_PREV_BUT1 = (1 << 0), + MOUSE_STATUS_MOVE_IRQ = (1 << 1), + MOUSE_STATUS_BUT_IRQ = (1 << 2), + MOUSE_STATUS_VBL_IRQ = (1 << 3), + MOUSE_STATUS_BUT1 = (1 << 4), + MOUSE_STATUS_MOVED = (1 << 5), + MOUSE_STATUS_PREV_BUT0 = (1 << 6), + MOUSE_STATUS_BUT0 = (1 << 7), + + MOUSE_MODE_ON = (1 << 0), + MOUSE_MODE_MOVE_IRQ = (1 << 1), + MOUSE_MODE_BUT_IRQ = (1 << 2), + MOUSE_MODE_VBL_IRQ = (1 << 3), +}; + enum { CLAMP_MIN_LO = 0x478, CLAMP_MIN_HI = 0x578, @@ -80,61 +96,57 @@ enum { MOUSE_MODE = 0x0738, }; -enum { - mouseEnabled = 1, - mouseIntMoveEnabled = 2, - mouseIntButtonEnabled = 4, - mouseIntVBlankEnabled = 8, -}; - typedef struct mii_card_mouse_t { + STAILQ_ENTRY(mii_card_mouse_t) self; struct mii_slot_t * slot; mii_t * mii; + uint8_t irq_num; // MII IRQ line uint8_t timer_id; // 60hz timer uint8_t slot_offset; - uint8_t mode; // cached mode byte + uint8_t mode; // cached mode byte + uint8_t status; // cached status byte struct { uint16_t x, y; bool button; } last; } mii_card_mouse_t; +STAILQ_HEAD(, mii_card_mouse_t) + _mii_card_mouse = STAILQ_HEAD_INITIALIZER(_mii_card_mouse); + static uint64_t _mii_mouse_vbl_handler( mii_t * mii, void *param) { mii_card_mouse_t *c = param; - /* this is not exact, the VBL interrupt should still work when - * the mouse is disabled, but it's not really important -- for the moment - */ - if (!mii->mouse.enabled) - return 1000000 / 60; mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; // mii_bank_t * sw = &mii->bank[MII_BANK_SW]; - uint8_t status = mii_bank_peek(main, MOUSE_STATUS + c->slot_offset); + uint8_t status = c->status; uint8_t old = status; - if (c->mode & mouseIntMoveEnabled) { + if (c->mode & MOUSE_MODE_MOVE_IRQ) { if ((mii->mouse.x != c->last.x) || (mii->mouse.y != c->last.y)) { - mii->cpu_state.irq = 1; - status |= 1 << 1; + mii_irq_raise(mii, c->irq_num); + status |= MOUSE_STATUS_MOVE_IRQ; } } - if (c->mode & mouseIntButtonEnabled) { + if (c->mode & MOUSE_MODE_BUT_IRQ) { if (mii->mouse.button && !c->last.button) { - mii->cpu_state.irq = 1; - status |= 1 << 2; + mii_irq_raise(mii, c->irq_num); + status |= MOUSE_STATUS_BUT_IRQ; } } - if (c->mode & mouseIntVBlankEnabled) { - mii->cpu_state.irq = 1; - status |= 1 << 3; + if (c->mode & MOUSE_MODE_VBL_IRQ && !(status & MOUSE_STATUS_VBL_IRQ)) { + mii_irq_raise(mii, c->irq_num); + status |= MOUSE_STATUS_VBL_IRQ; } // if (mii->cpu_state.irq) mii->trace_cpu = true; - if (status != old) + if (status != old) { mii_bank_poke(main, MOUSE_STATUS + c->slot_offset, status); + c->status = status; + } return 1000000 / 60; } @@ -151,11 +163,20 @@ _mii_mouse_init( printf("%s loading in slot %d\n", __func__, slot->id + 1); c->slot_offset = slot->id + 1 + 0xc0; - uint8_t data[256] = {}; c->timer_id = mii_timer_register(mii, - _mii_mouse_vbl_handler, c, - 1000000 / 60, __func__); + _mii_mouse_vbl_handler, c, + 1000000 / 60, __func__); + STAILQ_INSERT_TAIL(&_mii_card_mouse, c, self); + c->irq_num = mii_irq_register(mii, "mouse"); + + /* + * The mouse card ROM is a 256 bytes, and has entry points for the + * various calls, and a signature at the start. + * This re-create it as a bare minimum - the entry points poke at the + * softswitches, which in turn calls back the emulator. + */ + uint8_t data[256] = {}; // Identification as a mouse card // From Technical Note Misc #8, "Pascal 1.1 Firmware Protocol ID Bytes": data[0x05] = 0x38; @@ -189,6 +210,10 @@ _mii_mouse_dispose( struct mii_slot_t *slot ) { mii_card_mouse_t *c = slot->drv_priv; + +// mii_timer_unregister(mii, c->timer_id); + mii_irq_unregister(mii, c->irq_num); + STAILQ_REMOVE(&_mii_card_mouse, c, mii_card_mouse_t, self); free(c); slot->drv_priv = NULL; } @@ -205,37 +230,51 @@ _mii_mouse_access( int psw = addr & 0x0F; mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; + switch (psw) { case 2: { if (write) { byte &= 0xf; mii_bank_poke(main, MOUSE_MODE + c->slot_offset, byte); - mii->mouse.enabled = byte & mouseEnabled; + mii->mouse.enabled = byte & MOUSE_MODE_ON; printf("%s: mode %02x: %s Move:%d Button:%d VBL:%d\n", __func__, byte, mii->mouse.enabled ? "ON " : "OFF", - byte & mouseIntMoveEnabled ? 1 : 0, - byte & mouseIntButtonEnabled ? 1 : 0, - byte & mouseIntVBlankEnabled ? 1 : 0); + byte & MOUSE_MODE_MOVE_IRQ ? 1 : 0, + byte & MOUSE_MODE_BUT_IRQ ? 1 : 0, + byte & MOUSE_MODE_VBL_IRQ ? 1 : 0); c->mode = byte; } } break; - case 3: // service mouse - // no need to handle that, the VBL handler does it - break; + case 3: {// service mouse + // call the timer now, and reset it + // mii_timer_set(mii, c->timer_id, 1000000 / 60); + // _mii_mouse_vbl_handler(mii, c); + // now they've been read, clear the flags + uint8_t status = c->status; + status &= ~(MOUSE_STATUS_BUT_IRQ| + MOUSE_STATUS_MOVE_IRQ|MOUSE_STATUS_VBL_IRQ); + mii_bank_poke(main, MOUSE_STATUS + c->slot_offset, status); + c->status = status; + mii_irq_clear(mii, c->irq_num); + } break; case 4: {// read mouse if (!mii->mouse.enabled) break; - uint8_t status = 0; - if ((mii->mouse.x != c->last.x) || (mii->mouse.y != c->last.y)) - status |= 1 << 5; - status |= c->last.button ? 1 << 6 : 0; - status |= mii->mouse.button ? 1 << 7 : 0; mii_bank_poke(main, MOUSE_X_HI + c->slot_offset, mii->mouse.x >> 8); mii_bank_poke(main, MOUSE_Y_HI + c->slot_offset, mii->mouse.y >> 8); mii_bank_poke(main, MOUSE_X_LO + c->slot_offset, mii->mouse.x); mii_bank_poke(main, MOUSE_Y_LO + c->slot_offset, mii->mouse.y); + // update the status byte. IRQ flags were set by timer + uint8_t status = c->status; + status &= ~MOUSE_STATUS_MOVED; + if ((mii->mouse.x != c->last.x) || (mii->mouse.y != c->last.y)) + status |= MOUSE_STATUS_MOVED; + status = (status & ~MOUSE_STATUS_PREV_BUT0) | + (c->last.button ? MOUSE_STATUS_PREV_BUT0 : 0); + status = (status & ~MOUSE_STATUS_BUT0) | + (mii->mouse.button ? MOUSE_STATUS_BUT0 : 0); mii_bank_poke(main, MOUSE_STATUS + c->slot_offset, status); - // mii_bank_poke(main, MOUSE_MODE + c->slot_offset, 0xf); // already in place + c->status = status; c->last.x = mii->mouse.x; c->last.y = mii->mouse.y; c->last.button = mii->mouse.button; @@ -254,9 +293,9 @@ _mii_mouse_access( mii->mouse.max_y = mii_bank_peek(main, CLAMP_MAX_LO) | (mii_bank_peek(main, CLAMP_MAX_HI) << 8); } - // printf("Mouse clamp to %d,%d - %d,%d\n", - // mii->mouse.min_x, mii->mouse.min_y, - // mii->mouse.max_x, mii->mouse.max_y); + printf("Mouse clamp to %d,%d - %d,%d\n", + mii->mouse.min_x, mii->mouse.min_y, + mii->mouse.max_x, mii->mouse.max_y); break; case 8: // home mouse mii->mouse.x = mii->mouse.min_x; @@ -284,3 +323,65 @@ static mii_slot_drv_t _driver = { .access = _mii_mouse_access, }; MI_DRIVER_REGISTER(_driver); + + + +#include "mish.h" + +static void +_mii_mish_mouse( + void * param, + int argc, + const char * argv[]) +{ + mii_t * mii = param; + mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; + + if (!argv[1] || !strcmp(argv[1], "status")) { + mii_card_mouse_t *c; + printf("mouse: cards:\n"); + STAILQ_FOREACH(c, &_mii_card_mouse, self) { + printf("mouse %d:\n", c->slot->id + 1); + + #define MCM(__n) { .a = __n, .s = (#__n)+6 } + static const struct { + uint16_t a; + const char * s; + } mouse_regs[] = { + MCM(MOUSE_X_LO), MCM(MOUSE_X_HI), MCM(MOUSE_Y_LO), + MCM(MOUSE_Y_HI), MCM(MOUSE_STATUS), MCM(MOUSE_MODE), + }; + for (int i = 0; i < 6; i++) { + uint8_t val = mii_bank_peek(main, mouse_regs[i].a + c->slot_offset); + printf(" $%04x: %-10s: $%02x\n", + mouse_regs[i].a, mouse_regs[i].s, val); + } + uint8_t status = mii_bank_peek(main, MOUSE_STATUS + c->slot_offset); + // printf individual status bits + static char * status_bits[] = { + "PREV_BUT1", "MOVE_IRQ", "BUT_IRQ", "VBL_IRQ", + "BUT1", "MOVED", "PREV_BUT0", "BUT0", + }; + printf(" status: "); + for (int i = 0; i < 8; i++) + printf("%s ", (status & (1 << i)) ? status_bits[i] : "."); + printf("\n"); + // printf individual mode bits + uint8_t mode = mii_bank_peek(main, MOUSE_MODE + c->slot_offset); + static char * mode_bits[] = { + "ON", "MOVE_IRQ", "BUT_IRQ", "VBL_IRQ", + }; + printf(" mode: "); + for (int i = 0; i < 4; i++) + printf("%s ", (mode & (1 << i)) ? mode_bits[i] : "."); + } + return; + } +} + +MISH_CMD_NAMES(_mouse, "mouse"); +MISH_CMD_HELP(_mouse, + "mouse: Mouse card internals", + " : dump status" + ); +MII_MISH(_mouse, _mii_mish_mouse); diff --git a/src/drivers/mii_mouse.h b/src/drivers/mii_mouse.h index b3bd521..81608bc 100644 --- a/src/drivers/mii_mouse.h +++ b/src/drivers/mii_mouse.h @@ -14,7 +14,7 @@ // mins/max are set by the card, x,y,button are set by the UI typedef struct mii_mouse_t { bool enabled; // read only, set by driver - uint16_t min_x, max_x, + int16_t min_x, max_x, min_y, max_y; // set by driver when enabled uint16_t x, y; bool button; diff --git a/src/drivers/mii_ssc.c b/src/drivers/mii_ssc.c index e21ce1f..209e39a 100644 --- a/src/drivers/mii_ssc.c +++ b/src/drivers/mii_ssc.c @@ -34,8 +34,8 @@ make && A2_TTY=/dev/tntX ./surl-server #include #include #include -#include -#include +#include + #include "mii.h" #include "mii_bank.h" #include "mii_sw.h" @@ -160,6 +160,7 @@ typedef struct mii_card_ssc_t { struct mii_slot_t * slot; struct mii_bank_t * rom; mii_t * mii; + uint8_t irq_num; // MII IRQ line uint8_t slot_offset; mii_ssc_setconf_t conf; int state; // current state, MII_SSC_STATE_* @@ -184,6 +185,7 @@ STAILQ_HEAD(, mii_card_ssc_t) _mii_card_ssc_started = STAILQ_HEAD_INITIALIZER(_mii_card_ssc_started); pthread_t _mii_ssc_thread_id = 0; mii_ssc_cmd_fifo_t _mii_ssc_cmd = {}; +int _mii_ssc_signal[2] = {0, 0}; static int _mii_scc_set_conf( @@ -196,11 +198,6 @@ _mii_ssc_thread( void *param) { printf("%s: start\n", __func__); - // ignore the signal, we use it to wake up the thread - sigaction(SIGUSR1, &(struct sigaction){ - .sa_handler = SIG_IGN, - .sa_flags = SA_RESTART, - }, NULL); do { /* * Get commands from the MII running thread. Add/remove cards @@ -226,15 +223,20 @@ _mii_ssc_thread( return NULL; } } - /* here we use select. This is not optimal on linux, but it is portable - to other OSes -- perhaps I'll add an epoll() version later */ + /* + * Here we use select. This is not optimal on linux, but it is portable + * to other OSes -- perhaps I'll add an epoll() version later + */ fd_set rfds, wfds; FD_ZERO(&rfds); FD_ZERO(&wfds); int maxfd = 0; + FD_SET(_mii_ssc_signal[0], &rfds); + if (_mii_ssc_signal[0] > maxfd) + maxfd = _mii_ssc_signal[0]; mii_card_ssc_t *c = NULL, *safe; STAILQ_FOREACH_SAFE(c, &_mii_card_ssc_started, self, safe) { - // guy might be being reconfigured, or perhaps had an error + // this guy might be being reconfigured, or perhaps had an error if (c->tty_fd < 0) continue; if (!mii_ssc_fifo_isempty(&c->tx)) { @@ -259,11 +261,17 @@ _mii_ssc_thread( } if (res == 0) // timeout continue; + if (FD_ISSET(_mii_ssc_signal[0], &rfds)) { + uint8_t b; + if (read(_mii_ssc_signal[0], &b, sizeof(b))) + /* ignored */ {}; + } STAILQ_FOREACH(c, &_mii_card_ssc_started, self) { - /* Here we know the read fifo isn't full, otherwise we wouldn'ty - have asked for more data. - See what space we have in the fifo, try reading as much as that, - and push it to the FIFO */ + /* + * Here we know the read fifo isn't full, otherwise we wouldn't have + * asked for more data. See what space we have in the fifo, try + * reading as much as that, and push it to the FIFO + */ if (FD_ISSET(c->tty_fd, &rfds)) { uint8_t buf[mii_ssc_cmd_fifo_fifo_size]; int max = mii_ssc_fifo_get_write_size(&c->rx); @@ -278,10 +286,12 @@ _mii_ssc_thread( for (int i = 0; i < res; i++) mii_ssc_fifo_write(&c->rx, buf[i]); } - /* here as well, this wouldn't be set if we hadn't got stuff - to send -- see what's in the fifo, 'peek' the bytes into - an aligned buffer, try to write it all, and then *actually* - remove them (read) the one that were sent from the fifo */ + /* + * Same here, this wouldn't be set if we hadn't got stuff to send -- + * see what's in the fifo, 'peek' the bytes into an aligned buffer, + * try to write it all, and then *actually* remove them (read) the + * one that were sent from the fifo + */ if (FD_ISSET(c->tty_fd, &wfds)) { uint8_t buf[mii_ssc_cmd_fifo_fifo_size]; int max = mii_ssc_fifo_get_read_size(&c->tx); @@ -303,6 +313,20 @@ _mii_ssc_thread( return NULL; } +/* + * Write a byte to 'our' end of the socketpair (the other end is in the thread). + * This is used to wake up the thread for example when the FIFO is full/empty + */ +static void +_mii_ssc_thread_signal( + mii_card_ssc_t *c) +{ + uint8_t b = 0x55; + if (_mii_ssc_signal[1] > 0) { + write(_mii_ssc_signal[1], &b, sizeof(b)); + } +} + static void _mii_ssc_thread_start( mii_card_ssc_t *c) @@ -313,6 +337,11 @@ _mii_ssc_thread_start( printf("%s TTY not open, skip\n", __func__); return; } + // create as socketpair in _mii_ssc_signal + if (socketpair(AF_UNIX, SOCK_STREAM, 0, _mii_ssc_signal) < 0) { + printf("%s: socketpair: %s\n", __func__, strerror(errno)); + return; + } c->state = MII_SSC_STATE_START; mii_ssc_cmd_t cmd = { .cmd = MII_SSC_STATE_START, .card = c }; mii_ssc_cmd_fifo_write(&_mii_ssc_cmd, cmd); @@ -323,16 +352,23 @@ _mii_ssc_thread_start( printf("%s: starting thread\n", __func__); pthread_create(&_mii_ssc_thread_id, NULL, _mii_ssc_thread, NULL); } else { - pthread_kill(_mii_ssc_thread_id, SIGUSR1); + _mii_ssc_thread_signal(c); } } /* * This is called when the CPU touches the CX00-CXFF ROM area, and we * need to install the secondary part of the ROM. - * Also, if the access was from the ROM, we start the card as there's no - * other way to know when the card is started -- we don't want to create - * a thread fro SSC if the card is being ignored by the Apple IIe + * This is what the hardware does, and that ROM gets 'reset' when something + * touches $cfff. (see _mii_deselect_cXrom()) + * + * Also, the other key thing here is that if we detect the PC is in the the main + * part of our ROM code, we start the card as there's no other way to know when + * the card is started -- we don't want to create a thread for SSC if the card + * is installed but never used. + * + * This seems to work pretty well, it handles most programs that use the SSC + * that I have tried. */ static bool _mii_ssc_select( @@ -364,11 +400,11 @@ _mii_ssc_select( return false; } -/* Called a some sort of proportional number of cycles related to the - baudrate to check the FIFOs and update the status/raise IRQs. - this doesn't have to be exact, it just have to be often enough to - not miss any data. - */ +/* + * Called at some sort of proportional number of cycles related to the baudrate + * to check the FIFOs and update the status/raise IRQs. this doesn't have to be + * exact, it just have to be often enough to not miss any data. + */ static uint64_t _mii_ssc_timer_cb( mii_t * mii, @@ -384,7 +420,7 @@ _mii_ssc_timer_cb( uint8_t rx_full = !mii_ssc_fifo_isempty(&c->rx); // what it really mean is 'there room for more data', not 'full' uint8_t tx_empty = !mii_ssc_fifo_isfull(&c->tx); - uint8_t old = c->status; +// uint8_t old = c->status; c->status = (c->status & ~(1 << SSC_6551_RX_FULL)) | (rx_full << SSC_6551_RX_FULL); c->status = (c->status & ~(1 << SSC_6551_TX_EMPTY)) | @@ -392,32 +428,33 @@ _mii_ssc_timer_cb( uint8_t irq = 0;//(c->status & (1 << SSC_6551_IRQ)); uint8_t t_irqen = ((c->command >> SSC_6551_COMMAND_IRQ_T) & 3) == 1; uint8_t r_irqen = !(c->command & (1 << SSC_6551_COMMAND_IRQ_R)); - +#if 0 if (old != c->status) printf("SSC%d New Status %08b RX:%2d TX:%2d t_irqen:%d r_irqen:%d\n", c->slot->id+1, c->status, mii_ssc_fifo_get_read_size(&c->rx), mii_ssc_fifo_get_write_size(&c->tx), t_irqen, r_irqen); +#endif // we set the IRQ flag even if the real IRQs are disabled. // rising edge triggers the IRQR if (!irq && rx_full) { // raise the IRQ if (r_irqen) { irq = 1; - printf("SSC%d: IRQ RX\n", c->slot->id+1); - mii->cpu_state.irq = 1; + // printf("SSC%d: IRQ RX\n", c->slot->id+1); } } if (!irq && (tx_empty)) { // raise the IRQ if (t_irqen) { irq = 1; - printf("SSC%d: IRQ TX\n", c->slot->id+1); - mii->cpu_state.irq = 1; + // printf("SSC%d: IRQ TX\n", c->slot->id+1); } } - if (irq) + if (irq) { c->status |= 1 << SSC_6551_IRQ; + mii_irq_raise(mii, c->irq_num); + } return c->timer_delay; } @@ -549,7 +586,10 @@ _mii_ssc_init( snprintf(name, sizeof(name), "SSC %d", slot->id+1); c->timer_check = mii_timer_register(mii, _mii_ssc_timer_cb, c, 0, strdup(name)); - // fastest speed we could get to? + c->irq_num = mii_irq_register(mii, strdup(name)); + + // this is semi random for now, it is recalculated once the program + // changes the baud rate/config c->timer_delay = 11520; c->tty_fd = -1; STAILQ_INSERT_TAIL(&_mii_card_ssc_slots, c, self); @@ -577,7 +617,7 @@ _mii_ssc_dispose( if (c->state == MII_SSC_STATE_RUNNING) { mii_ssc_cmd_t cmd = { .cmd = MII_SSC_STATE_STOP, .card = c }; mii_ssc_cmd_fifo_write(&_mii_ssc_cmd, cmd); - pthread_kill(_mii_ssc_thread_id, SIGUSR1); + _mii_ssc_thread_signal(c); while (c->state == MII_SSC_STATE_RUNNING) usleep(1000); printf("SSC%d: stopped\n", c->slot->id+1); @@ -588,10 +628,11 @@ _mii_ssc_dispose( _mii_ssc_thread_id = 0; mii_ssc_cmd_t cmd = { .cmd = MII_THREAD_TERMINATE }; mii_ssc_cmd_fifo_write(&_mii_ssc_cmd, cmd); - pthread_kill(id, SIGUSR1); + _mii_ssc_thread_signal(c); pthread_join(id, NULL); printf("SSC%d: thread stopped\n", c->slot->id+1); } + mii_irq_unregister(mii, c->irq_num); free(c); slot->drv_priv = NULL; } @@ -617,7 +658,7 @@ _mii_ssc_command_set( if ((c->command & (1 << SSC_6551_COMMAND_IRQ_R)) && !(byte & (1 << SSC_6551_COMMAND_IRQ_R))) { if (c->status & (1 << SSC_6551_IRQ)) - mii->cpu_state.irq = 1; + mii_irq_raise(mii, c->irq_num); } int status; if (ioctl(c->tty_fd, TIOCMGET, &status) == -1) { @@ -693,7 +734,7 @@ _mii_ssc_access( c->total_tx++; mii_ssc_fifo_write(&c->tx, byte); if (tx_empty) // wake thread if it's sleeping - pthread_kill(_mii_ssc_thread_id, SIGUSR1); + _mii_ssc_thread_signal(c); bool isfull = mii_ssc_fifo_isfull(&c->tx); if (isfull) { c->status &= ~(1 << SSC_6551_TX_EMPTY); @@ -710,12 +751,12 @@ _mii_ssc_access( c->status &= ~(1 << SSC_6551_RX_FULL); } else { if (wasfull) // wake thread to read more - pthread_kill(_mii_ssc_thread_id, SIGUSR1); + _mii_ssc_thread_signal(c); // send another irq? uint8_t r_irqen = !(c->command & (1 << SSC_6551_COMMAND_IRQ_R)); if (r_irqen) { - mii->cpu_state.irq = 1; + mii_irq_raise(mii, c->irq_num); } } } @@ -723,13 +764,14 @@ _mii_ssc_access( } break; case 0x9: {// STATUS if (write) { - printf("SSC%d: RESET requesdt\n",c->slot->id+1); + printf("SSC%d: RESET request\n", c->slot->id+1); _mii_ssc_command_set(c, 0x10); break; } res = c->status; // if it was set before, clear it. c->status &= ~(1 << SSC_6551_IRQ); + mii_irq_clear(mii, c->irq_num); } break; case 0xa: {// COMMAND if (!write) { @@ -750,24 +792,36 @@ _mii_ssc_access( int baud = _mii_ssc_to_baud[c->control & 0x0F]; cfsetospeed(&tio, baud); cfsetispeed(&tio, baud); + tio.c_cflag &= ~(CSTOPB|CSIZE|PARENB|PARODD); // Update stop bits bit 7: 0 = 1 stop bit, 1 = 2 stop bits - tio.c_cflag &= ~CSTOPB; tio.c_cflag |= _mii_ssc_to_stop[(c->control >> 7) & 1]; // Update data bits bit 5-6 0=8 bits, 1=7 bits, 2=6 bits, 3=5 bits - tio.c_cflag &= ~CSIZE; tio.c_cflag |= _mii_ssc_to_bits[(c->control >> 6) & 3]; // parity are in c->command, bits 5-7, // 0=None, 1=Odd, 2=Even, 3=Mark, 4=Space - tio.c_cflag &= ~PARENB; - tio.c_cflag &= ~PARODD; tio.c_cflag |= _mii_ssc_to_parity[(c->command >> 5) & 3]; tcsetattr(c->tty_fd, TCSANOW, &tio); - printf("SSC%d: set %02x baud %d stop %d bits %d parity %d\n", - c->slot->id+1, byte, + int framesize = 1 + (((c->control >> 6) & 3)) + + ((c->control >> 7) & 1 ? 2 : 1) + + _mii_scc_to_bits_count[(c->control >> 5) & 3] + + (((c->command >> 5) & 3) ? 1 : 0); + // recalculate the timer delay between characters + float cps = (float)_mii_ssc_to_baud_rate[c->control & 0x0F] / framesize; + c->timer_delay = (1000000.0 * mii->speed) / cps; + + printf("SSC%d: baud:%5d stop:%d data:%d parity:%d (total %d)\n", + c->slot->id+1, _mii_ssc_to_baud_rate[c->control & 0x0F], (c->control >> 7) & 1 ? 2 : 1, _mii_scc_to_bits_count[(c->control >> 5) & 3], - (c->command >> 5) & 3); + (c->command >> 5) & 3, + framesize); +// printf("SSC%d: cps %.2f timer cycles %u\n", +// c->slot->id+1, cps, c->timer_delay); + // update the timer if it is too far in the future + if (mii_timer_get(mii, c->timer_check) > c->timer_delay) { + mii_timer_set(mii, c->timer_check, c->timer_delay); + } } break; default: // printf("%s PC:%04x addr %04x %02x wr:%d\n", __func__, @@ -794,6 +848,12 @@ _mii_ssc_command( printf("SSC%d: set tty %s: %s\n", slot->id+1, conf->device, c->human_config); } break; + case MII_SLOT_SSC_GET_TTY: { + mii_ssc_setconf_t * conf = param; + mii_card_ssc_t *c = slot->drv_priv; + *conf = c->conf; + res = 0; + } break; } return res; } diff --git a/src/mii.c b/src/mii.c index c2e0868..43a0f82 100644 --- a/src/mii.c +++ b/src/mii.c @@ -49,12 +49,21 @@ static const mii_bank_t _mii_banks_init[MII_BANK_COUNT] = { .mem_offset = 0xc000, .no_alloc = 1, }, + /* + * Aux memory is multiple times 64KB (with the ramworks card). *This* bank + * is always mapped to that aux bank zero, and is used for video as the + * video is always locked to that bank. + * + * So in MOST cases MII_BANK_AUX_BASE is pretty much the same as + * MII_BANK_AUX whent he ramworks is not in use. + */ [MII_BANK_AUX_BASE] = { .name = "AUX_BASE", .base = 0x0000, .size = 0xd0, // 208 pages, 48KB .no_alloc = 1, }, + /* This one is the one that is remapped with ramworks is in use */ [MII_BANK_AUX] = { .name = "AUX", .base = 0x0000, @@ -698,8 +707,9 @@ mii_prepare( mii->bank[MII_BANK_ROM].mem = (uint8_t*)&mii_rom_iic[0]; } - int banks = (flags >> MII_INIT_RAMWORKS_BIT) & 0xf; - banks = 12; +// int banks = (flags >> MII_INIT_RAMWORKS_BIT) & 0xf; + // hard code it for now, doesn't seem to have any detrimental effect + int banks = 12; if (banks > 12) banks = 12; for (int i = 0; i < banks; i++) // add available banks @@ -861,7 +871,7 @@ mii_timer_register( int64_t when, const char *name) { - if (mii->timer.map == (uint64_t)-1) { + if (mii->timer.map == (uint64_t)-1ll) { printf("%s no more timers!!\n", __func__); return 0xff; } @@ -916,7 +926,62 @@ mii_timer_run( } } -#if MII_65C02_DIRECT_ACCESS +uint8_t +mii_irq_register( + mii_t *mii, + const char *name ) +{ + if (mii->irq.map == 0xff) { + printf("%s no more IRQs!!\n", __func__); + return 0xff; + } + for (int i = 0; i < (int)sizeof(mii->irq.map) * 8; i++) { + if (!(mii->irq.map & (1 << i))) { + mii->irq.map |= 1 << i; + mii->irq.irq[i].name = name; + return i; + } + } + return 0xff; +} + +void +mii_irq_unregister( + mii_t *mii, + uint8_t irq_id ) +{ + if (irq_id >= (int)sizeof(mii->irq.map) * 8) + return; + mii->irq.map &= ~(1 << irq_id); +} + +void +mii_irq_raise( + mii_t *mii, + uint8_t irq_id ) +{ + if (irq_id >= (int)sizeof(mii->irq.map) * 8) + return; + if (!(mii->irq.raised & (1 << irq_id))) + mii->irq.irq[irq_id].count++; + mii->irq.raised |= 1 << irq_id; +} + +void +mii_irq_clear( + mii_t *mii, + uint8_t irq_id ) +{ + if (irq_id >= (int)sizeof(mii->irq.map) * 8) + return; + mii->irq.raised &= ~(1 << irq_id); +} + + +#if MII_65C02_DIRECT_ACCESS == 0 +#error "MII_65C02_DIRECT_ACCESS *has* to be enabled here" +#endif + static mii_cpu_state_t _mii_cpu_direct_access_cb( struct mii_cpu_t *cpu, @@ -924,6 +989,7 @@ _mii_cpu_direct_access_cb( { mii_t *mii = cpu->access_param; + mii->cpu_state = access; // update to latest state uint8_t cycle = mii->timer.last_cycle; mii_timer_run(mii, mii->cpu.cycle > cycle ? mii->cpu.cycle - cycle : @@ -962,8 +1028,11 @@ _mii_cpu_direct_access_cb( } } } - mii_mem_access(mii, addr, &access.data, wr, true); - return access; + mii_mem_access(mii, addr, &mii->cpu_state.data, wr, true); + // if any registered IRQ is raise, raise the CPU IRQ + mii->cpu_state.irq = mii->cpu_state.irq | !!mii->irq.raised; + + return mii->cpu_state; } void mii_run( @@ -981,63 +1050,6 @@ mii_run( if (unlikely(mii->cpu_state.trap)) _mii_handle_trap(mii); } -#else - -void -mii_run( - mii_t *mii) -{ - /* this runs all cycles for one instruction */ - uint16_t cycle = mii->cpu.cycle; - do { - if (unlikely(mii->trace_cpu > 1)) - mii_dump_trace_state(mii); - mii->cpu_state = mii_cpu_run(&mii->cpu, mii->cpu_state); - - mii_timer_run(mii, - mii->cpu.cycle > cycle ? mii->cpu.cycle - cycle : - mii->cpu.cycle); - cycle = mii->cpu.cycle; - - // extract 16-bit address from pin mask - const uint16_t addr = mii->cpu_state.addr; -// const uint8_t data = mii->cpu_state.data; - int wr = mii->cpu_state.w; -// uint8_t d = data; - if (unlikely(mii->debug.bp_map)) { - for (int i = 0; i < (int)sizeof(mii->debug.bp_map) * 8; i++) { - if (!(mii->debug.bp_map & (1 << i))) - continue; - if (addr >= mii->debug.bp[i].addr && - addr < mii->debug.bp[i].addr + mii->debug.bp[i].size) { - if (((mii->debug.bp[i].kind & MII_BP_R) && !wr) || - ((mii->debug.bp[i].kind & MII_BP_W) && wr)) { - - if (1 || !mii->debug.bp[i].silent) { - printf("BREAKPOINT %d at %04x PC:%04x\n", - i, addr, mii->cpu.PC); - mii_dump_run_trace(mii); - mii_dump_trace_state(mii); - mii->state = MII_STOPPED; - } - } - if (!(mii->debug.bp[i].kind & MII_BP_STICKY)) - mii->debug.bp_map &= ~(1 << i); - mii->debug.bp[i].kind |= MII_BP_HIT; - } - } - } - mii_mem_access(mii, addr, &mii->cpu_state.data, wr, true); - if (unlikely(mii->cpu_state.trap)) { - _mii_handle_trap(mii); - } - } while (!(mii->cpu_state.sync)); - - // log PC for the running disassembler display - mii->trace.log[mii->trace.idx] = mii->cpu.PC; - mii->trace.idx = (mii->trace.idx + 1) & (MII_PC_LOG_SIZE - 1); -} -#endif //! Read one byte from and addres, using the current memory mapping uint8_t @@ -1116,7 +1128,7 @@ mii_cpu_next( printf("NEXT opcode %04x:%02x\n", mii->cpu.PC, op); if (op == 0x20) { // JSR here? // set a temp breakpoint on reading 3 bytes from PC - if (mii->debug.bp_map != 0xffff) { + if (mii->debug.bp_map != (uint16_t)-1) { int i = ffsl(~mii->debug.bp_map) - 1; mii->debug.bp[i].addr = mii->cpu.PC + 3; mii->debug.bp[i].kind = MII_BP_R; diff --git a/src/mii.h b/src/mii.h index e925e9f..6d85bc1 100644 --- a/src/mii.h +++ b/src/mii.h @@ -48,7 +48,10 @@ enum { /* * A 'trap' is a sequence of 2 special NOPs that are used to trigger - * a callback. The callback is called with the mii_t * and the trap ID + * a callback. The callback is called with the mii_t * and the trap ID, + * + * This is used extensively by the smartport driver to jump back to the + * emulator code when a disk access is needed. */ typedef void (*mii_trap_handler_cb)( mii_t * mii, @@ -103,12 +106,31 @@ typedef uint64_t (*mii_timer_p)( * principal emulator state, for a faceless emulation */ typedef struct mii_t { + // this is the 'emulation' type, IIEE or IIC [currently only IIe works] uint emu; // MII_EMU_* mii_cpu_t cpu; mii_cpu_state_t cpu_state; - /* this is the video frame/VBL rate vs 60hz, default to MII_SPEED_NTSC */ + /* this is the CPU speed, default to MII_SPEED_NTSC */ float speed; unsigned int state; + /* + * These are used as MUX for IRQ requests from drivers. Each driver + * can request an IRQ number, and 'raise' and 'clear' it, and the + * CPU IRQ line will be set if any of them are raised, and cleared + * when none are raised. + * + * This fixes the problem of multiple drivers trying to raise the + * only IRQ line on the CPU. Typically if you have the mouse card and + * the serial card, or the mockingboard. + */ + struct { + uint16_t map; + uint16_t raised; + struct { + const char * name; + uint8_t count; + } irq[16]; + } irq; /* * These are 'cycle timers' -- they count down from a set value, * and stop at 0 (or possibly -1 or -2, depending on the instructions) @@ -141,10 +163,23 @@ typedef struct mii_t { }; } mem[256]; int mem_dirty; // recalculate mem[] on next access + /* + * RAMWORKS card emulation, this is a 16MB address space, with 128 + * possible 64KB banks. The 'avail' bitfield marks the banks that + * are 'possible' (depending on what size of RAMWORKS card is installed). + * The 'bank' array is a pointer to the actual memory block. + * + * These memory blocks replace the main AUX bank when a register is set. + */ struct { unsigned __int128 avail; uint8_t * bank[128]; } ramworks; + /* + * These are the 'real' state of the soft switches, as opposed to the + * value stores in the C000 page. This is made so they can be easily + * manipulated, copied, and restored... use the macros in mii_sw.h + */ uint32_t sw_state; // B_SW* bitfield mii_trace_t trace; int trace_cpu; @@ -163,11 +198,17 @@ typedef struct mii_t { } bp[16]; } debug; mii_bank_t bank[MII_BANK_COUNT]; - // the page c000 can have individual callbacks to override/supplement - // existing default behaviour. This is currently used for the titan - // accelerator 'card' + /* + * The page c000 can have individual callbacks to override/supplement + * existing default behaviour. This is currently used for the titan + * accelerator 'card' + */ mii_bank_access_t * soft_switches_override; mii_slot_t slot[7]; + + /* + * These are all the state of the various subsystems. + */ mii_video_t video; mii_speaker_t speaker; mii_mouse_t mouse; @@ -330,6 +371,23 @@ mii_timer_set( uint8_t timer_id, int64_t when); +uint8_t +mii_irq_register( + mii_t *mii, + const char *name ); +void +mii_irq_unregister( + mii_t *mii, + uint8_t irq_id ); +void +mii_irq_raise( + mii_t *mii, + uint8_t irq_id ); +void +mii_irq_clear( + mii_t *mii, + uint8_t irq_id ); + void mii_dump_trace_state( mii_t *mii); diff --git a/src/mii_slot.h b/src/mii_slot.h index 5ec2408..16d51e8 100644 --- a/src/mii_slot.h +++ b/src/mii_slot.h @@ -76,7 +76,8 @@ enum { MII_SLOT_DRIVE_LOAD = 0x20, // + drive index 0...n MII_SLOT_DRIVE_WP = 0x30, // + drive index 0...n - MII_SLOT_SSC_SET_TTY = 0x10, // param is a pathname, or NULL for a pty + MII_SLOT_SSC_SET_TTY = 0x10, // param is a mii_ssc_setconf_t + MII_SLOT_SSC_GET_TTY = 0x11, // param is a mii_ssc_setconf_t // + drive index 0..1. Param is a mii_floppy_t ** MII_SLOT_D2_GET_FLOPPY = 0x40, }; diff --git a/src/mii_video.c b/src/mii_video.c index 4629f86..df9c6ec 100644 --- a/src/mii_video.c +++ b/src/mii_video.c @@ -222,7 +222,6 @@ _mii_line_to_video_addr( return addr; } - static inline int _mii_addr_to_line_text_lores( uint16_t a) // ZERO based, not 0x400 based @@ -315,22 +314,23 @@ _mii_line_check_hires_dires( static void _mii_line_render_dhires_mono( - mii_t *mii) + struct mii_video_t *video, + uint32_t sw, + mii_bank_t * main, + mii_bank_t * aux ) { - mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; - mii_bank_t * aux = &mii->bank[MII_VIDEO_BANK]; - bool page2 = SW_GETSTATE(mii, SW80STORE) ? 0 : SW_GETSTATE(mii, SWPAGE2); -// uint8_t mode = mii->video.color_mode; + bool page2 = SWW_GETSTATE(sw, SW80STORE) ? 0 : SWW_GETSTATE(sw, SWPAGE2); uint16_t a = (0x2000 + (0x2000 * page2)); - mii->video.base_addr = a; - a = _mii_line_to_video_addr(a, mii->video.line); - mii->video.line_addr = a; - uint32_t * screen = mii->video.pixels + - (mii->video.line * MII_VRAM_WIDTH * 2); + + video->base_addr = a; + a = _mii_line_to_video_addr(a, video->line); + video->line_addr = a; + uint32_t * screen = video->pixels + + (video->line * MII_VRAM_WIDTH * 2); const uint32_t clut[2] = { - mii->video.clut.mono[0], - mii->video.clut.mono[1] }; + video->clut.mono[0], + video->clut.mono[1] }; for (int x = 0; x < 40; x++) { uint32_t ext = (mii_bank_peek(aux, a + x) & 0x7f) | ((mii_bank_peek(main, a + x) & 0x7f) << 7); @@ -357,18 +357,19 @@ _mii_get_1bits( static void _mii_line_render_dhires_color( - mii_t *mii) + struct mii_video_t *video, + uint32_t sw, + mii_bank_t * main, + mii_bank_t * aux ) { - mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; - mii_bank_t * aux = &mii->bank[MII_VIDEO_BANK]; - bool page2 = SW_GETSTATE(mii, SW80STORE) ? 0 : SW_GETSTATE(mii, SWPAGE2); - + bool page2 = SWW_GETSTATE(sw, SW80STORE) ? 0 : SWW_GETSTATE(sw, SWPAGE2); uint16_t a = (0x2000 + (0x2000 * page2)); - mii->video.base_addr = a; - a = _mii_line_to_video_addr(a, mii->video.line); - mii->video.line_addr = a; - uint32_t * screen = mii->video.pixels + - (mii->video.line * MII_VRAM_WIDTH * 2); + + video->base_addr = a; + a = _mii_line_to_video_addr(a, video->line); + video->line_addr = a; + uint32_t * screen = video->pixels + + (video->line * MII_VRAM_WIDTH * 2); uint8_t bits[71] = { 0 }; @@ -392,78 +393,26 @@ _mii_line_render_dhires_color( (_mii_get_1bits(bits, i + 2) << (3 - ((d + 2) % 4))) + (_mii_get_1bits(bits, i + 1) << (3 - ((d + 1) % 4))) + (_mii_get_1bits(bits, i) << (3 - (d % 4))); - uint32_t col = mii->video.clut.dhires[pixel]; + uint32_t col = video->clut.dhires[pixel]; *screen++ = col; } } -#if 0 -static int _trace = 0; -#define TRACE -#ifdef TRACE -#define T(_w) if (_trace) { _w; } -#else -#define T(_w) -#endif - static void _mii_line_render_hires( - mii_t *mii) -{ - mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; - bool page2 = SW_GETSTATE(mii, SW80STORE) ? 0 : SW_GETSTATE(mii, SWPAGE2); - uint8_t mode = mii->video.color_mode; - uint16_t a = (0x2000 + (0x2000 * page2)); - mii->video.base_addr = a; - a = _mii_line_to_video_addr(a, mii->video.line); - mii->video.line_addr = a; - uint32_t * screen = mii->video.pixels + - (mii->video.line * MII_VRAM_WIDTH * 2); - uint8_t bits[280] = { 0 }; - uint8_t *src = main->mem + a; - uint bits_dx = 0; - uint8_t last_run = 0; - for (int x = 0; x < 40; x += 2) { - /* prepare fourteen pixels */ - uint8_t b0 = src[x]; - uint8_t b1 = src[x + 1]; - uint8_t t[2] = { (b0 >> 7) << 2, (b1 >> 7) << 2 }; // prepare high bits - // just want the pixels as a nice clean 14 bits bitfield here - uint16_t run = ((reverse8(b0) >> 1) << 7) | (reverse8(b1) >> 1); -// uint16_t run = ((b1 & 0x7f) << 9) | ((b0 & 0x7f) << 2) | last_run; - uint8_t px; - for (int dx = 0 ; dx < 14; dx++) { - // take 2 bits, add the corresponding 'high bit' to it - px = run & 3; - px |= t[dx > 7]; - // now we have 2 pixels, we can set them in the bit buffer - bits[bits_dx++] = px; - } - last_run = run >> 14; - } - // now we have a 'nice' table of actual colors, without artifacts in bits - // we can plot this - for (int i = 0; i < 280; i++) { - uint8_t pixel = bits[i]; - uint32_t col = mii->video.clut.hires2[pixel]; - *screen++ = col; - *screen++ = col; - } -} -#else -static void -_mii_line_render_hires( - mii_t *mii) + struct mii_video_t *video, + uint32_t sw, + mii_bank_t * main, + mii_bank_t * aux ) { - mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; - bool page2 = SW_GETSTATE(mii, SW80STORE) ? 0 : SW_GETSTATE(mii, SWPAGE2); -// uint8_t mode = mii->video.color_mode; + bool page2 = SWW_GETSTATE(sw, SW80STORE) ? 0 : SWW_GETSTATE(sw, SWPAGE2); uint16_t a = (0x2000 + (0x2000 * page2)); - mii->video.base_addr = a; - a = _mii_line_to_video_addr(a, mii->video.line); - mii->video.line_addr = a; - uint32_t * screen = mii->video.pixels + - (mii->video.line * MII_VRAM_WIDTH * 2); + + video->base_addr = a; + a = _mii_line_to_video_addr(a, video->line); + video->line_addr = a; + uint32_t * screen = video->pixels + + (video->line * MII_VRAM_WIDTH * 2); uint8_t *src = main->mem; uint8_t b0 = 0; @@ -477,7 +426,7 @@ _mii_line_render_hires( ((b2 & 0x03) << ( 9 )); int odd = (x & 1) << 1; int offset = (b1 & 0x80) >> 5; - if (!mii->video.monochrome) { + if (!video->monochrome) { for (int i = 0; i < 7; i++) { uint8_t left = (run >> (1 + i)) & 1; uint8_t pixel = (run >> (2 + i)) & 1; @@ -495,9 +444,9 @@ _mii_line_render_hires( idx = offset + odd + 1 - (i & 1) + 1; } } - uint32_t col = mii->video.clut.hires[idx]; + uint32_t col = video->clut.hires[idx]; if (col != lastcol) { - uint32_t nc = mii->video.clut_low.hires[idx]; + uint32_t nc = video->clut_low.hires[idx]; *screen++ = nc; //col & C_SCANLINE_MASK; *screen++ = nc; //col & C_SCANLINE_MASK; lastcol = col; @@ -509,7 +458,7 @@ _mii_line_render_hires( } else { for (int i = 0; i < 7; i++) { uint8_t pixel = (run >> (2 + i)) & 1; - uint32_t col = mii->video.clut.mono[pixel]; + uint32_t col = video->clut.mono[pixel]; if (col != lastcol) { *screen++ = col & C_SCANLINE_MASK; lastcol = col; @@ -522,31 +471,32 @@ _mii_line_render_hires( b1 = b2; } } -#endif static void _mii_line_render_text( - mii_t *mii) + struct mii_video_t *video, + uint32_t sw, + mii_bank_t * main, + mii_bank_t * aux ) { - mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; - mii_bank_t * aux = &mii->bank[MII_VIDEO_BANK]; - bool page2 = SW_GETSTATE(mii, SW80STORE) ? 0 : SW_GETSTATE(mii, SWPAGE2); -// uint8_t mode = mii->video.color_mode; - + bool page2 = SWW_GETSTATE(sw, SW80STORE) ? 0 : SWW_GETSTATE(sw, SWPAGE2); uint16_t a = (0x400 + (0x400 * page2)); - mii->video.base_addr = a; - int i = mii->video.line >> 3; - a += ((i & 0x07) << 7) | ((i >> 3) << 5) | ((i >> 3) << 3); - mii->video.line_addr = a; - const uint8_t *rom_base = mii->emu == MII_EMU_IIEE ? - mii_rom_iiee_video : mii_rom_iic_video; - bool col80 = SW_GETSTATE(mii, SW80COL); - bool altset = SW_GETSTATE(mii, SWALTCHARSET); - int flash = mii->video.frame_count & MII_VIDEO_FLASH_FRAME_MASK ? + video->base_addr = a; + int i = video->line >> 3; + a += ((i & 0x07) << 7) | ((i >> 3) << 5) | ((i >> 3) << 3); + video->line_addr = a; +// const uint8_t *rom_base = mii->emu == MII_EMU_IIEE ? +// mii_rom_iiee_video : mii_rom_iic_video; + // TODO custom fonts + const uint8_t *rom_base = mii_rom_iiee_video; + + bool col80 = SWW_GETSTATE(sw, SW80COL); + bool altset = SWW_GETSTATE(sw, SWALTCHARSET); + int flash = video->frame_count & MII_VIDEO_FLASH_FRAME_MASK ? -0x40 : 0x40; - uint32_t * screen = mii->video.pixels + - (mii->video.line * MII_VRAM_WIDTH * 2); + uint32_t * screen = video->pixels + + (video->line * MII_VRAM_WIDTH * 2); for (int x = 0; x < 40 + (40 * col80); x++) { uint8_t c = 0; @@ -559,10 +509,10 @@ _mii_line_render_text( c = (int)c + flash; } const uint8_t * rom = rom_base + (c << 3); - uint8_t bits = rom[mii->video.line & 0x07]; + uint8_t bits = rom[video->line & 0x07]; for (int pi = 0; pi < 7; pi++) { uint8_t pixel = (bits >> pi) & 1; - uint32_t col = mii->video.clut.mono[!pixel]; + uint32_t col = video->clut.mono[!pixel]; *screen++ = col; if (!col80) *screen++ = col; @@ -572,24 +522,24 @@ _mii_line_render_text( static void _mii_line_render_lores( - mii_t *mii ) + struct mii_video_t *video, + uint32_t sw, + mii_bank_t * main, + mii_bank_t * aux ) { - mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; - mii_bank_t * aux = &mii->bank[MII_VIDEO_BANK]; - bool page2 = SW_GETSTATE(mii, SW80STORE) ? 0 : SW_GETSTATE(mii, SWPAGE2); -// uint8_t mode = mii->video.color_mode; - + bool page2 = SWW_GETSTATE(sw, SW80STORE) ? 0 : SWW_GETSTATE(sw, SWPAGE2); uint16_t a = (0x400 + (0x400 * page2)); - mii->video.base_addr = a; - int i = mii->video.line >> 3; + + video->base_addr = a; + int i = video->line >> 3; a += ((i & 0x07) << 7) | ((i >> 3) << 5) | ((i >> 3) << 3); - mii->video.line_addr = a; + video->line_addr = a; - bool col80 = SW_GETSTATE(mii, SW80COL); - uint32_t * screen = mii->video.pixels + - (mii->video.line * MII_VRAM_WIDTH * 2); - mii_video_clut_t * clut = &mii->video.clut; - mii_video_clut_t * clut_low = &mii->video.clut_low; + bool col80 = SWW_GETSTATE(sw, SW80COL); + uint32_t * screen = video->pixels + + (video->line * MII_VRAM_WIDTH * 2); + mii_video_clut_t * clut = &video->clut; + mii_video_clut_t * clut_low = &video->clut_low; uint32_t lastcolor = 0; for (int x = 0; x < 40 + (40 * col80); x++) { uint16_t c = 0; @@ -598,12 +548,12 @@ _mii_line_render_lores( else c = mii_bank_peek(main, a + x); - int lo_line = mii->video.line / 4; + int lo_line = video->line / 4; c = (c >> ((lo_line & 1) * 4)) & 0xf; // c |= (c << 4); uint32_t color = clut->lores[(x & col80) ^ col80][c & 0x0f]; uint32_t dim = clut_low->lores[(x & col80) ^ col80][c & 0x0f]; - if (!mii->video.monochrome) { + if (!video->monochrome) { for (int pi = 0; pi < 7; pi++) { uint32_t pixel = color; if (pixel != lastcolor) { @@ -636,21 +586,6 @@ _mii_line_render_lores( } } -/* - * This is called when writes are made from outside the 6502 emulation, for - * example the DMA froms smartport. Otherwise you could BLOAD an image in video - * ram and there would be now way of knowing if the addresses *were* in the - * video ram. So this call is used by anything doing DMA (curretnly just smartport) - */ -void -mii_video_OOB_write_check( - mii_t *mii, - uint16_t addr, - uint16_t size) -{ - for (int i = 0; i < size; i += 40) - mii->video.line_cb.check(&mii->video, mii->sw_state, addr + i); -} /* * This return the correct line drawing function callback for the mode @@ -658,7 +593,7 @@ mii_video_OOB_write_check( */ static mii_video_cb_t _mii_video_get_line_render_cb( - mii_t *mii, + mii_video_t *video, uint32_t sw_state) { mii_video_cb_t res = { 0 }; @@ -669,9 +604,8 @@ _mii_video_get_line_render_cb( bool dhires = SWW_GETSTATE(sw_state, SWDHIRES); if (hires && !text && col80 && dhires) { - mii_bank_t * sw = &mii->bank[MII_BANK_SW]; - uint8_t reg = mii_bank_peek(sw, SWAN3_REGISTER); - if (reg != 0 && !mii->video.monochrome) + uint8_t reg = video->an3_mode; + if (reg != 0 && !video->monochrome) res.render = _mii_line_render_dhires_color; else res.render = _mii_line_render_dhires_mono; @@ -705,31 +639,47 @@ _mii_video_mark_dirty( */ static void _mii_video_mode_changed( - mii_t *mii) + mii_video_t *video, + uint32_t sw_state) { - uint32_t sw_state = mii->sw_state; - - mii_video_cb_t res = _mii_video_get_line_render_cb(mii, sw_state); - if (res.render != mii->video.line_cb.render) { - mii->video.line_cb = res; - _mii_video_mark_dirty(&mii->video); + mii_video_cb_t res = _mii_video_get_line_render_cb(video, sw_state); + if (res.render != video->line_cb.render) { + video->line_cb = res; + _mii_video_mark_dirty(video); } } + +/* + * This is called when writes are made from outside the 6502 emulation, for + * example the DMA from smartport. Otherwise you could BLOAD an image in video + * ram and there would be no way of knowing if the addresses *were* in the video + * ram. So this call is used by anything doing DMA (currently just smartport) + */ +void +mii_video_OOB_write_check( + mii_t *mii, + uint16_t addr, + uint16_t size) +{ + for (int i = 0; i < size; i += 40) + mii->video.line_cb.check(&mii->video, mii->sw_state, addr + i); +} + /* - * This is the state of the video output + * This is the state machine to draw a line of the video output * All timings lifted from https://rich12345.tripod.com/aiivideo/vbl.html * * This is a 'protothread' basically cooperative scheduling using an * old compiler trick. It's not a real thread, but it's a way to * write code that looks like a thread, and is easy to read. - * The 'pt_start' macro starts the thread, and pt_yield() yields - * the thread to the main loop. - * The pt_end() macro ends the thread. + * + pt_start macro starts the 'thread' (and return to last yield point). + * + pt_yield() yields until the function is called again. + * + pt_end() macro ends the thread. * Remeber you cannot have locals in the thread, they must be * static or global. * *everything* before the pt_start call is ran every time, so you can use - * that to reload some sort of state, as here, were we reload all the + * that to reload some sort of state, as here, were we get all the * video mode softswitches. * * This function is also a 'cycle timer' it returns the number of 6502 @@ -740,40 +690,44 @@ _mii_video_mode_changed( */ static uint64_t mii_video_timer_cb( - mii_t *mii, - void *param) + mii_t *mii, + void *param) { uint64_t res = MII_VIDEO_H_CYCLES * mii->speed; + mii_bank_t * sw = &mii->bank[MII_BANK_SW]; - uint32_t sw_state = mii->sw_state; + uint32_t sw_state = mii->sw_state; + mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; + mii_bank_t * aux = &mii->bank[MII_VIDEO_BANK]; + mii_video_t * video = &mii->video; - pt_start(mii->video.state); + pt_start(video->state); /* - We cheat and draw a whole line at a time, then 'wait' until + We 'cheat' and draw a whole line at a time, then 'wait' until horizontal blanking, then wait until vertical blanking. */ do { // 'clear' VBL flag. Flag is 0 during retrace mii_bank_poke(sw, SWVBL, 0x80); - mii_video_line_drawing_cb line_drawing = mii->video.line_cb.render; + mii_video_line_drawing_cb line_drawing = video->line_cb.render; /* If we are in mixed mode past line 160, check if we need to * switch from the 'main mode' callback to the text callback */ - if (mii->video.line >= MII_VIDEO_MIXED_LINE) { + if (video->line >= MII_VIDEO_MIXED_LINE) { bool mixed = SWW_GETSTATE(sw_state, SWMIXED); if (mixed) { uint32_t sw = sw_state; SWW_SETSTATE(sw, SWTEXT, 1); if (sw != sw_state) - line_drawing = _mii_video_get_line_render_cb(mii, sw).render; + line_drawing = _mii_video_get_line_render_cb(video, sw).render; } } - if (mii->video.lines_dirty[mii->video.line / 64] & - (1ULL << (mii->video.line & 63))) { - line_drawing(mii); + if (video->lines_dirty[video->line / 64] & + (1ULL << (video->line & 63))) { + line_drawing(video, sw_state, main, aux); - uint32_t * screen = mii->video.pixels + - (mii->video.line * MII_VRAM_WIDTH * 2); + uint32_t * screen = video->pixels + + (video->line * MII_VRAM_WIDTH * 2); uint32_t * l2 = screen + MII_VRAM_WIDTH; #if defined(__AVX2__) @@ -807,57 +761,65 @@ mii_video_timer_cb( #endif #endif - mii->video.lines_dirty[mii->video.line / 64] &= - ~(1ULL << (mii->video.line & 63)); - mii->video.frame_dirty = 1; + video->lines_dirty[video->line / 64] &= + ~(1ULL << (video->line & 63)); + video->frame_dirty = 1; #if MII_VIDEO_DEBUG_HEAPMAP - mii->video.video_hmap[mii->video.line] = 0xff; + video->video_hmap[video->line] = 0xff; #endif } - mii->video.line++; - if (mii->video.line == 192) { - mii->video.line = 0; - mii->video.line_addr = mii->video.base_addr; - mii->video.timer_max = MII_VIDEO_H_CYCLES; - res = mii->video.timer_max * mii->speed; - pt_yield(mii->video.state); + video->line++; + if (video->line == 192) { + video->line = 0; + video->line_addr = video->base_addr; + video->timer_max = MII_VIDEO_H_CYCLES; + res = video->timer_max * mii->speed; + pt_yield(video->state); mii_bank_poke(sw, SWVBL, 0x00); - mii->video.timer_max = MII_VBL_UP_CYCLES; - res = mii->video.timer_max * mii->speed; + video->timer_max = MII_VBL_UP_CYCLES; + res = video->timer_max * mii->speed; /* - * This is to handle the corner case where text has some blinking - * text, and we need to redraw the screen. - * We only check every 16 frames, so we don't waste time - * redrawing the screen every frame. Also, the alt char set needs - * to be off, as the blinking text is only in the main charset. + * This is to handle the corner case where text screen has some + * blinking text, and we need to redraw the screen. + * + * We only check every 16 frames, so we don't waste time redrawing + * the screen every frame. Also, the alt char set needs to be off, + * as the blinking text is only in the main charset. */ - uint32_t new_frame = mii->video.frame_count + 1; + uint32_t new_frame = video->frame_count + 1; if ((new_frame & MII_VIDEO_FLASH_FRAME_MASK) != - (mii->video.frame_count & MII_VIDEO_FLASH_FRAME_MASK)) { + (video->frame_count & MII_VIDEO_FLASH_FRAME_MASK)) { if (!SW_GETSTATE(mii, SWALTCHARSET)) - _mii_video_mark_dirty(&mii->video); + _mii_video_mark_dirty(video); } - mii->video.frame_count = new_frame; - pt_yield(mii->video.state); + video->frame_count = new_frame; + pt_yield(video->state); // check if we need to switch the video mode, in case the UI switches // Color/mono palette etc mii->cpu.instruction_run = 0; // stop current instruction run! - if (mii->video.frame_dirty) - mii->video.frame_seed++; - mii->video.frame_dirty = 0; + if (video->frame_dirty) + video->frame_seed++; + video->frame_dirty = 0; } else { - mii->video.timer_max = MII_VIDEO_H_CYCLES + MII_VIDEO_HB_CYCLES; - res = mii->video.timer_max * mii->speed; - pt_yield(mii->video.state); + video->timer_max = MII_VIDEO_H_CYCLES + MII_VIDEO_HB_CYCLES; + res = video->timer_max * mii->speed; + pt_yield(video->state); } } while (1); - pt_end(mii->video.state); + pt_end(video->state); return res; } /* * TODO: this doesn't work yet. Don't get overexcited about this. * Or, get overexcited about this and fix it! :-) + * + * This is mostly how it's supposed to work anyway. Any 'read' access should + * be able to deduce where the 'beam' is by looking at what the remaining + * timer cycles are, and the current line. + * And then deduce the exact position horizontally on the screen. + * + * But, it doesn't really work for now. */ uint8_t mii_video_get_vapor( @@ -914,7 +876,7 @@ mii_access_video( // res = true; // we return false here, so generic SW code is called SW_SETSTATE(mii, SWHIRES, addr & 1); mii_bank_poke(sw, SWHIRES, (addr & 1) << 7); - _mii_video_mode_changed(mii); + _mii_video_mode_changed(&mii->video, mii->sw_state); break; case SWPAGE2OFF: case SWPAGE2ON: @@ -925,7 +887,7 @@ mii_access_video( *byte = mii_bank_peek(sw, SWPAGE2); // 80STORE completely changes the meaning of PAGE2 if (!SW_GETSTATE(mii, SW80STORE)) { - _mii_video_mode_changed(mii); + _mii_video_mode_changed(&mii->video, mii->sw_state); _mii_video_mark_dirty(&mii->video); } break; @@ -935,11 +897,12 @@ mii_access_video( res = true; SW_SETSTATE(mii, SW80COL, addr & 1); mii_bank_poke(sw, SW80COL, (addr & 1) << 7); - _mii_video_mode_changed(mii); + _mii_video_mode_changed(&mii->video, mii->sw_state); break; case SWDHIRESOFF: // 0xc05f, case SWDHIRESON: { // = 0xc05e, res = true; + mii_video_t * video = &mii->video; uint8_t an3 = !!mii_bank_peek(sw, SWAN3); bool an3_on = !!(addr & 1); // 5f is ON, 5e is OFF uint8_t reg = mii_bank_peek(sw, SWAN3_REGISTER); @@ -947,21 +910,22 @@ mii_access_video( uint8_t bit = SW_GETSTATE(mii, SW80COL); reg = ((reg << 1) | bit) & 3; // printf("VIDEO 80:%d REG now %x\n", bit, reg); + video->an3_mode = reg; mii_bank_poke(sw, SWAN3_REGISTER, reg); } mii_bank_poke(sw, SWAN3, an3_on ? 0x80 : 0); // printf("DHRES IS %s mode:%d\n", (addr & 1) ? "OFF" : "ON ", reg); SW_SETSTATE(mii, SWDHIRES, !(addr & 1)); mii_bank_poke(sw, SWRDDHIRES, (!(addr & 1)) << 7); - _mii_video_mark_dirty(&mii->video); - _mii_video_mode_changed(mii); + _mii_video_mark_dirty(video); + _mii_video_mode_changed(&mii->video, mii->sw_state); } break; case SWTEXTOFF: case SWTEXTON: res = true; SW_SETSTATE(mii, SWTEXT, addr & 1); mii_bank_poke(sw, SWTEXT, (addr & 1) << 7); - _mii_video_mode_changed(mii); + _mii_video_mode_changed(&mii->video, mii->sw_state); if (!write) *byte = mii_video_get_vapor(mii); break; @@ -970,7 +934,7 @@ mii_access_video( res = true; SW_SETSTATE(mii, SWMIXED, addr & 1); mii_bank_poke(sw, SWMIXED, (addr & 1) << 7); - _mii_video_mode_changed(mii); + _mii_video_mode_changed(&mii->video, mii->sw_state); if (!write) *byte = mii_video_get_vapor(mii); break; @@ -978,6 +942,11 @@ mii_access_video( return res; } +/* + * This is mostly a debug function, to be able to 'force' a full screen + * refresh, for example when the emulator is paused, and you want to see + * the screen 'as it is' for example if you poke memory while debugging. + */ void mii_video_full_refresh( mii_t *mii) @@ -1006,7 +975,7 @@ mii_video_init( // start the DHRES in color mii_bank_t * sw = &mii->bank[MII_BANK_SW]; mii_bank_poke(sw, SWAN3_REGISTER, 1); - _mii_video_mode_changed(mii); + _mii_video_mode_changed(&mii->video, mii->sw_state); mii_video_set_mode(mii, 0); } @@ -1136,22 +1105,31 @@ _mii_rgb_to_lumed_color( #endif } - +/* + * All this kitchen mess is to implement a 'palette' system, where we can + * cycle through different color palettes. The palettes are defined in the + * palettes array, and the mii_video_set_mode() function is used to switch + * between them. + * + * It calculates a 'dimmed' version of the colors, and stores them in the + * clut_low structure, the 'dimmed' colors are used for creating artifacts. + */ void mii_video_set_mode( mii_t *mii, uint8_t mode) { + mii_video_t * video = &mii->video; // used to implement cycling through palettes if (mode >= (sizeof(palettes) / sizeof(palettes[0]))) mode = 0; // printf("%s mode %d\n", __func__, mode); - mii->video.color_mode = mode; - mii_video_clut_t * clut = &mii->video.clut; + video->color_mode = mode; + mii_video_clut_t * clut = &video->clut; uint32_t base = palettes[mode].mono_color; - mii->video.monochrome = base != 0; - if (mii->video.monochrome) { + video->monochrome = base != 0; + if (video->monochrome) { // convert one set of RGB colors to monochrome. arbitrarily 0 const mii_palette_t * pal = &palettes[0]; // base CLUT is using color *indexes* in the palette we picked @@ -1173,8 +1151,8 @@ mii_video_set_mode( bb = out.b * 255; base = HI_RGB(br, bg, bb); - clut = &mii->video.clut_low; - *clut = mii->video.clut; + clut = &video->clut_low; + *clut = video->clut; for (uint i = 0; i < sizeof(clut->colors) / sizeof(clut->colors[0]); i++) { clut->colors[i] = _mii_rgb_to_lumed_color( @@ -1185,8 +1163,8 @@ mii_video_set_mode( // base CLUT is using color *indexes* in the palette we picked for (uint i = 0; i < sizeof(clut->colors) / sizeof(clut->colors[0]); i++) clut->colors[i] = pal->color[mii_base_clut.colors[i]]; - clut = &mii->video.clut_low; - *clut = mii->video.clut; + clut = &video->clut_low; + *clut = video->clut; for (uint i = 0; i < sizeof(clut->colors) / sizeof(clut->colors[0]); i++) { uint8_t br, bg, bb; @@ -1213,13 +1191,14 @@ _mii_mish_video( const char * argv[]) { mii_t * mii = param; + mii_video_t * video = &mii->video; if (!argv[1] || !strcmp(argv[1], "list")) { for (int i = 0; i < 16; i++) { printf("%01x: %08x %08x %08x\n", i, - mii->video.clut.lores[0][i], - mii->video.clut.lores[1][i], - mii->video.clut.dhires[i]); + video->clut.lores[0][i], + video->clut.lores[1][i], + video->clut.dhires[i]); } return; } @@ -1227,8 +1206,9 @@ _mii_mish_video( mii_bank_t * sw = &mii->bank[MII_BANK_SW]; uint8_t reg = mii_bank_peek(sw, SWAN3_REGISTER); printf("AN3 REG %d -> %d\n", reg, 1); + video->an3_mode = 1; mii_bank_poke(sw, SWAN3_REGISTER, 1); - _mii_video_mode_changed(mii); + _mii_video_mode_changed(video, mii->sw_state); mii_video_full_refresh(mii); return; } @@ -1236,13 +1216,14 @@ _mii_mish_video( mii_bank_t * sw = &mii->bank[MII_BANK_SW]; uint8_t reg = mii_bank_peek(sw, SWAN3_REGISTER); printf("AN3 REG %d -> %d\n", reg, 0); + video->an3_mode = 0; mii_bank_poke(sw, SWAN3_REGISTER, 0); - _mii_video_mode_changed(mii); + _mii_video_mode_changed(video, mii->sw_state); mii_video_full_refresh(mii); return; } if (!strcmp(argv[1], "dirty")) { - _mii_video_mode_changed(mii); + _mii_video_mode_changed(video, mii->sw_state); mii_video_full_refresh(mii); return; } diff --git a/src/mii_video.h b/src/mii_video.h index 5038176..6c7960e 100644 --- a/src/mii_video.h +++ b/src/mii_video.h @@ -35,7 +35,10 @@ struct mii_t; struct mii_video_t; typedef void (*mii_video_line_drawing_cb)( - mii_t *mii ); + struct mii_video_t *video, + uint32_t sw, + mii_bank_t * main, + mii_bank_t * aux ); typedef void (*mii_video_line_check_cb)( struct mii_video_t *video, uint32_t sw, @@ -58,7 +61,6 @@ typedef union mii_video_clut_t { mii_color_t lores[2][16]; // lores (main, and aux page DLORES) mii_color_t dhires[16]; mii_color_t hires[10]; -// mii_color_t hires2[8]; mii_color_t text[2]; // text mii_color_t mono[2]; // DHRES mono mode }; @@ -69,6 +71,7 @@ typedef struct mii_video_t { void * state; // protothread state in mii_video.c uint8_t timer_id; // timer id for the video thread uint8_t line; // current line for cycle timer + uint8_t an3_mode; // current mode uint16_t base_addr; // current mode base address uint16_t line_addr; // VRAM address for the line we are on uint64_t timer_max; // timer start value diff --git a/ui_gl/mii_mui_about.c b/ui_gl/mii_mui_about.c index 8f220f8..30bc7e4 100644 --- a/ui_gl/mii_mui_about.c +++ b/ui_gl/mii_mui_about.c @@ -114,7 +114,10 @@ static const char * thanksto = "Jef Raskin\n" "Susan Kare\n" "Thierry Magniez\n" + "Xavier Schott\n" + "Yann Jacob\n" "Matthew Bloch\n" + "Bill Martens\n" "Charles \"regnips\" Springer\n" "Jeroen \"Sprite_tm\" Domburg\n" "Claude \"Claude\" Schwarz\n"