• Please review our updated Terms and Rules here

How Emulators Work....

well, this is going to be a long post... here's my implementation of the 8086 (actually 80186) - i know you're not looking to emulate one, but the major ideas demonstrated in this apply to all CPUs.

first, you are going to want to take a look at this document i compiled when i started working on this. it describes the general prefixes/opcode format. it also explains in detail the mode/reg/rm addressing mode byte which is dealt with in some of the code: http://rubbermallet.org/8086 notes.pdf

first the "cpu.h" header file:

Code:
#define regax 0
#define regcx 1
#define regdx 2
#define regbx 3
#define regsp 4
#define regbp 5
#define regsi 6
#define regdi 7
#define reges 0
#define regcs 1
#define regss 2
#define regds 3

#ifdef __BIG_ENDIAN__
     #define regal 1
     #define regah 0
     #define regcl 3
     #define regch 2
     #define regdl 5
     #define regdh 4
     #define regbl 7
     #define regbh 6
#else
     #define regal 0
     #define regah 1
     #define regcl 2
     #define regch 3
     #define regdl 4
     #define regdh 5
     #define regbl 6
     #define regbh 7
#endif

union _bytewordregs_ {
	uint16_t wordregs[8];
	uint8_t byteregs[8];
};

#define StepIP(x) ip+=x
#define getmem8(x,y) read86(segbase(x)+y)
#define getmem16(x,y) (read86(segbase(x)+y) | ((uint16_t)read86(segbase(x)+y+1)<<8))
#define putmem8(x,y,z) write86(segbase(x)+y, z)
#define putmem16(x,y,z) write86(segbase(x)+y, (z)&0xFF); write86(segbase(x)+y+1, (z)>>8) 
#define signext(value) ((((uint16_t)value&0x80)*0x1FE)|(uint16_t)value)
#define signext32(value) ((((uint32_t)value&0x8000)*0x1FFFE)|(uint32_t)value)
#define getreg16(regid) regs.wordregs[regid]
#define getreg8(regid) regs.byteregs[byteregtable[regid]]
#define putreg16(regid, writeval) regs.wordregs[regid] = writeval
#define putreg8(regid, writeval) regs.byteregs[byteregtable[regid]] = writeval
#define getsegreg(regid) segregs[regid]
#define putsegreg(regid, writeval) segregs[regid] = writeval
#define segbase(x) ((uint32_t)x<<4)


and "cpu.c" now... declaring some important stuff at the top.

Code:
#include "cpu.h"

uint16_t segregs[4];
uint8_t opcode = 0, segoverride = 0, reptype = 0;
uint16_t savecs = 0, saveip = 0, ip = 0, useseg = 0, oldsp = 0;
uint8_t tempcf, oldcf, cf, pf, af, zf, sf, tf, ifl, df, of; //stuff for CPU flags
uint8_t mode, reg, rm; //data from adressing mode bytes
uint16_t oper1 = 0, oper2 = 0, res16 = 0, disp16 = 0, temp16 = 0, dummy = 0, stacksize = 0, frametemp = 0;
uint8_t oper1b = 0, oper2b = 0, res8 = 0, disp8 = 0, temp8 = 0, nestlev = 0, addrbyte = 0;
uint32_t temp1 = 0, temp2 = 0, temp3 = 0, temp4 = 0, temp5 = 0, temp32 = 0, tempaddr32 = 0, ea = 0;
int32_t result = 0;
uint64_t totalexec = 0; //keep track of total instructions executed

union _bytewordregs_ regs; //general registers are in a union of uint16 and uint8's

//for example, AX and AH/AL share memory space:
// |     AX      |
// |  AL  |  AH  |


//next is a lookup table for the "register" field in the 8-bit register addressing mode
static const uint8_t byteregtable[8] = { regal, regcl, regdl, regbl, regah, regch, regdh, regbh };

#define makeflagsword() (2 | (uint16_t)cf | ((uint16_t)pf << 2) | ((uint16_t)af << 4) | ((uint16_t)zf << 6) \
        | ((uint16_t)sf << 7) | ((uint16_t)tf << 8) | ((uint16_t)ifl << 9) | ((uint16_t)df << 10) | ((uint16_t)of << 11))

#define decodeflagsword(x) {\
        temp16 = x;\
        cf = temp16 & 1;\
        pf = (temp16 >> 2) & 1;\
        af = (temp16 >> 4) & 1;\
        zf = (temp16 >> 6) & 1;\
        sf = (temp16 >> 7) & 1;\
        tf = (temp16 >> 8) & 1;\
        ifl = (temp16 >> 9) & 1;\
        df = (temp16 >> 10) & 1;\
        of = (temp16 >> 11) & 1;\
}

uint32_t framenum = 0;
extern const char *build;
uint16_t newoffs = 0;
uint8_t interrupt_ok = 0, savesp;
extern uint8_t dopktrecv, pktokay;
extern uint16_t rcvseg, rcvoff, hdrlen, handpkt;

void intcall86(uint8_t intnum);

extern struct struct_drive {
        FILE *diskfile;
        uint32_t filesize;
        uint16_t cyls;
        uint16_t sects;
        uint16_t heads;
        uint8_t inserted;
} disk[256];

extern uint8_t readVGA(uint32_t addr32);
extern void writeVGA(uint32_t addr32, uint8_t value);
extern void vidinterrupt();
extern void diskhandler();
extern void readdisk(uint8_t drivenum, uint16_t dstseg, uint16_t dstoff, uint16_t cyl, uint16_t sect, uint16_t head, uint16_t sectcount);
extern void doirq(uint8_t irqnum);
extern uint8_t nextintr();
extern void portout(uint16_t portnum, uint16_t value);
extern uint16_t portin(uint16_t portnum);


this function is called on CPU power-up/reset:

Code:
void reset86() {
        uint16_t i, cnt, bitcount;
        segregs[regcs] = 0xFFFF; //upon power-up or reset, the CPU's program counter starts at FFFF:0000
        ip = 0x0000;
        
        //generate parity lookup table
        for (i=0; i<256; i++) {
                bitcount = 0;
                for (cnt=0; cnt<8; cnt++)
                bitcount += ((i >> cnt) & 1);
                if (bitcount & 1) parity[i] = 0; else parity[i] = 1;
        }
}


routines to calculate flag states used after most instructions are shown next. i should point out that the flag_log8 and flag_log16 are the flag calculation functions called for following bitwise logical operations: AND, OR, XOR. they calculate only sign, zero, and parity flag values. carry and overflow flags are always cleared by them, as it's not possible for bitwise logic ops to cause a carry/overflow.

Code:
void flag_szp8(uint8_t value) {
   if (!value) zf = 1; else zf = 0;
   if (value & 0x80) sf = 1; else sf = 0;
   pf = parity[value];
}
 
void flag_szp16(uint16_t value) {
     if (!value) zf = 1; else zf = 0;
     if (value & 0x8000) sf = 1; else sf = 0;
     pf = parity[value & 255];
}
 
void flag_log8(uint8_t value) {
     flag_szp8(value);
     cf = 0; of = 0;
}
 
void flag_log16(uint16_t value) {
     flag_szp16(value);
     cf = 0; of = 0;
}
 
void flag_adc8(uint8_t v1, uint8_t v2, uint8_t v3) { //v1 = dest operand, v2 = source operand, v3 = carry flag
        uint16_t dst;
        dst = (uint16_t)v1 + (uint16_t)v2 + (uint16_t)v3;
        flag_szp8((uint8_t)dst); //go set/clear sign, zero, and parity flags
        if (((dst ^ v1) & (dst ^ v2) & 0x80L) == 0x80L) of = 1; else of = 0; //set or clear overflow flag
        if (dst & 0xFF00L) cf = 1; else cf = 0; //set or clear carry flag
        if (((v1 ^ v2 ^ dst) & 0x10L) == 0x10L) af = 1; else af = 0; //set or clear auxiliary flag
}
 
void flag_adc16(uint16_t v1, uint16_t v2, uint16_t v3) {
        uint32_t dst;
        dst = (uint32_t)v1 + (uint32_t)v2 + (uint32_t)v3;
        flag_szp16((uint16_t)dst);
        if ((((dst ^ v1) & (dst ^ v2)) & 0x8000L) == 0x8000L) of = 1; else of = 0;
        if (dst & 0xFFFF0000L) cf = 1; else cf = 0;
        if (((v1 ^ v2 ^ dst) & 0x10L) == 0x10L) af = 1; else af = 0;
}
 
void flag_add8(uint8_t v1, uint8_t v2) {
        uint16_t dst;
        dst = (uint16_t)v1 + (uint16_t)v2;
        flag_szp8((uint8_t)dst);
        if (dst & 0xFF00L) cf = 1; else cf = 0;
        if (((dst ^ v1) & (dst ^ v2) & 0x80L) == 0x80L) of = 1; else of = 0;
        if (((v1 ^ v2 ^ dst) & 0x10L) == 0x10L) af = 1; else af = 0;
}
 
void flag_add16(uint16_t v1, uint16_t v2) {
        uint32_t dst;
        dst = (uint32_t)v1 + (uint32_t)v2;
        flag_szp16((uint16_t)dst);
        if (dst & 0xFFFF0000L) cf = 1; else cf = 0;
        if (((dst ^ v1) & (dst ^ v2) & 0x8000L) == 0x8000L) of = 1; else of = 0;
        if (((v1 ^ v2 ^ dst) & 0x10L) == 0x10L) af = 1; else af = 0;
}
 
void flag_sbb8(uint8_t v1, uint8_t v2, uint8_t v3) {
    uint16_t dst;
    v2 += v3;
    dst = (uint16_t)v1 - (uint16_t)v2;
    flag_szp8(dst & 0xFFL);
    if (dst & 0xFF00L) cf = 1; else cf = 0;
    if ((dst ^ v1) & (v1 ^ v2) & 0x80L) of = 1; else of = 0;
    if ((v1 ^ v2 ^ dst) & 0x10L) af = 1; else af = 0;
}
 
void flag_sbb16(uint16_t v1, uint16_t v2, uint16_t v3) {
    uint32_t dst;
    v2 += v3;
    dst = (uint32_t)v1 - (uint32_t)v2;
    flag_szp16(dst & 0xFFFFL);
    if (dst & 0xFFFF0000L) cf = 1; else cf = 0;
    if ((dst ^ v1) & (v1 ^ v2) & 0x8000L) of = 1; else of = 0;
    if ((v1 ^ v2 ^ dst) & 0x10L) af = 1; else af = 0;
}
 
void flag_sub8(uint8_t v1, uint8_t v2) {
    uint16_t dst;
    dst = (uint16_t)v1 - (uint16_t)v2;
    flag_szp8(dst & 0xFFL);
    if (dst & 0xFF00L) cf = 1; else cf = 0;
    if ((dst ^ v1) & (v1 ^ v2) & 0x80L) of = 1; else of = 0;
    if ((v1 ^ v2 ^ dst) & 0x10L) af = 1; else af = 0;
}
 
void flag_sub16(uint16_t v1, uint16_t v2) {
    uint32_t dst;
    dst = (uint32_t)v1 - (uint32_t)v2;
    flag_szp16(dst & 0xFFFFL);
    if (dst & 0xFFFF0000L) cf = 1; else cf = 0;
    if ((dst ^ v1) & (v1 ^ v2) & 0x8000L) of = 1; else of = 0;
    if ((v1 ^ v2 ^ dst) & 0x10L) af = 1; else af = 0;
}



next, since most instructions are just the same thing using different addressing schemes or registers we have some general routines for the common instructions. oper1/oper2 (16-bit), or oper1b/oper2b (8-bit) are set before calling these where the first is the destination operand and the second is the source operand. the result is stuck into a 16-bit or 8-bit result variable which is used to store the result after these return.

