Super 8086 Box, my home-built PC XT emulator with Adlib, MPU-401, joystick and NE2000 support

0.00 avg. rating (0% score) - 0 votes

In 2019 I played with 8086 Tiny Plus v1.34, developed by Jaybert Software and improved from the original 8086 Tiny by Adrian Cable. The software emulates a 8088 XT Machine with 640KB of RAM, MCGA adapter, PC speaker sound, mouse and serial port support. There is however no full-screen support, although zoom level can be set to either x1 or x2, and graphics mode can be toggled between SDL or Win32 API. Although up to 512MB hard disk drive is supported, the emulator only supports booting from floppy and not hard disk). There is also no emulation support for a math co-processor. Despite the limitation, 8086 Tiny Plus v1.34 was good enough for me and many of my beloved DOS apps and games run flawlessly on it.

Still, I wasn’t totally happy with the various limitations of this emulator and decided to spend time improving it further. After a month of hard work, I came up with an improved product, Super 8086 Box, which provided me with many of the features that I wanted such as Adlib, MPU-401, Joystick and NE2000. This article will discuss the work I have done as well as provide you with the links to download the new Super 8086 Box.

Limitations of 8086 Tiny Plus

After a few days playing around with 8086 Tiny Plus, I found a few issues with 8086 Tiny Plus, summarized below:

  • No math co-processor support
  • No hard disk booting support (only floppy booting)
  • No support for secondary floppy drive (e.g. drive B:)
  • No full-screen support, only x1 and x2 zoom modes.
  • Does not implement all CGA modes, thus some games will not work
  • No support for parallel port (output redirection or printer)
  • Emulated CPU does not support IMUL and has issues with REP INSW instruction
  • Unfaithful PC speaker sound emulation.

The following are features I would love to be available

  • Support for Disney/Coovox, Adlib/Sound Blaster sound card
  • Support for MT-32 MIDI device
  • Support for NE2000 compatible network card
  • Support for USB joystick
  • Support for BASIC in ROM
  • Make the integrated debugger more user-friendly. The built-in debugger is very basic.

After around a month of work, around 80% of the items on the list were completed. The code changes that I have made to fix the issues or to add new features will be discussed below.

Adding NE2000 support

Although 8086 Tiny Plus supports serial networking, setting it up as designed by the author is troublesome and useless in this day and age. I therefore decided to add NE2000 support using the NE2000 emulation code from the TEMU project here. To get it working, some understanding of how the code is supposed to work is needed. In its simplest forms, whether from our emulator, DOSBos or QEMU, most peripherals are emulated as state machines. The peripheral state would then be initialized as the emulator starts up, and updates as soon as it receive commands from the emulator. The emulator would then need to capture read/write accesses on the peripheral port, and handle these appropriately.

For the NE2000, using the above library, I will need to reset the NE2000 state and set IRQ as well as various event handlers:

ne2000_reset(NE2000Adapter);
NE2000SetIRQHandler(NE2000IRQHandler);
NE2000SetSendHandler(NE2000SendHandler);
NE2000SetModeChangeHandler(NE2000ModeChangeHandler);

The NE2000 library has function for 8-bit and 16-bit read/writes:

void ne2000_ioport_write(NE2000State *s, uint8_t addr, uint8_t val);
uint8_t ne2000_ioport_read(NE2000State *s, uint8_t addr);
void ne2000_asic_ioport_write(NE2000State *s, uint32_t addr, uint16_t val);
uint16_t ne2000_asic_ioport_read(NE2000State *s, uint32_t addr);

These functions would then need to be implemented in ReadPort() and WritePort() under win32_8086tiny_intergace.cpp, so that the emulated NE2000 will respond to commands sent:

unsigned char T8086TinyInterface_t::ReadPort(int Address, bool isWord, bool is2ndByte)
{
if (NE2000Enabled && Address >= NE2000_ADDR && Address <= NE2000_ADDR + 0x1F)
{
lastNE2000WordRead = ne2000_asic_ioport_read(NE2000Adapter, baseAddress);
}
}

