From 2579d6974c1246c943a4fd6771f6cd769fdcb99a Mon Sep 17 00:00:00 2001 From: "(Tim) Efthimis Kritikos" Date: Mon, 12 Feb 2024 13:43:13 +0000 Subject: [PATCH] CPU: Added support for CALL, RET and setting the stack pointer --- assembly.c | 91 ++++++++++++++++++++++++++++++++++++++++------------ cpu.c | 93 +++++++++++++++++++++++++++++++++++++++++++++--------- cpu.h | 4 ++- main.c | 3 ++ test.asm | 19 +++++------ 5 files changed, 164 insertions(+), 46 deletions(-) diff --git a/assembly.c b/assembly.c index 212bd98..0d45011 100644 --- a/assembly.c +++ b/assembly.c @@ -47,17 +47,36 @@ // +-------+----------------------------------+--------------+ // | NUM | DESCRIPTION | AFFECT FLAGS | // +-------+----------------------------------+--------------+ -// | 5'd0 | NOP | NO | +// | 5'h0 | NOP | NO | // +-------+----------------------------------+--------------+ -// | 5'd1 | Unconditional Jump | NO | +// | 5'h1 | Unconditional Jump | NO | // +-------+----------------------------------+--------------+ -// | 5'd2 | Jump if zero flag is set | NO | +// | 5'h2 | Jump if zero flag is set | NO | // +-------+----------------------------------+--------------+ -// | 5'd3 | Jump if zero flag is not set | NO | +// | 5'h3 | Jump if zero flag is not set | NO | // +-------+----------------------------------+--------------+ -// | 5'd4 | Jump if carry flag is set | NO | +// | 5'h4 | Jump if carry flag is set | NO | // +-------+----------------------------------+--------------+ -// | 5'd5 | Jump if carry flag is not set | NO | +// | 5'h5 | Jump if carry flag is not set | NO | +// +-------+----------------------------------+--------------+ +// | 5'h10 | Unconditional ret | NO | +// +-------+----------------------------------+--------------+ +// | 5'h11 | Unconditional call | NO | +// +-------+----------------------------------+--------------+ +// | 5'h12 | Call if zero flag is set | NO | +// +-------+----------------------------------+--------------+ +// | 5'h13 | Call if zero flag is not set | NO | +// +-------+----------------------------------+--------------+ +// | 5'h14 | Call if carry flag is set | NO | +// +-------+----------------------------------+--------------+ +// | 5'h15 | Call if carry flag is not set | NO | +// +// | | | | +// +-------+----------------------------------+--------------+ +// | 5'h0F | MOV immediate to SP | | +// +-------+----------------------------------+--------------+ +// | | | | +// // // // INSTRUCTION FORMAT 1 OPCODE NUM: @@ -111,16 +130,27 @@ char *disassemble(uint32_t opcode_be){ case 0x00: snprintf(ret,MAX_INSTRUCTION_LENGTH,"NOP"); break; + case 0x10: + snprintf(ret,MAX_INSTRUCTION_LENGTH,"RET"); + break; case 0x01: - snprintf(ret,MAX_INSTRUCTION_LENGTH,"JMP $%06X",val); + case 0x11: + snprintf(ret,MAX_INSTRUCTION_LENGTH,"%s $%06X",(opcode_num&0x10)?"CALL":"JMP",val); break; case 0x02: case 0x03: - snprintf(ret,MAX_INSTRUCTION_LENGTH,"JMP,%s $%06X",opcode_num&1?"NZ":"Z",val); + case 0x12: + case 0x13: + snprintf(ret,MAX_INSTRUCTION_LENGTH,"%s,%s $%06X",(opcode_num&0x10)?"CALL":"JMP",opcode_num&1?"NZ":"Z",val); break; case 0x04: case 0x05: - snprintf(ret,MAX_INSTRUCTION_LENGTH,"JMP,%s $%06X",opcode_num&1?"NC":"C",val); + case 0x14: + case 0x15: + snprintf(ret,MAX_INSTRUCTION_LENGTH,"%s,%s $%06X",(opcode_num&0x10)?"CALL":"JMP",opcode_num&1?"NC":"C",val); + break; + case 0x0F: + snprintf(ret,MAX_INSTRUCTION_LENGTH,"MOV $%06X,%%SP",val); break; default: snprintf(ret,MAX_INSTRUCTION_LENGTH,"UNRECOGNISED INSTR"); @@ -182,25 +212,32 @@ char *disassemble(uint32_t opcode_be){ uint32_t assemble_line(char *line, __attribute__((unused)) struct assembler_context_t *assembler_context){ uint16_t opcode; int x; - if(strncmp(line,"JMP",3)==0){ - x=3; + int call; + if( ((call=strncmp(line,"JMP",3))==0) || (strncmp(line,"CALL",4)==0) ){ + if(call){ + x=4; + opcode=0x10; + }else{ + x=3; + opcode=0x00; + } if(line[x]==' '){ - opcode=0x01; + opcode|=0x01; }else if(line[x]==','){ x++; if(line[x]=='Z'){ - opcode=0x02; + opcode|=0x02; x++; }else if(line[x]=='C'){ - opcode=0x04; + opcode|=0x04; x++; }else if(line[x]=='N'){ x++; if(line[x]=='Z'){ - opcode=0x03; + opcode|=0x03; x++; }else if(line[x]=='C'){ - opcode=0x05; + opcode|=0x09; x++; }else return 0xFFFFFFFF; @@ -285,9 +322,13 @@ uint32_t assemble_line(char *line, __attribute__((unused)) struct assembler_cont while(line[x]==' ')x++; if(line[x]=='$'){ x++; - if(sscanf(line+x,"%04X",&data)==1){ + if(sscanf(line+x,"%06X",&data)==1){ x+=4; while(line[x]==' ')x++; + if(line[x]!=','){ //24 bit TODO: not the best way to parse this + x+=2; + while(line[x]==' ')x++; + } if(line[x]==','){ x++; while(line[x]==' ')x++; @@ -299,13 +340,19 @@ uint32_t assemble_line(char *line, __attribute__((unused)) struct assembler_cont r1=line[x]-'0'; x++; if(line[x]=='l') - return 0x40000000|r1<<16|data; + return 0x40000000|r1<<16|(data&0xFFFF); else if(line[x]=='h') - return 0x41000000|r1<<16|data; + return 0x41000000|r1<<16|(data&0xFFFF); else return 0xFFFFFFFF; }else return 0xFFFFFFFF; + }else if(line[x]=='S'){ + x++; + if(line[x]=='P'){ + return 0x0F000000|(data&0x00FFFFFF); + }else + return 0xFFFFFFFF; }else return 0xFFFFFFFF; }else @@ -323,8 +370,10 @@ uint32_t assemble_line(char *line, __attribute__((unused)) struct assembler_cont return 0x20050000; else return 0xFFFFFFFF; - }else - return 0xFFFFFFFF; + }else if(strncmp(line,"RET",3)==0){ + return 0x10000000; + } + return 0xFFFFFFFF; } } diff --git a/cpu.c b/cpu.c index db3dcdd..3af8e53 100644 --- a/cpu.c +++ b/cpu.c @@ -34,24 +34,40 @@ int decode(struct simdata_t *simdata){ case 0: opcode=(simdata->decode_data->in_bytecode&0x1F000000)>>24; switch(opcode){ - case 0: + case 0x00: simdata->exec_data->EXEC_ACTION=NOP; break; - case 1: - case 2: - case 3: - case 4: - case 5: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: switch(opcode){ - case 1: simdata->exec_data->COND=NONE; break; - case 2: simdata->exec_data->COND=ZERO; break; - case 3: simdata->exec_data->COND=NZERO; break; - case 4: simdata->exec_data->COND=CARRY; break; - case 5: simdata->exec_data->COND=NCARRY; break; + case 0x01: case 0x11: simdata->exec_data->COND=NONE; break; + case 0x02: case 0x12: simdata->exec_data->COND=ZERO; break; + case 0x03: case 0x13: simdata->exec_data->COND=NZERO; break; + case 0x04: case 0x14: simdata->exec_data->COND=CARRY; break; + case 0x05: case 0x15: simdata->exec_data->COND=NCARRY; break; + default: return 1; } simdata->exec_data->out_op->OP_ADDR=IMMEDIATE; simdata->exec_data->out_op->data=simdata->decode_data->in_bytecode&0x00FFFFFF; - simdata->exec_data->EXEC_ACTION=JUMP; + simdata->exec_data->EXEC_ACTION=(opcode&0x10)?CALL:JUMP; + break; + case 0x0F: + simdata->exec_data->EXEC_ACTION=MOVE; + simdata->exec_data->in_op1->OP_ADDR=IMMEDIATE; + simdata->exec_data->in_op1->data=simdata->decode_data->in_bytecode&0x00FFFFFF; + simdata->exec_data->out_op->OP_ADDR=REGISTER; + simdata->exec_data->out_op->data=0; + break; + case 0x10: + simdata->exec_data->EXEC_ACTION=RET; break; default: return 1; @@ -172,6 +188,7 @@ void free_exec_data(struct exec_data_t *tofree){ int exec(struct simdata_t *simdata){ free_instr_list(&simdata->cpu_gui_hints->executing_list); switch(simdata->exec_data->EXEC_ACTION){ + case CALL: case JUMP: int condition=0; switch(simdata->exec_data->COND){ @@ -182,12 +199,20 @@ int exec(struct simdata_t *simdata){ case NCARRY: condition=!(simdata->registers->FLAGS&2); break; } if(condition){ + if(simdata->exec_data->EXEC_ACTION==CALL){ + simdata->RAM[simdata->SP]=simdata->PC; + simdata->SP+=6; + } if(simdata->exec_data->out_op->OP_ADDR==IMMEDIATE) simdata->PC=simdata->exec_data->out_op->data; else return 1; } break; + case RET: + simdata->SP-=6; + simdata->PC=simdata->RAM[simdata->SP]; + break; case EXEC_ALU: if( simdata->exec_data->in_op1->OP_ADDR==REGISTER && simdata->exec_data->in_op2->OP_ADDR==REGISTER && @@ -245,6 +270,19 @@ int exec(struct simdata_t *simdata){ simdata->registers->GPR[simdata->exec_data->out_op->data]=(simdata->registers->GPR[simdata->exec_data->out_op->data]&0x0000FFFF)|((0x0000FFFF& simdata->registers->GPR[simdata->exec_data->in_op1->data])<<16); break; + case REGISTER: /* This is for special registers like the SP which is 24bits long */ + if(simdata->exec_data->in_op1->OP_ADDR==IMMEDIATE){ + if( (simdata->exec_data->in_op1->data&0xFF000000) == 0 ) + simdata->SP=simdata->exec_data->in_op1->data; + else + return 2; + }else{ + if( (simdata->registers->GPR[simdata->exec_data->in_op1->data]&0xFF000000) == 0 ) + simdata->SP=simdata->registers->GPR[simdata->exec_data->in_op1->data]; + else + return 2; + } + break; default: return 1; } @@ -264,14 +302,39 @@ int exec(struct simdata_t *simdata){ int state=0; + +/* + * RETURN CODES: + * 0 : success + * 1 : error in execution stage + * 2 : error in decode stage + * 3 : error in fetch stage + * 4 : internal error + */ int cpu_cycle_clock(struct simdata_t *simdata){ free_instr_list(&simdata->cpu_gui_hints->fetching_list); free_instr_list(&simdata->cpu_gui_hints->decoding_list); free_instr_list(&simdata->cpu_gui_hints->executing_list); switch(state){ - case 0: if( fetch(simdata) ) return 3; break; - case 1: if( decode(simdata) ) return 2; break; - case 2: if( exec(simdata) ) return 1; break; + case 0: + switch(fetch(simdata)){ + case 0: break; + default: return 3; + } + break; + case 1: + switch(decode(simdata)){ + case 0: break; + default: return 2; + } + break; + case 2: + switch(exec(simdata)){ + case 0: break; + case 2: return 4; + default: return 1; + } + break; } if(state==2) state=0; diff --git a/cpu.h b/cpu.h index 223fd6a..e231db2 100644 --- a/cpu.h +++ b/cpu.h @@ -17,8 +17,10 @@ enum EXEC_ACTION_t { EXEC_ALU, MOVE, JUMP, + CALL, NOP, - HALT + HALT, + RET }; enum ALU_OP_t { diff --git a/main.c b/main.c index 7955f76..f41e76d 100644 --- a/main.c +++ b/main.c @@ -198,6 +198,9 @@ int main(int argc, char* argd[] ){ case 3: printf("Failed to fetch instruction\n"); break; + case 4: + printf("Internal simulator error\n"); + break; default: printf("Unkown CPU failure\n"); } diff --git a/test.asm b/test.asm index 11dd054..0154e2a 100644 --- a/test.asm +++ b/test.asm @@ -1,10 +1,11 @@ -MOV $0001,%R0l -MOV $0000,%R0h -MOV $0001,%R1l -MOV $0000,%R1h -MOV $0010,%R2l -MOV $0000,%R2h -ADD %R0,%R1 -CMP %R1,%R2 -JMP,NZ $000018 +MOV $123456,%SP +MOV $0x0001,%R0l +MOV $0x0000,%R0h +MOV $0x0000,%R1l +MOV $0x0000,%R1h +CALL $0x0024 +CALL $0x0024 +CALL $0x0024 HALT +ADD %R0,%R1 +RET