Code:
#define op_adc8() {\
    res8 = oper1b + oper2b + cf;\
    flag_adc8(oper1b, oper2b, cf);\
}

#define op_adc16() {\
    res16 = oper1 + oper2 + cf;\
    flag_adc16(oper1, oper2, cf);\
}

#define op_add8() {\
    res8 = oper1b + oper2b;\
    flag_add8(oper1b, oper2b);\
}

#define op_add16() {\
    res16 = oper1 + oper2;\
    flag_add16(oper1, oper2);\
}

#define op_and8() {\
    res8 = oper1b & oper2b;\
    flag_log8(res8);\
}

#define op_and16() {\
    res16 = oper1 & oper2;\
    flag_log16(res16);\
}

#define op_or8() {\
    res8 = oper1b | oper2b;\
    flag_log8(res8);\
}

#define op_or16() {\
    res16 = oper1 | oper2;\
    flag_log16(res16);\
}

#define op_xor8() {\
    res8 = oper1b ^ oper2b;\
    flag_log8(res8);\
}

#define op_xor16() {\
    res16 = oper1 ^ oper2;\
    flag_log16(res16);\
}

#define op_sub8() {\
    res8 = oper1b - oper2b;\
    flag_sub8(oper1b, oper2b);\
}

#define op_sub16() {\
    res16 = oper1 - oper2;\
    flag_sub16(oper1, oper2);\
}

#define op_sbb8() {\
    res8 = oper1b - (oper2b + cf);\
    flag_sbb8(oper1b, oper2b, cf);\
}

#define op_sbb16() {\
    res16 = oper1 - (oper2 + cf);\
    flag_sbb16(oper1, oper2, cf);\
}


next up, we of course need some functions to read/write memory. the functions to emulate stack pushes and pops are also here.

Code:
uint8_t read86(uint32_t addr32) {
        addr32 &= 0xFFFFF; //just to be safe, AND-mask addr32 to 20 bits
	if ((addr32>=0xA0000) && (addr32<=0xBFFFF)) { //are we reading from video RAM?
            if ((vidmode!=0x13) && (vidmode!=0x12) && (vidmode!=0xD)) return(RAM[addr32]); //is our video state a VGA mode? if not, read from regular RAM.
                else return(readVGA(addr32-0xA0000)); //if it's a VGA mode, send to VGA memory handler for processing (for unchained modes, etc)
	}
        switch (addr32) {
                case 0x410: //0040:0010 is the equipment word, by forcing a value here we simulate the DIP switch on a PC or XT motherboard
                    return(0x41); //report CGA video (0x41 is VGA/EGA, 0x61 is CGA, 0x31 = MDA)
                case 0x475: //hard drive count
                    return(hdcount);
                default:
                    return(RAM[addr32]);
        }
}

void write86(uint32_t addr32, uint8_t value) {
	tempaddr32 = addr32 & 0xFFFFF; //just to be safe, AND-mask addr32 to 20 bits
	if (readonly[tempaddr32]) return; //if requested address is not allowed to be written to, bail out
	if ((tempaddr32>=0xA0000) && (tempaddr32<=0xBFFFF)) { //are we writing to video RAM?
		if ((vidmode!=0x13) && (vidmode!=0x12) RAM[tempaddr32] = value; //is our video state a VGA mode? if not, write to regular RAM.
		else writeVGA(tempaddr32-0xA0000, value); //if it's a VGA mode, send to VGA memory handler for processing (for unchained modes, etc)
	} else {
		RAM[tempaddr32] = value;
	}
}

void push(uint16_t pushval) {
        putreg16(regsp, getreg16(regsp) - 2); //decrement stack pointer by one word
        putmem16(segregs[regss], getreg16(regsp), pushval); //shove our value into there
}

uint16_t pop() {
        uint16_t tempval;
        tempval = getmem16(segregs[regss], getreg16(regsp)); //get word from stack
        putreg16(regsp, getreg16(regsp) + 2); //increment stack pointer by one word
        return(tempval);
}


this next stuff is very important, the 8086 uses an addressing mode byte in most instructions. it contains three fields. the addressing mode (values 0 to 3), register field (0 to 7), and register/memory field (0 to 7)

the mode field indicates the meaning of the register/memory field. refer to the PDF i linked at the beginning of the post. also note that several instructions (called the "GRP" or group instructions) have an addressing mode byte, but the "register" field is actually used as an extension to determine the actual operation to be performed. yes, x86 is freakin' weird.

below are the functions that parse the addressing mode byte, and another one which is used to calculate the effective memory address based on the fields in that byte.

Code:
void modregrm() { //parse values from the addresing mode byte in many instructions
	addrbyte = getmem8(segregs[regcs], ip); StepIP(1); //three bitfields in this, parsed by next 3 lines
	mode = addrbyte >> 6;
	reg = (addrbyte >> 3) & 7;
	rm = addrbyte & 7;
	switch (mode) {
	    case 0:
		if (rm == 6) { disp16 = getmem16(segregs[regcs], ip); StepIP(2); }
		if (((rm == 2) || (rm == 3)) && !segoverride) useseg = segregs[regss]; break;
	    case 1:
		disp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
		if (((rm == 2) || (rm == 3) || (rm == 6)) && !segoverride) useseg = segregs[regss]; break;
	    case 2:
		disp16 = getmem16(segregs[regcs], ip); StepIP(2);
		if (((rm == 2) || (rm == 3) || (rm == 6)) && !segoverride) useseg = segregs[regss]; break;
	    default:
		disp8 = 0; disp16 = 0;
	}
}

void getea(uint8_t rmval) { //calculate effective address for the memory addressing modes
	uint32_t tempea;
	tempea = 0;
	switch (mode) {
		case 0:
		    switch (rmval) {
			case 0: tempea = regs.wordregs[regbx] + regs.wordregs[regsi]; break;
			case 1: tempea = regs.wordregs[regbx] + regs.wordregs[regdi]; break;
			case 2: tempea = regs.wordregs[regbp] + regs.wordregs[regsi]; break;
			case 3: tempea = regs.wordregs[regbp] + regs.wordregs[regdi]; break;
			case 4: tempea = regs.wordregs[regsi]; break;
			case 5: tempea = regs.wordregs[regdi]; break;
			case 6: tempea = disp16; break;
			case 7: tempea = regs.wordregs[regbx]; break;
		    } break;
		case 1: case 2:
		    switch (rmval) {
			case 0: tempea = regs.wordregs[regbx] + regs.wordregs[regsi] + disp16; break;
			case 1: tempea = regs.wordregs[regbx] + regs.wordregs[regdi] + disp16; break;
			case 2: tempea = regs.wordregs[regbp] + regs.wordregs[regsi] + disp16; break;
			case 3: tempea = regs.wordregs[regbp] + regs.wordregs[regdi] + disp16; break;
			case 4: tempea = regs.wordregs[regsi] + disp16; break;
			case 5: tempea = regs.wordregs[regdi] + disp16; break;
			case 6: tempea = regs.wordregs[regbp] + disp16; break;
			case 7: tempea = regs.wordregs[regbx] + disp16; break;
		    } break;
	}
	ea = (tempea & 0xFFFF) + (useseg << 4);
}


the next functions read and write data from registers or memory depending on the meaning of the "register/memory" field in the addr mode byte:

Code:
uint16_t readrm16(uint8_t rmval) {
        if (mode < 3) { //addressing mode table values lower than 3 indicate using RAM
            getea(rmval); //go calculate effective address
            return(read86(ea) | ((uint16_t)read86(ea+1)<<8));
        } else { //mode table 3 means it's a register transaction
            return(getreg16(rmval));
        }
}

uint8_t readrm8(uint8_t rmval) {
        if (mode < 3) { //addressing mode table values lower than 3 indicate using RAM
            getea(rmval); //go calculate effective address
            return(read86(ea));
        } else { //mode table 3 means it's a register transaction
            return(getreg8(rmval));
        }
}

void writerm16(uint8_t rmval, uint16_t value) {
        if (mode < 3) { //addressing mode table values lower than 3 indicate using RAM
            getea(rmval); //go calculate effective address
            write86(ea, value&0xFF);
	    write86(ea+1, value>>8);
        } else { //mode table 3 means it's a register transaction
	    putreg16(rmval, value);
        }
}

void writerm8(uint8_t rmval, uint8_t value) {
        if (mode < 3) { //addressing mode table values lower than 3 indicate using RAM
            getea(rmval); //go calculate effective address
            write86(ea, value);
        } else { //mode table 3 means it's a register transaction
            putreg8(rmval, value);
        }
}


the next instruction is very important, it handles interrupt calls by looking up the entry in the IVT at 0000:0000, pushes the flags, current CS and IP to the stack and then jumps to the handler described by the vector. there are a couple things it checks for first because my PC emu needs to handle some things on a higher level rather than let the actual BIOS ROM handle, like disk accesses on int 13h, etc..

Code:
void intcall86(uint8_t intnum) {
	switch (intnum) {
		case 0x10: //video services
		    if ((regs.byteregs[regah]==0x00) || (regs.byteregs[regah]==0x10)) {
		        vidinterrupt(); //intercept video mode change and palette change interrupt calls for high-level processing
		    }
		    break;

		case 0x13: //disk services, don't let actual BIOS code handle it
		    diskhandler();
		    return;

		case 0x16: //keyboard (the BIOS handles it, but this is to wrap extended keystroke requests to normal functions)
		     if ((regs.byteregs[regah]>=0x10) && (regs.byteregs[regah]<=0x12)) regs.byteregs[regah] -= 0x10;
		     break;

		case 0x19: //bootstrap
		    if (verbose) { sprintf(msg, "BOOTSTRAP!\n"); print(msg); }

		    //read first sector of boot drive into 07C0:0000 and execute it
		    regs.byteregs[regdl] = bootdrive;
		    readdisk(regs.byteregs[regdl], 0x07C0, 0x0000, 0, 1, 0, 1);
		    segregs[regcs] = 0x0000; ip = 0x7C00;
		    return;

		default: break;
	}
	
	push(makeflagsword()); //push flags word
	push(segregs[regcs]); //push CS
	push(ip); //push IP

	segregs[regcs] = getmem16(0, ((uint16_t)intnum<<2)+2); //get interrupt vector to jump to from the IVT at 0x00000h
	ip = getmem16(0, (uint16_t)intnum<<2);
	ifl = 0; //clear interrupt flag
	tf = 0; //clear trap flag
}


this next huge chunk is a big bucket of fun. most of the GRP opcodes have seperate functions here that check the register field in the addressing mode byte to determine the actual operation that should be performed. lots of stuff here, like GRP2 which is bit shifing, others handle NEG, NOT, MUL, DIV, etc.

Code:
uint8_t op_grp2_8(uint8_t cnt) {
	uint16_t s, shift, oldcf, msb;
	//if (cnt>0x8) return(0x7F); //80186+ limits shift count
	s = oper1b;
	oldcf = cf;
	switch (reg) {
		case 0: //ROL r/m8
		for (shift=1; shift<=cnt; shift++) {
			if (s & 0x80) cf = 1; else cf = 0;
			s = s << 1;
			s = s | cf;
		}
		if (cnt==1) of = cf ^ ((s >> 7) & 1);
		break;
		
		case 1: //ROR r/m8
		for (shift=1; shift<=cnt; shift++) {
			cf = s & 1;
			s = (s >> 1) | (cf << 7);
		}
		if (cnt==1) of = (s >> 7) ^ ((s >> 6) & 1);
		break;
		
		case 2: //RCL r/m8
		for (shift=1; shift<=cnt; shift++) {
			oldcf = cf;
			if (s & 0x80) cf = 1; else cf = 0;
			s = s << 1;
			s = s | oldcf;
		}
		if (cnt==1) of = cf ^ ((s >> 7) & 1);
		break;
		
		case 3: //RCR r/m8
		for (shift=1; shift<=cnt; shift++) {
			oldcf = cf;
			cf = s & 1;
			s = (s >> 1) | (oldcf << 7);
		}
		if (cnt==1) of = (s >> 7) ^ ((s >> 6) & 1);
		break;
		
		case 4: case 6: //SHL r/m8
		for (shift=1; shift<=cnt; shift++) {
			if (s & 0x80) cf = 1; else cf = 0;
			s = (s << 1) & 0xFF;
		}
		if ((cnt==1) && (cf==(s>>7))) of = 0; else of = 1;
		flag_szp8((uint8_t)s); break;
		
		case 5: //SHR r/m8
		if ((cnt==1) && (s & 0x80)) of = 1; else of = 0;
		for (shift=1; shift<=cnt; shift++) {
			cf = s & 1;
			s = s >> 1;
		}
		flag_szp8((uint8_t)s); break;
		
		case 7: //SAR r/m8
		for (shift=1; shift<=cnt; shift++) {
			msb = s & 0x80;
			cf = s & 1;
			s = (s >> 1) | msb;
		}
		of = 0;
		flag_szp8((uint8_t)s); break;
		
	}
	return(s & 0xFF);
}

uint16_t op_grp2_16(uint8_t cnt) {
	uint32_t s, shift, oldcf, msb;
	//if (cnt>0x10) return(0x7FFF); //80186+ limits shift count
	s = oper1;
	oldcf = cf;
	switch (reg) {
		case 0: //ROL r/m8
		for (shift=1; shift<=cnt; shift++) {
			if (s & 0x8000) cf = 1; else cf = 0;
			s = s << 1;
			s = s | cf;
		}
		if (cnt==1) of = cf ^ ((s >> 15) & 1);
		break;
		
		case 1: //ROR r/m8
		for (shift=1; shift<=cnt; shift++) {
			cf = s & 1;
			s = (s >> 1) | (cf << 15);
		}
		if (cnt==1) of = (uint8_t)((s >> 15) ^ ((s >> 14) & 1));
		break;
		
		case 2: //RCL r/m8
		for (shift=1; shift<=cnt; shift++) {
			oldcf = cf;
			if (s & 0x8000) cf = 1; else cf = 0;
			s = s << 1;
			s = s | oldcf;
		}
		if (cnt==1) of = cf ^ ((s >> 15) & 1);
		break;
		
		case 3: //RCR r/m8
		for (shift=1; shift<=cnt; shift++) {
			oldcf = cf;
			cf = s & 1;
			s = (s >> 1) | (oldcf << 15);
		}
		if (cnt==1) of = (uint8_t)((s >> 15) ^ ((s >> 14) & 1));
		break;
		
		case 4: case 6: //SHL r/m8
		for (shift=1; shift<=cnt; shift++) {
			if (s & 0x8000) cf = 1; else cf = 0;
			s = (s << 1) & 0xFFFF;
		}
		if ((cnt==1) && (cf==(s>>15))) of = 0; else of = 1;
		flag_szp16((uint16_t)s); break;
		
		case 5: //SHR r/m8
		if ((cnt==1) && (s & 0x8000)) of = 1; else of = 0;
		for (shift=1; shift<=cnt; shift++) {
			cf = s & 1;
			s = s >> 1;
		}
		flag_szp16((uint16_t)s); break;
		
		case 7: //SAR r/m8
		for (shift=1; shift<=cnt; shift++) {
			msb = s & 0x8000;
			cf = s & 1;
			s = (s >> 1) | msb;
		}
		of = 0;
		flag_szp16((uint16_t)s); break;
		
	}
	return((uint16_t)s & 0xFFFF);
}

void op_div8(uint16_t valdiv, uint8_t divisor) {
	if (divisor==0) { intcall86(0); return; }
	if ((valdiv / (uint16_t)divisor) > 0xFF) { intcall86(0); return; }
	regs.byteregs[regah] = valdiv % (uint16_t)divisor;
	regs.byteregs[regal] = valdiv / (uint16_t)divisor;
}

void op_idiv8(uint16_t valdiv, uint8_t divisor) {
	uint16_t s1, s2, d1, d2;
	int sign;
	if (divisor==0) { intcall86(0); return; }
	s1 = valdiv;
	s2 = divisor;
	sign = (((s1 ^ s2) & 0x8000) != 0);
	s1 = (s1 < 0x8000) ? s1 : ((~s1 + 1) & 0xffff);
	s2 = (s2 < 0x8000) ? s2 : ((~s2 + 1) & 0xffff);
	d1 = s1 / s2;
	d2 = s1 % s2;
	if (d1 & 0xFF00) { intcall86(0); return; }
	if (sign) {
		d1 = (~d1 + 1) & 0xff;
		d2 = (~d2 + 1) & 0xff;
	}
	regs.byteregs[regah] = (uint8_t)d2;
	regs.byteregs[regal] = (uint8_t)d1;
}

#define op_grp3_8() {\
	oper1 = signext(oper1b); oper2 = signext(oper2b);\
	switch (reg) {\
		case 0: case 1: /*TEST*/\
		flag_log8(oper1b & getmem8(segregs[regcs], ip)); StepIP(1);\
		break;\
		\
		case 2: /*NOT*/\
		res8 = ~oper1b; break;\
		\
		case 3: /*NEG*/\
		res8 = (~oper1b)+1;\
		flag_sub8(0, oper1b);\
		if (res8==0) cf = 0; else cf = 1;\
		break;\
		\
		case 4: /*MUL*/\
		temp1 = (uint32_t)oper1b * (uint32_t)regs.byteregs[regal];\
		putreg16(regax, temp1 & 0xFFFF);\
		flag_szp8((uint8_t)temp1);\
		if (regs.byteregs[regah]) { cf = 1; of = 1; } else { cf = 0; of = 0; }\
		break;\
		\
		case 5: /*IMUL*/\
		oper1 = signext(oper1b);\
		temp1 = signext(regs.byteregs[regal]);\
		temp2 = oper1;\
		if ((temp1 & 0x80)==0x80) temp1 = temp1 | 0xFFFFFF00;\
		if ((temp2 & 0x80)==0x80) temp2 = temp2 | 0xFFFFFF00;\
		temp3 = (temp1 * temp2) & 0xFFFF;\
		putreg16(regax, temp3 & 0xFFFF);\
		if (regs.byteregs[regah]) { cf = 1; of = 1; } else { cf = 0; of = 0; }\
		break;\
		\
		case 6: /*DIV*/\
		op_div8(getreg16(regax), oper1b);\
		break;\
		\
		case 7: /*IDIV*/\
		op_idiv8(getreg16(regax), oper1b);\
		break;\
	}\
}

