]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/c64/c64.c
blit,c64,gb*: fix phaser coolant leak during resize
[plan9front.git] / sys / src / games / c64 / c64.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <draw.h>
5 #include <mouse.h>
6 #include <keyboard.h>
7 #include "dat.h"
8 #include "fns.h"
9
10 char *bindir = "/sys/lib/c64";
11 Image *tmp, *bg, *red;
12 Rectangle picr, progr;
13 Mousectl *mc;
14 QLock pauselock;
15 int paused, scale;
16 u8int *rom;
17 int nrom;
18 u64int keys;
19 u16int joys;
20 uchar *tape, tapever, tapeplay;
21 ulong tapelen;
22 int joymode;
23
24 void
25 progress(int a, int b)
26 {
27         static int cur;
28         int w;
29         
30         if(b == 0 || a == 0){
31                 if(cur != 0){
32                         draw(screen, progr, bg, nil, ZP);
33                         cur = 0;
34                 }
35                 return;
36         }
37         w = a * Dx(progr) / b;
38         if(cur == w)
39                 return;
40         draw(screen, Rect(progr.min.x, progr.min.y, progr.min.x + w, progr.max.y), red, nil, ZP);
41         cur = w;
42 }
43
44 static void
45 loadsys(char *name, u8int *p, int n)
46 {
47         static char buf[256];
48         int fd;
49
50         snprint(buf, sizeof(buf), "%s/%s", bindir, name);
51         fd = open(buf, OREAD);
52         if(fd < 0)
53                 sysfatal("open: %r");
54         if(readn(fd, p, n) < n)
55                 sysfatal("readn: %r");
56         close(fd);
57 }
58
59 static void
60 loadrom(char *name)
61 {
62         int fd;
63         
64         fd = open(name, OREAD);
65         if(fd < 0)
66                 sysfatal("open: %r");
67         nrom = seek(fd, 0, 2);
68         if(nrom > 4096)
69                 sysfatal("large ROM not supported");
70         if((nrom & nrom-1) != 0)
71                 sysfatal("non-power-of-two ROM size");
72         rom = malloc(nrom);
73         if(rom == nil)
74                 sysfatal("malloc: %r");
75         pread(fd, rom, nrom, 0);
76         close(fd);
77 }
78
79 static void
80 loadcart(char *name)
81 {
82         int fd;
83         u16int t, l;
84         u8int buf[80];
85
86         fd = open(name, OREAD);
87         if(fd < 0)
88                 sysfatal("open: %r");
89         read(fd, buf, 80);
90         if(memcmp(buf, "C64 CARTRIDGE   ", 16) != 0)
91                 sysfatal("not a c64 cartridge");
92         t = buf[0x16] << 8 | buf[0x17];
93         if(t != 0)
94                 sysfatal("unsupported type %d", t);
95         if(buf[0x18] == 0) pla &= ~EXROM;
96         if(buf[0x19] == 0) pla &= ~GAME;
97         t = buf[0x4c] << 8 | buf[0x4d];
98         if(t < 0x8000 || t >= 0xc000 && t < 0xe000)
99                 sysfatal("odd starting address %x", t);
100         if(t >= 0xe000)
101                 t -= 0x4000;
102         t -= 0x8000;
103         l = buf[0x4e] << 8 | buf[0x4f];
104         if(l + t > 16384)
105                 sysfatal("cart too large");
106         read(fd, cart + t, l);
107         close(fd);
108 }
109
110 static void
111 loadtape(char *name)
112 {
113         int fd;
114         uchar buf[20];
115         
116         fd = open(name, OREAD);
117         if(fd < 0)
118                 sysfatal("open: %r");
119         read(fd, buf, 20);
120         if(memcmp(buf, "C64-TAPE-RAW", 12) != 0)
121                 sysfatal("not a c64 raw tape");
122         tapever = buf[12];
123         if(tapever > 1)
124                 sysfatal("unsupported tape version %d", tapever);
125         tapelen = buf[16] | buf[17] << 8 | buf[18] << 16 | buf[19] << 24;
126         tape = malloc(tapelen);
127         readn(fd, tape, tapelen);
128         close(fd);
129 }
130
131 static void
132 keyproc(void *)
133 {
134         int fd, i, n, setnmi;
135         u16int j;
136         u64int k;
137         static Rune keymap[64] = {
138                 Kbs, '\n', Kleft, KF|7, KF|1, KF|3, KF|5, Kup,
139                 '3', 'w', 'a', '4', 'z', 's', 'e', Kshift,
140                 '5', 'r', 'd', '6', 'c', 'f', 't', 'x',
141                 '7', 'y', 'g', '8', 'b', 'h', 'u', 'v',
142                 '9', 'i', 'j', '0', 'm', 'k', 'o', 'n',
143                 '\'', 'p', 'l', '-', '.', '\\', '@', ',',
144                 '[', '*', ';', Khome, Kalt, '=', ']', '/',
145                 '1', Kins, '\t', '2', ' ', Kctl, 'q', Kdel
146         };
147         static char buf[256];
148         char *s;
149         Rune r;
150
151         fd = open("/dev/kbd", OREAD);
152         if(fd < 0)
153                 sysfatal("open: %r");
154         for(;;){
155                 if(buf[0] != 0){
156                         n = strlen(buf)+1;
157                         memmove(buf, buf+n, sizeof(buf)-n);
158                 }
159                 if(buf[0] == 0){
160                         n = read(fd, buf, sizeof(buf)-1);
161                         if(n <= 0)
162                                 sysfatal("read /dev/kbd: %r");
163                         buf[n-1] = 0;
164                         buf[n] = 0;
165                 }
166                 if(buf[0] == 'c'){
167                         if(utfrune(buf, Kend)){
168                                 close(fd);
169                                 threadexitsall(nil);
170                         }
171                         if(utfrune(buf, KF|12))
172                                 trace ^= 1;
173                 }
174                 if(buf[0] != 'k' && buf[0] != 'K')
175                         continue;
176                 s = buf + 1;
177                 j = 0;
178                 k = 0;
179                 setnmi = 0;
180                 while(*s != 0){
181                         s += chartorune(&r, s);
182                         switch(r){
183                         case Kend: close(fd); threadexitsall(nil);
184                         case Kesc:
185                                 if(paused)
186                                         qunlock(&pauselock);
187                                 else
188                                         qlock(&pauselock);
189                                 paused = !paused;
190                                 break;
191                         case '`':
192                                 setnmi = 1;
193                                 break;
194                         case Kleft: if(joymode) j |= 1<<2+5*(joymode-1); break;
195                         case Kright: if(joymode) j |= 1<<3+5*(joymode-1); break;
196                         case Kup: if(joymode) j |= 1<<0+5*(joymode-1); break;
197                         case Kdown: if(joymode) j |= 1<<1+5*(joymode-1); break;
198                         case Kctl: if(joymode) j |= 1<<4+5*(joymode-1); break;
199                         }
200                         for(i = 0; i < 64; i++)
201                                 if(keymap[i] == r)
202                                         k |= 1ULL<<i;
203                 }
204                 if(setnmi)
205                         nmi |= IRQRESTORE;
206                 else
207                         nmi &= ~IRQRESTORE;
208                 keys = k;
209                 joys = j;
210         }
211
212 }
213
214 static void
215 screeninit(void)
216 {
217         Point p, q;
218
219         p = divpt(addpt(screen->r.min, screen->r.max), 2);
220         picr = (Rectangle){subpt(p, Pt(picw/2*scale, pich/2*scale)), addpt(p, Pt(picw/2*scale, pich/2*scale))};
221         p.y += pich*scale*3/4;
222         q = Pt(Dx(screen->r) * 2/5, 8);
223         progr = (Rectangle){subpt(p, q), addpt(p, q)};
224         freeimage(tmp);
225         tmp = allocimage(display, Rect(0, 0, picw*scale, scale > 1 ? 1 : pich), XRGB32, 1, 0);
226         draw(screen, screen->r, bg, nil, ZP);
227 }
228
229 static void
230 usage(void)
231 {
232         fprint(2, "usage: %s [ -23a ] [ rom ]\n", argv0);
233         exits("usage");
234 }
235
236 void
237 threadmain(int argc, char **argv)
238 {
239         scale = 1;
240
241         memreset();
242
243         ARGBEGIN {
244         case '2':
245                 scale = 2;
246                 break;
247         case '3':
248                 scale = 3;
249                 break;
250         case 'c':
251                 loadcart(EARGF(usage()));
252                 break;
253         case 't':
254                 loadtape(EARGF(usage()));
255                 break;
256         case 'N':
257                 region = NTSC0;
258                 break;
259         case 'p':
260                 region = PAL;
261                 break;
262         case 'd':
263                 bindir = strdup(EARGF(usage()));
264                 break;
265         default:
266                 usage();
267         } ARGEND;
268         if(argc >= 2)
269                 usage();
270         loadsys("kernal.bin", krom, 8192);
271         loadsys("basic.bin", brom, 8192);
272         loadsys("crom.bin", crom, 4096);
273         
274         vicreset();
275         
276         if(initdraw(nil, nil, nil) < 0)
277                 sysfatal("initdraw: %r");
278         mc = initmouse(nil, screen);
279         if(mc == nil)
280                 sysfatal("initmouse: %r");
281         bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
282         red = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xFF0000FF);
283         screeninit();
284         proccreate(keyproc, nil, mainstacksize);
285
286         nmien = IRQRESTORE;
287         pc = memread(0xFFFC) | memread(0xFFFD) << 8;
288         rP = FLAGI;
289         for(;;){
290                 if(paused){
291                                 qlock(&pauselock);
292                                 qunlock(&pauselock);
293                         }
294                 step();
295         }
296 }
297
298 static void
299 menu(void)
300 {
301         enum { JOY, TAPE };
302         static char joystr[32] = "joy: none";
303         static char tapestr[32] = "tape: play";
304         static char *items[] = {
305                 [JOY] joystr,
306                 [TAPE] tapestr,
307                 nil
308         };
309         static Menu m = {
310                 items, nil, 0
311         };
312         
313         switch(menuhit(3, mc, &m, nil)){
314         case JOY:
315                 joymode = (joymode + 1) % 3;
316                 if(joymode == 0)
317                         strcpy(joystr, "joy: none");
318                 else
319                         sprint(joystr, "joy: %d", joymode);
320                 break;
321         case TAPE:
322                 tapeplay ^= 1;
323                 if(tapeplay == 0){
324                         strcpy(tapestr, "tape: play");
325                         progress(0, 0);
326                 }else
327                         strcpy(tapestr, "tape: stop");
328                 break;
329         }
330 }
331
332 void
333 flush(void)
334 {
335         extern u8int pic[];
336 //      vlong new, diff;
337 //      static vlong old, delta;
338
339         if(nbrecvul(mc->resizec) > 0){
340                 if(getwindow(display, Refnone) < 0)
341                         sysfatal("resize failed: %r");
342                 screeninit();
343         }
344         while(nbrecv(mc->c, &mc->Mouse) > 0)
345                 if((mc->buttons & 4) != 0)
346                         menu();
347         if(scale == 1){
348                 loadimage(tmp, tmp->r, pic, picw*pich*4);
349                 draw(screen, picr, tmp, nil, ZP);
350         }else{
351                 Rectangle r;
352                 uchar *s;
353                 int w;
354
355                 s = pic;
356                 r = picr;
357                 w = picw*4*scale;
358                 while(r.min.y < picr.max.y){
359                         loadimage(tmp, tmp->r, s, w);
360                         s += w;
361                         r.max.y = r.min.y+scale;
362                         draw(screen, r, tmp, nil, ZP);
363                         r.min.y = r.max.y;
364                 }
365         }
366         flushimage(display, 1);
367 /*
368         if(audioout() < 0){
369                 new = nsec();
370                 diff = 0;
371                 if(old != 0){
372                         diff = BILLION/60 - (new - old) - delta;
373                         if(diff >= MILLION)
374                                 sleep(diff/MILLION);
375                 }
376                 old = nsec();
377                 if(diff != 0){
378                         diff = (old - new) - (diff / MILLION) * MILLION;
379                         delta += (diff - delta) / 100;
380                 }
381         }
382 */
383 }