diff options
Diffstat (limited to 'src')
36 files changed, 8867 insertions, 0 deletions
diff --git a/src/fake86/adlib.c b/src/fake86/adlib.c new file mode 100755 index 0000000..cf17584 --- /dev/null +++ b/src/fake86/adlib.c @@ -0,0 +1,240 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* adlib.c: very ugly Adlib OPL2 emulation for Fake86. very much a work in progress. :) */ + +#include "config.h" +#include <SDL/SDL.h> +#include <stdint.h> +#include <stdio.h> + +extern void set_port_write_redirector (uint16_t startport, uint16_t endport, void *callback); +extern void set_port_read_redirector (uint16_t startport, uint16_t endport, void *callback); + +extern int32_t usesamplerate; + +double samprateadjust = 1.0; +uint8_t optable[0x16] = { 0, 0, 0, 1, 1, 1, 255, 255, 0, 0, 0, 1, 1, 1, 255, 255, 0, 0, 0, 1, 1, 1 }; +uint16_t adlibregmem[0xFF], adlibaddr = 0; + +int8_t waveform[4][64] = { +	{ 1, 8, 13, 20, 26, 31, 37, 41, 47, 49, 54, 58, 58, 62, 63, 63, 64, 63, 62, 61, 58, 55, 52, 47, 45, 38, 34, 27, 23, 17, 10, 4,-2,-8,-15,-21,-26,-34,-36,-42,-48,-51,-54,-59,-60,-62,-64,-65,-65,-63,-64,-61,-59,-56,-53,-48,-46,-39,-36,-28,-24,-17,-11,-6 }, +	{ 1, 8, 13, 20, 25, 32, 36, 42, 46, 50, 54, 57, 60, 61, 62, 64, 63, 65, 61, 61, 58, 55, 51, 49, 44, 38, 34, 28, 23, 16, 11, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +	{ 1, 8, 13, 21, 25, 31, 36, 43, 45, 50, 54, 57, 59, 62, 63, 63, 63, 64, 63, 59, 59, 55, 52, 48, 44, 38, 34, 28, 23, 16, 10, 4, 2, 7, 14, 20, 26, 31, 36, 42, 45, 51, 54, 56, 60, 62, 62, 63, 65, 63, 62, 60, 58, 55, 52, 48, 44, 38, 34, 28, 23, 17, 10, 3 }, +	{ 1, 8, 13, 20, 26, 31, 36, 42, 46, 51, 53, 57, 60, 62, 61, 66, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 13, 21, 25, 32, 36, 41, 47, 50, 54, 56, 60, 62, 61, 67, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +int8_t oplwave[4][256] = { +	{ +		0, 1, 3, 4, 6, 7, 9, 11, 12, 14, 15, 17, 18, 20, 22, 23, 24, 26, 27, 29, 30, 31, 33, 34, 36, 37, 38, 40, 40, 42, 43, 44, 46, 46, 48, 49, 50, 51, 51, 53, +		53, 54, 55, 56, 57, 57, 58, 59, 59, 60, 61, 61, 62, 62, 63, 63, 63, 64, 64, 64, 116, 116, 116, 116, 116, 116, 116, 116, 116, 64, 64, 64, 63, 63, 63, 62, 62, 61, 61, 60, +		59, 59, 58, 57, 57, 56, 55, 54, 53, 53, 51, 51, 50, 49, 48, 46, 46, 44, 43, 42, 40, 40, 38, 37, 36, 34, 33, 31, 30, 29, 27, 26, 24, 23, 22, 20, 18, 17, 15, 14, +		12, 11, 9, 7, 6, 4, 3, 1, 0, -1, -3, -4, -6, -7, -9, -11, -12, -14, -15, -17, -18, -20, -22, -23, -24, -26, -27, -29, -30, -31, -33, -34, -36, -37, -38, -40, -40, -42, -43, -44, +		-46, -46, -48, -49, -50, -51, -51, -53, -53, -54, -55, -56, -57, -57, -58, -59, -59, -60, -61, -61, -62, -62, -63, -63, -63, -64, -64, -64, -116, -116, -116, -116, -116, -116, -116, -116, -116, -64, -64, -64, +		-63, -63, -63, -62, -62, -61, -61, -60, -59, -59, -58, -57, -57, -56, -55, -54, -53, -53, -51, -51, -50, -49, -48, -46, -46, -44, -43, -42, -40, -40, -38, -37, -36, -34, -33, -31, -30, -29, -27, -26, +		-24, -23, -22, -20, -18, -17, -15, -14, -12, -11, -9, -7, -6, -4, -3, -1 +	}, + +	{ +		0, 1, 3, 4, 6, 7, 9, 11, 12, 14, 15, 17, 18, 20, 22, 23, 24, 26, 27, 29,30, 31, 33, 34, 36, 37, 38, 40, 40, 42, 43, 44, 46, 46, 48, 49, 50, 51, 51, 53, +		53, 54, 55, 56, 57, 57, 58, 59, 59, 60, 61, 61, 62, 62, 63, 63, 63, 64, 64, 64, 116, 116, 116, 116, 116, 116, 116, 116, 116, 64, 64, 64, 63, 63, 63, 62, 62, 61, 61, 60, +		59, 59, 58, 57, 57, 56, 55, 54, 53, 53, 51, 51, 50, 49, 48, 46, 46, 44, 43, 42, 40, 40, 38, 37, 36, 34, 33, 31, 30, 29, 27, 26, 24, 23, 22, 20, 18, 17, 15, 14, +		12, 11, 9, 7, 6, 4, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +	}, + + +	{ +		0, 1, 3, 4, 6, 7, 9, 11, 12, 14, 15, 17, 18, 20, 22, 23, 24, 26, 27, 29, 30, 31, 33, 34, 36, 37, 38, 40, 40, 42, 43, 44, 46, 46, 48, 49, 50, 51, 51, 53, +		53, 54, 55, 56, 57, 57, 58, 59, 59, 60, 61, 61, 62, 62, 63, 63, 63, 64, 64, 64, 116, 116, 116, 116, 116, 116, 116, 116, 116, 64, 64, 64, 63, 63, 63, 62, 62, 61, 61, 60, +		59, 59, 58, 57, 57, 56, 55, 54, 53, 53, 51, 51, 50, 49, 48, 46, 46, 44, 43, 42, 40, 40, 38, 37, 36, 34, 33, 31, 30, 29, 27, 26, 24, 23, 22, 20, 18, 17, 15, 14, +		12, 11, 9, 7, 6, 4, 3, 1, 0, 1, 3, 4, 6, 7, 9, 11, 12, 14, 15, 17, 18, 20, 22, 23, 24, 26, 27, 29, 30, 31, 33, 34, 36, 37, 38, 40, 40, 42, 43, 44, +		46, 46, 48, 49, 50, 51, 51, 53, 53, 54, 55, 56, 57, 57, 58, 59, 59, 60, 61, 61, 62, 62, 63, 63, 63, 64, 64, 64, 116, 116, 116, 116, 116, 116, 116, 116, 116, 64, 64, 64, +		63, 63, 63, 62, 62, 61, 61, 60, 59, 59, 58, 57, 57, 56, 55, 54, 53, 53, 51, 51, 50, 49, 48, 46, 46, 44, 43, 42, 40, 40, 38, 37, 36, 34, 33, 31, 30, 29, 27, 26, +		24, 23, 22, 20, 18, 17, 15, 14, 12, 11, 9, 7, 6, 4, 3, 1 +	}, + + +	{ +		0, 1, 3, 4, 6, 7, 9, 11, 12, 14, 15, 17, 18, 20, 22, 23, 24, 26, 27, 29, 30, 31, 33, 34, 36, 37, 38, 40, 40, 42, 43, 44, 46, 46, 48, 49, 50, 51, 51, 53, +		53, 54, 55, 56, 57, 57, 58, 59, 59, 60, 61, 61, 62, 62, 63, 63, 63, 64, 64, 64, 116, 116, 116, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 4, 6, 7, 9, 11, 12, 14, 15, 17, 18, 20, 22, 23, 24, 26, 27, 29, 30, 31, 33, 34, 36, 37, 38, 40, 40, 42, 43, 44, +		46, 46, 48, 49, 50, 51, 51, 53, 53, 54, 55, 56, 57, 57, 58, 59, 59, 60, 61, 61, 62, 62, 63, 63, 63, 64, 64, 64, 116, 116, 116, 116, 0, 0, 0, 0, 0, 0, 0, 0, +		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +	} + +}; + +uint8_t oplstep[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +struct structadlibop { +	uint8_t wave; +} adlibop[9][2]; + +struct structadlibchan { +	uint16_t freq; +	double convfreq; +	uint8_t keyon; +	uint16_t octave; +	uint8_t wavesel; +} adlibch[9]; + +double attacktable[16] = { 1.0003, 1.00025, 1.0002, 1.00015, 1.0001, 1.00009, 1.00008, 1.00007, 1.00006, 1.00005, 1.00004, 1.00003, 1.00002, 1.00001, 1.000005 }; //1.003, 1.05, 1.01, 1.015, 1.02, 1.025, 1.03, 1.035, 1.04, 1.045, 1.05, 1.055, 1.06, 1.065, 1.07, 1.075 }; +double decaytable[16] = { 0.99999, 0.999985, 0.99998, 0.999975, 0.99997, 0.999965, 0.99996, 0.999955, 0.99995, 0.999945, 0.99994, 0.999935, 0.99994, 0.999925, 0.99992, 0.99991 }; +double adlibenv[9], adlibdecay[9], adlibattack[9]; +uint8_t adlibdidattack[9], adlibpercussion = 0, adlibstatus = 0; + +uint16_t adlibport = 0x388; + +void outadlib (uint16_t portnum, uint8_t value) { +	if (portnum==adlibport) { +			adlibaddr = value; +			return; +		} +	portnum = adlibaddr; +	adlibregmem[portnum] = value; +	switch (portnum) { +			case 4: //timer control +				if (value&0x80) { +						adlibstatus = 0; +						adlibregmem[4] = 0; +					} +				break; +			case 0xBD: +				if (value & 0x10) adlibpercussion = 1; +				else adlibpercussion = 0; +				break; +		} +	if ( (portnum >= 0x60) && (portnum <= 0x75) ) { //attack/decay +			portnum &= 15; +			adlibattack[portnum] = attacktable[15- (value>>4) ]*1.006; +			adlibdecay[portnum] = decaytable[value&15]; +		} +	else if ( (portnum >= 0xA0) && (portnum <= 0xB8) ) { //octave, freq, key on +			portnum &= 15; +			if (!adlibch[portnum].keyon && ( (adlibregmem[0xB0+portnum]>>5) &1) ) { +					adlibdidattack[portnum] = 0; +					adlibenv[portnum] = 0.0025; +				} +			adlibch[portnum].freq = adlibregmem[0xA0+portnum] | ( (adlibregmem[0xB0+portnum]&3) <<8); +			adlibch[portnum].convfreq = ( (double) adlibch[portnum].freq * 0.7626459); +			adlibch[portnum].keyon = (adlibregmem[0xB0+portnum]>>5) &1; +			adlibch[portnum].octave = (adlibregmem[0xB0+portnum]>>2) &7; +		} +	else if ( (portnum >= 0xE0) && (portnum <= 0xF5) ) { //waveform select +			portnum &= 15; +			if (portnum<9) adlibch[portnum].wavesel = value&3; +		} +} + +uint8_t inadlib (uint16_t portnum) { +	if (!adlibregmem[4]) adlibstatus = 0; +	else adlibstatus = 0x80; +	adlibstatus = adlibstatus + (adlibregmem[4]&1) *0x40 + (adlibregmem[4]&2) *0x10; +	return (adlibstatus); +} + +uint16_t adlibfreq (uint8_t chan) { +	uint16_t tmpfreq; +	if (!adlibch[chan].keyon) return (0); +	tmpfreq = (uint16_t) adlibch[chan].convfreq; +	switch (adlibch[chan].octave) { +			case 0: +				tmpfreq = tmpfreq >> 4; +				break; +			case 1: +				tmpfreq = tmpfreq >> 3; +				break; +			case 2: +				tmpfreq = tmpfreq >> 2; +				break; +			case 3: +				tmpfreq = tmpfreq >> 1; +				break; +			case 5: +				tmpfreq = tmpfreq << 1; +				break; +			case 6: +				tmpfreq = tmpfreq << 2; +				break; +			case 7: +				tmpfreq = tmpfreq << 3; +		} + +	return (tmpfreq); +} + +uint64_t fullstep, adlibstep[9]; +double adlibenv[9], adlibdecay[9], adlibattack[9]; +uint8_t adlibdidattack[9]; + +extern SDL_AudioSpec wanted; +int32_t adlibsample (uint8_t curchan) { +	int32_t tempsample; +	double tempstep; + +	if (adlibpercussion && (curchan>=6) && (curchan<=8) ) return (0); + +	fullstep = usesamplerate/adlibfreq (curchan); + +	tempsample = (int32_t) oplwave[adlibch[curchan].wavesel][ (uint8_t) ( (double) adlibstep[curchan]/ ( (double) fullstep/ (double) 256) ) ]; +	tempstep = adlibenv[curchan]; +	if (tempstep>1.0) tempstep = 1; +	tempsample = (int32_t) ( (double) tempsample * tempstep * 2.0); + +	adlibstep[curchan]++; +	if (adlibstep[curchan]>fullstep) adlibstep[curchan] = 0; +	return (tempsample); +} + +int16_t adlibgensample() { +	uint8_t curchan; +	int16_t adlibaccum; +	adlibaccum = 0; +	for (curchan=0; curchan<9; curchan++) { +			if (adlibfreq (curchan) !=0) { +					adlibaccum += (int16_t) adlibsample (curchan); +				} +		} +	return (adlibaccum); +} + +void tickadlib() { +	uint8_t curchan; +	for (curchan=0; curchan<9; curchan++) { +			if (adlibfreq (curchan) !=0) { +					if (adlibdidattack[curchan]) { +							adlibenv[curchan] *= adlibdecay[curchan]; +						} +					else { +							adlibenv[curchan] *= adlibattack[curchan]; +							if (adlibenv[curchan]>=1.0) adlibdidattack[curchan] = 1; +						} +				} +		} +} + +void initadlib (uint16_t baseport) { +	set_port_write_redirector (baseport, baseport + 1, &outadlib); +	set_port_read_redirector (baseport, baseport + 1, &inadlib); +} diff --git a/src/fake86/ata.c b/src/fake86/ata.c new file mode 100755 index 0000000..df3b50a --- /dev/null +++ b/src/fake86/ata.c @@ -0,0 +1,123 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +#include "config.h" +#ifdef DISK_CONTROLLER_ATA +#include <stdint.h> +#include <stdio.h> +#include <memory.h> +#include <string.h> + +uint8_t idATA[512]; + +#define ATA_STATUS_ERROR 0x01 +#define ATA_STATUS_DRQ   0x08 +#define ATA_STATUS_SRV   0x10 +#define ATA_STATUS_FAULT 0x20 +#define ATA_STATUS_READY 0x40 +#define ATA_STATUS_BUSY  0x80 + +#define flip16(x) ((x&255) | (x>>8)) +#define flip32(x) ((x>>24) | (((x>>16)&255)<<8) | (((x>>8)&255)<<16) | ((x&255)<<24)) + +uint8_t statusreg = 0, errorreg = 0, drivesel = 0, databuf[512]; +uint16_t dataptr = 512; + +void bufclear() { +	memset (databuf, 0, 512); +} + +void bufcook() { +	uint16_t i; +	uint8_t cc; +	for (i=0; i<512; i+=2) { +			cc = databuf[i]; +			databuf[i] = databuf[i+1]; +			databuf[i+1] = cc; +		} +} + +void bufwrite8 (uint16_t bufpos, uint8_t value) { +	databuf[bufpos] = value; +} + +void bufwrite16 (uint16_t bufpos, uint16_t value) { +	databuf[bufpos] = (uint8_t) value; +	databuf[bufpos+1] = (uint8_t) (value>>8); +} + +void flipstring (uint8_t *dest, uint8_t *src) { +	uint16_t i; +	uint8_t cc; +	strcpy (dest, src); +	for (i=0; i<strlen (dest); i+=2) { +			cc = databuf[i]; +			databuf[i] = databuf[i+1]; +			databuf[i+1] = cc; +		} +} + +void cmdATA (uint8_t value) { +	switch (value) { +			case 0x91: //INITIALIZE DEVICE PARAMETERS +				dataptr = 512; +				break; +			case 0xEC: //IDENTIFY +				memset (&idATA, 0, sizeof (idATA) ); +				memcpy (databuf, &idATA, 512); +				dataptr = 0; +				break; +		} +} + +void outATA (uint16_t portnum, uint8_t value) { +	//printf("[DEBUG] ATA port %Xh write: %02X\n", portnum, value); +	//getch(); +	switch (portnum) { +			case 0x1F6: +				drivesel = (value >> 4) & 1; +				break; +			case 0x1F7: //command register +				cmdATA (value); +				break; +		} +} + +uint8_t inATA (uint16_t portnum) { +	//if (portnum != 0x1F0) printf("[DEBUG] ATA port %Xh read\n", portnum); +	//getch(); +	switch (portnum) { +			case 0x1F0: //data read +				if (dataptr < 512) { +						//printf("%c", databuf[dataptr]); +						return (databuf[dataptr++]); +					} +				else return (0); +			case 0x1F1: //error register +				if (drivesel == 1) return (1); +				else return (0); +			case 0x1F7: //status register +				statusreg = ATA_STATUS_READY; +				if (drivesel == 1) statusreg |= ATA_STATUS_ERROR; +				if (dataptr < 512) statusreg |= ATA_STATUS_DRQ; +				return (statusreg); +		} +	return (0); +} +#endif diff --git a/src/fake86/audio.c b/src/fake86/audio.c new file mode 100755 index 0000000..fb8c76b --- /dev/null +++ b/src/fake86/audio.c @@ -0,0 +1,152 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* audio.c: functions to mix the audio channels, and handle SDL's audio interface. */ + +#include "config.h" +#include <SDL/SDL.h> +#ifdef _WIN32 +#include <Windows.h> +#include <process.h> +#else +#include <pthread.h> +#endif +#include <stdint.h> +#include <stdio.h> +#include <memory.h> +#include "blaster.h" +#include "audio.h" + +extern SDL_Surface *screen; +struct wav_hdr_s wav_hdr; +FILE *wav_file = NULL; + +SDL_AudioSpec wanted; +int8_t audbuf[96000]; +int32_t audbufptr, usebuffersize, usesamplerate = AUDIO_DEFAULT_SAMPLE_RATE, latency = AUDIO_DEFAULT_LATENCY; +uint8_t speakerenabled = 0; + +extern uint64_t gensamplerate, sampleticks, hostfreq; +extern int16_t adlibgensample(); +extern int16_t speakergensample(); +extern int16_t getssourcebyte(); +extern int16_t getBlasterSample(); +extern uint8_t usessource; + +void create_output_wav (uint8_t *filename) { +	printf ("Creating %s for audio logging... ", filename); +	wav_file = fopen (filename, "wb"); +	if (wav_file == NULL) { +			printf ("failed!\n"); +			return; +		} +	printf ("OK!\n"); + +	wav_hdr.AudioFormat = 1; //PCM +	wav_hdr.bitsPerSample = 8; +	wav_hdr.blockAlign = 1; +	wav_hdr.ChunkSize = sizeof (wav_hdr) - 4; +	sprintf (&wav_hdr.WAVE[0], "WAVE"); +	sprintf (&wav_hdr.fmt[0], "fmt "); +	wav_hdr.NumOfChan = 1; +	wav_hdr.bytesPerSec = usesamplerate * (uint32_t) (wav_hdr.bitsPerSample >> 3) * (uint32_t) wav_hdr.NumOfChan; +	sprintf (&wav_hdr.RIFF[0], "RIFF"); +	wav_hdr.Subchunk1Size = 16; +	wav_hdr.SamplesPerSec = usesamplerate; +	sprintf (&wav_hdr.Subchunk2ID[0], "data"); +	wav_hdr.Subchunk2Size = 0; +	//fwrite((void *)&wav_hdr, 1, sizeof(wav_hdr), wav_file); +} + +uint64_t doublesamplecount, cursampnum = 0, sampcount = 0, framecount = 0; +uint8_t bmpfilename[256]; + +void savepic() { +	SDL_SaveBMP (screen, &bmpfilename[0]); +} + +int8_t samps[2400]; + +uint8_t audiobufferfilled() { +	if (audbufptr >= usebuffersize) return(1); +	return(0); +} + +void tickaudio() { +	int16_t sample; +	if (audbufptr >= usebuffersize) return; +	sample = adlibgensample() >> 4; +	if (usessource) sample += getssourcebyte(); +	sample += getBlasterSample(); +	if (speakerenabled) sample += (speakergensample() >> 1); +	if (audbufptr < sizeof(audbuf) ) audbuf[audbufptr++] = (uint8_t) ((uint16_t) sample+128); +} + +extern uint64_t timinginterval; +extern void inittiming(); +void fill_audio (void *udata, int8_t *stream, int len) { +	memcpy (stream, audbuf, len); +	memmove (audbuf, &audbuf[len], usebuffersize - len); + +	audbufptr -= len; +	if (audbufptr < 0) audbufptr = 0; +} + +void initaudio() { +	printf ("Initializing audio stream... "); + +	if (usesamplerate < 4000) usesamplerate = 4000; +	else if (usesamplerate > 96000) usesamplerate = 96000; +	if (latency < 10) latency = 10; +	else if (latency > 1000) latency = 1000; +	audbufptr = usebuffersize = (usesamplerate / 1000) * latency; +	gensamplerate = usesamplerate; +	doublesamplecount = (uint32_t) ( (double) usesamplerate * (double) 0.01); + +	wanted.freq = usesamplerate; +	wanted.format = AUDIO_U8; +	wanted.channels = 1; +	wanted.samples = (uint16_t) usebuffersize >> 1; +	wanted.callback = (void *) fill_audio; +	wanted.userdata = NULL; + +	if (SDL_OpenAudio (&wanted, NULL) <0) { +			printf ("Error: %s\n", SDL_GetError() ); +			return; +		} +	else { +			printf ("OK! (%lu Hz, %lu ms, %lu sample latency)\n", usesamplerate, latency, usebuffersize); +		} + +	memset (audbuf, 128, sizeof (audbuf) ); +	audbufptr = usebuffersize; +	//create_output_wav("fake86.wav"); +	SDL_PauseAudio (0); +	return; +} + +void killaudio() { +	SDL_PauseAudio (1); + +	if (wav_file == NULL) return; +	wav_hdr.ChunkSize = wav_hdr.Subchunk2Size + sizeof(wav_hdr) - 8; +	fseek(wav_file, 0, SEEK_SET); +	fwrite((void *)&wav_hdr, 1, sizeof(wav_hdr), wav_file); +	fclose (wav_file); +} diff --git a/src/fake86/audio.h b/src/fake86/audio.h new file mode 100755 index 0000000..112d00c --- /dev/null +++ b/src/fake86/audio.h @@ -0,0 +1,36 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2012 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +#include <stdint.h> + +struct wav_hdr_s { +	uint8_t	RIFF[4];	/* RIFF Header	  */ //Magic header +	uint32_t ChunkSize;	  /* RIFF Chunk Size  */ +	uint8_t	WAVE[4];	/* WAVE Header	  */ +	uint8_t	fmt[4];	 /* FMT header	   */ +	uint32_t Subchunk1Size;  /* Size of the fmt chunk				*/ +	uint16_t AudioFormat;	/* Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM */ +	uint16_t NumOfChan;	  /* Number of channels 1=Mono 2=Sterio		   */ +	uint32_t SamplesPerSec;  /* Sampling Frequency in Hz				 */ +	uint32_t bytesPerSec;	/* bytes per second */ +	uint16_t blockAlign;	 /* 2=16-bit mono, 4=16-bit stereo */ +	uint16_t bitsPerSample;  /* Number of bits per sample	  */ +	uint8_t	Subchunk2ID[4]; /* "data"  string   */ +	uint32_t Subchunk2Size;  /* Sampled data length	*/ +}; diff --git a/src/fake86/blaster.c b/src/fake86/blaster.c new file mode 100755 index 0000000..33c95c6 --- /dev/null +++ b/src/fake86/blaster.c @@ -0,0 +1,316 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* blaster.c: functions to emulate a Creative Labs Sound Blaster Pro. */ + +#include "config.h" +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include "blaster.h" + +extern void set_port_write_redirector (uint16_t startport, uint16_t endport, void *callback); +extern void set_port_read_redirector (uint16_t startport, uint16_t endport, void *callback); +extern void doirq (uint8_t irqnum); +extern uint8_t read8237 (uint8_t channel); + +extern void outadlib (uint16_t portnum, uint8_t value); //on the Sound Blaster Pro, ports (base+0) and (base+1) are for +extern uint8_t inadlib (uint16_t portnum); //the OPL FM music chips, and are also mirrored at (base+8) (base+9) +//as well as 0x388 and 0x389 to remain compatible with the older adlib cards + +struct blaster_s blaster; + +void bufNewData (uint8_t value) { +	if (blaster.memptr >= sizeof (blaster.mem) ) return; +	blaster.mem[blaster.memptr] = value; +	blaster.memptr++; +} + +extern uint64_t hostfreq; +void setsampleticks() { +	if (blaster.samplerate == 0) { +			blaster.sampleticks = 0; +			return; +		} +	blaster.sampleticks = hostfreq / (uint64_t) blaster.samplerate; +} + +void cmdBlaster (uint8_t value) { +	uint8_t recognized = 1; +	if (blaster.waitforarg) { +			switch (blaster.lastcmdval) { +					case 0x10: //direct 8-bit sample output +						blaster.sample = value; +						break; +					case 0x14: //8-bit single block DMA output +					case 0x24: +					case 0x91: +						if (blaster.waitforarg == 2) { +								blaster.blocksize = (blaster.blocksize & 0xFF00) | (uint32_t) value; +								blaster.waitforarg = 3; +								return; +							} +						else { +								blaster.blocksize = (blaster.blocksize & 0x00FF) | ( (uint32_t) value << 8); +#ifdef DEBUG_BLASTER +								printf ("[NOTICE] Sound Blaster DSP block transfer size set to %u\n", blaster.blocksize); +#endif +								blaster.usingdma = 1; +								blaster.blockstep = 0; +								blaster.useautoinit = 0; +								blaster.paused8 = 0; +								blaster.speakerstate = 1; +							} +						break; +					case 0x40: //set time constant +						blaster.samplerate = (uint16_t) ( (uint32_t) 1000000 / (uint32_t) (256 - (uint32_t) value) ); +						setsampleticks(); +#ifdef DEBUG_BLASTER +						printf ("[DEBUG] Sound Blaster time constant received, sample rate = %u\n", blaster.samplerate); +#endif +						break; +					case 0x48: //set DSP block transfer size +						if (blaster.waitforarg == 2) { +								blaster.blocksize = (blaster.blocksize & 0xFF00) | (uint32_t) value; +								blaster.waitforarg = 3; +								return; +							} +						else { +								blaster.blocksize = (blaster.blocksize & 0x00FF) | ( (uint32_t) value << 8); +								//if (blaster.blocksize == 0) blaster.blocksize = 65536; +								blaster.blockstep = 0; +#ifdef DEBUG_BLASTER +								printf ("[NOTICE] Sound Blaster DSP block transfer size set to %u\n", blaster.blocksize); +#endif +							} +						break; +					case 0xE0: //DSP identification for Sound Blaster 2.0 and newer (invert each bit and put in read buffer) +						bufNewData (~value); +						break; +					case 0xE4: //DSP write test, put data value into read buffer +						bufNewData (value); +						blaster.lasttestval = value; +						break; +					default: +						recognized = 0; +				} +			//blaster.waitforarg--; // = 0; +			if (recognized) return; +		} + +	switch (value) { +			case 0x10: +			case 0x40: +			case 0xE0: +			case 0xE4: +				blaster.waitforarg = 1; +				break; + +			case 0x14: //8-bit single block DMA output +			case 0x24: +			case 0x48: +			case 0x91: +				blaster.waitforarg = 2; +				break; + +			case 0x1C: //8-bit auto-init DMA output +			case 0x2C: +				blaster.usingdma = 1; +				blaster.blockstep = 0; +				blaster.useautoinit = 1; +				blaster.paused8 = 0; +				blaster.speakerstate = 1; +				break; + +			case 0xD0: //pause 8-bit DMA I/O +				blaster.paused8 = 1; +			case 0xD1: //speaker output on +				blaster.speakerstate = 1; +				break; +			case 0xD3: //speaker output off +				blaster.speakerstate = 0; +				break; +			case 0xD4: //continue 8-bit DMA I/O +				blaster.paused8 = 0; +				break; +			case 0xD8: //get speaker status +				if (blaster.speakerstate) bufNewData (0xFF); +				else bufNewData (0x00); +				break; +			case 0xDA: //exit 8-bit auto-init DMA I/O mode +				blaster.usingdma = 0; +				break; +			case 0xE1: //get DSP version info +				blaster.memptr = 0; +				bufNewData (blaster.dspmaj); +				bufNewData (blaster.dspmin); +				break; +			case 0xE8: //DSP read test +				blaster.memptr = 0; +				bufNewData (blaster.lasttestval); +				break; +			case 0xF2: //force 8-bit IRQ +				doirq (blaster.sbirq); +				break; +			case 0xF8: //undocumented command, clears in-buffer and inserts a null byte +				blaster.memptr = 0; +				bufNewData (0); +				break; +			default: +				printf ("[NOTICE] Sound Blaster received unhandled command %02Xh\n", value); +				break; +		} +} + +uint8_t mixer[256], mixerindex = 0; +void outBlaster (uint16_t portnum, uint8_t value) { +#ifdef DEBUG_BLASTER +	printf ("[DEBUG] outBlaster: port %Xh, value %02X\n", portnum, value); +#endif +	portnum &= 0xF; +	switch (portnum) { +			case 0x0: +			case 0x8: +				outadlib (0x388, value); +				break; +			case 0x1: +			case 0x9: +				outadlib (0x389, value); +				break; +			case 0x4: //mixer address port +				mixerindex = value; +				break; +			case 0x5: //mixer data +				mixer[mixerindex] = value; +				break; +			case 0x6: //reset port +				if ( (value == 0x00) && (blaster.lastresetval == 0x01) ) { +						blaster.speakerstate = 0; +						blaster.sample = 128; +						blaster.waitforarg = 0; +						blaster.memptr = 0; +						blaster.usingdma = 0; +						blaster.blocksize = 65535; +						blaster.blockstep = 0; +						bufNewData (0xAA); +						memset (mixer, 0xEE, sizeof (mixer) ); +#ifdef DEBUG_BLASTER +						printf ("[DEBUG] Sound Blaster received reset!\n"); +#endif +					} +				blaster.lastresetval = value; +				break; +			case 0xC: //write command/data +				cmdBlaster (value); +				if (blaster.waitforarg != 3) blaster.lastcmdval = value; +				break; +		} +} + +uint8_t inBlaster (uint16_t portnum) { +	uint8_t ret = 0; +#ifdef DEBUG_BLASTER +	static uint16_t lastread = 0; +#endif +#ifdef DEBUG_BLASTER +	//if (lastread != portnum) printf ("[DEBUG] inBlaster: port %Xh, value ", portnum); +#endif +	portnum &= 0xF; +	switch (portnum) { +			case 0x0: +			case 0x8: +				ret = inadlib (0x388); +				break; +			case 0x1: +			case 0x9: +				ret = inadlib (0x389); +				break; +			case 0x5: //mixer data +				ret = mixer[mixerindex]; +				break; +			case 0xA: //read data +				if (blaster.memptr == 0) { +						ret = 0; +					} +				else { +						ret = blaster.mem[0]; +						memmove (&blaster.mem[0], &blaster.mem[1], sizeof (blaster.mem) - 1); +						blaster.memptr--; +					} +				break; +			case 0xE: //read-buffer status +				if (blaster.memptr > 0) ret = 0x80; +				else ret = 0x00; +				break; +			default: +				ret = 0x00; +		} +#ifdef DEBUG_BLASTER +	//if (lastread != portnum) printf ("%02X\n", ret); +	//lastread = portnum; +#endif +	return (ret); +} + +//FILE *sbout = NULL; +void tickBlaster() { +	if (!blaster.usingdma) return; +	/*if (blaster.paused8) { +	blaster.sample = 128; +	return; +	}*/ +	//printf("tickBlaster();\n"); +	blaster.sample = read8237 (blaster.sbdma); +	//if (sbout != NULL) fwrite(&blaster.sample, 1, 1, sbout); +	blaster.blockstep++; +	if (blaster.blockstep > blaster.blocksize) { +			doirq (blaster.sbirq); +#ifdef DEBUG_BLASTER +			printf ("[NOTICE] Sound Blaster did IRQ\n"); +#endif +			if (blaster.useautoinit) { +					blaster.blockstep = 0; +				} +			else { +					blaster.usingdma = 0; +				} +		} +} + +int16_t getBlasterSample() { +	if (blaster.speakerstate == 0) return (0); +	else return ( (int16_t) blaster.sample - 128); +} + +void mixerReset() { +	memset (blaster.mixer.reg, 0, sizeof (blaster.mixer.reg) ); +	blaster.mixer.reg[0x22] = blaster.mixer.reg[0x26] = blaster.mixer.reg[0x04] = (4 << 5) | (4 << 1); +} + +void initBlaster (uint16_t baseport, uint8_t irq) { +	//sbout = fopen("sbout.raw", "wb"); +	memset (&blaster, 0, sizeof (blaster) ); +	blaster.dspmaj = 2; //emulate a Sound Blaster 2.0 +	blaster.dspmin = 0; +	blaster.sbirq = irq; +	blaster.sbdma = 1; +	mixerReset(); +	set_port_write_redirector (baseport, baseport + 0xE, &outBlaster); +	set_port_read_redirector (baseport, baseport + 0xE, &inBlaster); +} diff --git a/src/fake86/blaster.h b/src/fake86/blaster.h new file mode 100755 index 0000000..647dc1d --- /dev/null +++ b/src/fake86/blaster.h @@ -0,0 +1,48 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2012 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +#include <stdint.h> + +struct blaster_s { +	uint8_t mem[1024]; +	uint16_t memptr; +	uint16_t samplerate; +	uint8_t dspmaj; +	uint8_t dspmin; +	uint8_t speakerstate; +	uint8_t lastresetval; +	uint8_t lastcmdval; +	uint8_t lasttestval; +	uint8_t waitforarg; +	uint8_t paused8; +	uint8_t paused16; +	uint8_t sample; +	uint8_t sbirq; +	uint8_t sbdma; +	uint8_t usingdma; +	uint8_t maskdma; +	uint8_t useautoinit; +	uint32_t blocksize; +	uint32_t blockstep; +	uint64_t sampleticks; +	struct mixer_s { +		uint8_t index; +		uint8_t reg[256]; +	} mixer; +}; diff --git a/src/fake86/config.h b/src/fake86/config.h new file mode 100755 index 0000000..359c1c0 --- /dev/null +++ b/src/fake86/config.h @@ -0,0 +1,60 @@ +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#define BUILD_STRING "Fake86 v0.13.9.16" + +//be sure to only define ONE of the CPU_* options at any given time, or +//you will likely get some unexpected/bad results! +//#define CPU_8086 +//#define CPU_186 +#define CPU_V20 +//#define CPU_286 + +#if defined(CPU_8086) +	#define CPU_CLEAR_ZF_ON_MUL +	#define CPU_ALLOW_POP_CS +#else +	#define CPU_ALLOW_ILLEGAL_OP_EXCEPTION +	#define CPU_LIMIT_SHIFT_COUNT +#endif + +#if defined(CPU_V20) +	#define CPU_NO_SALC +#endif + +#if defined(CPU_286) || defined(CPU_386) +	#define CPU_286_STYLE_PUSH_SP +#else +	#define CPU_SET_HIGH_FLAGS +#endif + +#define TIMING_INTERVAL 15 + +//when USE_PREFETCH_QUEUE is defined, Fake86's CPU emulator uses a 6-byte +//read-ahead cache for opcode fetches just as a real 8086/8088 does. +//by default, i just leave this disabled because it wastes a very very +//small amount of CPU power. however, for the sake of more accurate +//emulation, it can be enabled by uncommenting the line below and recompiling. +//#define USE_PREFETCH_QUEUE + +//#define CPU_ADDR_MODE_CACHE + +//when compiled with network support, fake86 needs libpcap/winpcap. +//if it is disabled, the ethernet card is still emulated, but no actual +//communication is possible -- as if the ethernet cable was unplugged. +#define NETWORKING_OLDCARD //planning to support an NE2000 in the future + +//when DISK_CONTROLLER_ATA is defined, fake86 will emulate a true IDE/ATA1 controller +//card. if it is disabled, emulated disk access is handled by directly intercepting +//calls to interrupt 13h. +//*WARNING* - the ATA controller is not currently complete. do not use! +//#define DISK_CONTROLLER_ATA + +#define AUDIO_DEFAULT_SAMPLE_RATE 48000 +#define AUDIO_DEFAULT_LATENCY 100 + +//#define DEBUG_BLASTER +//#define DEBUG_DMA + +//#define BENCHMARK_BIOS +#endif diff --git a/src/fake86/console.c b/src/fake86/console.c new file mode 100755 index 0000000..03edebb --- /dev/null +++ b/src/fake86/console.c @@ -0,0 +1,133 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* console.c: functions for a simple interactive console on stdio. */ + +#include "config.h" +#include <SDL/SDL.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#ifdef _WIN32 +#include <conio.h> +#define strcmpi _strcmpi +#else +#define strcmpi strcasecmp +#endif + +uint8_t inputline[1024]; +uint16_t inputptr = 0; +extern uint8_t running; + +extern uint8_t insertdisk (uint8_t drivenum, char *filename); +extern void ejectdisk (uint8_t drivenum); + +void waitforcmd (uint8_t *dst, uint16_t maxlen) { +#ifdef _WIN32 +	uint16_t inputptr; +	uint8_t cc; + +	inputptr = 0; +	maxlen -= 2; +	inputline[0] = 0; +	while (running) { +			if (_kbhit () ) { +					cc = (uint8_t) _getch (); +					switch (cc) { +							case 0: +							case 9: +							case 10: +								break; +							case 8: //backspace +								if (inputptr > 0) { +										printf ("%c %c", 8, 8); +										inputline[--inputptr] = 0; +									} +								break; +							case 13: //enter +								printf ("\n"); +								return; +							default: +								if (inputptr < maxlen) { +										inputline[inputptr++] = cc; +										inputline[inputptr] = 0; +										printf ("%c",cc); +									} +						} +				} +			SDL_Delay(10); //don't waste CPU time while in the polling loop +		} +#else +	gets (dst); +#endif +} + +void consolehelp () { +	printf ("\nConsole command summary:\n"); +	printf ("  The console is not very robust yet. There are only a few commands:\n\n"); +	printf ("    change fd0        Mount a new image file on first floppy drive.\n"); +	printf ("                      Entering a blank line just ejects any current image file.\n"); +	printf ("    change fd1        Mount a new image file on first floppy drive.\n"); +	printf ("                      Entering a blank line just ejects any current image file.\n"); +	printf ("    help              This help display.\n"); +	printf ("    quit              Immediately abort emulation and close Fake86.\n"); +} + +#ifdef _WIN32 +void runconsole (void *dummy) { +#else +void *runconsole (void *dummy) { +#endif +	printf ("\nFake86 management console\n"); +	printf ("Type \"help\" for a summary of commands.\n"); +	while (running) { +			printf ("\n>"); +			waitforcmd (inputline, sizeof(inputline) ); +			if (strcmpi ( (const char *) inputline, "change fd0") == 0) { +					printf ("Path to new image file: "); +					waitforcmd (inputline, sizeof(inputline) ); +					if (strlen (inputline) > 0) { +							insertdisk (0, (char *) inputline); +						} +					else { +							ejectdisk (0); +							printf ("Floppy image ejected from first drive.\n"); +						} +				} +			else if (strcmpi ( (const char *) inputline, "change fd1") == 0) { +					printf ("Path to new image file: "); +					waitforcmd (inputline, sizeof(inputline) ); +					if (strlen (inputline) > 0) { +							insertdisk (1, (char *) inputline); +						} +					else { +							ejectdisk (1); +							printf ("Floppy image ejected from second drive.\n"); +						} +				} +			else if (strcmpi ( (const char *) inputline, "help") == 0) { +					consolehelp (); +				} +			else if (strcmpi ( (const char *) inputline, "quit") == 0) { +					running = 0; +				} +			else printf("Invalid command was entered.\n"); +		} +} diff --git a/src/fake86/cpu.c b/src/fake86/cpu.c new file mode 100755 index 0000000..828b4d7 --- /dev/null +++ b/src/fake86/cpu.c @@ -0,0 +1,3532 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* cpu.c: functions to emulate the 8086/V20 CPU in software. the heart of Fake86. */ + +#include "config.h" +#ifndef CPU_INSTRUCTION_FLOW_CACHE + +#include <stdint.h> +#include <stdio.h> +#include "cpu.h" +#include "i8259.h" +#include "i8253.h" +#include "modregrm.h" + +extern struct i8253_s i8253; + +extern struct structpic i8259; +uint64_t curtimer, lasttimer, timerfreq; + +uint8_t byteregtable[8] = { regal, regcl, regdl, regbl, regah, regch, regdh, regbh }; + +static const uint8_t parity[0x100] = { +	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, +	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, +	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, +	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, +	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, +	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, +	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, +	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 +}; + +uint8_t	RAM[0x100000], readonly[0x100000]; +uint8_t	opcode, segoverride, reptype, bootdrive = 0, hdcount = 0, hltstate = 0; +uint16_t segregs[4], savecs, saveip, ip, useseg, oldsp; +uint8_t	tempcf, oldcf, cf, pf, af, zf, sf, tf, ifl, df, of, mode, reg, rm; +uint16_t oper1, oper2, res16, disp16, temp16, dummy, stacksize, frametemp; +uint8_t	oper1b, oper2b, res8, disp8, temp8, nestlev, addrbyte; +uint32_t temp1, temp2, temp3, temp4, temp5, temp32, tempaddr32, ea; +int32_t	result; +uint64_t totalexec; + +extern uint16_t	VGA_SC[0x100], VGA_CRTC[0x100], VGA_ATTR[0x100], VGA_GC[0x100]; +extern uint8_t updatedscreen; +union _bytewordregs_ regs; + +uint8_t	portram[0x10000]; +uint8_t	running = 0, debugmode, showcsip, verbose, mouseemu, didbootstrap = 0; +uint8_t	ethif; + +extern uint8_t	vidmode; +extern uint8_t verbose; + +extern void vidinterrupt(); + +extern uint8_t readVGA (uint32_t addr32); + +void intcall86 (uint8_t intnum); + +#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; \ +	} + +extern void	writeVGA (uint32_t addr32, uint8_t value); +extern void	portout (uint16_t portnum, uint8_t value); +extern void	portout16 (uint16_t portnum, uint16_t value); +extern uint8_t	portin (uint16_t portnum); +extern uint16_t portin16 (uint16_t portnum); + +void write86 (uint32_t addr32, uint8_t value) { +	tempaddr32 = addr32 & 0xFFFFF; +#ifdef CPU_ADDR_MODE_CACHE +	if (!readonly[tempaddr32]) addrcachevalid[tempaddr32] = 0; +#endif +	if (readonly[tempaddr32] || (tempaddr32 >= 0xC0000) ) { +			return; +		} + +	if ( (tempaddr32 >= 0xA0000) && (tempaddr32 <= 0xBFFFF) ) { +			if ( (vidmode != 0x13) && (vidmode != 0x12) && (vidmode != 0xD) && (vidmode != 0x10) ) { +					RAM[tempaddr32] = value; +					updatedscreen = 1; +				} +			else if ( ( (VGA_SC[4] & 6) == 0) && (vidmode != 0xD) && (vidmode != 0x10) && (vidmode != 0x12) ) { +					RAM[tempaddr32] = value; +					updatedscreen = 1; +				} +			else { +					writeVGA (tempaddr32 - 0xA0000, value); +				} + +			updatedscreen = 1; +		} +	else { +			RAM[tempaddr32] = value; +		} +} + +void writew86 (uint32_t addr32, uint16_t value) { +	write86 (addr32, (uint8_t) value); +	write86 (addr32 + 1, (uint8_t) (value >> 8) ); +} + +uint8_t read86 (uint32_t addr32) { +	addr32 &= 0xFFFFF; +	if ( (addr32 >= 0xA0000) && (addr32 <= 0xBFFFF) ) { +			if ( (vidmode == 0xD) || (vidmode == 0xE) || (vidmode == 0x10) || (vidmode == 0x12) ) return (readVGA (addr32 - 0xA0000) ); +			if ( (vidmode != 0x13) && (vidmode != 0x12) && (vidmode != 0xD) ) return (RAM[addr32]); +			if ( (VGA_SC[4] & 6) == 0) +				return (RAM[addr32]); +			else +				return (readVGA (addr32 - 0xA0000) ); +		} + +	if (!didbootstrap) { +			RAM[0x410] = 0x41; //ugly hack to make BIOS always believe we have an EGA/VGA card installed +			RAM[0x475] = hdcount; //the BIOS doesn't have any concept of hard drives, so here's another hack +		} + +	return (RAM[addr32]); +} + +uint16_t readw86 (uint32_t addr32) { +	return ( (uint16_t) read86 (addr32) | (uint16_t) (read86 (addr32 + 1) << 8) ); +} + +void flag_szp8 (uint8_t value) { +	if (!value) { +			zf = 1; +		} +	else { +			zf = 0;	/* set or clear zero flag */ +		} + +	if (value & 0x80) { +			sf = 1; +		} +	else { +			sf = 0;	/* set or clear sign flag */ +		} + +	pf = parity[value]; /* retrieve parity state from lookup table */ +} + +void flag_szp16 (uint16_t value) { +	if (!value) { +			zf = 1; +		} +	else { +			zf = 0;	/* set or clear zero flag */ +		} + +	if (value & 0x8000) { +			sf = 1; +		} +	else { +			sf = 0;	/* set or clear sign flag */ +		} + +	pf = parity[value & 255];	/* retrieve parity state from lookup table */ +} + +void flag_log8 (uint8_t value) { +	flag_szp8 (value); +	cf = 0; +	of = 0; /* bitwise logic ops always clear carry and overflow */ +} + +void flag_log16 (uint16_t value) { +	flag_szp16 (value); +	cf = 0; +	of = 0; /* bitwise logic ops always clear carry and overflow */ +} + +void flag_adc8 (uint8_t v1, uint8_t v2, uint8_t v3) { + +	/* v1 = destination 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); +	if ( ( (dst ^ v1) & (dst ^ v2) & 0x80) == 0x80) { +			of = 1; +		} +	else { +			of = 0; /* set or clear overflow flag */ +		} + +	if (dst & 0xFF00) { +			cf = 1; +		} +	else { +			cf = 0; /* set or clear carry flag */ +		} + +	if ( ( (v1 ^ v2 ^ dst) & 0x10) == 0x10) { +			af = 1; +		} +	else { +			af = 0; /* set or clear auxilliary 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) ) & 0x8000) == 0x8000) { +			of = 1; +		} +	else { +			of = 0; +		} + +	if (dst & 0xFFFF0000) { +			cf = 1; +		} +	else { +			cf = 0; +		} + +	if ( ( (v1 ^ v2 ^ dst) & 0x10) == 0x10) { +			af = 1; +		} +	else { +			af = 0; +		} +} + +void flag_add8 (uint8_t v1, uint8_t v2) { +	/* v1 = destination operand, v2 = source operand */ +	uint16_t	dst; + +	dst = (uint16_t) v1 + (uint16_t) v2; +	flag_szp8 ( (uint8_t) dst); +	if (dst & 0xFF00) { +			cf = 1; +		} +	else { +			cf = 0; +		} + +	if ( ( (dst ^ v1) & (dst ^ v2) & 0x80) == 0x80) { +			of = 1; +		} +	else { +			of = 0; +		} + +	if ( ( (v1 ^ v2 ^ dst) & 0x10) == 0x10) { +			af = 1; +		} +	else { +			af = 0; +		} +} + +void flag_add16 (uint16_t v1, uint16_t v2) { +	/* v1 = destination operand, v2 = source operand */ +	uint32_t	dst; + +	dst = (uint32_t) v1 + (uint32_t) v2; +	flag_szp16 ( (uint16_t) dst); +	if (dst & 0xFFFF0000) { +			cf = 1; +		} +	else { +			cf = 0; +		} + +	if ( ( (dst ^ v1) & (dst ^ v2) & 0x8000) == 0x8000) { +			of = 1; +		} +	else { +			of = 0; +		} + +	if ( ( (v1 ^ v2 ^ dst) & 0x10) == 0x10) { +			af = 1; +		} +	else { +			af = 0; +		} +} + +void flag_sbb8 (uint8_t v1, uint8_t v2, uint8_t v3) { + +	/* v1 = destination operand, v2 = source operand, v3 = carry flag */ +	uint16_t	dst; + +	v2 += v3; +	dst = (uint16_t) v1 - (uint16_t) v2; +	flag_szp8 ( (uint8_t) dst); +	if (dst & 0xFF00) { +			cf = 1; +		} +	else { +			cf = 0; +		} + +	if ( (dst ^ v1) & (v1 ^ v2) & 0x80) { +			of = 1; +		} +	else { +			of = 0; +		} + +	if ( (v1 ^ v2 ^ dst) & 0x10) { +			af = 1; +		} +	else { +			af = 0; +		} +} + +void flag_sbb16 (uint16_t v1, uint16_t v2, uint16_t v3) { + +	/* v1 = destination operand, v2 = source operand, v3 = carry flag */ +	uint32_t	dst; + +	v2 += v3; +	dst = (uint32_t) v1 - (uint32_t) v2; +	flag_szp16 ( (uint16_t) dst); +	if (dst & 0xFFFF0000) { +			cf = 1; +		} +	else { +			cf = 0; +		} + +	if ( (dst ^ v1) & (v1 ^ v2) & 0x8000) { +			of = 1; +		} +	else { +			of = 0; +		} + +	if ( (v1 ^ v2 ^ dst) & 0x10) { +			af = 1; +		} +	else { +			af = 0; +		} +} + +void flag_sub8 (uint8_t v1, uint8_t v2) { + +	/* v1 = destination operand, v2 = source operand */ +	uint16_t	dst; + +	dst = (uint16_t) v1 - (uint16_t) v2; +	flag_szp8 ( (uint8_t) dst); +	if (dst & 0xFF00) { +			cf = 1; +		} +	else { +			cf = 0; +		} + +	if ( (dst ^ v1) & (v1 ^ v2) & 0x80) { +			of = 1; +		} +	else { +			of = 0; +		} + +	if ( (v1 ^ v2 ^ dst) & 0x10) { +			af = 1; +		} +	else { +			af = 0; +		} +} + +void flag_sub16 (uint16_t v1, uint16_t v2) { + +	/* v1 = destination operand, v2 = source operand */ +	uint32_t	dst; + +	dst = (uint32_t) v1 - (uint32_t) v2; +	flag_szp16 ( (uint16_t) dst); +	if (dst & 0xFFFF0000) { +			cf = 1; +		} +	else { +			cf = 0; +		} + +	if ( (dst ^ v1) & (v1 ^ v2) & 0x8000) { +			of = 1; +		} +	else { +			of = 0; +		} + +	if ( (v1 ^ v2 ^ dst) & 0x10) { +			af = 1; +		} +	else { +			af = 0; +		} +} + +void op_adc8() { +	res8 = oper1b + oper2b + cf; +	flag_adc8 (oper1b, oper2b, cf); +} + +void op_adc16() { +	res16 = oper1 + oper2 + cf; +	flag_adc16 (oper1, oper2, cf); +} + +void op_add8() { +	res8 = oper1b + oper2b; +	flag_add8 (oper1b, oper2b); +} + +void op_add16() { +	res16 = oper1 + oper2; +	flag_add16 (oper1, oper2); +} + +void op_and8() { +	res8 = oper1b & oper2b; +	flag_log8 (res8); +} + +void op_and16() { +	res16 = oper1 & oper2; +	flag_log16 (res16); +} + +void op_or8() { +	res8 = oper1b | oper2b; +	flag_log8 (res8); +} + +void op_or16() { +	res16 = oper1 | oper2; +	flag_log16 (res16); +} + +void op_xor8() { +	res8 = oper1b ^ oper2b; +	flag_log8 (res8); +} + +void op_xor16() { +	res16 = oper1 ^ oper2; +	flag_log16 (res16); +} + +void op_sub8() { +	res8 = oper1b - oper2b; +	flag_sub8 (oper1b, oper2b); +} + +void op_sub16() { +	res16 = oper1 - oper2; +	flag_sub16 (oper1, oper2); +} + +void op_sbb8() { +	res8 = oper1b - (oper2b + cf); +	flag_sbb8 (oper1b, oper2b, cf); +} + +void op_sbb16() { +	res16 = oper1 - (oper2 + cf); +	flag_sbb16 (oper1, oper2, cf); +} + +void getea (uint8_t rmval) { +	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); +} + +void push (uint16_t pushval) { +	regs.wordregs[regsp] = regs.wordregs[regsp] - 2; +	putmem16 (segregs[regss], regs.wordregs[regsp], pushval); +} + +uint16_t pop() { + +	uint16_t	tempval; + +	tempval = getmem16 (segregs[regss], regs.wordregs[regsp]); +	regs.wordregs[regsp] = regs.wordregs[regsp] + 2; +	return tempval; +} + +void reset86() { +	segregs[regcs] = 0xFFFF; +	ip = 0x0000; +	hltstate = 0; +} + +uint16_t readrm16 (uint8_t rmval) { +	if (mode < 3) { +			getea (rmval); +			return read86 (ea) | ( (uint16_t) read86 (ea + 1) << 8); +		} +	else { +			return getreg16 (rmval); +		} +} + +uint8_t readrm8 (uint8_t rmval) { +	if (mode < 3) { +			getea (rmval); +			return read86 (ea); +		} +	else { +			return getreg8 (rmval); +		} +} + +void writerm16 (uint8_t rmval, uint16_t value) { +	if (mode < 3) { +			getea (rmval); +			write86 (ea, value & 0xFF); +			write86 (ea + 1, value >> 8); +		} +	else { +			putreg16 (rmval, value); +		} +} + +void writerm8 (uint8_t rmval, uint8_t value) { +	if (mode < 3) { +			getea (rmval); +			write86 (ea, value); +		} +	else { +			putreg8 (rmval, value); +		} +} + +uint8_t op_grp2_8 (uint8_t cnt) { + +	uint16_t	s; +	uint16_t	shift; +	uint16_t	oldcf; +	uint16_t	msb; + +	s = oper1b; +	oldcf = cf; +#ifdef CPU_LIMIT_SHIFT_COUNT +	cnt &= 0x1F; +#endif +	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); +					if ((s & 0x80) && cf) of = 1; else of = 0; +					} else of = 0; +				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; +	uint32_t	shift; +	uint32_t	oldcf; +	uint32_t	msb; + +	s = oper1; +	oldcf = cf; +#ifdef CPU_LIMIT_SHIFT_COUNT +	cnt &= 0x1F; +#endif +	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 = (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 = (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; +	uint16_t	s2; +	uint16_t	d1; +	uint16_t	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; +} + +void 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]; +				regs.wordregs[regax] = temp1 & 0xFFFF; +				flag_szp8 ( (uint8_t) temp1); +				if (regs.byteregs[regah]) { +						cf = 1; +						of = 1; +					} +				else { +						cf = 0; +						of = 0; +					} +#ifdef CPU_CLEAR_ZF_ON_MUL +				zf = 0; +#endif +				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; +				regs.wordregs[regax] = temp3 & 0xFFFF; +				if (regs.byteregs[regah]) { +						cf = 1; +						of = 1; +					} +				else { +						cf = 0; +						of = 0; +					} +#ifdef CPU_CLEAR_ZF_ON_MUL +				zf = 0; +#endif +				break; + +			case 6: /* DIV */ +				op_div8 (regs.wordregs[regax], oper1b); +				break; + +			case 7: /* IDIV */ +				op_idiv8 (regs.wordregs[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; +		} + +	regs.wordregs[regdx] = valdiv % (uint32_t) divisor; +	regs.wordregs[regax] = valdiv / (uint32_t) divisor; +} + +void op_idiv16 (uint32_t valdiv, uint16_t divisor) { + +	uint32_t	d1; +	uint32_t	d2; +	uint32_t	s1; +	uint32_t	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; +		} + +	regs.wordregs[regax] = d1; +	regs.wordregs[regdx] = d2; +} + +void 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) regs.wordregs[regax]; +				regs.wordregs[regax] = temp1 & 0xFFFF; +				regs.wordregs[regdx] = temp1 >> 16; +				flag_szp16 ( (uint16_t) temp1); +				if (regs.wordregs[regdx]) { +						cf = 1; +						of = 1; +					} +				else { +						cf = 0; +						of = 0; +					} +#ifdef CPU_CLEAR_ZF_ON_MUL +				zf = 0; +#endif +				break; + +			case 5: /* IMUL */ +				temp1 = regs.wordregs[regax]; +				temp2 = oper1; +				if (temp1 & 0x8000) { +						temp1 |= 0xFFFF0000; +					} + +				if (temp2 & 0x8000) { +						temp2 |= 0xFFFF0000; +					} + +				temp3 = temp1 * temp2; +				regs.wordregs[regax] = temp3 & 0xFFFF;	/* into register ax */ +				regs.wordregs[regdx] = temp3 >> 16;	/* into register dx */ +				if (regs.wordregs[regdx]) { +						cf = 1; +						of = 1; +					} +				else { +						cf = 0; +						of = 0; +					} +#ifdef CPU_CLEAR_ZF_ON_MUL +				zf = 0; +#endif +				break; + +			case 6: /* DIV */ +				op_div16 ( ( (uint32_t) regs.wordregs[regdx] << 16) + regs.wordregs[regax], oper1); +				break; + +			case 7: /* DIV */ +				op_idiv16 ( ( (uint32_t) regs.wordregs[regdx] << 16) + regs.wordregs[regax], oper1); +				break; +		} +} + +void 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; +		} +} + +uint8_t dolog = 0, didintr = 0; +FILE	*logout; +uint8_t printops = 0; + +#ifdef NETWORKING_ENABLED +extern void nethandler(); +#endif +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); + +void intcall86 (uint8_t intnum) { +	static uint16_t lastint10ax; +	uint16_t oldregax; +	didintr = 1; + +	if (intnum == 0x19) didbootstrap = 1; + +	switch (intnum) { +			case 0x10: +				updatedscreen = 1; +				/*if (regs.byteregs[regah]!=0x0E) { +					printf("Int 10h AX = %04X\n", regs.wordregs[regax]); +				}*/ +				if ( (regs.byteregs[regah]==0x00) || (regs.byteregs[regah]==0x10) ) { +						oldregax = regs.wordregs[regax]; +						vidinterrupt(); +						regs.wordregs[regax] = oldregax; +						if (regs.byteregs[regah]==0x10) return; +						if (vidmode==9) return; +					} +				if ( (regs.byteregs[regah]==0x1A) && (lastint10ax!=0x0100) ) { //the 0x0100 is a cheap hack to make it not do this if DOS EDIT/QBASIC +						regs.byteregs[regal] = 0x1A; +						regs.byteregs[regbl] = 0x8; +						return; +					} +				lastint10ax = regs.wordregs[regax]; +				if (regs.byteregs[regah]==0x1B) { +						regs.byteregs[regal] = 0x1B; +						segregs[reges] = 0xC800; +						regs.wordregs[regdi] = 0x0000; +						writew86(0xC8000, 0x0000); +						writew86(0xC8002, 0xC900); +						write86(0xC9000, 0x00); +						write86(0xC9001, 0x00); +						write86(0xC9002, 0x01); +						return; +					} +				break; + +#ifndef DISK_CONTROLLER_ATA +			case 0x19: //bootstrap +#ifdef BENCHMARK_BIOS +				running = 0; +#endif +				if (bootdrive<255) { //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; +					} +				else { +						segregs[regcs] = 0xF600;	//start ROM BASIC at bootstrap if requested +						ip = 0x0000; +					} +				return; + +			case 0x13: +			case 0xFD: +				diskhandler(); +				return; +#endif +#ifdef NETWORKING_OLDCARD +			case 0xFC: +#ifdef NETWORKING_ENABLED +				nethandler(); +#endif +				return; +#endif +		} + +	push (makeflagsword() ); +	push (segregs[regcs]); +	push (ip); +	segregs[regcs] = getmem16 (0, (uint16_t) intnum * 4 + 2); +	ip = getmem16 (0, (uint16_t) intnum * 4); +	ifl = 0; +	tf = 0; +} + +#if defined(NETWORKING_ENABLED) +extern struct netstruct { +	uint8_t	enabled; +	uint8_t	canrecv; +	uint16_t	pktlen; +} net; +#endif +uint64_t	frametimer = 0, didwhen = 0, didticks = 0; +uint32_t	makeupticks = 0; +extern float	timercomp; +uint64_t	timerticks = 0, realticks = 0; +uint64_t	lastcountertimer = 0, counterticks = 10000; +extern uint8_t	nextintr(); +extern void	timing(); + +#ifdef USE_PREFETCH_QUEUE +uint8_t prefetch[6]; +uint32_t prefetch_base = 0; +#endif + +void exec86 (uint32_t execloops) { + +	uint32_t loopcount; +	uint8_t docontinue; +	static uint16_t firstip; +	static uint16_t trap_toggle = 0; + +	counterticks = (uint64_t) ( (double) timerfreq / (double) 65536.0); + +	for (loopcount = 0; loopcount < execloops; loopcount++) { + +			if ( (totalexec & TIMING_INTERVAL) == 0) timing(); + +			if (trap_toggle) { +					intcall86 (1); +				} + +			if (tf) { +					trap_toggle = 1; +				} +			else { +					trap_toggle = 0; +				} + +			if (!trap_toggle && (ifl && (i8259.irr & (~i8259.imr) ) ) ) { +					hltstate = 0; +					intcall86 (nextintr() );	/* get next interrupt from the i8259, if any */ +				} + +			if (hltstate) goto skipexecution; + +			/*if ((((uint32_t)segregs[regcs] << 4) + (uint32_t)ip) == 0xFEC59) { +					//printf("Entered F000:EC59, returning to "); +					ip = pop(); +					segregs[regcs] = pop(); +					decodeflagsword(pop()); +					//printf("%04X:%04X\n", segregs[regcs], ip); +					diskhandler(); +				}*/ + +			reptype = 0; +			segoverride = 0; +			useseg = segregs[regds]; +			docontinue = 0; +			firstip = ip; + +			if ( (segregs[regcs] == 0xF000) && (ip == 0xE066) ) didbootstrap = 0; //detect if we hit the BIOS entry point to clear didbootstrap because we've rebooted + +			while (!docontinue) { +					segregs[regcs] = segregs[regcs] & 0xFFFF; +					ip = ip & 0xFFFF; +					savecs = segregs[regcs]; +					saveip = ip; +#ifdef USE_PREFETCH_QUEUE +					ea = segbase(savecs) + (uint32_t)saveip; +					if ( (ea < prefetch_base) || (ea > (prefetch_base + 5)) ) { +							memcpy (&prefetch[0], &RAM[ea], 6); +							prefetch_base = ea; +						} +					opcode = prefetch[ea - prefetch_base]; +#else +					opcode = getmem8 (segregs[regcs], ip); +#endif +					StepIP (1); + +					switch (opcode) { +								/* 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; +								break; +						} +				} + +			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 eAX Iv */ +						oper1 = regs.wordregs[regax]; +						oper2 = getmem16 (segregs[regcs], ip); +						StepIP (2); +						op_add16(); +						regs.wordregs[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 eAX Iv */ +						oper1 = regs.wordregs[regax]; +						oper2 = getmem16 (segregs[regcs], ip); +						StepIP (2); +						op_or16(); +						regs.wordregs[regax] = res16; +						break; + +					case 0xE:	/* 0E PUSH segregs[regcs] */ +						push (segregs[regcs]); +						break; + +#ifdef CPU_ALLOW_POP_CS //only the 8086/8088 does this. +					case 0xF: //0F POP CS +						segregs[regcs] = pop(); +						break; +#endif + +					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 eAX Iv */ +						oper1 = regs.wordregs[regax]; +						oper2 = getmem16 (segregs[regcs], ip); +						StepIP (2); +						op_adc16(); +						regs.wordregs[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 eAX Iv */ +						oper1 = regs.wordregs[regax]; +						oper2 = getmem16 (segregs[regcs], ip); +						StepIP (2); +						op_sbb16(); +						regs.wordregs[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 eAX Iv */ +						oper1 = regs.wordregs[regax]; +						oper2 = getmem16 (segregs[regcs], ip); +						StepIP (2); +						op_and16(); +						regs.wordregs[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]  > 0x9F) || (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 eAX Iv */ +						oper1 = regs.wordregs[regax]; +						oper2 = getmem16 (segregs[regcs], ip); +						StepIP (2); +						op_sub16(); +						regs.wordregs[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 eAX Iv */ +						oper1 = regs.wordregs[regax]; +						oper2 = getmem16 (segregs[regcs], ip); +						StepIP (2); +						op_xor16(); +						regs.wordregs[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 eAX Iv */ +						oper1 = regs.wordregs[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 eAX */ +						oldcf = cf; +						oper1 = regs.wordregs[regax]; +						oper2 = 1; +						op_add16(); +						cf = oldcf; +						regs.wordregs[regax] = res16; +						break; + +					case 0x41:	/* 41 INC eCX */ +						oldcf = cf; +						oper1 = regs.wordregs[regcx]; +						oper2 = 1; +						op_add16(); +						cf = oldcf; +						regs.wordregs[regcx] = res16; +						break; + +					case 0x42:	/* 42 INC eDX */ +						oldcf = cf; +						oper1 = regs.wordregs[regdx]; +						oper2 = 1; +						op_add16(); +						cf = oldcf; +						regs.wordregs[regdx] = res16; +						break; + +					case 0x43:	/* 43 INC eBX */ +						oldcf = cf; +						oper1 = regs.wordregs[regbx]; +						oper2 = 1; +						op_add16(); +						cf = oldcf; +						regs.wordregs[regbx] = res16; +						break; + +					case 0x44:	/* 44 INC eSP */ +						oldcf = cf; +						oper1 = regs.wordregs[regsp]; +						oper2 = 1; +						op_add16(); +						cf = oldcf; +						regs.wordregs[regsp] = res16; +						break; + +					case 0x45:	/* 45 INC eBP */ +						oldcf = cf; +						oper1 = regs.wordregs[regbp]; +						oper2 = 1; +						op_add16(); +						cf = oldcf; +						regs.wordregs[regbp] = res16; +						break; + +					case 0x46:	/* 46 INC eSI */ +						oldcf = cf; +						oper1 = regs.wordregs[regsi]; +						oper2 = 1; +						op_add16(); +						cf = oldcf; +						regs.wordregs[regsi] = res16; +						break; + +					case 0x47:	/* 47 INC eDI */ +						oldcf = cf; +						oper1 = regs.wordregs[regdi]; +						oper2 = 1; +						op_add16(); +						cf = oldcf; +						regs.wordregs[regdi] = res16; +						break; + +					case 0x48:	/* 48 DEC eAX */ +						oldcf = cf; +						oper1 = regs.wordregs[regax]; +						oper2 = 1; +						op_sub16(); +						cf = oldcf; +						regs.wordregs[regax] = res16; +						break; + +					case 0x49:	/* 49 DEC eCX */ +						oldcf = cf; +						oper1 = regs.wordregs[regcx]; +						oper2 = 1; +						op_sub16(); +						cf = oldcf; +						regs.wordregs[regcx] = res16; +						break; + +					case 0x4A:	/* 4A DEC eDX */ +						oldcf = cf; +						oper1 = regs.wordregs[regdx]; +						oper2 = 1; +						op_sub16(); +						cf = oldcf; +						regs.wordregs[regdx] = res16; +						break; + +					case 0x4B:	/* 4B DEC eBX */ +						oldcf = cf; +						oper1 = regs.wordregs[regbx]; +						oper2 = 1; +						op_sub16(); +						cf = oldcf; +						regs.wordregs[regbx] = res16; +						break; + +					case 0x4C:	/* 4C DEC eSP */ +						oldcf = cf; +						oper1 = regs.wordregs[regsp]; +						oper2 = 1; +						op_sub16(); +						cf = oldcf; +						regs.wordregs[regsp] = res16; +						break; + +					case 0x4D:	/* 4D DEC eBP */ +						oldcf = cf; +						oper1 = regs.wordregs[regbp]; +						oper2 = 1; +						op_sub16(); +						cf = oldcf; +						regs.wordregs[regbp] = res16; +						break; + +					case 0x4E:	/* 4E DEC eSI */ +						oldcf = cf; +						oper1 = regs.wordregs[regsi]; +						oper2 = 1; +						op_sub16(); +						cf = oldcf; +						regs.wordregs[regsi] = res16; +						break; + +					case 0x4F:	/* 4F DEC eDI */ +						oldcf = cf; +						oper1 = regs.wordregs[regdi]; +						oper2 = 1; +						op_sub16(); +						cf = oldcf; +						regs.wordregs[regdi] = res16; +						break; + +					case 0x50:	/* 50 PUSH eAX */ +						push (regs.wordregs[regax]); +						break; + +					case 0x51:	/* 51 PUSH eCX */ +						push (regs.wordregs[regcx]); +						break; + +					case 0x52:	/* 52 PUSH eDX */ +						push (regs.wordregs[regdx]); +						break; + +					case 0x53:	/* 53 PUSH eBX */ +						push (regs.wordregs[regbx]); +						break; + +					case 0x54:	/* 54 PUSH eSP */ +#ifdef USE_286_STYLE_PUSH_SP +						push (regs.wordregs[regsp]); +#else +						push (regs.wordregs[regsp] - 2); +#endif +						break; + +					case 0x55:	/* 55 PUSH eBP */ +						push (regs.wordregs[regbp]); +						break; + +					case 0x56:	/* 56 PUSH eSI */ +						push (regs.wordregs[regsi]); +						break; + +					case 0x57:	/* 57 PUSH eDI */ +						push (regs.wordregs[regdi]); +						break; + +					case 0x58:	/* 58 POP eAX */ +						regs.wordregs[regax] = pop(); +						break; + +					case 0x59:	/* 59 POP eCX */ +						regs.wordregs[regcx] = pop(); +						break; + +					case 0x5A:	/* 5A POP eDX */ +						regs.wordregs[regdx] = pop(); +						break; + +					case 0x5B:	/* 5B POP eBX */ +						regs.wordregs[regbx] = pop(); +						break; + +					case 0x5C:	/* 5C POP eSP */ +						regs.wordregs[regsp] = pop(); +						break; + +					case 0x5D:	/* 5D POP eBP */ +						regs.wordregs[regbp] = pop(); +						break; + +					case 0x5E:	/* 5E POP eSI */ +						regs.wordregs[regsi] = pop(); +						break; + +					case 0x5F:	/* 5F POP eDI */ +						regs.wordregs[regdi] = pop(); +						break; + +#ifndef CPU_8086 +					case 0x60:	/* 60 PUSHA (80186+) */ +						oldsp = regs.wordregs[regsp]; +						push (regs.wordregs[regax]); +						push (regs.wordregs[regcx]); +						push (regs.wordregs[regdx]); +						push (regs.wordregs[regbx]); +						push (oldsp); +						push (regs.wordregs[regbp]); +						push (regs.wordregs[regsi]); +						push (regs.wordregs[regdi]); +						break; + +					case 0x61:	/* 61 POPA (80186+) */ +						regs.wordregs[regdi] = pop(); +						regs.wordregs[regsi] = pop(); +						regs.wordregs[regbp] = pop(); +						dummy = pop(); +						regs.wordregs[regbx] = pop(); +						regs.wordregs[regdx] = pop(); +						regs.wordregs[regcx] = pop(); +						regs.wordregs[regax] = pop(); +						break; + +					case 0x62: /* 62 BOUND Gv, Ev (80186+) */ +						modregrm(); +						getea (rm); +						if (signext32 (getreg16 (reg) ) < signext32 ( getmem16 (ea >> 4, ea & 15) ) ) { +								intcall86 (5); //bounds check exception +							} +						else { +								ea += 2; +								if (signext32 (getreg16 (reg) ) > signext32 ( getmem16 (ea >> 4, ea & 15) ) ) { +										intcall86(5); //bounds check exception +									} +							} +						break; + +					case 0x68:	/* 68 PUSH Iv (80186+) */ +						push (getmem16 (segregs[regcs], ip) ); +						StepIP (2); +						break; + +					case 0x69:	/* 69 IMUL Gv 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; +						putreg16 (reg, 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 Gv Eb Ib (80186+) */ +						modregrm(); +						temp1 = readrm16 (rm); +						temp2 = signext (getmem8 (segregs[regcs], ip) ); +						StepIP (1); +						if ( (temp1 & 0x8000L) == 0x8000L) { +								temp1 = temp1 | 0xFFFF0000L; +							} + +						if ( (temp2 & 0x8000L) == 0x8000L) { +								temp2 = temp2 | 0xFFFF0000L; +							} + +						temp3 = temp1 * temp2; +						putreg16 (reg, temp3 & 0xFFFFL); +						if (temp3 & 0xFFFF0000L) { +								cf = 1; +								of = 1; +							} +						else { +								cf = 0; +								of = 0; +							} +						break; + +					case 0x6C:	/* 6E INSB */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						putmem8 (useseg, regs.wordregs[regsi], portin (regs.wordregs[regdx]) ); +						if (df) { +								regs.wordregs[regsi] = regs.wordregs[regsi] - 1; +								regs.wordregs[regdi] = regs.wordregs[regdi] - 1; +							} +						else { +								regs.wordregs[regsi] = regs.wordregs[regsi] + 1; +								regs.wordregs[regdi] = regs.wordregs[regdi] + 1; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					case 0x6D:	/* 6F INSW */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						putmem16 (useseg, regs.wordregs[regsi], portin16 (regs.wordregs[regdx]) ); +						if (df) { +								regs.wordregs[regsi] = regs.wordregs[regsi] - 2; +								regs.wordregs[regdi] = regs.wordregs[regdi] - 2; +							} +						else { +								regs.wordregs[regsi] = regs.wordregs[regsi] + 2; +								regs.wordregs[regdi] = regs.wordregs[regdi] + 2; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					case 0x6E:	/* 6E OUTSB */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						portout (regs.wordregs[regdx], getmem8 (useseg, regs.wordregs[regsi]) ); +						if (df) { +								regs.wordregs[regsi] = regs.wordregs[regsi] - 1; +								regs.wordregs[regdi] = regs.wordregs[regdi] - 1; +							} +						else { +								regs.wordregs[regsi] = regs.wordregs[regsi] + 1; +								regs.wordregs[regdi] = regs.wordregs[regdi] + 1; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					case 0x6F:	/* 6F OUTSW */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						portout16 (regs.wordregs[regdx], getmem16 (useseg, regs.wordregs[regsi]) ); +						if (df) { +								regs.wordregs[regsi] = regs.wordregs[regsi] - 2; +								regs.wordregs[regdi] = regs.wordregs[regdi] - 2; +							} +						else { +								regs.wordregs[regsi] = regs.wordregs[regsi] + 2; +								regs.wordregs[regdi] = regs.wordregs[regdi] + 2; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; +#endif + +					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; + +					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, ea - 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 = regs.wordregs[regcx]; +						regs.wordregs[regcx] = regs.wordregs[regax]; +						regs.wordregs[regax] = oper1; +						break; + +					case 0x92:	/* 92 XCHG eDX eAX */ +						oper1 = regs.wordregs[regdx]; +						regs.wordregs[regdx] = regs.wordregs[regax]; +						regs.wordregs[regax] = oper1; +						break; + +					case 0x93:	/* 93 XCHG eBX eAX */ +						oper1 = regs.wordregs[regbx]; +						regs.wordregs[regbx] = regs.wordregs[regax]; +						regs.wordregs[regax] = oper1; +						break; + +					case 0x94:	/* 94 XCHG eSP eAX */ +						oper1 = regs.wordregs[regsp]; +						regs.wordregs[regsp] = regs.wordregs[regax]; +						regs.wordregs[regax] = oper1; +						break; + +					case 0x95:	/* 95 XCHG eBP eAX */ +						oper1 = regs.wordregs[regbp]; +						regs.wordregs[regbp] = regs.wordregs[regax]; +						regs.wordregs[regax] = oper1; +						break; + +					case 0x96:	/* 96 XCHG eSI eAX */ +						oper1 = regs.wordregs[regsi]; +						regs.wordregs[regsi] = regs.wordregs[regax]; +						regs.wordregs[regax] = oper1; +						break; + +					case 0x97:	/* 97 XCHG eDI eAX */ +						oper1 = regs.wordregs[regdi]; +						regs.wordregs[regdi] = regs.wordregs[regax]; +						regs.wordregs[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) { +								regs.wordregs[regdx] = 0xFFFF; +							} +						else { +								regs.wordregs[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 */ +#ifdef CPU_SET_HIGH_FLAGS +						push (makeflagsword() | 0xF800); +#else +						push (makeflagsword() | 0x0800); +#endif +						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 eAX Ov */ +						oper1 = getmem16 (useseg, getmem16 (segregs[regcs], ip) ); +						StepIP (2); +						regs.wordregs[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 eAX */ +						putmem16 (useseg, getmem16 (segregs[regcs], ip), regs.wordregs[regax]); +						StepIP (2); +						break; + +					case 0xA4:	/* A4 MOVSB */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						putmem8 (segregs[reges], regs.wordregs[regdi], getmem8 (useseg, regs.wordregs[regsi]) ); +						if (df) { +								regs.wordregs[regsi] = regs.wordregs[regsi] - 1; +								regs.wordregs[regdi] = regs.wordregs[regdi] - 1; +							} +						else { +								regs.wordregs[regsi] = regs.wordregs[regsi] + 1; +								regs.wordregs[regdi] = regs.wordregs[regdi] + 1; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					case 0xA5:	/* A5 MOVSW */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						putmem16 (segregs[reges], regs.wordregs[regdi], getmem16 (useseg, regs.wordregs[regsi]) ); +						if (df) { +								regs.wordregs[regsi] = regs.wordregs[regsi] - 2; +								regs.wordregs[regdi] = regs.wordregs[regdi] - 2; +							} +						else { +								regs.wordregs[regsi] = regs.wordregs[regsi] + 2; +								regs.wordregs[regdi] = regs.wordregs[regdi] + 2; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					case 0xA6:	/* A6 CMPSB */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						oper1b = getmem8 (useseg, regs.wordregs[regsi]); +						oper2b = getmem8 (segregs[reges], regs.wordregs[regdi]); +						if (df) { +								regs.wordregs[regsi] = regs.wordregs[regsi] - 1; +								regs.wordregs[regdi] = regs.wordregs[regdi] - 1; +							} +						else { +								regs.wordregs[regsi] = regs.wordregs[regsi] + 1; +								regs.wordregs[regdi] = regs.wordregs[regdi] + 1; +							} + +						flag_sub8 (oper1b, oper2b); +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						if ( (reptype == 1) && !zf) { +								break; +							} +						else if ( (reptype == 2) && (zf == 1) ) { +								break; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					case 0xA7:	/* A7 CMPSW */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						oper1 = getmem16 (useseg,regs.wordregs[regsi]); +						oper2 = getmem16 (segregs[reges], regs.wordregs[regdi]); +						if (df) { +								regs.wordregs[regsi] = regs.wordregs[regsi] - 2; +								regs.wordregs[regdi] = regs.wordregs[regdi] - 2; +							} +						else { +								regs.wordregs[regsi] = regs.wordregs[regsi] + 2; +								regs.wordregs[regdi] = regs.wordregs[regdi] + 2; +							} + +						flag_sub16 (oper1, oper2); +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						if ( (reptype == 1) && !zf) { +								break; +							} + +						if ( (reptype == 2) && (zf == 1) ) { +								break; +							} + +						totalexec++; +						loopcount++; +						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 = regs.wordregs[regax]; +						oper2 = getmem16 (segregs[regcs], ip); +						StepIP (2); +						flag_log16 (oper1 & oper2); +						break; + +					case 0xAA:	/* AA STOSB */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						putmem8 (segregs[reges], regs.wordregs[regdi], regs.byteregs[regal]); +						if (df) { +								regs.wordregs[regdi] = regs.wordregs[regdi] - 1; +							} +						else { +								regs.wordregs[regdi] = regs.wordregs[regdi] + 1; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					case 0xAB:	/* AB STOSW */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						putmem16 (segregs[reges], regs.wordregs[regdi], regs.wordregs[regax]); +						if (df) { +								regs.wordregs[regdi] = regs.wordregs[regdi] - 2; +							} +						else { +								regs.wordregs[regdi] = regs.wordregs[regdi] + 2; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					case 0xAC:	/* AC LODSB */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						regs.byteregs[regal] = getmem8 (useseg, regs.wordregs[regsi]); +						if (df) { +								regs.wordregs[regsi] = regs.wordregs[regsi] - 1; +							} +						else { +								regs.wordregs[regsi] = regs.wordregs[regsi] + 1; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					case 0xAD:	/* AD LODSW */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						oper1 = getmem16 (useseg, regs.wordregs[regsi]); +						regs.wordregs[regax] = oper1; +						if (df) { +								regs.wordregs[regsi] = regs.wordregs[regsi] - 2; +							} +						else { +								regs.wordregs[regsi] = regs.wordregs[regsi] + 2; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					case 0xAE:	/* AE SCASB */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						oper1b = regs.byteregs[regal]; +						oper2b = getmem8 (segregs[reges], regs.wordregs[regdi]); +						flag_sub8 (oper1b, oper2b); +						if (df) { +								regs.wordregs[regdi] = regs.wordregs[regdi] - 1; +							} +						else { +								regs.wordregs[regdi] = regs.wordregs[regdi] + 1; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						if ( (reptype == 1) && !zf) { +								break; +							} +						else if ( (reptype == 2) && (zf == 1) ) { +								break; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					case 0xAF:	/* AF SCASW */ +						if (reptype && (regs.wordregs[regcx] == 0) ) { +								break; +							} + +						oper1 = regs.wordregs[regax]; +						oper2 = getmem16 (segregs[reges], regs.wordregs[regdi]); +						flag_sub16 (oper1, oper2); +						if (df) { +								regs.wordregs[regdi] = regs.wordregs[regdi] - 2; +							} +						else { +								regs.wordregs[regdi] = regs.wordregs[regdi] + 2; +							} + +						if (reptype) { +								regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +							} + +						if ( (reptype == 1) && !zf) { +								break; +							} +						else if ( (reptype == 2) & (zf == 1) ) { +								break; +							} + +						totalexec++; +						loopcount++; +						if (!reptype) { +								break; +							} + +						ip = firstip; +						break; + +					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); +						regs.wordregs[regax] = oper1; +						break; + +					case 0xB9:	/* B9 MOV eCX Iv */ +						oper1 = getmem16 (segregs[regcs], ip); +						StepIP (2); +						regs.wordregs[regcx] = oper1; +						break; + +					case 0xBA:	/* BA MOV eDX Iv */ +						oper1 = getmem16 (segregs[regcs], ip); +						StepIP (2); +						regs.wordregs[regdx] = oper1; +						break; + +					case 0xBB:	/* BB MOV eBX Iv */ +						oper1 = getmem16 (segregs[regcs], ip); +						StepIP (2); +						regs.wordregs[regbx] = oper1; +						break; + +					case 0xBC:	/* BC MOV eSP Iv */ +						regs.wordregs[regsp] = getmem16 (segregs[regcs], ip); +						StepIP (2); +						break; + +					case 0xBD:	/* BD MOV eBP Iv */ +						regs.wordregs[regbp] = getmem16 (segregs[regcs], ip); +						StepIP (2); +						break; + +					case 0xBE:	/* BE MOV eSI Iv */ +						regs.wordregs[regsi] = getmem16 (segregs[regcs], ip); +						StepIP (2); +						break; + +					case 0xBF:	/* BF MOV eDI Iv */ +						regs.wordregs[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); +						oper2 = getmem8 (segregs[regcs], ip); +						StepIP (1); +						writerm16 (rm, op_grp2_16 ( (uint8_t) oper2) ); +						break; + +					case 0xC2:	/* C2 RET Iw */ +						oper1 = getmem16 (segregs[regcs], ip); +						ip = pop(); +						regs.wordregs[regsp] = regs.wordregs[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 (regs.wordregs[regbp]); +						frametemp = regs.wordregs[regsp]; +						if (nestlev) { +								for (temp16 = 1; temp16 < nestlev; temp16++) { +										regs.wordregs[regbp] = regs.wordregs[regbp] - 2; +										push (regs.wordregs[regbp]); +									} + +								push (regs.wordregs[regsp]); +							} + +						regs.wordregs[regbp] = frametemp; +						regs.wordregs[regsp] = regs.wordregs[regbp] - stacksize; + +						break; + +					case 0xC9:	/* C9 LEAVE (80186+) */ +						regs.wordregs[regsp] = regs.wordregs[regbp]; +						regs.wordregs[regbp] = pop(); +						break; + +					case 0xCA:	/* CA RETF Iw */ +						oper1 = getmem16 (segregs[regcs], ip); +						ip = pop(); +						segregs[regcs] = pop(); +						regs.wordregs[regsp] = regs.wordregs[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); +								break; +							}	/* division by zero */ + +						regs.byteregs[regah] = (regs.byteregs[regal] / oper1) & 255; +						regs.byteregs[regal] = (regs.byteregs[regal] % oper1) & 255; +						flag_szp16 (regs.wordregs[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 0xD6:	/* D6 XLAT on V20/V30, SALC on 8086/8088 */ +#ifndef CPU_NO_SALC +						regs.byteregs[regal] = cf ? 0xFF : 0x00; +						break; +#endif + +					case 0xD7:	/* D7 XLAT */ +						regs.byteregs[regal] = read86(useseg * 16 + (regs.wordregs[regbx]) + regs.byteregs[regal]); +						break; + +					case 0xD8: +					case 0xD9: +					case 0xDA: +					case 0xDB: +					case 0xDC: +					case 0xDE: +					case 0xDD: +					case 0xDF:	/* escape to x87 FPU (unsupported) */ +						modregrm(); +						break; + +					case 0xE0:	/* E0 LOOPNZ Jb */ +						temp16 = signext (getmem8 (segregs[regcs], ip) ); +						StepIP (1); +						regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +						if ( (regs.wordregs[regcx]) && !zf) { +								ip = ip + temp16; +							} +						break; + +					case 0xE1:	/* E1 LOOPZ Jb */ +						temp16 = signext (getmem8 (segregs[regcs], ip) ); +						StepIP (1); +						regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +						if (regs.wordregs[regcx] && (zf == 1) ) { +								ip = ip + temp16; +							} +						break; + +					case 0xE2:	/* E2 LOOP Jb */ +						temp16 = signext (getmem8 (segregs[regcs], ip) ); +						StepIP (1); +						regs.wordregs[regcx] = regs.wordregs[regcx] - 1; +						if (regs.wordregs[regcx]) { +								ip = ip + temp16; +							} +						break; + +					case 0xE3:	/* E3 JCXZ Jb */ +						temp16 = signext (getmem8 (segregs[regcs], ip) ); +						StepIP (1); +						if (!regs.wordregs[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); +						regs.wordregs[regax] = portin16 (oper1b); +						break; + +					case 0xE6:	/* E6 OUT Ib regs.byteregs[regal] */ +						oper1b = getmem8 (segregs[regcs], ip); +						StepIP (1); +						portout (oper1b, regs.byteregs[regal]); +						break; + +					case 0xE7:	/* E7 OUT Ib eAX */ +						oper1b = getmem8 (segregs[regcs], ip); +						StepIP (1); +						portout16 (oper1b, regs.wordregs[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 = regs.wordregs[regdx]; +						regs.byteregs[regal] = (uint8_t) portin (oper1); +						break; + +					case 0xED:	/* ED IN eAX regdx */ +						oper1 = regs.wordregs[regdx]; +						regs.wordregs[regax] = portin16 (oper1); +						break; + +					case 0xEE:	/* EE OUT regdx regs.byteregs[regal] */ +						oper1 = regs.wordregs[regdx]; +						portout (oper1, regs.byteregs[regal]); +						break; + +					case 0xEF:	/* EF OUT regdx eAX */ +						oper1 = regs.wordregs[regdx]; +						portout16 (oper1, regs.wordregs[regax]); +						break; + +					case 0xF0:	/* F0 LOCK */ +						break; + +					case 0xF4:	/* F4 HLT */ +						hltstate = 1; +						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: +#ifdef CPU_ALLOW_ILLEGAL_OP_EXCEPTION +						intcall86 (6); /* trip invalid opcode exception (this occurs on the 80186+, 8086/8088 CPUs treat them as NOPs. */ +						               /* technically they aren't exactly like NOPs in most cases, but for our pursoses, that's accurate enough. */ +#endif +						if (verbose) { +								printf ("Illegal opcode: %02X %02X /%X @ %04X:%04X\n", getmem8(savecs, saveip), getmem8(savecs, saveip+1), (getmem8(savecs, saveip+2) >> 3) & 7, savecs, saveip); +							} +						break; +				} + +skipexecution: +			if (!running) { +					return; +				} +		} +} +#endif diff --git a/src/fake86/cpu.h b/src/fake86/cpu.h new file mode 100755 index 0000000..6b37f47 --- /dev/null +++ b/src/fake86/cpu.h @@ -0,0 +1,110 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2012 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +#ifdef _WIN32 +#include <windows.h> +#else +#include <time.h> +#endif + +#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]; +}; + +#ifdef CPU_ADDR_MODE_CACHE +struct addrmodecache_s { +	uint16_t exitcs; +	uint16_t exitip; +	uint16_t disp16; +	uint32_t len; +	uint8_t mode; +	uint8_t reg; +	uint8_t rm; +	uint8_t forcess; +	uint8_t valid; +}; +#endif + +#define StepIP(x)	ip += x +#define getmem8(x, y)	read86(segbase(x) + y) +#define getmem16(x, y)	readw86(segbase(x) + y) +#define putmem8(x, y, z)	write86(segbase(x) + y, z) +#define putmem16(x, y, z)	writew86(segbase(x) + y, z) +#define signext(value)	(int16_t)(int8_t)(value) +#define signext32(value)	(int32_t)(int16_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) + +#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; \ +	} diff --git a/src/fake86/disk.c b/src/fake86/disk.c new file mode 100755 index 0000000..b09c8e2 --- /dev/null +++ b/src/fake86/disk.c @@ -0,0 +1,182 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* disk.c: disk emulation routines for Fake86. works at the BIOS interrupt 13h level. */ + +#include <stdint.h> +#include <stdio.h> +#include "disk.h" +#include "cpu.h" + +extern uint8_t RAM[0x100000], cf, hdcount; +extern uint16_t segregs[4]; +extern union _bytewordregs_ regs; + +extern uint8_t read86 (uint32_t addr32); +extern void write86 (uint32_t addr32, uint8_t value); + +struct struct_drive disk[256]; +uint8_t sectorbuffer[512]; + +uint8_t insertdisk (uint8_t drivenum, char *filename) { +	if (disk[drivenum].inserted) fclose (disk[drivenum].diskfile); +	else disk[drivenum].inserted = 1; +	disk[drivenum].diskfile = fopen (filename, "r+b"); +	if (disk[drivenum].diskfile==NULL) { +			disk[drivenum].inserted = 0; +			return (1); +		} +	fseek (disk[drivenum].diskfile, 0L, SEEK_END); +	disk[drivenum].filesize = ftell (disk[drivenum].diskfile); +	fseek (disk[drivenum].diskfile, 0L, SEEK_SET); +	if (drivenum >= 0x80) { //it's a hard disk image +			disk[drivenum].sects = 63; +			disk[drivenum].heads = 16; +			disk[drivenum].cyls = disk[drivenum].filesize / (disk[drivenum].sects * disk[drivenum].heads * 512); +			hdcount++; +		} +	else {   //it's a floppy image +			disk[drivenum].cyls = 80; +			disk[drivenum].sects = 18; +			disk[drivenum].heads = 2; +			if (disk[drivenum].filesize <= 1228800) disk[drivenum].sects = 15; +			if (disk[drivenum].filesize <= 737280) disk[drivenum].sects = 9; +			if (disk[drivenum].filesize <= 368640) { +					disk[drivenum].cyls = 40; +					disk[drivenum].sects = 9; +				} +			if (disk[drivenum].filesize <= 163840) { +					disk[drivenum].cyls = 40; +					disk[drivenum].sects = 8; +					disk[drivenum].heads = 1; +				} +		} +	return (0); +} + +void ejectdisk (uint8_t drivenum) { +	disk[drivenum].inserted = 0; +	if (disk[drivenum].diskfile != NULL) fclose (disk[drivenum].diskfile); +} + +void readdisk (uint8_t drivenum, uint16_t dstseg, uint16_t dstoff, uint16_t cyl, uint16_t sect, uint16_t head, uint16_t sectcount) { +	uint32_t memdest, lba, fileoffset, cursect, sectoffset; +	if (!sect || !disk[drivenum].inserted) return; +	lba = ( (uint32_t) cyl * (uint32_t) disk[drivenum].heads + (uint32_t) head) * (uint32_t) disk[drivenum].sects + (uint32_t) sect - 1; +	fileoffset = lba * 512; +	if (fileoffset>disk[drivenum].filesize) return; +	fseek (disk[drivenum].diskfile, fileoffset, SEEK_SET); +	memdest = ( (uint32_t) dstseg << 4) + (uint32_t) dstoff; +	//for the readdisk function, we need to use write86 instead of directly fread'ing into +	//the RAM array, so that read-only flags are honored. otherwise, a program could load +	//data from a disk over BIOS or other ROM code that it shouldn't be able to. +	for (cursect=0; cursect<sectcount; cursect++) { +			if (fread (sectorbuffer, 1, 512, disk[drivenum].diskfile) < 512) break; +			for (sectoffset=0; sectoffset<512; sectoffset++) { +					write86 (memdest++, sectorbuffer[sectoffset]); +				} +		} +	regs.byteregs[regal] = cursect; +	cf = 0; +	regs.byteregs[regah] = 0; +} + +void writedisk (uint8_t drivenum, uint16_t dstseg, uint16_t dstoff, uint16_t cyl, uint16_t sect, uint16_t head, uint16_t sectcount) { +	uint32_t memdest, lba, fileoffset, cursect, sectoffset; +	if (!sect || !disk[drivenum].inserted) return; +	lba = ( (uint32_t) cyl * (uint32_t) disk[drivenum].heads + (uint32_t) head) * (uint32_t) disk[drivenum].sects + (uint32_t) sect - 1; +	fileoffset = lba * 512; +	if (fileoffset>disk[drivenum].filesize) return; +	fseek (disk[drivenum].diskfile, fileoffset, SEEK_SET); +	memdest = ( (uint32_t) dstseg << 4) + (uint32_t) dstoff; +	for (cursect=0; cursect<sectcount; cursect++) { +			for (sectoffset=0; sectoffset<512; sectoffset++) { +					sectorbuffer[sectoffset] = read86 (memdest++); +				} +			fwrite (sectorbuffer, 1, 512, disk[drivenum].diskfile); +		} +	regs.byteregs[regal] = (uint8_t) sectcount; +	cf = 0; +	regs.byteregs[regah] = 0; +} + +void diskhandler() { +	static uint8_t lastdiskah[256], lastdiskcf[256]; +	switch (regs.byteregs[regah]) { +			case 0: //reset disk system +				regs.byteregs[regah] = 0; +				cf = 0; //useless function in an emulator. say success and return. +				break; +			case 1: //return last status +				regs.byteregs[regah] = lastdiskah[regs.byteregs[regdl]]; +				cf = lastdiskcf[regs.byteregs[regdl]]; +				return; +			case 2: //read sector(s) into memory +				if (disk[regs.byteregs[regdl]].inserted) { +						readdisk (regs.byteregs[regdl], segregs[reges], getreg16 (regbx), regs.byteregs[regch] + (regs.byteregs[regcl]/64) *256, regs.byteregs[regcl] & 63, regs.byteregs[regdh], regs.byteregs[regal]); +						cf = 0; +						regs.byteregs[regah] = 0; +					} +				else { +						cf = 1; +						regs.byteregs[regah] = 1; +					} +				break; +			case 3: //write sector(s) from memory +				if (disk[regs.byteregs[regdl]].inserted) { +						writedisk (regs.byteregs[regdl], segregs[reges], getreg16 (regbx), regs.byteregs[regch] + (regs.byteregs[regcl]/64) *256, regs.byteregs[regcl] & 63, regs.byteregs[regdh], regs.byteregs[regal]); +						cf = 0; +						regs.byteregs[regah] = 0; +					} +				else { +						cf = 1; +						regs.byteregs[regah] = 1; +					} +				break; +			case 4: +			case 5: //format track +				cf = 0; +				regs.byteregs[regah] = 0; +				break; +			case 8: //get drive parameters +				if (disk[regs.byteregs[regdl]].inserted) { +						cf = 0; +						regs.byteregs[regah] = 0; +						regs.byteregs[regch] = disk[regs.byteregs[regdl]].cyls - 1; +						regs.byteregs[regcl] = disk[regs.byteregs[regdl]].sects & 63; +						regs.byteregs[regcl] = regs.byteregs[regcl] + (disk[regs.byteregs[regdl]].cyls/256) *64; +						regs.byteregs[regdh] = disk[regs.byteregs[regdl]].heads - 1; +						if (regs.byteregs[regdl]<0x80) { +								regs.byteregs[regbl] = 4; //else regs.byteregs[regbl] = 0; +								regs.byteregs[regdl] = 2; +							} +						else regs.byteregs[regdl] = hdcount; +					} +				else { +						cf = 1; +						regs.byteregs[regah] = 0xAA; +					} +				break; +			default: +				cf = 1; +		} +	lastdiskah[regs.byteregs[regdl]] = regs.byteregs[regah]; +	lastdiskcf[regs.byteregs[regdl]] = cf; +	if (regs.byteregs[regdl] & 0x80) RAM[0x474] = regs.byteregs[regah]; +} diff --git a/src/fake86/disk.h b/src/fake86/disk.h new file mode 100755 index 0000000..34f062a --- /dev/null +++ b/src/fake86/disk.h @@ -0,0 +1,31 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2012 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +#include <stdint.h> +#include <stdio.h> + +struct struct_drive { +	FILE *diskfile; +	uint32_t filesize; +	uint16_t cyls; +	uint16_t sects; +	uint16_t heads; +	uint8_t inserted; +	char *filename; +}; diff --git a/src/fake86/i8237.c b/src/fake86/i8237.c new file mode 100755 index 0000000..2ad9498 --- /dev/null +++ b/src/fake86/i8237.c @@ -0,0 +1,133 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* i8237.c: functions to emulate the Intel 8237 DMA controller. +   the Sound Blaster emulation functions rely on this! */ + +#include "config.h" +#include <stdint.h> +#include <stdio.h> +#include <memory.h> +#include "blaster.h" +#include "i8237.h" + +extern struct blaster_s blaster; + +struct dmachan_s dmachan[4]; +uint8_t flipflop = 0; + +extern void set_port_write_redirector (uint16_t startport, uint16_t endport, void *callback); +extern void set_port_read_redirector (uint16_t startport, uint16_t endport, void *callback); +extern uint8_t read86 (uint32_t addr32); + +extern uint8_t RAM[0x100000]; +uint8_t read8237 (uint8_t channel) { +	uint8_t ret; +	if (dmachan[channel].masked) return (128); +	if (dmachan[channel].autoinit && (dmachan[channel].count > dmachan[channel].reload) ) dmachan[channel].count = 0; +	if (dmachan[channel].count > dmachan[channel].reload) return (128); +	//if (dmachan[channel].direction) ret = RAM[dmachan[channel].page + dmachan[channel].addr + dmachan[channel].count]; +	//	else ret = RAM[dmachan[channel].page + dmachan[channel].addr - dmachan[channel].count]; +	if (dmachan[channel].direction == 0) ret = RAM[dmachan[channel].page + dmachan[channel].addr + dmachan[channel].count]; +	else ret = RAM[dmachan[channel].page + dmachan[channel].addr - dmachan[channel].count]; +	dmachan[channel].count++; +	return (ret); +} + +void out8237 (uint16_t addr, uint8_t value) { +	uint8_t channel; +#ifdef DEBUG_DMA +	printf ("out8237(0x%X, %X);\n", addr, value); +#endif +	switch (addr) { +			case 0x2: //channel 1 address register +				if (flipflop == 1) dmachan[1].addr = (dmachan[1].addr & 0x00FF) | ( (uint32_t) value << 8); +				else dmachan[1].addr = (dmachan[1].addr & 0xFF00) | value; +#ifdef DEBUG_DMA +				if (flipflop == 1) printf ("[NOTICE] DMA channel 1 address register = %04X\n", dmachan[1].addr); +#endif +				flipflop = ~flipflop & 1; +				break; +			case 0x3: //channel 1 count register +				if (flipflop == 1) dmachan[1].reload = (dmachan[1].reload & 0x00FF) | ( (uint32_t) value << 8); +				else dmachan[1].reload = (dmachan[1].reload & 0xFF00) | value; +				if (flipflop == 1) { +						if (dmachan[1].reload == 0) dmachan[1].reload = 65536; +						dmachan[1].count = 0; +#ifdef DEBUG_DMA +						printf ("[NOTICE] DMA channel 1 reload register = %04X\n", dmachan[1].reload); +#endif +					} +				flipflop = ~flipflop & 1; +				break; +			case 0xA: //write single mask register +				channel = value & 3; +				dmachan[channel].masked = (value >> 2) & 1; +#ifdef DEBUG_DMA +				printf ("[NOTICE] DMA channel %u masking = %u\n", channel, dmachan[channel].masked); +#endif +				break; +			case 0xB: //write mode register +				channel = value & 3; +				dmachan[channel].direction = (value >> 5) & 1; +				dmachan[channel].autoinit = (value >> 4) & 1; +				dmachan[channel].writemode = (value >> 2) & 1; //not quite accurate +#ifdef DEBUG_DMA +				printf ("[NOTICE] DMA channel %u write mode reg: direction = %u, autoinit = %u, write mode = %u\n", +				        channel, dmachan[channel].direction, dmachan[channel].autoinit, dmachan[channel].writemode); +#endif +				break; +			case 0xC: //clear byte pointer flip-flop +#ifdef DEBUG_DMA +				printf ("[NOTICE] DMA cleared byte pointer flip-flop\n"); +#endif +				flipflop = 0; +				break; +			case 0x83: //DMA channel 1 page register +				dmachan[1].page = (uint32_t) value << 16; +#ifdef DEBUG_DMA +				printf ("[NOTICE] DMA channel 1 page base = %05X\n", dmachan[1].page); +#endif +				break; +		} +} + +uint8_t in8237 (uint16_t addr) { +#ifdef DEBUG_DMA +	printf ("in8237(0x%X);\n", addr); +#endif +	switch (addr) { +		case 3: +			if (flipflop == 1) return(dmachan[1].reload >> 8); +			else return(dmachan[1].reload); +			flipflop = ~flipflop & 1; +			break; +	} +	return (0); +} + +void init8237() { +	memset (dmachan, 0, sizeof (dmachan) ); + +	set_port_write_redirector (0x00, 0x0F, &out8237); +	set_port_read_redirector (0x00, 0x0F, &in8237); + +	set_port_write_redirector (0x80, 0x8F, &out8237); +	set_port_read_redirector (0x80, 0x8F, &in8237); +} diff --git a/src/fake86/i8237.h b/src/fake86/i8237.h new file mode 100755 index 0000000..ea2e24e --- /dev/null +++ b/src/fake86/i8237.h @@ -0,0 +1,31 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2012 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +#include <stdint.h> + +struct dmachan_s { +	uint32_t page; +	uint32_t addr; +	uint32_t reload; +	uint32_t count; +	uint8_t direction; +	uint8_t autoinit; +	uint8_t writemode; +	uint8_t masked; +}; diff --git a/src/fake86/i8253.c b/src/fake86/i8253.c new file mode 100755 index 0000000..49d30e1 --- /dev/null +++ b/src/fake86/i8253.c @@ -0,0 +1,94 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* i8253.c: functions to emulate the Intel 8253 programmable interval timer. +   these are required for the timer interrupt and PC speaker to be +   properly emulated! */ + +#include <stdint.h> +#include <stdio.h> +#include <memory.h> +#include "i8253.h" +#include "mutex.h" + +extern void set_port_write_redirector (uint16_t startport, uint16_t endport, void *callback); +extern void set_port_read_redirector (uint16_t startport, uint16_t endport, void *callback); + +struct i8253_s i8253; + +extern uint64_t hostfreq, lasttick, curtick, tickgap, totalexec; + +void out8253 (uint16_t portnum, uint8_t value) { +	uint8_t curbyte; +	portnum &= 3; +	switch (portnum) { +			case 0: +			case 1: +			case 2: //channel data +				if ( (i8253.accessmode[portnum] == PIT_MODE_LOBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 0) ) ) curbyte = 0; +				else if ( (i8253.accessmode[portnum] == PIT_MODE_HIBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 1) ) ) curbyte = 1; +				if (curbyte == 0) { //low byte +						i8253.chandata[portnum] = (i8253.chandata[portnum] & 0xFF00) | value; +					} +				else {   //high byte +						i8253.chandata[portnum] = (i8253.chandata[portnum] & 0x00FF) | ( (uint16_t) value << 8); +					} +				if (i8253.chandata[portnum] == 0) i8253.effectivedata[portnum] = 65536; +				else i8253.effectivedata[portnum] = i8253.chandata[portnum]; +				i8253.active[portnum] = 1; +				tickgap = (uint64_t) ( (float) hostfreq / (float) ( (float) 1193182 / (float) i8253.effectivedata[0]) ); +				if (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) i8253.bytetoggle[portnum] = (~i8253.bytetoggle[portnum]) & 1; +				i8253.chanfreq[portnum] = (float) ( (uint32_t) ( ( (float) 1193182.0 / (float) i8253.effectivedata[portnum]) * (float) 1000.0) ) / (float) 1000.0; +				//printf("[DEBUG] PIT channel %u counter changed to %u (%f Hz)\n", portnum, i8253.chandata[portnum], i8253.chanfreq[portnum]); +				break; +			case 3: //mode/command +				i8253.accessmode[value>>6] = (value >> 4) & 3; +				if (i8253.accessmode[value>>6] == PIT_MODE_TOGGLE) i8253.bytetoggle[value>>6] = 0; +				break; +		} +} + +uint8_t in8253 (uint16_t portnum) { +	uint8_t curbyte; +	portnum &= 3; +	switch (portnum) { +			case 0: +			case 1: +			case 2: //channel data +				if ( (i8253.accessmode[portnum] == 0) || (i8253.accessmode[portnum] == PIT_MODE_LOBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 0) ) ) curbyte = 0; +				else if ( (i8253.accessmode[portnum] == PIT_MODE_HIBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 1) ) ) curbyte = 1; +				if ( (i8253.accessmode[portnum] == 0) || (i8253.accessmode[portnum] == PIT_MODE_LOBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 0) ) ) curbyte = 0; +				else if ( (i8253.accessmode[portnum] == PIT_MODE_HIBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 1) ) ) curbyte = 1; +				if ( (i8253.accessmode[portnum] == 0) || (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) ) i8253.bytetoggle[portnum] = (~i8253.bytetoggle[portnum]) & 1; +				if (curbyte == 0) { //low byte +						return ( (uint8_t) i8253.counter[portnum]); +					} +				else {   //high byte +						return ( (uint8_t) (i8253.counter[portnum] >> 8) ); +					} +				break; +		} +	return (0); +} + +void init8253() { +	memset (&i8253, 0, sizeof (i8253) ); +	set_port_write_redirector (0x40, 0x43, &out8253); +	set_port_read_redirector (0x40, 0x43, &in8253); +} diff --git a/src/fake86/i8253.h b/src/fake86/i8253.h new file mode 100755 index 0000000..de28ccd --- /dev/null +++ b/src/fake86/i8253.h @@ -0,0 +1,33 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2012 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +#define PIT_MODE_LATCHCOUNT	0 +#define PIT_MODE_LOBYTE	1 +#define PIT_MODE_HIBYTE	2 +#define PIT_MODE_TOGGLE	3 + +struct i8253_s { +	uint16_t chandata[3]; +	uint8_t accessmode[3]; +	uint8_t bytetoggle[3]; +	uint32_t effectivedata[3]; +	float chanfreq[3]; +	uint8_t active[3]; +	uint16_t counter[3]; +}; diff --git a/src/fake86/i8259.c b/src/fake86/i8259.c new file mode 100755 index 0000000..97a297a --- /dev/null +++ b/src/fake86/i8259.c @@ -0,0 +1,99 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* i8259.c: functions to emulate the Intel 8259 prioritized interrupt controller. +   note: this is not a very complete 8259 implementation, but for the purposes +   of a PC, it's all we need. */ + +#include <stdint.h> +#include <string.h> +#include "i8259.h" + +struct structpic i8259; + +extern uint8_t keyboardwaitack; + +extern void set_port_write_redirector(uint16_t startport, uint16_t endport, void *callback); +extern void set_port_read_redirector(uint16_t startport, uint16_t endport, void *callback); + +uint8_t in8259(uint16_t portnum) { +	switch (portnum & 1) { +		   case 0: +			if (i8259.readmode==0) return(i8259.irr); else return(i8259.isr); +		   case 1: //read mask register +			return(i8259.imr); +	} +	return (0); +} + +extern uint32_t makeupticks; +void out8259(uint16_t portnum, uint8_t value) { +	 uint8_t i; +	 switch (portnum & 1) { +		case 0: +		 if (value & 0x10) { //begin initialization sequence +			i8259.icwstep = 1; +			i8259.imr = 0; //clear interrupt mask register +			i8259.icw[i8259.icwstep++] = value; +			return; +		 } +		 if ((value & 0x98)==8) { //it's an OCW3 +			if (value & 2) i8259.readmode = value & 2; +		 } +		 if (value & 0x20) { //EOI command +	keyboardwaitack = 0; +			for (i=0; i<8; i++) +			if ((i8259.isr >> i) & 1) { +			   i8259.isr ^= (1 << i); +			   if ((i==0) && (makeupticks>0)) { makeupticks = 0; i8259.irr |= 1; } +			   return; +			} +		 } +		 break; +		case 1: +		 if ((i8259.icwstep==3) && (i8259.icw[1] & 2)) i8259.icwstep = 4; //single mode, so don't read ICW3 +		 if (i8259.icwstep<5) { i8259.icw[i8259.icwstep++] = value; return; } +		 //if we get to this point, this is just a new IMR value +		 i8259.imr = value; +		 break; +	 } +} + +uint8_t nextintr() { +	uint8_t i, tmpirr; +	tmpirr = i8259.irr & (~i8259.imr); //XOR request register with inverted mask register +	for (i=0; i<8; i++) +		if ((tmpirr >> i) & 1) { +		   i8259.irr ^= (1 << i); +		   i8259.isr |= (1 << i); +		   return(i8259.icw[2] + i); +		} +	return(0); //this won't be reached, but without it the compiler gives a warning +} + +void doirq(uint8_t irqnum) { +	 i8259.irr |= (1 << irqnum); +	 if (irqnum == 1) keyboardwaitack = 1; +} + +void init8259() { +	 memset((void *)&i8259, 0, sizeof(i8259)); +	 set_port_write_redirector(0x20, 0x21, &out8259); +	 set_port_read_redirector(0x20, 0x21, &in8259); +} diff --git a/src/fake86/i8259.h b/src/fake86/i8259.h new file mode 100755 index 0000000..4056184 --- /dev/null +++ b/src/fake86/i8259.h @@ -0,0 +1,31 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2012 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +struct structpic { +	uint8_t imr; //mask register +	uint8_t irr; //request register +	uint8_t isr; //service register +	uint8_t icwstep; //used during initialization to keep track of which ICW we're at +	uint8_t icw[5]; +	uint8_t intoffset; //interrupt vector offset +	uint8_t priority; //which IRQ has highest priority +	uint8_t autoeoi; //automatic EOI mode +	uint8_t readmode; //remember what to return on read register from OCW3 +	uint8_t enabled; +}; diff --git a/src/fake86/input.c b/src/fake86/input.c new file mode 100755 index 0000000..f92428d --- /dev/null +++ b/src/fake86/input.c @@ -0,0 +1,361 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* input.c: functions for translation of SDL scancodes to BIOS scancodes, +   and handling of SDL events in general. */ + +#include <SDL/SDL.h> +#include <stdint.h> +#include "sermouse.h" + +uint8_t keydown[0x100], keyboardwaitack = 0; +extern uint32_t usegrabmode; + +extern void doirq (uint8_t irqnum); +extern uint8_t running, portram[0x10000]; +extern SDL_Surface *screen; + +uint8_t translatescancode (uint16_t keyval) { +	switch (keyval) { +			case 0x1B: +				return (1); +				break; //Esc +			case 0x30: +				return (0xB); +				break; //zero +			case 0x31: +			case 0x32: +			case 0x33: +			case 0x34: +			case 0x35: +			case 0x36: +			case 0x37: +			case 0x38: +			case 0x39: +				return (keyval - 0x2F); +				break; //other number keys +			case 0x2D: +				return (0xC); +				break; //-_ +			case 0x3D: +				return (0xD); +				break; //=+ +			case 0x8: +				return (0xE); +				break; //backspace +			case 0x9: +				return (0xF); +				break; //tab +			case 0x71: +				return (0x10); +				break; +			case 0x77: +				return (0x11); +				break; +			case 0x65: +				return (0x12); +				break; +			case 0x72: +				return (0x13); +				break; +			case 0x74: +				return (0x14); +				break; +			case 0x79: +				return (0x15); +				break; +			case 0x75: +				return (0x16); +				break; +			case 0x69: +				return (0x17); +				break; +			case 0x6F: +				return (0x18); +				break; +			case 0x70: +				return (0x19); +				break; +			case 0x5B: +				return (0x1A); +				break; +			case 0x5D: +				return (0x1B); +				break; +			case 0xD: +			case 0x10F: +				return (0x1C); +				break; //enter +			case 0x131: +			case 0x132: +				return (0x1D); +				break; //ctrl +			case 0x61: +				return (0x1E); +				break; +			case 0x73: +				return (0x1F); +				break; +			case 0x64: +				return (0x20); +				break; +			case 0x66: +				return (0x21); +				break; +			case 0x67: +				return (0x22); +				break; +			case 0x68: +				return (0x23); +				break; +			case 0x6A: +				return (0x24); +				break; +			case 0x6B: +				return (0x25); +				break; +			case 0x6C: +				return (0x26); +				break; +			case 0x3B: +				return (0x27); +				break; +			case 0x27: +				return (0x28); +				break; +			case 0x60: +				return (0x29); +				break; +			case 0x130: +				return (0x2A); +				break; //left shift +			case 0x5C: +				return (0x2B); +				break; +			case 0x7A: +				return (0x2C); +				break; +			case 0x78: +				return (0x2D); +				break; +			case 0x63: +				return (0x2E); +				break; +			case 0x76: +				return (0x2F); +				break; +			case 0x62: +				return (0x30); +				break; +			case 0x6E: +				return (0x31); +				break; +			case 0x6D: +				return (0x32); +				break; +			case 0x2C: +				return (0x33); +				break; +			case 0x2E: +				return (0x34); +				break; +			case 0x2F: +				return (0x35); +				break; +			case 0x12F: +				return (0x36); +				break; //right shift +			case 0x13C: +				return (0x37); +				break; //print screen +			case 0x133: +			case 0x134: +				return (0x38); +				break; //alt +			case 0x20: +				return (0x39); +				break; //space +			case 0x12D: +				return (0x3A); +				break; //caps lock +			case 0x11A: +			case 0x11B: +			case 0x11C: +			case 0x11D: +			case 0x11E: +			case 0x11F: +			case 0x120: +			case 0x121: +			case 0x122: +			case 0x123: +				return (keyval - 0x11A + 0x3B); +				break; //F1 to F10 +			case 0x12C: +				return (0x45); +				break; //num lock +			case 0x12E: +				return (0x46); +				break; //scroll lock +			case 0x116: +			case 0x107: +				return (0x47); +				break; //home +			case 0x111: +			case 0x108: +				return (0x48); +				break; //up +			case 0x118: +			case 0x109: +				return (0x49); +				break; //pgup +			case 0x10D: +				return (0x4A); +				break; //keypad - +			case 0x114: +			case 0x104: +				return (0x4B); +				break; //left +			case 0x105: +				return (0x4C); +				break; //center +			case 0x113: +			case 0x106: +				return (0x4D); +				break; //right +			case 0x10E: +				return (0x4E); +				break; //keypad + +			case 0x117: +			case 0x101: +				return (0x4F); +				break; //end +			case 0x112: +			case 0x102: +				return (0x50); +				break; //down +			case 0x119: +			case 0x103: +				return (0x51); +				break; //pgdn +			case 0x115: +			case 0x100: +				return (0x52); +				break; //ins +			case 0x7F: +			case 0x10A: +				return (0x53); +				break; //del +			default: +				return (0); +		} +} + +uint8_t buttons = 0; +extern void sermouseevent (uint8_t buttons, int8_t xrel, int8_t yrel); +extern struct sermouse_s sermouse; +extern void setwindowtitle (uint8_t *extra); + +void mousegrabtoggle() { +	if (usegrabmode == SDL_GRAB_ON) { +			usegrabmode = SDL_GRAB_OFF; +			SDL_WM_GrabInput (SDL_GRAB_OFF); +			SDL_ShowCursor (SDL_ENABLE); +			setwindowtitle (""); +		} +	else { +			usegrabmode = SDL_GRAB_ON; +			SDL_WM_GrabInput (SDL_GRAB_ON); +			SDL_ShowCursor (SDL_DISABLE); +			setwindowtitle (" (press Ctrl + Alt to release mouse)"); +		} +} + +extern uint8_t scrmodechange; +extern uint32_t usefullscreen; +void handleinput() { +	SDL_Event event; +	int mx = 0, my = 0; +	uint8_t tempbuttons; +	if (SDL_PollEvent (&event) ) { +			switch (event.type) { +					case SDL_KEYDOWN: +						portram[0x60] = translatescancode (event.key.keysym.sym); +						portram[0x64] |= 2; +						doirq (1); +						//printf("%02X\n", translatescancode(event.key.keysym.sym)); +						keydown[translatescancode (event.key.keysym.sym) ] = 1; +						if (keydown[0x38] && keydown[0x1D] && (SDL_WM_GrabInput (SDL_GRAB_QUERY) == SDL_GRAB_ON) ) { +								keydown[0x1D] = 0; +								keydown[0x32] = 0; +								mousegrabtoggle(); +								break; +							} +						if (keydown[0x38] && keydown[0x1C]) { +								keydown[0x1D] = 0; +								keydown[0x38] = 0; +								if (usefullscreen) usefullscreen = 0; +								else usefullscreen = SDL_FULLSCREEN; +								scrmodechange = 1; +								break; +							} +						break; +					case SDL_KEYUP: +						portram[0x60] = translatescancode (event.key.keysym.sym) | 0x80; +						portram[0x64] |= 2; +						doirq (1); +						keydown[translatescancode (event.key.keysym.sym) ] = 0; +						break; +					case SDL_MOUSEBUTTONDOWN: +						if (SDL_WM_GrabInput (SDL_GRAB_QUERY) == SDL_GRAB_OFF) { +								mousegrabtoggle(); +								break; +							} +						tempbuttons = SDL_GetMouseState (NULL, NULL); +						if (tempbuttons & 1) buttons = 2; +						else buttons = 0; +						if (tempbuttons & 4) buttons |= 1; +						sermouseevent (buttons, 0, 0); +						break; +					case SDL_MOUSEBUTTONUP: +						if (SDL_WM_GrabInput (SDL_GRAB_QUERY) == SDL_GRAB_OFF) break; +						tempbuttons = SDL_GetMouseState (NULL, NULL); +						if (tempbuttons & 1) buttons = 2; +						else buttons = 0; +						if (tempbuttons & 4) buttons |= 1; +						sermouseevent (buttons, 0, 0); +						break; +					case SDL_MOUSEMOTION: +						if (SDL_WM_GrabInput (SDL_GRAB_QUERY) == SDL_GRAB_OFF) break; +						SDL_GetRelativeMouseState (&mx, &my); +						sermouseevent (buttons, (int8_t) mx, (int8_t) my); +						SDL_WarpMouse (screen->w / 2, screen->h / 2); +						while (1) { +								SDL_PollEvent (&event); +								SDL_GetRelativeMouseState (&mx, &my); +								if ( (mx == 0) && (my == 0) ) break; +							} +						break; +					case SDL_QUIT: +						running = 0; +						break; +					default: +						break; +				} +		} +} diff --git a/src/fake86/main.c b/src/fake86/main.c new file mode 100755 index 0000000..78b3c43 --- /dev/null +++ b/src/fake86/main.c @@ -0,0 +1,321 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* main.c: functions to initialize the different components of Fake86, +   load ROM binaries, and kickstart the CPU emulator. */ + +#include "config.h" +#ifdef __APPLE__      /* Memory leaks occur in OS X when the SDL window gets */ +#include <SDL/SDL.h>  /* resized if SDL.h not included in file with main() */ +#endif +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <memory.h> +#include "mutex.h" +#ifdef _WIN32 +CRITICAL_SECTION screenmutex; +#else +#ifndef __APPLE__ +#include <X11/Xlib.h> +#endif +pthread_t consolethread; +#endif + +const uint8_t *build = BUILD_STRING; + +extern uint8_t RAM[0x100000], readonly[0x100000]; +extern uint8_t running, renderbenchmark; + +extern void reset86(); +extern void exec86 (uint32_t execloops); +extern uint8_t initscreen (uint8_t *ver); +extern void VideoThread(); +extern doscrmodechange(); +extern void handleinput(); + +#ifdef CPU_ADDR_MODE_CACHE +extern uint64_t cached_access_count, uncached_access_count; +#endif + +extern uint8_t scrmodechange, doaudio; +extern uint64_t totalexec, totalframes; +uint64_t starttick, endtick, lasttick; + +uint8_t *biosfile = NULL, verbose = 0, cgaonly = 0, useconsole = 0; +uint32_t speed = 0; + + +uint32_t loadbinary (uint32_t addr32, uint8_t *filename, uint8_t roflag) { +	FILE *binfile = NULL; +	uint32_t readsize; + +	binfile = fopen (filename, "rb"); +	if (binfile == NULL) { +			return (0); +		} + +	fseek (binfile, 0, SEEK_END); +	readsize = ftell (binfile); +	fseek (binfile, 0, SEEK_SET); +	fread ( (void *) &RAM[addr32], 1, readsize, binfile); +	fclose (binfile); + +	memset ( (void *) &readonly[addr32], roflag, readsize); +	return (readsize); +} + +uint32_t loadrom (uint32_t addr32, uint8_t *filename, uint8_t failure_fatal) { +	uint32_t readsize; +	readsize = loadbinary (addr32, filename, 1); +	if (!readsize) { +			if (failure_fatal) printf("FATAL: "); +			else printf("WARNING: "); +			printf ("Unable to load %s\n", filename); +			return (0); +		} +	else { +			printf ("Loaded %s at 0x%05X (%lu KB)\n", filename, addr32, readsize >> 10); +			return (readsize); +		} +} + +uint32_t loadbios (uint8_t *filename) { +	FILE *binfile = NULL; +	uint32_t readsize; + +	binfile = fopen (filename, "rb"); +	if (binfile == NULL) { +			return (0); +		} + +	fseek (binfile, 0, SEEK_END); +	readsize = ftell (binfile); +	fseek (binfile, 0, SEEK_SET); +	fread ( (void *) &RAM[0x100000 - readsize], 1, readsize, binfile); +	fclose (binfile); + +	memset ( (void *) &readonly[0x100000 - readsize], 1, readsize); +	return (readsize); +} + +extern uint32_t SDL_GetTicks(); +extern uint8_t insertdisk (uint8_t drivenum, char *filename); +extern void ejectdisk (uint8_t drivenum); +extern uint8_t bootdrive, ethif, net_enabled; +extern void doirq (uint8_t irqnum); +//extern void isa_ne2000_init(int baseport, uint8_t irq); +extern void parsecl (int argc, char *argv[]); +void timing(); +void tickaudio(); +void inittiming(); +void initaudio(); +void init8253(); +void init8259(); +extern void init8237(); +extern void initVideoPorts(); +extern void killaudio(); +extern void initsermouse (uint16_t baseport, uint8_t irq); +extern void *port_write_callback[0x10000]; +extern void *port_read_callback[0x10000]; +extern void *port_write_callback16[0x10000]; +extern void *port_read_callback16[0x10000]; +extern void initadlib (uint16_t baseport); +extern void initsoundsource(); +extern void isa_ne2000_init (uint16_t baseport, uint8_t irq); +extern void initBlaster (uint16_t baseport, uint8_t irq); + +#ifdef NETWORKING_ENABLED +extern void initpcap(); +extern void dispatch(); +#endif + +void printbinary (uint8_t value) { +	int8_t curbit; + +	for (curbit=7; curbit>=0; curbit--) { +			if ( (value >> curbit) & 1) printf ("1"); +			else printf ("0"); +		} +} + +uint8_t usessource = 0; +void inithardware() { +#ifdef NETWORKING_ENABLED +	if (ethif != 254) initpcap(); +#endif +	printf ("Initializing emulated hardware:\n"); +	memset (port_write_callback, 0, sizeof (port_write_callback) ); +	memset (port_read_callback, 0, sizeof (port_read_callback) ); +	memset (port_write_callback16, 0, sizeof (port_write_callback16) ); +	memset (port_read_callback16, 0, sizeof (port_read_callback16) ); +	printf ("  - Intel 8253 timer: "); +	init8253(); +	printf ("OK\n"); +	printf ("  - Intel 8259 interrupt controller: "); +	init8259(); +	printf ("OK\n"); +	printf ("  - Intel 8237 DMA controller: "); +	init8237(); +	printf ("OK\n"); +	initVideoPorts(); +	if (usessource) { +			printf ("  - Disney Sound Source: "); +			initsoundsource(); +			printf ("OK\n"); +		} +#ifndef NETWORKING_OLDCARD +	printf ("  - Novell NE2000 ethernet adapter: "); +	isa_ne2000_init (0x300, 6); +	printf ("OK\n"); +#endif +	printf ("  - Adlib FM music card: "); +	initadlib (0x388); +	printf ("OK\n"); +	printf ("  - Creative Labs Sound Blaster 2.0: "); +	initBlaster (0x220, 7); +	printf ("OK\n"); +	printf ("  - Serial mouse (Microsoft compatible): "); +	initsermouse (0x3F8, 4); +	printf ("OK\n"); +	if (doaudio) initaudio(); +	inittiming(); +	initscreen ( (uint8_t *) build); +} + +uint8_t dohardreset = 0; +uint8_t audiobufferfilled(); + +#ifdef _WIN32 +void initmenus(); +void EmuThread (void *dummy) { +#else +pthread_t emuthread; +void *EmuThread (void *dummy) { +#endif +	while (running) { +			if (!speed) exec86 (10000); +			else { +				exec86(speed / 100); +				while (!audiobufferfilled()) { +					timing(); +					tickaudio(); +				} +#ifdef _WIN32 +				Sleep(10); +#else +				usleep(10000); +#endif +			} +			if (scrmodechange) doscrmodechange(); +			if (dohardreset) { +				reset86(); +				dohardreset = 0; +			} +		} +} + +#ifdef _WIN32 +void runconsole (void *dummy); +#else +void *runconsole (void *dummy); +#endif +extern void bufsermousedata (uint8_t value); +int main (int argc, char *argv[]) { +	uint32_t biossize; + +	printf ("%s (c)2010-2013 Mike Chambers\n", build); +	printf ("[A portable, open-source 8086 PC emulator]\n\n"); + +	parsecl (argc, argv); + +	memset (readonly, 0, 0x100000); +	biossize = loadbios (biosfile); +	if (!biossize) return (-1); +#ifdef DISK_CONTROLLER_ATA +	if (!loadrom (0xD0000UL, PATH_DATAFILES "ide_xt.bin", 1) ) return (-1); +#endif +	if (biossize <= 8192) { +		loadrom (0xF6000UL, PATH_DATAFILES "rombasic.bin", 0); +		if (!loadrom (0xC0000UL, PATH_DATAFILES "videorom.bin", 1) ) return (-1); +	} +	printf ("\nInitializing CPU... "); +	running = 1; +	reset86(); +	printf ("OK!\n"); + +#ifndef _WIN32 +#ifndef __APPLE__ +	XInitThreads(); +#endif +#endif +	inithardware(); + +#ifdef _WIN32 +	initmenus(); +	InitializeCriticalSection (&screenmutex); +#endif +	if (useconsole) { +#ifdef _WIN32 +			_beginthread (runconsole, 0, NULL); +#else +			pthread_create (&consolethread, NULL, (void *) runconsole, NULL); +#endif +		} + +#ifdef _WIN32 +			_beginthread (EmuThread, 0, NULL); +#else +			pthread_create (&emuthread, NULL, (void *) EmuThread, NULL); +#endif + +	lasttick = starttick = SDL_GetTicks(); +	while (running) { +			handleinput(); +#ifdef NETWORKING_ENABLED +			if (ethif < 254) dispatch(); +#endif +#ifdef _WIN32 +			Sleep(1); +#else +			usleep(1000); +#endif +	} +	endtick = (SDL_GetTicks() - starttick) / 1000; +	if (endtick == 0) endtick = 1; //avoid divide-by-zero exception in the code below, if ran for less than 1 second + +	killaudio(); + +	if (renderbenchmark) { +			printf ("\n%llu frames rendered in %llu seconds.\n", totalframes, endtick); +			printf ("Average framerate: %llu FPS.\n", totalframes / endtick); +		} + +	printf ("\n%llu instructions executed in %llu seconds.\n", totalexec, endtick); +	printf ("Average speed: %llu instructions/second.\n", totalexec / endtick); + +#ifdef CPU_ADDR_MODE_CACHE +	printf ("\n  Cached modregrm data access count: %llu\n", cached_access_count); +	printf ("Uncached modregrm data access count: %llu\n", uncached_access_count); +#endif + +	if (useconsole)	exit (0); //makes sure console thread quits even if blocking + +	return (0); +} diff --git a/src/fake86/modregrm.h b/src/fake86/modregrm.h new file mode 100644 index 0000000..026e030 --- /dev/null +++ b/src/fake86/modregrm.h @@ -0,0 +1,129 @@ +#include "config.h" + +#ifdef CPU_ADDR_MODE_CACHE +struct addrmodecache_s addrcache[0x100000]; +uint8_t addrcachevalid[0x100000]; + +uint32_t addrdatalen, dataisvalid, setvalidptr; +uint64_t cached_access_count = 0, uncached_access_count = 0; +#define modregrm() { \ +	tempaddr32 = (((uint32_t)savecs << 4) + ip) & 0xFFFFF; \ +	if (addrcachevalid[tempaddr32]) { \ +		switch (addrcache[tempaddr32].len) { \ +			case 0: \ +				dataisvalid = 1; \ +				break; \ +			case 1: \ +				if (addrcachevalid[tempaddr32+1]) dataisvalid = 1; else dataisvalid = 0; \ +				break; \ +			case 2: \ +				if (addrcachevalid[tempaddr32+1] && addrcachevalid[tempaddr32+2]) dataisvalid = 1; else dataisvalid = 0; \ +				break; \ +		} \ +	} else dataisvalid = 0; \ +	if (dataisvalid) { \ +		cached_access_count++; \ +		disp16 = addrcache[tempaddr32].disp16; \ +		segregs[regcs] = addrcache[tempaddr32].exitcs; \ +		ip = addrcache[tempaddr32].exitip; \ +		mode = addrcache[tempaddr32].mode; \ +		reg = addrcache[tempaddr32].reg; \ +		rm = addrcache[tempaddr32].rm; \ +		if ((!segoverride) && addrcache[tempaddr32].forcess) useseg = segregs[regss]; \ +	} else { \ +		uncached_access_count++; \ +		addrbyte = getmem8(segregs[regcs], ip); \ +		StepIP(1); \ +		mode = addrbyte >> 6; \ +		reg = (addrbyte >> 3) & 7; \ +		rm = addrbyte & 7; \ +		addrdatalen = 0; \ +		addrcache[tempaddr32].forcess = 0; \ +		switch(mode) \ +		{ \ +		case 0: \ +		if(rm == 6) { \ +		disp16 = getmem16(segregs[regcs], ip); \ +		addrdatalen = 2; \ +		StepIP(2); \ +		} \ +		if (((rm == 2) || (rm == 3))) { \ +		if (!segoverride) useseg = segregs[regss]; \ +		addrcache[tempaddr32].forcess = 1; \ +		} \ +		break; \ +	\ +		case 1: \ +		disp16 = signext(getmem8(segregs[regcs], ip)); \ +		addrdatalen = 1; \ +		StepIP(1); \ +		if (((rm == 2) || (rm == 3) || (rm == 6))) { \ +		if (!segoverride) useseg = segregs[regss]; \ +		addrcache[tempaddr32].forcess = 1; \ +		} \ +		break; \ +	\ +		case 2: \ +		disp16 = getmem16(segregs[regcs], ip); \ +		addrdatalen = 2; \ +		StepIP(2); \ +		if (((rm == 2) || (rm == 3) || (rm == 6))) { \ +		if (!segoverride) useseg = segregs[regss]; \ +		addrcache[tempaddr32].forcess = 1; \ +		} \ +		break; \ +	\ +		default: \ +		disp16 = 0; \ +		} \ +		addrcache[tempaddr32].disp16 = disp16; \ +		addrcache[tempaddr32].exitcs = segregs[regcs]; \ +		addrcache[tempaddr32].exitip = ip; \ +		addrcache[tempaddr32].mode = mode; \ +		addrcache[tempaddr32].reg = reg; \ +		addrcache[tempaddr32].rm = rm; \ +		addrcache[tempaddr32].len = addrdatalen; \ +		memset(&addrcachevalid[tempaddr32], 1, addrdatalen+1); \ +	} \ +} +#else +#define modregrm() { \ +	addrbyte = getmem8(segregs[regcs], ip); \ +	StepIP(1); \ +	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; \ +	} \ +} +#endif
\ No newline at end of file diff --git a/src/fake86/mutex.h b/src/fake86/mutex.h new file mode 100755 index 0000000..e0f665d --- /dev/null +++ b/src/fake86/mutex.h @@ -0,0 +1,29 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2012 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +#ifdef _WIN32 +    #include <Windows.h> +    #include <process.h> +    #define MutexLock(mutex) EnterCriticalSection(&mutex) +    #define MutexUnlock(mutex) LeaveCriticalSection(&mutex) +#else +    #include <pthread.h> +    #define MutexLock(mutex) pthread_mutex_lock(&mutex) +    #define MutexUnlock(mutex) pthread_mutex_unlock(&mutex) +#endif diff --git a/src/fake86/netcard.c b/src/fake86/netcard.c new file mode 100755 index 0000000..ab7d6d5 --- /dev/null +++ b/src/fake86/netcard.c @@ -0,0 +1,83 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* netcard.c: this is a simple custom ethernet adapter interface for Fake86. +   it's not modeled after a real-world ethernet adapter, thus i had to create +   own DOS packet driver for programs to make use of it. this packet driver +   is included with this source code, in the data/ directory. the filename is +   pd.com - inject this file into any floppy or hard disk image if needed! */ + +#include "config.h" +#ifdef NETWORKING_ENABLED +#ifdef NETWORKING_OLDCARD +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include "cpu.h" + +extern uint8_t verbose; +extern union _bytewordregs_ regs; +extern uint8_t	RAM[0x100000], readonly[0x100000], ethif; +extern uint16_t	segregs[4]; +extern void sendpkt (uint8_t *src, uint16_t len); + +struct netstruct { +	uint8_t enabled; +	uint8_t canrecv; +	uint16_t pktlen; +} net; + +void nethandler() { +	uint32_t i; +	if (ethif==254) return; //networking not enabled +	switch (regs.byteregs[regah]) { //function number +			case 0x00: //enable packet reception +				net.enabled = 1; +				net.canrecv = 1; +				return; +			case 0x01: //send packet of CX at DS:SI +				if (verbose) { +						printf ("Sending packet of %u bytes.\n", regs.wordregs[regcx]); +					} +				sendpkt (&RAM[ ( (uint32_t) segregs[regds] << 4) + (uint32_t) regs.wordregs[regsi]], regs.wordregs[regcx]); +				return; +			case 0x02: //return packet info (packet buffer in DS:SI, length in CX) +				segregs[regds] = 0xD000; +				regs.wordregs[regsi] = 0x0000; +				regs.wordregs[regcx] = net.pktlen; +				return; +			case 0x03: //copy packet to final destination (given in ES:DI) +				memcpy (&RAM[ ( (uint32_t) segregs[reges] << 4) + (uint32_t) regs.wordregs[regdi]], &RAM[0xD0000], net.pktlen); +				return; +			case 0x04: //disable packets +				net.enabled = 0; +				net.canrecv = 0; +				return; +			case 0x05: //DEBUG: dump packet (DS:SI) of CX bytes to stdout +				for (i=0; i<regs.wordregs[regcx]; i++) { +						printf ("%c", RAM[ ( (uint32_t) segregs[regds] << 4) + (uint32_t) regs.wordregs[regsi] + i]); +					} +				return; +			case 0x06: //DEBUG: print milestone string +				//print("PACKET DRIVER MILESTONE REACHED\n"); +				return; +		} +} +#endif +#endif diff --git a/src/fake86/packet.c b/src/fake86/packet.c new file mode 100755 index 0000000..38ce12e --- /dev/null +++ b/src/fake86/packet.c @@ -0,0 +1,185 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* packet.c: functions to interface with libpcap/winpcap for ethernet emulation. */ + +#include "config.h" + +#ifdef NETWORKING_ENABLED +#define HAVE_REMOTE +#define WPCAP +#include <pcap.h> +#include <stdint.h> +#include <stdlib.h> +#include <memory.h> + +#ifndef _WIN32 +#define PCAP_OPENFLAG_PROMISCUOUS 1 +#endif + +extern uint8_t RAM[0x100000]; +extern uint8_t verbose; +uint8_t ethif, net_enabled = 0; +uint8_t dopktrecv = 0; +uint16_t rcvseg, rcvoff, hdrlen, handpkt; + +pcap_if_t *alldevs; +pcap_if_t *d; +pcap_t *adhandle; +const u_char *pktdata; +struct pcap_pkthdr *hdr; +int inum; +uint16_t curhandle = 0; +char errbuf[PCAP_ERRBUF_SIZE]; +uint8_t maclocal[6] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x13, 0x37 }; + +void initpcap() { +	int i=0; + +	printf ("\nObtaining NIC list via libpcap...\n"); + +	/* Retrieve the device list from the local machine */ +#if defined(_WIN32) +	if (pcap_findalldevs_ex (PCAP_SRC_IF_STRING, NULL /* auth is not needed */, &alldevs, errbuf) == -1) +#else +	if (pcap_findalldevs (&alldevs, errbuf) == -1) +#endif +		{ +			printf ("Error in pcap_findalldevs_ex: %s\n", errbuf); +			exit (1); +		} + +	/* Print the list */ +	for (d= alldevs; d != NULL; d= d->next) { +			i++; +			if (ethif==255) { +					printf ("%d. %s", i, d->name); +					if (d->description) { +							printf (" (%s)\n", d->description); +						} +					else { +							printf (" (No description available)\n"); +						} +				} +		} + +	if (i == 0) { +			printf ("\nNo interfaces found! Make sure WinPcap is installed.\n"); +			return; +		} + +	printf ("\n"); + +	if (ethif==255) exit (0); +	else inum = ethif; +	printf ("Using network interface %u.\n", ethif); + + +	if (inum < 1 || inum > i) { +			printf ("\nInterface number out of range.\n"); +			/* Free the device list */ +			pcap_freealldevs (alldevs); +			return; +		} + +	/* Jump to the selected adapter */ +	for (d=alldevs, i=0; i< inum-1 ; d=d->next, i++); + +	/* Open the device */ +#ifdef _WIN32 +	if ( (adhandle= pcap_open (d->name, 65536, PCAP_OPENFLAG_PROMISCUOUS, -1, NULL, errbuf) ) == NULL) +#else +	if ( (adhandle= pcap_open_live (d->name, 65535, 1, -1, NULL) ) == NULL) +#endif +		{ +			printf ("\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name); +			/* Free the device list */ +			pcap_freealldevs (alldevs); +			return; +		} + +	printf ("\nEthernet bridge on %s...\n", d->description); + +	/* At this point, we don't need any more the device list. Free it */ +	pcap_freealldevs (alldevs); +	net_enabled = 1; +} + +void setmac() { +	memcpy (&RAM[0xE0000], &maclocal[0], 6); +} + +#ifndef NETWORKING_OLDCARD +extern int ne2000_can_receive(); +extern void ne2000_receive (const uint8_t *buf, int size); + +uint8_t newrecv[5000]; + +void dispatch() { +	uint16_t i; + +	if (pcap_next_ex (adhandle, &hdr, &pktdata) <=0) return; +	if (hdr->len==0) return; +	if (ne2000_can_receive() ) { +			for (i=0; i<hdr->len; i++) { +					newrecv[i<<1] = pktdata[i]; +					newrecv[ (i<<1) +1] = 0; +				} +			ne2000_receive (newrecv, hdr->len); +		} +	return; +} + +void sendpkt (uint8_t *src, uint16_t len) { +	uint16_t i; +	for (i=0; i<len; i++) { +			printf ("%02X ", src[i]); +		} +	printf ("\n"); +	pcap_sendpacket (adhandle, src, len); +} +#else +extern struct netstruct { +	uint8_t enabled; +	uint8_t canrecv; +	uint16_t pktlen; +} net; + +extern void doirq (uint8_t irqnum); + +void dispatch() { +	if (pcap_next_ex (adhandle, &hdr, &pktdata) <=0) return; +	if (hdr->len==0) return; + +	net.canrecv = 0; +	memcpy (&RAM[0xD0000], &pktdata[0], hdr->len); +	net.pktlen = (uint16_t) hdr->len; +	if (verbose) { +			printf ("Received packet of %u bytes.\n", net.pktlen); +		} +	doirq (6); +	return; +} + +void sendpkt (uint8_t *src, uint16_t len) { +	pcap_sendpacket (adhandle, src, len); +} +#endif + +#endif diff --git a/src/fake86/parsecl.c b/src/fake86/parsecl.c new file mode 100755 index 0000000..8751920 --- /dev/null +++ b/src/fake86/parsecl.c @@ -0,0 +1,228 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* parsecl.c: Fake86 command line parsing for runtime options. */ + +#include "config.h" +#include <SDL/SDL.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include "disk.h" + +extern struct struct_drive disk[256]; +#ifndef _WIN32 +#define strcmpi strcasecmp +#else +#define strcmpi _strcmpi +#endif + +extern uint8_t bootdrive, ethif, verbose, cgaonly, *biosfile, usessource, noscale, nosmooth, renderbenchmark, useconsole, doaudio; +extern uint32_t framedelay, textbase, usefullscreen, speed; +extern int32_t usesamplerate, latency; +uint16_t constantw = 0, constanth = 0; +uint8_t slowsystem = 0; + +extern uint8_t insertdisk (uint8_t drivenum, char *filename); +extern uint32_t loadrom (uint32_t addr32, uint8_t *filename, uint8_t failure_fatal); + +uint32_t hextouint(char *src) { +	uint32_t tempuint = 0, cc; +	uint16_t i; + +	for (i=0; i<strlen(src); i++) { +		cc = src[i]; +		if (cc == 0) break; +		if ((cc >= 'a') && (cc <= 'F')) cc = cc - 'a' + 10; +			else if ((cc >= 'A') && (cc <= 'F')) cc =  cc - 'A' + 10; +			else if ((cc >= '0') && (cc <= '9')) cc = cc - '0'; +			else return(0); +		tempuint <<= 4; +		tempuint |= cc; +	} +	return(tempuint); +} + +void showhelp () { +	printf ("Fake86 requires some command line parameters to run.\nValid options:\n"); + +	printf ("  -fd0 filename    Specify a floppy disk image file to use as floppy 0.\n"); +	printf ("  -fd1 filename    Specify a floppy disk image file to use as floppy 1.\n"); +	printf ("  -hd0 filename    Specify a hard disk image file to use as hard drive 0.\n"); +	printf ("  -hd1 filename    Specify a hard disk image file to use as hard drive 1.\n"); +	printf ("  -boot #          Specify which BIOS drive ID should be the boot device in #.\n"); +	printf ("                   Examples: -boot 0 will boot from floppy 0.\n"); +	printf ("                             -boot 1 will boot from floppy 1.\n"); +	printf ("                             -boot 128 will boot from hard drive 0.\n"); +	printf ("                             -boot rom will boot to ROM BASIC if available.\n"); +	printf ("                   Default boot device is hard drive 0, if it exists.\n"); +	printf ("                   Otherwise, the default is floppy 0.\n"); +	printf ("  -bios filename   Specify alternate BIOS ROM image to use.\n"); +#ifdef NETWORKING_ENABLED +#ifdef _WIN32 +	printf ("  -net #           Enable ethernet emulation via winpcap, where # is the\n"); +#else +	printf ("  -net #           Enable ethernet emulation via libpcap, where # is the\n"); +#endif +	printf ("                   numeric ID of your host's network interface to bridge.\n"); +	printf ("                   To get a list of possible interfaces, use -net list\n"); +#endif +	printf ("  -nosound         Disable audio emulation and output.\n"); +	printf ("  -fullscreen      Start Fake86 in fullscreen mode.\n"); +	printf ("  -verbose         Verbose mode. Operation details will be written to stdout.\n"); +	printf ("  -delay           Specify how many milliseconds the render thread should idle\n"); +	printf ("                   between drawing each frame. Default is 20 ms. (~50 FPS)\n"); +	printf ("  -slowsys         If your machine is very slow and you have audio dropouts,\n"); +	printf ("                   use this option to sacrifice audio quality to compensate.\n"); +	printf ("                   If you still have dropouts, then also decrease sample rate\n"); +	printf ("                   and/or increase latency.\n"); +	printf ("  -resw # -resh #  Force a constant window size in pixels.\n"); +	printf ("  -smooth          Apply smoothing to screen rendering.\n"); +	printf ("  -noscale         Disable 2x scaling of low resolution video modes.\n"); +	printf ("  -ssource         Enable Disney Sound Source emulation on LPT1.\n"); +	printf ("  -latency #       Change audio buffering and output latency. (default: 100 ms)\n"); +	printf ("  -samprate #      Change audio emulation sample rate. (default: 48000 Hz)\n"); +	printf ("  -console         Enable console on stdio during emulation.\n"); +	printf ("  -oprom addr rom  Inject a custom option ROM binary at an address in hex.\n"); +	printf ("                   Example: -oprom F4000 monitor.bin\n"); +	printf ("                            This loads the data from monitor.bin at 0xF4000.\n"); + +	printf ("\nThis program is free software; you can redistribute it and/or\n"); +	printf ("modify it under the terms of the GNU General Public License\n"); +	printf ("as published by the Free Software Foundation; either version 2\n"); +	printf ("of the License, or (at your option) any later version.\n\n"); + +	printf ("This program is distributed in the hope that it will be useful,\n"); +	printf ("but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); +	printf ("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"); +	printf ("GNU General Public License for more details.\n"); + +	exit (0); +} + +void parsecl (int argc, char *argv[]) { +	uint32_t tempuint; +	int i, abort = 0; + +	if (argc<2) { +			printf ("Invoke Fake86 with the parameter -h for help and usage information.\n"); +#ifndef _WIN32 +			exit (0); +#endif +		} + +	bootdrive = 254; +	textbase = 0xB8000; +	ethif = 254; +	usefullscreen = 0; +	biosfile = PATH_DATAFILES "pcxtbios.bin"; +	for (i=1; i<argc; i++) { +            if (strcmpi (argv[i], "-h") ==0) showhelp (); +            else if (strcmpi (argv[i], "-?") ==0) showhelp (); +            else if (strcmpi (argv[i], "-help") ==0) showhelp (); +			else if (strcmpi (argv[i], "-fd0") ==0) { +					i++; +					if (insertdisk (0, argv[i]) ) { +							printf ("ERROR: Unable to open image file %s\n", argv[i]); +						} +				} +			else if (strcmpi (argv[i], "-fd1") ==0) { +					i++; +					if (insertdisk (1, argv[i]) ) { +							printf ("ERROR: Unable to open image file %s\n", argv[i]); +						} +				} +			else if (strcmpi (argv[i], "-hd0") ==0) { +					i++; +					if (insertdisk (0x80, argv[i]) ) { +							printf ("ERROR: Unable to open image file %s\n", argv[i]); +						} +				} +			else if (strcmpi (argv[i], "-hd1") ==0) { +					i++; +					if (insertdisk (0x81, argv[i]) ) { +							printf ("ERROR: Unable to open image file %s\n", argv[i]); +						} +				} +			else if (strcmpi (argv[i], "-net") ==0) { +					i++; +					if (strcmpi (argv[i], "list") ==0) ethif = 255; +					else ethif = atoi (argv[i]); +				} +			else if (strcmpi (argv[i], "-boot") ==0) { +					i++; +					if (strcmpi (argv[i], "rom") ==0) bootdrive = 255; +					else bootdrive = atoi (argv[i]); +				} +			else if (strcmpi (argv[i], "-ssource") ==0) { +					i++; +					usessource = 1; +				} +			else if (strcmpi (argv[i], "-latency") ==0) { +					i++; +					latency = atol (argv[i]); +				} +			else if (strcmpi (argv[i], "-samprate") ==0) { +					i++; +					usesamplerate = atol (argv[i]); +				} +			else if (strcmpi (argv[i], "-bios") ==0) { +					i++; +					biosfile = argv[i]; +				} +			else if (strcmpi (argv[i], "-resw") ==0) { +					i++; +					constantw = (uint16_t) atoi (argv[i]); +				} +			else if (strcmpi (argv[i], "-resh") ==0) { +					i++; +					constanth = (uint16_t) atoi (argv[i]); +				} +			else if (strcmpi (argv[i], "-speed") ==0) { +					i++; +					speed= (uint32_t) atol (argv[i]); +				} +			else if (strcmpi (argv[i], "-noscale") ==0) noscale = 1; +			else if (strcmpi (argv[i], "-verbose") ==0) verbose = 1; +			else if (strcmpi (argv[i], "-smooth") ==0) nosmooth = 0; +			else if (strcmpi (argv[i], "-fps") ==0) renderbenchmark = 1; +			else if (strcmpi (argv[i], "-nosound") ==0) doaudio = 0; +			else if (strcmpi (argv[i], "-fullscreen") ==0) usefullscreen = SDL_FULLSCREEN; +			else if (strcmpi (argv[i], "-delay") ==0) framedelay = atol (argv[++i]); +			else if (strcmpi (argv[i], "-console") ==0) useconsole = 1; +			else if (strcmpi (argv[i], "-slowsys") ==0) slowsystem = 1; +			else if (strcmpi (argv[i], "-oprom") ==0) { +					i++; +					tempuint = hextouint (argv[i++]); +					loadrom (tempuint, argv[i], 0); +				} +			else { +					printf ("Unrecognized parameter: %s\n", argv[i]); +					exit (1); +				} +		} + +	if (bootdrive==254) { +			if (disk[0x80].inserted) bootdrive = 0x80; +				else if (disk[0x00].inserted) bootdrive = 0; +				else bootdrive = 0xFF; //ROM BASIC fallback +		} +} + + diff --git a/src/fake86/ports.c b/src/fake86/ports.c new file mode 100755 index 0000000..88f047a --- /dev/null +++ b/src/fake86/ports.c @@ -0,0 +1,119 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* ports.c: functions to handle port I/O from the CPU module, as well +   as functions for emulated hardware components to register their +   read/write callback functions across the port address range. */ + +#include <stdint.h> +#include <stdio.h> +#include "cpu.h" + +extern uint8_t portram[0x10000]; +extern uint8_t speakerenabled; +extern uint8_t keyboardwaitack; + +void (*do_callback_write) (uint16_t portnum, uint8_t value) = NULL; +uint8_t (*do_callback_read) (uint16_t portnum) = NULL; +void (*do_callback_write16) (uint16_t portnum, uint16_t value) = NULL; +uint16_t (*do_callback_read16) (uint16_t portnum) = NULL; +void * (port_write_callback[0x10000]); +void * (port_read_callback[0x10000]); +void * (port_write_callback16[0x10000]); +void * (port_read_callback16[0x10000]); + +extern uint8_t verbose; +void portout (uint16_t portnum, uint8_t value) { +	portram[portnum] = value; +	//if (verbose) printf("portout(0x%X, 0x%02X);\n", portnum, value); +	switch (portnum) { +			case 0x61: +				if ( (value & 3) == 3) speakerenabled = 1; +				else speakerenabled = 0; +				return; +		} +	do_callback_write = (void (*) (uint16_t portnum, uint8_t value) ) port_write_callback[portnum]; +	if (do_callback_write != (void *) 0) (*do_callback_write) (portnum, value); +} + +uint8_t portin (uint16_t portnum) { +	//if (verbose) printf("portin(0x%X);\n", portnum); +	switch (portnum) { +			case 0x62: +				return (0x00); +			case 0x60: +			case 0x61: +			case 0x63: +			case 0x64: +				return (portram[portnum]); +		} +	do_callback_read = (uint8_t (*) (uint16_t portnum) ) port_read_callback[portnum]; +	if (do_callback_read != (void *) 0) return ( (*do_callback_read) (portnum) ); +	return (0xFF); +} + +void portout16 (uint16_t portnum, uint16_t value) { +	do_callback_write16 = (void (*) (uint16_t portnum, uint16_t value) ) port_write_callback16[portnum]; +	if (do_callback_write16 != (void *) 0) { +			(*do_callback_write16) (portnum, value); +			return; +		} + +	portout (portnum, (uint8_t) value); +	portout (portnum + 1, (uint8_t) (value >> 8) ); +} + +uint16_t portin16 (uint16_t portnum) { +	uint16_t ret; + +	do_callback_read16 = (uint16_t (*) (uint16_t portnum) ) port_read_callback16[portnum]; +	if (do_callback_read16 != (void *) 0) return ( (*do_callback_read16) (portnum) ); + +	ret = (uint16_t) portin (portnum); +	ret |= (uint16_t) portin (portnum+1) << 8; +	return (ret); +} + +void set_port_write_redirector (uint16_t startport, uint16_t endport, void *callback) { +	uint16_t i; +	for (i=startport; i<=endport; i++) { +			port_write_callback[i] = callback; +		} +} + +void set_port_read_redirector (uint16_t startport, uint16_t endport, void *callback) { +	uint16_t i; +	for (i=startport; i<=endport; i++) { +			port_read_callback[i] = callback; +		} +} + +void set_port_write_redirector_16 (uint16_t startport, uint16_t endport, void *callback) { +	uint16_t i; +	for (i=startport; i<=endport; i++) { +			port_write_callback16[i] = callback; +		} +} + +void set_port_read_redirector_16 (uint16_t startport, uint16_t endport, void *callback) { +	uint16_t i; +	for (i=startport; i<=endport; i++) { +			port_read_callback16[i] = callback; +		} +} diff --git a/src/fake86/render.c b/src/fake86/render.c new file mode 100755 index 0000000..4f51639 --- /dev/null +++ b/src/fake86/render.c @@ -0,0 +1,570 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* render.c: functions for SDL initialization, as well as video scaling/rendering. +   it is a bit messy. i plan to rework much of this in the future. i am also +   going to add hardware accelerated scaling soon. */ + +#include <SDL/SDL.h> +#include <stdint.h> +#include <stdio.h> +#include "mutex.h" + +#ifdef _WIN32 +CRITICAL_SECTION screenmutex; +#else +pthread_t vidthread; +pthread_mutex_t screenmutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +SDL_Surface *screen = NULL; +uint32_t *scalemap = NULL; +uint8_t regenscalemap = 1; + +extern uint8_t RAM[0x100000], portram[0x10000]; +extern uint8_t VRAM[262144], vidmode, cgabg, blankattr, vidgfxmode, vidcolor, running; +extern uint16_t cursx, cursy, cols, rows, vgapage, cursorposition, cursorvisible; +extern uint8_t updatedscreen, clocksafe, port3da, port6, portout16; +extern uint16_t VGA_SC[0x100], VGA_CRTC[0x100], VGA_ATTR[0x100], VGA_GC[0x100]; +extern uint32_t videobase, textbase, x, y; +extern uint8_t fontcga[32768]; +extern uint32_t palettecga[16], palettevga[256]; +extern uint32_t usefullscreen, usegrabmode; + +uint64_t totalframes = 0; +uint32_t framedelay = 20; +uint8_t scrmodechange = 0, noscale = 0, nosmooth = 1, renderbenchmark = 0, doaudio = 1; +char windowtitle[128]; + +void initcga(); +#ifdef _WIN32 +void VideoThread (void *dummy); +#else +void *VideoThread (void *dummy); +#endif + +void setwindowtitle (uint8_t *extra) { +	char temptext[128]; +	sprintf (temptext, "%s%s", windowtitle, extra); +	SDL_WM_SetCaption ( (const char *) temptext, NULL); +} + +uint8_t initscreen (uint8_t *ver) { +	if (doaudio) { +			if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) ) return (0); +		} +	else { +			if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_TIMER) ) return (0); +		} +	screen = SDL_SetVideoMode (640, 400, 32, SDL_HWSURFACE); +	if (screen == NULL) return (0); +	sprintf (windowtitle, "%s", ver); +	setwindowtitle (""); +	initcga(); +#ifdef _WIN32 +	InitializeCriticalSection (&screenmutex); +	_beginthread (VideoThread, 0, NULL); +#else +	pthread_create (&vidthread, NULL, (void *) VideoThread, NULL); +#endif + +	return (1); +} + +uint32_t prestretch[1024][1024]; +uint32_t nw, nh; //native width and height, pre-stretching (i.e. 320x200 for mode 13h) +void createscalemap() { +	uint32_t srcx, srcy, dstx, dsty, scalemapptr; +	double xscale, yscale; + +	xscale = (double) nw / (double) screen->w; +	yscale = (double) nh / (double) screen->h; +	if (scalemap != NULL) free(scalemap); +	scalemap = (void *)malloc( ((uint32_t)screen->w + 1) * (uint32_t)screen->h * 4); +	if (scalemap == NULL) { +			printf("\nFATAL: Unable to allocate memory for scalemap!\n"); +			exit(1); +		} +	scalemapptr = 0; +	for (dsty=0; dsty<(uint32_t)screen->h; dsty++) { +			srcy = (uint32_t) ( (double) dsty * yscale); +			scalemap[scalemapptr++] = srcy; +			for (dstx=0; dstx<(uint32_t)screen->w; dstx++) { +					srcx = (uint32_t) ( (double) dstx * xscale); +					scalemap[scalemapptr++] = srcx; +				} +		} + +	regenscalemap = 0; +} + +extern uint16_t oldw, oldh, constantw, constanth; +void draw(); +extern void handleinput(); +#ifdef _WIN32 +void VideoThread (void *dummy) { +#else +void *VideoThread (void *dummy) { +#endif +	uint32_t cursorprevtick, cursorcurtick, delaycalc; +	cursorprevtick = SDL_GetTicks(); +	cursorvisible = 0; + +	while (running) { +			cursorcurtick = SDL_GetTicks(); +			if ( (cursorcurtick - cursorprevtick) >= 250) { +					updatedscreen = 1; +					cursorvisible = ~cursorvisible & 1; +					cursorprevtick = cursorcurtick; +				} + +			if (updatedscreen || renderbenchmark) { +					updatedscreen = 0; +					if (screen != NULL) { +							MutexLock (screenmutex); +							if (regenscalemap) createscalemap(); +							draw(); +							MutexUnlock (screenmutex); +						} +					totalframes++; +				} +			if (!renderbenchmark) { +					delaycalc = framedelay - (SDL_GetTicks() - cursorcurtick); +					if (delaycalc > framedelay) delaycalc = framedelay; +					SDL_Delay (delaycalc); +				} +		} +} + +#ifdef _WIN32 +void ShowMenu(); +void HideMenu(); +#endif + +void doscrmodechange() { +	MutexLock (screenmutex); +	if (scrmodechange) { +			if (screen != NULL) SDL_FreeSurface (screen); +#ifdef _WIN32 +			if (usefullscreen) HideMenu(); else ShowMenu(); +#endif +			if (constantw && constanth) screen = SDL_SetVideoMode (constantw, constanth, 32, SDL_HWSURFACE | usefullscreen); +			else if (noscale) screen = SDL_SetVideoMode (nw, nh, 32, SDL_HWSURFACE | usefullscreen); +			else { +					if ( (nw >= 640) || (nh >= 400) ) screen = SDL_SetVideoMode (nw, nh, 32, SDL_HWSURFACE | usefullscreen); +					else screen = SDL_SetVideoMode (640, 400, 32, SDL_HWSURFACE | usefullscreen); +				} +			if (usefullscreen) SDL_WM_GrabInput (SDL_GRAB_ON); //always have mouse grab turned on for full screen mode +			else SDL_WM_GrabInput (usegrabmode); +			SDL_ShowCursor (SDL_DISABLE); +			if (!usefullscreen) { +					if (usegrabmode == SDL_GRAB_ON) setwindowtitle (" (press Ctrl + Alt to release mouse)"); +					else setwindowtitle (""); +				} +			regenscalemap = 1; +			createscalemap(); +		} +	MutexUnlock (screenmutex); +	scrmodechange = 0; +} + +void stretchblit (SDL_Surface *target) { +	uint32_t srcx, srcy, dstx, dsty, lastx, lasty, r, g, b; +	uint32_t consecutivex, consecutivey = 0, limitx, limity, scalemapptr; +	uint32_t ofs; +	uint8_t *pixelrgb; + +	limitx = (uint32_t)((double) nw / (double) target->w); +	limity = (uint32_t)((double) nh / (double) target->h); + +	if (SDL_MUSTLOCK (target) ) +		if (SDL_LockSurface (target) < 0) +			return; + +	lasty = 0; +	scalemapptr = 0; +	for (dsty=0; dsty<(uint32_t)target->h; dsty++) { +			srcy = scalemap[scalemapptr++]; +			ofs = dsty*target->w; +			consecutivex = 0; +			lastx = 0; +			if (srcy == lasty) consecutivey++; +			else consecutivey = 0; +			for (dstx=0; dstx<(uint32_t)target->w; dstx++) { +					srcx = scalemap[scalemapptr++]; +					pixelrgb = (uint8_t *) &prestretch[srcy][srcx]; +					r = pixelrgb[0]; +					g = pixelrgb[1]; +					b = pixelrgb[2]; +					if (srcx == lastx) consecutivex++; +					else consecutivex = 0; +					if ( (consecutivex > limitx) && (consecutivey > limity) ) { +							pixelrgb = (uint8_t *) &prestretch[srcy][srcx+1]; +							r += pixelrgb[0]; +							g += pixelrgb[1]; +							b += pixelrgb[2]; +							pixelrgb = (uint8_t *) &prestretch[srcy+1][srcx]; +							r += pixelrgb[0]; +							g += pixelrgb[1]; +							b += pixelrgb[2]; +							pixelrgb = (uint8_t *) &prestretch[srcy+1][srcx+1]; +							r += pixelrgb[0]; +							g += pixelrgb[1]; +							b += pixelrgb[2]; +							r = r >> 2; +							g = g >> 2; +							b = b >> 2; +							//r = 255; g = 0; b = 0; +						} +					else if (consecutivex > limitx) { +							pixelrgb = (uint8_t *) &prestretch[srcy][srcx+1]; +							r += pixelrgb[0]; +							r = r >> 1; +							g += pixelrgb[1]; +							g = g >> 1; +							b += pixelrgb[2]; +							b = b >> 1; +							//r = 0; g = 255; b = 0; +						} +					else if (consecutivey > limity) { +							pixelrgb = (uint8_t *) &prestretch[srcy+1][srcx]; +							r += pixelrgb[0]; +							r = r >> 1; +							g += pixelrgb[1]; +							g = g >> 1; +							b += pixelrgb[2]; +							b = b >> 1; +							//r = 0; g = 0; b = 255; +						} +					( (uint32_t *) target->pixels) [ofs++] = SDL_MapRGB (target->format, (uint8_t) r, (uint8_t) g, (uint8_t) b); +					lastx = srcx; +				} +			lasty = srcy; +		} + +	if (SDL_MUSTLOCK (target) ) +		SDL_UnlockSurface (target); +	SDL_UpdateRect (target, 0, 0, target->w, target->h); +} + +void roughblit (SDL_Surface *target) { +	uint32_t srcx, srcy, dstx, dsty, scalemapptr; +	int32_t ofs; +	uint8_t *pixelrgb; + +	if (SDL_MUSTLOCK (target) ) +		if (SDL_LockSurface (target) < 0) +			return; + +	scalemapptr = 0; +	for (dsty=0; dsty<(uint32_t)target->h; dsty++) { +			srcy = scalemap[scalemapptr++]; +			ofs = dsty*target->w; +			for (dstx=0; dstx<(uint32_t)target->w; dstx++) { +					srcx = scalemap[scalemapptr++]; +					pixelrgb = (uint8_t *) &prestretch[srcy][srcx]; +					( (uint32_t *) target->pixels) [ofs++] = SDL_MapRGB (target->format, pixelrgb[0], pixelrgb[1], pixelrgb[2]); +				} +		} + +	if (SDL_MUSTLOCK (target) ) +		SDL_UnlockSurface (target); +	SDL_UpdateRect (target, 0, 0, target->w, target->h); +} + +/* NOTE: doubleblit is only used when smoothing is not enabled, and the SDL window size +         is exactly double of native resolution for the current video mode. we can take +         advantage of the fact that every pixel is simply doubled both horizontally and +         vertically. this way, we do not need to waste mountains of CPU time doing +         floating point multiplication for each and every on-screen pixel. it makes the +         difference between games being smooth and playable, and being jerky on my old +         400 MHz PowerPC G3 iMac. +*/ +void doubleblit (SDL_Surface *target) { +	uint32_t srcx, srcy, dstx, dsty, curcolor; +	int32_t ofs, startofs; +	uint8_t *pixelrgb; + +	if (SDL_MUSTLOCK (target) ) +		if (SDL_LockSurface (target) < 0) +			return; + +	for (dsty=0; dsty<(uint32_t)target->h; dsty += 2) { +			srcy = (uint32_t) (dsty >> 1); +			startofs = ofs = dsty*target->w; +			for (dstx=0; dstx<(uint32_t)target->w; dstx += 2) { +					srcx = (uint32_t) (dstx >> 1); +					pixelrgb = (uint8_t *) &prestretch[srcy][srcx]; +					curcolor = SDL_MapRGB (target->format, pixelrgb[0], pixelrgb[1], pixelrgb[2]); +					( (uint32_t *) target->pixels) [ofs+target->w] = curcolor; +					( (uint32_t *) target->pixels) [ofs++] = curcolor; +					( (uint32_t *) target->pixels) [ofs+target->w] = curcolor; +					( (uint32_t *) target->pixels) [ofs++] = curcolor; +				} +		} + +	if (SDL_MUSTLOCK (target) ) +		SDL_UnlockSurface (target); +	SDL_UpdateRect (target, 0, 0, target->w, target->h); +} + +extern uint16_t vtotal; +void draw () { +	uint32_t planemode, vgapage, color, chary, charx, vidptr, divx, divy, curchar, curpixel, usepal, intensity, blockw, curheight, x1, y1; +	switch (vidmode) { +			case 0: +			case 1: +			case 2: //text modes +			case 3: +			case 7: +			case 0x82: +				nw = 640; +				nh = 400; +				vgapage = ( (uint32_t) VGA_CRTC[0xC]<<8) + (uint32_t) VGA_CRTC[0xD]; +				for (y=0; y<400; y++) +					for (x=0; x<640; x++) { +							if (cols==80) { +									charx = x/8; +									divx = 1; +								} +							else { +									charx = x/16; +									divx = 2; +								} +							if ( (portram[0x3D8]==9) && (portram[0x3D4]==9) ) { +									chary = y/4; +									vidptr = vgapage + videobase + chary*cols*2 + charx*2; +									curchar = RAM[vidptr]; +									color = fontcga[curchar*128 + (y%4) *8 + ( (x/divx) %8) ]; +								} +							else { +									chary = y/16; +									vidptr = videobase + chary*cols*2 + charx*2; +									curchar = RAM[vidptr]; +									color = fontcga[curchar*128 + (y%16) *8 + ( (x/divx) %8) ]; +								} +							if (vidcolor) { +									/*if (!color) if (portram[0x3D8]&128) color = palettecga[ (RAM[vidptr+1]/16) &7]; +										else*/ if (!color) color = palettecga[RAM[vidptr+1]/16]; //high intensity background +									else color = palettecga[RAM[vidptr+1]&15]; +								} +							else { +									if ( (RAM[vidptr+1] & 0x70) ) { +											if (!color) color = palettecga[7]; +											else color = palettecga[0]; +										} +									else { +											if (!color) color = palettecga[0]; +											else color = palettecga[7]; +										} +								} +							prestretch[y][x] = color; +						} +				break; +			case 4: +			case 5: +				nw = 320; +				nh = 200; +				usepal = (portram[0x3D9]>>5) & 1; +				intensity = ( (portram[0x3D9]>>4) & 1) << 3; +				for (y=0; y<200; y++) { +						for (x=0; x<320; x++) { +								charx = x; +								chary = y; +								vidptr = videobase + ( (chary>>1) * 80) + ( (chary & 1) * 8192) + (charx >> 2); +								curpixel = RAM[vidptr]; +								switch (charx & 3) { +										case 3: +											curpixel = curpixel & 3; +											break; +										case 2: +											curpixel = (curpixel>>2) & 3; +											break; +										case 1: +											curpixel = (curpixel>>4) & 3; +											break; +										case 0: +											curpixel = (curpixel>>6) & 3; +											break; +									} +								if (vidmode==4) { +										curpixel = curpixel * 2 + usepal + intensity; +										if (curpixel == (usepal + intensity) )  curpixel = cgabg; +										color = palettecga[curpixel]; +										prestretch[y][x] = color; +									} +								else { +										curpixel = curpixel * 63; +										color = palettecga[curpixel]; +										prestretch[y][x] = color; +									} +							} +					} +				break; +			case 6: +				nw = 640; +				nh = 200; +				for (y=0; y<400; y+=2) { +						for (x=0; x<640; x++) { +								charx = x; +								chary = y >> 1; +								vidptr = videobase + ( (chary>>1) * 80) + ( (chary&1) * 8192) + (charx>>3); +								curpixel = (RAM[vidptr]>> (7- (charx&7) ) ) &1; +								color = palettecga[curpixel*15]; +								prestretch[y][x] = color; +								prestretch[y+1][x] = color; +							} +					} +				break; +			case 127: +				nw = 720; +				nh = 348; +				for (y=0; y<348; y++) { +						for (x=0; x<720; x++) { +								charx = x; +								chary = y>>1; +								vidptr = videobase + ( (y & 3) << 13) + (y >> 2) *90 + (x >> 3); +								curpixel = (RAM[vidptr]>> (7- (charx&7) ) ) &1; +#ifdef __BIG_ENDIAN__ +								if (curpixel) color = 0xFFFFFF00; +#else +								if (curpixel) color = 0x00FFFFFF; +#endif +								else color = 0x00000000; +								prestretch[y][x] = color; +							} +					} +				break; +			case 0x8: //160x200 16-color (PCjr) +				nw = 640; //fix this +				nh = 400; //part later +				for (y=0; y<400; y++) +					for (x=0; x<640; x++) { +							vidptr = 0xB8000 + (y>>2) *80 + (x>>3) + ( (y>>1) &1) *8192; +							if ( ( (x>>1) &1) ==0) color = palettecga[RAM[vidptr] >> 4]; +							else color = palettecga[RAM[vidptr] & 15]; +							prestretch[y][x] = color; +						} +				break; +			case 0x9: //320x200 16-color (Tandy/PCjr) +				nw = 640; //fix this +				nh = 400; //part later +				for (y=0; y<400; y++) +					for (x=0; x<640; x++) { +							vidptr = 0xB8000 + (y>>3) *160 + (x>>2) + ( (y>>1) &3) *8192; +							if ( ( (x>>1) &1) ==0) color = palettecga[RAM[vidptr] >> 4]; +							else color = palettecga[RAM[vidptr] & 15]; +							prestretch[y][x] = color; +						} +				break; +			case 0xD: +			case 0xE: +				nw = 640; //fix this +				nh = 400; //part later +				for (y=0; y<400; y++) +					for (x=0; x<640; x++) { +							divx = x>>1; +							divy = y>>1; +							vidptr = divy*40 + (divx>>3); +							x1 = 7 - (divx & 7); +							color = (VRAM[vidptr] >> x1) & 1; +							color += ( ( (VRAM[0x10000 + vidptr] >> x1) & 1) << 1); +							color += ( ( (VRAM[0x20000 + vidptr] >> x1) & 1) << 2); +							color += ( ( (VRAM[0x30000 + vidptr] >> x1) & 1) << 3); +							color = palettevga[color]; +							prestretch[y][x] = color; +						} +				break; +			case 0x10: +				nw = 640; +				nh = 350; +				for (y=0; y<350; y++) +					for (x=0; x<640; x++) { +							vidptr = y*80 + (x>>3); +							x1 = 7 - (x & 7); +							color = (VRAM[vidptr] >> x1) & 1; +							color |= ( ( (VRAM[0x10000 + vidptr] >> x1) & 1) << 1); +							color |= ( ( (VRAM[0x20000 + vidptr] >> x1) & 1) << 2); +							color |= ( ( (VRAM[0x30000 + vidptr] >> x1) & 1) << 3); +							color = palettevga[color]; +							prestretch[y][x] = color; +						} +				break; +			case 0x12: +				nw = 640; +				nh = 480; +				vgapage = ( (uint32_t) VGA_CRTC[0xC]<<8) + (uint32_t) VGA_CRTC[0xD]; +				for (y=0; y<nh; y++) +					for (x=0; x<nw; x++) { +							vidptr = y*80 + (x/8); +							color  = (VRAM[vidptr] >> (~x & 7) ) & 1; +							color |= ( (VRAM[vidptr+0x10000] >> (~x & 7) ) & 1) << 1; +							color |= ( (VRAM[vidptr+0x20000] >> (~x & 7) ) & 1) << 2; +							color |= ( (VRAM[vidptr+0x30000] >> (~x & 7) ) & 1) << 3; +							prestretch[y][x] = palettevga[color]; +						} +				break; +			case 0x13: +				if (vtotal == 11) { //ugly hack to show Flashback at the proper resolution +						nw = 256; +						nh = 224; +					} +				else { +						nw = 320; +						nh = 200; +					} +				if (VGA_SC[4] & 6) planemode = 1; +				else planemode = 0; +				//vgapage = ( (uint32_t) VGA_CRTC[0xC]<<8) + (uint32_t) VGA_CRTC[0xD]; +				vgapage = (( (uint32_t) VGA_CRTC[0xC]<<8) + (uint32_t) VGA_CRTC[0xD]) << 2; +				for (y=0; y<nh; y++) +					for (x=0; x<nw; x++) { +							if (!planemode) color = palettevga[RAM[videobase + ((vgapage + y*nw + x) & 0xFFFF) ]]; +							//if (!planemode) color = palettevga[RAM[videobase + y*nw + x]]; +							else { +									vidptr = y*nw + x; +									vidptr = vidptr/4 + (x & 3) *0x10000; +									vidptr = vidptr + vgapage - (VGA_ATTR[0x13] & 15); +									color = palettevga[VRAM[vidptr]]; +								} +							prestretch[y][x] = color; +						} +		} + +	if (vidgfxmode==0) { +			if (cursorvisible) { +					curheight = 2; +					if (cols==80) blockw = 8; +					else blockw = 16; +					x1 = cursx * blockw; +					y1 = cursy * 8 + 8 - curheight; +					for (y=y1*2; y<=y1*2+curheight-1; y++) +						for (x=x1; x<=x1+blockw-1; x++) { +								color = palettecga[RAM[videobase+cursy*cols*2+cursx*2+1]&15]; +								prestretch[y&1023][x&1023] = color; +							} +				} +		} +	if (nosmooth) { +			if ( ((nw << 1) == screen->w) && ((nh << 1) == screen->h) ) doubleblit (screen); +			else roughblit (screen); +		} +	else stretchblit (screen); +} + diff --git a/src/fake86/sermouse.c b/src/fake86/sermouse.c new file mode 100755 index 0000000..98e25ab --- /dev/null +++ b/src/fake86/sermouse.c @@ -0,0 +1,97 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* sermouse.c: functions to emulate a standard Microsoft-compatible serial mouse. */ + +#include "config.h" +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include "sermouse.h" + +extern void set_port_write_redirector (uint16_t startport, uint16_t endport, void *callback); +extern void set_port_read_redirector (uint16_t startport, uint16_t endport, void *callback); +extern void doirq (uint8_t irqnum); + +struct sermouse_s sermouse; + +void bufsermousedata (uint8_t value) { +	if (sermouse.bufptr == 16) return; +	if (sermouse.bufptr == 0 ) doirq (4); +	sermouse.buf[sermouse.bufptr++] = value; +} + +void outsermouse (uint16_t portnum, uint8_t value) { +	uint8_t oldreg; +	//printf("[DEBUG] Serial mouse, port %X out: %02X\n", portnum, value); +	portnum &= 7; +	oldreg = sermouse.reg[portnum]; +	sermouse.reg[portnum] = value; +	switch (portnum) { +			case 4: //modem control register +				if ( (value & 1) != (oldreg & 1) ) { //software toggling of this register +						sermouse.bufptr = 0; //causes the mouse to reset and fill the buffer +						bufsermousedata ('M'); //with a bunch of ASCII 'M' characters. +						bufsermousedata ('M'); //this is intended to be a way for +						bufsermousedata ('M'); //drivers to verify that there is +						bufsermousedata ('M'); //actually a mouse connected to the port. +						bufsermousedata ('M'); +						bufsermousedata ('M'); +					} +				break; +		} +} + +uint8_t insermouse (uint16_t portnum) { +	uint8_t temp; +	//printf("[DEBUG] Serial mouse, port %X in\n", portnum); +	portnum &= 7; +	switch (portnum) { +			case 0: //data receive +				temp = sermouse.buf[0]; +				memmove (sermouse.buf, &sermouse.buf[1], 15); +				sermouse.bufptr--; +				if (sermouse.bufptr < 0) sermouse.bufptr = 0; +				if (sermouse.bufptr > 0) doirq (4); +				sermouse.reg[4] = ~sermouse.reg[4] & 1; +				return (temp); +			case 5: //line status register (read-only) +				if (sermouse.bufptr > 0) temp = 1; +				else temp = 0; +				return (0x1); +				return (0x60 | temp); +		} +	return (sermouse.reg[portnum & 7]); +} + +void initsermouse (uint16_t baseport, uint8_t irq) { +	sermouse.bufptr = 0; +	set_port_write_redirector (baseport, baseport + 7, &outsermouse); +	set_port_read_redirector (baseport, baseport + 7, &insermouse); +} + +void sermouseevent (uint8_t buttons, int8_t xrel, int8_t yrel) { +	uint8_t highbits = 0; +	if (xrel < 0) highbits = 3; +	else highbits = 0; +	if (yrel < 0) highbits |= 12; +	bufsermousedata (0x40 | (buttons << 4) | highbits); +	bufsermousedata (xrel & 63); +	bufsermousedata (yrel & 63); +} diff --git a/src/fake86/sermouse.h b/src/fake86/sermouse.h new file mode 100755 index 0000000..d9d71ba --- /dev/null +++ b/src/fake86/sermouse.h @@ -0,0 +1,26 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2012 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +#include <stdint.h> + +struct sermouse_s { +	uint8_t reg[8]; +	uint8_t buf[16]; +	int8_t bufptr; +}; diff --git a/src/fake86/sndsource.c b/src/fake86/sndsource.c new file mode 100755 index 0000000..515d1c7 --- /dev/null +++ b/src/fake86/sndsource.c @@ -0,0 +1,82 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* ssource.c: functions to emulate the Disney Sound Source's 16-byte FIFO buffer. */ + +#include "config.h" +#include <stdint.h> + +extern void set_port_write_redirector (uint16_t startport, uint16_t endport, void *callback); +extern void set_port_read_redirector (uint16_t startport, uint16_t endport, void *callback); + +extern uint8_t portram[0x10000]; +uint8_t ssourcebuf[16], ssourceptr = 0, ssourceactive = 0; +int16_t ssourcecursample = 0; + +int16_t getssourcebyte() { +	return (ssourcecursample); +} + +void tickssource() { +	uint8_t rotatefifo; +	if ( (ssourceptr==0) || (!ssourceactive) ) { +			ssourcecursample = 0; +			return; +		} +	ssourcecursample = ssourcebuf[0]; +	for (rotatefifo=1; rotatefifo<16; rotatefifo++) +		ssourcebuf[rotatefifo-1] = ssourcebuf[rotatefifo]; +	ssourceptr--; +	portram[0x379] = 0; +} + +void putssourcebyte (uint8_t value) { +	if (ssourceptr==16) return; +	ssourcebuf[ssourceptr++] = value; +	if (ssourceptr==16) portram[0x379] = 0x40; +} + +uint8_t ssourcefull() { +	if (ssourceptr==16) return (0x40); +	else return (0x00); +} + +void outsoundsource (uint16_t portnum, uint8_t value) { +	static uint8_t last37a = 0; +	switch (portnum) { +			case 0x378: +				putssourcebyte (value); +				break; +			case 0x37A: +				if ( (value & 4) && ! (last37a & 4) ) putssourcebyte (portram[0x378]); +				last37a = value; +				break; +		} +} + +uint8_t insoundsource (uint16_t portnum) { +	return (ssourcefull() ); +} + +void initsoundsource() { +	set_port_write_redirector (0x378, 0x378, &outsoundsource); +	set_port_write_redirector (0x37A, 0x37A, &outsoundsource); +	set_port_read_redirector (0x379, 0x379, &insoundsource); +	ssourceactive = 1; +} diff --git a/src/fake86/speaker.c b/src/fake86/speaker.c new file mode 100755 index 0000000..df3e651 --- /dev/null +++ b/src/fake86/speaker.c @@ -0,0 +1,47 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* speaker.c: function to generate output samples for PC speaker emulation. */ + +#include "config.h" +#include <stdint.h> +#include "i8253.h" +#include "mutex.h" + +extern struct i8253_s i8253; + +extern uint64_t gensamplerate; +uint64_t speakerfullstep, speakerhalfstep, speakercurstep = 0; +int16_t speakerpos = 0; + +int16_t speakergensample() { +	int16_t speakervalue; + +	speakerfullstep = (uint64_t) ( (float) gensamplerate / (float) i8253.chanfreq[2]); +	if (speakerfullstep < 2) speakerfullstep = 2; +	speakerhalfstep = speakerfullstep >> 1; +	if (speakercurstep < speakerhalfstep) { +			speakervalue = 32; +		} +	else { +			speakervalue = -32; +		} +	speakercurstep = (speakercurstep + 1) % speakerfullstep; +	return (speakervalue); +} diff --git a/src/fake86/timing.c b/src/fake86/timing.c new file mode 100755 index 0000000..b3c4b23 --- /dev/null +++ b/src/fake86/timing.c @@ -0,0 +1,136 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* timing.c: critical functions to provide accurate timing for the +   system timer interrupt, and to generate new audio output samples. */ + +#include "config.h" +#include <SDL/SDL.h> +#include <stdint.h> +#include <stdio.h> +#ifdef _WIN32 +#include <Windows.h> +LARGE_INTEGER queryperf; +#else +#include <sys/time.h> +struct timeval tv; +#endif +#include "i8253.h" +#include "blaster.h" + +extern struct blaster_s blaster; +extern struct i8253_s i8253; +extern void doirq (uint8_t irqnum); +extern void tickaudio(); +extern void tickssource(); +extern void tickadlib(); +extern void tickBlaster(); + +uint64_t hostfreq = 1000000, lasttick = 0, curtick = 0, tickgap, i8253tickgap, lasti8253tick, scanlinetiming, lastscanlinetick, curscanline = 0; +uint64_t sampleticks, lastsampletick, ssourceticks, lastssourcetick, adlibticks, lastadlibtick, lastblastertick, gensamplerate; + +uint16_t pit0counter = 65535; +extern uint64_t totalexec; +extern uint32_t speed; +extern uint8_t port3da, doaudio, slowsystem; + +void inittiming() { +#ifdef _WIN32 +	QueryPerformanceFrequency (&queryperf); +	hostfreq = queryperf.QuadPart; +	QueryPerformanceCounter (&queryperf); +	curtick = queryperf.QuadPart; +#else +	hostfreq = 1000000; +	gettimeofday (&tv, NULL); +	curtick = (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; +#endif +	lasti8253tick = lastblastertick = lastadlibtick = lastssourcetick = lastsampletick = lastscanlinetick = lasttick = curtick; +	scanlinetiming = hostfreq / 31500; +	ssourceticks = hostfreq / 8000; +	adlibticks = hostfreq / 48000; +	if (doaudio) sampleticks = hostfreq / gensamplerate; +	else sampleticks = -1; +	i8253tickgap = hostfreq / 119318; +} + +void timing() { +	uint8_t i8253chan; + +#ifdef _WIN32 +	QueryPerformanceCounter (&queryperf); +	curtick = queryperf.QuadPart; +#else +	gettimeofday (&tv, NULL); +	curtick = (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; +#endif + +	if (curtick >= (lastscanlinetick + scanlinetiming) ) { +			curscanline = (curscanline + 1) % 525; +			if (curscanline > 479) port3da = 8; +			else port3da = 0; +			if (curscanline & 1) port3da |= 1; +			pit0counter++; +			lastscanlinetick = curtick; +		} + +	if (i8253.active[0]) { //timer interrupt channel on i8253 +			if (curtick >= (lasttick + tickgap) ) { +					lasttick = curtick; +					doirq (0); +				} +		} + +	if (curtick >= (lasti8253tick + i8253tickgap) ) { +			for (i8253chan=0; i8253chan<3; i8253chan++) { +					if (i8253.active[i8253chan]) { +							if (i8253.counter[i8253chan] < 10) i8253.counter[i8253chan] = i8253.chandata[i8253chan]; +							i8253.counter[i8253chan] -= 10; +						} +				} +			lasti8253tick = curtick; +		} + +	if (curtick >= (lastssourcetick + ssourceticks) ) { +			tickssource(); +			lastssourcetick = curtick - (curtick - (lastssourcetick + ssourceticks) ); +		} + +	if (blaster.samplerate > 0) { +			if (curtick >= (lastblastertick + blaster.sampleticks) ) { +					tickBlaster(); +					lastblastertick = curtick - (curtick - (lastblastertick + blaster.sampleticks) ); +				} +		} + +	if (curtick >= (lastsampletick + sampleticks) ) { +			tickaudio(); +			if (slowsystem) { +					tickaudio(); +					tickaudio(); +					tickaudio(); +				} +			lastsampletick = curtick - (curtick - (lastsampletick + sampleticks) ); +		} + +	if (curtick >= (lastadlibtick + adlibticks) ) { +			tickadlib(); +			lastadlibtick = curtick - (curtick - (lastadlibtick + adlibticks) ); +		} +} diff --git a/src/fake86/video.c b/src/fake86/video.c new file mode 100755 index 0000000..95c9c61 --- /dev/null +++ b/src/fake86/video.c @@ -0,0 +1,841 @@ +/* +  Fake86: A portable, open-source 8086 PC emulator. +  Copyright (C)2010-2013 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +/* video.c: many various functions to emulate bits of the video controller. +   a lot of this code is inefficient, and just plain ugly. i plan to rework +   large sections of it soon. */ + +#include <SDL/SDL.h> +#include <stdint.h> +#include <stdio.h> +#include "mutex.h" +#include "cpu.h" + +extern void set_port_write_redirector (uint16_t startport, uint16_t endport, void *callback); +extern void set_port_read_redirector (uint16_t startport, uint16_t endport, void *callback); + +extern SDL_Surface *screen; +extern uint8_t verbose; +extern union _bytewordregs_ regs; +extern uint8_t RAM[0x100000], readonly[0x100000]; +extern uint8_t portram[0x10000]; +extern uint16_t segregs[4]; + +extern uint8_t read86 (uint32_t addr32); +extern uint8_t write86 (uint32_t addr32, uint8_t value); +extern uint8_t scrmodechange; + +uint8_t VRAM[262144], vidmode, cgabg, blankattr, vidgfxmode, vidcolor; +uint16_t cursx, cursy, cols = 80, rows = 25, vgapage, cursorposition, cursorvisible; +uint8_t updatedscreen, clocksafe, port3da, port6, portout16; +uint16_t VGA_SC[0x100], VGA_CRTC[0x100], VGA_ATTR[0x100], VGA_GC[0x100]; +uint32_t videobase= 0xB8000, textbase = 0xB8000, x, y; +uint8_t fontcga[32768]; +uint32_t palettecga[16], palettevga[256]; +uint32_t usefullscreen = 0, usegrabmode = SDL_GRAB_OFF; + +uint8_t latchRGB = 0, latchPal = 0, VGA_latch[4], stateDAC = 0; +uint8_t latchReadRGB = 0, latchReadPal = 0; +uint32_t tempRGB; +uint16_t oldw, oldh; //used when restoring screen mode + +uint32_t rgb(uint32_t r, uint32_t g, uint32_t b) { +#ifdef __BIG_ENDIAN__ +	return ( (r<<24) | (g<<16) | (b<<8) ); +#else +	return (r | (g<<8) | (b<<16) ); +#endif +} + +extern uint32_t nw, nh; +void vidinterrupt() { +	uint32_t tempcalc, memloc, n; +	updatedscreen = 1; +	switch (regs.byteregs[regah]) { //what video interrupt function? +			case 0: //set video mode +				if (verbose) { +						printf ("Set video mode %02Xh\n", regs.byteregs[regal]); +					} +				VGA_SC[0x4] = 0; //VGA modes are in chained mode by default after a mode switch +				//regs.byteregs[regal] = 3; +				switch (regs.byteregs[regal] & 0x7F) { +						case 0: //40x25 mono text +							videobase = textbase; +							cols = 40; +							rows = 25; +							vidcolor = 0; +							vidgfxmode = 0; +							blankattr = 7; +							for (tempcalc = videobase; tempcalc<videobase+16384; tempcalc+=2) { +									RAM[tempcalc] = 0; +									RAM[tempcalc+1] = blankattr; +								} +							break; +						case 1: //40x25 color text +							videobase = textbase; +							cols = 40; +							rows = 25; +							vidcolor = 1; +							vidgfxmode = 0; +							blankattr = 7; +							for (tempcalc = videobase; tempcalc<videobase+16384; tempcalc+=2) { +									RAM[tempcalc] = 0; +									RAM[tempcalc+1] = blankattr; +								} +							portram[0x3D8] = portram[0x3D8] & 0xFE; +							break; +						case 2: //80x25 mono text +							videobase = textbase; +							cols = 80; +							rows = 25; +							vidcolor = 1; +							vidgfxmode = 0; +							blankattr = 7; +							for (tempcalc = videobase; tempcalc<videobase+16384; tempcalc+=2) { +									RAM[tempcalc] = 0; +									RAM[tempcalc+1] = blankattr; +								} +							portram[0x3D8] = portram[0x3D8] & 0xFE; +							break; +						case 3: //80x25 color text +							videobase = textbase; +							cols = 80; +							rows = 25; +							vidcolor = 1; +							vidgfxmode = 0; +							blankattr = 7; +							for (tempcalc = videobase; tempcalc<videobase+16384; tempcalc+=2) { +									RAM[tempcalc] = 0; +									RAM[tempcalc+1] = blankattr; +								} +							portram[0x3D8] = portram[0x3D8] & 0xFE; +							break; +						case 4: +						case 5: //80x25 color text +							videobase = textbase; +							cols = 40; +							rows = 25; +							vidcolor = 1; +							vidgfxmode = 1; +							blankattr = 7; +							for (tempcalc = videobase; tempcalc<videobase+16384; tempcalc+=2) { +									RAM[tempcalc] = 0; +									RAM[tempcalc+1] = blankattr; +								} +							if (regs.byteregs[regal]==4) portram[0x3D9] = 48; +							else portram[0x3D9] = 0; +							break; +						case 6: +							videobase = textbase; +							cols = 80; +							rows = 25; +							vidcolor = 0; +							vidgfxmode = 1; +							blankattr = 7; +							for (tempcalc = videobase; tempcalc<videobase+16384; tempcalc+=2) { +									RAM[tempcalc] = 0; +									RAM[tempcalc+1] = blankattr; +								} +							portram[0x3D8] = portram[0x3D8] & 0xFE; +							break; +						case 127: +							videobase = 0xB8000; +							cols = 90; +							rows = 25; +							vidcolor = 0; +							vidgfxmode = 1; +							for (tempcalc = videobase; tempcalc<videobase+16384; tempcalc++) { +									RAM[tempcalc] = 0; +								} +							portram[0x3D8] = portram[0x3D8] & 0xFE; +							break; +						case 0x9: //320x200 16-color +							videobase = 0xB8000; +							cols = 40; +							rows = 25; +							vidcolor = 1; +							vidgfxmode = 1; +							blankattr = 0; +							if ( (regs.byteregs[regal]&0x80) ==0) for (tempcalc = videobase; tempcalc<videobase+65535; tempcalc+=2) { +										RAM[tempcalc] = 0; +										RAM[tempcalc+1] = blankattr; +									} +							portram[0x3D8] = portram[0x3D8] & 0xFE; +							break; +						case 0xD: //320x200 16-color +						case 0x12: //640x480 16-color +						case 0x13: //320x200 256-color +							videobase = 0xA0000; +							cols = 40; +							rows = 25; +							vidcolor = 1; +							vidgfxmode = 1; +							blankattr = 0; +							for (tempcalc = videobase; tempcalc<videobase+65535; tempcalc+=2) { +									RAM[tempcalc] = 0; +									RAM[tempcalc+1] = blankattr; +								} +							portram[0x3D8] = portram[0x3D8] & 0xFE; +							break; +					} +				vidmode = regs.byteregs[regal] & 0x7F; +				RAM[0x449] = vidmode; +				RAM[0x44A] = (uint8_t) cols; +				RAM[0x44B] = 0; +				RAM[0x484] = (uint8_t) (rows - 1); +				cursx = 0; +				cursy = 0; +				if ( (regs.byteregs[regal] & 0x80) == 0x00) { +						memset (&RAM[0xA0000], 0, 0x1FFFF); +						memset (VRAM, 0, 262144); +					} +				switch (vidmode) { +						case 127: //hercules +							nw = oldw = 720; +							nh = oldh = 348; +							scrmodechange = 1; +							break; +						case 0x12: +							nw = oldw = 640; +							nh = oldh = 480; +							scrmodechange = 1; +							break; +						case 0x13: +							oldw = 640; +							oldh = 400; +							nw = 320; +							nh = 200; +							scrmodechange = 1; +							break; +						default: +							nw = oldw = 640; +							nh = oldh = 400; +							scrmodechange = 1; +							break; +					} +				break; +			case 0x10: //VGA DAC functions +				switch (regs.byteregs[regal]) { +						case 0x10: //set individual DAC register +							palettevga[getreg16 (regbx) ] = rgb((regs.byteregs[regdh] & 63) << 2, (regs.byteregs[regch] & 63) << 2, (regs.byteregs[regcl] & 63) << 2); +							break; +						case 0x12: //set block of DAC registers +							memloc = segregs[reges]*16+getreg16 (regdx); +							for (n=getreg16 (regbx); n< (uint32_t) (getreg16 (regbx) +getreg16 (regcx) ); n++) { +									palettevga[n] = rgb(read86(memloc) << 2, read86(memloc + 1) << 2, read86(memloc + 2) << 2); +									memloc += 3; +								} +					} +				break; +			case 0x1A: //get display combination code (ps, vga/mcga) +				regs.byteregs[regal] = 0x1A; +				regs.byteregs[regbl] = 0x8; +				break; +		} +} + +void initcga() { +	FILE *fontfile; +	fontfile = fopen (PATH_DATAFILES "asciivga.dat", "rb"); +	if (fontfile==NULL) { +			printf ("FATAL: Cannot open " PATH_DATAFILES "asciivga!\n"); +			exit (1); +		} +	fread (&fontcga[0], 32768, 1, fontfile); +	fclose (fontfile); + +	palettecga[0] = 0; +	palettecga[1] = rgb (0, 0, 0xAA); +	palettecga[2] = rgb (0, 0xAA, 0); +	palettecga[3] = rgb (0, 0xAA, 0xAA); +	palettecga[4] = rgb (0xAA, 0, 0); +	palettecga[5] = rgb (0xAA, 0, 0xAA); +	palettecga[6] = rgb (0xAA, 0x55, 0); +	palettecga[7] = rgb (0xAA, 0xAA, 0xAA); +	palettecga[8] = rgb (0x55, 0x55, 0x55); +	palettecga[9] = rgb (0x55, 0x55, 0xFF); +	palettecga[10] = rgb (0x55, 0xFF, 0x55); +	palettecga[11] = rgb (0x55, 0xFF, 0xFF); +	palettecga[12] = rgb (0xFF, 0x55, 0x55); +	palettecga[13] = rgb (0xFF, 0x55, 0xFF); +	palettecga[14] = rgb (0xFF, 0xFF, 0x55); +	palettecga[15] = rgb (0xFF, 0xFF, 0xFF); +	palettevga[0] = rgb (0, 0, 0); +	palettevga[1] = rgb (0, 0, 169); +	palettevga[2] = rgb (0, 169, 0); +	palettevga[3] = rgb (0, 169, 169); +	palettevga[4] = rgb (169, 0, 0); +	palettevga[5] = rgb (169, 0, 169); +	palettevga[6] = rgb (169, 169, 0); +	palettevga[7] = rgb (169, 169, 169); +	palettevga[8] = rgb (0, 0, 84); +	palettevga[9] = rgb (0, 0, 255); +	palettevga[10] = rgb (0, 169, 84); +	palettevga[11] = rgb (0, 169, 255); +	palettevga[12] = rgb (169, 0, 84); +	palettevga[13] = rgb (169, 0, 255); +	palettevga[14] = rgb (169, 169, 84); +	palettevga[15] = rgb (169, 169, 255); +	palettevga[16] = rgb (0, 84, 0); +	palettevga[17] = rgb (0, 84, 169); +	palettevga[18] = rgb (0, 255, 0); +	palettevga[19] = rgb (0, 255, 169); +	palettevga[20] = rgb (169, 84, 0); +	palettevga[21] = rgb (169, 84, 169); +	palettevga[22] = rgb (169, 255, 0); +	palettevga[23] = rgb (169, 255, 169); +	palettevga[24] = rgb (0, 84, 84); +	palettevga[25] = rgb (0, 84, 255); +	palettevga[26] = rgb (0, 255, 84); +	palettevga[27] = rgb (0, 255, 255); +	palettevga[28] = rgb (169, 84, 84); +	palettevga[29] = rgb (169, 84, 255); +	palettevga[30] = rgb (169, 255, 84); +	palettevga[31] = rgb (169, 255, 255); +	palettevga[32] = rgb (84, 0, 0); +	palettevga[33] = rgb (84, 0, 169); +	palettevga[34] = rgb (84, 169, 0); +	palettevga[35] = rgb (84, 169, 169); +	palettevga[36] = rgb (255, 0, 0); +	palettevga[37] = rgb (255, 0, 169); +	palettevga[38] = rgb (255, 169, 0); +	palettevga[39] = rgb (255, 169, 169); +	palettevga[40] = rgb (84, 0, 84); +	palettevga[41] = rgb (84, 0, 255); +	palettevga[42] = rgb (84, 169, 84); +	palettevga[43] = rgb (84, 169, 255); +	palettevga[44] = rgb (255, 0, 84); +	palettevga[45] = rgb (255, 0, 255); +	palettevga[46] = rgb (255, 169, 84); +	palettevga[47] = rgb (255, 169, 255); +	palettevga[48] = rgb (84, 84, 0); +	palettevga[49] = rgb (84, 84, 169); +	palettevga[50] = rgb (84, 255, 0); +	palettevga[51] = rgb (84, 255, 169); +	palettevga[52] = rgb (255, 84, 0); +	palettevga[53] = rgb (255, 84, 169); +	palettevga[54] = rgb (255, 255, 0); +	palettevga[55] = rgb (255, 255, 169); +	palettevga[56] = rgb (84, 84, 84); +	palettevga[57] = rgb (84, 84, 255); +	palettevga[58] = rgb (84, 255, 84); +	palettevga[59] = rgb (84, 255, 255); +	palettevga[60] = rgb (255, 84, 84); +	palettevga[61] = rgb (255, 84, 255); +	palettevga[62] = rgb (255, 255, 84); +	palettevga[63] = rgb (255, 255, 255); +	palettevga[64] = rgb (255, 125, 125); +	palettevga[65] = rgb (255, 157, 125); +	palettevga[66] = rgb (255, 190, 125); +	palettevga[67] = rgb (255, 222, 125); +	palettevga[68] = rgb (255, 255, 125); +	palettevga[69] = rgb (222, 255, 125); +	palettevga[70] = rgb (190, 255, 125); +	palettevga[71] = rgb (157, 255, 125); +	palettevga[72] = rgb (125, 255, 125); +	palettevga[73] = rgb (125, 255, 157); +	palettevga[74] = rgb (125, 255, 190); +	palettevga[75] = rgb (125, 255, 222); +	palettevga[76] = rgb (125, 255, 255); +	palettevga[77] = rgb (125, 222, 255); +	palettevga[78] = rgb (125, 190, 255); +	palettevga[79] = rgb (125, 157, 255); +	palettevga[80] = rgb (182, 182, 255); +	palettevga[81] = rgb (198, 182, 255); +	palettevga[82] = rgb (218, 182, 255); +	palettevga[83] = rgb (234, 182, 255); +	palettevga[84] = rgb (255, 182, 255); +	palettevga[85] = rgb (255, 182, 234); +	palettevga[86] = rgb (255, 182, 218); +	palettevga[87] = rgb (255, 182, 198); +	palettevga[88] = rgb (255, 182, 182); +	palettevga[89] = rgb (255, 198, 182); +	palettevga[90] = rgb (255, 218, 182); +	palettevga[91] = rgb (255, 234, 182); +	palettevga[92] = rgb (255, 255, 182); +	palettevga[93] = rgb (234, 255, 182); +	palettevga[94] = rgb (218, 255, 182); +	palettevga[95] = rgb (198, 255, 182); +	palettevga[96] = rgb (182, 255, 182); +	palettevga[97] = rgb (182, 255, 198); +	palettevga[98] = rgb (182, 255, 218); +	palettevga[99] = rgb (182, 255, 234); +	palettevga[100] = rgb (182, 255, 255); +	palettevga[101] = rgb (182, 234, 255); +	palettevga[102] = rgb (182, 218, 255); +	palettevga[103] = rgb (182, 198, 255); +	palettevga[104] = rgb (0, 0, 113); +	palettevga[105] = rgb (28, 0, 113); +	palettevga[106] = rgb (56, 0, 113); +	palettevga[107] = rgb (84, 0, 113); +	palettevga[108] = rgb (113, 0, 113); +	palettevga[109] = rgb (113, 0, 84); +	palettevga[110] = rgb (113, 0, 56); +	palettevga[111] = rgb (113, 0, 28); +	palettevga[112] = rgb (113, 0, 0); +	palettevga[113] = rgb (113, 28, 0); +	palettevga[114] = rgb (113, 56, 0); +	palettevga[115] = rgb (113, 84, 0); +	palettevga[116] = rgb (113, 113, 0); +	palettevga[117] = rgb (84, 113, 0); +	palettevga[118] = rgb (56, 113, 0); +	palettevga[119] = rgb (28, 113, 0); +	palettevga[120] = rgb (0, 113, 0); +	palettevga[121] = rgb (0, 113, 28); +	palettevga[122] = rgb (0, 113, 56); +	palettevga[123] = rgb (0, 113, 84); +	palettevga[124] = rgb (0, 113, 113); +	palettevga[125] = rgb (0, 84, 113); +	palettevga[126] = rgb (0, 56, 113); +	palettevga[127] = rgb (0, 28, 113); +	palettevga[128] = rgb (56, 56, 113); +	palettevga[129] = rgb (68, 56, 113); +	palettevga[130] = rgb (84, 56, 113); +	palettevga[131] = rgb (97, 56, 113); +	palettevga[132] = rgb (113, 56, 113); +	palettevga[133] = rgb (113, 56, 97); +	palettevga[134] = rgb (113, 56, 84); +	palettevga[135] = rgb (113, 56, 68); +	palettevga[136] = rgb (113, 56, 56); +	palettevga[137] = rgb (113, 68, 56); +	palettevga[138] = rgb (113, 84, 56); +	palettevga[139] = rgb (113, 97, 56); +	palettevga[140] = rgb (113, 113, 56); +	palettevga[141] = rgb (97, 113, 56); +	palettevga[142] = rgb (84, 113, 56); +	palettevga[143] = rgb (68, 113, 56); +	palettevga[144] = rgb (56, 113, 56); +	palettevga[145] = rgb (56, 113, 68); +	palettevga[146] = rgb (56, 113, 84); +	palettevga[147] = rgb (56, 113, 97); +	palettevga[148] = rgb (56, 113, 113); +	palettevga[149] = rgb (56, 97, 113); +	palettevga[150] = rgb (56, 84, 113); +	palettevga[151] = rgb (56, 68, 113); +	palettevga[152] = rgb (80, 80, 113); +	palettevga[153] = rgb (89, 80, 113); +	palettevga[154] = rgb (97, 80, 113); +	palettevga[155] = rgb (105, 80, 113); +	palettevga[156] = rgb (113, 80, 113); +	palettevga[157] = rgb (113, 80, 105); +	palettevga[158] = rgb (113, 80, 97); +	palettevga[159] = rgb (113, 80, 89); +	palettevga[160] = rgb (113, 80, 80); +	palettevga[161] = rgb (113, 89, 80); +	palettevga[162] = rgb (113, 97, 80); +	palettevga[163] = rgb (113, 105, 80); +	palettevga[164] = rgb (113, 113, 80); +	palettevga[165] = rgb (105, 113, 80); +	palettevga[166] = rgb (97, 113, 80); +	palettevga[167] = rgb (89, 113, 80); +	palettevga[168] = rgb (80, 113, 80); +	palettevga[169] = rgb (80, 113, 89); +	palettevga[170] = rgb (80, 113, 97); +	palettevga[171] = rgb (80, 113, 105); +	palettevga[172] = rgb (80, 113, 113); +	palettevga[173] = rgb (80, 105, 113); +	palettevga[174] = rgb (80, 97, 113); +	palettevga[175] = rgb (80, 89, 113); +	palettevga[176] = rgb (0, 0, 64); +	palettevga[177] = rgb (16, 0, 64); +	palettevga[178] = rgb (32, 0, 64); +	palettevga[179] = rgb (48, 0, 64); +	palettevga[180] = rgb (64, 0, 64); +	palettevga[181] = rgb (64, 0, 48); +	palettevga[182] = rgb (64, 0, 32); +	palettevga[183] = rgb (64, 0, 16); +	palettevga[184] = rgb (64, 0, 0); +	palettevga[185] = rgb (64, 16, 0); +	palettevga[186] = rgb (64, 32, 0); +	palettevga[187] = rgb (64, 48, 0); +	palettevga[188] = rgb (64, 64, 0); +	palettevga[189] = rgb (48, 64, 0); +	palettevga[190] = rgb (32, 64, 0); +	palettevga[191] = rgb (16, 64, 0); +	palettevga[192] = rgb (0, 64, 0); +	palettevga[193] = rgb (0, 64, 16); +	palettevga[194] = rgb (0, 64, 32); +	palettevga[195] = rgb (0, 64, 48); +	palettevga[196] = rgb (0, 64, 64); +	palettevga[197] = rgb (0, 48, 64); +	palettevga[198] = rgb (0, 32, 64); +	palettevga[199] = rgb (0, 16, 64); +	palettevga[200] = rgb (32, 32, 64); +	palettevga[201] = rgb (40, 32, 64); +	palettevga[202] = rgb (48, 32, 64); +	palettevga[203] = rgb (56, 32, 64); +	palettevga[204] = rgb (64, 32, 64); +	palettevga[205] = rgb (64, 32, 56); +	palettevga[206] = rgb (64, 32, 48); +	palettevga[207] = rgb (64, 32, 40); +	palettevga[208] = rgb (64, 32, 32); +	palettevga[209] = rgb (64, 40, 32); +	palettevga[210] = rgb (64, 48, 32); +	palettevga[211] = rgb (64, 56, 32); +	palettevga[212] = rgb (64, 64, 32); +	palettevga[213] = rgb (56, 64, 32); +	palettevga[214] = rgb (48, 64, 32); +	palettevga[215] = rgb (40, 64, 32); +	palettevga[216] = rgb (32, 64, 32); +	palettevga[217] = rgb (32, 64, 40); +	palettevga[218] = rgb (32, 64, 48); +	palettevga[219] = rgb (32, 64, 56); +	palettevga[220] = rgb (32, 64, 64); +	palettevga[221] = rgb (32, 56, 64); +	palettevga[222] = rgb (32, 48, 64); +	palettevga[223] = rgb (32, 40, 64); +	palettevga[224] = rgb (44, 44, 64); +	palettevga[225] = rgb (48, 44, 64); +	palettevga[226] = rgb (52, 44, 64); +	palettevga[227] = rgb (60, 44, 64); +	palettevga[228] = rgb (64, 44, 64); +	palettevga[229] = rgb (64, 44, 60); +	palettevga[230] = rgb (64, 44, 52); +	palettevga[231] = rgb (64, 44, 48); +	palettevga[232] = rgb (64, 44, 44); +	palettevga[233] = rgb (64, 48, 44); +	palettevga[234] = rgb (64, 52, 44); +	palettevga[235] = rgb (64, 60, 44); +	palettevga[236] = rgb (64, 64, 44); +	palettevga[237] = rgb (60, 64, 44); +	palettevga[238] = rgb (52, 64, 44); +	palettevga[239] = rgb (48, 64, 44); +	palettevga[240] = rgb (44, 64, 44); +	palettevga[241] = rgb (44, 64, 48); +	palettevga[242] = rgb (44, 64, 52); +	palettevga[243] = rgb (44, 64, 60); +	palettevga[244] = rgb (44, 64, 64); +	palettevga[245] = rgb (44, 60, 64); +	palettevga[246] = rgb (44, 52, 64); +	palettevga[247] = rgb (44, 48, 64); +	palettevga[248] = rgb (0, 0, 0); +	palettevga[249] = rgb (0, 0, 0); +	palettevga[250] = rgb (0, 0, 0); +	palettevga[251] = rgb (0, 0, 0); +	palettevga[252] = rgb (0, 0, 0); +	palettevga[253] = rgb (0, 0, 0); +	palettevga[254] = rgb (0, 0, 0); +	palettevga[255] = rgb (0, 0, 0); +} + +uint16_t vtotal = 0; +void outVGA (uint16_t portnum, uint8_t value) { +	static uint8_t oldah, oldal; +	uint8_t flip3c0 = 0; +	updatedscreen = 1; +	switch (portnum) { +			case 0x3B8: //hercules support +				if ( ( (value & 2) == 2) && (vidmode != 127) ) { +						oldah = regs.byteregs[regah]; +						oldal = regs.byteregs[regal]; +						regs.byteregs[regah] = 0; +						regs.byteregs[regal] = 127; +						vidinterrupt(); +						regs.byteregs[regah] = oldah; +						regs.byteregs[regal] = oldal; +					} +				if (value & 0x80) videobase = 0xB8000; +				else videobase = 0xB0000; +				break; +			case 0x3C0: +				if (flip3c0) { +						flip3c0 = 0; +						portram[0x3C0] = value & 255; +						return; +					} +				else { +						flip3c0 = 1; +						VGA_ATTR[portram[0x3C0]] = value & 255; +						return; +					} +			case 0x3C4: //sequence controller index +				portram[0x3C4] = value & 255; +				//if (portout16) VGA_SC[value & 255] = value >> 8; +				break; +			case 0x3C5: //sequence controller data +				VGA_SC[portram[0x3C4]] = value & 255; +				/*if (portram[0x3C4] == 2) { +				printf("VGA_SC[2] = %02X\n", value); +				}*/ +				break; +			case 0x3D4: //CRT controller index +				portram[0x3D4] = value & 255; +				//if (portout16) VGA_CRTC[value & 255] = value >> 8; +				break; +			case 0x3C7: //color index register (read operations) +				latchReadPal = value & 255; +				latchReadRGB = 0; +				stateDAC = 0; +				break; +			case 0x3C8: //color index register (write operations) +				latchPal = value & 255; +				latchRGB = 0; +				tempRGB = 0; +				stateDAC = 3; +				break; +			case 0x3C9: //RGB data register +				value = value & 63; +				switch (latchRGB) { +#ifdef __BIG_ENDIAN__ +						case 0: //red +							tempRGB = value << 26; +							break; +						case 1: //green +							tempRGB |= value << 18; +							break; +						case 2: //blue +							tempRGB |= value << 10; +							palettevga[latchPal] = tempRGB; +							latchPal = latchPal + 1; +							break; +#else +						case 0: //red +							tempRGB = value << 2; +							break; +						case 1: //green +							tempRGB |= value << 10; +							break; +						case 2: //blue +							tempRGB |= value << 18; +							palettevga[latchPal] = tempRGB; +							latchPal = latchPal + 1; +							break; +#endif +					} +				latchRGB = (latchRGB + 1) % 3; +				break; +			case 0x3D5: //cursor position latch +				VGA_CRTC[portram[0x3D4]] = value & 255; +				if (portram[0x3D4]==0xE) cursorposition = (cursorposition&0xFF) | (value<<8); +				else if (portram[0x3D4]==0xF) cursorposition = (cursorposition&0xFF00) |value; +				cursy = cursorposition/cols; +				cursx = cursorposition%cols; +				if (portram[0x3D4] == 6) { +						vtotal = value | ( ( (uint16_t) VGA_GC[7] & 1) << 8) | ( ( (VGA_GC[7] & 32) ? 1 : 0) << 9); +						//printf("Vertical total: %u\n", vtotal); +					} +				break; +			case 0x3CF: +				VGA_GC[portram[0x3CE]] = value; +				break; +			default: +				portram[portnum] = value; +		} +} + +uint8_t inVGA (uint16_t portnum) { +	switch (portnum) { +			case 0x3C1: +				return ( (uint8_t) VGA_ATTR[portram[0x3C0]]); +			case 0x3C5: +				return ( (uint8_t) VGA_SC[portram[0x3C4]]); +			case 0x3D5: +				return ( (uint8_t) VGA_CRTC[portram[0x3D4]]); +			case 0x3C7: //DAC state +				return (stateDAC); +			case 0x3C8: //palette index +				return (latchReadPal); +			case 0x3C9: //RGB data register +				switch (latchReadRGB++) { +#ifdef __BIG_ENDIAN__ +						case 0: //blue +							return ( (palettevga[latchReadPal] >> 26) & 63); +						case 1: //green +							return ( (palettevga[latchReadPal] >> 18) & 63); +						case 2: //red +							latchReadRGB = 0; +							return ( (palettevga[latchReadPal++] >> 10) & 63); +#else +						case 0: //blue +							return ( (palettevga[latchReadPal] >> 2) & 63); +						case 1: //green +							return ( (palettevga[latchReadPal] >> 10) & 63); +						case 2: //red +							latchReadRGB = 0; +							return ( (palettevga[latchReadPal++] >> 18) & 63); +#endif +					} +			case 0x3DA: +				return (port3da); +		} +	return (portram[portnum]); //this won't be reached, but without it the compiler gives a warning +} + +#define shiftVGA(value) {\ +	for (cnt=0; cnt<(VGA_GC[3] & 7); cnt++) {\ +		value = (value >> 1) | ((value & 1) << 7);\ +	}\ +} + +#define logicVGA(curval, latchval) {\ +	switch ((VGA_GC[3]>>3) & 3) {\ +		   case 1: curval &= latchval; break;\ +		   case 2: curval |= latchval; break;\ +		   case 3: curval ^= latchval; break;\ +	}\ +} + +uint8_t lastmode = 0, tempvalue; +void writeVGA (uint32_t addr32, uint8_t value) { +	uint32_t planesize; +	uint8_t curval, tempand, cnt; +	updatedscreen = 1; +	planesize = 0x10000; +	//if (lastmode != VGA_GC[5] & 3) printf("write mode %u\n", VGA_GC[5] & 3); +	//lastmode = VGA_GC[5] & 3; +	switch (VGA_GC[5] & 3) { //get write mode +			case 0: +				shiftVGA (value); +				if (VGA_SC[2] & 1) { +						if (VGA_GC[1] & 1) +							if (VGA_GC[0] & 1) curval = 255; +							else curval = 0; +						else curval = value; +						logicVGA (curval, VGA_latch[0]); +						curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[0]); +						VRAM[addr32] = curval; +					} +				if (VGA_SC[2] & 2) { +						if (VGA_GC[1] & 2) +							if (VGA_GC[0] & 2) curval = 255; +							else curval = 0; +						else curval = value; +						logicVGA (curval, VGA_latch[1]); +						curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[1]); +						VRAM[addr32+planesize] = curval; +					} +				if (VGA_SC[2] & 4) { +						if (VGA_GC[1] & 4) +							if (VGA_GC[0] & 4) curval = 255; +							else curval = 0; +						else curval = value; +						logicVGA (curval, VGA_latch[2]); +						curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[2]); +						VRAM[addr32+planesize*2] = curval; +					} +				if (VGA_SC[2] & 8) { +						if (VGA_GC[1] & 8) +							if (VGA_GC[0] & 8) curval = 255; +							else curval = 0; +						else curval = value; +						logicVGA (curval, VGA_latch[3]); +						curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[3]); +						VRAM[addr32+planesize*3] = curval; +					} +				break; +			case 1: +				if (VGA_SC[2] & 1) VRAM[addr32] = VGA_latch[0]; +				if (VGA_SC[2] & 2) VRAM[addr32+planesize] = VGA_latch[1]; +				if (VGA_SC[2] & 4) VRAM[addr32+planesize*2] = VGA_latch[2]; +				if (VGA_SC[2] & 8) VRAM[addr32+planesize*3] = VGA_latch[3]; +				break; +			case 2: +				if (VGA_SC[2] & 1) { +						if (VGA_GC[1] & 1) +							if (value & 1) curval = 255; +							else curval = 0; +						else curval = value; +						logicVGA (curval, VGA_latch[0]); +						curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[0]); +						VRAM[addr32] = curval; +					} +				if (VGA_SC[2] & 2) { +						if (VGA_GC[1] & 2) +							if (value & 2) curval = 255; +							else curval = 0; +						else curval = value; +						logicVGA (curval, VGA_latch[1]); +						curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[1]); +						VRAM[addr32+planesize] = curval; +					} +				if (VGA_SC[2] & 4) { +						if (VGA_GC[1] & 4) +							if (value & 4) curval = 255; +							else curval = 0; +						else curval = value; +						logicVGA (curval, VGA_latch[2]); +						curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[2]); +						VRAM[addr32+planesize*2] = curval; +					} +				if (VGA_SC[2] & 8) { +						if (VGA_GC[1] & 8) +							if (value & 8) curval = 255; +							else curval = 0; +						else curval = value; +						logicVGA (curval, VGA_latch[3]); +						curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[3]); +						VRAM[addr32+planesize*3] = curval; +					} +				break; +			case 3: +				tempand = value & VGA_GC[8]; +				shiftVGA (value); +				if (VGA_SC[2] & 1) { +						if (VGA_GC[0] & 1) curval = 255; +						else curval = 0; +						//logicVGA (curval, VGA_latch[0]); +						curval = (~tempand & curval) | (tempand & VGA_latch[0]); +						VRAM[addr32] = curval; +					} +				if (VGA_SC[2] & 2) { +						if (VGA_GC[0] & 2) curval = 255; +						else curval = 0; +						//logicVGA (curval, VGA_latch[1]); +						curval = (~tempand & curval) | (tempand & VGA_latch[1]); +						VRAM[addr32+planesize] = curval; +					} +				if (VGA_SC[2] & 4) { +						if (VGA_GC[0] & 4) curval = 255; +						else curval = 0; +						//logicVGA (curval, VGA_latch[2]); +						curval = (~tempand & curval) | (tempand & VGA_latch[2]); +						VRAM[addr32+planesize*2] = curval; +					} +				if (VGA_SC[2] & 8) { +						if (VGA_GC[0] & 8) curval = 255; +						else curval = 0; +						//logicVGA (curval, VGA_latch[3]); +						curval = (~tempand & curval) | (tempand & VGA_latch[3]); +						VRAM[addr32+planesize*3] = curval; +					} +				break; +		} +} + +uint8_t readmode; +uint32_t readmap; +uint8_t readVGA (uint32_t addr32) { +	uint32_t planesize; +	planesize = 0x10000; + +	VGA_latch[0] = VRAM[addr32]; +	VGA_latch[1] = VRAM[addr32+planesize]; +	VGA_latch[2] = VRAM[addr32+planesize*2]; +	VGA_latch[3] = VRAM[addr32+planesize*3]; +	if (VGA_SC[2] & 1) return (VRAM[addr32]); +	if (VGA_SC[2] & 2) return (VRAM[addr32+planesize]); +	if (VGA_SC[2] & 4) return (VRAM[addr32+planesize*2]); +	if (VGA_SC[2] & 8) return (VRAM[addr32+planesize*3]); +	return (0); //this won't be reached, but without it some compilers give a warning +} + +void initVideoPorts() { +	set_port_write_redirector (0x3B0, 0x3DA, &outVGA); +	set_port_read_redirector (0x3B0, 0x3DA, &inVGA); +} diff --git a/src/fake86/win32/menus.c b/src/fake86/win32/menus.c new file mode 100644 index 0000000..738798a --- /dev/null +++ b/src/fake86/win32/menus.c @@ -0,0 +1,161 @@ +#include "../config.h" +#include "../../../win32/resource.h" +#include <Windows.h> +#include <SDL/SDL.h> +#include <SDL/SDL_syswm.h> + +HWND myWindow; +HINSTANCE myInstance; +HMENU myMenu; +WNDPROC oldProc; +HWND GetHwnd(); +HICON myIcon; +void SetWndProc(); +void MenuItemClick(WPARAM wParam); + +void ShowMenu() { +	SetMenu(myWindow, myMenu); +} + +void HideMenu() { +	SetMenu(myWindow, NULL); +} + +void initmenus() { +	myWindow = GetHwnd(); +	myInstance = GetModuleHandle(NULL); +	myMenu = LoadMenu(NULL, MAKEINTRESOURCE(IDR_MENU1)); +	ShowMenu(); +	SetWndProc(); +	//myIcon = LoadIcon(myInstance, MAKEINTRESOURCE(IDI_ICON1)); +	//SetClassLong(myWindow, GCLP_HICON, (LONG)(uint64_t)myIcon); +	//SetClassLong(myWindow, GCLP_HICONSM, (LONG)(uint64_t)myIcon); + +	return; +} + +HWND GetHwnd() { +	SDL_SysWMinfo wmi; +	SDL_VERSION(&wmi.version); + +	if (!SDL_GetWMInfo(&wmi)) return(NULL); +	return(wmi.window); +} + +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { +	switch (msg) { +		case WM_COMMAND: +			MenuItemClick(wParam); +			return(TRUE); +	} + +	return(CallWindowProc(oldProc, hwnd, msg, wParam, lParam)); +} + +void SetWndProc() { +	oldProc = (WNDPROC)SetWindowLong(myWindow, GWL_WNDPROC, (LONG_PTR)WndProc); +} + +extern uint8_t running, bootdrive, dohardreset, scrmodechange; +extern uint16_t constantw, constanth; +uint8_t insertdisk (uint8_t drivenum, char *filename); +uint8_t ejectdisk (uint8_t drivenum); + +void MenuItemClick(WPARAM wParam) { +	OPENFILENAME of_dlg; +	uint8_t filename[MAX_PATH] = { 0 }; + +	switch (LOWORD(wParam)) { +		//file menu +		case ID_FILE_EXIT: +			running = 0; +			break; + +		//emulation menu +		case ID_EMULATION_HARDRESETEMULATOR: +			dohardreset = 1; +			break; + +		case ID_FLOPPY0_INSERTDISK: +		case ID_FLOPPY1_INSERTDISK: +			memset(&of_dlg, 0, sizeof(of_dlg)); +			of_dlg.lStructSize = sizeof(of_dlg); +			of_dlg.lpstrTitle = "Open disk image"; +			of_dlg.hInstance = NULL; +			of_dlg.lpstrFile = filename; +			of_dlg.lpstrFilter = "Floppy disk images (*.img)\0*.img\0All files (*.*)\0*.*\0\0"; +			of_dlg.nMaxFile = MAX_PATH; +			of_dlg.Flags = OFN_FILEMUSTEXIST | OFN_LONGNAMES; +			if (GetOpenFileName(&of_dlg)) { +				if (LOWORD(wParam) == ID_FLOPPY0_INSERTDISK) { +					insertdisk(0, (char *)of_dlg.lpstrFile); +					if (bootdrive == 255) bootdrive = 0; +				} else insertdisk(1, (char *)of_dlg.lpstrFile); +			} +			break; +		case ID_FLOPPY0_EJECTDISK: +			ejectdisk(0); +			break; +		case ID_FLOPPY1_EJECTDISK: +			ejectdisk(1); +			break; + +		case ID_HARDDRIVE0_INSERTDISK: +		case ID_HARDDRIVE1_INSERTDISK: +			memset(&of_dlg, 0, sizeof(of_dlg)); +			of_dlg.lStructSize = sizeof(of_dlg); +			of_dlg.lpstrTitle = "Open disk image"; +			of_dlg.hInstance = NULL; +			of_dlg.lpstrFile = filename; +			of_dlg.lpstrFilter = "Raw disk images (*.raw, *.img)\0*.raw;*.img\0All files (*.*)\0*.*\0\0"; +			of_dlg.nMaxFile = MAX_PATH; +			of_dlg.Flags = OFN_FILEMUSTEXIST | OFN_LONGNAMES; +			if (GetOpenFileName(&of_dlg)) { +				if (LOWORD(wParam) == ID_HARDDRIVE0_INSERTDISK) { +					insertdisk(128, (char *)of_dlg.lpstrFile); +					if (bootdrive == 255) bootdrive = 128; +				} else insertdisk(129, (char *)of_dlg.lpstrFile); +			} +			break; +		case ID_HARDDRIVE0_EJECTDISK: +			ejectdisk(128); +			break; +		case ID_HARDDRIVE1_EJECTDISK: +			ejectdisk(129); +			break; + +		case ID_SETBOOTDRIVE_FLOPPY0: +			bootdrive = 0; +			break; +		case ID_SETBOOTDRIVE_HARDDRIVE0: +			bootdrive = 128; +			break; + +		//video menu +		case ID_WINDOWRESOLUTION_AUTOMATIC: +			constantw = 0; +			constanth = 0; +			scrmodechange = 1; +			break; +		case ID_WINDOWRESOLUTION_320X200: +			constantw = 320; +			constanth = 200; +			scrmodechange = 1; +			break; +		case ID_WINDOWRESOLUTION_640X400: +			constantw = 640; +			constanth = 400; +			scrmodechange = 1; +			break; +		case ID_WINDOWRESOLUTION_960X600: +			constantw = 960; +			constanth = 600; +			scrmodechange = 1; +			break; +		case ID_WINDOWRESOLUTION_1280X800: +			constantw = 1280; +			constanth = 800; +			scrmodechange = 1; +			break; +	} +} diff --git a/src/fake86/win32/resource.h b/src/fake86/win32/resource.h Binary files differnew file mode 100644 index 0000000..d6b04fe --- /dev/null +++ b/src/fake86/win32/resource.h diff --git a/src/imagegen/imagegen.c b/src/imagegen/imagegen.c new file mode 100755 index 0000000..5a5cafb --- /dev/null +++ b/src/imagegen/imagegen.c @@ -0,0 +1,68 @@ +/* +  Imagegen: A blank disk image generator for use with Fake86 +  Copyright (C)2010-2012 Mike Chambers + +  This program is free software; you can redistribute it and/or +  modify it under the terms of the GNU General Public License +  as published by the Free Software Foundation; either version 2 +  of the License, or (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +*/ + +#include <stdio.h> +#include <stdlib.h> + +const char *build = "Imagegen v1.1"; + +int main(int argc, char *argv[]) { +	FILE			*image; +	char			*blank; +	unsigned long	i, size; + +	printf("%s (c)2010-2012 Mike Chambers\n", build); +	printf("[Blank disk image generator for Fake86]\n\n"); + +	if(argc < 3) { +		printf("Usage syntax:\n"); +		printf("    imagegen imagefile size\n\n"); +		printf("imagefile denotes the filename of the new disk image to create.\n"); +		printf("size denotes the size in megabytes that it should be.\n"); +		return(1); +	} + +	size = atoi(argv[2]); +	if((size > 503) || !size) { +		printf("Invalid size specified! Valid range is 1 to 503 MB.\n"); +		return(1); +	} + +	image = fopen(argv[1], "wb"); +	if(image == NULL) { +		printf("Unable to create new file: %s\n", argv[1]); +		return(1); +	} + +	blank = (void *)malloc(1048576); +	if (blank == NULL) { +		printf("Unable to allocate enough memory!\n"); +		return(1); +	} + +	printf("Please wait, generating new image...\n"); + +	for(i = 0; i < size; i++) { +		fwrite(&blank[0], 1048576, 1, image); +		printf("\rWriting to file: %u MB", i); +	} + +	printf(" complete.\n"); +	return(0); +}  | 
