CPU: Added support for CALL, RET and setting the stack pointer

This commit is contained in:
(Tim) Efthimis Kritikos 2024-02-12 13:43:13 +00:00
parent 1e04a7ab0d
commit 2579d6974c
5 changed files with 164 additions and 46 deletions

View File

@ -47,17 +47,36 @@
// +-------+----------------------------------+--------------+ // +-------+----------------------------------+--------------+
// | NUM | DESCRIPTION | AFFECT FLAGS | // | 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: // INSTRUCTION FORMAT 1 OPCODE NUM:
@ -111,16 +130,27 @@ char *disassemble(uint32_t opcode_be){
case 0x00: case 0x00:
snprintf(ret,MAX_INSTRUCTION_LENGTH,"NOP"); snprintf(ret,MAX_INSTRUCTION_LENGTH,"NOP");
break; break;
case 0x10:
snprintf(ret,MAX_INSTRUCTION_LENGTH,"RET");
break;
case 0x01: 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; break;
case 0x02: case 0x02:
case 0x03: 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; break;
case 0x04: case 0x04:
case 0x05: 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; break;
default: default:
snprintf(ret,MAX_INSTRUCTION_LENGTH,"UNRECOGNISED INSTR"); 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){ uint32_t assemble_line(char *line, __attribute__((unused)) struct assembler_context_t *assembler_context){
uint16_t opcode; uint16_t opcode;
int x; int x;
if(strncmp(line,"JMP",3)==0){ int call;
x=3; 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]==' '){ if(line[x]==' '){
opcode=0x01; opcode|=0x01;
}else if(line[x]==','){ }else if(line[x]==','){
x++; x++;
if(line[x]=='Z'){ if(line[x]=='Z'){
opcode=0x02; opcode|=0x02;
x++; x++;
}else if(line[x]=='C'){ }else if(line[x]=='C'){
opcode=0x04; opcode|=0x04;
x++; x++;
}else if(line[x]=='N'){ }else if(line[x]=='N'){
x++; x++;
if(line[x]=='Z'){ if(line[x]=='Z'){
opcode=0x03; opcode|=0x03;
x++; x++;
}else if(line[x]=='C'){ }else if(line[x]=='C'){
opcode=0x05; opcode|=0x09;
x++; x++;
}else }else
return 0xFFFFFFFF; return 0xFFFFFFFF;
@ -285,9 +322,13 @@ uint32_t assemble_line(char *line, __attribute__((unused)) struct assembler_cont
while(line[x]==' ')x++; while(line[x]==' ')x++;
if(line[x]=='$'){ if(line[x]=='$'){
x++; x++;
if(sscanf(line+x,"%04X",&data)==1){ if(sscanf(line+x,"%06X",&data)==1){
x+=4; x+=4;
while(line[x]==' ')x++; 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]==','){ if(line[x]==','){
x++; x++;
while(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'; r1=line[x]-'0';
x++; x++;
if(line[x]=='l') if(line[x]=='l')
return 0x40000000|r1<<16|data; return 0x40000000|r1<<16|(data&0xFFFF);
else if(line[x]=='h') else if(line[x]=='h')
return 0x41000000|r1<<16|data; return 0x41000000|r1<<16|(data&0xFFFF);
else else
return 0xFFFFFFFF; return 0xFFFFFFFF;
}else }else
return 0xFFFFFFFF; return 0xFFFFFFFF;
}else if(line[x]=='S'){
x++;
if(line[x]=='P'){
return 0x0F000000|(data&0x00FFFFFF);
}else
return 0xFFFFFFFF;
}else }else
return 0xFFFFFFFF; return 0xFFFFFFFF;
}else }else
@ -323,8 +370,10 @@ uint32_t assemble_line(char *line, __attribute__((unused)) struct assembler_cont
return 0x20050000; return 0x20050000;
else else
return 0xFFFFFFFF; return 0xFFFFFFFF;
}else }else if(strncmp(line,"RET",3)==0){
return 0xFFFFFFFF; return 0x10000000;
}
return 0xFFFFFFFF;
} }
} }

93
cpu.c
View File