void op_div16(uint32_t valdiv, uint16_t divisor) {
	if (divisor==0) { intcall86(0); return; }
	if ((valdiv / (uint32_t)divisor) > 0xFFFF) { intcall86(0); return; }
	putreg16(regdx, (uint16_t)(valdiv % (uint32_t)divisor));
	putreg16(regax, (uint16_t)(valdiv / (uint32_t)divisor));
}

void op_idiv16(uint32_t valdiv, uint16_t divisor) {
	uint32_t d1, d2, s1, s2;
	int sign;
	if (divisor==0) { intcall86(0); return; }
	s1 = valdiv;
	s2 = divisor;
	s2 = (s2 & 0x8000) ? (s2 | 0xffff0000) : s2;
	sign = (((s1 ^ s2) & 0x80000000) != 0);
	s1 = (s1 < 0x80000000) ? s1 : ((~s1 + 1) & 0xffffffff);
	s2 = (s2 < 0x80000000) ? s2 : ((~s2 + 1) & 0xffffffff);
	d1 = s1 / s2;
	d2 = s1 % s2;
	if (d1 & 0xFFFF0000) { intcall86(0); return; }
	if (sign) {
		d1 = (~d1 + 1) & 0xffff;
		d2 = (~d2 + 1) & 0xffff;
	}
	putreg16(regax, (uint16_t)d1);
	putreg16(regdx, (uint16_t)d2);
}

#define op_grp3_16() {\
	switch (reg) {\
		case 0: case 1: /*TEST*/\
		flag_log16(oper1 & getmem16(segregs[regcs], ip)); StepIP(2); break;\
		case 2: /*NOT*/\
		res16 = ~oper1; break;\
		case 3: /*NEG*/\
		res16 = (~oper1) + 1;\
		flag_sub16(0, oper1);\
		if (res16) cf = 1; else cf = 0;\
		break;\
		case 4: /*MUL*/\
		temp1 = (uint32_t)oper1 * (uint32_t)getreg16(regax);\
		putreg16(regax, temp1 & 0xFFFF);\
		putreg16(regdx, temp1 >> 16);\
		flag_szp16((uint16_t)temp1);\
		if (getreg16(regdx)) { cf = 1; of = 1; } else { cf = 0; of = 0; }\
		break;\
		case 5: /*IMUL*/\
		temp1 = getreg16(regax);\
		temp2 = oper1;\
		if (temp1 & 0x8000) temp1 |= 0xFFFF0000;\
		if (temp2 & 0x8000) temp2 |= 0xFFFF0000;\
		temp3 = temp1 * temp2;\
		putreg16(regax, temp3 & 0xFFFF); /*into register ax*/\
		putreg16(regdx, temp3 >> 16); /*into register dx*/\
		if (getreg16(regdx)) { cf = 1; of = 1; } else { cf = 0; of = 0; }\
		break;\
		case 6: /*DIV*/\
		op_div16(((uint32_t)getreg16(regdx)<<16) + getreg16(regax), oper1); break;\
		case 7: /*DIV*/\
		op_idiv16(((uint32_t)getreg16(regdx)<<16) + getreg16(regax), oper1); break;\
	}\
}

#define op_grp5() {\
	switch (reg) {\
		case 0: /* INC Ev */\
			oper2 = 1;\
			tempcf = cf;\
			op_add16();\
			cf = tempcf;\
			writerm16(rm, res16); break;\
		case 1: /* DEC Ev */\
			oper2 = 1;\
			tempcf = cf;\
			op_sub16();\
			cf = tempcf;\
			writerm16(rm, res16); break;\
		case 2: /* CALL Ev */\
			push(ip);\
			ip = oper1; break;\
		case 3: /* CALL Mp */\
			push(segregs[regcs]); push(ip);\
			getea(rm);\
			ip = (uint16_t)read86(ea) + (uint16_t)read86(ea + 1) * 256;\
			segregs[regcs] = (uint16_t)read86(ea + 2) + (uint16_t)read86(ea + 3) * 256; break;\
		case 4: /* JMP Ev */\
			ip = oper1; break;\
		case 5: /* JMP Mp */\
			getea(rm);\
			ip = (uint16_t)read86(ea) + (uint16_t)read86(ea + 1) * 256;\
			segregs[regcs] = (uint16_t)read86(ea + 2) + (uint16_t)read86(ea + 3) * 256; break;\
		case 6: /* PUSH Ev */\
			push(oper1); break;\
		case 7: /*invalid */\
			intcall86(6);\
			break;\
	}\
}


the rest is too much, it won't let me paste in a single post. :eek:
 
Last edited:
now here comes the big one. the main execution loop which handles traversing the memory based on CS and IP, and decodes the instructions to act on them. hold your breath, we're going in!

