Home of the original IBM PC emulator for browsers.
The machine below is configured to run the original, unmodified, copy-protected software fromdBASE III 1.0 (Disk 1). More information on dBASE III copy-protectionis availablebelow.
[PCjs Machine "ibm5160"]
Waiting for machine "ibm5160" to load....
Two of the PCjs disks shown below:
are from our private collection and should be exact copies of the original dBASE III 1.0 distribution disks, with theexception ofDBASE.EXE
, which was patched to eliminate copy-protection checks. Details of that patch are availablein thePCjs Document Archive,along with thisblog post.
Also, for completeness, the originalDBASE.EXE
has been recreated by “unpatching” it and saving it on theLocked disk image. Even though we no longer have the original distribution disk,we have “annotated” our disk image to simulate the same damaged sector that the original disk contained, so the originalprogram runs as well.
Another variation of the dBASE III disks come from theWinWorld archive:
and, with the exception ofDBASE.EXE
, the files onDisk 1 match the files on ourPatched disk; the newer dates probably just reflect an update by Vault Corporationto the copy-protection code inDBASE.EXE
. However,Disk 2 contains more filesthan ourSamples disk, and WinWorld’s README admits that:
Some of the sample programs have been run/changed.
so overall, the disks from our private collection may be more authentic.
Volume in drive A has no label Directory of A:\DBASE EXE 112720 9-28-84 3:46pDBASE OVL 147456 6-26-84 5:36pHELP DBS 53760 6-26-84 5:36pASSIST HLP 15223 6-26-84 5:37pREAD ME 4224 6-26-84 5:37pCONFIG SYS 22 6-26-84 5:37p 6 file(s) 333405 bytes 15360 bytes free
Volume in drive A has no label Directory of A:\DFORMAT EXE 39424 6-12-84 2:56pDCONVERT EXE 48128 6-06-84 3:10pDFM MSG 50432 6-12-84 2:46pDEPOSITS DBF 227 6-14-84 3:42aCHECKFIL DBF 355 6-14-84 3:42aINVGET DBF 419 6-14-84 3:40aINVOICES DBF 598 6-14-84 3:43aBILLINGS DBF 1024 4-29-84 7:57pNAMES DBF 1024 5-01-84 12:52pPERSON DBF 1248 4-30-84 5:31pPOSTFILE DBF 1339 6-05-84 10:51aHOLD84 DBF 1412 4-01-84 10:56pCLADDRES DBF 1477 4-08-84 9:01pPERSONNE DBF 1925 6-05-84 10:55aRENTALS DBF 2560 6-15-84 2:06pSUPPLIER DBF 2659 4-09-84 7:40pCOSTBASE DBF 3301 6-14-84 3:46aWAGES DBF 3801 4-01-84 10:55pNAMES FMT 398 4-07-84 7:38pKONSTANT MEM 770 5-01-84 3:58pCOSTJOBS NDX 1024 1-01-80 1:45aBILLINGS NDX 1024 1-01-80 1:46aBILLJOBS NDX 1024 1-01-80 1:46aINVOICES NDX 1024 1-01-80 1:46aCOSTNAME NDX 2048 1-01-80 1:46aSUPPLIER NDX 2560 3-28-84 5:55pINVSUBTO PRG 768 3-28-84 9:54pDEPPRINT PRG 896 3-28-84 9:49pINVUPDAT PRG 896 5-01-84 8:37aDEPMENU PRG 1280 6-15-84 2:47pBIG PRG 1280 6-15-84 3:26pACCOUNTS PRG 1408 4-30-84 9:08aINVEDIT PRG 1408 5-01-84 8:38aPAYMENU PRG 1408 5-01-84 8:45aINDEXING PRG 1408 5-01-84 10:11aINVCHECK PRG 1536 5-01-84 8:38aPAYRECAP PRG 1536 6-15-84 4:41pNAMETEST PRG 1664 5-01-84 1:59pDEPTRANS PRG 1664 5-01-84 8:31aINVMENU PRG 1792 5-01-84 8:33aCOSTUPDA PRG 2048 6-15-84 2:45pCOSTMENU PRG 2688 5-01-84 7:57aPAYEMPS PRG 2944 6-15-84 2:43pDEPOSITS PRG 3584 6-15-84 2:41pCOSTTIME PRG 3584 6-15-84 2:45pTIMECALC PRG 3712 6-15-84 2:50pCHECKSTU PRG 3712 6-15-84 5:21pPAYFIND PRG 3712 6-15-84 4:42pINVOICES PRG 3712 6-15-84 4:53pCOSTBILL PRG 4608 5-01-84 1:32pSALESTAX PRG 5376 6-15-84 5:43pINVPRINT PRG 5888 6-15-84 4:37pJOBCOSTS PRG 6016 6-15-84 2:43pPAYBILLS PRG 9472 6-15-84 4:44pPAYROLL PRG 13056 6-15-84 4:50p 55 file(s) 264281 bytes 75776 bytes free
Volume in drive A has no label Directory of A:\DBASE EXE 112720 8-11-17 9:51aDBASE OVL 147456 6-26-84 5:36pHELP DBS 53760 6-26-84 5:36pASSIST HLP 15223 6-26-84 5:37pREAD ME 4224 6-26-84 5:37pCONFIG SYS 22 6-26-84 5:37p 6 file(s) 333405 bytes 15360 bytes free
Volume in drive A has no label Directory of A:\DBASE EXE 112720 7-15-84 8:28aDBASE OVL 147456 7-15-84 8:28aHELP DBS 53760 7-15-84 8:29aASSIST HLP 15223 7-15-84 8:29aREAD ME 4224 7-15-84 8:29aCONFIG SYS 22 7-15-84 8:29a 6 file(s) 333405 bytes 10240 bytes free
Volume in drive A has no label Directory of A:\DFORMAT EXE 39424 6-12-84 2:56pDCONVERT EXE 48128 6-06-84 3:10pDFM MSG 50432 6-12-84 2:46pDEPOSITS DBF 227 6-14-84 3:42aCHECKFIL DBF 355 6-14-84 3:42aINVGET DBF 419 6-14-84 3:40aINVOICES DBF 598 6-14-84 3:43aBILLINGS DBF 1024 4-29-84 7:57pNAMES DBF 1342 11-13-84 9:31aPERSON DBF 1248 4-30-84 5:31pPOSTFILE DBF 1339 6-05-84 10:51aHOLD84 DBF 1412 4-01-84 10:56pCLADDRES DBF 1477 4-08-84 9:01pPERSONNE DBF 1925 6-05-84 10:55aRENTALS DBF 2560 11-12-84 4:01aSUPPLIER DBF 2659 4-09-84 7:40pCOSTBASE DBF 3301 6-14-84 3:46aWAGES DBF 3801 4-01-84 10:55pNAMES FMT 398 4-07-84 7:38pKONSTANT MEM 770 5-01-84 3:58pCOSTJOBS NDX 1024 1-01-80 1:45aBILLINGS NDX 1024 1-01-80 1:46aBILLJOBS NDX 1024 1-01-80 1:46aINVOICES NDX 1024 1-01-80 1:46aCOSTNAME NDX 2048 1-01-80 1:46aSUPPLIER NDX 2560 3-28-84 5:55pCONSTANT MEM 426 11-06-84 3:04aDEPPRINT PRG 896 3-28-84 9:49pINVUPDAT PRG 896 5-01-84 8:37aDEPMENU PRG 1280 6-15-84 2:47pBIG PRG 1280 6-15-84 3:26pACCOUNTS PRG 1408 4-30-84 9:08aINVEDIT PRG 1408 5-01-84 8:38aPAYMENU PRG 1408 5-01-84 8:45aINDEXING PRG 1408 5-01-84 10:11aINVCHECK PRG 1536 5-01-84 8:38aPAYRECAP PRG 1536 6-15-84 4:41pNAMETEST PRG 1664 5-01-84 1:59pDEPTRANS PRG 1664 5-01-84 8:31aINVMENU PRG 1792 5-01-84 8:33aCOSTUPDA PRG 2048 6-15-84 2:45pCOSTMENU PRG 2688 5-01-84 7:57aPAYEMPS PRG 2944 6-15-84 2:43pDEPOSITS PRG 3584 6-15-84 2:41pCOSTTIME PRG 3584 6-15-84 2:45pTIMECALC PRG 3712 6-15-84 2:50pCHECKSTU PRG 3712 6-15-84 5:21pPAYFIND PRG 3712 6-15-84 4:42pINVOICES PRG 3712 6-15-84 4:53pCOSTBILL PRG 4608 5-01-84 1:32pSALESTAX PRG 5376 6-15-84 5:43pINVPRINT PRG 5888 6-15-84 4:37pJOBCOSTS PRG 6016 6-15-84 2:43pPAYBILLS PRG 9472 6-15-84 4:44pPAYROLL PRG 13056 6-15-84 4:50pLAST NDX 1024 11-13-84 9:22aINVSUBTO PRB 768 3-28-84 9:54pADDRESS NDX 1024 11-13-84 9:27aZIP NDX 1024 11-13-84 9:23aMONEY NDX 1024 11-06-84 3:36aFULL NDX 1024 11-06-84 3:38aWHO NDX 1024 11-06-84 4:12aMONTH NDX 1024 11-13-84 9:32aTEST TXT 588 11-07-84 12:22pCHAPTER6 TXT 2414 11-07-84 1:11pTEST BAK 190 11-07-84 1:09pTEST PRG 223 11-07-84 1:48pSALES NDX 1024 11-07-84 2:01pRENTS DBF 1317 11-07-84 2:00pCOMMISS FRM 1990 11-07-84 2:26pSUMMARY TXT 1262 11-07-84 2:19pSTATESLS NDX 1024 11-07-84 2:39pPERSON NDX 1024 11-12-84 10:38aNOTES DBF 274 11-12-84 4:20aNOTES DBT 512 11-12-84 4:14aDOCTORS DBF 253 11-13-84 10:18a 76 file(s) 284288 bytes 48128 bytes free
MFM SectorSector ID:005Track ID:039 - Side ID:000Size:00512 (ID:0x02)DataMark:0xFBHead CRC:0x60C8 (Ok)Data CRC:0x0013 (BAD CRC!)Start Sector cell:44272Start Sector Data cell:44984End Sector cell:53272Number of cells:90000000| 2C DB 06 D4 FF F8 23 03 | ,.....#.0008| 2E DF 0A D6 01 F1 1C F9 | ........0010| 24 E5 10 D4 FF E6 11 0B | $.......0018| 36 E5 10 C6 F1 E2 0D 01 | 6.......0020| 2C FD 28 C4 EF C6 F1 02 | ,.(.....0028| 2D 1D 48 C6 F1 A2 CD 18 | -.H.....0030| 43 25 50 A4 CF B6 E1 2A | C%P....*0038| 55 05 30 B6 E1 D2 FD 00 | U.0.....0040| 2B 1D 48 F4 1F 97 C2 C2 | +.H.....0048| ED 1E 49 36 61 93 BE B8 | ..I6a...0050| E3 66 91 34 5F 47 72 8A | .f.4_Gr.0058| B5 A6 D1 66 91 03 2E 40 | ...f...@0060| 6B FE 29 A5 D0 E7 12 1E | k.).....0068| 49 DE 09 83 AE C3 EE 67 | I......g0070| 92 26 51 55 80 97 C2 45 | .&QU...E0078| 70 06 31 B3 DE F3 1E 20 | p.1.... 0080| 4B DE 09 F5 20 B8 E3 9D | K... ...0088| C8 5F 8A 73 9E 30 5B 27 | ._.s.0['0090| 52 E3 0E E6 11 B9 E4 A4 | R.......0098| CF 50 7B 7C A7 C9 F4 16 | .P{|....00A0| 41 44 6F EE 19 C2 ED B4 | ADo.....00A8| DF 41 6C 74 9F C6 F1 36 | .Alt...600B0| 61 59 84 C6 F1 22 4D 54 | aY..."MT00B8| 7F E9 14 DD 08 B7 E2 A9 | ........00C0| D4 42 6D 4B 76 F3 1E EC | .BmKv...00C8| 17 83 AE 8C B7 34 5F 2E | .....4_.00D0| 59 C7 F2 CE F9 64 8F 6C | Y....d.l00D8| 97 1B 46 04 2F D4 FF BE | ..F./...00E0| E9 6F 9A 66 91 14 3F 1C | .o.f..?.00E8| 47 B3 DE CC F7 54 7F 7E | G....T.~00F0| A9 F7 22 2F 5A A4 CF DF | .."/Z...00F8| 0A 4C 77 89 B4 F5 20 36 | .Lw... 60100| 61 A0 CB 1E 49 B5 E0 34 | a...I..40108| 5F 9C C7 24 4F BD E8 36 | _..$O..60110| 61 90 BC 00 00 0C 48 00 | a.....H.0118| 00 00 04 10 1B 00 00 00 | ........0120| 00 40 82 42 04 30 0F F3 | .@.B.0..0128| 80 42 30 33 00 00 07 F1 | .B03....0130| 8C 0E 00 00 7C 00 06 13 | ....|...0138| E0 60 07 02 40 01 F0 00 | .`..@...0140| 99 10 4C 00 06 20 00 00 | ..L.. ..0148| 9E 10 09 00 10 66 08 00 | .....f..0150| 80 03 C0 00 04 3C 02 03 | .....<..0158| 90 84 13 02 40 81 1C 20 | ....@.. 0160| 04 8C 02 00 1C C9 80 02 | ........0168| 00 0C 42 42 30 01 FC 60 | ..BB0..`0170| 01 24 02 00 87 88 01 00 | .$......0178| 47 C1 30 00 F1 E7 88 30 | G.0....00180| 09 08 41 20 06 1E 20 30 | ..A .. 00188| 0E 00 99 23 00 C7 38 21 | ...#..8!0190| 04 00 11 21 04 4E 00 23 | ...!.N.#0198| 00 60 01 33 00 66 08 00 | .`.3.f..01A0| 64 78 00 10 0C 7E 00 03 | dx...~..01A8| 90 66 08 E3 80 44 33 C1 | .f...D3.01B0| 24 03 C0 00 41 8C 02 00 | $...A...01B8| 01 06 18 24 02 7C 02 00 | ...$.|..01C0| FE 60 07 C0 20 10 0C 00 | .`.. ...01C8| 78 20 06 44 32 19 E0 01 | x .D2...01D0| E4 C1 3C C1 24 00 79 02 | ..<.$.y.01D8| 70 40 3C 42 00 08 00 00 | p@<B....01E0| 04 01 C4 00 F9 C7 38 E0 | ......8.01E8| 81 20 01 8C 03 0E 40 00 | . ....@.01F0| 7E 48 00 70 08 06 18 00 | ~H.p....01F8| 18 10 C8 19 E0 CC 83 E3 | ........
The followingDiskImage command was used to reflect the above error in our “dBASE III 1.0 (Disk 1)” disk image:
diskimage.js --disk=archive/DBIII-100-DISK1-KF.img --output=DBIII-100-DISK1-KF.json --sectorError=39:0:5:272
Note that we used an IMG built from the Kryoflux stream files instead of the IMG provided by WinWorld. However, theonly difference was in the 512-byte sector at offset 0x58400, which corresponds precisely to the bad sector at 39:0:5(shown above).
It was initially unclear whether the original contents of the bad sector mattered to dBASE III’s copy-protection logic.During the copy-protection check, the sector is read, an error is expected, and then the sector is rewritten, read again,and only about the first 272 (+/-10) bytes must change for the copy-protection test to pass.
Also, it seems that not all dBASE III 1.0 files/disks were the same, because in order to run the “unpatched” copyof dBASE III that I restored onto the “dBASE III 1.0 (Locked)” disk image, sector 2 (not sector 5) on track 39 must bethe damaged sector, and the point of damage must occur at approximately byte 204 rather than 272. So I updated thatdisk image as follows:
diskimage.js --disk=archive/DBIII-100-LOCKED.img --output=DBIII-100-LOCKED.json --sectorError=39:0:2:204
I didn’t have the original contents of the bad sector, but since that copy of DBASE.EXE now runs, it’s safe to say thatthe copy-protection code doesn’t actually care about the sector’s initial state.
Before running DBASE.EXE, turn on FDC messages (m fdc on
) in thePCjs Debugger and set an executionbreakpoint at 0626:161E (bp 626:161E
). When the breakpoint is hit, you’ll see code that is checking a word at DS:989 [0xAAAA]against the first word of the sector 39:0:5, which it just read back into DS:742 (0626:0742
) after attempting tocompletely rewrite the sector.
It then goes on to see how many of the 512 bytes in the sector were successfully rewritten. Ifall of them were modified(ie, the REPZ SCASB count in CX goes to zero), then the code is definitely unhappy. It then goes on to verify that the numberof “unmodifiable” (ie, bad) bytes in the sector is not less than 0xE5 nor greater than 0xF9. That exact range may vary fromdisk to disk, since the midpoint of that range (0xEF) comes from a word at DS:0186, whose origins I have not yet investigated.
>> rAX=AAAA BX=0742 CX=2705 DX=0001 SP=0124 BP=0010 SI=1974 DI=0942 SS=0626 DS=0626 ES=0626 PS=F246 V0 D0 I1 T0 S0 Z1 A0 P1 C0 &0626:1625 B0AA MOV AL,AA>> trAX=AAAA BX=0742 CX=2705 DX=0001 SP=0124 BP=0010 SI=1974 DI=0942 SS=0626 DS=0626 ES=0626 PS=F246 V0 D0 I1 T0 S0 Z1 A0 P1 C0 &0626:1627 B90002 MOV CX,0200 ;cycles=12>> trAX=AAAA BX=0742 CX=0200 DX=0001 SP=0124 BP=0010 SI=1974 DI=0942 SS=0626 DS=0626 ES=0626 PS=F246 V0 D0 I1 T0 S0 Z1 A0 P1 C0 &0626:162A BF4207 MOV DI,0742 ;cycles=12>> trAX=AAAA BX=0742 CX=0200 DX=0001 SP=0124 BP=0010 SI=1974 DI=0742 SS=0626 DS=0626 ES=0626 PS=F246 V0 D0 I1 T0 S0 Z1 A0 P1 C0 &0626:162D F3 REPZ &0626:162E AE SCASB ;cycles=12>> prAX=AAAA BX=0742 CX=00FF DX=0001 SP=0124 BP=0010 SI=1974 DI=0843 SS=0626 DS=0626 ES=0626 PS=FA02 V1 D0 I1 T0 S0 Z0 A0 P0 C0 &0626:162F 83F900 CMP CX,0000>> trAX=AAAA BX=0742 CX=00FF DX=0001 SP=0124 BP=0010 SI=1974 DI=0843 SS=0626 DS=0626 ES=0626 PS=F206 V0 D0 I1 T0 S0 Z0 A0 P1 C0 &0626:1632 7502 JNZ 1636 (DBASE.EXE+0x1C45) ;cycles=12>> u 1636DBASE.EXE+0x1C45:&0626:1636 A18601 MOV AX,[0186]&0626:1639 2D0A00 SUB AX,000A&0626:163C 3BC8 CMP CX,AX&0626:163E 7247 JC 1687 (DBASE.EXE+0x1C45)&0626:1640 051400 ADD AX,0014&0626:1643 3BC8 CMP CX,AX&0626:1645 7740 JA 1687 (DBASE.EXE+0x1C45)...
So, to simulate failure at just the “write” point, I added the following hard-coded logic to thewriteData() functioninfdc.js:
if(drive.sector['dataError']&&drive.iByte>=266){break;}
Originally, I didn’t allowany of the bytes to be written in a sector whosedataError property was set, and thenI decided to let exactly half (256) of the bytes to be written, and neither attempt satisfied the copy-protection check.It was only after I started debugging the dBASE III code that I discovered I was close, and that by allowing 266 bytesto be written, the check would pass.
This hack is now generalized, by allowing a sector’sdataError property to be set to a number (eg, 266), which is thenused to limit the number of bytes that can be written to that sector.