From 0860a4d6d17b86772b388c69d42c49d33e3a0898 Mon Sep 17 00:00:00 2001 From: Jörg Hohensohn Date: Thu, 30 Oct 2003 23:47:34 +0000 Subject: 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 --- apps/plugins/chip8.c | 553 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 553 insertions(+) create mode 100644 apps/plugins/chip8.c (limited to 'apps') 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 -- cgit v1.2.3