summaryrefslogtreecommitdiff
path: root/apps/plugins/chip8.c
diff options
context:
space:
mode:
authorJörg Hohensohn <hohensoh@rockbox.org>2003-10-30 23:47:34 +0000
committerJörg Hohensohn <hohensoh@rockbox.org>2003-10-30 23:47:34 +0000
commit0860a4d6d17b86772b388c69d42c49d33e3a0898 (patch)
tree7139cb267daf1489c3044a0a9703993c95bbaf77 /apps/plugins/chip8.c
parentd8b31814c710324b554d445b610ea26cd5d12fa4 (diff)
from request #628509: ported the Chip-8 emulator as a plugin
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@3996 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/chip8.c')
-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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Orginal from Vision-8 Emulator / Copyright (C) 1997-1999 Marcel de Kogel
+ * Modified for Archos by Blueloop (a.wenger@gmx.de)
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+
+/* Only build for (correct) target */
+#ifndef SIMULATOR
+#ifdef HAVE_LCD_BITMAP
+
+static struct plugin_api* rb; /* here is a global api struct pointer */
+/* plugins have no framebuffer access, we need a copy */
+unsigned char lcd_framebuf[8][64];
+
+typedef unsigned char byte; /* sizeof(byte)==1 */
+typedef unsigned short word; /* sizeof(word)>=2 */
+
+struct chip8_regs_struct
+{
+ byte alg[16]; /* 16 general registers */
+ byte delay,sound; /* delay and sound timer */
+ word i; /* index register */
+ word pc; /* program counter */
+ word sp; /* stack pointer */
+};
+
+static struct chip8_regs_struct chip8_regs;
+
+#define chip8_iperiod 10 /* number of opcodes per */
+ /* timeslice (1/50sec.) */
+static byte chip8_key_pressed;
+static byte chip8_keys[16]; /* if 1, key is held down */
+static byte chip8_display[64*32]; /* 0xff if pixel is set, */
+ /* 0x00 otherwise */
+static byte chip8_mem[4096]; /* machine memory. program */
+ /* is loaded at 0x200 */
+
+#define read_mem(a) (chip8_mem[(a)&4095])
+#define write_mem(a,v) (chip8_mem[(a)&4095]=(v))
+
+static byte chip8_running; /* Flag for End-of-Emulation */
+
+#define get_reg_offset(opcode) (chip8_regs.alg+(opcode>>8))
+#define get_reg_value(opcode) (*get_reg_offset(opcode))
+#define get_reg_offset_2(opcode) (chip8_regs.alg+((opcode>>4)&0x0f))
+#define get_reg_value_2(opcode) (*get_reg_offset_2(opcode))
+
+typedef void (*opcode_fn) (word opcode);
+typedef void (*math_fn) (byte *reg1,byte reg2);
+
+/****************************************************************************/
+/* Turn sound on */
+/****************************************************************************/
+static void chip8_sound_on (void) { }
+
+/****************************************************************************/
+/* Turn sound off */
+/****************************************************************************/
+static void chip8_sound_off (void) { }
+
+static void op_call (word opcode)
+{
+ chip8_regs.sp--;
+ write_mem (chip8_regs.sp,chip8_regs.pc&0xff);
+ chip8_regs.sp--;
+ write_mem (chip8_regs.sp,chip8_regs.pc>>8);
+ chip8_regs.pc=opcode;
+}
+
+static void op_jmp (word opcode)
+{
+ chip8_regs.pc=opcode;
+}
+
+static void op_key (word opcode)
+{
+ byte key_value,cp_value;
+ if ((opcode&0xff)==0x9e)
+ cp_value=1;
+ else if ((opcode&0xff)==0xa1)
+ cp_value=0;
+ else
+ return;
+ key_value=chip8_keys[get_reg_value(opcode)&0x0f];
+ if (cp_value==key_value) chip8_regs.pc+=2;
+}
+
+static void op_skeq_const (word opcode)
+{
+ if (get_reg_value(opcode)==(opcode&0xff)) chip8_regs.pc+=2;
+}
+
+static void op_skne_const (word opcode)
+{
+ if (get_reg_value(opcode)!=(opcode&0xff)) chip8_regs.pc+=2;
+}
+
+static void op_skeq_reg (word opcode)
+{
+ if (get_reg_value(opcode)==get_reg_value_2(opcode)) chip8_regs.pc+=2;
+}
+
+static void op_skne_reg (word opcode)
+{
+ if (get_reg_value(opcode)!=get_reg_value_2(opcode)) chip8_regs.pc+=2;
+}
+
+static void op_mov_const (word opcode)
+{
+ *get_reg_offset(opcode)=opcode&0xff;
+}
+
+static void op_add_const (word opcode)
+{
+ *get_reg_offset(opcode)+=opcode&0xff;
+}
+
+static void op_mvi (word opcode)
+{
+ chip8_regs.i=opcode;
+}
+
+static void op_jmi (word opcode)
+{
+ chip8_regs.pc=opcode+chip8_regs.alg[0];
+}
+
+static void op_rand (word opcode)
+{
+ *get_reg_offset(opcode)=rb->rand()&(opcode&0xff);
+}
+
+static void math_or (byte *reg1,byte reg2)
+{
+ *reg1|=reg2;
+}
+
+static void math_mov (byte *reg1,byte reg2)
+{
+ *reg1=reg2;
+}
+
+static void math_nop (byte *reg1,byte reg2)
+{
+ (void)reg1;
+ (void)reg2;
+}
+
+static void math_and (byte *reg1,byte reg2)
+{
+ *reg1&=reg2;
+}
+
+static void math_xor (byte *reg1,byte reg2)
+{
+ *reg1^=reg2;
+}
+
+static void math_add (byte *reg1,byte reg2)
+{
+ word tmp;
+ tmp=*reg1+reg2;
+ *reg1=(byte)tmp;
+ chip8_regs.alg[15]=tmp>>8;
+}
+
+static void math_sub (byte *reg1,byte reg2)
+{
+ word tmp;
+ tmp=*reg1-reg2;
+ *reg1=(byte)tmp;
+ chip8_regs.alg[15]=((byte)(tmp>>8))+1;
+}
+
+static void math_shr (byte *reg1,byte reg2)
+{
+ (void)reg2;
+ chip8_regs.alg[15]=*reg1&1;
+ *reg1>>=1;
+}
+
+static void math_shl (byte *reg1,byte reg2)
+{
+ (void)reg2;
+ chip8_regs.alg[15]=*reg1>>7;
+ *reg1<<=1;
+}
+
+static void math_rsb (byte *reg1,byte reg2)
+{
+ word tmp;
+ tmp=reg2-*reg1;
+ *reg1=(byte)tmp;
+ chip8_regs.alg[15]=((byte)(tmp>>8))+1;
+}
+
+static void op_system (word opcode)
+{
+ switch ((byte)opcode)
+ {
+ case 0xe0:
+ rb->memset (chip8_display,0,sizeof(chip8_display));
+ break;
+ case 0xee:
+ chip8_regs.pc=read_mem(chip8_regs.sp)<<8;
+ chip8_regs.sp++;
+ chip8_regs.pc+=read_mem(chip8_regs.sp);
+ chip8_regs.sp++;
+ break;
+ }
+}
+
+static void op_misc (word opcode)
+{
+ byte *reg,i,j;
+ reg=get_reg_offset(opcode);
+ switch ((byte)opcode)
+ {
+ case 0x07:
+ *reg=chip8_regs.delay;
+ break;
+ case 0x0a:
+ if (chip8_key_pressed)
+ *reg=chip8_key_pressed-1;
+ else
+ chip8_regs.pc-=2;
+ break;
+ case 0x15:
+ chip8_regs.delay=*reg;
+ break;
+ case 0x18:
+ chip8_regs.sound=*reg;
+ if (chip8_regs.sound) chip8_sound_on();
+ break;
+ case 0x1e:
+ chip8_regs.i+=(*reg);
+ break;
+ case 0x29:
+ chip8_regs.i=((word)(*reg&0x0f))*5;
+ break;
+ case 0x33:
+ i=*reg;
+ for (j=0;i>=100;i-=100) j++;
+ write_mem (chip8_regs.i,j);
+ for (j=0;i>=10;i-=10) j++;
+ write_mem (chip8_regs.i+1,j);
+ write_mem (chip8_regs.i+2,i);
+ break;
+ case 0x55:
+ for (i=0,j=(opcode>>8)&0x0f;i<=j;++i)
+ write_mem(chip8_regs.i+i,chip8_regs.alg[i]);
+ break;
+ case 0x65:
+ for (i=0,j=(opcode>>8)&0x0f;i<=j;++i)
+ chip8_regs.alg[i]=read_mem(chip8_regs.i+i);
+ break;
+ }
+}
+
+static void op_sprite (word opcode)
+{
+ byte *q;
+ byte n,x,x2,y,collision;
+ word p;
+ x=get_reg_value(opcode)&63;
+ y=get_reg_value_2(opcode)&31;
+ p=chip8_regs.i;
+ q=chip8_display+y*64;
+ n=opcode&0x0f;
+ if (n+y>32) n=32-y;
+ for (collision=1;n;--n,q+=64)
+ {
+ for (y=read_mem(p++),x2=x;y;y<<=1,x2=(x2+1)&63)
+ if (y&0x80) collision&=(q[x2]^=0xff);
+ }
+ chip8_regs.alg[15]=collision^1;
+}
+
+static math_fn math_opcodes[16]=
+{
+ math_mov,
+ math_or,
+ math_and,
+ math_xor,
+ math_add,
+ math_sub,
+ math_shr,
+ math_rsb,
+ math_nop,
+ math_nop,
+ math_nop,
+ math_nop,
+ math_nop,
+ math_nop,
+ math_shl,
+ math_nop
+};
+
+static void op_math (word opcode)
+{
+ (*(math_opcodes[opcode&0x0f]))
+ (get_reg_offset(opcode),get_reg_value_2(opcode));
+}
+
+static opcode_fn main_opcodes[16]=
+{
+ op_system,
+ op_jmp,
+ op_call,
+ op_skeq_const,
+ op_skne_const,
+ op_skeq_reg,
+ op_mov_const,
+ op_add_const,
+ op_math,
+ op_skne_reg,
+ op_mvi,
+ op_jmi,
+ op_rand,
+ op_sprite,
+ op_key,
+ op_misc
+};
+
+/****************************************************************************/
+/* Update the display */
+/****************************************************************************/
+static void chip8_update_display(void)
+{
+ int x,y,i;
+ byte w;
+
+// lcd_clear_display();
+ for (y=0;y<=7;++y) /* 32 rows */
+ {
+ for (x=0;x<64;++x) /* 64 columns */
+ {
+ w = 0;
+ for (i=0;i<=3;i++)
+ {
+ w = w >> 2;
+ if (chip8_display[x+(y*4+i)*64] != 0)
+ {
+ w += 128+64;
+ }
+ lcd_framebuf[y][x] = w;
+ }
+ }
+ }
+ rb->lcd_bitmap(lcd_framebuf[0], 24, 0*8, 64, 8, true);
+ rb->lcd_bitmap(lcd_framebuf[1], 24, 1*8, 64, 8, true);
+ rb->lcd_bitmap(lcd_framebuf[2], 24, 2*8, 64, 8, true);
+ rb->lcd_bitmap(lcd_framebuf[3], 24, 3*8, 64, 8, true);
+ rb->lcd_bitmap(lcd_framebuf[4], 24, 4*8, 64, 8, true);
+ rb->lcd_bitmap(lcd_framebuf[5], 24, 5*8, 64, 8, true);
+ rb->lcd_bitmap(lcd_framebuf[6], 24, 6*8, 64, 8, true);
+ rb->lcd_bitmap(lcd_framebuf[7], 24, 7*8, 64, 8, true);
+ rb->lcd_update_rect(24,0,64,64);
+}
+
+
+static void chip8_keyboard(void)
+{
+ switch (rb->button_get(false))
+ {
+ case BUTTON_OFF: /* Abort Emulator */
+ chip8_running = false;
+ break;
+
+ case BUTTON_UP: chip8_keys[2] = 1; break;
+ case BUTTON_UP | BUTTON_REL: chip8_keys[2] = 0; break;
+ case BUTTON_LEFT: chip8_keys[4] = 1; break;
+ case BUTTON_LEFT | BUTTON_REL: chip8_keys[4] = 0; break;
+ case BUTTON_RIGHT: chip8_keys[6] = 1; break;
+ case BUTTON_RIGHT | BUTTON_REL: chip8_keys[6] = 0; break;
+ case BUTTON_DOWN: chip8_keys[8] = 1; break;
+ case BUTTON_DOWN | BUTTON_REL: chip8_keys[8] = 0; break;
+ case BUTTON_PLAY: chip8_keys[5] = 1; break;
+ case BUTTON_PLAY | BUTTON_REL: chip8_keys[5] = 0; break;
+ case BUTTON_F1: chip8_keys[1] = 1; break;
+ case BUTTON_F1 | BUTTON_REL: chip8_keys[1] = 0; break;
+ case BUTTON_F2: chip8_keys[7] = 1; break;
+ case BUTTON_F2 | BUTTON_REL: chip8_keys[7] = 0; break;
+ case BUTTON_F3: chip8_keys[3] = 1; break;
+ case BUTTON_F3 | BUTTON_REL: chip8_keys[3] = 0; break;
+ case BUTTON_ON: chip8_keys[9] = 1; break;
+ case BUTTON_ON | BUTTON_REL: chip8_keys[9] = 0; break;
+
+ case SYS_USB_CONNECTED:
+#ifdef HAVE_LCD_CHARCELLS
+ status_set_param(false);
+#endif
+ chip8_running = false;
+ break;
+ }
+}
+
+
+/****************************************************************************/
+/* Execute chip8_iperiod opcodes */
+/****************************************************************************/
+static void chip8_execute(void)
+{
+ byte i;
+ byte key_pressed=0;
+ word opcode;
+ for (i = chip8_iperiod ; i ;--i)
+ { /* Fetch the opcode */
+ opcode=(read_mem(chip8_regs.pc)<<8)+read_mem(chip8_regs.pc+1);
+
+ chip8_regs.pc+=2;
+ (*(main_opcodes[opcode>>12]))(opcode&0x0fff); /* Emulate this opcode */
+ }
+ /* Update timers */
+ if (chip8_regs.delay) --chip8_regs.delay;
+ if (chip8_regs.sound) --chip8_regs.sound; /* How could we make sound on the archos? */
+ /* Update the machine status */
+ chip8_update_display();
+ chip8_keyboard();
+ rb->sleep(HZ/70); /* ca. 70Hz */
+
+ for (i=key_pressed=0;i<16;++i) /* check if a key was first */
+ if (chip8_keys[i]) key_pressed=i+1; /* pressed */
+ if (key_pressed && key_pressed!=chip8_key_pressed)
+ chip8_key_pressed=key_pressed;
+ else
+ chip8_key_pressed=0;
+}
+
+/****************************************************************************/
+/* Reset the virtual chip8 machine */
+/****************************************************************************/
+static void chip8_reset(void)
+{
+ static byte chip8_sprites[16*5]=
+ {
+ 0xf9,0x99,0xf2,0x62,0x27,
+ 0xf1,0xf8,0xff,0x1f,0x1f,
+ 0x99,0xf1,0x1f,0x8f,0x1f,
+ 0xf8,0xf9,0xff,0x12,0x44,
+ 0xf9,0xf9,0xff,0x9f,0x1f,
+ 0xf9,0xf9,0x9e,0x9e,0x9e,
+ 0xf8,0x88,0xfe,0x99,0x9e,
+ 0xf8,0xf8,0xff,0x8f,0x88,
+ };
+ byte i;
+ for (i=0;i<16*5;++i)
+ {
+ write_mem (i<<1,chip8_sprites[i]&0xf0);
+ write_mem ((i<<1)+1,chip8_sprites[i]<<4);
+ }
+ rb->memset (chip8_regs.alg,0,sizeof(chip8_regs.alg));
+ rb->memset (chip8_keys,0,sizeof(chip8_keys));
+ chip8_key_pressed=0;
+ rb->memset (chip8_display,0,sizeof(chip8_display));
+ chip8_regs.delay=chip8_regs.sound=chip8_regs.i=0;
+ chip8_regs.sp=0x1e0;
+ chip8_regs.pc=0x200;
+ chip8_sound_off ();
+ chip8_running=1;
+}
+
+static bool chip8_init(char* file)
+{
+ int numread;
+ int fd;
+
+ fd = rb->open(file, O_RDONLY);
+ if (fd==-1) return false;
+ numread = rb->read(fd, chip8_mem+0x200, 4096-0x200);
+ if (numread==-1) return false;
+
+ rb->close(fd);
+ return true;
+}
+
+bool chip8_run(char* file)
+{
+ int ok;
+
+ ok = chip8_init(file);
+ if (!ok) {
+ rb->lcd_clear_display();
+ rb->lcd_puts(0, 0, "Error");
+ rb->lcd_update();
+ rb->sleep(HZ);
+ return false;
+ }
+ rb->lcd_clear_display();
+ rb->lcd_puts(0, 0, "Chip8 Emulator ");
+ rb->lcd_puts(0, 1, " (c) by ");
+ rb->lcd_puts(0, 2, "Marcel de Kogel");
+ rb->lcd_puts(0, 3, " Archos: ");
+ rb->lcd_puts(0, 4, " Blueloop ");
+ rb->lcd_puts(0, 5, "---------------");
+ rb->lcd_puts(0, 6, "File OK...");
+ rb->lcd_update();
+ rb->sleep(HZ*1);
+ rb->lcd_clear_display();
+ rb->lcd_drawrect(23,0,66,64);
+ rb->lcd_update();
+ chip8_reset();
+ while (chip8_running) chip8_execute();
+
+ return true;
+}
+
+
+/***************** Plugin Entry Point *****************/
+
+enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
+{
+ char* filename;
+
+ /* this macro should be called as the first thing you do in the plugin.
+ it test that the api version and model the plugin was compiled for
+ matches the machine it is running on */
+ TEST_PLUGIN_API(api);
+ rb = api; /* copy to global api pointer */
+
+ if (parameter == NULL)
+ {
+ rb->lcd_puts(0, 0, "Play .ch8 file!");
+ rb->lcd_update();
+ rb->sleep(HZ);
+ return PLUGIN_ERROR;
+ }
+ else
+ {
+ filename = (char*) parameter;
+ }
+
+ /* now go ahead and have fun! */
+ return chip8_run(filename) ? PLUGIN_OK : PLUGIN_ERROR;
+}
+
+#endif // #ifdef HAVE_LCD_BITMAP
+#endif // #ifndef SIMULATOR