Chip 8 emulator made using gint
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

374 lines
10 KiB

#include "Chip8.h"
#include "opcode.h"
extern struct Chip8 chip8;
uint16_t opcode;
/*
Chip8 Keyboard and Casio keyboard
_________ | ___________
|1|2|3|C| | |7|8|9|DEL|
--------- | -----------
|4|5|6|D| | |4|5|6| x |
--------- | -----------
|7|8|9|E| | |1|2|3| + |
--------- | -----------
|A|0|B|F| | |0|.|E|(-)| (that's the little E for *10^)
*/
const int Chip8Kb2Casio[0xF+1] =
{
KEY_DOT, KEY_7, KEY_8, KEY_9, KEY_4, KEY_5, KEY_6, KEY_1, KEY_2, KEY_3, KEY_0, KEY_EXP, KEY_DEL, KEY_MUL, KEY_ADD, KEY_NEG
};
/*
keycode are progressing from right to left and bottom to top with gap in it, so it will need a magic function to map the key we are interrested in [0x11-0x44] to [0x0-0xF]
Casiokeycode, Casiokeycode after magic function Keyboard and Chip8 keyboard
_____________ | _________ | _________
|41|42|43|44| | |C|D|E|F| | |1|2|3|C|
------------- | --------- | ---------
|31|32|33|34| | |8|9|A|B| | |4|5|6|D|
------------- | --------- | ---------
|21|22|23|24| | |4|5|6|7| | |7|8|9|E|
------------- | --------- | ---------
|11|12|13|14| | |0|1|2|3| | |A|0|B|F|
the magic function consist to first substracting 0xC from keycode for each "tens" and then substract 0x5
new_kcode = old_kcode - (0xC * (old_kcode / 0x10)) - 0x5
care should be taken that this is only used on those 16 keys aboves
*/
const int CasioKb2Chip8[0xF+1] =
{
0xA, 0x0, 0xB, 0xF, 0x7, 0x8, 0x9, 0xE, 0x4, 0x5, 0x6, 0xD, 0x1, 0x2, 0x3, 0xC
};
void (*Chip8Table[0xF+1]) ()=
{
op0table, op1NNN, op2NNN, op3XKK, op4XKK, op5XY0, op6XKK, op7XKK, op8table, op9XY0, opANNN, opBNNN, opCXKK, opDXYN, opEtable, opFtable
};
void (*Chip8Table0[0xF+1]) ()=
{
op00E0, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, op00EE, uninplemented
};
void (*Chip8Table8[0xF+1]) ()=
{
op8XY0, op8XY1, op8XY2, op8XY3, op8XY4, op8XY5, op8XY6, op8XY7, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, op8XYE, uninplemented
};
void (*Chip8TableE[0xF+1]) ()=
{
uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, opEX9E, opEXA1, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented
};
void (*Chip8TableF[0xF+1]) ()=
{
uninplemented, uninplemented, uninplemented, opFX33, uninplemented, opF5table, uninplemented, opFX07, opFX18, opFX29, opFX0A, uninplemented, uninplemented, uninplemented, opFX1E, uninplemented
};
void (*Chip8TableF5[0xF+1]) ()=
{
uninplemented, opFX15, uninplemented, uninplemented, uninplemented, opFX55, opFX65, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented, uninplemented
};
void execute()
{
char op[5]= "0000";
char address[4]= "000";
char addressI[]= "0000";
char const hexa[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
opcode = (((uint16_t) chip8.memory[chip8.PC]) << 8) + (chip8.memory[chip8.PC+1]);
for(int i=0; i<4;i++)
{
op[i] =hexa[((opcode & (0xF000 >> (4*i) ))>>(4*(3-i)))] ;
addressI[i] =hexa[((chip8.I & (0xF000 >> (4*i) ))>>(4*(3-i)))] ;
}
for(int i=0; i<3;i++)
{
address[i] =hexa[((chip8.PC & (0xF00 >> (4*i) ))>>(4*(2-i)))] ;
}
//dclear(C_WHITE);
dprint(1, 9, C_WHITE, C_BLACK, "%s", op);
dprint(1, 17, C_WHITE, C_BLACK, "%s", address);
dprint(1, 25, C_WHITE, C_BLACK, "%s", addressI);
dupdate();
Chip8Table[(opcode&0xF000)>>12]();
//while(getkey().key != KEY_EXE) {}
chip8.PC +=2;
}
void op0table()
{
Chip8Table0[opcode&0x000F]();
}
void op00E0() //clear screen
{
for (int i=0; i<32; i++) chip8.vram[i]=0; //clear vram
dclear(C_BLACK); //clear gint vram
dupdate();
}
void op00EE() //return
{
chip8.PC=chip8.stack[chip8.SP--]; //move and decrement stak pointeur
if(chip8.SP == UINT8_MAX) //check overflow in case too much return
{
//TODO message 4 2 much return
}
}
void op1NNN() //jump to NNN
{
chip8.PC= NNN - 2; //PC get incremented afterward
}
void op2NNN() //call function at NNN
{
if(chip8.SP == STACK_SIZE - 1) //where gonna buffer overflow
{
//TODO message 4 2 much call
}
chip8.stack[++chip8.SP] = chip8.PC; //increment stack pointeur and store address on top
chip8.PC = NNN - 2; //PC get incremented afterward
}
void op3XKK() //skip if reg[X] equal KK
{
if(chip8.regist[X] == KK) chip8.PC += 2;
}
void op4XKK() //skip if reg[X] not equal KK
{
if(chip8.regist[X] != KK) chip8.PC += 2;
}
void op5XY0() //skip if reg[X] equal reg[Y]
{
if(chip8.regist[X] == chip8.regist[Y]) chip8.PC += 2;
}
void op6XKK() //Load KK in reg[X]
{
chip8.regist[X] = KK;
}
void op7XKK() //reg[X] += KK, no carry
{
chip8.regist[X] = chip8.regist[X] + KK;
}
void op8table()
{
Chip8Table8[opcode&0x000F]();
}
void op8XY0() //load reg[Y] in reg [X]
{
chip8.regist[X] = chip8.regist[Y];
}
void op8XY1() //OR reg[X] and reg[Y] in reg [X]
{
chip8.regist[X] |= chip8.regist[Y];
}
void op8XY2() //AND reg[X] and reg[Y] in reg [X]
{
chip8.regist[X] &= chip8.regist[Y];
}
void op8XY3() //XOR reg[X] and reg[Y] in reg [X]
{
chip8.regist[X] ^= chip8.regist[Y];
}
void op8XY4() //add reg[X] and reg[Y] in reg[X] whit carry
{
chip8.regist[0xF] = (UINT8_MAX - chip8.regist[X] < chip8.regist[Y]);//carry
chip8.regist[X] += chip8.regist[Y];
}
void op8XY5() //reg[X] = reg[X] -reg[Y] with NOT borrow
{
chip8.regist[0xF] = (chip8.regist[X] > chip8.regist[Y]);
chip8.regist[X] -= chip8.regist[Y];
}
void op8XY6() //rotate reg[X] -> and save the least significant bit in the carry flag
{
chip8.regist[0xF] = chip8.regist[X] & 0x1;
chip8.regist[X] >>= 1;
}
void op8XY7() //reg[X] = reg[Y] - reg[X] with NOT borrow
{
chip8.regist[0xF] = (chip8.regist[X] < chip8.regist[Y]);
chip8.regist[X] = chip8.regist[Y] - chip8.regist[X];
}
void op8XYE() //rotate reg[X] <- and save the least significant bit in the carry flag
{
chip8.regist[0xF] = (chip8.regist[X] & 0x80) >> 7;;
chip8.regist[X] <<= 1;
}
void op9XY0() //skip if reg[X] equal reg[Y]
{
if(chip8.regist[X] != chip8.regist[Y]) chip8.PC += 2;
}
void opANNN() //load NNN into I
{
chip8.I = NNN;
}
void opBNNN() //jump to address regist[0]+NNN
{
chip8.PC= chip8.regist[0] + NNN - 2; //PC get incremented afterward
}
void opCXKK() //set regist[X] = random(0,255) AND KK
{
chip8.regist[X] = 0xFF & KK; //random 0xFF determined by a fair D6 throw... JK, TODO
}
//xor the sprite of size 8*N at adress I on the screen at position (regist[X],regist[Y])
//if a pixel is erased, set set carry flag.
//sprite are supposed to warp round the screen, so it will get messy
void opDXYN()
{
uint64_t old_ram; //to save old state in order to know if pixel got erased
uint64_t new_ram;
uint64_t sprite_row; //temporary storage for the
uint8_t Xpos = chip8.regist[X]% 64;
uint8_t Ypos = chip8.regist[Y]% 32;
chip8.regist[0xF] = 0; //reseting flag
for (int i = 0; i<N; i++)
{
sprite_row = ((uint64_t) chip8.memory[chip8.I + i])<<56; //load row and put sprite in rigt 'cause it's easier for me afterward
sprite_row = (sprite_row >> Xpos)|(sprite_row<<(64-Xpos));//move the sprite in the correct collums with wraping
old_ram = chip8.vram[(Ypos+i)%32];
new_ram = old_ram ^ sprite_row;
chip8.regist[0xF] |= (((new_ram^old_ram)&old_ram) != 0);
chip8.vram[(Ypos+i)%32] = new_ram;
}
// drawing routine, not optimized at all TODO
dclear(C_BLACK);
for(int x = 0; x < 64; x++)
{
for(int y = 0; y < 32; y++)
{
if((chip8.vram[y]>>(63-x))&0x1) drect(2*x, 2*y, 2*x+1, 2*y+1, C_WHITE);
}
}
dprint(1,33, C_WHITE, C_BLACK, "%d %d", Xpos, Ypos);
dupdate();
}
void opEtable()
{
Chip8TableE[(opcode&0x00F0)>>4]();
}
void opEX9E() //skip if regist[X] equal value of key pressed
{
if(getkey().key == Chip8Kb2Casio[chip8.regist[X] & 0xF]) chip8.PC += 2;
}
void opEXA1() //skip if regist[X] not equal value of key pressed
{
if(getkey().key != Chip8Kb2Casio[chip8.regist[X] & 0xF]) chip8.PC += 2;
}
void opFtable()
{
Chip8TableF[opcode&0x000F]();
}
void opF5table()
{
Chip8TableF5[(opcode&0x00F0)>>4]();
}
void opFX07() //set regist[X] = DelayTimer
{
chip8.regist[X] = chip8.DT;
}
void opFX0A() //set regist[X] = keycode of pressed key (wait for press if not any)
{
uint kcode = getkey().key;
if(kcode % 0xF < 5 && kcode / 0x10 < 5) chip8.regist[X] = CasioKb2Chip8[kcode - (0xC * (kcode/0x10)) - 0x5]; //see .h for explanation
}
void opFX15() //set DelayTimer = regist[X]
{
chip8.DT = chip8.regist[X];
}
void opFX18() //set SoundTimer = regist[X]
{
chip8.ST = chip8.regist[X];
}
void opFX1E() //set I = I + regist[X]
{
chip8.I += chip8.regist[X];
}
void opFX29() //set I to the location to draw the char stored in regist[X]
{
chip8.I = 5 * (chip8.regist[X] & 0xF);//char bitmaps are 5 bytes
//&0xF 'cause not precised what to do if regist[x] is larger than 15
}
void opFX33() //store BCD representation of regist[X] in the adress I(hundreds), I+1(tens), I+2(digits)
{
chip8.memory[chip8.I + 2] = chip8.regist[X] %10;
chip8.memory[chip8.I + 1] = (chip8.regist[X] /10) % 10;
chip8.memory[chip8.I] = chip8.regist[X] / 100;
}
void opFX55() //store regist[0]~regist[X] into memory adress I~I+X
{
for(int i=0; i<=X;i++) chip8.memory[chip8.I+i] = chip8.regist[i];
}
void opFX65() //load regist[0]~regist[X] from memory adress I~I+X
{
for(int i=0; i<=X;i++) chip8.regist[i] = chip8.memory[chip8.I+i];
}
void uninplemented()
{
char op[5]= "0000";
char address[4] = "000";
char const hexa[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
for(int i=0; i<4;i++)
{
op[i] =hexa[((opcode & (0xF000 >> (4*i) ))>>(4*(3-i)))] ;
}
for(int i=0; i<3;i++)
{
address[i] =hexa[((chip8.PC & (0xF00 >> (4*i) ))>>(4*(2-i)))] ;
}
dclear(C_WHITE);
dprint(1, 1, C_WHITE, C_BLACK, "%s unimplemented", op);
dprint(1, 9, C_WHITE, C_BLACK, "Position: %s", address);
dupdate();
while(getkey().key != KEY_EXIT) {}
}