• Please review our updated Terms and Rules here

8086 emulator tech discussion

Hey Mike, any plans for Tandy/PCjr graphics & sound?

I know I'd *love* that, and I'm sure more than a few others.

I've really enjoyed following your progress and am very impressed. Keep up the good work!
__
Trevor

i'm glad you think it's interesting.. i'm having a great time with this! yeah, tandy/PCjr stuff i actually do plan to add eventually. i need to find some good tech resources on it. i'll check out oldskool.org - that's always a good place to check. :)
 
Mike, Hi. I've not had chance to look at your latest version yet but will get on to that.

I wondered if the way that you'd developed this might lend itself to emulating other architectures? For example, a Z80A emulator (that could then be turned into a Tandy Model 4, for example).
 
Mike, Hi. I've not had chance to look at your latest version yet but will get on to that.

I wondered if the way that you'd developed this might lend itself to emulating other architectures? For example, a Z80A emulator (that could then be turned into a Tandy Model 4, for example).

nope, it would be a pretty major undertaking to make it emulate something non-PC. it WOULD be pretty cool to make, say, an osborne 1 emulator though! :p

i've got one of those, it gets turned on maybe an hour per year. it's a cool vintage machine, but damn is it useless now. lol.
 
so, yesterday i wrote code to emulate the Intel 8259 interrupt controller. this fixed a few issues i was having. on rare occasions, keystrokes would cause whatever was running in the emulator to hang. i was previously calling the timer, keyboard, and the network emulation interrupts directly rather than having them properly prioritized through the 8259.

the network stuff was especially unstable since it generated an interrupt every time there was an incoming packet on the network after the packet driver started. as you might imagine, those not being prioritized tended to cause hangs within a minute of starting the packet driver.

now, using the 8259 code, it seems extremely stable. i've got a machine running IRCjr inside fake86 at my office, and it's been connected without problems for over 24 hours now! :cool:

for some reason, though, only mike brutman's mTCP stuff seems to want to work through my network emulation. anything else, like NTCPDRV, WATTCP apps, and arachne appear to not actually be receiving any packets from my packet driver. i'm trying to sort that out now.

once i fix that, i should even be able to run a packet driver to ODI or IPX shim and use the MS Network Client 3.0 software to used network shares in fake86! that would be nifty...

and for anybody wondering how i am giving the emu network access, it's through libpcap/winpcap so fake86 can have it's very own separate MAC address and appear to be an independent card on the network. the default MAC address is DE:AD:BE:EF:13:37 </nerd>
 
Mike, congrats on the 8259 emulation--do you implement the complete command set? Are you emulating the 8254 yet? In other words, if a program decides to bump the timer "tick" interval from 55 msec. to something else, do you handle that?

How are you doing with floppy controller emulation?
 
Mike, congrats on the 8259 emulation--do you implement the complete command set? Are you emulating the 8254 yet? In other words, if a program decides to bump the timer "tick" interval from 55 msec. to something else, do you handle that?

How are you doing with floppy controller emulation?

i'm not emulating the floppy controller or hard drive controller on the register I/O level, at least not for now. they're both just through high-level emulation of interrupt 13h. naturally some odd programs won't work like this, but they're basically limited to hard drive utility programs like spinrite. anything else is going to use 13h for compatibility reasons (i.e. so it'd work on SCSI and IDE drives the same way)

i do emulate the programmable timer.

this is run if theres output to port 40h:
Code:
		case 0x40: //pit 0 data port
		switch (pit0command) {
			case 0x36:
			if (pit0latch==0) {
				pit0divisor = (pit0divisor & 0xFF00) | (value & 0xFF);
				pit0latch = 1;
				return;
				} else {
				pit0divisor = (pit0divisor & 0xFF) | ((value & 0xFF) << 8);
				pit0latch = 0;
				if (pit0divisor==0) pit0divisor = 65536; //just in case, prevent divide by zero error
				if (verbose) { sprintf(msg, "Hardware clock tick interval set to %u Hz\n", (uint32_t)(1193180/pit0divisor)); print(msg); }
				return;
			} break;
		} break;

and out to port 43h:
Code:
		case 0x43: //pit 0 command port
		pit0command = value;
		switch (pit0command) {
			case 0x36: //reprogram pit 0 divisor (system timer)
			pit0latch = 0; break;
			case 0xB6: //reprogram pit 2 divisor (PC speaker)
			latch42 = 0; break;
		} break;

and of course the values can be read back by doing input from the ports. then i poll very high-precision timers every few hundred emulated instructions:
Code:
#if defined(_WIN32)
#define timing() {\
    QueryPerformanceCounter(&curtimer);\
	if (doaudio=1) genaudio();\
	if (pit0divisor==0) pit0divisor = 65536;\
	if (clocksafe && ((curtimer - lasttimer) >= (timerfreq/(1193180/pit0divisor)))) {\
	    doirq(0);\
		lasttimer = curtimer;\
		lasttimercheck = totalexec;\
	}\
	if ((curtimer - lastcurstimer) >= (timerfreq/3)) {\
		if (cursorvisible) cursorvisible = 0; else cursorvisible = 1;\
		lastcurstimer = curtimer; updatedscreen = 1;\
	}\
}
#else
//changed from clock_gettime(CLOCK_REALTIME, &timedata) below to gettimeofday(&timedata)
//for compatibility with Mac OS X
#define timing() {\
    gettimeofday(&timedata);\
    curtimer = timedata.tv_usec;\
	if (doaudio==1) genaudio();\
	if (pit0divisor==0) pit0divisor = 65536;\
	if (clocksafe && ((curtimer - lasttimer) >= (timerfreq/(1193180/pit0divisor)))) {\
	    doirq(0);\
		lasttimer = curtimer;\
		lasttimercheck = totalexec;\
	}\
	if ((curtimer - lastcurstimer) >= (timerfreq/3)) {\
		if (cursorvisible) cursorvisible = 0; else cursorvisible = 1;\
		lastcurstimer = curtimer; updatedscreen = 1;\
	}\
}
#endif

