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
a2_scroll(apple2_state_t * st)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
a2_printc_1(apple2_state_t * st,char c,int scroll_p)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
a2_printc(apple2_state_t * st,char c)131 a2_printc(apple2_state_t *st, char c)
132 {
133 a2_printc_1(st, c, 1);
134 }
135
136 void
a2_printc_noscroll(apple2_state_t * st,char c)137 a2_printc_noscroll(apple2_state_t *st, char c)
138 {
139 a2_printc_1(st, c, 0);
140 }
141
142
143 void
a2_prints(apple2_state_t * st,char * s)144 a2_prints(apple2_state_t *st, char *s)
145 {
146 while (*s) a2_printc(st, *s++);
147 }
148
149 void
a2_goto(apple2_state_t * st,int r,int c)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
a2_cls(apple2_state_t * st)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
a2_clear_gr(apple2_state_t * st)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
a2_clear_hgr(apple2_state_t * st)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
a2_invalidate(apple2_state_t * st)188 a2_invalidate(apple2_state_t *st)
189 {
190 }
191
192 void
a2_poke(apple2_state_t * st,int addr,int val)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
a2_hplot(apple2_state_t * st,int hcolor,int x,int y)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
a2_hline(apple2_state_t * st,int hcolor,int x1,int y1,int x2,int y2)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
a2_plot(apple2_state_t * st,int color,int x,int y)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
a2_display_image_loading(apple2_state_t * st,unsigned char * image,int lineno)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
a2_init_memory_active(apple2_sim_t * sim)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
a2_make_font(apple2_sim_t * sim)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
a2_make_font(apple2_sim_t * sim)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 *
apple2_start(Display * dpy,Window window,int delay,void (* controller)(apple2_sim_t * sim,int * stepno,double * next_actiontime))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
apple2_one_frame(apple2_sim_t * sim)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
886