void T8086TinyInterface_t::WritePort(int Address, unsigned char Value, bool isWord, bool is2ndByte)
{
if (NE2000Enabled && Address >= NE2000_ADDR && Address <= NE2000_ADDR + 0x1F)
{
int baseAddress = Address - NE2000_ADDR;
ne2000_asic_ioport_write(NE2000Adapter, baseAddress, wordToWrite);
}
}

To be able to actually send out any network traffic, there needs to be a way to pass the traffic to the physical network card, for example by using WinPcap 4.1.3 library. Sending a data packet to the physical network card can be done with the following code:

if (pcap_sendpacket(pCapDevHandle, data, len) != 0)
{
   printf("Error sending the packet: %s\n", pcap_geterr(pCapDevHandle));
}

Receiving is a bit more tricky. First of all, as the emulated NE2000 network card has its own MAC address, any packets sent from our emulator will have the NE2000 MAC address as the source address. The destination machine will then send its own reply packet with the destination MAC address set to our emulated NE2000’s MAC address. However, this packet will arrive at the host machine’s network card which has a different MAC address and might reject the packet altogether. To address this issue, WinPcap has PCAP_OPENFLAG_PROMISCUOUS (=1) flag in its pcap_open method. If set, the library will attempt to configure the network card in promiscuous mode, allowing it to receive packets destined for other network cards with different MAC addresses. This flag does not work with all devices and might fail with no errors – the only way to know for sure if it will work is by trial and error. Once in promiscuous mode, our code will then have to select only packets destined for the NE2000 (as well as broadcast packets, which are used by DHCP server) and pass them to the NE2000 library. The process is demonstrated in the following code:

// init PCAP
if ((pCapDevHandle = pcap_open_live(d->name,	// name of the device
	65536,			// portion of the packet to capture.
					// 65536 grants that the whole packet will be captured on all the MACs.
	1,				// promiscuous mode (nonzero means promiscuous)
	1,				// read timeout. shorter values mean packets arrive at the emulator faster
	errbuf			// error buffer
	)))
{
   printf("Unable to open the adapter. %s is not supported by WinPcap\n", d->name);
}

// only get packet destined for our emulated NE2000 or broadcast message (all FF in mac address)
sprintf(packet_filter,
	    "ether dst  %02X:%02X:%02X:%02X:%02X:%02X or ether dst ff:ff:ff:ff:ff:ff",
	    pcap_mac_addr[0], pcap_mac_addr[1], pcap_mac_addr[2], pcap_mac_addr[3], pcap_mac_addr[4], pcap_mac_addr[5]);

To know when a data packet has arrived, the NE2000 driver makes used of IRQ (and not polling). Implementing this logic requires making Super 8086 Box aware of the NE2000 IRQ number, and raise this interrupt once there exists an Interrupt Service Request for the NE2000;

NE2000SetIRQHandler(NE2000IRQHandler);

void NE2000IRQHandler()
{
	NE2000IRQPending++;
}

bool T8086TinyInterface_t::IntPending(int &IntNumber)
{
  if (NE2000IRQPending > 0)
  {
	  IntNumber = NE2000_INT_NO;
	  NE2000IRQPending--;
	  return true;
  }
}

Initialize the NE2000 packet driver at address 0x60, IRQ 7 and IO address 0x300 using the following code. These values should match the values defined in ne2000.h

NE2000 0x60 0x7 0x300

The IRQ is defined in ne2000.h. Although interrupt 0xF is mapped to IRQ 7 for the parallel printer, we can use it freely as Super 8086 Box does not emulate the parallel port printer.

/*
INT 08 - IRQ0 - SYSTEM TIMER; CPU-generated (80286+)
INT 09 - IRQ1 - KEYBOARD DATA READY; CPU-generated (80286,80386)
INT 0A - IRQ2 - LPT2/EGA,VGA/IRQ9; CPU-generated (80286+)
INT 0B - IRQ3 - SERIAL COMMUNICATIONS (COM2); CPU-generated (80286+)
INT 0C - IRQ4 - SERIAL COMMUNICATIONS (COM1); CPU-generated (80286+)
INT 0D - IRQ5 - FIXED DISK/LPT2/reserved; CPU-generated (80286+)
INT 0E - IRQ6 - DISKETTE CONTROLLER; CPU-generated (80386+)
INT 0F - IRQ7 - PARALLEL PRINTER
*/
#define NE2000_INT_NO	   0x0F

