From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / m6502.c
1 /* -*- indent-tabs-mode:nil -*-
2  * Copyright (c) 2007 Jeremy English <jhe@jeremyenglish.org>
3  * 
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  * 
12  * Created: 07-May-2007 
13  */
14
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18 #if defined(HAVE_STDINT_H)
19 #include <stdint.h> 
20 #elif defined(HAVE_INTTYPES_H)
21 #include <inttypes.h>
22 #endif
23 #include <string.h>
24 #include "screenhack.h"
25 #include "analogtv.h"
26 #include "asm6502.h"
27
28 # ifdef __GNUC__
29   __extension__  /* don't warn about "string length is greater than the length
30                     ISO C89 compilers are required to support" when includng
31                     the following data file... */
32 # endif
33 static const char * const demo_files[] = {
34 # include "m6502.h"
35 };
36
37
38 #ifndef HAVE_MOBILE
39 # define READ_FILES
40 #endif
41
42
43 /* We want to paint on a 32 by 32 grid of pixels. We will needed to
44    divided the screen up into chuncks */
45 enum {
46   SCREEN_W = ANALOGTV_VIS_LEN,
47   SCREEN_H = ANALOGTV_VISLINES,
48   NUM_PROGS = 9
49 };
50
51 struct state {
52   Display *dpy;
53   Window window;
54   
55   Bit8 pixels[32][32];
56
57   machine_6502 *machine;
58
59   analogtv *tv;
60   analogtv_input *inp;
61   analogtv_reception reception;
62   int pixw; /* pixel width */
63   int pixh;/* pixel height */
64   int topb;/* top boarder */
65   int field_ntsc[4];/* used for clearing the screen*/ 
66   double dt;/* how long to wait before changing the demo*/
67   int which;/* the program to run*/
68   int demos;/* number of demos included */
69   double start_time;
70   int reset_p;
71   char *file;
72   double last_frame, last_delay;
73   unsigned ips;
74 };
75
76 static void
77 plot6502(Bit8 x, Bit8 y, Bit8 color, void *closure)
78 {
79   struct state *st = (struct state *) closure;
80   st->pixels[x][y] = color;
81 }
82
83 #undef countof
84 #define countof(x) (sizeof((x))/sizeof((*x)))
85
86
87 static void 
88 start_rand_bin_prog(machine_6502 *machine, struct state *st){
89   int n = st->which;
90   while(n == st->which)
91     n = random() % st->demos;
92   st->which = n;
93   m6502_start_eval_string(machine, demo_files[st->which], plot6502, st);
94 }
95
96 \f
97 /*
98  * double_time ()
99  *
100  * returns the current time as a floating-point value
101  */
102 static double double_time(void) {
103   struct timeval t;
104   double f;
105 #if GETTIMEOFDAY_TWO_ARGS
106   gettimeofday(&t, NULL);
107 #else
108   gettimeofday(&t);
109 #endif
110   f = ((double)t.tv_sec) + t.tv_usec*1e-6;
111   return f;
112 }
113
114 static void *
115 m6502_init (Display *dpy, Window window)
116 {
117   struct state *st = (struct state *) calloc (1, sizeof(*st));
118   int n = get_float_resource(dpy, "displaytime", "Displaytime");
119   int dh;
120   st->demos = countof(demo_files);
121   st->which = random() % st->demos;
122   st->dt = n;
123   st->dpy = dpy;
124   st->window = window;
125   st->tv=analogtv_allocate(st->dpy, st->window);
126   analogtv_set_defaults(st->tv, "");
127   
128   st->machine = m6502_build();
129   st->inp=analogtv_input_allocate();
130   analogtv_setup_sync(st->inp, 1, 0);
131   
132   st->reception.input = st->inp;
133   st->reception.level = 2.0;
134   st->reception.ofs=0;
135   
136   st->reception.multipath=0.0;
137   st->pixw = SCREEN_W / 32;
138   st->pixh = SCREEN_H / 32;
139   dh = SCREEN_H % 32;
140   st->topb = dh / 2;
141
142   st->last_frame = double_time();
143   st->last_delay = 0;
144   st->ips = get_integer_resource(dpy, "ips", "IPS");
145
146 #ifdef READ_FILES
147   st->file = get_string_resource (dpy, "file", "File");
148 #endif
149
150   st->reset_p = 1;
151
152   analogtv_lcp_to_ntsc(ANALOGTV_BLACK_LEVEL, 0.0, 0.0, st->field_ntsc);
153
154   analogtv_draw_solid(st->inp,
155                       ANALOGTV_VIS_START, ANALOGTV_VIS_END,
156                       ANALOGTV_TOP, ANALOGTV_BOT,
157                       st->field_ntsc);
158
159   return st;
160 }
161
162 static void
163 paint_pixel(struct state *st, int x, int y, int idx)
164 {
165   double clr_tbl[16][3] = {
166     {  0,   0,   0},
167     {255, 255, 255},
168     {136,   0,   0},
169     {170, 255, 238},
170     {204,  68, 204},
171     {  0, 204,  85},
172     {  0,   0, 170},
173     {238, 238, 119},
174     {221, 136,  85},
175     {102,  68,   0},
176     {255, 119, 119},
177     { 51,  51,  51},
178     {119, 119, 119},
179     {170, 255, 102},
180     {  0, 136, 255},
181     {187, 187, 187}
182   };
183   int ntsc[4], i;
184   int rawy,rawi,rawq;
185   /* RGB conversion taken from analogtv draw xpm */
186   rawy=( 5*clr_tbl[idx][0] + 11*clr_tbl[idx][1] + 2*clr_tbl[idx][2]) / 64;
187   rawi=(10*clr_tbl[idx][0] -  4*clr_tbl[idx][1] - 5*clr_tbl[idx][2]) / 64;
188   rawq=( 3*clr_tbl[idx][0] -  8*clr_tbl[idx][1] + 5*clr_tbl[idx][2]) / 64;
189
190   ntsc[0]=rawy+rawq;
191   ntsc[1]=rawy-rawi;
192   ntsc[2]=rawy-rawq;
193   ntsc[3]=rawy+rawi;
194
195   for (i=0; i<4; i++) {
196     if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
197     if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
198   }
199
200       
201   x *= st->pixw;
202   y *= st->pixh;
203   y += st->topb;
204   analogtv_draw_solid(st->inp,
205                       ANALOGTV_VIS_START + x, ANALOGTV_VIS_START + x + st->pixw,
206                       ANALOGTV_TOP + y, ANALOGTV_TOP + y + st->pixh, ntsc);                           
207 }
208
209 static unsigned long
210 m6502_draw (Display *dpy, Window window, void *closure)
211 {
212   struct state *st = (struct state *) closure;
213   unsigned int x = 0, y = 0;
214   double now, last_delay = st->last_delay >= 0 ? st->last_delay : 0;
215   double insno = st->ips * ((1 / 29.97) + last_delay - st->last_delay);
216   const analogtv_reception *reception = &st->reception;
217
218   if (st->reset_p){ /* do something more interesting here XXX */
219     st->reset_p = 0;
220     for(x = 0; x < 32; x++)
221       for(y = 0; y < 32; y++)
222         st->pixels[x][y] = 0;
223     st->start_time = st->last_frame + last_delay;
224
225 #ifdef READ_FILES
226     if (st->file && *st->file)
227       m6502_start_eval_file(st->machine, st->file, plot6502, st);
228     else
229 #endif
230       start_rand_bin_prog(st->machine,st);
231   }
232
233   if (insno < 10)
234     insno = 10;
235   else if (insno > 100000) /* Real 6502 went no faster than 3 MHz. */
236     insno = 100000;
237   m6502_next_eval(st->machine,insno);
238
239   for (x = 0; x < 32; x++)
240     for (y = 0; y < 32; y++)
241       paint_pixel(st,x,y,st->pixels[x][y]);
242   
243   analogtv_reception_update(&st->reception);
244   analogtv_draw(st->tv, 0.04, &reception, 1);
245   now = double_time();
246   st->last_delay = (1 / 29.97) + st->last_frame + last_delay - now;
247   st->last_frame = now;
248
249   if (now - st->start_time > st->dt)
250     st->reset_p = 1;
251
252   return st->last_delay >= 0 ? st->last_delay * 1e6 : 0;
253 }
254
255
256 \f
257
258 static const char *m6502_defaults [] = {
259   ".background:      black",
260   ".foreground:      white",
261   "*file:",
262   "*displaytime:     30.0",     /* demoscene: 24s, dmsc: 48s, sierpinsky: 26s
263                                    sflake, two runs: 35s
264                                  */
265   "*ips:             15000",    /* Actual MOS 6502 ran at least 1 MHz. */
266   ANALOGTV_DEFAULTS
267   0
268 };
269
270 static XrmOptionDescRec m6502_options [] = {
271   { "-file",           ".file",     XrmoptionSepArg, 0 },
272   { "-displaytime",    ".displaytime", XrmoptionSepArg, 0},
273   { "-ips",            ".ips",         XrmoptionSepArg, 0},
274   ANALOGTV_OPTIONS
275   { 0, 0, 0, 0 }
276 };
277
278 static void
279 m6502_reshape (Display *dpy, Window window, void *closure, 
280                  unsigned int w, unsigned int h)
281 {
282   struct state *st = (struct state *) closure;
283   analogtv_reconfigure (st->tv);
284 }
285
286 static Bool
287 m6502_event (Display *dpy, Window window, void *closure, XEvent *event)
288 {
289   struct state *st = (struct state *) closure;
290   if (screenhack_event_helper (dpy, window, event))
291     {
292       st->reset_p = 1;
293       return True;
294     }
295   return False;
296 }
297
298 static void
299 m6502_free (Display *dpy, Window window, void *closure)
300 {
301   struct state *st = (struct state *) closure;
302   analogtv_release(st->tv);
303   free (st->file);
304   free (st);
305 }
306
307 XSCREENSAVER_MODULE ("m6502", m6502)