Code:
void exec86(uint32_t execloops) {
        uint32_t loopcount;
        uint8_t docontinue;
        static uint16_t firstip;

        for (loopcount=0; loopcount<execloops; loopcount++) {
                if (ifl && (i8259.irr & (~i8259.imr))) intcall86(nextintr()); //if interrupt flag is set, get next interrupt from the i8259, if any
                reptype = 0; segoverride = 0; //clear these. must remember if the default segment was not used due to an override prefix. some addressing modes default to SS instead of the usual DS, but we shouldn't use SS in those modes if overriden.
                useseg = segregs[regds]; docontinue = 0; //for most isntructions, DS is the default segment for memory read/writes
                firstip = ip; //we need to remember this in case the instruction is a string operation with a repetition prefix, so we know where to go back to repeat from
                while(!docontinue) {
                        savecs = segregs[regcs]; saveip = ip;
                        opcode = getmem8(segregs[regcs], ip); StepIP(1); //grab byte at CS:IP to begin decoding the instruction
                        
                        switch (opcode) { //some values are prefixes to the actual instruction, loop and decode them until we hit a non-prefix value
                                //segment prefix check
                                case 0x2E: //segment segregs[regcs]
                                    useseg = segregs[regcs]; segoverride = 1; break;
                                case 0x3E: //segment segregs[regds]
                                    useseg = segregs[regds]; segoverride = 1; break;
                                case 0x26: //segment segregs[reges]
                                    useseg = segregs[reges]; segoverride = 1; break;
                                case 0x36: //segment segregs[regss]
                                    useseg = segregs[regss]; segoverride = 1; break;
                                
                                //repetition prefix check
                                case 0xF3: //REP/REPE/REPZ
                                    reptype = 1; break;
                                case 0xF2: //REPNE/REPNZ
                                    reptype = 2; break;
                                default: docontinue = 1; //if it's anything else, it's the actual opcode and not a prefix
                        }
                }
                totalexec++;
                
                switch (opcode) {
                        case 0x0: //00 ADD Eb Gb
                            modregrm();
                            oper1b = readrm8(rm); oper2b = getreg8(reg);
                            op_add8();
                            writerm8(rm, res8);
                            break;
                        case 0x1: //01 ADD Ev Gv
                            modregrm();
                            oper1 = readrm16(rm); oper2 = getreg16(reg);
                            op_add16();
                            writerm16(rm, res16);
                            break;
                        case 0x2: //02 ADD Gb Eb
                            modregrm();
                            oper1b = getreg8(reg); oper2b = readrm8(rm);
                            op_add8();
                            putreg8(reg, res8);
                            break;
                        case 0x3: //03 ADD Gv Ev
                            modregrm();
                            oper1 = getreg16(reg); oper2 = readrm16(rm);
                            op_add16();
                            putreg16(reg, res16);
                            break;
                        case 0x4: //04 ADD regs.byteregs[regal] Ib
                            oper1b = regs.byteregs[regal]; oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            op_add8();
                            regs.byteregs[regal] = res8;
                            break;
                        case 0x5: //05 ADD AX Iv
                            oper1 = (getreg16(regax)); oper2 = getmem16(segregs[regcs], ip); StepIP(2);
                            op_add16();
                            putreg16(regax, res16);
                            break;
                        case 0x6: //06 PUSH segregs[reges]
                            push(segregs[reges]);
                            break;
                        case 0x7: //07 POP segregs[reges]
                            segregs[reges] = pop();
                            break;
                        case 0x8: //08 OR Eb Gb
                            modregrm();
                            oper1b = readrm8(rm); oper2b = getreg8(reg);
                            op_or8();
                            writerm8(rm, res8);
                            break;
                        case 0x9: //09 OR Ev Gv
                            modregrm();
                            oper1 = readrm16(rm); oper2 = getreg16(reg);
                            op_or16();
                            writerm16(rm, res16);
                            break;
                        case 0xA: //0A OR Gb Eb
                            modregrm();
                            oper1b = getreg8(reg); oper2b = readrm8(rm);
                            op_or8();
                            putreg8(reg, res8);
                            break;
                        case 0xB: //0B OR Gv Ev
                            modregrm();
                            oper1 = getreg16(reg); oper2 = readrm16(rm);
                            op_or16();
                            if ((oper1==0xF802) && (oper2==0xF802)) sf = 0; //cheap hack to make Wolf 3D think we're a 286 so it plays
                            putreg16(reg, res16);
                            break;
                        case 0xC: //0C OR regs.byteregs[regal] Ib
                            oper1b = regs.byteregs[regal]; oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            op_or8();
                            regs.byteregs[regal] = res8;
                            break;
                        case 0xD: //0D OR AX Iv
                            oper1 = getreg16(regax); oper2 = getmem16(segregs[regcs], ip); StepIP(2);
                            op_or16();
                            putreg16(regax, res16);
                            break;
                        case 0xE: //0E PUSH segregs[regcs]
                            push(segregs[regcs]);
                            break;
                        //case 0xF: //opcode 0xF in the 8086 is the useless "POP CS" instruction, in later x86 CPUs it indicates a 286+ extended opcode
                        case 0x10: //10 ADC Eb Gb
                            modregrm();
                            oper1b = readrm8(rm); oper2b = getreg8(reg);
                            op_adc8();
                            writerm8(rm, res8);
                            break;
                        case 0x11: //11 ADC Ev Gv
                            modregrm();
                            oper1 = readrm16(rm); oper2 = getreg16(reg);
                            op_adc16();
                            writerm16(rm, res16);
                            break;
                        case 0x12: //12 ADC Gb Eb
                            modregrm();
                            oper1b = getreg8(reg); oper2b = readrm8(rm);
                            op_adc8();
                            putreg8(reg, res8);
                            break;
                        case 0x13: //13 ADC Gv Ev
                            modregrm();
                            oper1 = getreg16(reg); oper2 = readrm16(rm);
                            op_adc16();
                            putreg16(reg, res16);
                            break;
                        case 0x14: //14 ADC regs.byteregs[regal] Ib
                            oper1b = regs.byteregs[regal]; oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            op_adc8();
                            regs.byteregs[regal] = res8;
                            break;
                        case 0x15: //15 ADC AX Iv
                            oper1 = getreg16(regax); oper2 = getmem16(segregs[regcs], ip); StepIP(2);
                            op_adc16();
                            putreg16(regax, res16);
                            break;
                        case 0x16: //16 PUSH segregs[regss]
                            push(segregs[regss]);
                            break;
                        case 0x17: //17 POP segregs[regss]
                            segregs[regss] = pop();
                            break;
                        case 0x18: //18 SBB Eb Gb
                            modregrm();
                            oper1b = readrm8(rm); oper2b = getreg8(reg);
                            op_sbb8();
                            writerm8(rm, res8);
                            break;
                        case 0x19: //19 SBB Ev Gv
                            modregrm();
                            oper1 = readrm16(rm); oper2 = getreg16(reg);
                            op_sbb16();
                            writerm16(rm, res16);
                            break;
                        case 0x1A: //1A SBB Gb Eb
                            modregrm();
                            oper1b = getreg8(reg); oper2b = readrm8(rm);
                            op_sbb8();
                            putreg8(reg, res8);
                            break;
                        case 0x1B: //1B SBB Gv Ev
                            modregrm();
                            oper1 = getreg16(reg); oper2 = readrm16(rm);
                            op_sbb16();
                            putreg16(reg, res16);
                            break;
                        case 0x1C: //1C SBB regs.byteregs[regal] Ib;
                            oper1b = regs.byteregs[regal]; oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            op_sbb8();
                            regs.byteregs[regal] = res8;
                            break;
                        case 0x1D: //1D SBB AX Iv
                            oper1 = getreg16(regax); oper2 = getmem16(segregs[regcs], ip); StepIP(2);
                            op_sbb16();
                            putreg16(regax, res16);
                            break;
                        case 0x1E: //1E PUSH segregs[regds]
                            push(segregs[regds]);
                            break;
                        case 0x1F: //1F POP segregs[regds]
                            segregs[regds] = pop();
                            break;
                        case 0x20: //20 AND Eb Gb
                            modregrm();
                            oper1b = readrm8(rm); oper2b = getreg8(reg);
                            op_and8();
                            writerm8(rm, res8);
                            break;
                        case 0x21: //21 AND Ev Gv
                            modregrm();
                            oper1 = readrm16(rm); oper2 = getreg16(reg);
                            op_and16();
                            writerm16(rm, res16);
                            break;
                        case 0x22: //22 AND Gb Eb
                            modregrm();
                            oper1b = getreg8(reg); oper2b = readrm8(rm);
                            op_and8();
                            putreg8(reg, res8);
                            break;
                        case 0x23: //23 AND Gv Ev
                            modregrm();
                            oper1 = getreg16(reg); oper2 = readrm16(rm);
                            op_and16();
                            putreg16(reg, res16);
                            break;
                        case 0x24: //24 AND regs.byteregs[regal] Ib
                            oper1b = regs.byteregs[regal]; oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            op_and8();
                            regs.byteregs[regal] = res8;
                            break;
                        case 0x25: //25 AND AX Iv
                            oper1 = getreg16(regax); oper2 = getmem16(segregs[regcs], ip); StepIP(2);
                            op_and16();
                            putreg16(regax, res16);
                            break;
                        case 0x27: //27 DAA
                            if (((regs.byteregs[regal] & 0xF) > 9) || (af==1)) {
                                    oper1 = regs.byteregs[regal] + 6;
                                    regs.byteregs[regal] = oper1 & 255;
                                    if (oper1 & 0xFF00) cf = 1; else cf = 0;
                                    af = 1;
                            } else af = 0;
                            if (((regs.byteregs[regal] & 0xF0) > 0x90) || (cf==1)) {
                                    regs.byteregs[regal] = regs.byteregs[regal] + 0x60;
                                    cf = 1;
                            } else cf = 0;
                            regs.byteregs[regal] = regs.byteregs[regal] & 255;
                            flag_szp8(regs.byteregs[regal]);
                            break;
                        case 0x28: //28 SUB Eb Gb
                            modregrm();
                            oper1b = readrm8(rm); oper2b = getreg8(reg);
                            op_sub8();
                            writerm8(rm, res8);
                            break;
                        case 0x29: //29 SUB Ev Gv
                            modregrm();
                            oper1 = readrm16(rm); oper2 = getreg16(reg);
                            op_sub16();
                            writerm16(rm, res16);
                            break;
                        case 0x2A: //2A SUB Gb Eb
                            modregrm();
                            oper1b = getreg8(reg); oper2b = readrm8(rm);
                            op_sub8();
                            putreg8(reg, res8);
                            break;
                        case 0x2B: //2B SUB Gv Ev
                            modregrm();
                            oper1 = getreg16(reg); oper2 = readrm16(rm);
                            op_sub16();
                            putreg16(reg, res16);
                            break;
                        case 0x2C: //2C SUB regs.byteregs[regal] Ib
                            oper1b = regs.byteregs[regal]; oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            op_sub8();
                            regs.byteregs[regal] = res8;
                            break;
                        case 0x2D: //2D SUB AX Iv
                            oper1 = getreg16(regax); oper2 = getmem16(segregs[regcs], ip); StepIP(2);
                            op_sub16();
                            putreg16(regax, res16);
                            break;
                        case 0x2F: //2F DAS
                            if (((regs.byteregs[regal] & 15) > 9) || (af==1)) {
                                    oper1 = regs.byteregs[regal] - 6;
                                    regs.byteregs[regal] = oper1 & 255;
                                    if (oper1 & 0xFF00) cf = 1; else cf = 0;
                                    af = 1;
                            } else af = 0;
                            if (((regs.byteregs[regal] & 0xF0) > 0x90) || (cf==1)) {
                                    regs.byteregs[regal] = regs.byteregs[regal] - 0x60;
                                    cf = 1;
                            } else cf = 0;
                            flag_szp8(regs.byteregs[regal]);
                            break;
                        case 0x30: //30 XOR Eb Gb
                            modregrm();
                            oper1b = readrm8(rm); oper2b = getreg8(reg);
                            op_xor8();
                            writerm8(rm, res8);
                            break;
                        case 0x31: //31 XOR Ev Gv
                            modregrm();
                            oper1 = readrm16(rm); oper2 = getreg16(reg);
                            op_xor16();
                            writerm16(rm, res16);
                            break;
                        case 0x32: //32 XOR Gb Eb
                            modregrm();
                            oper1b = getreg8(reg); oper2b = readrm8(rm);
                            op_xor8();
                            putreg8(reg, res8);
                            break;
                        case 0x33: //33 XOR Gv Ev
                            modregrm();
                            oper1 = getreg16(reg); oper2 = readrm16(rm);
                            op_xor16();
                            putreg16(reg, res16);
                            break;
                        case 0x34: //34 XOR regs.byteregs[regal] Ib
                            oper1b = regs.byteregs[regal]; oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            op_xor8();
                            regs.byteregs[regal] = res8;
                            break;
                        case 0x35: //35 XOR AX Iv
                            oper1 = getreg16(regax); oper2 = getmem16(segregs[regcs], ip); StepIP(2);
                            op_xor16();
                            putreg16(regax, res16);
                            break;
                        case 0x37: //37 AAA ASCII
                            if (((regs.byteregs[regal] & 0xF) > 9) || (af==1)) {
                                    regs.byteregs[regal] = regs.byteregs[regal] + 6;
                                    regs.byteregs[regah] = regs.byteregs[regah] + 1;
                                    af = 1;
                                    cf = 1;
                                    } else {
                                    af = 0;
                                    cf = 0;
                            }
                            regs.byteregs[regal] = regs.byteregs[regal] & 0xF;
                            break;
                        case 0x38: //38 CMP Eb Gb
                            modregrm();
                            oper1b = readrm8(rm); oper2b = getreg8(reg);
                            flag_sub8(oper1b, oper2b);
                            break;
                        case 0x39: //39 CMP Ev Gv
                            modregrm();
                            oper1 = readrm16(rm); oper2 = getreg16(reg);
                            flag_sub16(oper1, oper2);
                            break;
                        case 0x3A: //3A CMP Gb Eb
                            modregrm();
                            oper1b = getreg8(reg); oper2b = readrm8(rm);
                            flag_sub8(oper1b, oper2b);
                            break;
                        case 0x3B: //3B CMP Gv Ev
                            modregrm();
                            oper1 = getreg16(reg); oper2 = readrm16(rm);
                            flag_sub16(oper1, oper2);
                            break;
                        case 0x3C: //3C CMP regs.byteregs[regal] Ib
                            oper1b = regs.byteregs[regal]; oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            flag_sub8(oper1b, oper2b);
                            break;
                        case 0x3D: //3D CMP AX Iv
                            oper1 = getreg16(regax); oper2 = getmem16(segregs[regcs], ip); StepIP(2);
                            flag_sub16(oper1, oper2);
                            break;
                        case 0x3F: //3F AAS ASCII
                            if (((regs.byteregs[regal] & 0xF) > 9) || (af==1)) {
                                    regs.byteregs[regal] = regs.byteregs[regal] - 6;
                                    regs.byteregs[regah] = regs.byteregs[regah] - 1;
                                    af = 1;
                                    cf = 1;
                                    } else {
                                    af = 0;
                                    cf = 0;
                            }
                            regs.byteregs[regal] = regs.byteregs[regal] & 0xF;
                            break;
                        case 0x40: //40 INC AX
                            oldcf = cf;
                            oper1 = getreg16(regax); oper2 = 1;
                            op_add16();
                            cf = oldcf;
                            putreg16(regax, res16);
                            break;
                        case 0x41: //41 INC CX
                            oldcf = cf;
                            oper1 = getreg16(regcx); oper2 = 1;
                            op_add16();
                            cf = oldcf;
                            putreg16(regcx, res16);
                            break;
                        case 0x42: //42 INC DX
                            oldcf = cf;
                            oper1 = getreg16(regdx); oper2 = 1;
                            op_add16();
                            cf = oldcf;
                            putreg16(regdx, res16);
                            break;
                        case 0x43: //43 INC BX
                            oldcf = cf;
                            oper1 = getreg16(regbx); oper2 = 1;
                            op_add16();
                            cf = oldcf;
                            putreg16(regbx, res16);
                            break;
                        case 0x44: //44 INC SP
                            oldcf = cf;
                            oper1 = getreg16(regsp); oper2 = 1;
                            op_add16();
                            cf = oldcf;
                            putreg16(regsp, res16);
                            break;
                        case 0x45: //45 INC BP
                            oldcf = cf;
                            oper1 = getreg16(regbp); oper2 = 1;
                            op_add16();
                            cf = oldcf;
                            putreg16(regbp, res16);
                            break;
                        case 0x46: //46 INC SI
                            oldcf = cf;
                            oper1 = getreg16(regsi); oper2 = 1;
                            op_add16();
                            cf = oldcf;
                            putreg16(regsi, res16);
                            break;
                        case 0x47: //47 INC DI
                            oldcf = cf;
                            oper1 = getreg16(regdi); oper2 = 1;
                            op_add16();
                            cf = oldcf;
                            putreg16(regdi, res16);
                            break;
                        case 0x48: //48 DEC AX
                            oldcf = cf;
                            oper1 = getreg16(regax); oper2 = 1;
                            op_sub16();
                            cf = oldcf;
                            putreg16(regax, res16);
                            break;
                        case 0x49: //49 DEC CX
                            oldcf = cf;
                            oper1 = getreg16(regcx); oper2 = 1;
                            op_sub16();
                            cf = oldcf;
                            putreg16(regcx, res16);
                            break;
                        case 0x4A: //4A DEC DX
                            oldcf = cf;
                            oper1 = getreg16(regdx); oper2 = 1;
                            op_sub16();
                            cf = oldcf;
                            putreg16(regdx, res16);
                            break;
                        case 0x4B: //4B DEC BX
                            oldcf = cf;
                            oper1 = getreg16(regbx); oper2 = 1;
                            op_sub16();
                            cf = oldcf;
                            putreg16(regbx, res16);
                            break;
                        case 0x4C: //4C DEC SP
                            oldcf = cf;
                            oper1 = getreg16(regsp); oper2 = 1;
                            op_sub16();
                            cf = oldcf;
                            putreg16(regsp, res16);
                            break;
                        case 0x4D: //4D DEC BP
                            oldcf = cf;
                            oper1 = getreg16(regbp); oper2 = 1;
                            op_sub16();
                            cf = oldcf;
                            putreg16(regbp, res16);
                            break;
                        case 0x4E: //4E DEC SI
                            oldcf = cf;
                            oper1 = getreg16(regsi); oper2 = 1;
                            op_sub16();
                            cf = oldcf;
                            putreg16(regsi, res16);
                            break;
                        case 0x4F: //4F DEC DI
                            oldcf = cf;
                            oper1 = getreg16(regdi); oper2 = 1;
                            op_sub16();
                            cf = oldcf;
                            putreg16(regdi, res16);
                            break;

i have to split it here, can't fit it all in. continued in next post.
 
Last edited:
Code:
                        case 0x50: //50 PUSH AX
                            push (getreg16(regax));
                            break;
                        case 0x51: //51 PUSH CX
                            push (getreg16(regcx));
                            break;
                        case 0x52: //52 PUSH DX
                            push (getreg16(regdx));
                            break;
                        case 0x53: //53 PUSH BX
                            push (getreg16(regbx));
                            break;
                        case 0x54: //54 PUSH SP
                            push (getreg16(regsp));
                            break;
                        case 0x55: //55 PUSH BP
                            push (getreg16(regbp));
                            break;
                        case 0x56: //56 PUSH SI
                            push (getreg16(regsi));
                            break;
                        case 0x57: //57 PUSH DI
                            push (getreg16(regdi));
                            break;
                        case 0x58: //58 POP AX
                            putreg16(regax, pop());
                            break;
                        case 0x59: //59 POP CX
                            putreg16(regcx, pop());
                            break;
                        case 0x5A: //5A POP DX
                            putreg16(regdx, pop());
                            break;
                        case 0x5B: //5B POP BX
                            putreg16(regbx, pop());
                            break;
                        case 0x5C: //5C POP SP
                            putreg16(regsp, pop());
                            break;
                        case 0x5D: //5D POP BP
                            putreg16(regbp, pop());
                            break;
                        case 0x5E: //5E POP SI
                            putreg16(regsi, pop());
                            break;
                        case 0x5F: //5F POP DI
                            putreg16(regdi, pop());
                            break;
                        case 0x60: //60 PUSHA (80186+)
                            oldsp = getreg16(regsp);
                            push(getreg16(regax));
                            push(getreg16(regcx));
                            push(getreg16(regdx));
                            push(getreg16(regbx));
                            push(oldsp);
                            push(getreg16(regbp));
                            push(getreg16(regsi));
                            push(getreg16(regdi));
                            break;
                        case 0x61: //61 POPA (80186+)
                            putreg16(regdi, pop());
                            putreg16(regsi, pop());
                            putreg16(regbp, pop());
                            dummy = pop();
                            putreg16(regbx, pop());
                            putreg16(regdx, pop());
                            putreg16(regcx, pop());
                            putreg16(regax, pop());
                            break;
                        case 0x68: //68 PUSH Iv (80186+)
                            push(getmem16(segregs[regcs], ip)); StepIP(2);
                            break;
                        case 0x69: //69 IMUL Ev Iv (80186+)
                            modregrm();
                            temp1 = readrm16(rm);
                            temp2 = getmem16(segregs[regcs], ip); StepIP(2);
                            if ((temp1 & 0x8000L) == 0x8000L) temp1 = temp1 | 0xFFFF0000L;
                            if ((temp2 & 0x8000L) == 0x8000L) temp2 = temp2 | 0xFFFF0000L;
                            temp3 = temp1 * temp2;
                            writerm16(rm, temp3 & 0xFFFFL);
                            if (temp3&0xFFFF0000L) { cf = 1; of = 1; } else { cf = 0; of = 0; }                        
                            break;
                        case 0x6A: //6A PUSH Ib (80186+)
                            push(getmem8(segregs[regcs], ip)); StepIP(1);
                            break;
                        case 0x6B: //6B IMUL Eb Ib (80186+)
                            modregrm();
                            oper1 = readrm16(rm);
                            temp1 = getmem8(segregs[regcs], ip); StepIP(1);
                            temp2 = oper1;
                            if ((temp1 & 0x80L) == 0x80L) temp1 = temp1 | 0xFFFFFF00L;
                            if ((temp2 & 0x8000L) == 0x8000L) temp2 = temp2 | 0xFFFF0000L;
                            temp3 = temp1 * temp2;
                            writerm16(rm, temp3&0xFFFFL);
                            if (temp3&0xFFFF0000L) { cf = 1; of = 1; } else { cf = 0; of = 0; }                        
                            break;


just splitting here to point out that the next 16 are conditional jumps which check flag states to detemine whether or not a branch is taken.

Code:
                        case 0x70: //70 JO Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (of) ip = ip + temp16;
                            break;
                        case 0x71: //71 JNO Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (!of) ip = ip + temp16;
                            break;
                        case 0x72: //72 JB Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (cf) ip = ip + temp16;
                            break;
                        case 0x73: //73 JNB Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (!cf) ip = ip + temp16;
                            break;
                        case 0x74: //74 JZ Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (zf) ip = ip + temp16;
                            break;
                        case 0x75: //75 JNZ Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (!zf) ip = ip + temp16;
                            break;
                        case 0x76: //76 JBE Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (cf || zf) ip = ip + temp16;
                            break;
                        case 0x77: //77 JA Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (!cf && !zf) ip = ip + temp16;
                            break;
                        case 0x78: //78 JS Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (sf) ip = ip + temp16;
                            break;
                        case 0x79: //79 JNS Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (!sf) ip = ip + temp16;
                            break;
                        case 0x7A: //7A JPE Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (pf) ip = ip + temp16;
                            break;
                        case 0x7B: //7B JPO Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (!pf) ip = ip + temp16;
                            break;
                        case 0x7C: //7C JL Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (sf != of) ip = ip + temp16;
                            break;
                        case 0x7D: //7D JGE Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (sf == of) ip = ip + temp16;
                            break;
                        case 0x7E: //7E JLE Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if ((sf != of) || zf) ip = ip + temp16;
                            break;
                        case 0x7F: //7F JG Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (!zf && (sf == of)) ip = ip + temp16;
                            break;
                        case 0x80: case 0x82: //80/82 GRP1 Eb Ib
                            modregrm();
                            oper1b = readrm8(rm);
                            oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            switch (reg) {
                                case 0: op_add8(); break;
                                case 1: op_or8(); break;
                                case 2: op_adc8(); break;
                                case 3: op_sbb8(); break;
                                case 4: op_and8(); break;
                                case 5: op_sub8(); break;
                                case 6: op_xor8(); break;
                                case 7: flag_sub8(oper1b, oper2b); break;
                                    default: break; //to avoid compiler warnings
                            }
                            if (reg < 7) writerm8(rm, res8);
                            break;
                        case 0x81: //81 GRP1 Ev Iv
                        case 0x83: //83 GRP1 Ev Ib
                            modregrm();
                            oper1 = readrm16(rm);
                            if (opcode == 0x81) {
                                    oper2 = getmem16(segregs[regcs], ip); StepIP(2);
                                    } else {
                                    oper2 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            }
                            switch (reg) {
                                case 0: op_add16(); break;
                                case 1: op_or16(); break;
                                case 2: op_adc16(); break;
                                case 3: op_sbb16(); break;
                                case 4: op_and16(); break;
                                case 5: op_sub16(); break;
                                case 6: op_xor16(); break;
                                case 7: flag_sub16(oper1, oper2); break;
                                    default: break; //to avoid compiler warnings
                            }
                            if (reg < 7) writerm16(rm, res16);
                            break;
                            break;
                        case 0x84: //84 TEST Gb Eb
                            modregrm();
                            oper1b = getreg8(reg); oper2b = readrm8(rm);
                            flag_log8(oper1b & oper2b);
                            break;
                        case 0x85: //85 TEST Gv Ev
                            modregrm();
                            oper1 = getreg16(reg); oper2 = readrm16(rm);
                            flag_log16(oper1 & oper2);
                            break;
                        case 0x86: //86 XCHG Gb Eb
                            modregrm();
                            oper1b = getreg8(reg);
                            putreg8(reg, readrm8(rm));
                            writerm8(rm, oper1b);
                            break;
                        case 0x87: //87 XCHG Gv Ev
                            modregrm();
                            oper1 = getreg16(reg);
                            putreg16(reg, readrm16(rm));
                            writerm16(rm, oper1);
                            break;
                        case 0x88: //88 MOV Eb Gb
                            modregrm();
                            writerm8(rm, getreg8(reg));
                            break;
                        case 0x89: //89 MOV Ev Gv
                            modregrm();
                            writerm16(rm, getreg16(reg));
                            break;
                        case 0x8A: //8A MOV Gb Eb
                            modregrm();
                            putreg8(reg, readrm8(rm));
                            break;
                        case 0x8B: //8B MOV Gv Ev
                            modregrm();
                            putreg16(reg, readrm16(rm));
                            break;
                        case 0x8C: //8C MOV Ew Sw
                            modregrm();
                            writerm16(rm, getsegreg(reg));
                            break;
                        case 0x8D: //8D LEA Gv M
                            modregrm();
                            getea(rm);
                            putreg16(reg, (uint16_t)(ea - (uint32_t)segbase(useseg)));
                            break;
                        case 0x8E: //8E MOV Sw Ew
                            modregrm();
                            putsegreg(reg, readrm16(rm));
                            break;
                        case 0x8F: //8F POP Ev
                            modregrm();
                            writerm16(rm, pop());
                            break;
                        case 0x90: //90 NOP
                            break;
                        case 0x91: //91 XCHG eCX eAX
                            oper1 = getreg16(regcx);
                            putreg16(regcx, getreg16(regax));
                            putreg16(regax, oper1);
                            break;
                        case 0x92: //92 XCHG eDX eAX
                            oper1 = getreg16(regdx);
                            putreg16(regdx, getreg16(regax));
                            putreg16(regax, oper1);
                            break;
                        case 0x93: //93 XCHG eBX eAX
                            oper1 = getreg16(regbx);
                            putreg16(regbx, getreg16(regax));
                            putreg16(regax, oper1);
                            break;
                        case 0x94: //94 XCHG eSP eAX
                            oper1 = getreg16(regsp);
                            putreg16(regsp, getreg16(regax));
                            putreg16(regax, oper1);
                            break;
                        case 0x95: //95 XCHG eBP eAX
                            oper1 = getreg16(regbp);
                            putreg16(regbp, getreg16(regax));
                            putreg16(regax, oper1);
                            break;
                        case 0x96: //96 XCHG eSI eAX
                            oper1 = getreg16(regsi);
                            putreg16(regsi, getreg16(regax));
                            putreg16(regax, oper1);
                            break;
                        case 0x97: //97 XCHG eDI eAX
                            oper1 = getreg16(regdi);
                            putreg16(regdi, getreg16(regax));
                            putreg16(regax, oper1);
                            break;
                        case 0x98: //98 CBW
                            if ((regs.byteregs[regal] & 0x80) == 0x80) regs.byteregs[regah] = 0xFF; else regs.byteregs[regah] = 0;
                            break;
                        case 0x99: //99 CWD
                            if ((regs.byteregs[regah] & 0x80) == 0x80) putreg16(regdx, 0xFFFF); else putreg16(regdx, 0);
                            break;
                        case 0x9A: //9A CALL Ap
                            oper1 = getmem16(segregs[regcs], ip); StepIP(2);
                            oper2 = getmem16(segregs[regcs], ip); StepIP(2);
                            push(segregs[regcs]); push(ip); ip = oper1; segregs[regcs] = oper2;
                            break;
                        case 0x9B: //9B WAIT
                            break;
                        case 0x9C: //9C PUSHF
                            push(makeflagsword() | 0xF800);
                            break;
                        case 0x9D: //9D POPF
                            temp16 = pop();
                            decodeflagsword(temp16);
                            break;
                        case 0x9E: //9E SAHF
                            decodeflagsword ((makeflagsword() & 0xFF00) | regs.byteregs[regah]);
                            break;
                        case 0x9F: //9F LAHF
                            regs.byteregs[regah] = makeflagsword() & 0xFF;
                            break;
                        case 0xA0: //A0 MOV regs.byteregs[regal] Ob
                            regs.byteregs[regal] = getmem8(useseg, getmem16(segregs[regcs], ip)); StepIP(2);
                            break;
                        case 0xA1: //A1 MOV AX Ov
                            oper1 = getmem16(useseg, getmem16(segregs[regcs], ip)); StepIP(2);
                            putreg16(regax, oper1);
                            break;
                        case 0xA2: //A2 MOV Ob regs.byteregs[regal]
                            putmem8(useseg, getmem16(segregs[regcs], ip), regs.byteregs[regal]); StepIP(2);
                            break;
                        case 0xA3: //A3 MOV Ov AX
                            putmem16(useseg, getmem16(segregs[regcs], ip), getreg16(regax)); StepIP(2);
                            break;
                        case 0xA4: //A4 MOVSB (this and a handful more below can be used with repetition prefixes, so we must account for such)
                            if (reptype && (getreg16(regcx)==0)) break;
                            putmem8(segregs[reges], getreg16(regdi), getmem8(useseg, getreg16(regsi)));
                            if (df) { putreg16(regsi, getreg16(regsi) - 1); putreg16(regdi, getreg16(regdi) - 1); }
                              else { putreg16(regsi, getreg16(regsi) + 1); putreg16(regdi, getreg16(regdi) + 1); }
                            if (reptype) putreg16(regcx, getreg16(regcx)-1);
                            if (!reptype) break;
                            ip = firstip;
                            break;
                        case 0xA5: //A5 MOVSW
                            if (reptype && (getreg16(regcx)==0)) break;
                            putmem16(segregs[reges], getreg16(regdi), getmem16(useseg, getreg16(regsi)));
                            if (df) { putreg16(regsi, getreg16(regsi) - 2); putreg16(regdi, getreg16(regdi) - 2); }
                              else { putreg16(regsi, getreg16(regsi) + 2); putreg16(regdi, getreg16(regdi) + 2); }
                            if (reptype) putreg16(regcx, getreg16(regcx)-1);
                            if (!reptype) break;
                            ip = firstip;
                            break;
                        case 0xA6: //A6 CMPSB
                            if (reptype && (getreg16(regcx)==0)) break;
                            oper1b = getmem8(useseg, getreg16(regsi)); oper2b = getmem8(segregs[reges], getreg16(regdi));
                            if (df) { putreg16(regsi, getreg16(regsi) - 1); putreg16(regdi, getreg16(regdi) - 1); }
                              else { putreg16(regsi, getreg16(regsi) + 1); putreg16(regdi, getreg16(regdi) + 1); }
                            flag_sub8(oper1b, oper2b);
                            if (reptype) putreg16(regcx, getreg16(regcx)-1);
                            if ((reptype == 1) && !zf) break;
                              else if ((reptype == 2) && (zf == 1)) break;
                            if (!reptype) break;
                            ip = firstip;
                            break;
                        case 0xA7: //A7 CMPSW
                            if (reptype && (getreg16(regcx)==0)) break;
                            oper1 = getmem16(useseg, getreg16(regsi)); oper2 = getmem16(segregs[reges], getreg16(regdi));
                            if (df) { putreg16(regsi, getreg16(regsi) - 2); putreg16(regdi, getreg16(regdi) - 2); }
                              else { putreg16(regsi, getreg16(regsi) + 2); putreg16(regdi, getreg16(regdi) + 2); }
                            flag_sub16(oper1, oper2);
                            if (reptype) putreg16(regcx, getreg16(regcx)-1);
                            if ((reptype == 1) && !zf) break;
                            if ((reptype == 2) && (zf == 1)) break;
                            if (!reptype) break;
                            ip = firstip;
                            break;
                        case 0xA8: //A8 TEST regs.byteregs[regal] Ib
                            oper1b = regs.byteregs[regal]; oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            flag_log8(oper1b & oper2b);
                            break;
                        case 0xA9: //A9 TEST eAX Iv
                            oper1 = getreg16(regax); oper2 = getmem16(segregs[regcs], ip); StepIP(2);
                            flag_log16(oper1 & oper2);
                            break;
                        case 0xAA: //AA STOSB
                            if (reptype && (getreg16(regcx)==0)) break;
                            putmem8(segregs[reges], getreg16(regdi), regs.byteregs[regal]);
                            if (df) putreg16(regdi, getreg16(regdi) - 1);
                              else putreg16(regdi, getreg16(regdi) + 1);
                            if (reptype) putreg16(regcx, getreg16(regcx)-1);
                            if (!reptype) break;
                            ip = firstip;
                            break;
                        case 0xAB: //AB STOSW
                            if (reptype && (getreg16(regcx)==0)) break;
                            putmem16(segregs[reges], getreg16(regdi), getreg16(regax));
                            if (df) putreg16(regdi, getreg16(regdi) - 2);
                              else putreg16(regdi, getreg16(regdi) + 2);
                            if (reptype) putreg16(regcx, getreg16(regcx)-1);
                            if (!reptype) break;
                            ip = firstip;
                            break;
                        case 0xAC: //AC LODSB
                            if (reptype && (getreg16(regcx)==0)) break;
                            regs.byteregs[regal] = getmem8(useseg, getreg16(regsi));
                            if (df) putreg16(regsi, getreg16(regsi) - 1);
                              else putreg16(regsi, getreg16(regsi) + 1);
                            if (reptype) putreg16(regcx, getreg16(regcx)-1);
                            if (!reptype) break;
                            ip = firstip;
                            break;
                        case 0xAD: //AD LODSW
                            if (reptype && (getreg16(regcx)==0)) break;
                            oper1 = getmem16(useseg, getreg16(regsi));
                            putreg16(regax, oper1);
                            if (df) putreg16(regsi, getreg16(regsi) - 2);
                              else putreg16(regsi, getreg16(regsi) + 2);
                            if (reptype) putreg16(regcx, getreg16(regcx)-1);
                            if (!reptype) break;
                            ip = firstip;
                            break;
                        case 0xAE: //AE SCASB
                            if (reptype && (getreg16(regcx)==0)) break;
                            oper1b = getmem8(segregs[reges], getreg16(regdi)); oper2b = regs.byteregs[regal];
                            flag_sub8(oper1b, oper2b);
                            if (df) putreg16(regdi, getreg16(regdi) - 1);
                              else putreg16(regdi, getreg16(regdi) + 1);
                            if (reptype) putreg16(regcx, getreg16(regcx)-1);
                            if ((reptype == 1) && !zf) break;
                              else if ((reptype == 2) && (zf == 1)) break;
                            if (!reptype) break;
                            ip = firstip;
                            break;
                        case 0xAF: //AF SCASW
                            if (reptype && (getreg16(regcx)==0)) break;
                            oper1 = getmem16(segregs[reges], getreg16(regdi)); oper2 = getreg16(regax);
                            flag_sub16(oper1, oper2);
                            if (df) putreg16(regdi, getreg16(regdi) - 2);
                              else putreg16(regdi, getreg16(regdi) + 2);
                            if (reptype) putreg16(regcx, getreg16(regcx)-1);
                            if ((reptype == 1) && !zf) break;
                              else if ((reptype == 2) & (zf == 1)) break;
                            if (!reptype) break;
                            ip = firstip;
                            break;

continued...
 
Code:
                        case 0xB0: //B0 MOV regs.byteregs[regal] Ib
                            regs.byteregs[regal] = getmem8(segregs[regcs], ip); StepIP(1);
                            break;
                        case 0xB1: //B1 MOV regs.byteregs[regcl] Ib
                            regs.byteregs[regcl] = getmem8(segregs[regcs], ip); StepIP(1);
                            break;
                        case 0xB2: //B2 MOV regs.byteregs[regdl] Ib
                            regs.byteregs[regdl] = getmem8(segregs[regcs], ip); StepIP(1);
                            break;
                        case 0xB3: //B3 MOV regs.byteregs[regbl] Ib
                            regs.byteregs[regbl] = getmem8(segregs[regcs], ip); StepIP(1);
                            break;
                        case 0xB4: //B4 MOV regs.byteregs[regah] Ib
                            regs.byteregs[regah] = getmem8(segregs[regcs], ip); StepIP(1);
                            break;
                        case 0xB5: //B5 MOV regs.byteregs[regch] Ib
                            regs.byteregs[regch] = getmem8(segregs[regcs], ip); StepIP(1);
                            break;
                        case 0xB6: //B6 MOV regs.byteregs[regdh] Ib
                            regs.byteregs[regdh] = getmem8(segregs[regcs], ip); StepIP(1);
                            break;
                        case 0xB7: //B7 MOV regs.byteregs[regbh] Ib
                            regs.byteregs[regbh] = getmem8(segregs[regcs], ip); StepIP(1);
                            break;
                        case 0xB8: //B8 MOV eAX Iv
                            oper1 = getmem16(segregs[regcs], ip); StepIP(2);
                            putreg16(regax, oper1);
                            break;
                        case 0xB9: //B9 MOV eCX Iv
                            oper1 = getmem16(segregs[regcs], ip); StepIP(2);
                            putreg16(regcx, oper1);
                            break;
                        case 0xBA: //BA MOV eDX Iv
                            oper1 = getmem16(segregs[regcs], ip); StepIP(2);
                            putreg16(regdx, oper1);
                            break;
                        case 0xBB: //BB MOV eBX Iv
                            oper1 = getmem16(segregs[regcs], ip); StepIP(2);
                            putreg16(regbx, oper1);
                            break;
                        case 0xBC: //BC MOV eSP Iv
                            putreg16(regsp, getmem16(segregs[regcs], ip)); StepIP(2);
                            break;
                        case 0xBD: //BD MOV eBP Iv
                            putreg16(regbp, getmem16(segregs[regcs], ip)); StepIP(2);
                            break;
                        case 0xBE: //BE MOV eSI Iv
                            putreg16(regsi, getmem16(segregs[regcs], ip)); StepIP(2);
                            break;
                        case 0xBF: //BF MOV eDI Iv
                            putreg16(regdi, getmem16(segregs[regcs], ip)); StepIP(2);
                            break;
                        case 0xC0: //C0 GRP2 byte imm8 (80186+)
                            modregrm();
                            oper1b = readrm8(rm);
                            oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            writerm8(rm, op_grp2_8(oper2b));                            
                            break;
                        case 0xC1: //C1 GRP2 word imm8 (80186+)
                            modregrm();
                            oper1 = readrm16(rm);
                            oper2b = getmem8(segregs[regcs], ip); StepIP(1);
                            writerm16(rm, op_grp2_16(oper2b));                            
                            break;
                        case 0xC2: //C2 RET Iw
                            oper1 = getmem16(segregs[regcs], ip);
                            ip = pop();
                            putreg16(regsp, getreg16(regsp) + oper1);
                            break;
                        case 0xC3: //C3 RET
                            ip = pop();
                            break;
                        case 0xC4: //C4 LES Gv Mp
                            modregrm();
                            getea(rm);
                            putreg16(reg, read86(ea) + read86(ea + 1) * 256);
                            segregs[reges] = read86(ea + 2) + read86(ea + 3) * 256;
                            break;
                        case 0xC5: //C5 LDS Gv Mp
                            modregrm();
                            getea(rm);
                            putreg16(reg, read86(ea) + read86(ea + 1) * 256);
                            segregs[regds] = read86(ea + 2) + read86(ea + 3) * 256;
                            break;
                        case 0xC6: //C6 MOV Eb Ib
                            modregrm();
                            writerm8(rm, getmem8(segregs[regcs], ip)); StepIP(1);
                            break;
                        case 0xC7: //C7 MOV Ev Iv
                            modregrm();
                            writerm16(rm, getmem16(segregs[regcs], ip)); StepIP(2);
                            break;
                        case 0xC8: //C8 ENTER (80186+)
                            stacksize = getmem16(segregs[regcs], ip); StepIP(2);
                            nestlev = getmem8(segregs[regcs], ip); StepIP(1);
                            push(getreg16(regbp));
                            frametemp = getreg16(regsp);
                            if (nestlev) {
                                    for (temp16=1; temp16<nestlev; temp16++) {
                                            putreg16(regbp, getreg16(regbp) - 2);
                                            push(getreg16(regbp));
                                    }
                                    push(getreg16(regsp));
                            }
                            putreg16(regbp, frametemp);
                            putreg16(regsp, getreg16(regbp) - stacksize);
                            
                            break;
                        case 0xC9: //C9 LEAVE (80186+)
                            putreg16(regsp, getreg16(regbp));
                            putreg16(regbp, pop());
                            
                            break;
                        case 0xCA: //CA RETF Iw
                            oper1 = getmem16(segregs[regcs], ip);
                            ip = pop(); segregs[regcs] = pop();
                            putreg16(regsp, getreg16(regsp) + oper1);
                            break;
                        case 0xCB: //CB RETF
                            ip = pop();; segregs[regcs] = pop();
                            break;
                        case 0xCC: //CC INT 3
                            intcall86(3);
                            break;
                        case 0xCD: //CD INT Ib
                            oper1b = getmem8(segregs[regcs], ip); StepIP(1);
                            intcall86(oper1b);
                            break;
                        case 0xCE: //CE INTO
                            if (of) intcall86(4);
                            break;
                        case 0xCF: //CF IRET
                            ip = pop(); segregs[regcs] = pop();
                            decodeflagsword(pop());
                            //if (net.enabled) net.canrecv = 1;
                            break;
                        case 0xD0: //D0 GRP2 Eb 1
                            modregrm();
                            oper1b = readrm8(rm);
                            writerm8(rm, op_grp2_8(1));
                            break;
                        case 0xD1: //D1 GRP2 Ev 1
                            modregrm();
                            oper1 = readrm16(rm);
                            writerm16(rm, op_grp2_16(1));
                            break;
                        case 0xD2: //D2 GRP2 Eb regs.byteregs[regcl]
                            modregrm();
                            oper1b = readrm8(rm);
                            writerm8(rm, op_grp2_8(regs.byteregs[regcl]));
                            break;
                        case 0xD3: //D3 GRP2 Ev regs.byteregs[regcl]
                            modregrm();
                            oper1 = readrm16(rm);
                            writerm16(rm, op_grp2_16(regs.byteregs[regcl]));
                            break;
                        case 0xD4: //D4 AAM I0
                            oper1 = getmem8(segregs[regcs], ip); StepIP(1);
                            if (!oper1) { intcall86(0); return; } //division by zero
                            regs.byteregs[regah] = (regs.byteregs[regal] / oper1) & 255;
                            regs.byteregs[regal] = (regs.byteregs[regal] % oper1) & 255;
                            flag_szp16 (getreg16(regax));
                            break;
                        case 0xD5: //D5 AAD I0
                            oper1 = getmem8(segregs[regcs], ip); StepIP(1);
                            regs.byteregs[regal] = (regs.byteregs[regah] * oper1 + regs.byteregs[regal]) & 255;
                            regs.byteregs[regah] = 0;
                            flag_szp16(regs.byteregs[regah] * oper1 + regs.byteregs[regal]);
                            sf = 0;
                            break;
                        case 0xD7: //D7 XLAT
                            putreg8(regal, read86(segbase(useseg) + (uint32_t)getreg16(regbx) + (uint32_t)getreg8(regal)));
                            break;
                        case 0xE0: //E0 LOOPNZ Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            putreg16(regcx, getreg16(regcx) - 1);
                            if ((getreg16(regcx)) && !zf) ip = ip + temp16;
                            break;
                        case 0xE1: //E1 LOOPZ Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            putreg16(regcx, (getreg16(regcx)) - 1);
                            if ((getreg16(regcx)) && (zf == 1)) ip = ip + temp16;
                            break;
                        case 0xE2: //E2 LOOP Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            putreg16(regcx, (getreg16(regcx)) - 1);
                            if (getreg16(regcx)) ip = ip + temp16;
                            break;
                        case 0xE3: //E3 JCXZ Jb
                            temp16 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            if (!(getreg16(regcx))) ip = ip + temp16;
                            break;
                        case 0xE4: //E4 IN regs.byteregs[regal] Ib                        
                            oper1b = getmem8(segregs[regcs], ip);
                            StepIP(1);
                            regs.byteregs[regal] = (uint8_t)portin(oper1b);
                            break;
                        case 0xE5: //E5 IN eAX Ib
                            oper1b = getmem8(segregs[regcs], ip);
                            StepIP(1);
                            putreg16(regax, portin(oper1b));
                            break;
                        case 0xE6: //E6 OUT Ib regs.byteregs[regal]
                            oper1b = getmem8(segregs[regcs], ip);
                            StepIP(1);
                            portout16 = 0;
                            portout(oper1b, regs.byteregs[regal]);
                            break;
                        case 0xE7: //E7 OUT Ib eAX
                            oper1b = getmem8(segregs[regcs], ip);
                            StepIP(1);
                            portout16 = 1;
                            portout(oper1b, (getreg16(regax)));
                            break;
                        case 0xE8: //E8 CALL Jv
                            oper1 = getmem16(segregs[regcs], ip); StepIP(2);
                            push(ip);
                            ip = ip + oper1;
                            break;
                        case 0xE9: //E9 JMP Jv
                            oper1 = getmem16(segregs[regcs], ip); StepIP(2);
                            ip = ip + oper1;
                            break;
                        case 0xEA: //EA JMP Ap
                            oper1 = getmem16(segregs[regcs], ip); StepIP(2);
                            oper2 = getmem16(segregs[regcs], ip);
                            ip = oper1; segregs[regcs] = oper2;
                            break;
                        case 0xEB: //EB JMP Jb
                            oper1 = signext(getmem8(segregs[regcs], ip)); StepIP(1);
                            ip = ip + oper1;
                            break;
                        case 0xEC: //EC IN regs.byteregs[regal] regdx
                            oper1 = (getreg16(regdx));
                            regs.byteregs[regal] = (uint8_t)portin(oper1);
                            break;
                        case 0xED: //ED IN eAX regdx
                            oper1 = (getreg16(regdx));
                            putreg16(regax, portin(oper1));
                            break;
                        case 0xEE: //EE OUT regdx regs.byteregs[regal]
                            oper1 = (getreg16(regdx));
                            portout16 = 0;
                            portout(oper1, regs.byteregs[regal]);
                            break;
                        case 0xEF: //EF OUT regdx eAX
                            oper1 = (getreg16(regdx));
                            portout16 = 1;
                            #ifdef REAL_LPT_IO
                                if ((oper1>=0x378) && (oper1<=0x37A)) outw(oper1, (getreg16(regax)));
                                   else portout(oper1, (getreg16(regax)));
                            #else
                                portout(oper1, (getreg16(regax)));
                            #endif
                            break;
                        case 0xF0: //F0 LOCK
                            break;
                        case 0xF4: //F4 HLT
                            ip--;
                            if (!ifl) {
                               sprintf(msg, "Fake86 %s [halted]", build);
                               SDL_WM_SetCaption(msg, NULL);
                               SDL_Delay(100);
                            }
                            break;
                        case 0xF5: //F5 CMC
                            if (!cf) cf = 1; else cf = 0;
                            break;
                        case 0xF6: //F6 GRP3a Eb
                            modregrm();
                            oper1b = readrm8(rm);
                            op_grp3_8();
                            if ((reg > 1) && (reg < 4))     writerm8(rm, res8);
                            
                            break;
                        case 0xF7: //F7 GRP3b Ev
                            modregrm();
                            oper1 = readrm16(rm);
                            op_grp3_16();
                            if ((reg > 1) && (reg < 4)) writerm16(rm, res16);
                            break;
                        case 0xF8: //F8 CLC
                            cf = 0;
                            break;
                        case 0xF9: //F9 STC
                            cf = 1;
                            break;
                        case 0xFA: //FA CLI
                            ifl = 0;
                            break;
                        case 0xFB: //FB STI
                            ifl = 1;
                            break;
                        case 0xFC: //FC CLD
                            df = 0;
                            break;
                        case 0xFD: //FD STD
                            df = 1;
                            break;
                        case 0xFE: //FE GRP4 Eb
                            modregrm();
                            oper1b = readrm8(rm); oper2b = 1;
                            if (!reg) {
                                    tempcf = cf;
                                    res8 = oper1b + oper2b;
                                    flag_add8(oper1b, oper2b);
                                    cf = tempcf; writerm8(rm, res8);
                            } else {
                                    tempcf = cf;
                                    res8 = oper1b - oper2b;
                                    flag_sub8(oper1b, oper2b);
                                    cf = tempcf; writerm8(rm, res8);
                            }
                            break;
                        case 0xFF: //FF GRP5 Ev
                            modregrm();
                            oper1 = readrm16(rm);
                            op_grp5();
                            break;
                        default: //invalid opcode
                            intcall86(6); //trip invalid opcode exception (this occurs on the 80186+, 8086/8088 CPUs treat them as NOPs)
                            break;
                }
                if (!running) return;
        }
}


