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