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