At startup, Super 8086 Box will print the list of Ethernet network devices available on the system:

8086 Tiny plus console

You will need to select the correct device index, and set this value in section NETWORK_DEVICE_INDEX of default.cfg config file, for NE2000 emulation to work. Updating config file will require restarting the emulator:

[NETWORK_DEVICE_INDEX]
7

PC Diagnostics ’95 now correctly detected the emulated NE2000 card at 0x300, IRQ 7:

ne2000

Microsoft Network Client now works properly on Super 8086 Box in static IP mode, mounting my router shared drive:

8086 Tiny plus MSCLIENT

Microsoft Network Client does not work in DHCP mode, which happens with my other DOS machines and is probably due to buggy DHCP implementation in the MS-DOS TCP/IP stack. MTCP has no issues getting a DHCP address from my router:

8086 Tiny plus MTCP

Support for wireless cards

The above network emulation works only if you have an Ethernet network card on your machine that can be configured to work in promiscuous mode. If you are using a laptop with no Ethernet port, it is not going to work, as WinPcap does not support wireless card. I then switched to AirPcap, which is designed with wireless cards, thinking that it would work. However, after a few hours of research, it turned out such an idea was not feasible after all. For one thing, Wifi uses 802.11 frames whereas Ethernet uses 802.3 frames (and 802.1Q if VLAN is in use). My modification using WinPcap works because MS-DOS Ethernet drivers, despite their age, still sends out 802.3 frames that can be parsed by the Ethernet card on my host machine. Using AirPcap to forward these packets to my wireless card, which expects 802.11 packets and not 802.3, only resulted in my wireless network card hang, requiring a reboot.

According to my research, WinPcap and AirPcap works at layer 2 (among the 7 layers in the original OSI model) and sends data frames to the network card directly whereas NDIS drivers works somewhere between layer 2 (data link layer) and layer 3 (network layer). As these drivers encapsulate the network packets into suitable data frames for the interfaces (be it wireless or Ethernet) before sending these frames to the data link layer, they could probably be made to work with Super 8086 Box. With this in mind, I played around with the raw Ethernet packet sender from CodeProject (using a custom NDIS Protocol Driver) as well with the ndisprot from Microsoft. The process took too much effort, requiring me to build Windows kernel drivers and putting Windows into test mode so that it will accept unsigned drivers. At the end, while I was able to get these drivers to accept commands and send/receive some data, I figured that getting an MS-DOS network driver running on Super 8086 Box to reliably talk to a wireless network card would not be feasible after all, and gave up. To achieve this, commercial emulator software such as VirtualBox or VMware install virtual switches or routers on the host machine, way beyond the scope of Super 8086 Box. Most likely for this reason, even DOSBox only supports NE2000 network emulation via physical Ethernet adapters.

Adlib support

With the knowledge gained while adding NE2000, it was not difficult for me to add additional peripheral support. For example, Adlib support is added by using emulation codes for the YM3812 controller (which the Adlib ISA sound card is based on), and for FM OPL synthesis. These libraries would then parse the commands sent to the Adlib sound card and generate the raw PCM audio frames, ready to be played using Win32 WAV API. To ensure smooth audio playback, I will need to decide when these audio frames should be delivered to the host sound card. This logic can be done in T8086TinyInterface_t::TimerTick() function

CWaveOut *WaveOutAdlib;

bool T8086TinyInterface_t::TimerTick(int nTicks)
{
  if (getAdlibEnabled())
  {
	  updateAdlibTimer();
	  outputAdlibFrame();
  }
}

void outputAdlibFrame() {
	DWORD currentTime = timeGetTime(); // in ms since Windows starts
	unsigned int timeLapsed = currentTime - lastAdlibAudioOut;
	if (timeLapsed > 0)
	{
		timeLapsed = min(timeLapsed, 50); // play only the last few ms to prevent buffer overflow

		unsigned int numOfSamples = timeLapsed * AudioSampleRate / 1000;
		lastAdlibAudioOut = currentTime;

		// printf("Samples needed: %d.\n", numOfSamples);

		INT16* buf = (INT16*)malloc(numOfSamples * 2);
		updateAdlibFrame(numOfSamples, buf);
		WaveOutAdlib->Write((PBYTE)buf, numOfSamples * 2);
		free(buf);

	}
}

