/* 
 * This file defines a 8086 virtual machine
 * 
 * Copywrite 1993, Hamish Coleman
 *
 * All things going well, this file would be the only file needing
 * changes for using this program on a non Intel x86 computer
 * (I have not even got one, so dont ask)
 * Have just thought about this - also needed would be _heaps_
 * of ntohl and ntohs etc etc...
 *
 */
 
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <termio.h>
#include <termcap.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/times.h>
#include <sys/time.h>
#include <limits.h>
#include <linux/fd.h>
#include "global.h"
#include "vm.h"
#include "int21_names.h"

int vm_status;
int vm_iflag;
int tmp = 0x55; 

int inb(int port)
{
	port &= 0xffff;
	debmsg("inb [0x%x] \n", port);
	return (tmp ^= 0xff);
}

void outb(int port, int byte)
{
	port &= 0xffff;
	byte &= 0xff;
	debmsg("outb [0x%x] 0x%x \n", port, byte);
}

void emulate_push(int i)
{
	*SEG_ADR((us *), ss, sp) = i;
	_regs.esp -= 2;
}

void emulate_int(int i)
{
	us *ssp;
	
#ifdef 0
	/* remove some annoying and _constant_ interrupts
	 * this was more usefull before I changed this
	 * from an IBM emulator to a DOS emulator
	 */
	if (i != 0x16 && i != 0x28 && i != 0x2a) {
		debmsg("INT%02x ",i);
#endif
		switch (i) {
			case 0x21: /* Dos int */
				debmsg("i21: fn %02x %s\n",HI(ax),names21[HI(ax)]);
				break;
			case 0x13: /* BIOS disk int */
				debmsg("i13: fn %02x %s\n",HI(ax),bios_13_names[HI(ax)]);
				break;
			case 0x10: /* Video */
				debmsg("i10: fn %02x %s\n",HI(ax),bios_10_names[HI(ax)]);
		}
#ifdef 0
		else
			debmsg("\n");
	}
#endif
	ssp = SEG_ADR((us *), ss, sp);
	*--ssp = _regs.eflags;
	*--ssp = _regs.cs;
	*--ssp = _regs.eip;
	_regs.esp -= 6;
	_regs.cs =  ((us *)0)[ (i<<1) +1];
	_regs.eip = ((us *)0)[  i<<1    ];
	_regs.eflags &= 0xfffffcff;
}

void emulate_iret(void)
{
	us *ssp;
	
	ssp = SEG_ADR((us *), ss, sp);
	_regs.eip = *ssp++;
	_regs.cs = *ssp++;
	_regs.eflags = (_regs.eflags & 0xffff0000) | *ssp++;
	_regs.esp += 6;
}

void sigalrm(int sig)
{
	vm_status = F_sigalrm;
}

