I’ve recently begun to use TRS-80 Model I emulators to recapture some of the programming experiences of my younger days. The emulator I’m currently using under Windows is trs80gp which can be found here:
http://members.shaw.ca/gp2000/trs80gp.html
I invoke the emulator with the command-line parameters -m1 to force Model I emulation and -na for non-authentic display.
While trs80gp does not support any of the Disk-Operating-Systems of yesteryear, it does provide a menu option that will load /CMD executable files and BASIC files. You really can’t save any information back to the Windows filesystem, but that’s okay. For my purposes, I want to write some programs in a cross-assembler or cross-compiler environment and will just load the files to try them out. At some point, I will either look for or will build a utility that will tokenize a text listing to a BASIC listing so that I can create BASIC programs as well.
My initial goal, however, is to write Z-80 code and run it on the emulator.
My first task was to find a cross-assembler for the Z-80 that would run under Windows. The trs80gp site references ZMac which sounds like a great editor, but it doesn’t appear to compile in its current form under Windows.
I found a utility called Pasmo here:
http://www.arrakis.es/~ninsesabe/pasmo/
Like some of the others I tried, Pasmo basically generates a binary machine-image file. I chose Pasmo because of the -d option which shows an assembly listing on the standard output device.
I would like to be able to package up anything I write into a standard /CMD file so that it can be used with other emulators or on the real hardware itself. In order to do that, I was going to have to determine how to convert a machine-image file into a /CMD file.
As a test program, I would use the example routine I published in my post http://jimlawless.wordpress.com/2009/11/07/learning-z-80-assembly-language-on-the-trs-80/. That routine fills the video memory with the all-white character (191) and then returns.
I have several /CMD images of games that I have on cassette and began looking through them. I could see some control-codes and such, but my initial stab at trying to interpret them was not successful. After a little searching on the web, I found a reference to an article from The LDOS Journal volume 1, issue 4. Tim Mann has copies of this issue and others on his site here:
http://www.tim-mann.org/misosys.html
The article that describes the format is in the column Roy’s Technical Corner.
Roy describes the record formats permissible in a /CMD file. The format is not unlike the binary tag-based system used in the TIFF graphical image file format. The first byte one encounters is a record-type byte. The next byte is a length of bytes that will follow … sort of. The remainder of bytes should match the length specified in the length byte. The next record in sequence should be another record ID / length / payload sequence, but that doesn’t seem to hold true either.
I wrote a short C program readcmd.exe that lists each record in a /CMD file. While number of my /CMD files parsed correctly just fine, some did not.
Roy explains that the 01 record indicates a loadable block of binary data. The length byte in many of my /CMD files was zero, which I correctly assumed would yield a 256-byte block. However some of the /CMD files in my possession use a value of two in the length byte and seem to have a payload bigger than two bytes in length.
The article further explains that each loadable block of data first contains a load-address and states that zero and one are special values that indicate a two-byte load-address will be followed by 254 and 255 bytes of data respectively. The article doesn’t mention the value two, but I assume that since the 01 record will always have a load-address, two bytes will always follow. The values zero, one, and two are then used for machine-images of size 254, 255, and 256 respectively. The value three is a complete mystery to me. I have seen a small block with a length of four and the payload that follows the load-address is four bytes in length. I’ll tinker later and see how the emulators load a record with a length value of three.
I should state that my readcmd program is dependent on the Intel representation of a 16-bit integer ( Least Significant Byte followed by Most Significant Byte ). An unsigned short integer must be 16-bits in width in order for the program below to run correctly.
readcmd.c
// readcmd
// Dump the record information for a TRS-80 /CMD
// executable file
//
// License: MIT / X11
// Copyright (c) 2009 by James K. Lawless
// jimbo@radiks.net http://www.radiks.net/~jimbo
// http://www.mailsend-online.com
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#include <stdio.h>
int main(int argc,char **argv) {
FILE *fp;
unsigned char buff[258];
unsigned int len;
unsigned short address;
printf("readcmd v1.0 by Jim Lawless\n");
printf("http://jimlawless.wordpress.com\n\n");
fp=fopen(argv[1],"rb");
for(;;) {
if(!fread(buff,1,1,fp))
break;
// record type is "load block"
if(*buff==1) {
fread(buff,1,1,fp);
len=*buff;
// compensate for special values 0,1, and 2.
if(len<3)
len+=256;
// read 16-bit load-address
fread(&address,1,2,fp);
printf("Reading 01 block, addr %x, length = %u.\n",address,len-2);
fread(buff,1,len-2,fp);
}
else
// record type is "entry address"
if(*buff==2) {
fread(buff,1,1,fp);
len=*buff;
printf("Reading 02 block length = %u.\n",len);
fread(&address,1,len,fp);
printf("Entry point is %d %x\n",address,address);
break;
}
else
// record type is "load module header"
if(*buff==5) {
fread(buff,1,1,fp);
len=*buff;
printf("Reading 05 block length = %u.\n",len);
fread(buff,1,len,fp);
}
else {
printf("Unknown code %u at %lx\n",*buff,ftell(fp)-1L);
break;
}
}
fclose(fp);
}
I found that after the 02 record is encountered, all kinds of garbage data can follow. I assume that most /CMD loaders halt interpretation of the file after the 02 record is encountered. You’ll notice a break out of the main input loop when readcmd encounters this record.
My readcmd program was able to parse through all of the /CMD files in my possession. Now that I have a way to verify the correctness of a /CMD file, it’s time to try and build my own.
I really can’t remember where the first bytes of free memory really start on a Model I. Address 17129 looks to be the spot where BASIC begins, but I’ve never owned a DOS on a Model I, so I don’t know if that address can change.
I noticed that a lot of the games I have begin at address 6C00H, so I chose that as the starting-address for my program.
Here is the slightly modified source code from my prior post:
fill.asm
ORG 6c00H
LD HL,3C00H ; 15360 in hex
LD A, 191
LD [HL],A
LD DE,3C01H
LD BC,1023
LDIR
RET
I then assembled it with the command:
pasmo -d fill.asm fill.out
The output from Pasmo is as follows:
ORG 6C00 6C00:21003C LD HL, 3C00 6C03:3EBF LD A, BF 6C05:77 LD (HL), A 6C06:11013C LD DE, 3C01 6C09:01FF03 LD BC, 03FF 6C0C:EDB0 LDIR 6C0E:C9 RET Emiting raw binary from 6C00 to 6C0E
My load-module is fifteen bytes in size. I need to create a load-record that accommodates fifteen plus two bytes for the load address. My 01 record will have a length of seventeen ( or 11H ) bytes. Here’s how the 01 record should look in hex:
01 11 00 6C 21 00 3C 3E BF 77 11 01 3C 01 FF 03 ED B0 C9
The total space occupied by the 01 record is nineteen bytes.
I then needed to add a 02 record to state the transfer address of 6C00H:
02 02 00 6C
The total size of the two records is twenty-three (17H) bytes in length.
I manually created the /CMD file using the Windows console DEBUG utility.
debug fill.cmd e 100 01 11 00 6C 21 00 3C 3E BF 77 11 01 3C 01 FF 03 e 110 ED B0 C9 02 02 00 6C rcx 17 w q
I first used readcmd to ascertain that the records looked reasonable:
readcmd fill.cmd
readcmd v1.0 by Jim Lawless http://jimlawless.wordpress.com Reading 01 block, addr 6c00, length = 15. Reading 02 block length = 2. Entry point is 27648 6c00
I loaded fill.cmd in trs80gp and it correctly filled the screen with whitespace.
I had expected the RET instruction to drop me back into BASIC, but it did not. I’m not sure if that’s an idiosyncrasy of trs80gp or if all emulators and the actual hardware/software will behave in this manner. ( NOTE: after fixing my code to use 1023 bytes to move instead of 4095 as George Phillips, author of trs80gp notes in the comments to the post, I did indeed drop into BASIC, but received a ?SN ERROR message at the prompt. I may try George’s suggestion of JP-ing to 1A19H at a later time. )
In the near future, I will write a program that will convert a larger memory-image file ( such as the one produced by Pasmo ) into a /CMD file.
The source and EXE files for readcmd can be found here, along with the source and /CMD file for the fill program.
http://www.mailsend-online.com/wp/readcmd.zip
Save to del.icio.us
Digg it
Save to Reddit
Share on Facebook
Share on Twitter
More bookmarks
Unless otherwise noted, all code and text entries are Copyright © 2009 by James K. Lawless