the end.. i hope i didn't annoy anybody which this huge string of code. i think it clearly demonstrates CPU emulation though. seeing the details of a real-world CPU emulator should show you exactly how this stuff works. any questions, just ask.
 
Last edited:
Brilliant Mike! Great job you have done...
Looking at the number of hits this thread has had, in such a short time,
shows that there are many interested people who want to learn about
computers in easy to understand terms. Your work would be a starting
point for many of us.

And another thank you note to everybody who contributed with their
informative responses. Please keep on adding more and more comments
if you find appropriate.

ziloo
 
As stated before, doing real emulation is very difficult. As an example, boot DOS on your emulator and run the following program.

Running it on my XT clone under DOS 6.22 (although the OS is irrelevant), I get an answer of 11.

What do you get? (you too, Valentin!)
 

Attachments

  • t1.zip
    164 bytes · Views: 1
As stated before, doing real emulation is very difficult. As an example, boot DOS on your emulator and run the following program.

Running it on my XT clone under DOS 6.22 (although the OS is irrelevant), I get an answer of 11.

What do you get? (you too, Valentin!)

running it in a windows 7 command prompt it returns 10, so does DOSBox, QEMU and Fake86.

i disassembled it in IDA Pro, and it looks like this:

Code:
public start
start proc near
push	cs
pop	ds
cli
mov	bx, 10Dh
mov	cx, 0EBh
mov	al, 0Ah
mov	[bx], cx
inc	al
sti
aam
add	ax, 3030h
xchg	al, ah
mov	word_133, ax
mov	ah, 9
mov	dx, 123h
int	21h		; DOS -	PRINT STRING
			; DS:DX	-> string terminated by	"$"
