233 lines
5.9 KiB
C++
233 lines
5.9 KiB
C++
#include <iostream>
|
|
#include <cstdint>
|
|
#include <fstream>
|
|
|
|
#define MEMORY_SIZE 1048576
|
|
|
|
union reg16 {
|
|
struct {
|
|
uint8_t low;
|
|
uint8_t high;
|
|
};
|
|
uint16_t value;
|
|
};
|
|
|
|
struct ModRM {
|
|
uint8_t mod; // addressing mode
|
|
uint8_t reg; // source
|
|
uint8_t rm; // dest
|
|
};
|
|
|
|
reg16 AX, BX, CX, DX; // general regs
|
|
uint16_t SI, DI; // source index, dest index
|
|
uint16_t BP, SP; // base ptr, stack ptr
|
|
uint16_t IP; // instruction ptr
|
|
uint16_t CS, DS, ES, SS; // code segmets reg, data segment reg, extra segment reg, stack segment reg
|
|
|
|
bool CF; // carry
|
|
bool PF; // parity
|
|
bool AF; // auxiliary carry
|
|
bool ZF; // zero
|
|
bool SF; // sign
|
|
bool TF; // trap
|
|
bool IF; // interrupt
|
|
bool DF; // direction
|
|
bool OF; // overflow
|
|
|
|
uint8_t ram[MEMORY_SIZE];
|
|
|
|
uint32_t physical_addr(uint16_t seg, uint16_t offset);
|
|
bool load_binary(char* filename, size_t addr);
|
|
ModRM parse_modrm(uint8_t byte);
|
|
|
|
uint8_t get_reg_byte(uint8_t code);
|
|
void set_reg_byte(uint8_t code, uint8_t value);
|
|
uint16_t get_reg_word(uint8_t code);
|
|
void set_reg_word(uint8_t code, uint16_t value);
|
|
|
|
uint16_t get_displacement(uint8_t size);
|
|
uint32_t calc_effective_addr(ModRM modrm);
|
|
|
|
void update_flags_add(uint8_t b1, uint8_t b2, uint8_t res);
|
|
void update_flags_add16(uint16_t w1, uint16_t w2, uint16_t res);
|
|
|
|
|
|
uint32_t physical_addr(uint16_t seg, uint16_t offset) {
|
|
return ((uint32_t) seg << 4) + offset;
|
|
}
|
|
|
|
bool load_binary(char* filename, size_t addr) {
|
|
std::ifstream file(filename, std::ios::binary);
|
|
if (!file) {
|
|
std::cerr << "failed to open file " << filename << "\n";
|
|
return false;
|
|
}
|
|
|
|
file.seekg(0, std::ios::end);
|
|
size_t file_size = file.tellg();
|
|
file.seekg(0, std::ios::beg);
|
|
|
|
if (file_size > MEMORY_SIZE - addr) {
|
|
std::cerr << "file is too big";
|
|
return false;
|
|
}
|
|
|
|
file.read(reinterpret_cast<char*>(ram + addr), file_size);
|
|
|
|
if (!file) {
|
|
std::cerr << "failed to read entire file";
|
|
return false;
|
|
}
|
|
|
|
std::cout << "file " << filename << " loaded to ram\n";
|
|
return true;
|
|
}
|
|
|
|
ModRM parse_modrm(uint8_t byte) {
|
|
ModRM modrm;
|
|
modrm.mod = (byte >> 6) & 0x3;
|
|
modrm.reg = (byte >> 3) & 0x7;
|
|
modrm.rm = byte & 0x7;
|
|
return modrm;
|
|
}
|
|
|
|
uint8_t get_reg_byte(uint8_t code) {
|
|
switch (code) {
|
|
case 0: return AX.low;
|
|
case 1: return CX.low;
|
|
case 2: return DX.low;
|
|
case 3: return BX.low;
|
|
case 4: return AX.high;
|
|
case 5: return CX.high;
|
|
case 6: return DX.high;
|
|
case 7: return BX.high;
|
|
default:
|
|
std::cerr << "invalid reg code for 8-bit reg";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void set_reg_byte(uint8_t code, uint8_t value) {
|
|
switch (code) {
|
|
case 0: AX.low = value; break;
|
|
case 1: CX.low = value; break;
|
|
case 2: DX.low = value; break;
|
|
case 3: BX.low = value; break;
|
|
case 4: AX.high = value; break;
|
|
case 5: CX.high = value; break;
|
|
case 6: DX.high = value; break;
|
|
case 7: BX.high = value; break;
|
|
default:
|
|
std::cerr << "invalid reg code for 8-bit reg";
|
|
}
|
|
}
|
|
|
|
uint16_t get_reg_word(uint8_t code) {
|
|
switch (code) {
|
|
case 0: return AX.value;
|
|
case 1: return CX.value;
|
|
case 2: return DX.value;
|
|
case 3: return BX.value;
|
|
case 4: return SP;
|
|
case 5: return BP;
|
|
case 6: return SI;
|
|
case 7: return DI;
|
|
default:
|
|
std::cerr << "invalid reg code for 8-bit reg";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void set_reg_word(uint8_t code, uint16_t value) {
|
|
switch (code)
|
|
{
|
|
case 0: AX.value = value; break;
|
|
case 1: CX.value = value; break;
|
|
case 2: DX.value = value; break;
|
|
case 3: BX.value = value; break;
|
|
case 4: SP = value; break;
|
|
case 5: BP = value; break;
|
|
case 6: SI = value; break;
|
|
case 7: DI = value; break;
|
|
|
|
default:
|
|
std::cerr << "invalid reg code for 8-bit reg";
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint16_t get_displacement(uint8_t size) {
|
|
uint16_t disp = 0;
|
|
uint32_t addr = physical_addr(CS, IP);
|
|
|
|
if (size == 1) { // 8-bit
|
|
disp = static_cast<int8_t>(ram[addr]);
|
|
IP++;
|
|
} else if (size == 2) {
|
|
disp = ram[addr] | (ram[addr + 1] << 8);
|
|
IP += 2;
|
|
}
|
|
|
|
return disp;
|
|
}
|
|
|
|
uint32_t calc_effective_addr(ModRM modrm) {
|
|
uint16_t base = 0;
|
|
uint16_t disp = 0;
|
|
bool has_disp = false;
|
|
|
|
switch (modrm.rm) {
|
|
case 0: base = BX.value + SI; break;
|
|
case 1: base = BX.value + DI; break;
|
|
case 2: base = BP + SI; break;
|
|
case 3: base = BP + DI; break;
|
|
case 4: base = SI; break;
|
|
case 5: base = DI; break;
|
|
case 6:
|
|
if (modrm.mod == 0) {
|
|
disp = get_displacement(2);
|
|
base = 0; // Для прямого адреса
|
|
} else base = BP;
|
|
break;
|
|
case 7: base = BX.value; break;
|
|
}
|
|
|
|
if (modrm.mod == 1) {
|
|
disp = get_displacement(1);
|
|
has_disp = true;
|
|
} else if (modrm.mod == 2) {
|
|
disp = get_displacement(2);
|
|
has_disp = true;
|
|
}
|
|
|
|
return base + (has_disp ? disp : 0);
|
|
}
|
|
|
|
void update_flags_add(uint8_t b1, uint8_t b2, uint8_t res) {
|
|
CF = (static_cast<uint16_t>(b1) + static_cast<uint16_t>(b2)) > 0xFF;
|
|
ZF = res == 0;
|
|
SF = (res & 0x80) != 0;
|
|
|
|
uint8_t count = 0;
|
|
for (int i = 0; i < 8; ++i)
|
|
count += (res >> i) & 1;
|
|
PF = (count % 2) == 0;
|
|
|
|
AF = ((b1 & 0xF) + (b2 & 0xF)) > 0xF;
|
|
OF = (~(b1 ^ b2) & (b2 ^ res) & 0x80) != 0;
|
|
}
|
|
|
|
void update_flags_add16(uint16_t w1, uint16_t w2, uint16_t res) {
|
|
CF = (static_cast<uint32_t>(w1) + static_cast<uint32_t>(w2)) > 0xFFFF;
|
|
ZF = (res == 0);
|
|
SF = (res & 0x8000) != 0;
|
|
|
|
uint8_t low_byte = res & 0xFF;
|
|
uint8_t count = 0;
|
|
for (int i = 0; i < 8; i++)
|
|
count += (low_byte >> i) & 1;
|
|
PF = (count % 2 == 0);
|
|
|
|
AF = ((w1 & 0xF) + (w2 & 0xF)) > 0xF;
|
|
OF = ((w1 ^ res) & (w2 ^ res) & 0x8000) != 0;
|
|
} |