First/assembly.c

383 lines
11 KiB
C

#include "assembly.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
// 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 |
// +-------+----------------------------------+--------------+
// | 13'd0 | ADD | YES |
// +-------+----------------------------------+--------------|
// | 13'd1 | SUBTRACT | YES |
// +-------+----------------------------------+--------------|
// | 13'd2 | SHIFT LEFT | YES |
// +-------+----------------------------------+--------------|
// | 13'd3 | SHIFT RIGHT | YES |
// +-------+----------------------------------+--------------|
// | 13'd4 | COMPARE (SUBTRACT WITHOUT SAVE) | YES |
// +-------+----------------------------------+--------------+
// | 13'd5 | HALT | NO |
//
//
// 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;
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;
}
uint32_t assemble_line(char *line, __attribute__((unused)) struct assembler_context_t *assembler_context){
uint16_t opcode;
int x;
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;
}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 0xFFFFFFFF;
}else
return 0xFFFFFFFF;
}else
return 0xFFFFFFFF;
while(line[x]==' ')x++;
uint32_t addr;
if(line[x]=='$'){
x++;
if(sscanf(line+x,"%06X",&addr)!=1)
return 0xFFFFFFFF;
else
return opcode<<24|(addr&0x00FFFFFF);
}else
return 0xFFFFFFFF;
}else{
uint8_t r1,r0,params;
if(strncmp(line,"ADD",3)==0){
x=3;
opcode=0x2000;
params=2;
}else if(strncmp(line,"SUB",3)==0){
x=3;
opcode=0x2001;
params=2;
}else if(strncmp(line,"SL",2)==0){
x=2;
opcode=0x2002;
params=1;
}else if(strncmp(line,"SR",2)==0){
x=2;
opcode=0x2003;
params=1;
}else if(strncmp(line,"CMP",3)==0){
x=3;
opcode=0x2004;
params=2;
}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 0xFFFFFFFF;
}else
return 0xFFFFFFFF;
}else
return 0xFFFFFFFF;
}else
return 0xFFFFFFFF;
}else
return (opcode<<16)|r0<<8;
}else
return 0xFFFFFFFF;
}else
return 0xFFFFFFFF;
}else
return 0xFFFFFFFF;
}else if(strncmp(line,"MOV",3)==0){
uint32_t data;
x=3;
while(line[x]==' ')x++;
if(line[x]=='$'){
x++;
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++;
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 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
return 0xFFFFFFFF;
}else
return 0xFFFFFFFF;
}else
return 0xFFFFFFFF;
}else
return 0xFFFFFFFF;
}else if(strncmp(line,"HALT",4)==0){
x=4;
while(line[x]==' ')x++;
if(line[x]==0)
return 0x20050000;
else
return 0xFFFFFFFF;
}else if(strncmp(line,"RET",3)==0){
return 0x10000000;
}
return 0xFFFFFFFF;
}
}
void free_assembler_context(struct assembler_context_t *tofree){
free(tofree);
}