int	20h		; DOS -	PROGRAM	TERMINATION
start endp		; returns to DOS--identical to INT 21/AH=00h

db 0Dh,	0Ah, 54h, 68h, 65h, 20h, 61h, 6Eh
db 73h,	77h, 65h, 72h, 20h, 69h, 73h, 20h
word_133 dw 3030h
or	ax, 240Ah
seg000 ends


end start
 
Yeah, now run it on a real 8088, not in someone's emulator.

It's not long; you can step through it with DEBUG, but you'll get 10 if you do.

indeed, my 5150 shows the answer is 11. this is very interesting, what happens here? i'm surprised QEMU didn't get it right. very surprised. must be some sort of obscure hardware bug? i can't imagine so many emulators (all of them?) don't return 11 if it wasn't a bug not in the documentation.
 
Last edited:
It's not a bug, Mike. It works exactly according to the published hardware specs. It also comes up with 11 on V20, V30 and 8086 CPUs.

My point is not to be a smart a** but to point out how difficult accurate emulation is. This one was just off the top of my head; I'm pretty sure that if I thought about it long enough, I could come up with other cases.
 
Ziloo,

Are you a programmer? If so, how much experience do you have?

Here is the no-nonsense, non pedantic answer ... Substitute the word emulator for simulator or simulator for emulator as you see fit; there is no point in getting wrapped up in knots on a basic question.