The above code deserves some explanation. Because the Adlib timer (to generate audio sample and to playback audio sample) is based on the system clock and not the emulated CPU clock, if the emulator execution pauses, for example because of an opened menu, the time lapsed will be huge causing the number of sample to grow very large and cWaveOut->Write to take a long time to write, thus freezing the system. The above workaround will play only the most recent samples, so that the system will not hang even during unexpected pause of execution.

With this, TEST.EXE as well as JUKEBOX (bundled with Adlib’s installation disk) should be able to work well. Sound in games such as Prince of Persia should also work when set to use Adlib.

Disney Sound Source and Coovox Speech Thing

Disney Sound Source and Coovox Speech Thing are parallel port audio devices meant for computers with no ISA sound cards. Emulating these devices require modifying 8086 Tiny Plus BIOS to report that a parallel port on LPT1 is present. To do this, update lpt1addr (part of the BIOS data area) in bios_cga.asm to 0x378 instead of 0:

; Standard PC-compatible BIOS data area - to copy to 40:0
bios_data:
com1addr	dw	0x03F8
com2addr	dw	0x02F8
com3addr	dw	0x03E8
com4addr	dw	0x02E8
lpt1addr	dw	0x378
lpt2addr	dw	0
lpt3addr	dw	0
lpt4addr	dw	0

After that, recompile the BIOS usins NASM 2.11 DOS version and replace the bios_cga binary file and make sure the parallel port is reported as present in system information tools such as CheckIt. After that, I modified WritePort() function to look out for writes on 0x378 and append these data to the parallel port audio buffer. Dr Hardware for DOS should correctly detect that the machine has 1 parallel port:

info1

To support the Disney Sound Source, which has an internal audio buffer and plays audio at a fixed frequency of 7kHz, unlike the Coovox which can (in theory) support frequency as fast as the parallel port speed, codes are written to derive different sampling frequency for both Disney and Coovox devices:

if (getLptPortType() == LPT_SOUND_TYPE_DISNEY)
{
	// Disney sound source has an internal buffer
	// and always plays audio at a fixed rate of 7kHz
	samplingRate = 7000;
	bytesToPlay = min(timeLapsed * samplingRate / 1000, curDisneyBufSize);
}
else {
	// For Covox Speech Thing, we output whatever in audio buffer
	// Try to calculate the raw sampling rate of the original audio (e.g. 8kHz) and upscale the samples
	// to match the sound card sampling rate (e.g. 44.1kHz)
	samplingRate = (double)curDisneyBufSize * 1000 / (double)timeLapsed;

	// for small set of samples, estimated sampling rate is not accurate. We assume lowest
	samplingRate = max(samplingRate, 8000);				

	bytesToPlay = curDisneyBufSize;
}

With this, Coovox and Disney sound sources now work properly with common games at the time such as Prince of Persia.

PC Speaker support

PC speaker emulation on 8086 Tiny Plus is poor. The audio quality is fine for a beep tone or to play simple music notes but will be distorted if the software attempts to play complicated sound such as game sound effects or WAV file playback (e.g. when using SBPLAY). This is probably due to poor instruction timing implementation. From the code, the emulator calls TimerTick(4) to execute 4 instructions per update interval. This causes the CPU speed to be reported as 30MHz by CheckIt (and not 4.77MHz). Changing it to Timertick(1) and the speed is reported correctly, but the emulation would become too slow to be used. If you play a continuous tone (e.g.via Turbo Pascal sound() function), there will be short gaps in the tone due to interruption in the raw audio frames as the emulator performs other non-audio related functions, causing buffer underrun. I tried very hard but was not able to fix this issue.

MPU-401 support

