/*************************************************************************** * __________ __ ___. * 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 */ #ifdef HAVE_LCD_BITMAP PLUGIN_HEADER static struct plugin_api* rb; /* here is a global api struct pointer */ #define EXTERN static #define STATIC static #define memset rb->memset #define memcpy rb->memcpy #define printf DEBUGF #define rand rb->rand /* #define CHIP8_DEBUG */ #if (LCD_WIDTH >= 112) && (LCD_HEIGHT >= 64) #define CHIP8_SUPER /* SCHIP even on the archos recorder (112x64) */ #endif /****************************************************************************/ /** (S)CHIP-8 core emulation, from Vision-8 + mods by Fdy **/ /** The core is completely generic and can be used outside of Rockbox, **/ /** thus the STATIC, EXTERN etc. Please do not modify. **/ /****************************************************************************/ /** Vision8: CHIP8 emulator *************************************************/ /** **/ /** CHIP8.h **/ /** **/ /** This file contains the portable CHIP8 emulation engine definitions **/ /** **/ /** Copyright (C) Marcel de Kogel 1997 **/ /** You are not allowed to distribute this software commercially **/ /** Please, notify me, if you make any changes to this file **/ /****************************************************************************/ #ifndef __CHIP8_H #define __CHIP8_H #ifndef EXTERN #define EXTERN extern #endif 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 */ }; EXTERN struct chip8_regs_struct chip8_regs; #ifdef CHIP8_SUPER #define CHIP8_WIDTH 128 #define CHIP8_HEIGHT 64 EXTERN byte chip8_super; /* != 0 if in SCHIP display mode */ #else #define CHIP8_WIDTH 64 #define CHIP8_HEIGHT 32 #endif EXTERN byte chip8_iperiod; /* number of opcodes per */ /* timeslice (1/50sec.) */ EXTERN byte chip8_keys[16]; /* if 1, key is held down */ EXTERN byte chip8_display[CHIP8_WIDTH*CHIP8_HEIGHT];/* 0xff if pixel is set, */ /* 0x00 otherwise */ EXTERN byte chip8_mem[4096]; /* machine memory. program */ /* is loaded at 0x200 */ EXTERN byte chip8_running; /* if 0, emulation stops */ EXTERN void chip8_execute (void); /* execute chip8_iperiod */ /* opcodes */ EXTERN void chip8_reset (void); /* reset virtual machine */ EXTERN void chip8 (void); /* start chip8 emulation */ EXTERN void chip8_sound_on (void); /* turn sound on */ EXTERN void chip8_sound_off (void); /* turn sound off */ EXTERN void chip8_interrupt (void); /* update keyboard, */ /* display, etc. */ #ifdef CHIP8_DEBUG EXTERN byte chip8_trace; /* if 1, call debugger */ /* every opcode */ EXTERN word chip8_trap; /* if pc==trap, set trace */ /* flag */ EXTERN void chip8_debug (word opcode,struct chip8_regs_struct *regs); #endif #endif /* __CHIP8_H */ /** Vision8: CHIP8 emulator *************************************************/ /** **/ /** CHIP8.c **/ /** **/ /** This file contains the portable CHIP8 emulation engine **/ /** SCHIP emulation (C) Frederic Devernay 2005 **/ /** **/ /** Copyright (C) Marcel de Kogel 1997 **/ /** You are not allowed to distribute this software commercially **/ /** Please, notify me, if you make any changes to this file **/ /****************************************************************************/ /* you can #define STATIC static #define EXTERN static and include this file for single-object generation */ #ifndef STATIC #include /* for memset, etc. */ #include #define STATIC #endif #ifdef CHIP8_DEBUG #include #define DBG_(_x) ((void)(_x)) #else #define DBG_(_x) ((void)0) #endif STATIC struct chip8_regs_struct chip8_regs; static byte chip8_key_pressed; STATIC byte chip8_keys[16]; /* if 1, key is held down */ STATIC byte chip8_display[CHIP8_WIDTH*CHIP8_HEIGHT]; /* 0xff if pixel is set, */ /* 0x00 otherwise */ #ifdef CHIP8_SUPER STATIC byte chip8_super; /* != 0 if in SCHIP display mode */ #endif 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_iperiod; 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); 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; #ifdef CHIP8_DEBUG if(chip8_regs.sp < 0x1c0) printf("warning: more than 16 subroutine calls, sp=%x\n", chip8_regs.sp); #endif } static void op_jmp (word opcode) { chip8_regs.pc=opcode; } static void op_key (word opcode) { #ifdef CHIP8_DEBUG static byte tested[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; #endif byte key, key_value,cp_value; if ((opcode&0xff)==0x9e) /* skp */ cp_value=1; else if ((opcode&0xff)==0xa1) /* sknp */ cp_value=0; else { DBG_(printf("unhandled key opcode 0x%x\n", opcode)); return; } key = get_reg_value(opcode)&0x0f; #ifdef CHIP8_DEBUG if (!tested[key]) { tested[key] = 1; DBG_(printf("testing key %d\n", key)); } #endif key_value=chip8_keys[key]; 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)=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; DBG_(printf("Warning: math nop!\n")); } 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; } #ifdef CHIP8_SUPER /* SUPER: scroll down n lines (or half in CHIP8 mode) */ static void scroll_down(word opcode) { int n = opcode & 0xf; byte *dst = chip8_display + CHIP8_WIDTH*CHIP8_HEIGHT -1; byte *src = dst - n*CHIP8_WIDTH; while(src >= chip8_display) { *dst-- = *src--; } while(dst >= chip8_display) { *dst-- = 0; } } /* SUPER: scroll 4 pixels left! */ static void scroll_left(void) { byte *dst = chip8_display; byte *src = dst; byte *eol = chip8_display + CHIP8_WIDTH; byte *eoi = chip8_display + CHIP8_WIDTH*CHIP8_HEIGHT; while(eol <= eoi) { src+=4; while(src < eol) { *dst++ = *src++; } *dst++ = 0; *dst++ = 0; *dst++ = 0; *dst++ = 0; eol += CHIP8_WIDTH; } } static void scroll_right(void) { byte *dst = chip8_display + CHIP8_WIDTH*CHIP8_HEIGHT -1; byte *src = dst; byte *bol = chip8_display + CHIP8_WIDTH*(CHIP8_HEIGHT-1); while(bol >= chip8_display) { src-=4; while(src >= bol) { *dst-- = *src--; } *dst-- = 0; *dst-- = 0; *dst-- = 0; *dst-- = 0; bol -= CHIP8_WIDTH; } } #endif static void op_system (word opcode) { switch ((byte)opcode) { #ifdef CHIP8_SUPER case 0xfb: scroll_right(); break; case 0xfc: scroll_left(); break; case 0xfd: DBG_(printf("SUPER: quit the emulator\n")); chip8_reset(); break; case 0xfe: DBG_(printf("SUPER: set CHIP-8 graphic mode\n")); memset (chip8_display,0,sizeof(chip8_display)); chip8_super = 0; break; case 0xff: DBG_(printf("SUPER: set SCHIP graphic mode\n")); memset (chip8_display,0,sizeof(chip8_display)); chip8_super = 1; break; #endif case 0xe0: 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; default: #ifdef CHIP8_SUPER if ((opcode & 0xF0) == 0xC0) scroll_down(opcode); else #endif { DBG_(printf("unhandled system opcode 0x%x\n", opcode)); chip8_running = 3; } break; } } static void op_misc (word opcode) { byte *reg,i,j; #ifdef CHIP8_DEBUG static byte firstwait = 1; #endif reg=get_reg_offset(opcode); switch ((byte)opcode) { case 0x07: /* gdelay */ *reg=chip8_regs.delay; break; case 0x0a: /* key */ #ifdef CHIP8_DEBUG if(firstwait) { printf("waiting for key press\n"); firstwait = 0; } #endif if (chip8_key_pressed) *reg=chip8_key_pressed-1; else chip8_regs.pc-=2; break; case 0x15: /* sdelay */ chip8_regs.delay=*reg; break; case 0x18: /* ssound */ chip8_regs.sound=*reg; if (chip8_regs.sound) chip8_sound_on(); break; case 0x1e: /* adi */ chip8_regs.i+=(*reg); break; case 0x29: /* font */ chip8_regs.i=((word)(*reg&0x0f))*5; break; #ifdef CHIP8_SUPER case 0x30: /* xfont */ chip8_regs.i=((word)(*reg&0x0f))*10+0x50; break; #endif case 0x33: /* bcd */ 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: /* str */ for (i=0,j=(opcode>>8)&0x0f; i<=j; ++i) write_mem(chip8_regs.i+i,chip8_regs.alg[i]); break; case 0x65: /* ldr */ for (i=0,j=(opcode>>8)&0x0f; i<=j; ++i) chip8_regs.alg[i]=read_mem(chip8_regs.i+i); break; #ifdef CHIP8_SUPER case 0x75: DBG_(printf("SUPER: save V0..V%x (X<8) in the HP48 flags\n", (opcode>>8)&0x0f)); break; case 0x85: DBG_(printf("SUPER: load V0..V%x (X<8) from the HP48 flags\n", (opcode>>8)&0x0f)); break; #endif default: DBG_(printf("unhandled misc opcode 0x%x\n", opcode)); break; } } static void op_sprite (word opcode) { byte *q; byte n,x,x2,y,collision; word p; x=get_reg_value(opcode); y=get_reg_value_2(opcode); p=chip8_regs.i; n=opcode&0x0f; #ifdef CHIP8_SUPER if (chip8_super) { /*printf("SUPER: sprite(%x)\n", opcode);*/ x &= 128-1; y &= 64-1; q=chip8_display+y*CHIP8_WIDTH; if(n == 0) { /* 16x16 sprite */ n = 16; if (n+y>64) n=64-y; for (collision=1;n;--n,q+=CHIP8_WIDTH) { /* first 8 bits */ for (y=read_mem(p++),x2=x;y;y<<=1,x2=(x2+1)&(CHIP8_WIDTH-1)) if (y&0x80) collision&=(q[x2]^=0xff); x2=(x+8)&(CHIP8_WIDTH-1); /* last 8 bits */ for (y=read_mem(p++);y;y<<=1,x2=(x2+1)&(CHIP8_WIDTH-1)) if (y&0x80) collision&=(q[x2]^=0xff); } } else { /* 8xn sprite */ if (n+y>64) n=64-y; for (collision=1;n;--n,q+=CHIP8_WIDTH) { for (y=read_mem(p++),x2=x;y;y<<=1,x2=(x2+1)&(CHIP8_WIDTH-1)) if (y&0x80) collision&=(q[x2]^=0xff); } } } else { x &= 64-1; y &= 32-1; q=chip8_display+y*CHIP8_WIDTH*2; if(n == 0) n = 16; if (n+y>32) n=32-y; for (collision=1;n;--n,q+=CHIP8_WIDTH*2) { for (y=read_mem(p++),x2=x*2;y;y<<=1,x2=(x2+2)&(CHIP8_WIDTH-1)) if (y&0x80) { q[x2]^=0xff; q[x2+1]^=0xff; q[x2+CHIP8_WIDTH]^=0xff; q[x2+CHIP8_WIDTH+1]^=0xff; collision &= q[x2]|q[x2+1]|q[x2+CHIP8_WIDTH]|q[x2+CHIP8_WIDTH+1]; } } } #else x &= 64-1; y &= 32-1; q=chip8_display+y*CHIP8_WIDTH; if (n+y>32) n=32-y; for (collision=1;n;--n,q+=CHIP8_WIDTH) { for (y=read_mem(p++),x2=x;y;y<<=1,x2=(x2+1)&(CHIP8_WIDTH-1)) if (y&0x80) collision&=(q[x2]^=0xff); } #endif 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 }; #ifdef CHIP8_DEBUG STATIC byte chip8_trace; STATIC word chip8_trap; /****************************************************************************/ /* This routine is called every opcode when chip8_trace==1. It prints the */ /* current register contents and the opcode being executed */ /****************************************************************************/ STATIC void chip8_debug (word opcode,struct chip8_regs_struct *regs) { int i; byte hextable[16] = "0123456789ABCDEF"; byte v1[3] = "Vx\0"; byte v2[3] = "Vx\0"; v1[1] = hextable[(opcode>>8)&0x0f]; v2[1] = hextable[(opcode>>8)&0x0f]; printf ("PC=%04X: %04X - ",regs->pc,opcode); switch (opcode>>12) { case 0: if ((opcode&0xff0) == 0xc0) { printf ("SCD %01X ; Scroll down n lines",opcode&0xf); } else switch (opcode&0xfff) { case 0xe0: printf ("CLS ; Clear screen"); break; case 0xee: printf ("RET ; Return from subroutine call"); break; case 0xfb: printf("SCR ; Scroll right"); break; case 0xfc: printf("SCL ; Scroll left"); break; case 0xfd: printf("EXIT ; Terminate the interpreter"); break; case 0xfe: printf("LOW ; Disable extended screen mode"); break; case 0xff: printf("HIGH ; Enable extended screen mode"); break; default: printf ("SYS %03X ; Unknown system call",opcode&0xff); } break; case 1: printf ("JP %03X ; Jump to address",opcode&0xfff); break; case 2: printf ("CALL %03X ; Call subroutine",opcode&0xfff); break; case 3: printf ("SE %s,%02X ; Skip if register == constant",v1,opcode&0xff); break; case 4: printf ("SNE %s,%02X ; Skip if register <> constant",v1,opcode&0xff); break; case 5: printf ("SE %s,%s ; Skip if register == register",v1,v2); break; case 6: printf ("LD %s,%02X ; Set VX = Byte",v1,opcode&0xff); break; case 7: printf ("ADD %s,%02X ; Set VX = VX + Byte",v1,opcode&0xff); break; case 8: switch (opcode&0x0f) { case 0: printf ("LD %s,%s ; Set VX = VY, VF updates",v1,v2); break; case 1: printf ("OR %s,%s ; Set VX = VX | VY, VF updates",v1,v2); break; case 2: printf ("AND %s,%s ; Set VX = VX & VY, VF updates",v1,v2); break; case 3: printf ("XOR %s,%s ; Set VX = VX ^ VY, VF updates",v1,v2); break; case 4: printf ("ADD %s,%s ; Set VX = VX + VY, VF = carry",v1,v2); break; case 5: printf ("SUB %s,%s ; Set VX = VX - VY, VF = !borrow",v1,v2); break; case 6: printf ("SHR %s,%s ; Set VX = VX >> 1, VF = carry",v1,v2); break; case 7: printf ("SUBN %s,%s ; Set VX = VY - VX, VF = !borrow",v1,v2); break; case 14: printf ("SHL %s,%s ; Set VX = VX << 1, VF = carry",v1,v2); break; default: printf ("Illegal opcode"); } break; case 9: printf ("SNE %s,%s ; Skip next instruction iv VX!=VY",v1,v2); break; case 10: printf ("LD I,%03X ; Set I = Addr",opcode&0xfff); break; case 11: printf ("JP V0,%03X ; Jump to Addr + V0",opcode&0xfff); break; case 12: printf ("RND %s,%02X ; Set VX = random & Byte",v1,opcode&0xff); break; case 13: printf ("DRW %s,%s,%X ; Draw n byte sprite stored at [i] at VX,VY. Set VF = collision",v1,v2,opcode&0x0f); break; case 14: switch (opcode&0xff) { case 0x9e: printf ("SKP %s ; Skip next instruction if key VX down",v1); break; case 0xa1: printf ("SKNP %s ; Skip next instruction if key VX up",v1); break; default: printf ("%04X ; Illegal opcode", opcode); } break; case 15: switch (opcode&0xff) { case 0x07: printf ("LD %s,DT ; Set VX = delaytimer",v1); break; case 0x0a: printf ("LD %s,K ; Set VX = key, wait for keypress",v1); break; case 0x15: printf ("LD DT,%s ; Set delaytimer = VX",v1); break; case 0x18: printf ("LD ST,%s ; Set soundtimer = VX",v1); break; case 0x1e: printf ("ADD I,%s ; Set I = I + VX",v1); break; case 0x29: printf ("LD LF,%s ; Point I to 5 byte numeric sprite for value in VX",v1); break; case 0x30: printf ("LD HF,%s ; Point I to 10 byte numeric sprite for value in VX",v1); break; case 0x33: printf ("LD B,%s ; Store BCD of VX in [I], [I+1], [I+2]",v1); break; case 0x55: printf ("LD [I],%s ; Store V0..VX in [I]..[I+X]",v1); break; case 0x65: printf ("LD %s,[I] ; Read V0..VX from [I]..[I+X]",v1); break; case 0x75: printf ("LD R,%s ; Store V0..VX in RPL user flags (X<=7)",v1); break; case 0x85: printf ("LD %s,R ; Read V0..VX from RPL user flags (X<=7)",v1); break; default: printf ("%04X ; Illegal opcode", opcode); } break; } printf ("\n; Registers: "); for (i=0;i<16;++i) printf ("%02x ",(regs->alg[i])&0xff); printf ("\n; Index: %03x Stack:%03x Delay:%02x Sound:%02x\n", regs->i&0xfff,regs->sp&0xfff,regs->delay&0xff,regs->sound&0xff); } #endif /****************************************************************************/ /* 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); #ifdef CHIP8_DEBUG /* Check if trap address has been reached */ if ((chip8_regs.pc&4095)==chip8_trap) chip8_trace=1; /* Call the debugger if chip8_trace!=0 */ if (chip8_trace) chip8_debug (opcode,&chip8_regs); #endif 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) if (--chip8_regs.sound == 0) chip8_sound_off(); /* Update the machine status */ chip8_interrupt (); for (i=key_pressed=0;i<16;++i) /* check if a key was first */ if (chip8_keys[i]) /* pressed */ key_pressed=i+1; 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[0x50]= { 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, }; /* 4x5 pixel hexadecimal character font patterns */ #ifdef CHIP8_SUPER static byte schip_sprites[10*10]= { 0x3C, 0x7E, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x7E, 0x3C, /* 0 */ 0x18, 0x38, 0x58, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, /* 1 */ 0x3E, 0x7F, 0xC3, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, /* 2 */ 0x3C, 0x7E, 0xC3, 0x03, 0x0E, 0x0E, 0x03, 0xC3, 0x7E, 0x3C, /* 3 */ 0x06, 0x0E, 0x1E, 0x36, 0x66, 0xC6, 0xFF, 0xFF, 0x06, 0x06, /* 4 */ 0xFF, 0xFF, 0xC0, 0xC0, 0xFC, 0xFE, 0x03, 0xC3, 0x7E, 0x3C, /* 5 */ 0x3E, 0x7C, 0xC0, 0xC0, 0xFC, 0xFE, 0xC3, 0xC3, 0x7E, 0x3C, /* 6 */ 0xFF, 0xFF, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x60, 0x60, /* 7 */ 0x3C, 0x7E, 0xC3, 0xC3, 0x7E, 0x7E, 0xC3, 0xC3, 0x7E, 0x3C, /* 8 */ 0x3C, 0x7E, 0xC3, 0xC3, 0x7F, 0x3F, 0x03, 0x03, 0x3E, 0x7C, /* 9 */ }; /* 8x10 pixel font patterns (only 10) */ #endif 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); } #ifdef CHIP8_SUPER /* for (i=0; i<100; i++) write_mem (i+0x50,schip_sprites[i]); */ memcpy(chip8_mem+0x50, schip_sprites, 100); chip8_super = 0; #endif memset (chip8_regs.alg,0,sizeof(chip8_regs.alg)); memset (chip8_keys,0,sizeof(chip8_keys)); chip8_key_pressed=0; 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; #ifdef CHIP8_DEBUG chip8_trace=0; #endif } /****************************************************************************/ /* Start CHIP8 emulation */ /****************************************************************************/ STATIC void chip8 (void) { chip8_reset (); while (chip8_running==1) chip8_execute (); } /****************************************************************************/ /** END OF (S)CHIP-8 core emulation, from Vision-8 + mods by Fdy **/ /****************************************************************************/ /* size of the displayed area */ #if (CHIP8_WIDTH == 128) && (LCD_WIDTH < 128) #define CHIP8_LCDWIDTH 112 #else #define CHIP8_LCDWIDTH CHIP8_WIDTH #endif #if (LCD_WIDTH > CHIP8_LCDWIDTH) #define CHIP8_X ((LCD_WIDTH - CHIP8_LCDWIDTH)/2) #else #define CHIP8_X 0 #endif #if (LCD_HEIGHT > CHIP8_HEIGHT) #define CHIP8_Y (((LCD_HEIGHT - CHIP8_HEIGHT)>>4)<<3) #else #define CHIP8_Y 0 #endif /* variable button definitions */ #if CONFIG_KEYPAD == RECORDER_PAD /* only 9 out of 16 chip8 buttons */ #define CHIP8_OFF BUTTON_OFF #define CHIP8_KEY1 BUTTON_F1 #define CHIP8_KEY2 BUTTON_UP #define CHIP8_KEY3 BUTTON_F3 #define CHIP8_KEY4 BUTTON_LEFT #define CHIP8_KEY5 BUTTON_PLAY #define CHIP8_KEY6 BUTTON_RIGHT #define CHIP8_KEY7 BUTTON_F2 #define CHIP8_KEY8 BUTTON_DOWN #define CHIP8_KEY9 BUTTON_ON #elif CONFIG_KEYPAD == ONDIO_PAD /* even more limited */ #define CHIP8_OFF BUTTON_OFF #define CHIP8_KEY2 BUTTON_UP #define CHIP8_KEY4 BUTTON_LEFT #define CHIP8_KEY5 BUTTON_MENU #define CHIP8_KEY6 BUTTON_RIGHT #define CHIP8_KEY8 BUTTON_DOWN #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ (CONFIG_KEYPAD == IRIVER_H300_PAD) #define CHIP8_OFF BUTTON_OFF #define CHIP8_KEY2 BUTTON_UP #define CHIP8_KEY4 BUTTON_LEFT #define CHIP8_KEY5 BUTTON_SELECT #define CHIP8_KEY6 BUTTON_RIGHT #define CHIP8_KEY8 BUTTON_DOWN #define CHIP8_RC_OFF BUTTON_RC_STOP #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ (CONFIG_KEYPAD == IPOD_3G_PAD) #define CHIP8_OFF BUTTON_MENU #define CHIP8_KEY2 BUTTON_SCROLL_BACK #define CHIP8_KEY4 BUTTON_LEFT #define CHIP8_KEY5 BUTTON_PLAY #define CHIP8_KEY6 BUTTON_RIGHT #define CHIP8_KEY8 BUTTON_SCROLL_FWD #elif (CONFIG_KEYPAD == IAUDIO_X5_PAD) #define CHIP8_OFF BUTTON_POWER #define CHIP8_KEY2 BUTTON_UP #define CHIP8_KEY4 BUTTON_LEFT #define CHIP8_KEY5 BUTTON_SELECT #define CHIP8_KEY6 BUTTON_RIGHT #define CHIP8_KEY8 BUTTON_DOWN #elif (CONFIG_KEYPAD == GIGABEAT_PAD) #define CHIP8_OFF BUTTON_A #define CHIP8_KEY2 BUTTON_UP #define CHIP8_KEY4 BUTTON_LEFT #define CHIP8_KEY5 BUTTON_MENU #define CHIP8_KEY6 BUTTON_RIGHT #define CHIP8_KEY8 BUTTON_DOWN #endif static byte chip8_virtual_keys[16]; static byte chip8_keymap[16]; static unsigned long starttimer; /* Timer value at the beginning */ static unsigned long cycles; /* Number of update cycles (50Hz) */ #ifndef SIMULATOR static bool is_playing; #endif /* one frame of bitswapped mp3 data */ static unsigned char beep[]={255, 223, 28, 35, 0,192,210, 35,226, 72,188,242, 1,128,166, 16, 68,146,252,151, 19, 10,180,245,127, 96,184, 3,184, 30, 0,118, 59,128,121,102, 6,212, 0, 97, 6, 42, 65, 28,134,192,145, 57, 38,136, 73, 29, 38,132, 15, 21, 70, 91,185, 99,198, 15,192, 83, 6, 33,129, 20, 6, 97, 33, 4, 6,245,128, 92, 6, 24, 0, 86, 6, 56,129, 44, 24,224, 25, 13, 48, 50, 82,180, 11,251,106,249, 59, 24, 82,175,223, 252,119, 76,134,120,236,149,250,247,115,254,145,173,174,168,180,255,107,195, 89, 24, 25, 48,131,192, 61, 48, 64, 10,176, 49, 64, 1,152, 50, 32, 8,140, 48, 16, 5,129, 51,196,187, 41,177, 23,138, 70, 50, 8, 10,242, 48,192, 3,248,226, 0, 20,100, 18, 96, 41, 96, 78,102, 7,201,122, 76,119, 20,137, 37,177, 15,132,224, 20, 17,191, 67,147,187,116,211, 41,169, 63,172,182,186,217,155,111,140,104,254, 111,181,184,144, 17,148, 21,101,166,227,100, 86, 85, 85, 85}; /* callback to request more mp3 data */ void callback(unsigned char** start, int* size) { *start = beep; /* give it the same frame again */ *size = sizeof(beep); } /****************************************************************************/ /* Turn sound on */ /****************************************************************************/ static void chip8_sound_on (void) { #ifndef SIMULATOR if (!is_playing) rb->mp3_play_pause(true); /* kickoff audio */ #endif } /****************************************************************************/ /* Turn sound off */ /****************************************************************************/ static void chip8_sound_off (void) { #ifndef SIMULATOR if (!is_playing) rb->mp3_play_pause(false); /* pause audio */ #endif } /****************************************************************************/ /* Update the display */ /****************************************************************************/ static void chip8_update_display(void) { int x, lcd_x, y, i; byte w; byte* row; /* frame buffer in hardware fomat */ unsigned char lcd_framebuf[CHIP8_HEIGHT/8][CHIP8_LCDWIDTH]; for (y=0;y> 1; if (chip8_display[x+(y*8+i)*CHIP8_WIDTH] != 0) { w += 128; } } #if (CHIP8_LCDWIDTH < 128) /* LCD_WIDTH between 112 and 127 */ if (x%8 == 1) { row[-1] |= w; /* overlay on */ } else #endif *row++ = w; } } #if defined(SIMULATOR) || (LCD_DEPTH > 1) rb->lcd_set_drawmode(DRMODE_SOLID); rb->lcd_mono_bitmap(lcd_framebuf[0], CHIP8_X, CHIP8_Y, CHIP8_LCDWIDTH, CHIP8_HEIGHT); rb->lcd_update(); #else rb->lcd_blit(lcd_framebuf[0], CHIP8_X, CHIP8_Y>>3, CHIP8_LCDWIDTH, CHIP8_HEIGHT>>3 , CHIP8_LCDWIDTH); #endif } static void chip8_keyboard(void) { int i; int button = rb->button_get(false); switch (button) { #ifdef CHIP8_RC_OFF case CHIP8_RC_OFF: #endif case CHIP8_OFF: /* Abort Emulator */ chip8_running = 0; break; case CHIP8_KEY2: chip8_virtual_keys[2] = 1; break; case CHIP8_KEY2 | BUTTON_REL: chip8_virtual_keys[2] = 0; break; case CHIP8_KEY4: chip8_virtual_keys[4] = 1; break; case CHIP8_KEY4 | BUTTON_REL: chip8_virtual_keys[4] = 0; break; case CHIP8_KEY6: chip8_virtual_keys[6] = 1; break; case CHIP8_KEY6 | BUTTON_REL: chip8_virtual_keys[6] = 0; break; case CHIP8_KEY8: chip8_virtual_keys[8] = 1; break; case CHIP8_KEY8 | BUTTON_REL: chip8_virtual_keys[8] = 0; break; case CHIP8_KEY5: chip8_virtual_keys[5] = 1; break; case CHIP8_KEY5 | BUTTON_REL: chip8_virtual_keys[5] = 0; break; #ifdef CHIP8_KEY1 case CHIP8_KEY1: chip8_virtual_keys[1] = 1; break; case CHIP8_KEY1 | BUTTON_REL: chip8_virtual_keys[1] = 0; break; #endif #ifdef CHIP8_KEY3 case CHIP8_KEY3: chip8_virtual_keys[3] = 1; break; case CHIP8_KEY3 | BUTTON_REL: chip8_virtual_keys[3] = 0; break; #endif #ifdef CHIP8_KEY7 case CHIP8_KEY7: chip8_virtual_keys[7] = 1; break; case CHIP8_KEY7 | BUTTON_REL: chip8_virtual_keys[7] = 0; break; #endif #ifdef CHIP8_KEY9 case CHIP8_KEY9: chip8_virtual_keys[9] = 1; break; case CHIP8_KEY9 | BUTTON_REL: chip8_virtual_keys[9] = 0; break; #endif default: if (rb->default_event_handler(button) == SYS_USB_CONNECTED) chip8_running = 2; /* indicates stopped because of USB */ break; } for(i=0; i<16; i++) { chip8_keys[i] = chip8_virtual_keys[chip8_keymap[i]]; } } /****************************************************************************/ /* Update keyboard and display, sync emulation with VDP clock */ /****************************************************************************/ static void chip8_interrupt (void) { unsigned long newtimer; unsigned long timer, runtime; chip8_update_display(); chip8_keyboard(); cycles ++; runtime = cycles * HZ / 50; timer = starttimer + runtime; newtimer = *rb->current_tick; if (TIME_AFTER(timer, newtimer)) { rb->sleep(timer - newtimer); } else { rb->yield(); } starttimer = newtimer - runtime; } static bool chip8_init(char* file) { int numread; int fd; int len; int i; fd = rb->open(file, O_RDONLY); if (fd==-1) { rb->lcd_puts(0, 6, "File Error."); return false; } numread = rb->read(fd, chip8_mem+0x200, 4096-0x200); if (numread==-1) { rb->lcd_puts(0, 6, "I/O Error."); return false; } rb->close(fd); /* is there a c8k file (chip8 keys) ? */ for(i=0; i<16; i++) { chip8_virtual_keys[i] = 0; chip8_keymap[i] = i; } len = rb->strlen(file); file[len-2] = '8'; file[len-1] = 'k'; fd = rb->open(file, O_RDONLY); if (fd!=-1) { rb->lcd_puts(0, 6, "File&Keymap OK."); numread = rb->read(fd, chip8_keymap, 16); rb->close(fd); } else { rb->lcd_puts(0, 6, "File OK."); } for(i=0; i<16; i++) { if (chip8_keymap[i] >= '0' && chip8_keymap[i] <= '9') chip8_keymap[i] -= '0'; else if (chip8_keymap[i] >= 'A' && chip8_keymap[i] <= 'F') chip8_keymap[i] -= 'A' - 10; else if (chip8_keymap[i] >= 'a' && chip8_keymap[i] <= 'f') chip8_keymap[i] -= 'a' - 10; else chip8_keymap[i] %= 16; } return true; } bool chip8_run(char* file) { int ok; rb->lcd_clear_display(); rb->lcd_puts(0, 0, "SChip8 Emulator"); rb->lcd_puts(0, 1, " (c) by "); rb->lcd_puts(0, 2, "Marcel de Kogel"); rb->lcd_puts(0, 3, " Rockbox: "); rb->lcd_puts(0, 4, " Blueloop/Fdy "); rb->lcd_puts(0, 5, "---------------"); ok = chip8_init(file); rb->lcd_update(); rb->sleep(HZ*1); if (!ok) return false; rb->lcd_clear_display(); #if (CHIP8_X > 0) && (CHIP8_Y > 0) rb->lcd_drawrect(CHIP8_X-1,CHIP8_Y-1,CHIP8_LCDWIDTH+2,CHIP8_HEIGHT+2); #endif rb->lcd_update(); #ifndef SIMULATOR /* init sound */ is_playing = rb->mp3_is_playing(); /* would we disturb playback? */ if (!is_playing) /* no? then we can make sound */ { /* prepare */ rb->mp3_play_data(beep, sizeof(beep), callback); } #endif starttimer = *rb->current_tick; chip8_iperiod=15; cycles = 0; chip8(); if (!ok) { rb->splash(HZ, true, "Error"); return false; } #ifndef SIMULATOR if (!is_playing) { /* stop it if we used audio */ rb->mp3_play_stop(); /* Stop audio playback */ } #endif if (chip8_running == 3) { /* unsupported instruction */ rb->splash(HZ, true, "Error: Unsupported" #ifndef CHIP8_SUPER " CHIP-8 instruction. (maybe S-CHIP)" #else " (S)CHIP-8 instruction." #endif ); return false; } return true; } /***************** Plugin Entry Point *****************/ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { char* filename; rb = api; /* copy to global api pointer */ if (parameter == NULL) { rb->splash(HZ, true, "Play a .ch8 file!"); return PLUGIN_ERROR; } else { filename = (char*) parameter; } /* now go ahead and have fun! */ if (chip8_run(filename)) if (chip8_running == 0) return PLUGIN_OK; else return PLUGIN_USB_CONNECTED; else return PLUGIN_ERROR; } #endif /* #ifdef HAVE_LCD_BITMAP */