A simulator or emulator is a program that mimics the behavior or something else. For example, lets say you have a nice x86 compatible PC and you want to relive the glory days of the 6502 processor. You could go find a 6502 based machine, or you could find or write a program that mimics one on your x86 machine.

How do you simulate another chip or system? Simple .. a few bits at a time. :) You need to change the way you look at the chip. Instead of being a black piece of ceramic and plastic that has something magical, look at it as a state machine. (And this is where some programming experience would be helpful.) The 6502 is just a hardware implementation of a program that reads a few bytes at a time, and performs some action based on the bytes that it reads.

For example, give a 6502 some form of "add" instruction and two operands, and it will probably add the two operands and store the results somewhere. The hardware specification will tell you where the operands can come from (memory, registers, etc.), how precise the math operation is, and where the result is stored. The add instruction probably has side effects on other parts of the chip, and those will be documented too.

Your program mimics the behavior of the 6502 by reading simulated "memory", performing operations on simulated registers, and doing everything that the 6502 is supposed to do. If the add command only works on 16 bits at a time and throws away overflow bits, you need to do that in your program too. To do all of this you probably need to allocate variables that will serve as simulated registers, allocate a big array to serve as simulated memory, and write a lot of code to simulate every instruction the 6502 knows. And then you need to simulate other behaviors, likes interrupts that result from invalid instructions, dividing by zero, etc.

