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