MPU-401 emulation is added by using Win32 MMEAPI library (midiOutOpen, midiOutShortMsg and midiOutLongMsg). In function WritePort(), the codes will watch out for writes on port 0x330 (default MPU-401 port), perform some basic parsing of the message to decide the type of message before passing it to Win32 API. To get software to detect the emulated MPU-401 device, we also implemented the auto-detection sequence as well as MPU-401 interrupt in function ReadPort():

case 0x330:
{
  if (getMidiPort() >= 0)
  {
	  if (shouldSentAck)
	  {
		  retval = mpuCommandAck;
		  shouldSentAck = false;
		  MPU401IRQPending++;
	  }
	  else {
		  retval = 0;
	  }

	  // printf("Read MPU401 Data Port. lastMpuCommand = %d. retval = %d\r\n", lastMpuCommand, retval);
	  lastMpuCommand = 0;
  }
}
break;

case 0x331:
{
  if (getMidiPort() >= 0)
  {
	  if (lastMpuCommand != 0 && !shouldSentAck)
	  {
		  retval = 0b10000000;
		  shouldSentAck = true;
		  MPU401IRQPending++;
	  }
	  else if (lastMpuCommand != 0 && shouldSentAck)
	  {
		  retval = 0b00000000;
	  }
	  else {
		  retval = 0b10000000;
	  }

	  // printf("Read MPU401 Status Port. lastMpuCommand = %d. RetVal = %d\r\n", lastMpuCommand, retval);
  }
}
break;

Here goes a bit of explanation for the above code. In UART mode, the MPU recognizes only a command which is the Reset command. After you write a command byte to the COMMAND port, the MPU acknowledges this command and sends an FE (hex) byte to its DATA port. When reading back a byte from the MPU-401, the highest bit (7) of this byte is the state of the DATA SET READY (DSR) line. This bit will be clear (0) if the MPU has some incoming MIDI byte (or Ack byte or Operation byte) waiting for you to read. The DSR line will remain low until you’ve read all of the bytes that the MPU has waiting in its hardware input buffer. You should continue reading bytes from the DATA port until the DSR bit of the STATUS port toggles to a 1 (ie, sets). When that happens, the MPU has no more bytes waiting to be read. The next highest bit (6) is the state of the DATA READ READY (DRR) line. This bit will be clear whenever it’s OK for you to write a byte to the MPU’s DATA or COMMAND ports. The MPU sets bit 6 of the STATUS port whenever it is NOT ready for you to write a byte to the DATA or COMMAND ports.

As with the NE2000, upon startup, Super 8086 Box will list out the index of available MIDI devices on the system. Take note of the index you want to use and set it under section MIDI_DEVICE of the config file:

[MIDI_DEVICE]
1

The default value is the Microsoft GS Wavetable Synth, the default MIDI synthesizer on Windows, which supports general MIDI but not things like MT-32. To emulator MT-32, use Munt instead. You will need to get the MT-32 ROM for Munt to emulate MT32. Where to get that is outside the scope of this article. Needless to say, only MPU-401 UART mode is implemented. Intelligent mode requires the MPU-401 device to have its own clock which is something I will not bother to attempt.

Both Adlib and MPU-401 are now detected by Dr Hardware:

info2

Joystick support

The emulator will report a joystick present on port 0x201 if JOYSTICK_DEVICES is set to 1 (for 1 joystick) or 2 (for 2 joysticks) in the config file. If a USB joystick is connected, the emulated joystick will be mapped to the USB joystick using functions in joystickapi.h. This should work well for games like Prince of Persia or Dave. AlleyCat however uses BIOS joystick services (service 84h of interrupt 15h), which is not emulated by Super 8086 Box, and will not work.

Dr Hardware now correctly detected the joysticks on my system, mapped to my USB joysticks:

joysticks

If more than 2 joysticks are connected, only the first 2 will be used by Super 8086 Box, as MS-DOS only supports 2 joysticks. If a game has a calibration function for joysticks, you should use it to improve accuracy. During my tests, however, calibration was not necessary for most games.

Missing IMUL instruction

