diff options
Diffstat (limited to 'hacks/apple2.c')
-rw-r--r-- | hacks/apple2.c | 885 |
1 files changed, 885 insertions, 0 deletions
diff --git a/hacks/apple2.c b/hacks/apple2.c new file mode 100644 index 0000000..1918a5c --- /dev/null +++ b/hacks/apple2.c @@ -0,0 +1,885 @@ +/* xscreensaver, Copyright (c) 1998-2010 Jamie Zawinski <jwz@jwz.org> + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org> + * with additional work by Jamie Zawinski <jwz@jwz.org> + */ + +#include <math.h> +#include "screenhackI.h" +#include "apple2.h" +#include "ximage-loader.h" + +#ifdef HAVE_XSHM_EXTENSION +#include "xshm.h" +#endif + +/* + * Implementation notes + * + * The A2 had 3 display modes: text, lores, and hires. Text was 40x24, and it + * disabled color in the TV. Lores gave you 40x48 graphics blocks, using the + * same memory as the text screen. Each could be one of 16 colors. Hires gave + * you 280x192 pixels. Odd pixels were blue or purple, and even pixels were + * orange or green depending on the setting of the high bit in each byte. + * + * The graphics modes could also have 4 lines of text at the bottom. This was + * fairly unreadable if you had a color monitor. + * + * Each mode had 2 different screens using different memory space. In hires + * mode this was sometimes used for double buffering, but more often the lower + * screen was full of code/data and the upper screen was used for display, so + * you got random garbage on the screen. + * + * The text font is based on X's standard 6x10 font, with a few tweaks like + * putting a slash across the zero. + * + * To use this, you'll call apple2(display, window, duration, + * controller) where the function controller defines what will happen. + * See bsod.c and apple2-main.c for example controllers. The + * controller function gets called whenever the machine ready to start + * something new. By setting sim->printing or sim->typing, it'll be + * busy for some time spitting characters out one at a time. By + * setting *next_actiontime+=X.X, it'll pause and just update the screen + * for that long before calling the controller function again. + * + * By setting stepno to A2CONTROLLER_DONE, the loop will end. It will also end + * after the time specified by the delay parameter. In either case, it calls + * the controller with stepno==A2CONTROLLER_FREE to allow it to release any + * memory. + * + * The void* apple2_sim_t::controller_data is for the use of the controller. + * It will be initialize to NULL, and the controller can store its own state + * there. + * + */ + +void +a2_scroll(apple2_state_t *st) +{ + int i; + st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off cursor */ + + for (i=0; i<23; i++) { + memcpy(st->textlines[i],st->textlines[i+1],40); + } + memset(st->textlines[23],0xe0,40); +} + +static void +a2_printc_1(apple2_state_t *st, char c, int scroll_p) +{ + st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */ + + if (c == '\n') /* ^J == NL */ + { + if (st->cursy==23) + { + if (scroll_p) + a2_scroll(st); + } + else + { + st->cursy++; + } + st->cursx=0; + } + else if (c == 014) /* ^L == CLS, Home */ + { + a2_cls(st); + a2_goto(st,0,0); + } + else if (c == '\t') /* ^I == tab */ + { + a2_goto(st, st->cursy, (st->cursx+8)&~7); + } + else if (c == 010) /* ^H == backspace */ + { + st->textlines[st->cursy][st->cursx]=0xe0; + a2_goto(st, st->cursy, st->cursx-1); + } + else if (c == '\r') /* ^M == CR */ + { + st->cursx=0; + } + else + { + st->textlines[st->cursy][st->cursx]=c ^ 0xc0; + st->cursx++; + if (st->cursx==40) { + if (st->cursy==23) { + if (scroll_p) + a2_scroll(st); + } else { + st->cursy++; + } + st->cursx=0; + } + } + + st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */ +} + +void +a2_printc(apple2_state_t *st, char c) +{ + a2_printc_1(st, c, 1); +} + +void +a2_printc_noscroll(apple2_state_t *st, char c) +{ + a2_printc_1(st, c, 0); +} + + +void +a2_prints(apple2_state_t *st, char *s) +{ + while (*s) a2_printc(st, *s++); +} + +void +a2_goto(apple2_state_t *st, int r, int c) +{ + if (r > 23) r = 23; + if (c > 39) c = 39; + st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */ + st->cursy=r; + st->cursx=c; + st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */ +} + +void +a2_cls(apple2_state_t *st) +{ + int i; + for (i=0; i<24; i++) { + memset(st->textlines[i],0xe0,40); + } +} + +void +a2_clear_gr(apple2_state_t *st) +{ + int i; + for (i=0; i<24; i++) { + memset(st->textlines[i],0x00,40); + } +} + +void +a2_clear_hgr(apple2_state_t *st) +{ + int i; + for (i=0; i<192; i++) { + memset(st->hireslines[i],0,40); + } +} + +void +a2_invalidate(apple2_state_t *st) +{ +} + +void +a2_poke(apple2_state_t *st, int addr, int val) +{ + + if (addr>=0x400 && addr<0x800) { + /* text memory */ + int row=((addr&0x380)/0x80) + ((addr&0x7f)/0x28)*8; + int col=(addr&0x7f)%0x28; + if (row<24 && col<40) { + st->textlines[row][col]=val; + if (!(st->gr_mode&(A2_GR_HIRES)) || + (!(st->gr_mode&(A2_GR_FULL)) && row>=20)) { + } + } + } + else if (addr>=0x2000 && addr<0x4000) { + int row=(((addr&0x1c00) / 0x400) * 1 + + ((addr&0x0380) / 0x80) * 8 + + ((addr&0x0078) / 0x28) * 64); + int col=((addr&0x07f)%0x28); + if (row<192 && col<40) { + st->hireslines[row][col]=val; + if (st->gr_mode&A2_GR_HIRES) { + } + } + } +} + +void +a2_hplot(apple2_state_t *st, int hcolor, int x, int y) +{ + int highbit,run; + + highbit=((hcolor<<5)&0x80) ^ 0x80; /* capture bit 2 into bit 7 */ + + if (y<0 || y>=192 || x<0 || x>=280) return; + + for (run=0; run<2 && x<280; run++) { + unsigned char *vidbyte = &st->hireslines[y][x/7]; + unsigned char whichbit=1<<(x%7); + int masked_bit; + + *vidbyte = (*vidbyte & 0x7f) | highbit; + + /* use either bit 0 or 1 of hcolor for odd or even pixels */ + masked_bit = (hcolor>>(1-(x&1)))&1; + + /* Set whichbit to 1 or 0 depending on color */ + *vidbyte = (*vidbyte & ~whichbit) | (masked_bit ? whichbit : 0); + + x++; + } +} + +void +a2_hline(apple2_state_t *st, int hcolor, int x1, int y1, int x2, int y2) +{ + int dx,dy,incx,incy,x,y,balance; + + /* Bresenham's line drawing algorithm */ + + if (x2>=x1) { + dx=x2-x1; + incx=1; + } else { + dx=x1-x2; + incx=-1; + } + if (y2>=y1) { + dy=y2-y1; + incy=1; + } else { + dy=y1-y2; + incy=-1; + } + + x=x1; y=y1; + + if (dx>=dy) { + dy*=2; + balance=dy-dx; + dx*=2; + while (x!=x2) { + a2_hplot(st, hcolor, x, y); + if (balance>=0) { + y+=incy; + balance-=dx; + } + balance+=dy; + x+=incx; + } + a2_hplot(st, hcolor, x, y); + } else { + dx*=2; + balance=dx-dy; + dy*=2; + while (y!=y2) { + a2_hplot(st, hcolor, x, y); + if (balance>=0) { + x+=incx; + balance-=dy; + } + balance+=dx; + y+=incy; + } + a2_hplot(st, hcolor, x, y); + } +} + +void +a2_plot(apple2_state_t *st, int color, int x, int y) +{ + int textrow=y/2; + unsigned char byte; + + if (x<0 || x>=40 || y<0 || y>=48) return; + + byte=st->textlines[textrow][x]; + if (y&1) { + byte = (byte&0xf0) | (color&0x0f); + } else { + byte = (byte&0x0f) | ((color&0x0f)<<4); + } + st->textlines[textrow][x]=byte; +} + +void +a2_display_image_loading(apple2_state_t *st, unsigned char *image, + int lineno) +{ + /* + When loading images,it would normally just load the big binary + dump into screen memory while you watched. Because of the way + screen memory was laid out, it wouldn't load from the top down, + but in a funny interleaved way. You should call this with lineno + increasing from 0 thru 191 over a period of a few seconds. + */ + + int row=(((lineno / 24) % 8) * 1 + + ((lineno / 3 ) % 8) * 8 + + ((lineno / 1 ) % 3) * 64); + + memcpy (st->hireslines[row], &image[row * 40], 40); +} + +/* + Simulate plausible initial memory contents for running a program. +*/ +void +a2_init_memory_active(apple2_sim_t *sim) +{ + int i,j,x,y,c; + int addr=0; + apple2_state_t *st=sim->st; + + while (addr<0x4000) { + int n; + + switch (random()%4) { + case 0: + case 1: + n=random()%500; + for (i=0; i<n && addr<0x4000; i++) { + unsigned char rb=((random()%6==0 ? 0 : random()%16) | + ((random()%5==0 ? 0 : random()%16)<<4)); + a2_poke(st, addr++, rb); + } + break; + + case 2: + /* Simulate shapes stored in memory. We use the font since we have it. + Unreadable, since rows of each character are stored in consecutive + bytes. It was typical to store each of the 7 possible shifts of + bitmaps, for fastest blitting to the screen. */ + x=random()%(sim->text_im->width); + for (i=0; i<100; i++) { + for (y=0; y<8; y++) { + c=0; + for (j=0; j<8; j++) { + c |= XGetPixel(sim->text_im, (x+j)%sim->text_im->width, y)<<j; + } + a2_poke(st, addr++, c); + } + x=(x+1)%(sim->text_im->width); + } + break; + + case 3: + if (addr>0x2000) { + n=random()%200; + for (i=0; i<n && addr<0x4000; i++) { + a2_poke(st, addr++, 0); + } + } + break; + + } + } +} + + +#if 1 /* jwz: since MacOS doesn't have "6x10", I dumped this font to a PNG... + */ + +#include "images/gen/apple2font_png.h" + +static void +a2_make_font(apple2_sim_t *sim) +{ + int pix_w, pix_h; + XWindowAttributes xgwa; + Pixmap m = 0; + Pixmap p = image_data_to_pixmap (sim->dpy, sim->window, + apple2font_png, sizeof(apple2font_png), + &pix_w, &pix_h, &m); + XImage *im = XGetImage (sim->dpy, p, 0, 0, pix_w, pix_h, ~0L, ZPixmap); + XImage *mm = XGetImage (sim->dpy, m, 0, 0, pix_w, pix_h, 1, XYPixmap); + unsigned long black = + BlackPixelOfScreen (DefaultScreenOfDisplay (sim->dpy)); + int x, y; + + XFreePixmap (sim->dpy, p); + XFreePixmap (sim->dpy, m); + if (pix_w != 64*7) abort(); + if (pix_h != 8) abort(); + + XGetWindowAttributes (sim->dpy, sim->window, &xgwa); + sim->text_im = XCreateImage (sim->dpy, xgwa.visual, 1, XYBitmap, 0, 0, + pix_w, pix_h, 8, 0); + sim->text_im->data = malloc (sim->text_im->bytes_per_line * + sim->text_im->height); + + /* Convert deep image to 1 bit */ + for (y = 0; y < pix_h; y++) + for (x = 0; x < pix_w; x++) + XPutPixel (sim->text_im, x, y, + (XGetPixel (mm, x, y) + ? XGetPixel (im, x, y) == black + : 0)); + + XDestroyImage (im); + XDestroyImage (mm); +} + +#else /* 0 */ + +/* This table lists fixes for characters that differ from the standard 6x10 + font. Each encodes a pixel, as (charindex*7 + x) + (y<<10) + (value<<15) + where value is 0 for white and 1 for black. */ +static const unsigned short a2_fixfont[] = { + /* Fix $ */ 0x8421, 0x941d, + /* Fix % */ 0x8024, 0x0028, 0x8425, 0x0426, 0x0825, 0x1027, 0x1426, 0x9427, + 0x1824, 0x9828, + /* Fix * */ 0x8049, 0x8449, 0x8849, 0x0c47, 0x0c48, 0x0c4a, 0x0c4b, 0x9049, + 0x9449, 0x9849, + /* Fix , */ 0x9057, 0x1458, 0x9856, 0x1857, 0x1c56, + /* Fix . */ 0x1465, 0x1864, 0x1866, 0x1c65, + /* Fix / */ 0x006e, 0x186a, + /* Fix 0 */ 0x8874, 0x8c73, 0x9072, + /* Fix 1 */ 0x0878, 0x1878, 0x187c, + /* Fix 5 */ 0x8895, 0x0c94, 0x0c95, + /* Fix 6 */ 0x809f, 0x8c9c, 0x109c, + /* Fix 7 */ 0x8ca4, 0x0ca5, 0x90a3, 0x10a4, + /* Fix 9 */ 0x08b3, 0x8cb3, 0x98b0, + /* Fix : */ 0x04b9, 0x08b8, 0x08ba, 0x0cb9, 0x90b9, 0x14b9, 0x18b8, 0x18b9, + 0x18ba, 0x1cb9, + /* Fix ; */ 0x04c0, 0x08bf, 0x08c1, 0x0cc0, 0x90c0, 0x14c1, 0x98bf, 0x18c0, + 0x1cbf, + /* Fix < */ 0x80c8, 0x00c9, 0x84c7, 0x04c8, 0x88c6, 0x08c7, 0x8cc5, 0x0cc6, + 0x90c6, 0x10c7, + 0x94c7, 0x14c8, 0x98c8, 0x18c9, + /* Fix > */ 0x80d3, 0x00d4, 0x84d4, 0x04d5, 0x88d5, 0x08d6, 0x8cd6, 0x0cd7, + 0x90d5, 0x10d6, + 0x94d4, 0x14d5, 0x98d3, 0x18d4, + /* Fix @ */ 0x88e3, 0x08e4, 0x8ce4, 0x98e5, + /* Fix B */ 0x84ef, 0x04f0, 0x88ef, 0x08f0, 0x8cef, 0x90ef, 0x10f0, 0x94ef, + 0x14f0, + /* Fix D */ 0x84fd, 0x04fe, 0x88fd, 0x08fe, 0x8cfd, 0x0cfe, 0x90fd, 0x10fe, + 0x94fd, 0x14fe, + /* Fix G */ 0x8116, 0x0516, 0x9916, + /* Fix J */ 0x0129, 0x012a, 0x052a, 0x852b, 0x092a, 0x892b, 0x0d2a, 0x8d2b, + 0x112a, 0x912b, + 0x152a, 0x952b, 0x992a, + /* Fix M */ 0x853d, 0x853f, 0x093d, 0x893e, 0x093f, + /* Fix Q */ 0x915a, 0x155a, 0x955b, 0x155c, 0x195b, 0x995c, 0x1d5c, + /* Fix V */ 0x8d7b, 0x0d7c, 0x0d7e, 0x8d7f, 0x917b, 0x117c, 0x117e, 0x917f, + /* Fix [ */ 0x819e, 0x81a2, 0x859e, 0x899e, 0x8d9e, 0x919e, 0x959e, 0x999e, + 0x99a2, + /* Fix \ */ 0x01a5, 0x19a9, + /* Fix ] */ 0x81ac, 0x81b0, 0x85b0, 0x89b0, 0x8db0, 0x91b0, 0x95b0, 0x99ac, + 0x99b0, + /* Fix ^ */ 0x01b5, 0x05b4, 0x05b6, 0x09b3, 0x89b5, 0x09b7, 0x8db4, 0x8db6, + 0x91b3, 0x91b7, + /* Fix _ */ 0x9db9, 0x9dbf, + 0, +}; + +static void +a2_make_font(apple2_sim_t *sim) +{ + /* + Generate the font. It used a 5x7 font which looks a lot like the standard X + 6x10 font, with a few differences. So we render up all the uppercase + letters of 6x10, and make a few tweaks (like putting a slash across the + zero) according to fixfont. + */ + + int i; + const char *def_font="6x10"; + XFontStruct *font; + Pixmap text_pm; + GC gc; + XGCValues gcv; + + font = load_font_retry (sim->dpy, def_font); + if (!font) { + fprintf(stderr, "%s: can't load font %s\n", progname, def_font); + abort(); + } + + text_pm=XCreatePixmap(sim->dpy, sim->window, 64*7, 8, 1); + + memset(&gcv, 0, sizeof(gcv)); + gcv.foreground=1; + gcv.background=0; + gcv.font=font->fid; + gc=XCreateGC(sim->dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv); + + XSetForeground(sim->dpy, gc, 0); + XFillRectangle(sim->dpy, text_pm, gc, 0, 0, 64*7, 8); + XSetForeground(sim->dpy, gc, 1); + for (i=0; i<64; i++) { + char c=32+i; + int x=7*i+1; + int y=7; + if (c=='0') { + c='O'; + XDrawString(sim->dpy, text_pm, gc, x, y, &c, 1); + } else { + XDrawString(sim->dpy, text_pm, gc, x, y, &c, 1); + } + } + +# if 0 + for (i=0; a2_fixfont[i]; i++) { + XSetForeground (sim->dpy, gc, (a2_fixfont[i]>>15)&1); + XDrawPoint(sim->dpy, text_pm, gc, a2_fixfont[i]&0x3ff, + (a2_fixfont[i]>>10)&0xf); + } + XWriteBitmapFile(sim->dpy, "/tmp/a2font.xbm", text_pm, 64*7, 8, -1, -1); +# endif + + sim->text_im = XGetImage(sim->dpy, text_pm, 0, 0, 64*7, 8, ~0L, ZPixmap); + XFreeGC(sim->dpy, gc); + XFreePixmap(sim->dpy, text_pm); + + for (i=0; a2_fixfont[i]; i++) { + XPutPixel(sim->text_im, a2_fixfont[i]&0x3ff, + (a2_fixfont[i]>>10)&0xf, + (a2_fixfont[i]>>15)&1); + } +} + +#endif /* 0 */ + +apple2_sim_t * +apple2_start(Display *dpy, Window window, int delay, + void (*controller)(apple2_sim_t *sim, + int *stepno, + double *next_actiontime)) +{ + apple2_sim_t *sim; + + sim=(apple2_sim_t *)calloc(1,sizeof(apple2_sim_t)); + sim->dpy = dpy; + sim->window = window; + sim->delay = delay; + sim->controller = controller; + + sim->st = (apple2_state_t *)calloc(1,sizeof(apple2_state_t)); + sim->dec = analogtv_allocate(dpy, window); + sim->inp = analogtv_input_allocate(); + + sim->reception.input = sim->inp; + sim->reception.level = 1.0; + + sim->prompt=']'; + + if (random()%4==0 && !sim->dec->use_cmap && sim->dec->use_color && sim->dec->visbits>=8) { + sim->dec->flutter_tint=1; + } + else if (random()%3==0) { + sim->dec->flutter_horiz_desync=1; + } + sim->typing_rate = 1.0; + + analogtv_set_defaults(sim->dec, ""); + sim->dec->squish_control=0.05; + analogtv_setup_sync(sim->inp, 1, 0); + + + a2_make_font(sim); + + sim->stepno=0; + a2_goto(sim->st,23,0); + + if (random()%2==0) sim->basetime_tv.tv_sec -= 1; /* random blink phase */ + sim->next_actiontime=0.0; + + sim->curtime=0.0; + sim->next_actiontime=sim->curtime; + sim->controller (sim, &sim->stepno, &sim->next_actiontime); + +# ifdef GETTIMEOFDAY_TWO_ARGS + gettimeofday(&sim->basetime_tv, NULL); +# else + gettimeofday(&sim->basetime_tv); +# endif + + return sim; +} + +int +apple2_one_frame (apple2_sim_t *sim) +{ + double blinkphase; + int i; + int textrow; + + if (sim->stepno==A2CONTROLLER_DONE) + goto DONE; /* when caller says we're done, be done, dammit! */ + + { + struct timeval curtime_tv; +# ifdef GETTIMEOFDAY_TWO_ARGS + struct timezone tzp; + gettimeofday(&curtime_tv, &tzp); +# else + gettimeofday(&curtime_tv); +# endif + sim->curtime=(curtime_tv.tv_sec - sim->basetime_tv.tv_sec) + + 0.000001*(curtime_tv.tv_usec - sim->basetime_tv.tv_usec); + if (sim->curtime > sim->dec->powerup) + sim->dec->powerup=sim->curtime; + } + + blinkphase=sim->curtime/0.8; + + /* The blinking rate was controlled by 555 timer with a resistor/capacitor + time constant. Because the capacitor was electrolytic, the flash rate + varied somewhat between machines. I'm guessing 1.6 seconds/cycle was + reasonable. (I soldered a resistor in mine to make it blink faster.) */ + i=sim->st->blink; + sim->st->blink=((int)blinkphase)&1; + if (sim->st->blink!=i && !(sim->st->gr_mode&A2_GR_FULL)) { + int downcounter=0; + /* For every row with blinking text, set the changed flag. This basically + works great except with random screen garbage in text mode, when we + end up redrawing the whole screen every second */ + int row, col; + for (row=(sim->st->gr_mode ? 20 : 0); row<24; row++) { + for (col=0; col<40; col++) { + int c=sim->st->textlines[row][col]; + if ((c & 0xc0) == 0x40) { + downcounter=4; + break; + } + } + if (downcounter>0) { + downcounter--; + } + } + } + + if (sim->curtime >= sim->delay) + sim->stepno = A2CONTROLLER_DONE; + + if (sim->printing) { + int nlcnt=0; + while (*sim->printing) { + if (*sim->printing=='\001') { /* pause */ + sim->printing++; + break; + } + else if (*sim->printing=='\n') { + a2_printc(sim->st,*sim->printing); + sim->printing++; + nlcnt++; + if (nlcnt>=2) break; + } + else { + a2_printc(sim->st,*sim->printing); + sim->printing++; + } + } + if (!*sim->printing) sim->printing=NULL; + } + else if (sim->curtime >= sim->next_actiontime) { + if (sim->typing) { + + int c; + /* If we're in the midst of typing a string, emit a character with + random timing. */ + c =*sim->typing; + if (c==0) { + sim->typing=NULL; + } + else { + sim->typing++; + a2_printc(sim->st, c); + if (c=='\r' || c=='\n') { + sim->next_actiontime = sim->curtime; + } + else if (c==010) { + sim->next_actiontime = sim->curtime + 0.1; + } + else { + sim->next_actiontime = (sim->curtime + + (((random()%1000)*0.001 + 0.3) * + sim->typing_rate)); + } + } + } else { + sim->next_actiontime = sim->curtime; + + sim->controller (sim, &sim->stepno, &sim->next_actiontime); + + if (sim->stepno==A2CONTROLLER_DONE) { + + DONE: + sim->stepno=A2CONTROLLER_FREE; + sim->controller (sim, &sim->stepno, &sim->next_actiontime); + /* if stepno is changed, return 1 */ + if (sim->stepno != A2CONTROLLER_FREE) + return 1; + + XClearWindow(sim->dpy, sim->window); + + /* free sim */ + /* This is from a2_make_font */ + free(sim->text_im->data); + sim->text_im->data = 0; + XDestroyImage(sim->text_im); + + /* And free else */ + analogtv_release(sim->dec); + free(sim->st); + free(sim->inp); + free(sim); + + return 0; + } + + } + } + + + analogtv_setup_sync(sim->inp, sim->st->gr_mode? 1 : 0, 0); + analogtv_setup_frame(sim->dec); + + for (textrow=0; textrow<24; textrow++) { + int row; + for (row=textrow*8; row<textrow*8+8; row++) { + + /* First we generate the pattern that the video circuitry shifts out + of memory. It has a 14.something MHz dot clock, equal to 4 times + the color burst frequency. So each group of 4 bits defines a color. + Each character position, or byte in hires, defines 14 dots, so odd + and even bytes have different color spaces. So, pattern[0..600] + gets the dots for one scan line. */ + + signed char *pp=&sim->inp->signal[row+ANALOGTV_TOP+4][ANALOGTV_PIC_START+100]; + + if ((sim->st->gr_mode&A2_GR_HIRES) && + (row<160 || (sim->st->gr_mode&A2_GR_FULL))) { + int col; + + /* Emulate the mysterious pink line, due to a bit getting + stuck in a shift register between the end of the last + row and the beginning of this one. */ + if ((sim->st->hireslines[row][0] & 0x80) && + (sim->st->hireslines[row][39]&0x40)) { + pp[-1]=ANALOGTV_WHITE_LEVEL; + } + + for (col=0; col<40; col++) { + unsigned char b=sim->st->hireslines[row][col]; + int shift=(b&0x80)?0:1; + + /* Each of the low 7 bits in hires mode corresponded to 2 dot + clocks, shifted by one if the high bit was set. */ + for (i=0; i<7; i++) { + pp[shift+1] = pp[shift] = (((b>>i)&1) + ?ANALOGTV_WHITE_LEVEL + :ANALOGTV_BLACK_LEVEL); + pp+=2; + } + } + } + else if ((sim->st->gr_mode&A2_GR_LORES) && + (row<160 || (sim->st->gr_mode&A2_GR_FULL))) { + int col; + for (col=0; col<40; col++) { + unsigned char nib=((sim->st->textlines[textrow][col] >> (((row/4)&1)*4)) + & 0xf); + /* The low or high nybble was shifted out one bit at a time. */ + for (i=0; i<14; i++) { + *pp = (((nib>>((col*14+i)&3))&1) + ?ANALOGTV_WHITE_LEVEL + :ANALOGTV_BLACK_LEVEL); + pp++; + } + } + } + else { + int col; + for (col=0; col<40; col++) { + int rev; + int c=sim->st->textlines[textrow][col]&0xff; + /* hi bits control inverse/blink as follows: + 0x00: inverse + 0x40: blink + 0x80: normal + 0xc0: normal */ + rev=!(c&0x80) && (!(c&0x40) || sim->st->blink); + + for (i=0; i<7; i++) { + unsigned long pix=XGetPixel(sim->text_im, + ((c&0x3f)^0x20)*7+i, + row%8); + pp[1] = pp[2] = ((pix^rev) + ?ANALOGTV_WHITE_LEVEL + :ANALOGTV_BLACK_LEVEL); + pp+=2; + } + } + } + } + } + analogtv_reception_update(&sim->reception); + { + const analogtv_reception *rec = &sim->reception; + analogtv_draw(sim->dec, 0.02, &rec, 1); + } + + return 1; +} + + +#if 0 +void +a2controller_test(apple2_sim_t *sim, int *stepno, double *next_actiontime) +{ + int row,col; + + switch(*stepno) { + case 0: + a2_invalidate(sim->st); + /* + For testing color rendering. The spec is: + red grn blu + 0 black 0 0 0 + 1 red 227 30 96 + 2 dk blue 96 78 189 + 3 purple 255 68 253 + 4 dk green 0 163 96 + 5 gray 156 156 156 + 6 med blue 20 207 253 + 7 lt blue 208 195 255 + 8 brown 96 114 3 + 9 orange 255 106 60 + 10 grey 156 156 156 + 11 pink 255 160 208 + 12 lt green 20 245 60 + 13 yellow 208 221 141 + 14 aqua 114 255 208 + 15 white 255 255 255 + */ + sim->st->gr_mode=A2_GR_LORES; + for (row=0; row<24; row++) { + for (col=0; col<40; col++) { + sim->st->textlines[row][col]=(row&15)*17; + } + } + *next_actiontime+=0.4; + *stepno=99; + break; + + case 99: + if (sim->curtime > 10) *stepno=-1; + break; + } +} +#endif |