Fixed serial port transmitter interrupts, and added a new blog post (RetroReview: SPEEDUP)

This commit is contained in:
Jeff 2017-07-22 23:08:46 -07:00 committed by Jeff Parsons
commit a85662632c
8 changed files with 810 additions and 541 deletions

View file

@ -1,11 +1,11 @@
---
layout: post
title: Flickery Fakery
title: Flicker Fakery
date: 2017-07-15 10:00:00
permalink: /blog/2017/07/15/
---
For years, one of my pet peeves about PC emulators has been their obsession with raw speed, and how hard they
One of my long-standing pet peeves about PC emulators has been their obsession with raw speed, and how hard they
try to squeeze every last drop of performance out of their instruction execution loop, on the assumption that
everyone wants an 8088 that runs at, say, 500Mhz, as opposed to the stately 4.77Mhz of the original IBM PC.

View file

@ -0,0 +1,244 @@
---
layout: post
title: RetroReview: SPEEDUP
date: 2017-07-21 10:00:00
permalink: /blog/2017/07/21/
machines:
- id: pcsig08
type: pcx86
resume: 1
config: /disks/pcx86/shareware/pcsig08/debugger/machine.xml
autoMount:
A:
name: PC-DOS 1.00
B:
name: PCSIG08: DISK0011
---
While browsing the June/July 1982 issue of PC Magazine (Vol. 1, No. 3), I found the following
[User-To-User](https://books.google.com/books?id=w_OhaFDePS4C&lpg=RA3-PA151&ots=cXovjlDNc8&pg=RA3-PA151#v=onepage&q&f=false)
article about an intriguing little program called `SPEEDUP`. Unfortunately, the first time I tried it,
it immediately hung my test machine. Here's the original article, followed by my analysis of the program
and why it hung.
> ### Disk Speedup
> The undisputed celebrity of this months user tips is SPEEDUP--14 lines of BASIC program code that will make your
disk drives perform their chores in half the time. The program popped up on Wes Merchant's Annandale, Virginia IBM
Bulletin Board Service (see Club News). It was sent there by Chris Carson of Aurora, Colorado, who found the program
circulating in the Denver area. Good news travels fast. Here it is:
05 REM SPEEDUP DISK-ZAP
10 FOR I=1 TO 37:READ N:C=C+N
15 NEXT:READ N:IF N<>C THEN 40
20 RESTORE:OPEN "R",1,"SPEEDUP.COM",1
25 FIELD 1,1 AS N$:FOR I=1 TO 37
30 READ N:LSET N$=CHR$(N):PUT 1
35 NEXT:CLOSE:PRINT "Created":END
40 PRINT "** ERROR - Verify Data **":END
45 DATA 186,18,0,184,30,37,205,33,139
50 DATA 250,190,26,1,185,11,0,243,164
55 DATA 51,192,205,19,139,215,205,39
60 DATA 223,2,37,2,8,42,255,80,246,0,4
65 DATA 3866
70 END
> Even if you've never touched BASIC, the gem is too good to pass up. To create the program, place the PC-DOS
disk into your A: drive and load BASIC by typing *BASIC* and hitting *Enter*. Then type each of the lines above,
exactly as written. Hit *Enter* after each line. When you've *Enter* following line 70, hit the *F2* key to run
this short program.
> If you get a message saying "\*\* ERROR - Verify Data \*\*" then you've made a mistake copying the numbers in line
45 through 60. Otherwise, you will have created a new file on your PC-DOS disk called SPEEDUP.COM.
> Go back into PC-DOS by typing *system* and *Enter* and run the new program by typing *speedup* and *Enter*. Now
load and run any program or PC-DOS utility and be amazed. Your disk drives will miraculously zip along at almost
double speed, singing instead of groaning.
> Exactly how much faster? Well, for example, to format a new disk with the /S option: 25 seconds instead of 45
seconds. To copy the PC-DOS disk via the "DISKCOPY" command: 42 seconds instead of 83. To copy the entire PC-DOS
disk with "COPY \*.\*": 139 seconds instead of 206. If you're a speed and performance fanatic (who isn't), you've
just made up to a 100 percent improvement in your disk operations.
> According to Chris Carson, the program works its magic by reloading the diskette parameters table in PC-DOS.
IBM programmed the step rate for the machine's Tandon disk drives at eight milliseconds; SPEEDUP shortens this to
six, still within the Tandon-rated minimum of five milliseconds. The program also sets the head settle rate to
zero milliseconds, which is the Tandon minimum rating (IBM's conservative default setting is 25).
Fortunately, we don't have to type the program in. If you skim our archived copy of the
[PC-SIG Library 8th Edition CD-ROM (April 1990)](/disks/pcx86/shareware/pcsig08/), you'll see `SPEEDUP.BAS`
in the [Directory of PCSIG08: DISK0011](/disks/pcx86/shareware/pcsig08/#directory-of-pcsig08-disk0011).
So, fire up the [PC-SIG Machine (with Debugger)](/disks/pcx86/shareware/pcsig08/debugger/). Or use the machine
below, which has conveniently pre-loaded "PC-DOS 1.00" in drive A and "PCSIG80: DISK0011" in drive B.
At the DOS prompt, run `BASIC` and type `LOAD "B:SPEEDUP.BAS"`.
{% include machine.html id="pcsig08" %}
When you `LIST` the program to the serial port:
LIST,"COM1:"
you'll see that the program is virtually identical to the PC Magazine listing:
10 FOR I = 1 TO 37
11 READ N
12 C = C + N
15 NEXT
16 READ N
17 IF N <> C THEN 40
20 RESTORE
21 OPEN "R",1,"SPEEDUP.COM",1
25 FIELD 1 , 1 AS N$
26 FOR I = 1 TO 37
30 READ N
31 LSET N$ = CHR$(N)
32 PUT 1
35 NEXT
36 CLOSE
37 PRINT "created"
38 END
40 PRINT "** error. Verify DATA"
41 END
45 DATA 186,18,0,184,30,37,205,33,139
50 DATA 250,190,26,1,185,11,0,243,164
55 DATA 51,192,205,19,139,215,205,39
60 DATA 223,2,37,2,8,42,255,80,246,0,4
65 DATA 3866
If you `RUN` it, it should report "created". Type `SYSTEM` to exit BASIC and then type `DIR SPEEDUP.COM`
to verify that the program was created:
SPEEDUP COM 128 07-21-17
You'll notice that BASIC created a 128-byte file, even though it wrote only 37 bytes. This is an
artifact of PC-DOS 1.00 and BASIC Version D1.00, which were limited to File Control Block (FCB) DOS
functions that operated on files using 128-byte "records".
Alternatively, if we boot from "PC-DOS 2.00 (Disk 1)" and run BASIC Version D2.00, loading and running
`SPEEDUP.BAS` will produce the following `SPEEDUP.COM`:
Volume in drive A has no label
Directory of A:\
SPEEDUP COM 37 7-21-17 12:00a
1 File(s) 30720 bytes free
Let's take a closer look at this 37-byte program, using the `DEBUG` program on "PC-DOS 2.00 (Disk 2)":
A>B:DEBUG SPEEDUP.COM
-u100
0913:0100 BA1200 MOV DX,0012
0913:0103 B81E25 MOV AX,251E
0913:0106 CD21 INT 21
0913:0108 8BFA MOV DI,DX
0913:010A BE1A01 MOV SI,011A
0913:010D B90B00 MOV CX,000B
0913:0110 F3 REPZ
0913:0111 A4 MOVSB
0913:0112 33C0 XOR AX,AX
0913:0114 CD13 INT 13
0913:0116 8BD7 MOV DX,DI
0913:0118 CD27 INT 27
-d 11a lb
0913:011A DF 02 25 02 08 2A FF 50 F6 00 04
The program uses the DOS Set Vector function (INT 21h, AH=25h) to modify interrupt vector 1Eh (the Diskette
Parameter Table vector) to point DS:0012, inside the program's Program Segment Prefix (PSP). It then copies
0Bh bytes from offset 011Ah into the PSP at offset 0012h, issues a BIOS Disk Reset (INT 13h, AH=00h), and
then executes a Terminate and Stay Resident (TSR) request (INT 27h) with DX set to 1Dh, retaining the first 12h
bytes of the PSP plus an additional 0Bh bytes containing the new diskette parameter values.
Note that this TSR program doesn't actually want to retain the first 12h bytes of the PSP, but it must leave
those bytes in place so that the program will exit correctly.
In PC-DOS 1.00 (August 1981), the first 12h bytes of the PSP were used as follows:
00h-01h 2 bytes (code) CP/M exit (always contains INT 20h)
02h-03h 2 bytes Segment of the first byte beyond the memory allocated to the program
04h 1 byte Reserved
05h-09h 5 bytes (code) Far call to CP/M compatibility code within DOS
0Ah-0Dh 4 bytes Terminate address of previous program (old INT 22h)
0Eh-11h 4 bytes Break address of previous program (old INT 23h)
with bytes 12h-5Bh marked "Reserved" and the remaining PSP bytes defined as:
5Ch-6Bh 16 bytes Unopened Standard FCB 1
6Ch-7Fh 20 bytes Unopened Standard FCB 2 (overwritten if FCB 1 is opened)
80h 1 byte Number of bytes on command-line
81h-FFh 127 bytes Command-line tail (terminated by a 0Dh)
NOTE: The 128 bytes at offset 80h are also used as the default DOS Disk Transfer Area (DTA).
In May 1982, PC-DOS 1.10 added:
12h-15h 4 bytes Critical error address of previous program (old INT 24h)
16h-17h 2 bytes Parent's PSP segment (usually COMMAND.COM - internal)
In March 1983, PC-DOS 2.00 added:
18h-2Bh 20 bytes Job File Table (JFT) (internal)
2Ch-2Dh 2 bytes Environment segment
2Eh-31h 4 bytes SS:SP on entry to last INT 21h call (internal)
And in August 1984, PC-DOS 3.00 added:
32h-33h 2 bytes JFT size (internal)
34h-37h 4 bytes Pointer to JFT (internal)
This explains why, when I first ran `SPEEDUP` on PC-DOS 2.00, it crashed. In fact, the *only* version
of DOS that supports `SPEEDUP` is version 1.00. It crashes on all other versions of DOS because it "trashes"
critical PSP bytes; specifically, the Parent's PSP segment at offset 16h. The author of `SPEEDUP` ignored IBM's
warning that PSP bytes at offset 12h and higher were "reserved" and used them anyway.
> NOTE: Other sites, like [PC DOS Retro](https://sites.google.com/site/pcdosretro/doshist), suggest that
the "Parent's PSP segment" at offset 16h wasn't introduced until PC-DOS 2.00, but that's incorrect. Even though
the PC-DOS 1.10 Manual (p. E-6) mentions only the addition of the "Critical Error Exit Address" at offset 12h,
both fields were in fact added in PC-DOS 1.10.
Sadly, the history of the IBM PC and DOS is littered with examples of programmers ignoring the "reserved"
admonitions of others -- even IBM, when they designed the IBM PC to use interrupt vectors that Intel had "reserved"
for future use. This is apparently normal human thinking: "If something isn't a problem today, then let's punt
any foreseeable problems to future generations, because we've got more important things to do."
And on PC-DOS 1.00, does `SPEEDUP` actually speed things up? In the real world, it presumably did, but in
the world of emulation, there isn't any measurable difference, because hardware features like stepping rates and
head settling times are rarely simulated. As I suggested in my [last blog post](/blog/2017/07/15/), this is a
degree of authenticity that all emulators should strive for, and PCjs is no exception to that rule -- I just haven't
gotten around to it yet.
### Epilogue
There were some nice side benefits to debugging the problems introduced by `SPEEDUP`.
First, when attempting to `LIST` a BASIC program to a serial port, I discovered that BASIC depends on the port
generating a "Transmitter Holding Register Empty" interrupt after outputting each byte, and that I had never actually
implemented that interrupt. Only the "Received Data Available" and "Modem Status" interrupts had been implemented,
largely for serial mouse support. That has since been fixed, although "Line Status" interrupts still need to be
implemented, along with the proper queuing of all *four* possible types of serial port interrupts.
NOTE: Serial I/O from DOS (ie, redirection to or from a COM port) already worked because DOS performs polled I/O
rather than interrupt-driven I/O. Additionally, when DOS terminates a line, it outputs both CR (0Dh) and LF (0Ah)
bytes, whereas BASIC outputs only CR; so, when a machine uses a &lt;textarea&gt; control for serial I/O, we
automatically convert transform stand-alone CR bytes into LF bytes.
Second, when `SPEEDUP` crashed on newer versions of DOS, an invalid stack pointer would be loaded, and I noticed
that I had neglected to fully implement stack wrap-around on 8086/8088 processors; specifically, in the case where the
stack pointer is *odd*.
Normally, when the stack pointer is *even*, it will smoothly auto-decrement from 0000h to FFFEh or auto-increment
from FFFEh to 0000h. However, when the stack pointer is *odd* and it reaches 0001h, the next PUSH on a 8086/8088 must
automatically store the high byte at 0000h and the low byte at FFFFh; similarly, when the stack pointer is FFFFh,
the next POP must fetch the low byte from FFFFh and the high byte from 0000h.
Most emulators simulating an 8086/8088 in real-mode actually use V86-mode, which doesn't support stack operands that
wrap around the top of the stack segment; V86-mode will throw an exception, and the machine is toast. PCjs makes more
of an effort simulate real-mode, so any PUSH or POP should work smoothly, regardless whether the stack pointer is even
or odd.
PCjs is still far from perfect. For example, instruction fetches that cross a 64K boundary are supposed to wrap around
to 0000h as well. That's not implemented yet, but stay tuned.
*[@jeffpar](http://twitter.com/jeffpar)*
*Jul 21, 2017*