343 lines
6.4 KiB
C
343 lines
6.4 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
typedef enum {
|
|
UNDEFINED = 0,
|
|
MOV_RM = 0b100010
|
|
} OpCodes;
|
|
|
|
typedef enum {
|
|
BX_SI = 0b000,
|
|
BX_DI = 0b001,
|
|
BP_SI = 0b010,
|
|
BP_DI = 0b011,
|
|
SI___ = 0b100,
|
|
DI___ = 0b101,
|
|
BP___ = 0b110,
|
|
Di_Ad = 0b110,
|
|
BX___ = 0b111
|
|
} EffectiveAddress;
|
|
|
|
typedef enum {
|
|
AX = 0b000,
|
|
AL = 0b000,
|
|
AH = 0b100,
|
|
|
|
CX = 0b001,
|
|
CL = 0b001,
|
|
CH = 0b101,
|
|
|
|
DX = 0b010,
|
|
DL = 0b010,
|
|
DH = 0b110,
|
|
|
|
BX = 0b011,
|
|
BL = 0b011,
|
|
BH = 0b111,
|
|
|
|
|
|
SP = 0b100,
|
|
BP = 0b101,
|
|
SI = 0b110,
|
|
DI = 0b111
|
|
} Registers;
|
|
|
|
void insttostr(OpCodes opcode, char* instStr)
|
|
{
|
|
switch(opcode)
|
|
{
|
|
case MOV_RM:
|
|
memcpy(instStr, "mov", 3);
|
|
break;
|
|
|
|
case UNDEFINED:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void regtostr(Registers reg, char* str, _Bool wide)
|
|
{
|
|
if(wide)
|
|
{
|
|
switch(reg)
|
|
{
|
|
case CX:
|
|
memcpy(str, "cx", 2);
|
|
break;
|
|
case DX:
|
|
memcpy(str, "dx", 2);
|
|
break;
|
|
case BX:
|
|
memcpy(str, "bx", 2);
|
|
break;
|
|
case SP:
|
|
memcpy(str, "sp", 2);
|
|
break;
|
|
case BP:
|
|
memcpy(str, "bp", 2);
|
|
break;
|
|
case SI:
|
|
memcpy(str, "si", 2);
|
|
break;
|
|
case DI:
|
|
memcpy(str, "di", 2);
|
|
break;
|
|
case AX:
|
|
memcpy(str, "ax", 2);
|
|
break;
|
|
}
|
|
} else
|
|
{
|
|
|
|
switch(reg)
|
|
{
|
|
case CL:
|
|
memcpy(str, "cl", 2);
|
|
break;
|
|
case DL:
|
|
memcpy(str, "dl", 2);
|
|
break;
|
|
case BL:
|
|
memcpy(str, "bl", 2);
|
|
break;
|
|
case AH:
|
|
memcpy(str, "ah", 2);
|
|
break;
|
|
case CH:
|
|
memcpy(str, "ch", 2);
|
|
break;
|
|
case DH:
|
|
memcpy(str, "dh", 2);
|
|
break;
|
|
case BH:
|
|
memcpy(str, "bh", 2);
|
|
break;
|
|
case AL:
|
|
memcpy(str, "al", 2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void eactostr(EffectiveAddress ea, char* str, unsigned short displacement, _Bool displace, _Bool directaddress)
|
|
{
|
|
str[0] = '[';
|
|
off_t offset = 1;
|
|
|
|
if(!directaddress)
|
|
{
|
|
switch(ea)
|
|
{
|
|
case BX_SI:
|
|
memcpy(str + offset + 1, "bx + si", 7);
|
|
offset += 7;
|
|
break;
|
|
case BX_DI:
|
|
memcpy(str + offset + 1, "bx + di", 7);
|
|
offset += 7;
|
|
break;
|
|
case BP_SI:
|
|
memcpy(str + offset + 1, "bp + si", 7);
|
|
offset += 7;
|
|
break;
|
|
case BP_DI:
|
|
memcpy(str + offset + 1, "bp + di", 7);
|
|
offset += 7;
|
|
break;
|
|
case SI___:
|
|
memcpy(str + offset + 1, "si", 2);
|
|
offset += 2;
|
|
break;
|
|
case DI___:
|
|
memcpy(str + offset + 1, "di", 2);
|
|
offset += 2;
|
|
break;
|
|
case BP___:
|
|
memcpy(str + offset + 1, "bp", 2);
|
|
offset += 2;
|
|
break;
|
|
case BX___:
|
|
memcpy(str + offset + 1, "bx", 2);
|
|
offset += 2;
|
|
break;
|
|
}
|
|
}
|
|
if(displace)
|
|
{
|
|
if(!directaddress)
|
|
{
|
|
memcpy(str + offset, " + ", 3);
|
|
offset += 3;
|
|
}
|
|
offset += snprintf(str + offset + 1, 6, "%hu", displacement);
|
|
}
|
|
|
|
str[offset + 1] = ']';
|
|
}
|
|
|
|
void print_instructions(unsigned char* bytes, size_t nbytes)
|
|
{
|
|
|
|
size_t bytes_used = 0;
|
|
for(size_t i = 0, iindx = 0; i < nbytes; i += bytes_used, ++iindx)
|
|
{
|
|
bytes_used = 0;
|
|
|
|
OpCodes opcode = (OpCodes)(bytes[i] >> 2);
|
|
_Bool direction = (bytes[i] >> 1) & 0b1;
|
|
_Bool wide = bytes[i] & 0b1;
|
|
unsigned short displacement;
|
|
++bytes_used;
|
|
|
|
char inststr[16] = { '\0' };
|
|
char srcostr[16] = { '\0' };
|
|
char dstostr[16] = { '\0' };
|
|
|
|
char tmp1, tmp2;
|
|
|
|
switch(opcode)
|
|
{
|
|
case MOV_RM: // Register/Memory
|
|
switch(bytes[i + 1] >> 6) // MOD field
|
|
{
|
|
case 0b00: // register to memory, no disp, 16-bit disp if R\M = 110
|
|
tmp1 = bytes[i + 1] & 0b111; // R/M field
|
|
tmp2 = (bytes[i + 1] >> 3) & 0b111; // REG field
|
|
++bytes_used;
|
|
|
|
if(!direction)
|
|
{
|
|
insttostr(opcode, inststr);
|
|
regtostr(tmp2, srcostr, wide);
|
|
if(tmp1 == Di_Ad)
|
|
{
|
|
displacement = bytes[i + 2] ^ (bytes[i + 3] << 4);
|
|
eactostr(tmp1, dstostr, displacement, 1, 1);
|
|
|
|
} else {
|
|
eactostr(tmp1, dstostr, 0, 0, 0);
|
|
}
|
|
} else {
|
|
insttostr(opcode, inststr);
|
|
regtostr(tmp2, dstostr, wide);
|
|
if(tmp1 == Di_Ad)
|
|
{
|
|
displacement = bytes[i + 2] ^ (bytes[i + 3] << 4);
|
|
eactostr(tmp1, srcostr, displacement, 1, 1);
|
|
|
|
} else {
|
|
eactostr(tmp1, srcostr, 0, 0, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0b01: // register to memory, 8-bit disp
|
|
tmp1 = bytes[i + 1] & 0b111; // R/M field
|
|
tmp2 = (bytes[i + 1] >> 3) & 0b111; // REG field
|
|
displacement = bytes[i + 2];
|
|
bytes_used += 2;
|
|
|
|
if(!direction)
|
|
{
|
|
insttostr(opcode, inststr);
|
|
regtostr(tmp2, srcostr, wide);
|
|
eactostr(tmp1, dstostr, displacement, 1, 0);
|
|
} else {
|
|
insttostr(opcode, inststr);
|
|
regtostr(tmp2, dstostr, wide);
|
|
eactostr(tmp1, srcostr, displacement, 1, 0);
|
|
}
|
|
break;
|
|
|
|
case 0b10: // register to memory, 16-bit disp
|
|
tmp1 = bytes[i + 1] & 0b111; // R/M field
|
|
tmp2 = (bytes[i + 1] >> 3) & 0b111; // REG field
|
|
displacement = bytes[i + 2] ^ (bytes[i + 3] << 4); // get displacement bytes
|
|
bytes_used += 3;
|
|
|
|
if(!direction)
|
|
{
|
|
insttostr(opcode, inststr);
|
|
regtostr(tmp2, srcostr, wide);
|
|
eactostr(tmp1, dstostr, displacement, 1, 0);
|
|
} else {
|
|
insttostr(opcode, inststr);
|
|
regtostr(tmp2, dstostr, wide);
|
|
eactostr(tmp1, srcostr, displacement, 1, 0);
|
|
}
|
|
break;
|
|
|
|
//case 0b11:
|
|
default: // register to register
|
|
tmp1 =(bytes[i + 1] >> 3) & 0b111; // REG field
|
|
tmp2 = bytes[i + 1] & 0b111; // R/M field
|
|
if(!direction) // 0 = REG is source
|
|
{
|
|
insttostr(opcode, inststr);
|
|
regtostr(tmp1, srcostr, wide);
|
|
regtostr(tmp2, dstostr, wide);
|
|
} else {
|
|
insttostr(opcode, inststr);
|
|
regtostr(tmp2, srcostr, wide);
|
|
regtostr(tmp1, dstostr, wide);
|
|
}
|
|
|
|
printf("%s %s, %s\n", inststr, dstostr, srcostr);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
if(argc < 2)
|
|
{
|
|
fputs("No input provided", stderr);
|
|
return -1;
|
|
}
|
|
|
|
int argi = 1;
|
|
while(argi < argc)
|
|
{
|
|
FILE* f = fopen(argv[argi], "r");
|
|
if(f == NULL)
|
|
{
|
|
perror(argv[argi]);
|
|
goto LOOP_END_NOFREE_NOCLOSE;
|
|
}
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
const long fsize = ftell(f);
|
|
if(fsize < 2)
|
|
{
|
|
fprintf(stderr, "%s: file too small", argv[argi]);
|
|
goto LOOP_END_NOFREE_CLOSE;
|
|
}
|
|
rewind(f);
|
|
|
|
unsigned char* bytes = calloc(1, fsize); // TODO: check if allocation failed
|
|
const size_t bytes_read = fread(bytes, 1, fsize, f);
|
|
|
|
printf("; disassembly for file %s\nbits 16\n\n", argv[argi]);
|
|
|
|
print_instructions(bytes, bytes_read);
|
|
|
|
printf("\n");
|
|
|
|
free(bytes);
|
|
LOOP_END_NOFREE_CLOSE:
|
|
fclose(f);
|
|
LOOP_END_NOFREE_NOCLOSE:
|
|
++argi;
|
|
}
|
|
return 0;
|
|
}
|
|
|