From http://www.jwz.org/xscreensaver/xscreensaver-5.39.tar.gz
[xscreensaver] / hacks / apple2.c
1 /* xscreensaver, Copyright (c) 1998-2010 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  *
11  * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org>
12  * with additional work by Jamie Zawinski <jwz@jwz.org>
13  */
14
15 #include <math.h>
16 #include "screenhackI.h"
17 #include "apple2.h"
18 #include "ximage-loader.h"
19
20 #ifdef HAVE_XSHM_EXTENSION
21 #include "xshm.h"
22 #endif
23
24 /*
25  * Implementation notes
26  *
27  * The A2 had 3 display modes: text, lores, and hires. Text was 40x24, and it
28  * disabled color in the TV. Lores gave you 40x48 graphics blocks, using the
29  * same memory as the text screen. Each could be one of 16 colors. Hires gave
30  * you 280x192 pixels. Odd pixels were blue or purple, and even pixels were
31  * orange or green depending on the setting of the high bit in each byte.
32  *
33  * The graphics modes could also have 4 lines of text at the bottom. This was
34  * fairly unreadable if you had a color monitor.
35  *
36  * Each mode had 2 different screens using different memory space. In hires
37  * mode this was sometimes used for double buffering, but more often the lower
38  * screen was full of code/data and the upper screen was used for display, so
39  * you got random garbage on the screen.
40  *
41  * The text font is based on X's standard 6x10 font, with a few tweaks like
42  * putting a slash across the zero.
43  *
44  * To use this, you'll call apple2(display, window, duration,
45  * controller) where the function controller defines what will happen.
46  * See bsod.c and apple2-main.c for example controllers. The
47  * controller function gets called whenever the machine ready to start
48  * something new. By setting sim->printing or sim->typing, it'll be
49  * busy for some time spitting characters out one at a time. By
50  * setting *next_actiontime+=X.X, it'll pause and just update the screen
51  * for that long before calling the controller function again.
52  *
53  * By setting stepno to A2CONTROLLER_DONE, the loop will end. It will also end
54  * after the time specified by the delay parameter. In either case, it calls
55  * the controller with stepno==A2CONTROLLER_FREE to allow it to release any
56  * memory.
57  *
58  * The void* apple2_sim_t::controller_data is for the use of the controller.
59  * It will be initialize to NULL, and the controller can store its own state
60  * there.
61  *
62  */
63
64 void
65 a2_scroll(apple2_state_t *st)
66 {
67   int i;
68   st->textlines[st->cursy][st->cursx] |= 0xc0;     /* turn off cursor */
69
70   for (i=0; i<23; i++) {
71     memcpy(st->textlines[i],st->textlines[i+1],40);
72   }
73   memset(st->textlines[23],0xe0,40);
74 }
75
76 static void
77 a2_printc_1(apple2_state_t *st, char c, int scroll_p)
78 {
79   st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
80
81   if (c == '\n')                      /* ^J == NL */
82     {
83       if (st->cursy==23)
84         {
85           if (scroll_p)
86             a2_scroll(st);
87         }
88       else
89         {
90           st->cursy++;
91         }
92       st->cursx=0;
93     }
94   else if (c == 014)                  /* ^L == CLS, Home */
95     {
96       a2_cls(st);
97       a2_goto(st,0,0);
98     }
99   else if (c == '\t')                 /* ^I == tab */
100     {
101       a2_goto(st, st->cursy, (st->cursx+8)&~7);
102     }
103   else if (c == 010)                  /* ^H == backspace */
104     {
105       st->textlines[st->cursy][st->cursx]=0xe0;
106       a2_goto(st, st->cursy, st->cursx-1);
107     }
108   else if (c == '\r')                 /* ^M == CR */
109     {
110       st->cursx=0;
111     }
112   else
113     {
114       st->textlines[st->cursy][st->cursx]=c ^ 0xc0;
115       st->cursx++;
116       if (st->cursx==40) {
117         if (st->cursy==23) {
118           if (scroll_p)
119             a2_scroll(st);
120         } else {
121           st->cursy++;
122         }
123         st->cursx=0;
124       }
125     }
126
127   st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
128 }
129
130 void
131 a2_printc(apple2_state_t *st, char c)
132 {
133   a2_printc_1(st, c, 1);
134 }
135
136 void
137 a2_printc_noscroll(apple2_state_t *st, char c)
138 {
139   a2_printc_1(st, c, 0);
140 }
141
142
143 void
144 a2_prints(apple2_state_t *st, char *s)
145 {
146   while (*s) a2_printc(st, *s++);
147 }
148
149 void
150 a2_goto(apple2_state_t *st, int r, int c)
151 {
152   if (r > 23) r = 23;
153   if (c > 39) c = 39;
154   st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
155   st->cursy=r;
156   st->cursx=c;
157   st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
158 }
159
160 void
161 a2_cls(apple2_state_t *st)
162 {
163   int i;
164   for (i=0; i<24; i++) {
165     memset(st->textlines[i],0xe0,40);
166   }
167 }
168
169 void
170 a2_clear_gr(apple2_state_t *st)
171 {
172   int i;
173   for (i=0; i<24; i++) {
174     memset(st->textlines[i],0x00,40);
175   }
176 }
177
178 void
179 a2_clear_hgr(apple2_state_t *st)
180 {
181   int i;
182   for (i=0; i<192; i++) {
183     memset(st->hireslines[i],0,40);
184   }
185 }
186
187 void
188 a2_invalidate(apple2_state_t *st)
189 {
190 }
191
192 void
193 a2_poke(apple2_state_t *st, int addr, int val)
194 {
195
196   if (addr>=0x400 && addr<0x800) {
197     /* text memory */
198     int row=((addr&0x380)/0x80) + ((addr&0x7f)/0x28)*8;
199     int col=(addr&0x7f)%0x28;
200     if (row<24 && col<40) {
201       st->textlines[row][col]=val;
202       if (!(st->gr_mode&(A2_GR_HIRES)) ||
203           (!(st->gr_mode&(A2_GR_FULL)) && row>=20)) {
204       }
205     }
206   }
207   else if (addr>=0x2000 && addr<0x4000) {
208     int row=(((addr&0x1c00) / 0x400) * 1 +
209              ((addr&0x0380) / 0x80) * 8 +
210              ((addr&0x0078) / 0x28) * 64);
211     int col=((addr&0x07f)%0x28);
212     if (row<192 && col<40) {
213       st->hireslines[row][col]=val;
214       if (st->gr_mode&A2_GR_HIRES) {
215       }
216     }
217   }
218 }
219
220 void
221 a2_hplot(apple2_state_t *st, int hcolor, int x, int y)
222 {
223   int highbit,run;
224
225   highbit=((hcolor<<5)&0x80) ^ 0x80; /* capture bit 2 into bit 7 */
226
227   if (y<0 || y>=192 || x<0 || x>=280) return;
228
229   for (run=0; run<2 && x<280; run++) {
230     unsigned char *vidbyte = &st->hireslines[y][x/7];
231     unsigned char whichbit=1<<(x%7);
232     int masked_bit;
233
234     *vidbyte = (*vidbyte & 0x7f) | highbit;
235
236     /* use either bit 0 or 1 of hcolor for odd or even pixels */
237     masked_bit = (hcolor>>(1-(x&1)))&1;
238
239     /* Set whichbit to 1 or 0 depending on color */
240     *vidbyte = (*vidbyte & ~whichbit) | (masked_bit ? whichbit : 0);
241
242     x++;
243   }
244 }
245
246 void
247 a2_hline(apple2_state_t *st, int hcolor, int x1, int y1, int x2, int y2)
248 {
249   int dx,dy,incx,incy,x,y,balance;
250
251   /* Bresenham's line drawing algorithm */
252
253   if (x2>=x1) {
254     dx=x2-x1;
255     incx=1;
256   } else {
257     dx=x1-x2;
258     incx=-1;
259   }
260   if (y2>=y1) {
261     dy=y2-y1;
262     incy=1;
263   } else {
264     dy=y1-y2;
265     incy=-1;
266   }
267
268   x=x1; y=y1;
269
270   if (dx>=dy) {
271     dy*=2;
272     balance=dy-dx;
273     dx*=2;
274     while (x!=x2) {
275       a2_hplot(st, hcolor, x, y);
276       if (balance>=0) {
277         y+=incy;
278         balance-=dx;
279       }
280       balance+=dy;
281       x+=incx;
282     }
283     a2_hplot(st, hcolor, x, y);
284   } else {
285     dx*=2;
286     balance=dx-dy;
287     dy*=2;
288     while (y!=y2) {
289       a2_hplot(st, hcolor, x, y);
290       if (balance>=0) {
291         x+=incx;
292         balance-=dy;
293       }
294       balance+=dx;
295       y+=incy;
296     }
297     a2_hplot(st, hcolor, x, y);
298   }
299 }
300
301 void
302 a2_plot(apple2_state_t *st, int color, int x, int y)
303 {
304   int textrow=y/2;
305   unsigned char byte;
306
307   if (x<0 || x>=40 || y<0 || y>=48) return;
308
309   byte=st->textlines[textrow][x];
310   if (y&1) {
311     byte = (byte&0xf0) | (color&0x0f);
312   } else {
313     byte = (byte&0x0f) | ((color&0x0f)<<4);
314   }
315   st->textlines[textrow][x]=byte;
316 }
317
318 void
319 a2_display_image_loading(apple2_state_t *st, unsigned char *image,
320                          int lineno)
321 {
322   /*
323     When loading images,it would normally just load the big binary
324     dump into screen memory while you watched. Because of the way
325     screen memory was laid out, it wouldn't load from the top down,
326     but in a funny interleaved way. You should call this with lineno
327     increasing from 0 thru 191 over a period of a few seconds.
328   */
329
330   int row=(((lineno / 24) % 8) * 1 +
331            ((lineno / 3 ) % 8) * 8 +
332            ((lineno / 1 ) % 3) * 64);
333
334   memcpy (st->hireslines[row], &image[row * 40], 40);
335 }
336
337 /*
338   Simulate plausible initial memory contents for running a program.
339 */
340 void
341 a2_init_memory_active(apple2_sim_t *sim)
342 {
343   int i,j,x,y,c;
344   int addr=0;
345   apple2_state_t *st=sim->st;
346
347   while (addr<0x4000) {
348     int n;
349
350     switch (random()%4) {
351     case 0:
352     case 1:
353       n=random()%500;
354       for (i=0; i<n && addr<0x4000; i++) {
355         unsigned char rb=((random()%6==0 ? 0 : random()%16) |
356                    ((random()%5==0 ? 0 : random()%16)<<4));
357         a2_poke(st, addr++, rb);
358       }
359       break;
360
361     case 2:
362       /* Simulate shapes stored in memory. We use the font since we have it.
363          Unreadable, since rows of each character are stored in consecutive
364          bytes. It was typical to store each of the 7 possible shifts of
365          bitmaps, for fastest blitting to the screen. */
366       x=random()%(sim->text_im->width);
367       for (i=0; i<100; i++) {
368         for (y=0; y<8; y++) {
369           c=0;
370           for (j=0; j<8; j++) {
371             c |= XGetPixel(sim->text_im, (x+j)%sim->text_im->width, y)<<j;
372           }
373           a2_poke(st, addr++, c);
374         }
375         x=(x+1)%(sim->text_im->width);
376       }
377       break;
378
379     case 3:
380       if (addr>0x2000) {
381         n=random()%200;
382         for (i=0; i<n && addr<0x4000; i++) {
383           a2_poke(st, addr++, 0);
384         }
385       }
386       break;
387
388     }
389   }
390 }
391
392
393 #if 1  /* jwz: since MacOS doesn't have "6x10", I dumped this font to a PNG...
394         */
395
396 #include "images/gen/apple2font_png.h"
397
398 static void
399 a2_make_font(apple2_sim_t *sim)
400 {
401   int pix_w, pix_h;
402   XWindowAttributes xgwa;
403   Pixmap m = 0;
404   Pixmap p = image_data_to_pixmap (sim->dpy, sim->window,
405                                    apple2font_png, sizeof(apple2font_png),
406                                    &pix_w, &pix_h, &m);
407   XImage *im = XGetImage (sim->dpy, p, 0, 0, pix_w, pix_h, ~0L, ZPixmap);
408   XImage *mm = XGetImage (sim->dpy, m, 0, 0, pix_w, pix_h, 1, XYPixmap);
409   unsigned long black =
410     BlackPixelOfScreen (DefaultScreenOfDisplay (sim->dpy));
411   int x, y;
412
413   XFreePixmap (sim->dpy, p);
414   XFreePixmap (sim->dpy, m);
415   if (pix_w != 64*7) abort();
416   if (pix_h != 8) abort();
417
418   XGetWindowAttributes (sim->dpy, sim->window, &xgwa);
419   sim->text_im = XCreateImage (sim->dpy, xgwa.visual, 1, XYBitmap, 0, 0,
420                                pix_w, pix_h, 8, 0);
421   sim->text_im->data = malloc (sim->text_im->bytes_per_line *
422                                sim->text_im->height);
423
424   /* Convert deep image to 1 bit */
425   for (y = 0; y < pix_h; y++)
426     for (x = 0; x < pix_w; x++)
427       XPutPixel (sim->text_im, x, y,
428                  (XGetPixel (mm, x, y)
429                   ? XGetPixel (im, x, y) == black
430                   : 0));
431
432   XDestroyImage (im);
433   XDestroyImage (mm);
434 }
435
436 #else /* 0 */
437
438 /* This table lists fixes for characters that differ from the standard 6x10
439    font. Each encodes a pixel, as (charindex*7 + x) + (y<<10) + (value<<15)
440    where value is 0 for white and 1 for black. */
441 static const unsigned short a2_fixfont[] = {
442   /* Fix $ */  0x8421, 0x941d,
443   /* Fix % */  0x8024, 0x0028, 0x8425, 0x0426, 0x0825, 0x1027, 0x1426, 0x9427,
444                0x1824, 0x9828,
445   /* Fix * */  0x8049, 0x8449, 0x8849, 0x0c47, 0x0c48, 0x0c4a, 0x0c4b, 0x9049,
446                0x9449, 0x9849,
447   /* Fix , */  0x9057, 0x1458, 0x9856, 0x1857, 0x1c56,
448   /* Fix . */  0x1465, 0x1864, 0x1866, 0x1c65,
449   /* Fix / */  0x006e, 0x186a,
450   /* Fix 0 */  0x8874, 0x8c73, 0x9072,
451   /* Fix 1 */  0x0878, 0x1878, 0x187c,
452   /* Fix 5 */  0x8895, 0x0c94, 0x0c95,
453   /* Fix 6 */  0x809f, 0x8c9c, 0x109c,
454   /* Fix 7 */  0x8ca4, 0x0ca5, 0x90a3, 0x10a4,
455   /* Fix 9 */  0x08b3, 0x8cb3, 0x98b0,
456   /* Fix : */  0x04b9, 0x08b8, 0x08ba, 0x0cb9, 0x90b9, 0x14b9, 0x18b8, 0x18b9,
457                0x18ba, 0x1cb9,
458   /* Fix ; */  0x04c0, 0x08bf, 0x08c1, 0x0cc0, 0x90c0, 0x14c1, 0x98bf, 0x18c0,
459                0x1cbf,
460   /* Fix < */  0x80c8, 0x00c9, 0x84c7, 0x04c8, 0x88c6, 0x08c7, 0x8cc5, 0x0cc6,
461                0x90c6, 0x10c7,
462                0x94c7, 0x14c8, 0x98c8, 0x18c9,
463   /* Fix > */  0x80d3, 0x00d4, 0x84d4, 0x04d5, 0x88d5, 0x08d6, 0x8cd6, 0x0cd7,
464                0x90d5, 0x10d6,
465                0x94d4, 0x14d5, 0x98d3, 0x18d4,
466   /* Fix @ */  0x88e3, 0x08e4, 0x8ce4, 0x98e5,
467   /* Fix B */  0x84ef, 0x04f0, 0x88ef, 0x08f0, 0x8cef, 0x90ef, 0x10f0, 0x94ef,
468                0x14f0,
469   /* Fix D */  0x84fd, 0x04fe, 0x88fd, 0x08fe, 0x8cfd, 0x0cfe, 0x90fd, 0x10fe,
470                0x94fd, 0x14fe,
471   /* Fix G */  0x8116, 0x0516, 0x9916,
472   /* Fix J */  0x0129, 0x012a, 0x052a, 0x852b, 0x092a, 0x892b, 0x0d2a, 0x8d2b,
473                0x112a, 0x912b,
474                0x152a, 0x952b, 0x992a,
475   /* Fix M */  0x853d, 0x853f, 0x093d, 0x893e, 0x093f,
476   /* Fix Q */  0x915a, 0x155a, 0x955b, 0x155c, 0x195b, 0x995c, 0x1d5c,
477   /* Fix V */  0x8d7b, 0x0d7c, 0x0d7e, 0x8d7f, 0x917b, 0x117c, 0x117e, 0x917f,
478   /* Fix [ */  0x819e, 0x81a2, 0x859e, 0x899e, 0x8d9e, 0x919e, 0x959e, 0x999e,
479                0x99a2,
480   /* Fix \ */  0x01a5, 0x19a9,
481   /* Fix ] */  0x81ac, 0x81b0, 0x85b0, 0x89b0, 0x8db0, 0x91b0, 0x95b0, 0x99ac,
482                0x99b0,
483   /* Fix ^ */  0x01b5, 0x05b4, 0x05b6, 0x09b3, 0x89b5, 0x09b7, 0x8db4, 0x8db6,
484                0x91b3, 0x91b7,
485   /* Fix _ */  0x9db9, 0x9dbf,
486   0,
487 };
488
489 static void
490 a2_make_font(apple2_sim_t *sim)
491 {
492   /*
493     Generate the font. It used a 5x7 font which looks a lot like the standard X
494     6x10 font, with a few differences. So we render up all the uppercase
495     letters of 6x10, and make a few tweaks (like putting a slash across the
496     zero) according to fixfont.
497    */
498
499   int i;
500   const char *def_font="6x10";
501   XFontStruct *font;
502   Pixmap text_pm;
503   GC gc;
504   XGCValues gcv;
505
506   font = load_font_retry (sim->dpy, def_font);
507   if (!font) {
508     fprintf(stderr, "%s: can't load font %s\n", progname, def_font);
509     abort();
510   }
511
512   text_pm=XCreatePixmap(sim->dpy, sim->window, 64*7, 8, 1);
513
514   memset(&gcv, 0, sizeof(gcv));
515   gcv.foreground=1;
516   gcv.background=0;
517   gcv.font=font->fid;
518   gc=XCreateGC(sim->dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
519
520   XSetForeground(sim->dpy, gc, 0);
521   XFillRectangle(sim->dpy, text_pm, gc, 0, 0, 64*7, 8);
522   XSetForeground(sim->dpy, gc, 1);
523   for (i=0; i<64; i++) {
524     char c=32+i;
525     int x=7*i+1;
526     int y=7;
527     if (c=='0') {
528       c='O';
529       XDrawString(sim->dpy, text_pm, gc, x, y, &c, 1);
530     } else {
531       XDrawString(sim->dpy, text_pm, gc, x, y, &c, 1);
532     }
533   }
534
535 # if 0
536   for (i=0; a2_fixfont[i]; i++) {
537     XSetForeground (sim->dpy, gc, (a2_fixfont[i]>>15)&1);
538     XDrawPoint(sim->dpy, text_pm, gc, a2_fixfont[i]&0x3ff,
539               (a2_fixfont[i]>>10)&0xf);
540   }
541   XWriteBitmapFile(sim->dpy, "/tmp/a2font.xbm", text_pm, 64*7, 8, -1, -1);
542 # endif
543
544   sim->text_im = XGetImage(sim->dpy, text_pm, 0, 0, 64*7, 8, ~0L, ZPixmap);
545   XFreeGC(sim->dpy, gc);
546   XFreePixmap(sim->dpy, text_pm);
547
548   for (i=0; a2_fixfont[i]; i++) {
549     XPutPixel(sim->text_im, a2_fixfont[i]&0x3ff,
550               (a2_fixfont[i]>>10)&0xf,
551               (a2_fixfont[i]>>15)&1);
552   }
553 }
554
555 #endif /* 0 */
556
557 apple2_sim_t *
558 apple2_start(Display *dpy, Window window, int delay,
559              void (*controller)(apple2_sim_t *sim,
560                                 int *stepno,
561                                 double *next_actiontime))
562 {
563   apple2_sim_t *sim;
564
565   sim=(apple2_sim_t *)calloc(1,sizeof(apple2_sim_t));
566   sim->dpy = dpy;
567   sim->window = window;
568   sim->delay = delay;
569   sim->controller = controller;
570
571   sim->st = (apple2_state_t *)calloc(1,sizeof(apple2_state_t));
572   sim->dec = analogtv_allocate(dpy, window);
573   sim->inp = analogtv_input_allocate();
574
575   sim->reception.input = sim->inp;
576   sim->reception.level = 1.0;
577
578   sim->prompt=']';
579
580   if (random()%4==0 && !sim->dec->use_cmap && sim->dec->use_color && sim->dec->visbits>=8) {
581     sim->dec->flutter_tint=1;
582   }
583   else if (random()%3==0) {
584     sim->dec->flutter_horiz_desync=1;
585   }
586   sim->typing_rate = 1.0;
587
588   analogtv_set_defaults(sim->dec, "");
589   sim->dec->squish_control=0.05;
590   analogtv_setup_sync(sim->inp, 1, 0);
591
592
593   a2_make_font(sim);
594
595   sim->stepno=0;
596   a2_goto(sim->st,23,0);
597
598   if (random()%2==0) sim->basetime_tv.tv_sec -= 1; /* random blink phase */
599   sim->next_actiontime=0.0;
600
601   sim->curtime=0.0;
602   sim->next_actiontime=sim->curtime;
603   sim->controller (sim, &sim->stepno, &sim->next_actiontime);
604
605 # ifdef GETTIMEOFDAY_TWO_ARGS
606   gettimeofday(&sim->basetime_tv, NULL);
607 # else
608   gettimeofday(&sim->basetime_tv);
609 # endif
610
611   return sim;
612 }
613
614 int
615 apple2_one_frame (apple2_sim_t *sim)
616 {
617     double blinkphase;
618     int i;
619     int textrow;
620
621     if (sim->stepno==A2CONTROLLER_DONE)
622       goto DONE;  /* when caller says we're done, be done, dammit! */
623
624     {
625       struct timeval curtime_tv;
626 # ifdef GETTIMEOFDAY_TWO_ARGS
627       struct timezone tzp;
628       gettimeofday(&curtime_tv, &tzp);
629 # else
630       gettimeofday(&curtime_tv);
631 # endif
632       sim->curtime=(curtime_tv.tv_sec - sim->basetime_tv.tv_sec) +
633         0.000001*(curtime_tv.tv_usec - sim->basetime_tv.tv_usec);
634       if (sim->curtime > sim->dec->powerup)
635         sim->dec->powerup=sim->curtime;
636     }
637
638     blinkphase=sim->curtime/0.8;
639
640     /* The blinking rate was controlled by 555 timer with a resistor/capacitor
641        time constant. Because the capacitor was electrolytic, the flash rate
642        varied somewhat between machines. I'm guessing 1.6 seconds/cycle was
643        reasonable. (I soldered a resistor in mine to make it blink faster.) */
644     i=sim->st->blink;
645     sim->st->blink=((int)blinkphase)&1;
646     if (sim->st->blink!=i && !(sim->st->gr_mode&A2_GR_FULL)) {
647       int downcounter=0;
648       /* For every row with blinking text, set the changed flag. This basically
649          works great except with random screen garbage in text mode, when we
650          end up redrawing the whole screen every second */
651       int row, col;
652       for (row=(sim->st->gr_mode ? 20 : 0); row<24; row++) {
653         for (col=0; col<40; col++) {
654           int c=sim->st->textlines[row][col];
655           if ((c & 0xc0) == 0x40) {
656             downcounter=4;
657             break;
658           }
659         }
660         if (downcounter>0) {
661           downcounter--;
662         }
663       }
664     }
665
666     if (sim->curtime >= sim->delay)
667       sim->stepno = A2CONTROLLER_DONE;
668
669     if (sim->printing) {
670       int nlcnt=0;
671       while (*sim->printing) {
672         if (*sim->printing=='\001') { /* pause */
673           sim->printing++;
674           break;
675         }
676         else if (*sim->printing=='\n') {
677           a2_printc(sim->st,*sim->printing);
678           sim->printing++;
679           nlcnt++;
680           if (nlcnt>=2) break;
681         }
682         else {
683           a2_printc(sim->st,*sim->printing);
684           sim->printing++;
685         }
686       }
687       if (!*sim->printing) sim->printing=NULL;
688     }
689     else if (sim->curtime >= sim->next_actiontime) {
690       if (sim->typing) {
691
692         int c;
693         /* If we're in the midst of typing a string, emit a character with
694            random timing. */
695         c =*sim->typing;
696         if (c==0) {
697           sim->typing=NULL;
698         }
699         else {
700           sim->typing++;
701           a2_printc(sim->st, c);
702           if (c=='\r' || c=='\n') {
703             sim->next_actiontime = sim->curtime;
704           }
705           else if (c==010) {
706             sim->next_actiontime = sim->curtime + 0.1;
707           }
708           else {
709             sim->next_actiontime = (sim->curtime +
710                                (((random()%1000)*0.001 + 0.3) *
711                                 sim->typing_rate));
712           }
713         }
714       } else {
715         sim->next_actiontime = sim->curtime;
716
717         sim->controller (sim, &sim->stepno, &sim->next_actiontime);
718
719         if (sim->stepno==A2CONTROLLER_DONE) {
720
721         DONE:
722           sim->stepno=A2CONTROLLER_FREE;
723           sim->controller (sim, &sim->stepno, &sim->next_actiontime);
724           /* if stepno is changed, return 1 */
725           if (sim->stepno != A2CONTROLLER_FREE)
726             return 1;
727
728           XClearWindow(sim->dpy, sim->window);
729
730           /* free sim */
731           /* This is from a2_make_font */
732           free(sim->text_im->data);
733           sim->text_im->data = 0;
734           XDestroyImage(sim->text_im);
735
736           /* And free else */
737           analogtv_release(sim->dec);
738           free(sim->st);
739           free(sim->inp);
740           free(sim);
741
742           return 0;
743         }
744
745       }
746     }
747
748
749     analogtv_setup_sync(sim->inp, sim->st->gr_mode? 1 : 0, 0);
750     analogtv_setup_frame(sim->dec);
751
752     for (textrow=0; textrow<24; textrow++) {
753       int row;
754       for (row=textrow*8; row<textrow*8+8; row++) {
755
756         /* First we generate the pattern that the video circuitry shifts out
757            of memory. It has a 14.something MHz dot clock, equal to 4 times
758            the color burst frequency. So each group of 4 bits defines a color.
759            Each character position, or byte in hires, defines 14 dots, so odd
760            and even bytes have different color spaces. So, pattern[0..600]
761            gets the dots for one scan line. */
762
763         signed char *pp=&sim->inp->signal[row+ANALOGTV_TOP+4][ANALOGTV_PIC_START+100];
764
765         if ((sim->st->gr_mode&A2_GR_HIRES) &&
766             (row<160 || (sim->st->gr_mode&A2_GR_FULL))) {
767           int col;
768
769           /* Emulate the mysterious pink line, due to a bit getting
770              stuck in a shift register between the end of the last
771              row and the beginning of this one. */
772           if ((sim->st->hireslines[row][0] & 0x80) &&
773               (sim->st->hireslines[row][39]&0x40)) {
774             pp[-1]=ANALOGTV_WHITE_LEVEL;
775           }
776
777           for (col=0; col<40; col++) {
778             unsigned char b=sim->st->hireslines[row][col];
779             int shift=(b&0x80)?0:1;
780
781             /* Each of the low 7 bits in hires mode corresponded to 2 dot
782                clocks, shifted by one if the high bit was set. */
783             for (i=0; i<7; i++) {
784               pp[shift+1] = pp[shift] = (((b>>i)&1)
785                                          ?ANALOGTV_WHITE_LEVEL
786                                          :ANALOGTV_BLACK_LEVEL);
787               pp+=2;
788             }
789           }
790         }
791         else if ((sim->st->gr_mode&A2_GR_LORES) &&
792                  (row<160 || (sim->st->gr_mode&A2_GR_FULL))) {
793           int col;
794           for (col=0; col<40; col++) {
795             unsigned char nib=((sim->st->textlines[textrow][col] >> (((row/4)&1)*4))
796                         & 0xf);
797             /* The low or high nybble was shifted out one bit at a time. */
798             for (i=0; i<14; i++) {
799               *pp = (((nib>>((col*14+i)&3))&1)
800                      ?ANALOGTV_WHITE_LEVEL
801                      :ANALOGTV_BLACK_LEVEL);
802               pp++;
803             }
804           }
805         }
806         else {
807           int col;
808           for (col=0; col<40; col++) {
809             int rev;
810             int c=sim->st->textlines[textrow][col]&0xff;
811             /* hi bits control inverse/blink as follows:
812                0x00: inverse
813                0x40: blink
814                0x80: normal
815                0xc0: normal */
816             rev=!(c&0x80) && (!(c&0x40) || sim->st->blink);
817
818             for (i=0; i<7; i++) {
819               unsigned long pix=XGetPixel(sim->text_im,
820                                           ((c&0x3f)^0x20)*7+i,
821                                           row%8);
822               pp[1] = pp[2] = ((pix^rev)
823                                ?ANALOGTV_WHITE_LEVEL
824                                :ANALOGTV_BLACK_LEVEL);
825               pp+=2;
826             }
827           }
828         }
829       }
830     }
831     analogtv_reception_update(&sim->reception);
832     {
833       const analogtv_reception *rec = &sim->reception;
834       analogtv_draw(sim->dec, 0.02, &rec, 1);
835     }
836  
837     return 1;
838 }
839
840
841 #if 0
842 void
843 a2controller_test(apple2_sim_t *sim, int *stepno, double *next_actiontime)
844 {
845   int row,col;
846
847   switch(*stepno) {
848   case 0:
849     a2_invalidate(sim->st);
850     /*
851       For testing color rendering. The spec is:
852       red grn blu
853       0  black       0   0   0
854       1  red       227  30  96
855       2  dk blue    96  78 189
856       3  purple    255  68 253
857       4  dk green    0 163  96
858       5  gray      156 156 156
859       6  med blue   20 207 253
860       7  lt blue   208 195 255
861       8  brown      96 114   3
862       9  orange    255 106  60
863       10 grey      156 156 156
864       11 pink      255 160 208
865       12 lt green   20 245  60
866       13 yellow    208 221 141
867       14 aqua      114 255 208
868       15 white     255 255 255
869     */
870     sim->st->gr_mode=A2_GR_LORES;
871     for (row=0; row<24; row++) {
872       for (col=0; col<40; col++) {
873         sim->st->textlines[row][col]=(row&15)*17;
874       }
875     }
876     *next_actiontime+=0.4;
877     *stepno=99;
878     break;
879
880   case 99:
881     if (sim->curtime > 10) *stepno=-1;
882     break;
883   }
884 }
885 #endif