@ -34,24 +34,40 @@ int decode(struct simdata_t *simdata){
case 0: case 0:
opcode=(simdata->decode_data->in_bytecode&0x1F000000)>>24; opcode=(simdata->decode_data->in_bytecode&0x1F000000)>>24;
switch(opcode){ switch(opcode){
case 0: case 0x00:
simdata->exec_data->EXEC_ACTION=NOP; simdata->exec_data->EXEC_ACTION=NOP;
break; break;
case 1: case 0x01:
case 2: case 0x02:
case 3: case 0x03:
case 4: case 0x04:
case 5: case 0x05:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
switch(opcode){ switch(opcode){
case 1: simdata->exec_data->COND=NONE; break; case 0x01: case 0x11: simdata->exec_data->COND=NONE; break;
case 2: simdata->exec_data->COND=ZERO; break; case 0x02: case 0x12: simdata->exec_data->COND=ZERO; break;
case 3: simdata->exec_data->COND=NZERO; break; case 0x03: case 0x13: simdata->exec_data->COND=NZERO; break;
case 4: simdata->exec_data->COND=CARRY; break; case 0x04: case 0x14: simdata->exec_data->COND=CARRY; break;
case 5: simdata->exec_data->COND=NCARRY; 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->OP_ADDR=IMMEDIATE;
simdata->exec_data->out_op->data=simdata->decode_data->in_bytecode&0x00FFFFFF; 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; break;
default: default:
return 1; return 1;
@ -172,6 +188,7 @@ void free_exec_data(struct exec_data_t *tofree){
int exec(struct simdata_t *simdata){ int exec(struct simdata_t *simdata){
free_instr_list(&simdata->cpu_gui_hints->executing_list); free_instr_list(&simdata->cpu_gui_hints->executing_list);
switch(simdata->exec_data->EXEC_ACTION){ switch(simdata->exec_data->EXEC_ACTION){
case CALL:
case JUMP: case JUMP:
int condition=0; int condition=0;
switch(simdata->exec_data->COND){ switch(simdata->exec_data->COND){
@ -182,12 +199,20 @@ int exec(struct simdata_t *simdata){
case NCARRY: condition=!(simdata->registers->FLAGS&2); break; case NCARRY: condition=!(simdata->registers->FLAGS&2); break;
} }
if(condition){ 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) if(simdata->exec_data->out_op->OP_ADDR==IMMEDIATE)
simdata->PC=simdata->exec_data->out_op->data; simdata->PC=simdata->exec_data->out_op->data;
else else
return 1; return 1;
} }
break; break;
case RET:
simdata->SP-=6;
simdata->PC=simdata->RAM[simdata->SP];
break;
case EXEC_ALU: case EXEC_ALU:
if( simdata->exec_data->in_op1->OP_ADDR==REGISTER && if( simdata->exec_data->in_op1->OP_ADDR==REGISTER &&
simdata->exec_data->in_op2->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->out_op->data]=(simdata->registers->GPR[simdata->exec_data->out_op->data]&0x0000FFFF)|((0x0000FFFF&
simdata->registers->GPR[simdata->exec_data->in_op1->data])<<16); simdata->registers->GPR[simdata->exec_data->in_op1->data])<<16);
break; 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: default:
return 1; return 1;
} }
@ -264,14 +302,39 @@ int exec(struct simdata_t *simdata){
int state=0; 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){ int cpu_cycle_clock(struct simdata_t *simdata){
free_instr_list(&simdata->cpu_gui_hints->fetching_list); 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->decoding_list);
free_instr_list(&simdata->cpu_gui_hints->executing_list); free_instr_list(&simdata->cpu_gui_hints->executing_list);
switch(state){ switch(state){
case 0: if( fetch(simdata) ) return 3; break; case 0:
case 1: if( decode(simdata) ) return 2; break; switch(fetch(simdata)){
case 2: if( exec(simdata) ) return 1; break; 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) if(state==2)
state=0; state=0;

4
cpu.h
View File

@ -17,8 +17,10 @@ enum EXEC_ACTION_t {
EXEC_ALU, EXEC_ALU,
MOVE, MOVE,
JUMP, JUMP,
CALL,
NOP, NOP,
HALT HALT,
RET
}; };
enum ALU_OP_t { enum ALU_OP_t {

3
main.c
View File

@ -198,6 +198,9 @@ int main(int argc, char* argd[] ){
case 3: case 3:
printf("Failed to fetch instruction\n"); printf("Failed to fetch instruction\n");
break; break;
case 4:
printf("Internal simulator error\n");
break;
default: default:
printf("Unkown CPU failure\n"); printf("Unkown CPU failure\n");
} }

View File

@ -1,10 +1,11 @@
MOV $0001,%R0l MOV $123456,%SP
MOV $0000,%R0h MOV $0x0001,%R0l
MOV $0001,%R1l MOV $0x0000,%R0h
MOV $0000,%R1h MOV $0x0000,%R1l
MOV $0010,%R2l MOV $0x0000,%R1h
MOV $0000,%R2h CALL $0x0024
ADD %R0,%R1 CALL $0x0024
CMP %R1,%R2 CALL $0x0024
JMP,NZ $000018
HALT HALT
ADD %R0,%R1
RET