-
Notifications
You must be signed in to change notification settings - Fork 1
8086tiny
Christian Mayer edited this page Aug 13, 2018
·
12 revisions
For this project I used 8086tiny as a reference to understand how the Intel 8086 works.
This page should help to understand the 8086tiny functions better.
// Reinterpretation cast
#define CAST(a) *(a*)&
// Returns number of top bit in operand (i.e. 8 for 8-bit operands, 16 for 16-bit operands)
#define TOP_BIT 8 * (i_w + 1)
if (i_w) {
16;
} else {
8;
}
#define GET_REG_ADDR(reg_id) (REGS_BASE + (i_w ? 2 * reg_id : 2 * reg_id + reg_id / 4 & 7))
REGS_BASE + (
i_w
? 2 * reg_id
: 2 * reg_id + reg_id / 4 & 7
)
#define SEGREG(reg_seg,reg_ofs,op) 16 * regs16[reg_seg] + (unsigned short)(op regs16[reg_ofs])
#define DECODE_RM_REG scratch2_uint = 4 * !i_mod, \
op_to_addr = rm_addr = i_mod < 3 ? SEGREG(seg_override_en ? seg_override : bios_table_lookup[scratch2_uint + 3][i_rm], bios_table_lookup[scratch2_uint][i_rm], regs16[bios_table_lookup[scratch2_uint + 1][i_rm]] + bios_table_lookup[scratch2_uint + 2][i_rm] * i_data1+) : GET_REG_ADDR(i_rm), \
op_from_addr = GET_REG_ADDR(i_reg), \
i_d && (scratch_uint = op_from_addr, op_from_addr = rm_addr, op_to_addr = scratch_uint)
scratch2_uint = 4 * !i_mod;
if (i_mod < 3){
if (seg_override_en){
reg_seg = seg_override;
} else {
reg_seg = bios_table_lookup[scratch2_uint + 3][i_rm];
}
reg_ofs = bios_table_lookup[scratch2_uint][i_rm];
op_to_addr = rm_addr =
16 * regs16[reg_seg]
+ (unsigned short)(
regs16[bios_table_lookup[scratch2_uint + 1][i_rm]]
+ bios_table_lookup[scratch2_uint + 2][i_rm] * i_data1
+ regs16[reg_ofs]
);
}else{
op_to_addr = rm_addr =
REGS_BASE + (i_w ? 2 * i_rm : 2 * i_rm + i_rm / 4 & 7);
}
op_from_addr = REGS_BASE + (i_w ? 2 * i_reg : 2 * i_reg + i_reg / 4 & 7);
if (i_d){
scratch_uint = op_from_addr;
op_from_addr = rm_addr;
op_to_addr = scratch_uint;
}
#define R_M_OP(dest,op,src) (i_w ? op_dest = CAST(unsigned short)dest, op_result = CAST(unsigned short)dest op (op_source = CAST(unsigned short)src) \
: (op_dest = dest, op_result = dest op (op_source = CAST(unsigned char)src)))
if (i_w) {
op_dest = CAST(unsigned short)dest;
op_source = CAST(unsigned short)src;
op_result = CAST(unsigned short)dest op op_source;
} else {
op_dest = dest;
op_source = CAST(unsigned char)src;
op_result = dest op (op_source);
}
// Returns sign bit of an 8-bit or 16-bit operand
#define SIGN_OF(a) (1 & (i_w ? CAST(short)a : a) >> (TOP_BIT - 1))
if (i_w) {
(1 & CAST(short)a) >> (8 * (i_w + 1) - 1);
} else {
(1 & a) >> (8 * (i_w + 1) - 1);
}
// [I]MUL/[I]DIV/DAA/DAS/ADC/SBB helpers
#define MUL_MACRO(op_data_type,out_regs) (set_opcode(0x10), \
out_regs[i_w + 1] = (op_result = CAST(op_data_type)mem[rm_addr] * (op_data_type)*out_regs) >> 16, \
regs16[REG_AX] = op_result, \
set_OF(set_CF(op_result - (op_data_type)op_result)))
(
set_opcode(0x10);
op_result = CAST(op_data_type)mem[rm_addr] * (op_data_type)*out_regs;
out_regs[i_w + 1] = op_result >> 16;
regs16[REG_AX] = op_result;
int setCf = op_result - (op_data_type)op_result;
set_CF(setCf);
set_OF(setCf);
)
#define DIV_MACRO(out_data_type,in_data_type,out_regs) (scratch_int = CAST(out_data_type)mem[rm_addr]) && !(scratch2_uint = (in_data_type)(scratch_uint = (out_regs[i_w+1] << 16) + regs16[REG_AX]) / scratch_int, scratch2_uint - (out_data_type)scratch2_uint) ? out_regs[i_w+1] = scratch_uint - scratch_int * (*out_regs = scratch2_uint) : pc_interrupt(0)
scratch_int = CAST(out_data_type)mem[rm_addr];
scratch_uint = (out_regs[i_w+1] << 16) + regs16[REG_AX];
scratch2_uint = (in_data_type)scratch_uint / scratch_int;
if(scratch_int && !(scratch2_uint - (out_data_type)scratch2_uint)){
*out_regs = scratch2_uint;
out_regs[i_w+1] = scratch_uint - scratch_int * *out_regs;
} else {
pc_interrupt(0);
}
#define ADC_SBB_MACRO(a) OP(a##= regs8[FLAG_CF] +), \
set_CF(regs8[FLAG_CF] && (op_result == op_dest) || (a op_result < a(int)op_dest)), \
set_AF_OF_arith()
OP(a##= regs8[FLAG_CF] +);
set_CF(regs8[FLAG_CF] && (op_result == op_dest) || (a op_result < a(int)op_dest));
set_AF_OF_arith();
#define INDEX_INC(reg_id) (regs16[reg_id] -= (2 * regs8[FLAG_DF] - 1)*(i_w + 1))
regs16[reg_id] -= (2 * regs8[FLAG_DF] - 1) * (i_w + 1)
reg_ip += (char)i_data0 * (
i_w ^ (
regs8[bios_table_lookup[TABLE_COND_JUMP_DECODE_A][scratch_uchar]]
|| regs8[bios_table_lookup[TABLE_COND_JUMP_DECODE_B][scratch_uchar]]
|| regs8[bios_table_lookup[TABLE_COND_JUMP_DECODE_C][scratch_uchar]] ^ regs8[bios_table_lookup[TABLE_COND_JUMP_DECODE_D][scratch_uchar]]
)
);
#define KEYBOARD_DRIVER read(0, mem + 0x4A6, 1) && (int8_asap = (mem[0x4A6] == 0x1B), pc_interrupt(7))
int x = read(0, mem + 0x4A6, 1);
if (x){
int8_asap = mem[0x4A6] == 0x1B;
pc_interrupt(7);
}
When you look at the 8086tiny.c source code there is the FLAG_XF
macro missing:
#define FLAG_CF 40
#define FLAG_PF 41
#define FLAG_AF 42
#define FLAG_ZF 43
#define FLAG_SF 44
#define FLAG_TF 45
#define FLAG_IF 46
#define FLAG_DF 47
#define FLAG_OF 48
This is the reason why this flag is set manually to 0
in the bios:
xor ax, ax
mov di, 24
stosw ; Set ZS = 0
mov di, 49
stosb ; Set XF = 0
But this is actually wrong. When we translate the FLAG_XF
(number 49) to the real flag bit number we get 12
. Regarding to Wikipedia flags 12 to 15 are reserved and should always be 1
.