8086 Tiny Plus does not support IMUL and attempting to use it will print “IMUL” to the console. Luckily, not many software use this instructions so the issue will most likely not affect you. Turbo Debugger uses IMUL however and hence will not work on 8086 Tiny Plus. To debug DOS software, you can use Insight Debugger for DOS, which supports most of Turbo Debugger feature (except “online” help). During my tests, Insight works flawlessly on 8086 Tiny Plus. It should be noted that Insight’s handling of VGA mode is less than ideal and will produce distorted display if run on newer CPUs (Pentium onwards) whereas Turbo Debugger can still work fine.

The affected code is at 8086TinyPlusVCC.cpp, line 856:

OPCODE 56 : // 80186, NEC V20: PUSH imm16
R_M_PUSH(i_data0)
OPCODE 57 : // 80186, NEC V20: PUSH imm8
R_M_PUSH(i_data0 & 0x00ff)
OPCODE 58 : // 80186 IMUL
// not implemented
printf("IMUL at %04X:%04X\n", regs16[REG_CS], reg_ip)

To make the decoding of instructions from raw memory bytes looks more “readable” to the naked eyes, obscure macros are used to hide unnecessary C constructs. For example, here OPCODE macro is defined as following:

#define OPCODE ;break; case

You can see that IMUL does not do anything and simply log the register values to the console. I followed HELPPC’s description of IMUL’s behavior and tried to implement IMUL. However, Turbo Debugger still crashed. Maybe it’s due to undocumented behavior or my lack of understanding of this instruction. Whatever it is, IMUL has to be left as unsupported for now.

Bug in REP INSW

When adding NE2000 support I realized that I could not get my emulated NE2000 card to work with many drivers. The card would be detected with the correct MAC address, however MS-DOS would hang while performing data transfer. The same behavior would happen with many NE2000 drivers. After further research I realized that I was able to get it to work perfectly if the NE1000 driver was used (the NE2000 is backwards compatible with NE1000), or if the NE2000 driver is modified to force 8-bit mode. Many NE2000 drivers detect the CPU type and if a 80186 (as emulated by Super 8086 Box) is detected, would default to 16-bit mode, with no easy way to change to 8-bit mode. This limits the issue to perhaps some incompatibilities in some 80186 instruction emulations.

Using the integrated debugger I traced the issue back to the REP INSW instruction, which somehow always returned 0x90, regardless of memory contents. The affected code for this is right below the above IMUL code, around line 863:

OPCODE 59 : // 80186: INSB INSW
            // Loads data from port to the destination ES:DI.
            // DI is adjusted by the size of the operand and increased if the
            // Direction Flag is cleared and decreased if the Direction Flag is set.

scratch2_uint = regs16[REG_DX];
for (scratch_uint = rep_override_en ? regs16[REG_CX] : 1; scratch_uint; scratch_uint--)
{
	io_ports[scratch2_uint] = Interface.ReadPort(scratch2_uint, i_w, false);
	if (i_w)
	{
		io_ports[scratch2_uint + 1] = Interface.ReadPort(scratch2_uint + 1, i_w, true);
	}

	R_M_OP(mem[SEGREG(REG_ES, REG_DI)], =, io_ports[scratch_uint]);
	INDEX_INC(REG_DI);
}

R_M_OP is declared as

// Execute arithmetic/logic operations in emulator memory/registers
#define R_M_OP(dest,op,src) (i_w ? op_dest = CAST(unsigned short)dest, op_result = CAST(unsigned short)dest op (op_source = CAST(unsigned short)src) \
								 : (op_dest = dest, op_result = dest op (op_source = CAST(unsigned char)src)))

Looking closely, you do not need to know the exact behavior of REP INSW instruction to suspect that something must be wrong with the for loop. After all, scratch_uint is the control variable for the for loop and decreases by 1 after each iteration. What good is it to execute arithmetic operations on various memory locations using this decreasing value? Are we simply printing all numbers in a range (e.g. 1 to 10)?

According to documentation, INSW inputs word from I/O port specified in DX (which is scratch2_uint in the above code) into memory location specified in ES:(E)DI. Realizing that this is probably a typo, I changed the code to the following:

R_M_OP(mem[SEGREG(REG_ES, REG_DI)], =, io_ports[scratch2_uint]);

