Dec 052017
Run TRS-80 programs on your PC. Emulator for TRS-80 model III.
File TRS80.ZIP from The Programmer’s Corner in
Category Assembly Language
Run TRS-80 programs on your PC. Emulator for TRS-80 model III.
File Name File Size Zip Size Zip Type
READDSK.C 5322 1862 deflated
READDSK.EXE 9769 6544 deflated
TPCREAD.ME 199 165 deflated
TRS80.CHR 3072 789 deflated
TRS80.EXE 114563 24921 deflated
TRS80.TXT 14073 5762 deflated

Download File TRS80.ZIP Here

Contents of the TRS80.TXT file

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;
Set H flag;
Resert H flag;

if(A>0x9f || (C flag is set)) {
set C flag;
Reset C flag;
else {
/* last operation was addition (ADD ADC INC) */
if( (A & 0x0f)>9 || (H flag is set)) {
set C flag;
set H flag;
reset H flag;

if((A >0x9f) || (C flag is set)) {
set C flag;
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:


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
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
50 PUT 1,X+1
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:


Adjust the file size to 14438 bytes by typing
at '-' prompt:
at ':' prompt:
at '-' prompt:
at ':' prompt:
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


where xxxx is that 4 digit hexadecimal number. DEBUG should
respond with "Writing 3800 bytes". Leave DEBUG by typing


at the '-' prompt. Rename DUMP to level2.m3. The emulator is now usable.

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.


 December 5, 2017  Add comments

Leave a Reply