void sigsegv(int sig)
{
	us *ssp;
	unsigned char *csp;

	csp = SEG_ADR((unsigned char *), cs, ip);
	switch (*csp) {
		case I_bound: /* bound instruction */
			debmsg("BOUND INSN\n");
			show_regs();
			_regs.eip += 4;  /* check this? */
			/* emulate_int(5) */
			break;
		case I_int: /* int xx */
			_regs.eip += 2;
			emulate_int((int)*++csp);
			break;
		case I_int3: /* int 3 */
			_regs.eip += 1;
			debmsg("sigsegv: INT3\n");
			debmsg("so this CAN happen!\n");
			_exit(80);
			emulate_int(3);
			break;
		case I_iret: /* iret */
			emulate_iret();
			break;           /* do we need to set RF ? */

		case I_inw: /* inw xx */
			_regs.eax &= ~0xff00;
			_regs.eax |= inb((int)csp[1] +1) << 8;
		case I_inb: /* inb xx */
			_regs.eax &= ~0xff;
			_regs.eax |= inb((int)csp[1]);
			_regs.eip += 2;
			break;
		case I_inw_dx: /* inw dx */
			_regs.eax &= ~0xff00;
			_regs.eax |= inb(_regs.edx +1) << 8;
		case I_inb_dx: /* inb dx */
			_regs.eax &= ~0xff;
			_regs.eax |= inb(_regs.edx);
			_regs.eip += 1;
			break;

		case I_outw: /* outw xx */
			outb((int)csp[1] +1, _regs.eax >>8);
		case I_outb: /* outb xx */
			outb((int)csp[1], _regs.eax);
			_regs.eip += 2;
			break;
		case I_outw_dx: /* outw dx */
			outb(_regs.edx +1, _regs.eax >>8);
		case I_outb_dx: /* outb dx */
			outb(_regs.edx, _regs.eax);
			_regs.eip += 1;
			break;
			
		case I_insb:
		case I_insw:
		case I_outsb:
		case I_outsw:
			debmsg("Error: in/out string w/b\n");
			show_regs();
			_regs.eip++;
			break;
	
		case I_cli: /* cli */
			vm_iflag = 0;
			_regs.eip += 1;
			break;
		case I_sti: /* sti */
			vm_iflag = 1;
			_regs.eip += 1;
			break;
	
		case I_pushf: /* pushf */
			_regs.eflags &= 0xfff ^ IF;
			if (vm_iflag) _regs.eflags |= IF;
			ssp = SEG_ADR((us *), ss, sp);
			*--ssp = (us)_regs.eflags;
			_regs.esp -= 2;
			_regs.eip += 1;
			break;
		case I_popf: /* popf */
			ssp = SEG_ADR((us *), ss, sp);
			_regs.eflags &= ~0xffff;
			_regs.eflags |= (int)*ssp;
			_regs.esp += 2;
			_regs.eip += 1;
			break;
	
		case I_hlt: /* HLT  */
			_regs.eip += 1;
			vm_status = I_hlt;
			return;
		case I_lock: /* lock */
			vm_status = I_lock;
			return;
		default:
			vm_status = F_sigsegv;
			return;
	}
	if ((_regs.eflags & TF) && !(_regs.eflags & RF) &&
	    (*csp != I_iret)) {                 /* if we are TF and not (IRET or RF) then */
		debmsg("Hmmm.... obviously this CAN happen !....");
		debmsg("found Single Step in the SigSegV Fn");
		_exit(80);
		emulate_int(1);                 /* we are in single step mode ... */
	}
}

void sigill(int sig)
{
unsigned char *csp;
int i, d;

	csp = SEG_ADR((unsigned char *), cs, ip);
	i = (csp[0] << 8) + csp[1]; /* swapped */
	if ((i & 0xf800) != 0xd800) { /* no fpu instruction */
		vm_status = F_sigill;
		return;
	}
	switch(i & 0xc0) {
		case 0x00:
			if ((i & 0x7) == 0x6) {
				d = *(short *)(csp +2);
				_regs.eip += 4;
			} else {
				_regs.eip += 2;
				d = 0;
			}
			break;
		case 0x40:
			d = (signed)csp[2];
			_regs.eip += 3;
			break;
		case 0x80:
			d = *(short *)(csp +2);
			_regs.eip += 4;
			break;
		default:
			_regs.eip += 2;
			d = 0;
	}
	vm_status = F_mathem;
}

void sigfpe(int sig)
{
	vm_status = F_sigfpe;
}

void sigtrap(int sig)
{
unsigned char *csp;

	csp = SEG_ADR((unsigned char *), cs, ip);
	if (*csp == 0xcc)
	{
		_regs.eip++;
		emulate_int(3);
	} else
	{
		emulate_int(1);
	}
}

#define SETSIG(sig, fun)	sa.sa_handler = fun; \
				sa.sa_flags = 0; \
				sa.sa_mask = 0; \
				sigaction(sig, &sa, NULL);


void vm_prep(void)
{
struct sigaction sa;

	SETSIG(SIGSEGV, sigsegv);
	SETSIG(SIGILL, sigill);
	SETSIG(SIGALRM, sigalrm);
	SETSIG(SIGFPE, sigfpe);
	SETSIG(SIGTRAP, sigtrap);
}

int vm(void)
{
	vm_status = 0;
	for (;vm_status==0;) 
		(void)vm86(&vm86s);
	return vm_status;
}