Spaces:
Runtime error
Runtime error
use crate::cpu::cpu::*; | |
use crate::cpu::global_pointers::*; | |
use crate::cpu::memory; | |
use crate::cpu::misc_instr::{getaf, getcf, getzf}; | |
fn int_log2(x: i32) -> i32 { 31 - x.leading_zeros() as i32 } | |
fn opsize_to_mask(op_size: i32) -> i32 { | |
dbg_assert!(op_size == OPSIZE_8 || op_size == OPSIZE_16 || op_size == OPSIZE_32); | |
(2 << op_size) - 1 | |
} | |
unsafe fn add(dest_operand: i32, source_operand: i32, op_size: i32) -> i32 { | |
let res = (dest_operand + source_operand) & opsize_to_mask(op_size); | |
*last_op1 = dest_operand; | |
*last_result = res; | |
*last_op_size = op_size; | |
*flags_changed = FLAGS_ALL; | |
return res; | |
} | |
unsafe fn adc(dest_operand: i32, source_operand: i32, op_size: i32) -> i32 { | |
let cf = getcf() as i32; | |
let res = (dest_operand + source_operand + cf) & opsize_to_mask(op_size); | |
*last_op1 = dest_operand; | |
*last_result = res; | |
*last_op_size = op_size; | |
*flags_changed = FLAGS_ALL & !FLAG_CARRY & !FLAG_ADJUST & !FLAG_OVERFLOW; | |
*flags = *flags & !FLAG_CARRY & !FLAG_ADJUST & !FLAG_OVERFLOW | |
| (dest_operand ^ ((dest_operand ^ source_operand) & (source_operand ^ res))) >> op_size | |
& FLAG_CARRY | |
| (dest_operand ^ source_operand ^ res) & FLAG_ADJUST | |
| ((source_operand ^ res) & (dest_operand ^ res)) >> op_size << 11 & FLAG_OVERFLOW; | |
return res; | |
} | |
unsafe fn sub(dest_operand: i32, source_operand: i32, op_size: i32) -> i32 { | |
let res = (dest_operand - source_operand) & opsize_to_mask(op_size); | |
*last_op1 = dest_operand; | |
*last_result = res; | |
*last_op_size = op_size; | |
*flags_changed = FLAGS_ALL | FLAG_SUB; | |
return res; | |
} | |
unsafe fn sbb(dest_operand: i32, source_operand: i32, op_size: i32) -> i32 { | |
let cf = getcf() as i32; | |
let res = (dest_operand - source_operand - cf) & opsize_to_mask(op_size); | |
*last_op1 = dest_operand; | |
*last_result = res; | |
*last_op_size = op_size; | |
*flags_changed = FLAGS_ALL & !FLAG_CARRY & !FLAG_ADJUST & !FLAG_OVERFLOW | FLAG_SUB; | |
*flags = *flags & !FLAG_CARRY & !FLAG_ADJUST & !FLAG_OVERFLOW | |
| (res ^ ((res ^ source_operand) & (source_operand ^ dest_operand))) >> op_size | |
& FLAG_CARRY | |
| (dest_operand ^ source_operand ^ res) & FLAG_ADJUST | |
| ((source_operand ^ dest_operand) & (res ^ dest_operand)) >> op_size << 11 & FLAG_OVERFLOW; | |
return res; | |
} | |
pub unsafe fn add8(x: i32, y: i32) -> i32 { | |
dbg_assert!(x >= 0 && x < 0x100); | |
dbg_assert!(y >= 0 && y < 0x100); | |
return add(x, y, OPSIZE_8); | |
} | |
pub unsafe fn add16(x: i32, y: i32) -> i32 { | |
dbg_assert!(x >= 0 && x < 0x10000); | |
dbg_assert!(y >= 0 && y < 0x10000); | |
return add(x, y, OPSIZE_16); | |
} | |
pub unsafe fn add32(x: i32, y: i32) -> i32 { return add(x, y, OPSIZE_32); } | |
pub unsafe fn sub8(x: i32, y: i32) -> i32 { return sub(x, y, OPSIZE_8); } | |
pub unsafe fn sub16(x: i32, y: i32) -> i32 { return sub(x, y, OPSIZE_16); } | |
pub unsafe fn sub32(x: i32, y: i32) -> i32 { return sub(x, y, OPSIZE_32); } | |
pub unsafe fn adc8(x: i32, y: i32) -> i32 { return adc(x, y, OPSIZE_8); } | |
pub unsafe fn adc16(x: i32, y: i32) -> i32 { return adc(x, y, OPSIZE_16); } | |
pub unsafe fn adc32(x: i32, y: i32) -> i32 { return adc(x, y, OPSIZE_32); } | |
pub unsafe fn sbb8(x: i32, y: i32) -> i32 { return sbb(x, y, OPSIZE_8); } | |
pub unsafe fn sbb16(x: i32, y: i32) -> i32 { return sbb(x, y, OPSIZE_16); } | |
pub unsafe fn sbb32(x: i32, y: i32) -> i32 { return sbb(x, y, OPSIZE_32); } | |
pub unsafe fn cmp8(x: i32, y: i32) { | |
dbg_assert!(x >= 0 && x < 0x100); | |
dbg_assert!(y >= 0 && y < 0x100); | |
sub(x, y, OPSIZE_8); | |
} | |
pub unsafe fn cmp16(x: i32, y: i32) { | |
dbg_assert!(x >= 0 && x < 0x10000); | |
dbg_assert!(y >= 0 && y < 0x10000); | |
sub(x, y, OPSIZE_16); | |
} | |
pub unsafe fn cmp32(x: i32, y: i32) { sub(x, y, OPSIZE_32); } | |
unsafe fn inc(dest_operand: i32, op_size: i32) -> i32 { | |
*flags = *flags & !1 | getcf() as i32; | |
let res = (dest_operand + 1) & opsize_to_mask(op_size); | |
*last_op1 = dest_operand; | |
*last_result = res; | |
*last_op_size = op_size; | |
*flags_changed = FLAGS_ALL & !1; | |
return res; | |
} | |
unsafe fn dec(dest_operand: i32, op_size: i32) -> i32 { | |
*flags = *flags & !1 | getcf() as i32; | |
let res = (dest_operand - 1) & opsize_to_mask(op_size); | |
*last_op1 = dest_operand; | |
*last_result = res; | |
*last_op_size = op_size; | |
*flags_changed = FLAGS_ALL & !1 | FLAG_SUB; | |
return res; | |
} | |
pub unsafe fn inc8(x: i32) -> i32 { return inc(x, OPSIZE_8); } | |
pub unsafe fn inc16(x: i32) -> i32 { return inc(x, OPSIZE_16); } | |
pub unsafe fn inc32(x: i32) -> i32 { return inc(x, OPSIZE_32); } | |
pub unsafe fn dec8(x: i32) -> i32 { return dec(x, OPSIZE_8); } | |
pub unsafe fn dec16(x: i32) -> i32 { return dec(x, OPSIZE_16); } | |
pub unsafe fn dec32(x: i32) -> i32 { return dec(x, OPSIZE_32); } | |
unsafe fn neg(dest_operand: i32, op_size: i32) -> i32 { sub(0, dest_operand, op_size) } | |
pub unsafe fn not8(x: i32) -> i32 { return !x; } | |
pub unsafe fn neg8(x: i32) -> i32 { return neg(x, OPSIZE_8); } | |
pub unsafe fn neg16(x: i32) -> i32 { return neg(x, OPSIZE_16); } | |
pub unsafe fn neg32(x: i32) -> i32 { return neg(x, OPSIZE_32); } | |
pub unsafe fn mul8(source_operand: i32) { | |
let result = source_operand * read_reg8(AL); | |
write_reg16(AX, result); | |
*last_result = result & 255; | |
*last_op_size = OPSIZE_8; | |
if result < 256 { | |
*flags &= !1 & !FLAG_OVERFLOW | |
} | |
else { | |
*flags |= 1 | FLAG_OVERFLOW | |
} | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
} | |
pub unsafe fn imul8(source_operand: i32) { | |
let result = source_operand * (read_reg8(AL) << 24 >> 24); | |
write_reg16(AX, result); | |
*last_result = result & 255; | |
*last_op_size = OPSIZE_8; | |
if result > 127 || result < -128 { | |
*flags |= 1 | FLAG_OVERFLOW | |
} | |
else { | |
*flags &= !1 & !FLAG_OVERFLOW | |
} | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
} | |
pub unsafe fn mul16(source_operand: u32) { | |
let result = source_operand * read_reg16(AX) as u32; | |
let high_result = result >> 16; | |
write_reg16(AX, result as i32); | |
write_reg16(DX, high_result as i32); | |
*last_result = (result & 0xFFFF) as i32; | |
*last_op_size = OPSIZE_16; | |
if high_result == 0 { | |
*flags &= !1 & !FLAG_OVERFLOW | |
} | |
else { | |
*flags |= 1 | FLAG_OVERFLOW | |
} | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
} | |
pub unsafe fn imul16(source_operand: i32) { | |
let result = source_operand * (read_reg16(AX) << 16 >> 16); | |
write_reg16(AX, result); | |
write_reg16(DX, result >> 16); | |
*last_result = result & 0xFFFF; | |
*last_op_size = OPSIZE_16; | |
if result > 32767 || result < -32768 { | |
*flags |= 1 | FLAG_OVERFLOW | |
} | |
else { | |
*flags &= !1 & !FLAG_OVERFLOW | |
} | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
} | |
pub unsafe fn imul_reg16(mut operand1: i32, mut operand2: i32) -> i32 { | |
operand1 = operand1 << 16 >> 16; | |
operand2 = operand2 << 16 >> 16; | |
let result = operand1 * operand2; | |
*last_result = result & 0xFFFF; | |
*last_op_size = OPSIZE_16; | |
if result > 32767 || result < -32768 { | |
*flags |= 1 | FLAG_OVERFLOW | |
} | |
else { | |
*flags &= !1 & !FLAG_OVERFLOW | |
} | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
return result; | |
} | |
pub unsafe fn mul32(source_operand: i32) { | |
let dest_operand = read_reg32(EAX); | |
let result = (dest_operand as u32 as u64) * (source_operand as u32 as u64); | |
let result_low = result as i32; | |
let result_high = (result >> 32) as i32; | |
write_reg32(EAX, result_low); | |
write_reg32(EDX, result_high); | |
*last_result = result_low; | |
*last_op_size = OPSIZE_32; | |
if result_high == 0 { | |
*flags &= !1 & !FLAG_OVERFLOW | |
} | |
else { | |
*flags |= 1 | FLAG_OVERFLOW | |
} | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
} | |
pub unsafe fn imul32(source_operand: i32) { | |
let dest_operand = read_reg32(EAX); | |
let result = dest_operand as i64 * source_operand as i64; | |
let result_low = result as i32; | |
let result_high = (result >> 32) as i32; | |
write_reg32(EAX, result_low); | |
write_reg32(EDX, result_high); | |
*last_result = result_low; | |
*last_op_size = OPSIZE_32; | |
if result_high == result_low >> 31 { | |
*flags &= !1 & !FLAG_OVERFLOW | |
} | |
else { | |
*flags |= 1 | FLAG_OVERFLOW | |
} | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
} | |
pub unsafe fn imul_reg32(operand1: i32, operand2: i32) -> i32 { | |
let result = operand1 as i64 * operand2 as i64; | |
let result_low = result as i32; | |
let result_high = (result >> 32) as i32; | |
*last_result = result_low; | |
*last_op_size = OPSIZE_32; | |
if result_high == result_low >> 31 { | |
*flags &= !1 & !FLAG_OVERFLOW | |
} | |
else { | |
*flags |= 1 | FLAG_OVERFLOW | |
} | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
return result_low; | |
} | |
pub unsafe fn xadd8(source_operand: i32, reg: i32) -> i32 { | |
let tmp = read_reg8(reg); | |
write_reg8(reg, source_operand); | |
return add(source_operand, tmp, OPSIZE_8); | |
} | |
pub unsafe fn xadd16(source_operand: i32, reg: i32) -> i32 { | |
let tmp = read_reg16(reg); | |
write_reg16(reg, source_operand); | |
return add(source_operand, tmp, OPSIZE_16); | |
} | |
pub unsafe fn xadd32(source_operand: i32, reg: i32) -> i32 { | |
let tmp = read_reg32(reg); | |
write_reg32(reg, source_operand); | |
return add(source_operand, tmp, OPSIZE_32); | |
} | |
pub unsafe fn cmpxchg8(data: i32, r: i32) -> i32 { | |
cmp8(read_reg8(AL), data); | |
if getzf() { | |
read_reg8(r) | |
} | |
else { | |
write_reg8(AL, data); | |
data | |
} | |
} | |
pub unsafe fn cmpxchg16(data: i32, r: i32) -> i32 { | |
cmp16(read_reg16(AX), data); | |
if getzf() { | |
read_reg16(r) | |
} | |
else { | |
write_reg16(AX, data); | |
data | |
} | |
} | |
pub unsafe fn cmpxchg32(data: i32, r: i32) -> i32 { | |
cmp32(read_reg32(EAX), data); | |
if getzf() { | |
read_reg32(r) | |
} | |
else { | |
write_reg32(EAX, data); | |
data | |
} | |
} | |
pub unsafe fn bcd_daa() { | |
let old_al = read_reg8(AL); | |
let old_cf = getcf(); | |
let old_af = getaf(); | |
*flags &= !1 & !FLAG_ADJUST; | |
if old_al & 15 > 9 || old_af { | |
write_reg8(AL, read_reg8(AL) + 6); | |
*flags |= FLAG_ADJUST | |
} | |
if old_al > 153 || old_cf { | |
write_reg8(AL, read_reg8(AL) + 96); | |
*flags |= 1 | |
} | |
*last_result = read_reg8(AL); | |
*last_op_size = OPSIZE_8; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_ADJUST & !FLAG_OVERFLOW; | |
} | |
pub unsafe fn bcd_das() { | |
let old_al = read_reg8(AL); | |
let old_cf = getcf(); | |
*flags &= !1; | |
if old_al & 15 > 9 || getaf() { | |
write_reg8(AL, read_reg8(AL) - 6); | |
*flags |= FLAG_ADJUST; | |
*flags = *flags & !1 | old_cf as i32 | (old_al < 6) as i32 | |
} | |
else { | |
*flags &= !FLAG_ADJUST | |
} | |
if old_al > 153 || old_cf { | |
write_reg8(AL, read_reg8(AL) - 96); | |
*flags |= 1 | |
} | |
*last_result = read_reg8(AL); | |
*last_op_size = OPSIZE_8; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_ADJUST & !FLAG_OVERFLOW; | |
} | |
pub unsafe fn bcd_aad(imm8: i32) { | |
let result = read_reg8(AL) + read_reg8(AH) * imm8; | |
*last_result = result & 255; | |
write_reg16(AX, *last_result); | |
*last_op_size = OPSIZE_8; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_ADJUST & !FLAG_OVERFLOW; | |
*flags &= !1 & !FLAG_ADJUST & !FLAG_OVERFLOW; | |
if result > 0xFFFF { | |
*flags |= 1 | |
}; | |
} | |
pub unsafe fn bcd_aam(imm8: i32) { | |
// ascii adjust after multiplication | |
if imm8 == 0 { | |
trigger_de(); | |
} | |
else { | |
let temp = read_reg8(AL); | |
write_reg8(AH, temp as i32 / imm8); | |
write_reg8(AL, temp as i32 % imm8); | |
*last_result = read_reg8(AL); | |
*last_op_size = OPSIZE_8; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_ADJUST & !FLAG_OVERFLOW; | |
*flags &= !1 & !FLAG_ADJUST & !FLAG_OVERFLOW | |
}; | |
} | |
pub unsafe fn bcd_aaa() { | |
if read_reg8(AL) & 15 > 9 || getaf() { | |
write_reg16(AX, read_reg16(AX) + 6); | |
write_reg8(AH, read_reg8(AH) + 1); | |
*flags |= FLAG_ADJUST | 1 | |
} | |
else { | |
*flags &= !FLAG_ADJUST & !1 | |
} | |
write_reg8(AL, read_reg8(AL) & 15); | |
*flags_changed &= !FLAG_ADJUST & !1; | |
} | |
pub unsafe fn bcd_aas() { | |
if read_reg8(AL) & 15 > 9 || getaf() { | |
write_reg16(AX, read_reg16(AX) - 6); | |
write_reg8(AH, read_reg8(AH) - 1); | |
*flags |= FLAG_ADJUST | 1 | |
} | |
else { | |
*flags &= !FLAG_ADJUST & !1 | |
} | |
write_reg8(AL, read_reg8(AL) & 15); | |
*flags_changed &= !FLAG_ADJUST & !1; | |
} | |
unsafe fn and(dest_operand: i32, source_operand: i32, op_size: i32) -> i32 { | |
let result = dest_operand & source_operand; | |
*last_result = result; | |
*last_op_size = op_size; | |
*flags &= !1 & !FLAG_OVERFLOW & !FLAG_ADJUST; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW & !FLAG_ADJUST; | |
return result; | |
} | |
unsafe fn or(dest_operand: i32, source_operand: i32, op_size: i32) -> i32 { | |
let result = dest_operand | source_operand; | |
*last_result = result; | |
*last_op_size = op_size; | |
*flags &= !1 & !FLAG_OVERFLOW & !FLAG_ADJUST; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW & !FLAG_ADJUST; | |
return result; | |
} | |
unsafe fn xor(dest_operand: i32, source_operand: i32, op_size: i32) -> i32 { | |
let result = dest_operand ^ source_operand; | |
*last_result = result; | |
*last_op_size = op_size; | |
*flags &= !1 & !FLAG_OVERFLOW & !FLAG_ADJUST; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW & !FLAG_ADJUST; | |
return result; | |
} | |
pub unsafe fn and8(x: i32, y: i32) -> i32 { return and(x, y, OPSIZE_8); } | |
pub unsafe fn and16(x: i32, y: i32) -> i32 { return and(x, y, OPSIZE_16); } | |
pub unsafe fn and32(x: i32, y: i32) -> i32 { return and(x, y, OPSIZE_32); } | |
pub unsafe fn test8(x: i32, y: i32) { and(x, y, OPSIZE_8); } | |
pub unsafe fn test16(x: i32, y: i32) { and(x, y, OPSIZE_16); } | |
pub unsafe fn test32(x: i32, y: i32) { and(x, y, OPSIZE_32); } | |
pub unsafe fn or8(x: i32, y: i32) -> i32 { return or(x, y, OPSIZE_8); } | |
pub unsafe fn or16(x: i32, y: i32) -> i32 { return or(x, y, OPSIZE_16); } | |
pub unsafe fn or32(x: i32, y: i32) -> i32 { return or(x, y, OPSIZE_32); } | |
pub unsafe fn xor8(x: i32, y: i32) -> i32 { return xor(x, y, OPSIZE_8); } | |
pub unsafe fn xor16(x: i32, y: i32) -> i32 { return xor(x, y, OPSIZE_16); } | |
pub unsafe fn xor32(x: i32, y: i32) -> i32 { return xor(x, y, OPSIZE_32); } | |
pub unsafe fn rol8(dest_operand: i32, mut count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
count &= 7; | |
let result = dest_operand << count | dest_operand >> 8 - count; | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result & 1 | |
| (result << 11 ^ result << 4) & FLAG_OVERFLOW; | |
return result & 0xFF; | |
}; | |
} | |
pub unsafe fn rol16(dest_operand: i32, mut count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
count &= 15; | |
let result = dest_operand << count | dest_operand >> 16 - count; | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result & 1 | |
| (result << 11 ^ result >> 4) & FLAG_OVERFLOW; | |
return result & 0xFFFF; | |
}; | |
} | |
pub unsafe fn rol32(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
let result = ((dest_operand << count) as u32 | dest_operand as u32 >> 32 - count) as i32; | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result & 1 | |
| (result << 11 ^ result >> 20) & FLAG_OVERFLOW; | |
return result; | |
}; | |
} | |
pub unsafe fn rcl8(dest_operand: i32, mut count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
count %= 9; | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
let result = | |
dest_operand << count | (getcf() as i32) << count - 1 | dest_operand >> 9 - count; | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result >> 8 & 1 | |
| (result << 3 ^ result << 4) & FLAG_OVERFLOW; | |
return result & 0xFF; | |
}; | |
} | |
pub unsafe fn rcl16(dest_operand: i32, mut count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
count %= 17; | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
let result = | |
dest_operand << count | (getcf() as i32) << count - 1 | dest_operand >> 17 - count; | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result >> 16 & 1 | |
| (result >> 5 ^ result >> 4) & FLAG_OVERFLOW; | |
return result & 0xFFFF; | |
}; | |
} | |
pub unsafe fn rcl32(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
let mut result = dest_operand << count | (getcf() as i32) << count - 1; | |
if count > 1 { | |
result = (result as u32 | dest_operand as u32 >> 33 - count) as i32 | |
} | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
let b = (dest_operand as u32 >> 32 - count & 1) as i32; | |
*flags = (*flags & !1 & !FLAG_OVERFLOW | b) | (b << 11 ^ result >> 20) & FLAG_OVERFLOW; | |
return result; | |
}; | |
} | |
pub unsafe fn ror8(dest_operand: i32, mut count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
count &= 7; | |
let result = dest_operand >> count | dest_operand << 8 - count; | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result >> 7 & 1 | |
| (result << 4 ^ result << 5) & FLAG_OVERFLOW; | |
return result & 0xFF; | |
}; | |
} | |
pub unsafe fn ror16(dest_operand: i32, mut count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
count &= 15; | |
let result = dest_operand >> count | dest_operand << 16 - count; | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result >> 15 & 1 | |
| (result >> 4 ^ result >> 3) & FLAG_OVERFLOW; | |
return result & 0xFFFF; | |
}; | |
} | |
pub unsafe fn ror32(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
let result = (dest_operand as u32 >> count | (dest_operand << 32 - count) as u32) as i32; | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result >> 31 & 1 | |
| (result >> 20 ^ result >> 19) & FLAG_OVERFLOW; | |
return result; | |
}; | |
} | |
pub unsafe fn rcr8(dest_operand: i32, mut count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
count %= 9; | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
let result = | |
dest_operand >> count | (getcf() as i32) << 8 - count | dest_operand << 9 - count; | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result >> 8 & 1 | |
| (result << 4 ^ result << 5) & FLAG_OVERFLOW; | |
return result & 0xFF; | |
}; | |
} | |
pub unsafe fn rcr16(dest_operand: i32, mut count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
count %= 17; | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
let result = | |
dest_operand >> count | (getcf() as i32) << 16 - count | dest_operand << 17 - count; | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result >> 16 & 1 | |
| (result >> 4 ^ result >> 3) & FLAG_OVERFLOW; | |
return result & 0xFFFF; | |
}; | |
} | |
pub unsafe fn rcr32(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if 0 == count { | |
return dest_operand; | |
} | |
else { | |
let mut result = | |
(dest_operand as u32 >> count | ((getcf() as i32) << 32 - count) as u32) as i32; | |
if count > 1 { | |
result |= dest_operand << 33 - count | |
} | |
*flags_changed &= !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| dest_operand >> count - 1 & 1 | |
| (result >> 20 ^ result >> 19) & FLAG_OVERFLOW; | |
return result; | |
}; | |
} | |
pub unsafe fn div8(source_operand: u32) { | |
if source_operand == 0 { | |
trigger_de(); | |
return; | |
} | |
let target_operand = read_reg16(AX) as u32; | |
let result = target_operand / source_operand; | |
if result >= 0x100 { | |
trigger_de(); | |
return; | |
} | |
write_reg8(AL, result as i32); | |
write_reg8(AH, (target_operand % source_operand) as i32); | |
} | |
pub unsafe fn idiv8(source_operand: i32) { | |
if source_operand == 0 { | |
trigger_de(); | |
return; | |
} | |
let target_operand = read_reg16(AX) << 16 >> 16; | |
let result = target_operand / source_operand; | |
if result >= 0x80 || result < -0x80 { | |
trigger_de(); | |
return; | |
} | |
write_reg8(AL, result); | |
write_reg8(AH, target_operand % source_operand); | |
} | |
pub unsafe fn div16_without_fault(source_operand: u32) -> bool { | |
let target_operand = (read_reg16(AX) | read_reg16(DX) << 16) as u32; | |
let result = match target_operand.checked_div(source_operand) { | |
None => return false, | |
Some(r) => r, | |
}; | |
if result >= 0x10000 { | |
return false; | |
} | |
write_reg16(AX, result as i32); | |
write_reg16(DX, (target_operand % source_operand) as i32); | |
return true; | |
} | |
pub unsafe fn div16(source_operand: u32) { | |
if !div16_without_fault(source_operand) { | |
trigger_de() | |
} | |
} | |
pub unsafe fn idiv16_without_fault(source_operand: i32) -> bool { | |
let target_operand = read_reg16(AX) | read_reg16(DX) << 16; | |
let result = match target_operand.checked_div(source_operand) { | |
None => return false, | |
Some(r) => r, | |
}; | |
if result >= 0x8000 || result < -0x8000 { | |
return false; | |
} | |
write_reg16(AX, result); | |
write_reg16(DX, (target_operand % source_operand) as i32); | |
return true; | |
} | |
pub unsafe fn idiv16(source_operand: i32) { | |
if !idiv16_without_fault(source_operand) { | |
trigger_de() | |
} | |
} | |
pub unsafe fn div32_without_fault(source_operand: u32) -> bool { | |
let source_operand = source_operand as u64; | |
let target_low = read_reg32(EAX) as u32; | |
let target_high = read_reg32(EDX) as u32; | |
let target_operand = (target_high as u64) << 32 | target_low as u64; | |
let result = match target_operand.checked_div(source_operand) { | |
None => return false, | |
Some(r) => r, | |
}; | |
if result > 0xFFFFFFFF { | |
return false; | |
} | |
let modulo = target_operand % source_operand; | |
write_reg32(EAX, result as i32); | |
write_reg32(EDX, modulo as i32); | |
return true; | |
} | |
pub unsafe fn div32(source_operand: u32) { | |
if !div32_without_fault(source_operand) { | |
trigger_de() | |
} | |
} | |
pub unsafe fn idiv32_without_fault(source_operand: i32) -> bool { | |
let source_operand = source_operand as i64; | |
let target_low = read_reg32(EAX) as u32; | |
let target_high = read_reg32(EDX) as u32; | |
let target_operand = (target_high as i64) << 32 | target_low as i64; | |
let result = match target_operand.checked_div(source_operand) { | |
None => return false, | |
Some(r) => r, | |
}; | |
if result < -0x80000000 || result > 0x7FFFFFFF { | |
return false; | |
} | |
let modulo = target_operand % source_operand; | |
write_reg32(EAX, result as i32); | |
write_reg32(EDX, modulo as i32); | |
return true; | |
} | |
pub unsafe fn idiv32(source_operand: i32) { | |
if !idiv32_without_fault(source_operand) { | |
trigger_de() | |
} | |
} | |
pub unsafe fn shl8(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result = dest_operand << count; | |
*last_result = result; | |
*last_op_size = OPSIZE_8; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result >> 8 & 1 | |
| (result << 3 ^ result << 4) & FLAG_OVERFLOW; | |
return result & 0xFF; | |
}; | |
} | |
pub unsafe fn shl16(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result = dest_operand << count; | |
*last_result = result; | |
*last_op_size = OPSIZE_16; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| result >> 16 & 1 | |
| (result >> 5 ^ result >> 4) & FLAG_OVERFLOW; | |
return result & 0xFFFF; | |
}; | |
} | |
pub unsafe fn shl32(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result = dest_operand << count; | |
*last_result = result; | |
*last_op_size = OPSIZE_32; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
let b = dest_operand >> 32 - count & 1; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | b | (b ^ result >> 31) << 11 & FLAG_OVERFLOW; | |
return result; | |
}; | |
} | |
pub unsafe fn shr8(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result = dest_operand >> count; | |
*last_result = result; | |
*last_op_size = OPSIZE_8; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| dest_operand >> count - 1 & 1 | |
| (dest_operand >> 7 & 1) << 11 & FLAG_OVERFLOW; | |
return result; | |
}; | |
} | |
pub unsafe fn shr16(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result = dest_operand >> count; | |
*last_result = result; | |
*last_op_size = OPSIZE_16; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | |
| dest_operand >> count - 1 & 1 | |
| dest_operand >> 4 & FLAG_OVERFLOW; | |
return result; | |
}; | |
} | |
pub unsafe fn shr32(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result = (dest_operand as u32 >> count) as i32; | |
*last_result = result; | |
*last_op_size = OPSIZE_32; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
*flags = (*flags & !1 & !FLAG_OVERFLOW) | |
| (dest_operand as u32 >> count - 1 & 1) as i32 | |
| (dest_operand >> 20 & FLAG_OVERFLOW); | |
return result; | |
}; | |
} | |
pub unsafe fn sar8(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result; | |
if count < 8 { | |
result = dest_operand << 24 >> count + 24; | |
// of is zero | |
*flags = *flags & !1 & !FLAG_OVERFLOW | dest_operand >> count - 1 & 1 | |
} | |
else { | |
result = dest_operand << 24 >> 31; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | result & 1 | |
} | |
*last_result = result; | |
*last_op_size = OPSIZE_8; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
return result & 0xFF; | |
}; | |
} | |
pub unsafe fn sar16(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result; | |
if count < 16 { | |
result = dest_operand << 16 >> count + 16; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | dest_operand >> count - 1 & 1 | |
} | |
else { | |
result = dest_operand << 16 >> 31; | |
*flags = *flags & !1 & !FLAG_OVERFLOW | result & 1 | |
} | |
*last_result = result; | |
*last_op_size = OPSIZE_16; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
return result & 0xFFFF; | |
}; | |
} | |
pub unsafe fn sar32(dest_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result = dest_operand >> count; | |
*last_result = result; | |
*last_op_size = OPSIZE_32; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
*flags = (*flags & !1 & !FLAG_OVERFLOW) | (dest_operand as u32 >> count - 1 & 1) as i32; | |
return result; | |
}; | |
} | |
pub unsafe fn shrd16(dest_operand: i32, source_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result; | |
if count <= 16 { | |
result = dest_operand >> count | source_operand << 16 - count; | |
*flags = *flags & !1 | dest_operand >> count - 1 & 1 | |
} | |
else { | |
result = dest_operand << 32 - count | source_operand >> count - 16; | |
*flags = *flags & !1 | source_operand >> count - 17 & 1 | |
} | |
*last_result = result; | |
*last_op_size = OPSIZE_16; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !FLAG_OVERFLOW | (result ^ dest_operand) >> 4 & FLAG_OVERFLOW; | |
return result & 0xFFFF; | |
}; | |
} | |
pub unsafe fn shrd32(dest_operand: i32, source_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result = (dest_operand as u32 >> count | (source_operand << 32 - count) as u32) as i32; | |
*last_result = result; | |
*last_op_size = OPSIZE_32; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
*flags = ((*flags & !1 & !FLAG_OVERFLOW) | (dest_operand as u32 >> count - 1 & 1) as i32) | |
| (result ^ dest_operand) >> 20 & FLAG_OVERFLOW; | |
return result; | |
}; | |
} | |
pub unsafe fn shld16(dest_operand: i32, source_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result; | |
if count <= 16 { | |
result = ((dest_operand << count) as u32 | source_operand as u32 >> 16 - count) as i32; | |
*flags = (*flags & !1) | (dest_operand as u32 >> 16 - count & 1) as i32; | |
} | |
else { | |
result = dest_operand >> 32 - count | source_operand << count - 16; | |
*flags = (*flags & !1) | (source_operand as u32 >> 32 - count & 1) as i32; | |
} | |
*last_result = result; | |
*last_op_size = OPSIZE_16; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
*flags = *flags & !FLAG_OVERFLOW | (*flags & 1 ^ result >> 15 & 1) << 11; | |
return result & 0xFFFF; | |
}; | |
} | |
pub unsafe fn shld32(dest_operand: i32, source_operand: i32, count: i32) -> i32 { | |
dbg_assert!(count >= 0 && count < 32); | |
if count == 0 { | |
return dest_operand; | |
} | |
else { | |
let result = ((dest_operand << count) as u32 | source_operand as u32 >> 32 - count) as i32; | |
*last_result = result; | |
*last_op_size = OPSIZE_32; | |
*flags_changed = FLAGS_ALL & !1 & !FLAG_OVERFLOW; | |
*flags = (*flags & !1) | (dest_operand as u32 >> 32 - count & 1) as i32; | |
if count == 1 { | |
*flags = *flags & !FLAG_OVERFLOW | (*flags & 1 ^ result >> 31 & 1) << 11 | |
} | |
else { | |
*flags &= !FLAG_OVERFLOW | |
} | |
return result; | |
}; | |
} | |
pub unsafe fn bt_reg(bit_base: i32, bit_offset: i32) { | |
*flags = *flags & !1 | bit_base >> bit_offset & 1; | |
*flags_changed &= !1; | |
} | |
pub unsafe fn btc_reg(bit_base: i32, bit_offset: i32) -> i32 { | |
*flags = *flags & !1 | bit_base >> bit_offset & 1; | |
*flags_changed &= !1; | |
return bit_base ^ 1 << bit_offset; | |
} | |
pub unsafe fn bts_reg(bit_base: i32, bit_offset: i32) -> i32 { | |
*flags = *flags & !1 | bit_base >> bit_offset & 1; | |
*flags_changed &= !1; | |
return bit_base | 1 << bit_offset; | |
} | |
pub unsafe fn btr_reg(bit_base: i32, bit_offset: i32) -> i32 { | |
*flags = *flags & !1 | bit_base >> bit_offset & 1; | |
*flags_changed &= !1; | |
return bit_base & !(1 << bit_offset); | |
} | |
pub unsafe fn bt_mem(virt_addr: i32, mut bit_offset: i32) { | |
let bit_base = return_on_pagefault!(safe_read8(virt_addr + (bit_offset >> 3))); | |
bit_offset &= 7; | |
*flags = *flags & !1 | bit_base >> bit_offset & 1; | |
*flags_changed &= !1; | |
} | |
pub unsafe fn btc_mem(virt_addr: i32, mut bit_offset: i32) { | |
let phys_addr = return_on_pagefault!(translate_address_write(virt_addr + (bit_offset >> 3))); | |
let bit_base = memory::read8(phys_addr); | |
bit_offset &= 7; | |
*flags = *flags & !1 | bit_base >> bit_offset & 1; | |
*flags_changed &= !1; | |
memory::write8(phys_addr, bit_base ^ 1 << bit_offset); | |
} | |
pub unsafe fn btr_mem(virt_addr: i32, mut bit_offset: i32) { | |
let phys_addr = return_on_pagefault!(translate_address_write(virt_addr + (bit_offset >> 3))); | |
let bit_base = memory::read8(phys_addr); | |
bit_offset &= 7; | |
*flags = *flags & !1 | bit_base >> bit_offset & 1; | |
*flags_changed &= !1; | |
memory::write8(phys_addr, bit_base & !(1 << bit_offset)); | |
} | |
pub unsafe fn bts_mem(virt_addr: i32, mut bit_offset: i32) { | |
let phys_addr = return_on_pagefault!(translate_address_write(virt_addr + (bit_offset >> 3))); | |
let bit_base = memory::read8(phys_addr); | |
bit_offset &= 7; | |
*flags = *flags & !1 | bit_base >> bit_offset & 1; | |
*flags_changed &= !1; | |
memory::write8(phys_addr, bit_base | 1 << bit_offset); | |
} | |
pub unsafe fn bsf16(old: i32, bit_base: i32) -> i32 { | |
*flags_changed = FLAGS_ALL & !FLAG_ZERO & !FLAG_CARRY; | |
*flags &= !FLAG_CARRY; | |
*last_op_size = OPSIZE_16; | |
if bit_base == 0 { | |
*flags |= FLAG_ZERO; | |
*last_result = bit_base; | |
// not defined in the docs, but value doesn't change on my intel machine | |
return old; | |
} | |
else { | |
*flags &= !FLAG_ZERO; | |
*last_result = int_log2(-bit_base & bit_base); | |
return *last_result; | |
}; | |
} | |
pub unsafe fn bsf32(old: i32, bit_base: i32) -> i32 { | |
*flags_changed = FLAGS_ALL & !FLAG_ZERO & !FLAG_CARRY; | |
*flags &= !FLAG_CARRY; | |
*last_op_size = OPSIZE_32; | |
if bit_base == 0 { | |
*flags |= FLAG_ZERO; | |
*last_result = bit_base; | |
return old; | |
} | |
else { | |
*flags &= !FLAG_ZERO; | |
*last_result = int_log2(-bit_base & bit_base); | |
return *last_result; | |
}; | |
} | |
pub unsafe fn bsr16(old: i32, bit_base: i32) -> i32 { | |
*flags_changed = FLAGS_ALL & !FLAG_ZERO & !FLAG_CARRY; | |
*flags &= !FLAG_CARRY; | |
*last_op_size = OPSIZE_16; | |
if bit_base == 0 { | |
*flags |= FLAG_ZERO; | |
*last_result = bit_base; | |
return old; | |
} | |
else { | |
*flags &= !FLAG_ZERO; | |
*last_result = int_log2(bit_base); | |
return *last_result; | |
}; | |
} | |
pub unsafe fn bsr32(old: i32, bit_base: i32) -> i32 { | |
*flags_changed = FLAGS_ALL & !FLAG_ZERO & !FLAG_CARRY; | |
*flags &= !FLAG_CARRY; | |
*last_op_size = OPSIZE_32; | |
if bit_base == 0 { | |
*flags |= FLAG_ZERO; | |
*last_result = bit_base; | |
return old; | |
} | |
else { | |
*flags &= !FLAG_ZERO; | |
*last_result = int_log2(bit_base); | |
return *last_result; | |
}; | |
} | |
pub unsafe fn popcnt(v: i32) -> i32 { | |
*flags_changed = 0; | |
*flags &= !FLAGS_ALL; | |
if 0 != v { | |
return v.count_ones() as i32; | |
} | |
else { | |
*flags |= FLAG_ZERO; | |
return 0; | |
}; | |
} | |
pub unsafe fn saturate_sw_to_ub(v: u16) -> u8 { | |
let mut ret = v; | |
if ret >= 32768 { | |
ret = 0 | |
} | |
else if ret > 255 { | |
ret = 255 | |
} | |
return ret as u8; | |
} | |
pub unsafe fn saturate_sw_to_sb(v: i32) -> u8 { | |
dbg_assert!(v as u32 & 0xFFFF_0000 == 0); | |
let mut ret = v; | |
if ret > 65408 { | |
ret = ret & 255 | |
} | |
else if ret > 32767 { | |
ret = 128 | |
} | |
else if ret > 127 { | |
ret = 127 | |
} | |
dbg_assert!(ret as u32 & 0xFFFF_FF00 == 0); | |
return ret as u8; | |
} | |
pub unsafe fn saturate_sd_to_sw(v: u32) -> u16 { | |
let mut ret = v; | |
if ret > 4294934528 { | |
ret = ret & 0xFFFF | |
} | |
else if ret > 0x7FFFFFFF { | |
ret = 32768 | |
} | |
else if ret > 32767 { | |
ret = 32767 | |
} | |
dbg_assert!(ret & 0xFFFF_0000 == 0); | |
return ret as u16; | |
} | |
pub unsafe fn saturate_sd_to_sb(v: u32) -> i8 { | |
let mut ret = v; | |
if ret > 0xFFFFFF80 { | |
ret = ret & 255 | |
} | |
else if ret > 0x7FFFFFFF { | |
ret = 128 | |
} | |
else if ret > 127 { | |
ret = 127 | |
} | |
dbg_assert!(ret & 0xFFFF_FF00 == 0); | |
return ret as i8; | |
} | |
pub unsafe fn saturate_sd_to_ub(v: i32) -> i32 { | |
let mut ret = v; | |
if ret < 0 { | |
ret = 0 | |
} | |
dbg_assert!(ret as u32 & 0xFFFF_FF00 == 0); | |
return ret; | |
} | |
pub unsafe fn saturate_ud_to_ub(v: u32) -> u8 { | |
let mut ret = v; | |
if ret > 255 { | |
ret = 255 | |
} | |
dbg_assert!(ret & 0xFFFF_FF00 == 0); | |
return ret as u8; | |
} | |
pub unsafe fn saturate_uw(v: u32) -> u16 { | |
let mut ret = v; | |
if ret > 0x7FFFFFFF { | |
ret = 0 | |
} | |
else if ret > 0xFFFF { | |
ret = 0xFFFF | |
} | |
dbg_assert!(ret & 0xFFFF_0000 == 0); | |
return ret as u16; | |
} | |