summaryrefslogtreecommitdiffstats
path: root/hacks/apple2.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/apple2.c')
-rw-r--r--hacks/apple2.c885
1 files changed, 885 insertions, 0 deletions
diff --git a/hacks/apple2.c b/hacks/apple2.c
new file mode 100644
index 0000000..1918a5c
--- /dev/null
+++ b/hacks/apple2.c
@@ -0,0 +1,885 @@
+/* xscreensaver, Copyright (c) 1998-2010 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org>
+ * with additional work by Jamie Zawinski <jwz@jwz.org>
+ */
+
+#include <math.h>
+#include "screenhackI.h"
+#include "apple2.h"
+#include "ximage-loader.h"
+
+#ifdef HAVE_XSHM_EXTENSION
+#include "xshm.h"
+#endif
+
+/*
+ * Implementation notes
+ *
+ * The A2 had 3 display modes: text, lores, and hires. Text was 40x24, and it
+ * disabled color in the TV. Lores gave you 40x48 graphics blocks, using the
+ * same memory as the text screen. Each could be one of 16 colors. Hires gave
+ * you 280x192 pixels. Odd pixels were blue or purple, and even pixels were
+ * orange or green depending on the setting of the high bit in each byte.
+ *
+ * The graphics modes could also have 4 lines of text at the bottom. This was
+ * fairly unreadable if you had a color monitor.
+ *
+ * Each mode had 2 different screens using different memory space. In hires
+ * mode this was sometimes used for double buffering, but more often the lower
+ * screen was full of code/data and the upper screen was used for display, so
+ * you got random garbage on the screen.
+ *
+ * The text font is based on X's standard 6x10 font, with a few tweaks like
+ * putting a slash across the zero.
+ *
+ * To use this, you'll call apple2(display, window, duration,
+ * controller) where the function controller defines what will happen.
+ * See bsod.c and apple2-main.c for example controllers. The
+ * controller function gets called whenever the machine ready to start
+ * something new. By setting sim->printing or sim->typing, it'll be
+ * busy for some time spitting characters out one at a time. By
+ * setting *next_actiontime+=X.X, it'll pause and just update the screen
+ * for that long before calling the controller function again.
+ *
+ * By setting stepno to A2CONTROLLER_DONE, the loop will end. It will also end
+ * after the time specified by the delay parameter. In either case, it calls
+ * the controller with stepno==A2CONTROLLER_FREE to allow it to release any
+ * memory.
+ *
+ * The void* apple2_sim_t::controller_data is for the use of the controller.
+ * It will be initialize to NULL, and the controller can store its own state
+ * there.
+ *
+ */
+
+void
+a2_scroll(apple2_state_t *st)
+{
+ int i;
+ st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off cursor */
+
+ for (i=0; i<23; i++) {
+ memcpy(st->textlines[i],st->textlines[i+1],40);
+ }
+ memset(st->textlines[23],0xe0,40);
+}
+
+static void
+a2_printc_1(apple2_state_t *st, char c, int scroll_p)
+{
+ st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
+
+ if (c == '\n') /* ^J == NL */
+ {
+ if (st->cursy==23)
+ {
+ if (scroll_p)
+ a2_scroll(st);
+ }
+ else
+ {
+ st->cursy++;
+ }
+ st->cursx=0;
+ }
+ else if (c == 014) /* ^L == CLS, Home */
+ {
+ a2_cls(st);
+ a2_goto(st,0,0);
+ }
+ else if (c == '\t') /* ^I == tab */
+ {
+ a2_goto(st, st->cursy, (st->cursx+8)&~7);
+ }
+ else if (c == 010) /* ^H == backspace */
+ {
+ st->textlines[st->cursy][st->cursx]=0xe0;
+ a2_goto(st, st->cursy, st->cursx-1);
+ }
+ else if (c == '\r') /* ^M == CR */
+ {
+ st->cursx=0;
+ }
+ else
+ {
+ st->textlines[st->cursy][st->cursx]=c ^ 0xc0;
+ st->cursx++;
+ if (st->cursx==40) {
+ if (st->cursy==23) {
+ if (scroll_p)
+ a2_scroll(st);
+ } else {
+ st->cursy++;
+ }
+ st->cursx=0;
+ }
+ }
+
+ st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
+}
+
+void
+a2_printc(apple2_state_t *st, char c)
+{
+ a2_printc_1(st, c, 1);
+}
+
+void
+a2_printc_noscroll(apple2_state_t *st, char c)
+{
+ a2_printc_1(st, c, 0);
+}
+
+
+void
+a2_prints(apple2_state_t *st, char *s)
+{
+ while (*s) a2_printc(st, *s++);
+}
+
+void
+a2_goto(apple2_state_t *st, int r, int c)
+{
+ if (r > 23) r = 23;
+ if (c > 39) c = 39;
+ st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
+ st->cursy=r;
+ st->cursx=c;
+ st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
+}
+
+void
+a2_cls(apple2_state_t *st)
+{
+ int i;
+ for (i=0; i<24; i++) {
+ memset(st->textlines[i],0xe0,40);
+ }
+}
+
+void
+a2_clear_gr(apple2_state_t *st)
+{
+ int i;
+ for (i=0; i<24; i++) {
+ memset(st->textlines[i],0x00,40);
+ }
+}
+
+void
+a2_clear_hgr(apple2_state_t *st)
+{
+ int i;
+ for (i=0; i<192; i++) {
+ memset(st->hireslines[i],0,40);
+ }
+}
+
+void
+a2_invalidate(apple2_state_t *st)
+{
+}
+
+void
+a2_poke(apple2_state_t *st, int addr, int val)
+{
+
+ if (addr>=0x400 && addr<0x800) {
+ /* text memory */
+ int row=((addr&0x380)/0x80) + ((addr&0x7f)/0x28)*8;
+ int col=(addr&0x7f)%0x28;
+ if (row<24 && col<40) {
+ st->textlines[row][col]=val;
+ if (!(st->gr_mode&(A2_GR_HIRES)) ||
+ (!(st->gr_mode&(A2_GR_FULL)) && row>=20)) {
+ }
+ }
+ }
+ else if (addr>=0x2000 && addr<0x4000) {
+ int row=(((addr&0x1c00) / 0x400) * 1 +
+ ((addr&0x0380) / 0x80) * 8 +
+ ((addr&0x0078) / 0x28) * 64);
+ int col=((addr&0x07f)%0x28);
+ if (row<192 && col<40) {
+ st->hireslines[row][col]=val;
+ if (st->gr_mode&A2_GR_HIRES) {
+ }
+ }
+ }
+}
+
+void
+a2_hplot(apple2_state_t *st, int hcolor, int x, int y)
+{
+ int highbit,run;
+
+ highbit=((hcolor<<5)&0x80) ^ 0x80; /* capture bit 2 into bit 7 */
+
+ if (y<0 || y>=192 || x<0 || x>=280) return;
+
+ for (run=0; run<2 && x<280; run++) {
+ unsigned char *vidbyte = &st->hireslines[y][x/7];
+ unsigned char whichbit=1<<(x%7);
+ int masked_bit;
+
+ *vidbyte = (*vidbyte & 0x7f) | highbit;
+
+ /* use either bit 0 or 1 of hcolor for odd or even pixels */
+ masked_bit = (hcolor>>(1-(x&1)))&1;
+
+ /* Set whichbit to 1 or 0 depending on color */
+ *vidbyte = (*vidbyte & ~whichbit) | (masked_bit ? whichbit : 0);
+
+ x++;
+ }
+}
+
+void
+a2_hline(apple2_state_t *st, int hcolor, int x1, int y1, int x2, int y2)
+{
+ int dx,dy,incx,incy,x,y,balance;
+
+ /* Bresenham's line drawing algorithm */
+
+ if (x2>=x1) {
+ dx=x2-x1;
+ incx=1;
+ } else {
+ dx=x1-x2;
+ incx=-1;
+ }
+ if (y2>=y1) {
+ dy=y2-y1;
+ incy=1;
+ } else {
+ dy=y1-y2;
+ incy=-1;
+ }
+
+ x=x1; y=y1;
+
+ if (dx>=dy) {
+ dy*=2;
+ balance=dy-dx;
+ dx*=2;
+ while (x!=x2) {
+ a2_hplot(st, hcolor, x, y);
+ if (balance>=0) {
+ y+=incy;
+ balance-=dx;
+ }
+ balance+=dy;
+ x+=incx;
+ }
+ a2_hplot(st, hcolor, x, y);
+ } else {
+ dx*=2;
+ balance=dx-dy;
+ dy*=2;
+ while (y!=y2) {
+ a2_hplot(st, hcolor, x, y);
+ if (balance>=0) {
+ x+=incx;
+ balance-=dy;
+ }
+ balance+=dx;
+ y+=incy;
+ }
+ a2_hplot(st, hcolor, x, y);
+ }
+}
+
+void
+a2_plot(apple2_state_t *st, int color, int x, int y)
+{
+ int textrow=y/2;
+ unsigned char byte;
+
+ if (x<0 || x>=40 || y<0 || y>=48) return;
+
+ byte=st->textlines[textrow][x];
+ if (y&1) {
+ byte = (byte&0xf0) | (color&0x0f);
+ } else {
+ byte = (byte&0x0f) | ((color&0x0f)<<4);
+ }
+ st->textlines[textrow][x]=byte;
+}
+
+void
+a2_display_image_loading(apple2_state_t *st, unsigned char *image,
+ int lineno)
+{
+ /*
+ When loading images,it would normally just load the big binary
+ dump into screen memory while you watched. Because of the way
+ screen memory was laid out, it wouldn't load from the top down,
+ but in a funny interleaved way. You should call this with lineno
+ increasing from 0 thru 191 over a period of a few seconds.
+ */
+
+ int row=(((lineno / 24) % 8) * 1 +
+ ((lineno / 3 ) % 8) * 8 +
+ ((lineno / 1 ) % 3) * 64);
+
+ memcpy (st->hireslines[row], &image[row * 40], 40);
+}
+
+/*
+ Simulate plausible initial memory contents for running a program.
+*/
+void
+a2_init_memory_active(apple2_sim_t *sim)
+{
+ int i,j,x,y,c;
+ int addr=0;
+ apple2_state_t *st=sim->st;
+
+ while (addr<0x4000) {
+ int n;
+
+ switch (random()%4) {
+ case 0:
+ case 1:
+ n=random()%500;
+ for (i=0; i<n && addr<0x4000; i++) {
+ unsigned char rb=((random()%6==0 ? 0 : random()%16) |
+ ((random()%5==0 ? 0 : random()%16)<<4));
+ a2_poke(st, addr++, rb);
+ }
+ break;
+
+ case 2:
+ /* Simulate shapes stored in memory. We use the font since we have it.
+ Unreadable, since rows of each character are stored in consecutive
+ bytes. It was typical to store each of the 7 possible shifts of
+ bitmaps, for fastest blitting to the screen. */
+ x=random()%(sim->text_im->width);
+ for (i=0; i<100; i++) {
+ for (y=0; y<8; y++) {
+ c=0;
+ for (j=0; j<8; j++) {
+ c |= XGetPixel(sim->text_im, (x+j)%sim->text_im->width, y)<<j;
+ }
+ a2_poke(st, addr++, c);
+ }
+ x=(x+1)%(sim->text_im->width);
+ }
+ break;
+
+ case 3:
+ if (addr>0x2000) {
+ n=random()%200;
+ for (i=0; i<n && addr<0x4000; i++) {
+ a2_poke(st, addr++, 0);
+ }
+ }
+ break;
+
+ }
+ }
+}
+
+
+#if 1 /* jwz: since MacOS doesn't have "6x10", I dumped this font to a PNG...
+ */
+
+#include "images/gen/apple2font_png.h"
+
+static void
+a2_make_font(apple2_sim_t *sim)
+{
+ int pix_w, pix_h;
+ XWindowAttributes xgwa;
+ Pixmap m = 0;
+ Pixmap p = image_data_to_pixmap (sim->dpy, sim->window,
+ apple2font_png, sizeof(apple2font_png),
+ &pix_w, &pix_h, &m);
+ XImage *im = XGetImage (sim->dpy, p, 0, 0, pix_w, pix_h, ~0L, ZPixmap);
+ XImage *mm = XGetImage (sim->dpy, m, 0, 0, pix_w, pix_h, 1, XYPixmap);
+ unsigned long black =
+ BlackPixelOfScreen (DefaultScreenOfDisplay (sim->dpy));
+ int x, y;
+
+ XFreePixmap (sim->dpy, p);
+ XFreePixmap (sim->dpy, m);
+ if (pix_w != 64*7) abort();
+ if (pix_h != 8) abort();
+
+ XGetWindowAttributes (sim->dpy, sim->window, &xgwa);
+ sim->text_im = XCreateImage (sim->dpy, xgwa.visual, 1, XYBitmap, 0, 0,
+ pix_w, pix_h, 8, 0);
+ sim->text_im->data = malloc (sim->text_im->bytes_per_line *
+ sim->text_im->height);
+
+ /* Convert deep image to 1 bit */
+ for (y = 0; y < pix_h; y++)
+ for (x = 0; x < pix_w; x++)
+ XPutPixel (sim->text_im, x, y,
+ (XGetPixel (mm, x, y)
+ ? XGetPixel (im, x, y) == black
+ : 0));
+
+ XDestroyImage (im);
+ XDestroyImage (mm);
+}
+
+#else /* 0 */
+
+/* This table lists fixes for characters that differ from the standard 6x10
+ font. Each encodes a pixel, as (charindex*7 + x) + (y<<10) + (value<<15)
+ where value is 0 for white and 1 for black. */
+static const unsigned short a2_fixfont[] = {
+ /* Fix $ */ 0x8421, 0x941d,
+ /* Fix % */ 0x8024, 0x0028, 0x8425, 0x0426, 0x0825, 0x1027, 0x1426, 0x9427,
+ 0x1824, 0x9828,
+ /* Fix * */ 0x8049, 0x8449, 0x8849, 0x0c47, 0x0c48, 0x0c4a, 0x0c4b, 0x9049,
+ 0x9449, 0x9849,
+ /* Fix , */ 0x9057, 0x1458, 0x9856, 0x1857, 0x1c56,
+ /* Fix . */ 0x1465, 0x1864, 0x1866, 0x1c65,
+ /* Fix / */ 0x006e, 0x186a,
+ /* Fix 0 */ 0x8874, 0x8c73, 0x9072,
+ /* Fix 1 */ 0x0878, 0x1878, 0x187c,
+ /* Fix 5 */ 0x8895, 0x0c94, 0x0c95,
+ /* Fix 6 */ 0x809f, 0x8c9c, 0x109c,
+ /* Fix 7 */ 0x8ca4, 0x0ca5, 0x90a3, 0x10a4,
+ /* Fix 9 */ 0x08b3, 0x8cb3, 0x98b0,
+ /* Fix : */ 0x04b9, 0x08b8, 0x08ba, 0x0cb9, 0x90b9, 0x14b9, 0x18b8, 0x18b9,
+ 0x18ba, 0x1cb9,
+ /* Fix ; */ 0x04c0, 0x08bf, 0x08c1, 0x0cc0, 0x90c0, 0x14c1, 0x98bf, 0x18c0,
+ 0x1cbf,
+ /* Fix < */ 0x80c8, 0x00c9, 0x84c7, 0x04c8, 0x88c6, 0x08c7, 0x8cc5, 0x0cc6,
+ 0x90c6, 0x10c7,
+ 0x94c7, 0x14c8, 0x98c8, 0x18c9,
+ /* Fix > */ 0x80d3, 0x00d4, 0x84d4, 0x04d5, 0x88d5, 0x08d6, 0x8cd6, 0x0cd7,
+ 0x90d5, 0x10d6,
+ 0x94d4, 0x14d5, 0x98d3, 0x18d4,
+ /* Fix @ */ 0x88e3, 0x08e4, 0x8ce4, 0x98e5,
+ /* Fix B */ 0x84ef, 0x04f0, 0x88ef, 0x08f0, 0x8cef, 0x90ef, 0x10f0, 0x94ef,
+ 0x14f0,
+ /* Fix D */ 0x84fd, 0x04fe, 0x88fd, 0x08fe, 0x8cfd, 0x0cfe, 0x90fd, 0x10fe,
+ 0x94fd, 0x14fe,
+ /* Fix G */ 0x8116, 0x0516, 0x9916,
+ /* Fix J */ 0x0129, 0x012a, 0x052a, 0x852b, 0x092a, 0x892b, 0x0d2a, 0x8d2b,
+ 0x112a, 0x912b,
+ 0x152a, 0x952b, 0x992a,
+ /* Fix M */ 0x853d, 0x853f, 0x093d, 0x893e, 0x093f,
+ /* Fix Q */ 0x915a, 0x155a, 0x955b, 0x155c, 0x195b, 0x995c, 0x1d5c,
+ /* Fix V */ 0x8d7b, 0x0d7c, 0x0d7e, 0x8d7f, 0x917b, 0x117c, 0x117e, 0x917f,
+ /* Fix [ */ 0x819e, 0x81a2, 0x859e, 0x899e, 0x8d9e, 0x919e, 0x959e, 0x999e,
+ 0x99a2,
+ /* Fix \ */ 0x01a5, 0x19a9,
+ /* Fix ] */ 0x81ac, 0x81b0, 0x85b0, 0x89b0, 0x8db0, 0x91b0, 0x95b0, 0x99ac,
+ 0x99b0,
+ /* Fix ^ */ 0x01b5, 0x05b4, 0x05b6, 0x09b3, 0x89b5, 0x09b7, 0x8db4, 0x8db6,
+ 0x91b3, 0x91b7,
+ /* Fix _ */ 0x9db9, 0x9dbf,
+ 0,
+};
+
+static void
+a2_make_font(apple2_sim_t *sim)
+{
+ /*
+ Generate the font. It used a 5x7 font which looks a lot like the standard X
+ 6x10 font, with a few differences. So we render up all the uppercase
+ letters of 6x10, and make a few tweaks (like putting a slash across the
+ zero) according to fixfont.
+ */
+
+ int i;
+ const char *def_font="6x10";
+ XFontStruct *font;
+ Pixmap text_pm;
+ GC gc;
+ XGCValues gcv;
+
+ font = load_font_retry (sim->dpy, def_font);
+ if (!font) {
+ fprintf(stderr, "%s: can't load font %s\n", progname, def_font);
+ abort();
+ }
+
+ text_pm=XCreatePixmap(sim->dpy, sim->window, 64*7, 8, 1);
+
+ memset(&gcv, 0, sizeof(gcv));
+ gcv.foreground=1;
+ gcv.background=0;
+ gcv.font=font->fid;
+ gc=XCreateGC(sim->dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
+
+ XSetForeground(sim->dpy, gc, 0);
+ XFillRectangle(sim->dpy, text_pm, gc, 0, 0, 64*7, 8);
+ XSetForeground(sim->dpy, gc, 1);
+ for (i=0; i<64; i++) {
+ char c=32+i;
+ int x=7*i+1;
+ int y=7;
+ if (c=='0') {
+ c='O';
+ XDrawString(sim->dpy, text_pm, gc, x, y, &c, 1);
+ } else {
+ XDrawString(sim->dpy, text_pm, gc, x, y, &c, 1);
+ }
+ }
+
+# if 0
+ for (i=0; a2_fixfont[i]; i++) {
+ XSetForeground (sim->dpy, gc, (a2_fixfont[i]>>15)&1);
+ XDrawPoint(sim->dpy, text_pm, gc, a2_fixfont[i]&0x3ff,
+ (a2_fixfont[i]>>10)&0xf);
+ }
+ XWriteBitmapFile(sim->dpy, "/tmp/a2font.xbm", text_pm, 64*7, 8, -1, -1);
+# endif
+
+ sim->text_im = XGetImage(sim->dpy, text_pm, 0, 0, 64*7, 8, ~0L, ZPixmap);
+ XFreeGC(sim->dpy, gc);
+ XFreePixmap(sim->dpy, text_pm);
+
+ for (i=0; a2_fixfont[i]; i++) {
+ XPutPixel(sim->text_im, a2_fixfont[i]&0x3ff,
+ (a2_fixfont[i]>>10)&0xf,
+ (a2_fixfont[i]>>15)&1);
+ }
+}
+
+#endif /* 0 */
+
+apple2_sim_t *
+apple2_start(Display *dpy, Window window, int delay,
+ void (*controller)(apple2_sim_t *sim,
+ int *stepno,
+ double *next_actiontime))
+{
+ apple2_sim_t *sim;
+
+ sim=(apple2_sim_t *)calloc(1,sizeof(apple2_sim_t));
+ sim->dpy = dpy;
+ sim->window = window;
+ sim->delay = delay;
+ sim->controller = controller;
+
+ sim->st = (apple2_state_t *)calloc(1,sizeof(apple2_state_t));
+ sim->dec = analogtv_allocate(dpy, window);
+ sim->inp = analogtv_input_allocate();
+
+ sim->reception.input = sim->inp;
+ sim->reception.level = 1.0;
+
+ sim->prompt=']';
+
+ if (random()%4==0 && !sim->dec->use_cmap && sim->dec->use_color && sim->dec->visbits>=8) {
+ sim->dec->flutter_tint=1;
+ }
+ else if (random()%3==0) {
+ sim->dec->flutter_horiz_desync=1;
+ }
+ sim->typing_rate = 1.0;
+
+ analogtv_set_defaults(sim->dec, "");
+ sim->dec->squish_control=0.05;
+ analogtv_setup_sync(sim->inp, 1, 0);
+
+
+ a2_make_font(sim);
+
+ sim->stepno=0;
+ a2_goto(sim->st,23,0);
+
+ if (random()%2==0) sim->basetime_tv.tv_sec -= 1; /* random blink phase */
+ sim->next_actiontime=0.0;
+
+ sim->curtime=0.0;
+ sim->next_actiontime=sim->curtime;
+ sim->controller (sim, &sim->stepno, &sim->next_actiontime);
+
+# ifdef GETTIMEOFDAY_TWO_ARGS
+ gettimeofday(&sim->basetime_tv, NULL);
+# else
+ gettimeofday(&sim->basetime_tv);
+# endif
+
+ return sim;
+}
+
+int
+apple2_one_frame (apple2_sim_t *sim)
+{
+ double blinkphase;
+ int i;
+ int textrow;
+
+ if (sim->stepno==A2CONTROLLER_DONE)
+ goto DONE; /* when caller says we're done, be done, dammit! */
+
+ {
+ struct timeval curtime_tv;
+# ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+ gettimeofday(&curtime_tv, &tzp);
+# else
+ gettimeofday(&curtime_tv);
+# endif
+ sim->curtime=(curtime_tv.tv_sec - sim->basetime_tv.tv_sec) +
+ 0.000001*(curtime_tv.tv_usec - sim->basetime_tv.tv_usec);
+ if (sim->curtime > sim->dec->powerup)
+ sim->dec->powerup=sim->curtime;
+ }
+
+ blinkphase=sim->curtime/0.8;
+
+ /* The blinking rate was controlled by 555 timer with a resistor/capacitor
+ time constant. Because the capacitor was electrolytic, the flash rate
+ varied somewhat between machines. I'm guessing 1.6 seconds/cycle was
+ reasonable. (I soldered a resistor in mine to make it blink faster.) */
+ i=sim->st->blink;
+ sim->st->blink=((int)blinkphase)&1;
+ if (sim->st->blink!=i && !(sim->st->gr_mode&A2_GR_FULL)) {
+ int downcounter=0;
+ /* For every row with blinking text, set the changed flag. This basically
+ works great except with random screen garbage in text mode, when we
+ end up redrawing the whole screen every second */
+ int row, col;
+ for (row=(sim->st->gr_mode ? 20 : 0); row<24; row++) {
+ for (col=0; col<40; col++) {
+ int c=sim->st->textlines[row][col];
+ if ((c & 0xc0) == 0x40) {
+ downcounter=4;
+ break;
+ }
+ }
+ if (downcounter>0) {
+ downcounter--;
+ }
+ }
+ }
+
+ if (sim->curtime >= sim->delay)
+ sim->stepno = A2CONTROLLER_DONE;
+
+ if (sim->printing) {
+ int nlcnt=0;
+ while (*sim->printing) {
+ if (*sim->printing=='\001') { /* pause */
+ sim->printing++;
+ break;
+ }
+ else if (*sim->printing=='\n') {
+ a2_printc(sim->st,*sim->printing);
+ sim->printing++;
+ nlcnt++;
+ if (nlcnt>=2) break;
+ }
+ else {
+ a2_printc(sim->st,*sim->printing);
+ sim->printing++;
+ }
+ }
+ if (!*sim->printing) sim->printing=NULL;
+ }
+ else if (sim->curtime >= sim->next_actiontime) {
+ if (sim->typing) {
+
+ int c;
+ /* If we're in the midst of typing a string, emit a character with
+ random timing. */
+ c =*sim->typing;
+ if (c==0) {
+ sim->typing=NULL;
+ }
+ else {
+ sim->typing++;
+ a2_printc(sim->st, c);
+ if (c=='\r' || c=='\n') {
+ sim->next_actiontime = sim->curtime;
+ }
+ else if (c==010) {
+ sim->next_actiontime = sim->curtime + 0.1;
+ }
+ else {
+ sim->next_actiontime = (sim->curtime +
+ (((random()%1000)*0.001 + 0.3) *
+ sim->typing_rate));
+ }
+ }
+ } else {
+ sim->next_actiontime = sim->curtime;
+
+ sim->controller (sim, &sim->stepno, &sim->next_actiontime);
+
+ if (sim->stepno==A2CONTROLLER_DONE) {
+
+ DONE:
+ sim->stepno=A2CONTROLLER_FREE;
+ sim->controller (sim, &sim->stepno, &sim->next_actiontime);
+ /* if stepno is changed, return 1 */
+ if (sim->stepno != A2CONTROLLER_FREE)
+ return 1;
+
+ XClearWindow(sim->dpy, sim->window);
+
+ /* free sim */
+ /* This is from a2_make_font */
+ free(sim->text_im->data);
+ sim->text_im->data = 0;
+ XDestroyImage(sim->text_im);
+
+ /* And free else */
+ analogtv_release(sim->dec);
+ free(sim->st);
+ free(sim->inp);
+ free(sim);
+
+ return 0;
+ }
+
+ }
+ }
+
+
+ analogtv_setup_sync(sim->inp, sim->st->gr_mode? 1 : 0, 0);
+ analogtv_setup_frame(sim->dec);
+
+ for (textrow=0; textrow<24; textrow++) {
+ int row;
+ for (row=textrow*8; row<textrow*8+8; row++) {
+
+ /* First we generate the pattern that the video circuitry shifts out
+ of memory. It has a 14.something MHz dot clock, equal to 4 times
+ the color burst frequency. So each group of 4 bits defines a color.
+ Each character position, or byte in hires, defines 14 dots, so odd
+ and even bytes have different color spaces. So, pattern[0..600]
+ gets the dots for one scan line. */
+
+ signed char *pp=&sim->inp->signal[row+ANALOGTV_TOP+4][ANALOGTV_PIC_START+100];
+
+ if ((sim->st->gr_mode&A2_GR_HIRES) &&
+ (row<160 || (sim->st->gr_mode&A2_GR_FULL))) {
+ int col;
+
+ /* Emulate the mysterious pink line, due to a bit getting
+ stuck in a shift register between the end of the last
+ row and the beginning of this one. */
+ if ((sim->st->hireslines[row][0] & 0x80) &&
+ (sim->st->hireslines[row][39]&0x40)) {
+ pp[-1]=ANALOGTV_WHITE_LEVEL;
+ }
+
+ for (col=0; col<40; col++) {
+ unsigned char b=sim->st->hireslines[row][col];
+ int shift=(b&0x80)?0:1;
+
+ /* Each of the low 7 bits in hires mode corresponded to 2 dot
+ clocks, shifted by one if the high bit was set. */
+ for (i=0; i<7; i++) {
+ pp[shift+1] = pp[shift] = (((b>>i)&1)
+ ?ANALOGTV_WHITE_LEVEL
+ :ANALOGTV_BLACK_LEVEL);
+ pp+=2;
+ }
+ }
+ }
+ else if ((sim->st->gr_mode&A2_GR_LORES) &&
+ (row<160 || (sim->st->gr_mode&A2_GR_FULL))) {
+ int col;
+ for (col=0; col<40; col++) {
+ unsigned char nib=((sim->st->textlines[textrow][col] >> (((row/4)&1)*4))
+ & 0xf);
+ /* The low or high nybble was shifted out one bit at a time. */
+ for (i=0; i<14; i++) {
+ *pp = (((nib>>((col*14+i)&3))&1)
+ ?ANALOGTV_WHITE_LEVEL
+ :ANALOGTV_BLACK_LEVEL);
+ pp++;
+ }
+ }
+ }
+ else {
+ int col;
+ for (col=0; col<40; col++) {
+ int rev;
+ int c=sim->st->textlines[textrow][col]&0xff;
+ /* hi bits control inverse/blink as follows:
+ 0x00: inverse
+ 0x40: blink
+ 0x80: normal
+ 0xc0: normal */
+ rev=!(c&0x80) && (!(c&0x40) || sim->st->blink);
+
+ for (i=0; i<7; i++) {
+ unsigned long pix=XGetPixel(sim->text_im,
+ ((c&0x3f)^0x20)*7+i,
+ row%8);
+ pp[1] = pp[2] = ((pix^rev)
+ ?ANALOGTV_WHITE_LEVEL
+ :ANALOGTV_BLACK_LEVEL);
+ pp+=2;
+ }
+ }
+ }
+ }
+ }
+ analogtv_reception_update(&sim->reception);
+ {
+ const analogtv_reception *rec = &sim->reception;
+ analogtv_draw(sim->dec, 0.02, &rec, 1);
+ }
+
+ return 1;
+}
+
+
+#if 0
+void
+a2controller_test(apple2_sim_t *sim, int *stepno, double *next_actiontime)
+{
+ int row,col;
+
+ switch(*stepno) {
+ case 0:
+ a2_invalidate(sim->st);
+ /*
+ For testing color rendering. The spec is:
+ red grn blu
+ 0 black 0 0 0
+ 1 red 227 30 96
+ 2 dk blue 96 78 189
+ 3 purple 255 68 253
+ 4 dk green 0 163 96
+ 5 gray 156 156 156
+ 6 med blue 20 207 253
+ 7 lt blue 208 195 255
+ 8 brown 96 114 3
+ 9 orange 255 106 60
+ 10 grey 156 156 156
+ 11 pink 255 160 208
+ 12 lt green 20 245 60
+ 13 yellow 208 221 141
+ 14 aqua 114 255 208
+ 15 white 255 255 255
+ */
+ sim->st->gr_mode=A2_GR_LORES;
+ for (row=0; row<24; row++) {
+ for (col=0; col<40; col++) {
+ sim->st->textlines[row][col]=(row&15)*17;
+ }
+ }
+ *next_actiontime+=0.4;
+ *stepno=99;
+ break;
+
+ case 99:
+ if (sim->curtime > 10) *stepno=-1;
+ break;
+ }
+}
+#endif