it works very well. i wish there were a less CPU intensive way to poll high precision timers though, both QueryPerformanceCounter in windows and gettimeofday in *nix eats much more than i'd like. any ideas? i cant use RDTSC for two reasons: it wouldn't be portable on non-x86, and with multicore processors with unsynchronized pipelines it's not reliable anymore.
 
Well, I suppose you could figure out a fudge factor (real time vs. emulated time) and simulate the running of the timer that way.

How do you do running some of the old piano man sound files? Say, this one? Incredibly simple and tied to the speed of the PC.
 
the whole thing only ran for about 2 seconds lol. DOSBox was about the same. i have no method to actually limit the program to a certain CPU speed, i'd have to make it keep track of individual cycles for the instructions.
 
Way back when, I used to use the piano man sound files as a simple way to compare XT clones. The thing is surprisingly sensitive to things like RAM wait states, etc. and requires no special hardware to run. Even with two very similar clones (basically the same ERSO design), the sound files wouldn't finish simultaneously.
 
Way back when, I used to use the piano man sound files as a simple way to compare XT clones. The thing is surprisingly sensitive to things like RAM wait states, etc. and requires no special hardware to run. Even with two very similar clones (basically the same ERSO design), the sound files wouldn't finish simultaneously.

it's amazing how much faster machines have gotten since then. it would probably finish in a millisecond or two if you tried to run it in plain DOS on even a low-end pentium 4 made 10 years ago. kinda mind-blowing..... then compare that P4 to today's chips!
 
Funny that you should mention that. Before I posted the link, I wanted to make sure that it worked, so I clicked on the executable on my 2.4GHz P4 system. Window opened, window closed--I didn't even see the logo. Just tried it on a very slow 386/16 system with no cache. Takes about 15 seconds, I hear a rhythmic buzzing, but nothing that could be recognized as music.

Basman may want to give it a try on his emulator.
 
yeah i wonder how it sounds on his too. it might be somewhat recognizable, since the hardware he runs it on is pretty weak. wolf3d is pretty sluggish on it, but playable. from his video it looked to be about the equivalent of a turbo 8088 or maybe a very slow 286.
 
oh, also chuck.. do you know anything about the Tandy 320x200 16-color mode's memory layout? according to what i read in the tech ref, this should decode it properly:

(x,y go to 640,480 because i double up the pixels in low res modes like that)
Code:
            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;
                    ofs = y*screen->w + x;
                    if ((ofs >= 0) && (ofs <= maxptr))
                       ((uint32_t *)screen->pixels)[ofs] = SDL_MapRGB(screen->format, color&255, (color>>8)&255, color>>16);
                }

it should start at B8000h, then it should be divided into four blocks of 8000 bytes aligned on 8192 byte boundaries with 2 pixels packed in a byte, and the 8000 byte block should be the scanline number modulo 4.

problem is, that code draws stuff like this: (ultima 6)
fake86-tandy.png


i know the palette is all wrong, i was going to worry about that last.
 
gah, nevermind - i feel dumb.

