194 lines
5.0 KiB
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);
|
|
} |