fx9860-emulator-playground/fx9860-emulator/main.c

194 lines
5.0 KiB
C

#include "headers/main.h"
// Allocate memory and load the entire content of a G1A file (including the header).
// Returns only if everything went well, otherwise stops the program
uint8_t* load_g1a(const char* file_name, uint32_t* g1a_size) {
// Load file
FILE* file = fopen(file_name, "rb");
if (file == NULL) critical_error("Could not load G1A file");
// Get file size
fseek(file, 0, SEEK_END);
uint32_t file_size = ftell(file);
*g1a_size = file_size;
if (file_size == -1) critical_error("Could not get G1A file size");
// Allocate memory
uint8_t* file_content = malloc(file_size);
if (file_content == NULL) critical_error("Could not allocate memory for G1A file");
// Read file content
fseek(file, 0, SEEK_SET);
fread(file_content, 1, file_size, file);
fclose(file);
printf("File size: %d\n", file_size);
return file_content;
}
// Initializes the Memory component
memory_t* init_memory(uint8_t* g1a_content, uint32_t g1a_size) {
memory_t* mem = calloc(1, sizeof(memory_t));
if (mem == NULL) critical_error("Could not allocate memory for Memory (lol)");
mem->rom = g1a_content;
mem->rom_size = g1a_size;
mem->mallocs = calloc(1, sizeof(uint8_t));
mem->tmp = calloc(1024, sizeof(uint8_t));
// Initialize keyboard rows (1 = key not pressed)
for (int i = 0; i < 10; i++)
mem->keyboard[i] = 0xFF;
return mem;
}
// Initializes the Display component
display_t* init_display(memory_t* memory) {
display_t* disp = calloc(1, sizeof(display_t));
if(disp == NULL) critical_error("Could not allocate memory for Display");
disp->cursor.row = 1;
disp->cursor.col = 1;
return disp;
}
// Initializes the CPU
cpu_t* init_cpu(memory_t* mem, display_t* disp) {
cpu_t* cpu = calloc(1, sizeof(cpu_t));
if (cpu == NULL) critical_error("Could not allocate memory for CPU");
cpu->pc = PC_PROGRAM_START;
cpu->pr = PR_INIT_VALUE;
cpu->sr = SR_INIT_VALUE;
cpu->r[15] = RAM_START + RAM_SIZE;
cpu->mem = mem;
cpu->disp = disp;
return cpu;
}
// Run the next instruction from the position of PC.
// Handles syscalls, program finish and instruction decoding.
void run_next_instruction(cpu_t* cpu) {
// Current instruction address
uint32_t address = cpu->pc;
// Handle system calls
if (address == SYSCALL_ADDRESS) {
run_syscall(cpu);
return;
}
// Handle program execution finish
if (address == PR_INIT_VALUE) {
printf("Program execution finished!\n");
cpu->isExecutionFinished = 1;
return;
}
if (address % 2 != 0) {
critical_error("Address is not a multiple of 2 bytes! (0x%08X)", address);
}
// Extract 16bits instruction code
uint16_t instruction = (mem_read(cpu, address, 1) << 8) | mem_read(cpu, address + 1, 1);
// Get a pointer to the function implementing the current instruction
get_instruction_impl(instruction)
(cpu, instruction); // Actually run the instruction
}
int main() {
// Load G1A
uint32_t g1a_size;
uint8_t* g1a_content = load_g1a("JJSH3.G1A", &g1a_size);
// Init CPU, Memory and Display
memory_t* mem = init_memory(g1a_content, g1a_size);
display_t* disp = init_display(mem);
cpu_t* cpu = init_cpu(mem, disp);
// (Temporary) load the ASCII font texture
cpu->asciiTexture = load_character_set("U+0020.png");
cpu->printMiniTexture = load_character_set("PrintMini.bmp");
#ifdef USE_EMSCRIPT
init_loop_html(cpu);
#else
init_loop_c(cpu);
#endif
return 0;
}
// Init the HTML version of the emulator
void init_loop_html(cpu_t* cpu) {
#ifdef USE_EMSCRIPT
cpu->instruction_per_frame = 10000;
// Pass useful pointers to javascript
EM_ASM({
window.lcdImage = $0;
window.keyboardPointer = $1;
window.instructionPerFrame = $2;
window.instructionCount = $3;
window.cpuPointer = $4;
window.initGUI();
},
&cpu->disp->vram,
&cpu->mem->keyboard,
&cpu->instruction_per_frame,
&cpu->instruction_count,
cpu
);
// Setup the main animation frame loop
emscripten_set_main_loop_arg(main_loop_html, cpu, -1, 0);
#endif
}
// Main loop for the HTML version of the emulator
void main_loop_html(void* arg) {
cpu_t* cpu = (cpu_t*)arg;
if (cpu->isExecutionFinished) return;
for (int i = 0; i < cpu->instruction_per_frame; i++) {
run_next_instruction(cpu);
cpu->instruction_count++;
if (cpu->isExecutionFinished) break;
}
}
// Run the console version of the emulator
// (no display - VRAM dump at the end)
void init_loop_c(cpu_t* cpu) {
// Run up to 10M instructions
for (int i = 0; i < 1e7; i++) {
run_next_instruction(cpu);
cpu->instruction_count++;
if (cpu->isExecutionFinished) break;
}
vram_debug(cpu);
free(cpu->mem->mallocs);
free(cpu->mem->tmp);
free(cpu->mem->rom);
free(cpu->mem);
free(cpu->asciiTexture);
free(cpu->printMiniTexture);
free(cpu);
}