summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/plugins/chip8.c553
1 files changed, 553 insertions, 0 deletions
diff --git a/apps/plugins/chip8.c b/apps/plugins/chip8.c
new file mode 100644
index 0000000000..0be5e10436
--- /dev/null
+++ b/apps/plugins/chip8.c
@@ -0,0 +1,553 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Orginal from Vision-8 Emulator / Copyright (C) 1997-1999 Marcel de Kogel
11 * Modified for Archos by Blueloop (a.wenger@gmx.de)
12 *
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20#include "plugin.h"
21
22/* Only build for (correct) target */
23#ifndef SIMULATOR
24#ifdef HAVE_LCD_BITMAP
25
26static struct plugin_api* rb; /* here is a global api struct pointer */
27/* plugins have no framebuffer access, we need a copy */
28unsigned char lcd_framebuf[8][64];
29
30typedef unsigned char byte; /* sizeof(byte)==1 */
31typedef unsigned short word; /* sizeof(word)>=2 */
32
33struct chip8_regs_struct
34{
35 byte alg[16]; /* 16 general registers */
36 byte delay,sound; /* delay and sound timer */
37 word i; /* index register */
38 word pc; /* program counter */
39 word sp; /* stack pointer */
40};
41
42static struct chip8_regs_struct chip8_regs;
43
44#define chip8_iperiod 10 /* number of opcodes per */
45 /* timeslice (1/50sec.) */
46static byte chip8_key_pressed;
47static byte chip8_keys[16]; /* if 1, key is held down */
48static byte chip8_display[64*32]; /* 0xff if pixel is set, */
49 /* 0x00 otherwise */
50static byte chip8_mem[4096]; /* machine memory. program */
51 /* is loaded at 0x200 */
52
53#define read_mem(a) (chip8_mem[(a)&4095])
54#define write_mem(a,v) (chip8_mem[(a)&4095]=(v))
55
56static byte chip8_running; /* Flag for End-of-Emulation */
57
58#define get_reg_offset(opcode) (chip8_regs.alg+(opcode>>8))
59#define get_reg_value(opcode) (*get_reg_offset(opcode))
60#define get_reg_offset_2(opcode) (chip8_regs.alg+((opcode>>4)&0x0f))
61#define get_reg_value_2(opcode) (*get_reg_offset_2(opcode))
62
63typedef void (*opcode_fn) (word opcode);
64typedef void (*math_fn) (byte *reg1,byte reg2);
65
66/****************************************************************************/
67/* Turn sound on */
68/****************************************************************************/
69static void chip8_sound_on (void) { }
70
71/****************************************************************************/
72/* Turn sound off */
73/****************************************************************************/
74static void chip8_sound_off (void) { }
75
76static void op_call (word opcode)
77{
78 chip8_regs.sp--;
79 write_mem (chip8_regs.sp,chip8_regs.pc&0xff);
80 chip8_regs.sp--;
81 write_mem (chip8_regs.sp,chip8_regs.pc>>8);
82 chip8_regs.pc=opcode;
83}
84
85static void op_jmp (word opcode)
86{
87 chip8_regs.pc=opcode;
88}
89
90static void op_key (word opcode)
91{
92 byte key_value,cp_value;
93 if ((opcode&0xff)==0x9e)
94 cp_value=1;
95 else if ((opcode&0xff)==0xa1)
96 cp_value=0;
97 else
98 return;
99 key_value=chip8_keys[get_reg_value(opcode)&0x0f];
100 if (cp_value==key_value) chip8_regs.pc+=2;
101}
102
103static void op_skeq_const (word opcode)
104{
105 if (get_reg_value(opcode)==(opcode&0xff)) chip8_regs.pc+=2;
106}
107
108static void op_skne_const (word opcode)
109{
110 if (get_reg_value(opcode)!=(opcode&0xff)) chip8_regs.pc+=2;
111}
112
113static void op_skeq_reg (word opcode)
114{
115 if (get_reg_value(opcode)==get_reg_value_2(opcode)) chip8_regs.pc+=2;
116}
117
118static void op_skne_reg (word opcode)
119{
120 if (get_reg_value(opcode)!=get_reg_value_2(opcode)) chip8_regs.pc+=2;
121}
122
123static void op_mov_const (word opcode)
124{
125 *get_reg_offset(opcode)=opcode&0xff;
126}
127
128static void op_add_const (word opcode)
129{
130 *get_reg_offset(opcode)+=opcode&0xff;
131}
132
133static void op_mvi (word opcode)
134{
135 chip8_regs.i=opcode;
136}
137
138static void op_jmi (word opcode)
139{
140 chip8_regs.pc=opcode+chip8_regs.alg[0];
141}
142
143static void op_rand (word opcode)
144{
145 *get_reg_offset(opcode)=rb->rand()&(opcode&0xff);
146}
147
148static void math_or (byte *reg1,byte reg2)
149{
150 *reg1|=reg2;
151}
152
153static void math_mov (byte *reg1,byte reg2)
154{
155 *reg1=reg2;
156}
157
158static void math_nop (byte *reg1,byte reg2)
159{
160 (void)reg1;
161 (void)reg2;
162}
163
164static void math_and (byte *reg1,byte reg2)
165{
166 *reg1&=reg2;
167}
168
169static void math_xor (byte *reg1,byte reg2)
170{
171 *reg1^=reg2;
172}
173
174static void math_add (byte *reg1,byte reg2)
175{
176 word tmp;
177 tmp=*reg1+reg2;
178 *reg1=(byte)tmp;
179 chip8_regs.alg[15]=tmp>>8;
180}
181
182static void math_sub (byte *reg1,byte reg2)
183{
184 word tmp;
185 tmp=*reg1-reg2;
186 *reg1=(byte)tmp;
187 chip8_regs.alg[15]=((byte)(tmp>>8))+1;
188}
189
190static void math_shr (byte *reg1,byte reg2)
191{
192 (void)reg2;
193 chip8_regs.alg[15]=*reg1&1;
194 *reg1>>=1;
195}
196
197static void math_shl (byte *reg1,byte reg2)
198{
199 (void)reg2;
200 chip8_regs.alg[15]=*reg1>>7;
201 *reg1<<=1;
202}
203
204static void math_rsb (byte *reg1,byte reg2)
205{
206 word tmp;
207 tmp=reg2-*reg1;
208 *reg1=(byte)tmp;
209 chip8_regs.alg[15]=((byte)(tmp>>8))+1;
210}
211
212static void op_system (word opcode)
213{
214 switch ((byte)opcode)
215 {
216 case 0xe0:
217 rb->memset (chip8_display,0,sizeof(chip8_display));
218 break;
219 case 0xee:
220 chip8_regs.pc=read_mem(chip8_regs.sp)<<8;
221 chip8_regs.sp++;
222 chip8_regs.pc+=read_mem(chip8_regs.sp);
223 chip8_regs.sp++;
224 break;
225 }
226}
227
228static void op_misc (word opcode)
229{
230 byte *reg,i,j;
231 reg=get_reg_offset(opcode);
232 switch ((byte)opcode)
233 {
234 case 0x07:
235 *reg=chip8_regs.delay;
236 break;
237 case 0x0a:
238 if (chip8_key_pressed)
239 *reg=chip8_key_pressed-1;
240 else
241 chip8_regs.pc-=2;
242 break;
243 case 0x15:
244 chip8_regs.delay=*reg;
245 break;
246 case 0x18:
247 chip8_regs.sound=*reg;
248 if (chip8_regs.sound) chip8_sound_on();
249 break;
250 case 0x1e:
251 chip8_regs.i+=(*reg);
252 break;
253 case 0x29:
254 chip8_regs.i=((word)(*reg&0x0f))*5;
255 break;
256 case 0x33:
257 i=*reg;
258 for (j=0;i>=100;i-=100) j++;
259 write_mem (chip8_regs.i,j);
260 for (j=0;i>=10;i-=10) j++;
261 write_mem (chip8_regs.i+1,j);
262 write_mem (chip8_regs.i+2,i);
263 break;
264 case 0x55:
265 for (i=0,j=(opcode>>8)&0x0f;i<=j;++i)
266 write_mem(chip8_regs.i+i,chip8_regs.alg[i]);
267 break;
268 case 0x65:
269 for (i=0,j=(opcode>>8)&0x0f;i<=j;++i)
270 chip8_regs.alg[i]=read_mem(chip8_regs.i+i);
271 break;
272 }
273}
274
275static void op_sprite (word opcode)
276{
277 byte *q;
278 byte n,x,x2,y,collision;
279 word p;
280 x=get_reg_value(opcode)&63;
281 y=get_reg_value_2(opcode)&31;
282 p=chip8_regs.i;
283 q=chip8_display+y*64;
284 n=opcode&0x0f;
285 if (n+y>32) n=32-y;
286 for (collision=1;n;--n,q+=64)
287 {
288 for (y=read_mem(p++),x2=x;y;y<<=1,x2=(x2+1)&63)
289 if (y&0x80) collision&=(q[x2]^=0xff);
290 }
291 chip8_regs.alg[15]=collision^1;
292}
293
294static math_fn math_opcodes[16]=
295{
296 math_mov,
297 math_or,
298 math_and,
299 math_xor,
300 math_add,
301 math_sub,
302 math_shr,
303 math_rsb,
304 math_nop,
305 math_nop,
306 math_nop,
307 math_nop,
308 math_nop,
309 math_nop,
310 math_shl,
311 math_nop
312};
313
314static void op_math (word opcode)
315{
316 (*(math_opcodes[opcode&0x0f]))
317 (get_reg_offset(opcode),get_reg_value_2(opcode));
318}
319
320static opcode_fn main_opcodes[16]=
321{
322 op_system,
323 op_jmp,
324 op_call,
325 op_skeq_const,
326 op_skne_const,
327 op_skeq_reg,
328 op_mov_const,
329 op_add_const,
330 op_math,
331 op_skne_reg,
332 op_mvi,
333 op_jmi,
334 op_rand,
335 op_sprite,
336 op_key,
337 op_misc
338};
339
340/****************************************************************************/
341/* Update the display */
342/****************************************************************************/
343static void chip8_update_display(void)
344{
345 int x,y,i;
346 byte w;
347
348// lcd_clear_display();
349 for (y=0;y<=7;++y) /* 32 rows */
350 {
351 for (x=0;x<64;++x) /* 64 columns */
352 {
353 w = 0;
354 for (i=0;i<=3;i++)
355 {
356 w = w >> 2;
357 if (chip8_display[x+(y*4+i)*64] != 0)
358 {
359 w += 128+64;
360 }
361 lcd_framebuf[y][x] = w;
362 }
363 }
364 }
365 rb->lcd_bitmap(lcd_framebuf[0], 24, 0*8, 64, 8, true);
366 rb->lcd_bitmap(lcd_framebuf[1], 24, 1*8, 64, 8, true);
367 rb->lcd_bitmap(lcd_framebuf[2], 24, 2*8, 64, 8, true);
368 rb->lcd_bitmap(lcd_framebuf[3], 24, 3*8, 64, 8, true);
369 rb->lcd_bitmap(lcd_framebuf[4], 24, 4*8, 64, 8, true);
370 rb->lcd_bitmap(lcd_framebuf[5], 24, 5*8, 64, 8, true);
371 rb->lcd_bitmap(lcd_framebuf[6], 24, 6*8, 64, 8, true);
372 rb->lcd_bitmap(lcd_framebuf[7], 24, 7*8, 64, 8, true);
373 rb->lcd_update_rect(24,0,64,64);
374}
375
376
377static void chip8_keyboard(void)
378{
379 switch (rb->button_get(false))
380 {
381 case BUTTON_OFF: /* Abort Emulator */
382 chip8_running = false;
383 break;
384
385 case BUTTON_UP: chip8_keys[2] = 1; break;
386 case BUTTON_UP | BUTTON_REL: chip8_keys[2] = 0; break;
387 case BUTTON_LEFT: chip8_keys[4] = 1; break;
388 case BUTTON_LEFT | BUTTON_REL: chip8_keys[4] = 0; break;
389 case BUTTON_RIGHT: chip8_keys[6] = 1; break;
390 case BUTTON_RIGHT | BUTTON_REL: chip8_keys[6] = 0; break;
391 case BUTTON_DOWN: chip8_keys[8] = 1; break;
392 case BUTTON_DOWN | BUTTON_REL: chip8_keys[8] = 0; break;
393 case BUTTON_PLAY: chip8_keys[5] = 1; break;
394 case BUTTON_PLAY | BUTTON_REL: chip8_keys[5] = 0; break;
395 case BUTTON_F1: chip8_keys[1] = 1; break;
396 case BUTTON_F1 | BUTTON_REL: chip8_keys[1] = 0; break;
397 case BUTTON_F2: chip8_keys[7] = 1; break;
398 case BUTTON_F2 | BUTTON_REL: chip8_keys[7] = 0; break;
399 case BUTTON_F3: chip8_keys[3] = 1; break;
400 case BUTTON_F3 | BUTTON_REL: chip8_keys[3] = 0; break;
401 case BUTTON_ON: chip8_keys[9] = 1; break;
402 case BUTTON_ON | BUTTON_REL: chip8_keys[9] = 0; break;
403
404 case SYS_USB_CONNECTED:
405#ifdef HAVE_LCD_CHARCELLS
406 status_set_param(false);
407#endif
408 chip8_running = false;
409 break;
410 }
411}
412
413
414/****************************************************************************/
415/* Execute chip8_iperiod opcodes */
416/****************************************************************************/
417static void chip8_execute(void)
418{
419 byte i;
420 byte key_pressed=0;
421 word opcode;
422 for (i = chip8_iperiod ; i ;--i)
423 { /* Fetch the opcode */
424 opcode=(read_mem(chip8_regs.pc)<<8)+read_mem(chip8_regs.pc+1);
425
426 chip8_regs.pc+=2;
427 (*(main_opcodes[opcode>>12]))(opcode&0x0fff); /* Emulate this opcode */
428 }
429 /* Update timers */
430 if (chip8_regs.delay) --chip8_regs.delay;
431 if (chip8_regs.sound) --chip8_regs.sound; /* How could we make sound on the archos? */
432 /* Update the machine status */
433 chip8_update_display();
434 chip8_keyboard();
435 rb->sleep(HZ/70); /* ca. 70Hz */
436
437 for (i=key_pressed=0;i<16;++i) /* check if a key was first */
438 if (chip8_keys[i]) key_pressed=i+1; /* pressed */
439 if (key_pressed && key_pressed!=chip8_key_pressed)
440 chip8_key_pressed=key_pressed;
441 else
442 chip8_key_pressed=0;
443}
444
445/****************************************************************************/
446/* Reset the virtual chip8 machine */
447/****************************************************************************/
448static void chip8_reset(void)
449{
450 static byte chip8_sprites[16*5]=
451 {
452 0xf9,0x99,0xf2,0x62,0x27,
453 0xf1,0xf8,0xff,0x1f,0x1f,
454 0x99,0xf1,0x1f,0x8f,0x1f,
455 0xf8,0xf9,0xff,0x12,0x44,
456 0xf9,0xf9,0xff,0x9f,0x1f,
457 0xf9,0xf9,0x9e,0x9e,0x9e,
458 0xf8,0x88,0xfe,0x99,0x9e,
459 0xf8,0xf8,0xff,0x8f,0x88,
460 };
461 byte i;
462 for (i=0;i<16*5;++i)
463 {
464 write_mem (i<<1,chip8_sprites[i]&0xf0);
465 write_mem ((i<<1)+1,chip8_sprites[i]<<4);
466 }
467 rb->memset (chip8_regs.alg,0,sizeof(chip8_regs.alg));
468 rb->memset (chip8_keys,0,sizeof(chip8_keys));
469 chip8_key_pressed=0;
470 rb->memset (chip8_display,0,sizeof(chip8_display));
471 chip8_regs.delay=chip8_regs.sound=chip8_regs.i=0;
472 chip8_regs.sp=0x1e0;
473 chip8_regs.pc=0x200;
474 chip8_sound_off ();
475 chip8_running=1;
476}
477
478static bool chip8_init(char* file)
479{
480 int numread;
481 int fd;
482
483 fd = rb->open(file, O_RDONLY);
484 if (fd==-1) return false;
485 numread = rb->read(fd, chip8_mem+0x200, 4096-0x200);
486 if (numread==-1) return false;
487
488 rb->close(fd);
489 return true;
490}
491
492bool chip8_run(char* file)
493{
494 int ok;
495
496 ok = chip8_init(file);
497 if (!ok) {
498 rb->lcd_clear_display();
499 rb->lcd_puts(0, 0, "Error");
500 rb->lcd_update();
501 rb->sleep(HZ);
502 return false;
503 }
504 rb->lcd_clear_display();
505 rb->lcd_puts(0, 0, "Chip8 Emulator ");
506 rb->lcd_puts(0, 1, " (c) by ");
507 rb->lcd_puts(0, 2, "Marcel de Kogel");
508 rb->lcd_puts(0, 3, " Archos: ");
509 rb->lcd_puts(0, 4, " Blueloop ");
510 rb->lcd_puts(0, 5, "---------------");
511 rb->lcd_puts(0, 6, "File OK...");
512 rb->lcd_update();
513 rb->sleep(HZ*1);
514 rb->lcd_clear_display();
515 rb->lcd_drawrect(23,0,66,64);
516 rb->lcd_update();
517 chip8_reset();
518 while (chip8_running) chip8_execute();
519
520 return true;
521}
522
523
524/***************** Plugin Entry Point *****************/
525
526enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
527{
528 char* filename;
529
530 /* this macro should be called as the first thing you do in the plugin.
531 it test that the api version and model the plugin was compiled for
532 matches the machine it is running on */
533 TEST_PLUGIN_API(api);
534 rb = api; /* copy to global api pointer */
535
536 if (parameter == NULL)
537 {
538 rb->lcd_puts(0, 0, "Play .ch8 file!");
539 rb->lcd_update();
540 rb->sleep(HZ);
541 return PLUGIN_ERROR;
542 }
543 else
544 {
545 filename = (char*) parameter;
546 }
547
548 /* now go ahead and have fun! */
549 return chip8_run(filename) ? PLUGIN_OK : PLUGIN_ERROR;
550}
551
552#endif // #ifdef HAVE_LCD_BITMAP
553#endif // #ifndef SIMULATOR