#include "assembly.h" #include #include #include #define MAX_LABEL_SIZE 30 #define MAX_LABEL_SIZE_str "30" // Max instruction width is 25 characters without the null char // // INSTRUCTIONS: // BYTE 3 BYTE 2 BYTE 1 BYTE 0 // +===================================================+ // | | | | | // +===================================================+ // // // Exception: // The bytecode 0xFFFFFFFx is an invalid command // where x is any nibble // // // INSTRUCTION FORMAT 0: // +===================================================+ // | OPCODE | 24 BIT IMMEDIATE ADDRESS | // +===================================================+ // // INSTRUCTION FORMAT 1: // +===================================================+ // | OPCODE | SOURCE REG | DEST REG | // +===================================================+ // // INSTRUCTION FORMAT 2: // +===================================================+ // | OPCODE | DEST REG | 16 BIT IMMEDIATE | // +===================================================+ // // // // OPCODE FORMAT: // ______________________________________________________________________________________________________ // |BIT | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | // +----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ // | | INSTR. FORMAT | 8bit opcode num : 16bit opcode num | // +----+-----------------+-----------------------------+-----------------------------------------------+ // // // INSTRUCTION FORMAT 0 OPCODE NUM: // +-------+----------------------------------+--------------+ // | NUM | DESCRIPTION | AFFECT FLAGS | // +-------+----------------------------------+--------------+ // | 5'h0 | NOP | NO | // +-------+----------------------------------+--------------+ // | 5'h1 | Unconditional Jump | NO | // +-------+----------------------------------+--------------+ // | 5'h2 | Jump if zero flag is set | NO | // +-------+----------------------------------+--------------+ // | 5'h3 | Jump if zero flag is not set | NO | // +-------+----------------------------------+--------------+ // | 5'h4 | Jump if carry flag is 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: // +-------+---------------------------------------------+--------------+------------+------------+ // | NUM | DESCRIPTION | AFFECT FLAGS | SOURCE REG | DEST REG | // +-------+---------------------------------------------+--------------+------------+------------+ // | 13'h0 | ADD | YES | operand 1 | operand 2 | // +-------+---------------------------------------------+--------------|------------+------------+ // | 13'h1 | SUBTRACT | YES | operand 1 | operand 2 | // +-------+---------------------------------------------+--------------|------------+------------+ // | 13'h2 | SHIFT LEFT | YES | operand | | // +-------+---------------------------------------------+--------------|------------+------------+ // | 13'h3 | SHIFT RIGHT | YES | operand | | // +-------+---------------------------------------------+--------------|------------+------------+ // | 13'h4 | COMPARE (SUBTRACT WITHOUT SAVE) | YES | operand 1 | operand 2 | // +-------+---------------------------------------------+--------------+------------+------------+ // | 13'h5 | HALT | NO | | | // +-------+---------------------------------------------+--------------+------------+------------+ // | 13'h6 | PUSH | NO | operand | | // +-------+---------------------------------------------+--------------+------------+------------+ // | 13'h7 | POP | NO | operand | | // +-------+---------------------------------------------+--------------+------------+------------+ // | 13'h8 | MOV register to indirect register ( 32-bit )| NO | operand | (operand) | // +-------+---------------------------------------------+--------------+------------+------------+ // | 13'h9 | MOV indirect register to register ( 32-bit )| NO | (operand) | operand | // +-------+---------------------------------------------+--------------+------------+------------+ // | 14'hA | Floating point devision | YES | operand | operand | // +-------+---------------------------------------------+--------------+------------+------------+ // | 14'hB | Floating point Multiplication | YES | operand 1 | operand 2 | // +-------+---------------------------------------------+--------------+------------+------------+ // | 14'hC | Floating point addition | YES | operand 1 | operand 2 | // +-------+---------------------------------------------+--------------+------------+------------+ // | 14'hD | Floating point subtraction | YES | operand 1 | operand 2 | // +-------+---------------------------------------------+--------------+------------+------------+ // | 14'hE | Calculate the sin of a register | YES | operand | | // +-------+---------------------------------------------+--------------+------------+------------+ // | 14'hF | Calculate the cos of a register | YES | operand | | // +-------+---------------------------------------------+--------------+------------+------------+ // | 14'h10| DEC | YES | operand | | // +-------+---------------------------------------------+--------------+------------+------------+ // | 14'h11| INC | YES | operand | | // +-------+---------------------------------------------+--------------+------------+------------+ // | 14'h12| MOV register to register | YES | operand | | // // // INSTRUCTION FORMAT 2 OPCODE NUM: // +-------+----------------------------------------------+--------------+ // | NUM | DESCRIPTION | AFFECT FLAGS | // +-------+----------------------------------------------+--------------+ // | 5'd0 | MOV IMMEDIATE TO DEST LOW PART OF REGISTER | NO | // +-------+----------------------------------------------+--------------+ // | 5'd1 | MOV IMMEDIATE TO DEST HIGH PART OF REGISTER | NO | #define MAX_INSTRUCTION_LENGTH 26 char *disassemble(uint32_t opcode_be){ uint32_t opcode=(opcode_be&0x000000FF)<<24|(opcode_be&0x0000FF00)<<8|(opcode_be&0x00FF0000)>>8|(opcode_be&0xFF000000)>>24; char *ret=malloc(MAX_INSTRUCTION_LENGTH); if (!ret) return NULL; //////// PARSE BITECODE ////////// int instruction_format,opcode_num; uint32_t val; uint8_t val1; uint8_t val2; instruction_format=(opcode&0xE0000000)>>29; if(instruction_format==0){ // 8bit opcode opcode_num=(opcode&0x1F000000)>>24; val=opcode&0x00FFFFFF; switch(opcode_num){ case 0x00: snprintf(ret,MAX_INSTRUCTION_LENGTH,"NOP"); break; case 0x10: snprintf(ret,MAX_INSTRUCTION_LENGTH,"RET"); break; case 0x01: case 0x11: snprintf(ret,MAX_INSTRUCTION_LENGTH,"%s $%06X",(opcode_num&0x10)?"CALL":"JMP",val); break; case 0x02: case 0x03: 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: 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"); break; } }else if(instruction_format==1){ // 16bit opcode opcode_num=(opcode&0x1FFF0000)>>16; val1=(opcode&0x0000FF00)>>8; val2=opcode&0x000000FF; switch(opcode_num){ case 0x00: snprintf(ret,MAX_INSTRUCTION_LENGTH,"ADD %%R%0d, %%R%0d",val1,val2); break; case 0x01: snprintf(ret,MAX_INSTRUCTION_LENGTH,"SUB %%R%0d, %%R%0d",val1,val2); break; case 0x02: snprintf(ret,MAX_INSTRUCTION_LENGTH,"SL %%R%0d",val1); break; case 0x03: snprintf(ret,MAX_INSTRUCTION_LENGTH,"SR %%R%0d",val1); break; case 0x04: snprintf(ret,MAX_INSTRUCTION_LENGTH,"CMP %%R%0d, %%R%0d",val1,val2); break; case 0x05: snprintf(ret,MAX_INSTRUCTION_LENGTH,"HALT"); break; case 0x06: snprintf(ret,MAX_INSTRUCTION_LENGTH,"PUSH %%R%0d",val1); break; case 0x07: snprintf(ret,MAX_INSTRUCTION_LENGTH,"POP %%R%0d",val1); break; case 0x08: snprintf(ret,MAX_INSTRUCTION_LENGTH,"MOV %%R%0d,(%%R%0d)",val1,val2); break; case 0x09: snprintf(ret,MAX_INSTRUCTION_LENGTH,"MOV (%%R%0d),%%R%0d",val1,val2); break; case 0x0A: snprintf(ret,MAX_INSTRUCTION_LENGTH,"FDIV %%R%0d,%%R%0d",val1,val2); break; case 0x0B: snprintf(ret,MAX_INSTRUCTION_LENGTH,"FMUL %%R%0d,%%R%0d",val1,val2); break; case 0x0C: snprintf(ret,MAX_INSTRUCTION_LENGTH,"FADD %%R%0d,%%R%0d",val1,val2); break; case 0x0D: snprintf(ret,MAX_INSTRUCTION_LENGTH,"FSUB %%R%0d,%%R%0d",val1,val2); break; case 0x0E: snprintf(ret,MAX_INSTRUCTION_LENGTH,"SIN %%R%0d",val1); break; case 0x0F: snprintf(ret,MAX_INSTRUCTION_LENGTH,"COS %%R%0d",val1); break; case 0x10: snprintf(ret,MAX_INSTRUCTION_LENGTH,"DEC %%R%0d",val1); break; case 0x11: snprintf(ret,MAX_INSTRUCTION_LENGTH,"INC %%R%0d",val1); break; case 0x12: if(val1==0xFF){ //make it a global thing where every time we reference a register, 0xFF will refer to the SP since it seems to have become a standard snprintf(ret,MAX_INSTRUCTION_LENGTH,"MOV %%SP,%%R%0d",val2); }else{ snprintf(ret,MAX_INSTRUCTION_LENGTH,"MOV %%R%0d,%%R%0d",val1,val2); } break; default: snprintf(ret,MAX_INSTRUCTION_LENGTH,"UNRECOGNISED INSTRUCTION"); break; } }else if(instruction_format==2){ // 16bit opcode opcode_num=(opcode&0x1F000000)>>24; val1=(opcode&0x00FF0000)>>16; val=opcode&0x0000FFFF; switch(opcode_num){ case 0x00: snprintf(ret,MAX_INSTRUCTION_LENGTH,"MOV $%04X, %%R%0dl",val,val1); break; case 0x01: snprintf(ret,MAX_INSTRUCTION_LENGTH,"MOV $%04X, %%R%0dh",val,val1); break; default: snprintf(ret,MAX_INSTRUCTION_LENGTH,"UNRECOGNISED INSTRUCTION"); break; } }else snprintf(ret,MAX_INSTRUCTION_LENGTH,"UNRECOGNISED INSTRUCTION"); return ret; } struct imm_ret_t{ uint8_t status; uint32_t value; uint8_t size; }; struct imm_ret_t *parse_immediate(char* str,struct assembler_context_t *assembler_context){ struct imm_ret_t *imm_ret=malloc(sizeof(struct imm_ret_t)); if(!imm_ret) return 0; imm_ret->size=0; if(*str=='0'){ uint32_t addr; if(sscanf(str,"0x%08X",&addr)!=1){ imm_ret->status=1; return imm_ret; }else{ imm_ret->size+=2; if(str[0]=='0'&&str[1]=='x'){ int i=2; while(1){ if((str[i]>='0'&&str[i]<='9')||(str[i]>='a'&&str[i]<='f')||(str[i]>='A'&&str[i]<='F')) imm_ret->size++; else break; i++; } }else{ imm_ret->status=1; return imm_ret; } imm_ret->value=addr; imm_ret->status=0; return imm_ret; } }else{ int select=0; if(*str=='<'){ select=1; imm_ret->size++; str++; }else if(*str=='>'){ select=2; imm_ret->size++; str++; } // Get name size int len=0; for(int i=0;istatus=2; return imm_ret; } if(str[i]==0||str[i]=='\n'||str[i]==' '||str[i]==','||str[i]==')'){ break; }else len++; } imm_ret->size+=len; struct label_ll_t *search=assembler_context->label_ll; while(search){ if(strncmp(str,search->name,len)==0){ if(select==1) imm_ret->value=search->address>>16; else if(select==2) imm_ret->value=search->address&0x0000FFFF; else imm_ret->value=search->address; imm_ret->status=0; return imm_ret; } search=search->next; } imm_ret->status=2; return imm_ret; } } int64_t assemble_line(char *line, struct assembler_context_t *assembler_context){ uint16_t opcode; int x=0; int call=1; while(line[x]==' ')x++; if(line[x]==0||line[x]=='#') return -1;//empty line or comment if( (strncmp(line,"JMP,",4)==0) || (strncmp(line,"JMP ",4)==0) || ((call=strncmp(line,"CALL ",5))==0) ){ if(!call){ x=4; opcode=0x10; }else{ x=3; opcode=0x00; } if(line[x]==' '){ opcode|=0x01; }else if(line[x]==','){ x++; if(line[x]=='Z'){ opcode|=0x02; x++; }else if(line[x]=='C'){ opcode|=0x04; x++; }else if(line[x]=='N'){ x++; if(line[x]=='Z'){ opcode|=0x03; x++; }else if(line[x]=='C'){ opcode|=0x09; x++; }else return -2; }else return -2; }else return -2; while(line[x]==' ')x++; if(line[x]=='$'){ x++; struct imm_ret_t *imm=parse_immediate(line+x,assembler_context); uint32_t temp; if(!imm) return -2; if(imm->status){ switch(imm->status){ default: free(imm); return -2; case 2: free(imm); return -3; } }else{ temp=imm->value; free(imm); return opcode<<24|(temp&0x00FFFFFF); } }else return -2; }else{ uint8_t r1,r0,params; if(strncmp(line,"ADD ",4)==0){ x=3; opcode=0x2000; params=2; }else if(strncmp(line,"SUB ",4)==0){ x=3; opcode=0x2001; params=2; }else if(strncmp(line,"SL ",3)==0){ x=2; opcode=0x2002; params=1; }else if(strncmp(line,"SR ",3)==0){ x=2; opcode=0x2003; params=1; }else if(strncmp(line,"CMP ",4)==0){ x=3; opcode=0x2004; params=2; }else if(strncmp(line,"FDIV ",5)==0){ x=4; opcode=0x200A; params=2; }else if(strncmp(line,"FMUL ",5)==0){ x=4; opcode=0x200B; params=2; }else if(strncmp(line,"FADD ",5)==0){ x=4; opcode=0x200C; params=2; }else if(strncmp(line,"FSUB ",5)==0){ x=4; opcode=0x200D; params=2; }else if(strncmp(line,"SIN ",4)==0){ x=3; opcode=0x200E; params=1; }else if(strncmp(line,"COS ",4)==0){ x=3; opcode=0x200F; params=1; }else if(strncmp(line,"DEC ",4)==0){ x=3; opcode=0x2010; params=1; }else if(strncmp(line,"INC ",4)==0){ x=3; opcode=0x2011; params=1; }else params=0; if(params!=0){ while(line[x]==' ')x++; if(line[x]=='%'){ x++; if(line[x]=='R'){ x++; if(line[x]>='0'&&line[x]<='7'){ r0=line[x]-'0'; if(params==2){ x++; while(line[x]==' ')x++; if(line[x]==','){ x++; while(line[x]==' ')x++; if(line[x]=='%'){ x++; if(line[x]=='R'){ x++; if(line[x]>='0'&&line[x]<='7'){ r1=line[x]-'0'; return (opcode<<16)|r0<<8|r1; }else return -2; }else return -2; }else return -2; }else return -2; }else return (opcode<<16)|r0<<8; }else return -2; }else return -2; }else return -2; }else if(strncmp(line,"MOV ",4)==0){ uint32_t data; x=3; while(line[x]==' ')x++; if(line[x]=='$'){ x++; struct imm_ret_t *imm=parse_immediate(line+x,assembler_context); if(!imm) return -2; if(imm->status){ switch(imm->status){ default: free(imm); return -2; case 2: free(imm); return -3; } }else{ data=imm->value; x+=imm->size; free(imm); } while(line[x]==' ')x++; if(line[x]!=','){ x+=2; while(line[x]==' ')x++; } if(line[x]==','){ x++; while(line[x]==' ')x++; if(line[x]=='%'){ x++; if(line[x]=='R'){ x++; if(line[x]>='0'&&line[x]<='7'){ r1=line[x]-'0'; x++; if(line[x]=='l') return 0x40000000|r1<<16|(data&0xFFFF); else if(line[x]=='h') return 0x41000000|r1<<16|(data&0xFFFF); else return -2; }else return -2; }else if(line[x]=='S'){ x++; if(line[x]=='P'){ return 0x0F000000|(data&0x00FFFFFF); }else return -2; }else return -2; }else return -2; }else return -2; }else if(line[x]=='%'){ x++; if(line[x]=='R'){ x++; if(line[x]>='0'&&line[x]<='7'){ r0=line[x]-'0'; x++; while(line[x]==' ')x++; if(line[x]==','){ x++; while(line[x]==' ')x++; if(line[x]=='('){ x++; while(line[x]==' ')x++; if(line[x]=='%'){ x++; if(line[x]=='R'){ x++; if(line[x]>='0'&&line[x]<='7'){ r1=line[x]-'0'; x++; while(line[x]==' ')x++; if(line[x]==')'){ return 0x20080000|(r0&0xFF)<<8|(r1&0xFF); }else return -2; }else return -2; }else return -2; }else return -2; }else if(line[x]=='%'){ x++; if(line[x]=='R'){ x++; if(line[x]>='0'&&line[x]<='7'){ r1=line[x]-'0'; return 0x20120000|(r0&0xFF)<<8|(r1&0xFF); }else return -2; }else return -2; }else return -2; }else return -2; }else return -2; }else if(line[x]=='S'){ x++; if(line[x]=='P'){ x++; while(line[x]==' ')x++; if(line[x]==','){ x++; while(line[x]==' ')x++; if(line[x]=='%'){ x++; if(line[x]=='R'){ x++; if(line[x]>='0'&&line[x]<='7'){ r1=line[x]-'0'; return 0x20120000|0xFF<<8|(r1&0xFF); }else return -2; }else return -2; }else return -2; }else return -2; }else return -2; }else return -2; }else if(line[x]=='('){ while(line[x]==' ')x++; x++; if(line[x]=='%'){ x++; if(line[x]=='R'){ x++; if(line[x]>='0'&&line[x]<='7'){ r0=line[x]-'0'; x++; while(line[x]==' ')x++; if(line[x]==')'){ x++; while(line[x]==' ')x++; if(line[x]==','){ x++; while(line[x]==' ')x++; if(line[x]=='%'){ x++; if(line[x]=='R'){ x++; if(line[x]>='0'&&line[x]<='7'){ r1=line[x]-'0'; return 0x20090000|(r0&0xFF)<<8|(r1&0xFF); }else return -2; }else return -2; }else return -2; }else return -2; }else return -2; }else return -2; }else return -2; }else return -2; }else return -2; }else if(strncmp(line,"HALT ",5)==0||strncmp(line,"HALT\0",5)==0){ x=4; while(line[x]==' ')x++; if(line[x]==0) return 0x20050000; else return -2; }else if(strncmp(line,"RET ",4)==0||strncmp(line,"RET\0",4)==0){ return 0x10000000; }else if(*line==':'){ return -1; }else if(strncmp(line,"PUSH ",5)==0){ x=4; while(line[x]==' ')x++; if(line[x]=='%'){ x++; if(line[x]=='R'){ x++; if(line[x]>='0'&&line[x]<='7') return 0x20060000|(line[x]-'0')<<8; else return -2; } }else return -2; }else if(strncmp(line,"POP ",4)==0){ x=3; while(line[x]==' ')x++; if(line[x]=='%'){ x++; if(line[x]=='R'){ x++; if(line[x]>='0'&&line[x]<='7') return 0x20070000|(line[x]-'0')<<8; else return -2 ; } }else return -2; }else if(strncmp(line,"DDW ",4)==0){ x=4; while(line[x]==' ')x++; if(line[x]=='$'){ x++; struct imm_ret_t *imm=parse_immediate(line+x,assembler_context); uint32_t temp; if(!imm) return -2; if(imm->status){ switch(imm->status){ default: free(imm); return -2; case 2: free(imm); return -3; } }else{ temp=imm->value; free(imm); return ((temp&0xFF000000)>>24)|((temp&0x00FF0000)>>8)|((temp&0x0000FF00)<<8)|((temp&0x000000FF)<<24); } }else return -2; } return -2;//TODO: figure out which statemnt falls through to this } } int assembler_context_process(char* line,struct assembler_context_t *assembler_context){ int x=0; switch(line[0]){ case ':': if(line[1]==0) return 1; else{ char* label=malloc(MAX_LABEL_SIZE+1); sscanf(line+1,"%"MAX_LABEL_SIZE_str"s",label); struct label_ll_t **current; current=&(assembler_context->label_ll); while(*current) current=&((*current)->next); *current=malloc(sizeof(struct label_ll_t)); (*current)->name=label; (*current)->address=assembler_context->current_location; (*current)->next=NULL; } break; case 0: return 0; case ' ': while(line[x]==' ')x++; if(line[x]==0) return 0; else return 1; case '#': return 0; default: assembler_context->current_location+=4; } return 0; } struct assembler_context_t *malloc_assembler_context(){ struct assembler_context_t *ret; ret=malloc(sizeof(struct assembler_context_t)); ret->current_location=0x00000000; ret->label_ll=NULL; return ret; } void free_assembler_context(struct assembler_context_t *tofree){ struct label_ll_t *tofree_ll=tofree->label_ll; while(tofree_ll){ struct label_ll_t *temp=tofree_ll; tofree_ll=tofree_ll->next; free(temp->name); free(temp); } free(tofree); }