And simulating a single chip is nice, but usually not enough. To take it a step further you need to simulate other parts of the computer system, like the video memory connected on the bus, the floppy drive controller, the network controller, etc. The wide array of devices makes things complicated.

"Emulation" implies that you mimic the behavior correctly, but maybe the timing is not perfect. A good example of an emulator is DOSBox, which emulates a simple DOS machine - it does most things well, but it doesn't go through the trouble of interpreting every instruction and simulating every possible device. It does things at a high level. A good example of a simulator is Fake86, which actually tries to "play computer" by mimicking the lowest levels of the computer. Commercial grade simulators will even get the instruction timings perfect, which is important for chip design and operating system development.

So, are you planning on writing a few thousand lines of code to emulate or simulate something?


Mike
 
Great material Mike :)!


....
So, are you planning on writing a few thousand lines of code to emulate or simulate something?

Mike

At a time and age when "Flowcharting" is considered sacriligious and ineffective,
getting a mental image of what you are about to read is very important.
I always have difficulty reading a code when I have no idea what its
main components are doing, no matter how well-commented each line of
the program is. For an experienced programmer (not me) many programming
concepts could be trivial, and documenting it could be considered a waste of
time; but to me these explanations are very valuable as a reference for
anybody who wants to dabble in the field.

I am not about to write 5000 lines of an Emulating/Simulating program, but
I have looked at small pieces of code here and there and found that it was
necessary to ask our masters here to shed some light on what is involved.
So far, all the explanations that I have read here are the best pieces of
information I have found anywhere.

Thanks everybody again!

ziloo
 
Last edited:
As stated before, doing real emulation is very difficult. As an example, boot DOS on your emulator and run the following program.

Running it on my XT clone under DOS 6.22 (although the OS is irrelevant), I get an answer of 11.

What do you get? (you too, Valentin!)

Hi Chuck,

I get an answer of 11 on Flea86..

Regards Valentin
 
Very good, Valentin! I assume that you know the trick. Congratulations! :thumbsup:

Interestingly, that issue was also a source of problems when using the NEC V20 in 8080 mode...
 
wow, valentin. i am officially impressed. so what IS the trick? :)

i still can't believe mr. bellard didn't get that right in QEMU. same goes for microsoft's NTVDM. well, microsoft not so much. nevermind....
 
the INC AL is changed to a JMP 10Fh and the real 8088 caches it while it's still INC AL?? that would explain why it doesnt work on an 8088 when stepped.
 
yep, bingo! that's it. the 8088 has a 4 byte prefetch. 8086 has 6 byte. now i can fix it. woohoo! thanks for bringing up that trick chuck.

EDIT: although i can fix it, i'm starting to wonder if its worth the (very, very, very) slight CPU drain of keeping a prefetch queue array and keeping it updated. i can't imagine any real world code out there relies on it, but please correct me if i'm wrong. on the other hand, accurate emulation is always a good thing.
 
Last edited:
Back
Top