336 lines
7.6 KiB
C
336 lines
7.6 KiB
C
/* main.c
|
|
|
|
This file is part of the "First" CPU simulator project.
|
|
|
|
Copyright (c) 2024 Efthymios Kritikos
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "gui.h"
|
|
#include "gui_display.h"
|
|
#include "simdata.h"
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include "assembly.h"
|
|
#include "cpu.h"
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
void help(char* progname){
|
|
printf(
|
|
"Usage: %s [options] -i < in file > \n"\
|
|
"Options:\n"
|
|
" -a < out rom file > Assemble in file to out rom file\n"
|
|
" -r < framebuffer driver > Use one of the following for writing the framebuffer\n"
|
|
#if USE_AALIB==1
|
|
" aalib, braille\n"
|
|
#else
|
|
" braille\n"
|
|
#endif
|
|
" -o < file > Output simualtion data to specified json log file\n"
|
|
" -S < cpu structure > Select the structure of the cpu. You can choose one\n"
|
|
" of the following: simple, pipelined\n"
|
|
, progname);
|
|
}
|
|
|
|
#define ADDR_SIZE 16777216
|
|
|
|
int dump_rom(char* filename, struct simdata_t *simdata){
|
|
FILE* outfile=fopen(filename,"w");
|
|
for(int i=0;i<ADDR_SIZE;i++)
|
|
fputc(*(simdata->RAM+i),outfile);
|
|
fclose(outfile);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int main(int argc, char* argd[] ){
|
|
/// PARSE COMMAND LINE ///
|
|
char *infile = NULL;
|
|
char *assemble = NULL;
|
|
enum FB_DRIVER_t fb_driver=FB_BRAILLE;
|
|
int opt;
|
|
|
|
char *json_log_filename=NULL;
|
|
FILE *JSON_LOG_FILE;
|
|
|
|
char *structure="pipelined";
|
|
|
|
while ((opt = getopt(argc, argd, "hi:a:r:o:S:")) != -1) {
|
|
switch (opt) {
|
|
case 'h':
|
|
help(argd[0]);
|
|
return 0;
|
|
break;
|
|
case 'i':
|
|
infile=optarg;
|
|
break;
|
|
case 'a':
|
|
assemble=optarg;
|
|
break;
|
|
case 'r':
|
|
if(optarg==NULL){
|
|
help(argd[0]);
|
|
return 1;
|
|
}
|
|
|
|
if(strcmp(optarg,"aalib")==0)
|
|
fb_driver=FB_AALIB;
|
|
else if(strcmp(optarg,"braille")==0)
|
|
fb_driver=FB_BRAILLE;
|
|
else{
|
|
help(argd[0]);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 'o':
|
|
json_log_filename=optarg;
|
|
break;
|
|
case 'S':
|
|
structure=optarg;
|
|
break;
|
|
default:
|
|
help(argd[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
#if USE_AALIB==0
|
|
if(fb_driver==FB_AALIB){
|
|
printf("ERROR:Project wasn't compiled with aalib support.\n");
|
|
help(argd[0]);
|
|
return 1;
|
|
}
|
|
#endif
|
|
if (infile==NULL){
|
|
help(argd[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (json_log_filename){
|
|
JSON_LOG_FILE=fopen(json_log_filename,"w");
|
|
if(JSON_LOG_FILE==NULL){
|
|
printf("Error opening file \"%s\": %s\n",json_log_filename,strerror(errno));
|
|
return 1;
|
|
}
|
|
fprintf(JSON_LOG_FILE, "{\n\"end_of_simulation_data\" : {\n");
|
|
}
|
|
|
|
/// READ INPUT FILE ///
|
|
FILE* rom=fopen(infile,"r");
|
|
|
|
if(!rom){
|
|
printf("Couldn't open input file %s\n",infile);
|
|
return 1;
|
|
}
|
|
|
|
struct simdata_t *simdata = init_simdata();
|
|
if(!simdata){
|
|
printf("Couldn't allocate memory\n");
|
|
return 1;
|
|
}
|
|
|
|
if(strcmp("simple",structure)==0){
|
|
simdata->cpu_structure=CPU_STRUCTURE_SIMPLE_NON_PIPELINED;
|
|
}else if(strcmp("pipelined",structure)==0){
|
|
simdata->cpu_structure=CPU_STRUCTURE_SIMPLE_PIPELINED;
|
|
}else{
|
|
printf("Invalid cpu structure \"%s\"\n",structure);
|
|
fclose(rom);
|
|
free_simdata(simdata);
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
if ( assemble != NULL ){
|
|
/* RUN ASSEMBLER */
|
|
struct assembler_context_t *assembler_context=malloc_assembler_context();
|
|
|
|
for (int i=0;i<ADDR_SIZE;i++)
|
|
simdata->RAM[i]=0;
|
|
int linec=0;
|
|
if (rom != NULL) {
|
|
char line [1000];
|
|
int64_t opcode;
|
|
uint32_t addr=0;
|
|
|
|
//--------------- Build assembler context --------------------
|
|
while(fgets(line,sizeof(line),rom)!= NULL){
|
|
|
|
for(int i=0;line[i];i++)
|
|
if(line[i]==0x0a)
|
|
line[i]=0;
|
|
|
|
switch(assembler_context_process(line,assembler_context)){
|
|
case 0: break;
|
|
default:
|
|
printf("Error in getting assembler context at %s:%d\n", infile,linec+1);
|
|
fclose(rom);
|
|
free_assembler_context(assembler_context);
|
|
free_simdata(simdata);
|
|
return 1;
|
|
}
|
|
linec++;
|
|
}
|
|
|
|
fseek(rom, 0, SEEK_SET);
|
|
linec=0;
|
|
|
|
//---------------------- Assembler ---------------------------
|
|
while(fgets(line,sizeof(line),rom)!= NULL){
|
|
|
|
for(int i=0;line[i];i++)
|
|
if(line[i]==0x0a)
|
|
line[i]=0;
|
|
|
|
opcode=assemble_line(line,assembler_context);
|
|
|
|
if(opcode<-1){
|
|
if(opcode==-3)
|
|
printf("Error label not found %s:%d\n", infile,linec+1);
|
|
else
|
|
printf("Error assembling %s:%d\n", infile,linec+1);
|
|
fclose(rom);
|
|
free_assembler_context(assembler_context);
|
|
free_simdata(simdata);
|
|
return 1;
|
|
}else if(opcode==-1){
|
|
;/*it was empty, comment or label*/
|
|
}else{
|
|
simdata->RAM[addr]=(0xFF000000&opcode)>>24;
|
|
addr++;
|
|
simdata->RAM[addr]=(0xFF0000&opcode)>>16;
|
|
addr++;
|
|
simdata->RAM[addr]=(0xFF00&opcode)>>8;
|
|
addr++;
|
|
simdata->RAM[addr]=(0xFF&opcode);
|
|
addr++;
|
|
}
|
|
linec++;
|
|
}
|
|
}
|
|
free_assembler_context(assembler_context);
|
|
dump_rom(assemble,simdata);
|
|
free_simdata(simdata);
|
|
}else{
|
|
/* RUN SIMULATOR */
|
|
if (rom == NULL) {
|
|
printf("ERROR: Couldn't open rom file\n");
|
|
perror(infile);
|
|
fclose(rom);
|
|
free_simdata(simdata);
|
|
return 1;
|
|
}
|
|
|
|
fseek(rom, 0, SEEK_END);
|
|
if(ftell(rom)!=ADDR_SIZE){
|
|
printf("ERROR: ROM file isn't 16MiB\n");
|
|
fclose(rom);
|
|
free_simdata(simdata);
|
|
return 1;
|
|
}
|
|
fseek(rom, 0, SEEK_SET);
|
|
|
|
if(!simdata){
|
|
gui_error("failed to initialise simdata\n");
|
|
fclose(rom);
|
|
free_simdata(simdata);
|
|
return 1;
|
|
}
|
|
if(fread(simdata->RAM, ADDR_SIZE, 1, rom)==0){
|
|
printf("ERROR: failed to read input file\n");
|
|
free_simdata(simdata);
|
|
fclose(rom);
|
|
return 1;
|
|
}
|
|
fclose(rom);
|
|
|
|
|
|
/// INITIALISE GUI ///
|
|
if(start_gui(fb_driver)){
|
|
printf("Failed on start_gui()\n");
|
|
cpu_simdata_free(simdata);
|
|
free_simdata(simdata);
|
|
return 1;
|
|
}
|
|
|
|
cpu_simdata_malloc(simdata);
|
|
|
|
while(1){
|
|
update_gui(simdata);
|
|
|
|
int ret_code;
|
|
if((ret_code=gui_continue_request(simdata))){
|
|
if(ret_code==2)
|
|
break;
|
|
end_gui();
|
|
printf("Failed on gui_continue_request()\n");
|
|
cpu_simdata_free(simdata);
|
|
free_simdata(simdata);
|
|
return 1;
|
|
}
|
|
|
|
if(simdata->cpu_state==CPU_RUNNING){
|
|
int ret;
|
|
if((ret=cpu_cycle_clock(simdata))){
|
|
cpu_simdata_free(simdata);
|
|
free_simdata(simdata);
|
|
end_gui();
|
|
switch(ret){
|
|
case 1:
|
|
printf("Failed to execute instruction\n");
|
|
break;
|
|
case 2:
|
|
printf("Failed to decode instruction\n");
|
|
break;
|
|
case 3:
|
|
printf("Failed to fetch instruction\n");
|
|
break;
|
|
case 4:
|
|
printf("Internal simulator error\n");
|
|
break;
|
|
default:
|
|
printf("Unkown CPU failure\n");
|
|
}
|
|
return 1;
|
|
}
|
|
}else
|
|
usleep(100000);
|
|
}
|
|
|
|
if(json_log_filename){
|
|
fprintf(JSON_LOG_FILE, "\"cycles_run_for\" : %lu ,\n", simdata->current_clock);
|
|
fprintf(JSON_LOG_FILE, "\"instructions_executed\" : %lu\n", simdata->finished_instructions);
|
|
fprintf(JSON_LOG_FILE, "}\n}\n" );
|
|
|
|
fclose(JSON_LOG_FILE);
|
|
}
|
|
|
|
if(end_gui()){
|
|
printf("Failed on end_gui()\n");
|
|
cpu_simdata_free(simdata);
|
|
free_simdata(simdata);
|
|
return 1;
|
|
}
|
|
|
|
|
|
cpu_simdata_free(simdata);
|
|
free_simdata(simdata);
|
|
}
|
|
return 0;
|
|
}
|