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