lots of updates
This commit is contained in:
parent
84aa309a29
commit
19b7157a38
12
README.md
12
README.md
|
@ -48,12 +48,12 @@ yacemu [PATH_TO_YOUR_ROM] turbo
|
|||
- [x] Graphics
|
||||
- [x] Corax+ Required Instructions
|
||||
- [x] Proper Flag Handling
|
||||
- [ ] Working Input
|
||||
- [ ] Rest of Instructions
|
||||
- [ ] Tetris Working Running
|
||||
- [ ] Extended instruction set (MEGACHIP, SUPER CHIP-48)
|
||||
- [ ] Add my own custom instructions
|
||||
- [ ] Write a ROM that uses the above instructions
|
||||
- [x] Working Input
|
||||
- [x] More Instructions
|
||||
- [x] Tetris Working Running
|
||||
- 1/2 Pass Quirks Test (DispQuirk is a bit touchy)
|
||||
- [x] Add beeper (instead of sound, screen becomes red)
|
||||
- [ ] Rest of Instructions (I believe a few are missing)
|
||||
|
||||
### Screenshots
|
||||
|
||||
|
|
263
main.c
263
main.c
|
@ -5,6 +5,7 @@
|
|||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define BASE_ADDR 0x200
|
||||
#define FONTSET_SIZE 80
|
||||
|
@ -25,6 +26,11 @@ struct emu_state {
|
|||
uint16_t opcode;
|
||||
};
|
||||
|
||||
struct key_event {
|
||||
uint8_t keycode;
|
||||
uint8_t down;
|
||||
};
|
||||
|
||||
uint8_t fontset[FONTSET_SIZE] = {
|
||||
0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
|
||||
0x20, 0x60, 0x20, 0x20, 0x70, // 1
|
||||
|
@ -44,29 +50,102 @@ uint8_t fontset[FONTSET_SIZE] = {
|
|||
0xF0, 0x80, 0xF0, 0x80, 0x80 // F
|
||||
};
|
||||
|
||||
void load_rom(char *file_name, struct emu_state *emulator_state) {
|
||||
void get_key(struct key_event* kev) {
|
||||
SDL_Event event;
|
||||
|
||||
kev->down = 2;
|
||||
kev->keycode = 255;
|
||||
while(SDL_PollEvent(&event)) {
|
||||
switch(event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
kev->down = 1;
|
||||
case SDL_KEYUP:
|
||||
kev->down = kev->down == 1 ? 1 : 0;
|
||||
switch(event.key.keysym.sym) {
|
||||
case SDLK_0:
|
||||
kev->keycode = 0;
|
||||
break;
|
||||
case SDLK_1:
|
||||
kev->keycode = 1;
|
||||
break;
|
||||
case SDLK_2:
|
||||
kev->keycode = 2;
|
||||
break;
|
||||
case SDLK_3:
|
||||
kev->keycode = 3;
|
||||
break;
|
||||
case SDLK_4:
|
||||
kev->keycode = 4;
|
||||
break;
|
||||
case SDLK_5:
|
||||
kev->keycode = 5;
|
||||
break;
|
||||
case SDLK_6:
|
||||
kev->keycode = 6;
|
||||
break;
|
||||
case SDLK_7:
|
||||
kev->keycode = 7;
|
||||
break;
|
||||
case SDLK_8:
|
||||
kev->keycode = 8;
|
||||
break;
|
||||
case SDLK_9:
|
||||
kev->keycode = 9;
|
||||
break;
|
||||
case SDLK_a:
|
||||
kev->keycode = 10;
|
||||
break;
|
||||
case SDLK_b:
|
||||
kev->keycode = 11;
|
||||
break;
|
||||
case SDLK_c:
|
||||
kev->keycode = 12;
|
||||
break;
|
||||
case SDLK_d:
|
||||
kev->keycode = 13;
|
||||
break;
|
||||
case SDLK_e:
|
||||
kev->keycode = 14;
|
||||
break;
|
||||
case SDLK_f:
|
||||
kev->keycode = 15;
|
||||
break;
|
||||
case SDLK_p:
|
||||
kev->keycode = 254;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t load_rom(char *file_name, struct emu_state *emulator_state) {
|
||||
printf("Loading ROM %s\n", file_name);
|
||||
|
||||
FILE *file = fopen(file_name, "r");
|
||||
|
||||
if (file == NULL) {
|
||||
printf("Error reading ROM file.");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fseek(file, 0L, SEEK_END);
|
||||
int rom_size = ftell(file);
|
||||
uint16_t rom_size = ftell(file);
|
||||
fseek(file, 0L, SEEK_SET);
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("ROM size is %d bytes\n", rom_size);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < rom_size; i++) {
|
||||
for (uint16_t i = 0; i < rom_size; i++) {
|
||||
emulator_state->memory[BASE_ADDR + i] = (uint8_t)fgetc(file);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return rom_size;
|
||||
}
|
||||
|
||||
// SYS is a deprecated instruction, basically a NOP
|
||||
|
@ -113,14 +192,10 @@ void instr_SE(struct emu_state *emulator_state) {
|
|||
uint8_t register_val = emulator_state->registers[reg];
|
||||
uint8_t byte_val = emulator_state->opcode & 0x00FFU;
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("SE R[%#x] %d (reg_val: %d, skip?: %d)\n", reg, byte_val, register_val,
|
||||
register_val == byte_val);
|
||||
#endif
|
||||
|
||||
if (byte_val == register_val) {
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
|
@ -196,6 +271,7 @@ void instr_LD_reg(struct emu_state *emulator_state) {
|
|||
void instr_OR_reg(struct emu_state *emulator_state) {
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] |=
|
||||
emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4];
|
||||
emulator_state->registers[0xF] = 0;
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
|
@ -203,6 +279,7 @@ void instr_OR_reg(struct emu_state *emulator_state) {
|
|||
void instr_XOR_reg(struct emu_state *emulator_state) {
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] ^=
|
||||
emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4];
|
||||
emulator_state->registers[0xF] = 0;
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
|
@ -214,8 +291,7 @@ void instr_ADD_reg(struct emu_state *emulator_state) {
|
|||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] +=
|
||||
emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4];
|
||||
|
||||
emulator_state->registers[0xF] =
|
||||
(prev_value >
|
||||
emulator_state->registers[0xF] = (prev_value >
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8])
|
||||
? 1
|
||||
: 0;
|
||||
|
@ -245,7 +321,7 @@ void instr_SHR_reg(struct emu_state *emulator_state) {
|
|||
uint8_t reg_x_val =
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8];
|
||||
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] >>= 1;
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] = emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4] >> 1;
|
||||
|
||||
if ((reg_x_val & 0x01) == 1) {
|
||||
emulator_state->registers[0xF] = 1;
|
||||
|
@ -261,7 +337,7 @@ void instr_SHL_reg(struct emu_state *emulator_state) {
|
|||
uint8_t reg_x_val =
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8];
|
||||
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] <<= 1;
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] = emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4] << 1;
|
||||
|
||||
if (((reg_x_val & 0xA0) >> 7) == 1) {
|
||||
emulator_state->registers[0xF] = 1;
|
||||
|
@ -282,7 +358,7 @@ void instr_SUBN_reg(struct emu_state *emulator_state) {
|
|||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] =
|
||||
reg_y_val - reg_x_val;
|
||||
|
||||
if (reg_y_val > reg_x_val) {
|
||||
if (reg_y_val >= reg_x_val) {
|
||||
emulator_state->registers[0xF] = 1;
|
||||
} else {
|
||||
emulator_state->registers[0xF] = 0;
|
||||
|
@ -302,6 +378,7 @@ void instr_LD_reg_I(struct emu_state *emulator_state) {
|
|||
emulator_state->registers[i] =
|
||||
emulator_state->memory[emulator_state->index + i];
|
||||
}
|
||||
emulator_state->index++;
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
|
@ -311,6 +388,7 @@ void instr_LD_I_reg(struct emu_state *emulator_state) {
|
|||
emulator_state->memory[emulator_state->index + i] =
|
||||
emulator_state->registers[i];
|
||||
}
|
||||
emulator_state->index++;
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
|
@ -328,6 +406,7 @@ void instr_LD_B_reg(struct emu_state *emulator_state) {
|
|||
void instr_AND_reg(struct emu_state *emulator_state) {
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] &=
|
||||
emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4];
|
||||
emulator_state->registers[0xF] = 0;
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
|
@ -351,6 +430,7 @@ void instr_DRW(struct emu_state *emulator_state) {
|
|||
for (unsigned int c = 0; c < 8; c++) {
|
||||
uint32_t *screen_pixel =
|
||||
&emulator_state->screen[(r + y_cord) * SCREEN_WIDTH + (x_cord + c)];
|
||||
if(r+y_cord >= SCREEN_HEIGHT || x_cord + c >= SCREEN_WIDTH) continue;
|
||||
uint8_t sprite_pixel =
|
||||
emulator_state->memory[emulator_state->index + r] & (0x80U >> c);
|
||||
if (sprite_pixel) {
|
||||
|
@ -365,14 +445,79 @@ void instr_DRW(struct emu_state *emulator_state) {
|
|||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
void instr_RND_reg(struct emu_state *emulator_state) {
|
||||
uint8_t rand_num = rand() % 256;
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] =
|
||||
(emulator_state->opcode & 0x00FFU) & rand_num;
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
void instr_NOP(struct emu_state *emulator_state) {
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
void instr_SKP_reg(struct emu_state *emulator_state) {
|
||||
uint8_t wanted_key = emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8];
|
||||
if (emulator_state->keypad[wanted_key] == 1) {
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
void instr_SKNP_reg(struct emu_state *emulator_state) {
|
||||
uint8_t unwanted_key = emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8];
|
||||
if (emulator_state->keypad[unwanted_key] == 0) {
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
void instr_LD_DT_reg(struct emu_state *emulator_state) {
|
||||
emulator_state->delay_timer = emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8];
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
void instr_LD_ST_reg(struct emu_state *emulator_state) {
|
||||
emulator_state->sound_timer = emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8];
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
void instr_LD_reg_DT(struct emu_state *emulator_state) {
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] = emulator_state->delay_timer;
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
void instr_LD_F_reg(struct emu_state *emulator_state) {
|
||||
emulator_state->index = fontset[emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] = emulator_state->delay_timer];
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
void instr_JP_reg0(struct emu_state *emulator_state) {
|
||||
emulator_state->program_counter = emulator_state->opcode & 0x0FFFU;
|
||||
}
|
||||
|
||||
void instr_LD_reg_K(struct emu_state *emulator_state) {
|
||||
struct key_event kev;
|
||||
kev.keycode = 255;
|
||||
kev.down = 2;
|
||||
while(kev.keycode >= 254U && kev.down == 2) {
|
||||
SDL_FlushEvents(SDL_KEYDOWN, SDL_KEYUP);
|
||||
get_key(&kev);
|
||||
}
|
||||
emulator_state->delay_timer = 0;
|
||||
emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] = kev.keycode;
|
||||
emulator_state->program_counter += 2;
|
||||
}
|
||||
|
||||
void *get_op_func(uint16_t opcode) {
|
||||
// TODO: Move to lookup table
|
||||
if (opcode == 0x00E0U) {
|
||||
return &instr_CLS;
|
||||
} else if (opcode == 0x00EEU) {
|
||||
return &instr_RET;
|
||||
} else if ((opcode & 0xF000U) == 0x0000U) {
|
||||
return NULL;
|
||||
} else if ((opcode & 0xF000U) == 0) {
|
||||
// "This instruction is only used on the old computers on which Chip-8 was originally implemented. It is ignored by modern interpreters."
|
||||
return &instr_NOP;
|
||||
} else if ((opcode & 0xF000U) == 0x1000U) {
|
||||
return &instr_JP;
|
||||
} else if ((opcode & 0xF000U) == 0x2000U) {
|
||||
|
@ -421,20 +566,40 @@ void *get_op_func(uint16_t opcode) {
|
|||
return &instr_DRW;
|
||||
} else if ((opcode & 0xF00FU) == 0x8002U) {
|
||||
return &instr_AND_reg;
|
||||
} else if ((opcode & 0xF000U) == 0xC000U) {
|
||||
return &instr_RND_reg;
|
||||
} else if ((opcode & 0xF0FFU) == 0xE09EU) {
|
||||
return &instr_SKP_reg;
|
||||
} else if ((opcode & 0xF0FFU) == 0xE0A1U) {
|
||||
return &instr_SKNP_reg;
|
||||
} else if ((opcode & 0xF0FFU) == 0xF015U) {
|
||||
return &instr_LD_DT_reg;
|
||||
} else if ((opcode & 0xF0FFU) == 0xF007U) {
|
||||
return &instr_LD_reg_DT;
|
||||
} else if ((opcode & 0xF0FFU) == 0xF029U) {
|
||||
return &instr_LD_F_reg;
|
||||
} else if ((opcode & 0xF000U) == 0xB000U) {
|
||||
return &instr_JP_reg0;
|
||||
} else if ((opcode & 0xF0FFU) == 0xF00AU) {
|
||||
return &instr_LD_reg_K;
|
||||
} else if ((opcode & 0xF0FFU) == 0xF018U) {
|
||||
return &instr_LD_ST_reg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct emu_state state;
|
||||
load_rom(argv[1], &state);
|
||||
uint16_t rom_size = load_rom(argv[1], &state);
|
||||
state.program_counter = BASE_ADDR;
|
||||
memset(state.screen, 0, sizeof(state.screen));
|
||||
|
||||
SDL_Init(SDL_INIT_EVERYTHING);
|
||||
SDL_Window *window =
|
||||
SDL_CreateWindow("Chip-8 Emulator", // creates a window
|
||||
SDL_CreateWindow("Yet Another Chip-8 Emulator", // creates a window
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
SCREEN_WIDTH * 10, SCREEN_HEIGHT * 10, 0);
|
||||
SDL_Renderer *renderer =
|
||||
|
@ -451,7 +616,60 @@ int main(int argc, char *argv[]) {
|
|||
"extremely fast!\n");
|
||||
}
|
||||
|
||||
int is_supported = 1;
|
||||
for (uint16_t i = 0; i < rom_size ; i+=2) {
|
||||
uint16_t op = (((uint16_t)state.memory[BASE_ADDR + i]) << 8) |
|
||||
(uint16_t)state.memory[BASE_ADDR + i + 1];
|
||||
void (*op_func)(struct emu_state *) = get_op_func(op);
|
||||
if (op_func == NULL) {
|
||||
is_supported = 0;
|
||||
printf("Invalid ROM (Unsupported Instruction 0x%04X) at byte %d\n", op, i);
|
||||
}
|
||||
}
|
||||
|
||||
if(!is_supported && 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pause = 0;
|
||||
|
||||
struct timeval last, cur;
|
||||
gettimeofday(&cur, NULL);
|
||||
for (long i = 0; i != -1; i++) {
|
||||
if (state.delay_timer > 0 || state.sound_timer > 0) {
|
||||
gettimeofday(&cur, NULL);
|
||||
long msec = (cur.tv_sec - last.tv_sec) * 1000000 + cur.tv_usec - last.tv_usec;
|
||||
if (msec > 16666) {
|
||||
last = cur;
|
||||
if (state.sound_timer > 0)
|
||||
state.sound_timer -= 1 * (msec/16666);
|
||||
if (state.delay_timer > 0)
|
||||
state.delay_timer -= 1 * (msec/16666);
|
||||
}
|
||||
}
|
||||
|
||||
struct key_event kev;
|
||||
get_key(&kev);
|
||||
|
||||
if (kev.keycode == 254 && !kev.down) {
|
||||
pause = !pause;
|
||||
}
|
||||
if(pause) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (kev.keycode != 255) {
|
||||
printf("%d: %d / \n", kev.keycode, kev.down);
|
||||
state.keypad[kev.keycode] = kev.down;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 16; i++) {
|
||||
printf("%d: %d / ", i, state.keypad[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
|
||||
state.opcode = (((uint16_t)state.memory[state.program_counter]) << 8) |
|
||||
(uint16_t)state.memory[state.program_counter + 1];
|
||||
void (*op_func)(struct emu_state *) = get_op_func(state.opcode);
|
||||
|
@ -466,13 +684,18 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
op_func(&state);
|
||||
|
||||
if (state.sound_timer > 0)
|
||||
SDL_SetTextureColorMod(texture, 255, 0, 0);
|
||||
else
|
||||
SDL_SetTextureColorMod(texture, 255, 255, 255);
|
||||
|
||||
SDL_UpdateTexture(texture, NULL, state.screen,
|
||||
sizeof(state.screen[0]) * SCREEN_WIDTH);
|
||||
sizeof(state.screen[0]) * SCREEN_WIDTH);
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
usleep(turbo_mode ? 100 : 16000);
|
||||
usleep(turbo_mode ? 100 : 2000);
|
||||
|
||||
if (SDL_QuitRequested()) {
|
||||
printf("Received Quit from SDL. Goodbye!");
|
||||
|
|
Loading…
Reference in a new issue