TRS-80 Model III emulator - BETA RELEASE Written by Vincent Van Den Berghe and placed into public domain (for now) --------------------------------------------------------------- 0. Warning! This is a pre-release. Since hollidays are over, I decided to put this on Compuserve, "just to see what will happen". The program contains some deficiencies. Some of these will be remedied when the definitive version will arrive. Some other errors are not so simple to correct. This text file provides an overview of all known deficiencies, and should tell you which ones I plan to correct. This should give a pretty good idea of the final product. But I'm pretty sure the thing works, since I play sea dragon, Galaxy Invasion and Cosmic fighter with it every day! 1. Introduction Enclosed is the executable file TRS80.EXE, a TRS-80 Model III emulator. To run this file, you need at least an AT with an VGA card (or compatible) adapter and a hard disk. Your AT should have at least one 5 1/2 1.2Mb inch drive. In addition, you should have access to either MULTIDOS or Super Utility on the Model III. The reason why is explained in section 2. The file TRS-80.EXE by itself is unusable: you need to create another file called LEVEL2.M3, which should be exactly 14336 bytes long, and should contain the exact image of the TRS-80 Model III ROM. The file was not included to avoid copyright problems (I know americans like lawsuits). Appendix A explains one way of obtaining this file. I guess this is not the most elegant way and requires some hacking skills but what did you expect? I did not invent the copyright concept! On the other hand, you could create LEVEL2.M3 yourself (which could actually be any valid Z-80 program) and use that as "rom". Section 2 describes design issues faced during developement, and the resulting problems and pitfalls. You should read and understand section 2 before getting too excited. Any solutions for the problems below are welcome. Section 3 describe how to use the emulator. 2. Design issues, problems, pitfalls, ect... -------------------------------------------- To provide some insights into the problems of emulating the TRS-80, a brief description of the programs is made. The emulator is written entirely in C. It is not my intention to freely distribute the source code (yet). 2.1 The CPU ----------- The heart of the TRS-80 Emulator is a Z-80 emulator. This Z-80 emulator is actually a C module, interfacing with the outside world exclusively with the following functions: /* The following 4 functions define the interface between the Z-80 emulator and the outside world. */ byte GetMemByte(word address); void PutMemByte(word address,byte data); byte GetPortByte(byte address); void PutPortByte(byte address,byte data); void StartEmulator(void); void StopEmulator(void); void ResetCPU(void); int GenerateInterrupt(void); void GenerateNMI(void); As you can see, the entire interface goes through these functions. By writing GetMemByte and BytMemByte appropriately, one defines a Z-80 Memory Map. By writing GetPortByte and PutPortByte, one defines a Z-80 Port Map. The Z-80 emulator can be 'interrupted' by calling the GenerateInterrupt or the GenerateNMI function. The GenerateInterrupt function generates a maskable interrupt. What exactly happens depends on the interrupt mode the Z-80 is in. Currently, only interrupt mode 1 is implemented. This is the only interrupt mode used in the TRS-80. The GenerateNMI interrupt generates a non-maskable interrupt. NMI interrupts are used by the Model III floppy disk controller. Since all interfacing happens using functions, the function call overhead is a problem. Thus the Z-80 emulator suffers from a speed problem. On my 33Mhz 386, this is not much of a problem, but I agree that on a 6Mhz AT computer, the speed is not at all convincing. I have yet to find a solution to this problem. Assembly language programming is out of the question. The temporary solution is to use macros for inline expansion The Z-80 emulator emulates the Z-80 CPU exactly, with the following exceptions: - IM 1 is the only implemented maskable interrupt. - The lower 7 bits of the refresh register (R) are not incremented with each instruction fetch (as is the case with the Z-80), but contain a random value, obtained from a pseudo-random number generator. The 8th bit will remain as programmed as the result of a LD R,A instruction. This change does not affect the TRS-80, since register R is used as random number generator anyway. - The DAA instruction is implemented using the following algorithm: void ALUDaa(void) { Clear Z, S and P flags if(N flag is set) { /* last operation was substraction */ if( (A & 0x0f)>9 || (H flag is set)) { if((A & 0x0f)<6) set C flag; A-=6; Set H flag; } else Resert H flag; if(A>0x9f || (C flag is set)) { A-=0x60; set C flag; } else Reset C flag; } else { /* last operation was addition (ADD ADC INC) */ if( (A & 0x0f)>9 || (H flag is set)) { if(A+6>0xff) set C flag; A+=6; set H flag; } else reset H flag; if((A >0x9f) || (C flag is set)) { A+=0x60; set C flag; } else reset C flag; } update S,Z and P flags according to the value of A } For all bytes resulting of valid BCD operations (add or sub), this indeed produces the correct results. For invalid bytes, the result produced by this algorithm is different from the result produced by the Z-80. I have been unable to find out why, but it doesn't seem to matter for the Model I/III. - Interrupts are not recognised during LDIR and LDDR instructions. This improves the efficiency somewhat and does not harm functionality. - Illegal or undocumented instructions stop the emulator. I have been unable to find a reliable way of determining what such instructions do. Any suggestions? A most noticable problem occurs when booting Model III TRS-DOS 1.3. At some point (usually when attempting to load a program), the emulator terminates with an "illegal instruction". I am unable to determine why. As far as I could determine, this seems to be a genuine TRS-DOS 1.3 bug. 2.2 The Video ------------- Emulating the video circuitry is not that difficult. The character set of the TRS-80 is obtained by reprogramming the EGA/VGA character generator to use a 8x12 character matrix, and remap the PC video RAM to the TRS-80 video RAM. The following deficiencies are present: - no support for 32-character mode. This will be corrected in the future, as soon as I can figure out the correct CRTC register value for 8-dot-characters in 40-character mode. - I have no access to the Model III alternate character set, so this is not implemented. It should be in the final release. 2.3 Interrupts -------------- The Model III heartbeat interrupt (every 33ms) is emulated by reprogramming the 8259 PIC. No known problems there. 2.4 Keyboard ------------ The TRS-80 emulator contains its own keyboard driver. It will correctly drive enhanced keyboards. Other keyboards were not tested. The following nonstandard keys are implemented: - The clear key is implemented as . - The break key as - Left arrow is left arrow or backspace - right arrow is right arrow or tab - the @ key is not yet present Some keys may differ, since the exact TRS-80 layout is used. This will be corrected in the final version. It should be noted that in Belgium, other keyboard layouts are used (AZERTY). 2.5 Floppy disk controller ------------------------- This is by far the most tricky part of the whole system. The FDC 1793 (model III) and FDC 1771/FDC 1791 (Model I with Percom Doubler) seem to be much more flexible than that stupid FDC 776 used in a PC. Here are the problems: - For one thing, single density is not implemented on a PC. This means the emulator is unable to boot Model I disks. For the model III, this is no problem. (incidentally, the solution for the model I emulator was to make a full double density NEWDOS/80 disk (using my doubler-equipped expansion interface. You need to format-without-erase the NEWDOS/80 diskette though. Read on. ) - For another thing, the 765 is VERY sensitive to correct formatting. As some of you probably know, formatting a disk on a TRS-80 involves building byte-per-byte the track in memory, and writing it to the disk. Formatting a track on a PC is an intrinsic operation of the 765. The track format consists of intersector 'gaps' (analogous to blanks on music cassettes), sector id fields (telling the FDC what sector follows), and the actual data fields (what you get when you read a sector). The 765 is very sensitive to intersector gaps. There is even a special parameter (called 'gap') telling the 765 what to expect. Unfortunately, this only works during format. During read, the 765 expects: - 22 bytes 0x4e - 12 bytes 0x00 as the contents of "Gap 3". For the Model I and III, only MULTIDOS and Super Utility were found to adhere to this format. NEWDOS/80 2.0, TRS-DOS, and LDOS format a disk using the wrong format. For example, NEWDOS/80 2.0 uses: - 16 bytes 0x4e - 8 bytes 00 This causes the 765 on a PC to report "missing id" on the last physical sector on a track. The solution is to do a 'Format without erase' with Super Utility before using the disk. This will establish the correct gaps. There is a special "gap" parameter for the 776, but I tried all 256 possible values, but was unable to produce any difference. - When reading a sector on a TRS-80, one specifies the sector number and the track number (actually the numbers specified in the sector id field). When reading a sector on a PC, one specifies all that too, but in addition the sector SIZE. The net result is that if the sector size is not known in advance (as for protected disks), the sector will not be found. The result is that protected disks or disks with special formats will NOT boot. There is no efficient solution for this. All sectors read are assumed to be 256 bytes in size. There is a solution for this problem, but I'll hold it back until some other ideas come along. - The read track command is not implemented. I don't know why, but my code does not work. - The write track command will only work for standard track formats. - writing to a physical drive is not implemented. I have provided "virtual TRS-80 drives" to which one can read and write. 3. Using the emulator. Make sure the following files are present in the current directory: level2.m3 trs80.chr trs80.exe Start the emulator by typing TRS80. On the question "How many disks?", enter a digit from 0 to 4. If you enter 0, you will go to standard level 2 basic. For each disk, you will be asked: Enter name for disk x where x is 0 the max # of disks you just entered -1. The answer to this question is either: - a valid MSDOS filename - a valid MSDOS drive letter followed by a colon ':'. This drive is assumed to be a 1.2Mb Hi-density drive. You can "boot" by answering the drive letter for disk 0. If the filename you specify does not exist, you will be asked to enter the max # of tracks (usually 40) and the # of sides (1 or 2). Be aware that the file behaves like a disk, and has to be formatted before use. For example, the first time I ran the emulator, I asked for 2 drives: A: (my 1.2Mb drive) and multidos.m3, a 40 track, single sided "disk". After booting Multidos from drive A:, I issued the command "backup :0 :1" to create a multidos.m3 "bootable virtual drive". Appendix A: Making LEVEL2.M3 There are several ways to make this file. Here's the one I used. You need: - access to a TRS-80 Model III, with 2 drives - any Model III DOS with BASIC on it (NEWDOS, MULTIDOS, LDOS, not TRSDOS!) - a blank disk - DEBUG on MS-DOS Format the blank disk with the Model III DOS. If you have double sided drives, make sure the disk is single sided. The disk should contain nothing but the familiar BOOT/SYS and DIR/SYS. Boot DOS on the TRS-80, go to BASIC and type in the following program: 10 OPEN "R",1,"LEVEL2/M3:1",1 20 FIELD 1,1 AS B$ 30 FOR X=0 TO 14435 40 LSET B$=CHR$(PEEK(X)) 50 PUT 1,X+1 60 NEXT X 70 CLOSE 1 Run this program with the just formatted disk in drive 1. This will simply create a file with the ROM contents on that disk. Because the disk is blank, the file will be contained in continguous sectors, and start at the lowest usable sector on that disk. Now insert that disk in your PC drive. A program called READDSK.EXE has been included (with source code). This program will read the first 8 tracks from any double density disk in drive A: consisting of 18 256-byte sectors and save this information to a file called DUMP. The program should now execute. After completion, the file DUMP should be on the current directory of the current drive. Now, in MS-DOS, type: DEBUG DUMP Adjust the file size to 14438 bytes by typing at '-' prompt: rbx at ':' prompt: 0 at '-' prompt: rcx at ':' prompt: 3800 Now search for the start of the ROM image by typing at the '-' prompt: scs:0lffff f3 af c3 15 30 (these bytes are (c) Tandy (!)). DEBUG will respond with something like 1D43:xxxx, where xxxx is a 4 digit hexadecimal number. Now type wcs:xxxx where xxxx is that 4 digit hexadecimal number. DEBUG should respond with "Writing 3800 bytes". Leave DEBUG by typing q at the '-' prompt. Rename DUMP to level2.m3. The emulator is now usable. COMMENTS Direct all comments to Vincent (me). I do not have an account on compuserve, but can be reached through Johnny Penet, whose account I am using for uploading these files. Vincent