And that’s it, the issue with REP INSW was fixed and NE2000 emulation now works properly with all the drivers I tested. I wasted almost a week over this small issue.

Full-screen support

The original 8086 Tiny Plus did not support full-screen, only x1 and x2 zoom modes. For a moment I wondered why the author did not attempt full-screen after spending so much effort to improve the software from the original 8086 Tiny. The answer became clear once I took a closer took at the source code. In file win32_cga.cpp, there are various functions to handle various supported CGA modes such as CGA_DrawCO40:

8086 Tiny plus cga

These functions would then prepare the video bitmap frame, based on current video parameters (e.g. text or graphics, resolution, graphics buffer), and draw the bitmap onto the screen. Implementing full-screen would involving (at minimum) hiding the application menu bar, resizing the bitmap to fit the screen size (aspect fit or stretched), and draw it into the entire screen area. Resizing can be done using StretchDIBits supports either STRETCH_DELETESCANS or STRETCH_HALFTONE. The former is fast but introduce artifacts, which can be somewhat removed by offsetting the target height/width by a couple of pixels. The latter produces perfect quality image but is too slow to be used for games, but will work fine with things like Turbo Pascal or Norton Commander. I tried to write my own resize function before displaying the graphics on the screen using SetDIBitsToDevice, which produces perfect quality images but is still not fast enough for games. It is probably for this reason that the original author did not implement full screen, only x1 and x2 zoom.

Nevertheless, the full-screen options have been added to the menu (Configuration > Set Display Scaling). You might also wan to tweak the relevant config sections to improve full screen performance:

[FULL_SCREEN_ADJUST_X_PIXELS]
1
[FULL_SCREEN_ADJUST_Y_PIXELS]
2
[FULL_SCREEN_METHOD]
0

Set FULL_SCREEN_METHOD to 0 to use StretchDIBits with default STRETCH_DELETESCANS flag (fast with artifact), 1 to use STRETCH_HALFTONE (slow) or 2 to use my custom algorithm. If FULL_SCREEN_METHOD is 0, try tweaking FULL_SCREEN_ADJUST_X_PIXELS to reduce artifacts. A proper implementation will probably require the use of things like Direct3D, which I do not have the time to attempt.

Integrated debugger

During the process I have improved the integrated debugger to be much more sophisticated than the original debugger. The debugger now supports the following features:

  • Display register and flag values
  • Setting at runtime a particular register value (e.g. AX=8EC1)
  • Showing memory disassembly at instruction pointer CS:IP or at other address
  • Setting/clearing breakpoint at a particular address or clear all breakpoints
  • Display memory dump in HEX & ASCII, at CS:IP or at other address
  • Patching byte(s) at a particular memory address
  • Reading/writing port value
  • Searching for bytes within memory contents
  • Navigate to address specified by CS:IP
  • Dumping of memory and ports contents
  • Save current disassembly as text file
  • Enable execution trace and saving trace as text file

This is a screenshot of the improved debugger:

8086 Tiny plus Debugger

Downloads

The completed Visual Studio 2015 solution for the improved Super 8086 Box can be downloaded here. The project should compile out of the box or with only minor configuration changes. The Super 8086 Box executable can also be found in the root directory of the extracted folder. SDL support has been removed and the app now uses Win32 API exclusively.

The bundled configuration contains a 256MB hard disk image but will only boot from the bundled MS-DOS 6.22 bootable floppy image as the BIOS can’t boot from hard disk drive yet. Various multimedia software (Adlib Jukebox, DOSMID, GSPLAY), games and useful applications such as Turbo Pascal are also included. MTCP, Tsoft’s NFS Clients v1.02 as well as Microsoft Network Client (modified for 8088 using the instructions here)  are also included. Games such as Secret of Money Island or Prince of Persia should also work well with full audio effects. Feel free to try it out and let me know your feedback in the comment form below.

I am still working on upgrading Super 8086 Box to add dual floppy support and ability to boot from hard drive. Stay tuned for further updates to Super 8086 Box!

 

 

 

 

0.00 avg. rating (0% score) - 0 votes
ToughDev

ToughDev

A tough developer who likes to work on just about anything, from software development to electronics, and share his knowledge with the rest of the world.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>