this:
Code:
                    if (((x>>1)&1)==0) color = palettecga[RAM[vidptr]] >> 4;
                       else color = palettecga[RAM[vidptr]] & 15;

should have been this:
Code:
                    if (((x>>1)&1)==0) color = palettecga[RAM[vidptr] >> 4];
                       else color = palettecga[RAM[vidptr] & 15];

:facepalm:

now it looks like this:
fake86-tandy-fixed.png


derp. :p
 
oh hell yes, finally got MS-DOS versions after 3.2, or whatever it was, to boot! for some reason it didn't like the fact that i was handling int 13h function 15h (DISK - GET DISK TYPE (XT 1986/1/10 or later,XT286,AT,PS))

i dont believe i was handling incorrectly, but i suppose i must have been because i commented out that whole function handler, and it just returns with the carry flag set indicating an error when it called.... and now:

fake86-dos622.png


this has been driving me nuts for months!
 
Awesome!

BTW re timing, the infamous 8088-corruption is driven from the sound card, because of the lack of any kind of reliable timer in the XT etc. But I'm sure you're well aware of that code anyhow :)
 
Funny, because INT 15H used to be one of those functions that was implemented slightly differently by different BIOS vendors. Fortunately, most of the differences lie in the interpretation for hard drives.

For floppies, there really are only 4 returns possible:
  • AH = 1, CY=1; drive lies outside the range of permissible drive numbers; e.g. DL=9
  • AH = 0, CY = 0; no drive installed, but drive number is supported by the BIOS.
  • AH = 1, CY = 0; drive installed that cannot detect floppy changes (e.g. 360K, most 720K)
  • AH = 2, CY = 0; drive installed that can detect floppy changes (e.g. 1.2M, 1.44M).
Note that this has nothing to do with the floppy type inserted. A 1.2M drive with a 360K floppy inserted is still a change-detecting drive. Also, no registers should be destroyed by the call but for AX and carry.
 
Awesome!

BTW re timing, the infamous 8088-corruption is driven from the sound card, because of the lack of any kind of reliable timer in the XT etc. But I'm sure you're well aware of that code anyhow :)

yep, that's right it's driven by the sound card but i believe the reason is to ensure synchronization. the PC/XT system timer on the i8253 can actually be programmed all the way down to 1/1193182th of a second. it's actually very precise and reliable. :D
 
Funny, because INT 15H used to be one of those functions that was implemented slightly differently by different BIOS vendors. Fortunately, most of the differences lie in the interpretation for hard drives.

For floppies, there really are only 4 returns possible:
  • AH = 1, CY=1; drive lies outside the range of permissible drive numbers; e.g. DL=9
  • AH = 0, CY = 0; no drive installed, but drive number is supported by the BIOS.
  • AH = 1, CY = 0; drive installed that cannot detect floppy changes (e.g. 360K, most 720K)
  • AH = 2, CY = 0; drive installed that can detect floppy changes (e.g. 1.2M, 1.44M).
Note that this has nothing to do with the floppy type inserted. A 1.2M drive with a 360K floppy inserted is still a change-detecting drive. Also, no registers should be destroyed by the call but for AX and carry.

do you think there would really be any compelling reason to implement that one at all? because if not, i'll just leave it removed. things seem to work well.
 
Well, the INT 13H/15H functionality wasn't implemented until the 1/10/86 edition of the XT BIOS and is present in 286+ systems. The primary purpose was to convey which drives had "disk change" capability. Since this wasn't implemented until the 5170, anything on the 5160 is essentially a retrofit. The same goes for functions 16H, 17H and 18H. So if you're emulating a plain-Jane 5150/5160 with 360K drives, it's okay to leave support for 15H-18H INT 13H functions out.

The problem with using INT 13H, function 08H to determine drive type is that (a) you don't know for certain that the BIOS/controller supports "Disk Changed" status, and (b) the simple drive type ordinal (1-4) doesn't give enough information. A 720K drive can be run from a plain old 5150 FDC (with no change monitoring) and not all 720K drives had the "disk changed" signal on pin 34. I have an old Sony 3½"-to-5¼" adapter kit where the "DC" signal can be jumpered NC for those drives that put READY on pin 34.

The "disk changed" thing was a pain in the neck with DOS. If you don't at least do a checksum on first few sectors of the FAT or root directory before doing a write, you can clobber a floppy if the user changes the floppy on you. Or you might not notice that the floppy has changed at all. That, incidentally, is why Microsoft has been trying to get rid of 360K support in Windows products for years.
 
Last edited:
Back
Top