241 lines
6.6 KiB
C
241 lines
6.6 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));
|
|
|
|
return mem;
|
|
}
|
|
|
|
// Load a file into a buffer
|
|
void load_data_file(const char* file_name, uint32_t size, uint8_t* data) {
|
|
FILE* file = fopen(file_name, "rb");
|
|
if (file == NULL) critical_error("Could not load $s", file_name);
|
|
fread(data, 1, size, file);
|
|
fclose(file);
|
|
}
|
|
|
|
// 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");
|
|
|
|
// Init cursor
|
|
disp->cursor.row = 1;
|
|
disp->cursor.col = 1;
|
|
|
|
// Init useful SDK data
|
|
load_data_file("CharacterSet.data", CHARACTER_SET_SIZE, disp->character_set);
|
|
load_data_file("CharacterSetMini.data", CHARACTER_SET_SIZE, disp->character_set_mini);
|
|
load_data_file("FKeyIcons.data", FKEYICON_SIZE, disp->fkey_icons);
|
|
load_data_file("OpCodes.data", OPCODES_SIZE, disp->opcodes);
|
|
load_data_file("CursorIcons.data", CURSOR_ICONS_SIZE, disp->cursor_icons);
|
|
|
|
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_ADDRESS_P1 + RAM_SIZE;
|
|
|
|
cpu->mem = mem;
|
|
cpu->disp = disp;
|
|
cpu->fs = calloc(1, sizeof(fs_t));
|
|
|
|
cpu->keyboard = calloc(1, sizeof(keyboard_t));
|
|
cpu->keyboard->repeat_time_first = DEFAULT_REPEAT_TIME_FIRST_COUNT;
|
|
cpu->keyboard->repeat_time_next = DEFAULT_REPEAT_TIME_NEXT_COUNT;
|
|
|
|
cpu->malloc_manager = calloc(1, sizeof(malloc_manager_t));
|
|
cpu->malloc_manager->mallocs = malloc(MALLOC_SIZE * sizeof(int8_t));
|
|
|
|
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;
|
|
cpu->instruction_count++;
|
|
|
|
// Handle system calls
|
|
if (address == SYSCALL_ADDRESS) {
|
|
run_syscall(cpu);
|
|
return;
|
|
}
|
|
// Handle program execution finish
|
|
if (address == PR_INIT_VALUE || cpu->execution_finished) {
|
|
printf("Program execution finished!\n");
|
|
cpu->execution_finished = 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
|
|
}
|
|
|
|
// Main emulator loop
|
|
void main_loop(void* arg) {
|
|
cpu_t* cpu = (cpu_t*)arg;
|
|
|
|
if ((cpu->execution_paused && !cpu->execute_one_step) || cpu->execution_finished) return;
|
|
|
|
for (int i = 0; i < cpu->instruction_per_frame; i++) {
|
|
run_next_instruction(cpu);
|
|
|
|
if (cpu->execution_paused || cpu->execution_finished) break;
|
|
}
|
|
|
|
if (cpu->execute_one_step) cpu->execute_one_step = 0;
|
|
}
|
|
|
|
int main() {
|
|
// Load G1A
|
|
uint32_t g1a_size;
|
|
uint8_t* g1a_content = load_g1a("CBASIC.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);
|
|
|
|
#ifdef USE_EMSCRIPT
|
|
init_loop_html(cpu);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_EMSCRIPT
|
|
|
|
EM_ASYNC_JS(int*, init_html_async, (), {
|
|
await initFileSystem();
|
|
window.initGUI();
|
|
})
|
|
|
|
void init_loop_html(cpu_t* cpu) {
|
|
cpu->instruction_per_frame = 50000;
|
|
|
|
// Pass useful pointers to javascript
|
|
EM_ASM({
|
|
window.vramPointer = $0;
|
|
window.lcdPointer = $1;
|
|
window.keyboardPointer = $2;
|
|
window.instructionPerFrame = $3;
|
|
window.instructionCount = $4;
|
|
window.rtcTicksPointer = $5;
|
|
window.fsNeedsSyncPointer = $6;
|
|
window.cpuPointer = $7;
|
|
},
|
|
&cpu->disp->vram,
|
|
&cpu->disp->lcd,
|
|
&cpu->keyboard->row_state,
|
|
&cpu->instruction_per_frame,
|
|
&cpu->instruction_count,
|
|
&cpu->RTC_Ticks,
|
|
&cpu->fs->needs_sync,
|
|
cpu
|
|
);
|
|
|
|
// Wait for javascript to initialize file system and GUI
|
|
init_html_async();
|
|
|
|
// Launch the main loop
|
|
emscripten_set_main_loop_arg(main_loop, cpu, -1, 0);
|
|
}
|
|
|
|
#endif
|
|
|
|
// Functions called from javascript
|
|
|
|
void reset_execution(cpu_t* cpu) {
|
|
cpu->pc = PC_PROGRAM_START;
|
|
cpu->pr = PR_INIT_VALUE;
|
|
cpu->sr = SR_INIT_VALUE;
|
|
cpu->r[15] = RAM_ADDRESS_P1 + RAM_SIZE;
|
|
|
|
cpu->instruction_count = 0;
|
|
cpu->execution_finished = 0;
|
|
|
|
for (int i = 0; i < VRAM_SIZE; i++) {
|
|
cpu->disp->vram[i] = 0;
|
|
cpu->disp->lcd[i] = 0;
|
|
}
|
|
|
|
free(cpu->fs);
|
|
cpu->fs = calloc(1, sizeof(fs_t));
|
|
|
|
cpu->malloc_manager->currentSize = 0;
|
|
cpu->malloc_manager->mallocCount = 0;
|
|
|
|
cpu->disp->cursor.row = 1;
|
|
cpu->disp->cursor.col = 1;
|
|
cpu->disp->cursor.flash_mode = 0;
|
|
}
|
|
|
|
void load_new_g1a(cpu_t* cpu, uint8_t* g1a_content, uint32_t g1a_size) {
|
|
reset_execution(cpu);
|
|
|
|
printf("Loading new G1A file %d\n", g1a_size);
|
|
|
|
free(cpu->mem->rom);
|
|
cpu->mem->rom = g1a_content;
|
|
cpu->mem->rom_size = g1a_size;
|
|
} |