Sunday, 16 January 2022

UPLOAD.COM for Z80 CP/M - usage

 I feel very honoured to have written a utility which will be distributed as part of the system included with RC2014 kits with CP/M or ROMWBW.


Since upgrading my first RC2014 to CP/M, transferring files has been a bit tricky. My storage medium is a compact flash card, which I can't read with my modern computer system. I can "dd" it to make a backup, which is easy to transfer to a new card. But that doesn't allow me to put a file onto the card, or copy one off using my Mac.

There are methods of reading the CF card on a new system, but I've not made anything work for me. 

My favourite method of transferring my files onto the card is a combination of the DOWNLOAD.COM utility which comes with the CP/M system on the A: drive, and my own utility for creating a .PKG file. That's a hex format that works with the download utility. I've written about that here.  Spencer now provides a web utility which creates the same pkg file. 

The problem

That still leaves me unable to acquire a file that I've created on the RC2014. I tend to develop in Forth and assembly on my modern computer, transferring the files to the RC to run them, so I rarely create files there on the card. But there have been times when I've done so, and it would also be good to selectively back up files from the card rather than making an image of the whole thing.

Spencer asked me to have a go at writing UPLOAD.COM, the partner to DOWNLOAD.COM. I've written about the process of creating the utility separately if you're interested in Z80 programming and the CP/M's BDOS.

Using UPLOAD.COM

Sit UPLOAD.COM in A: alongside DOWNLOAD.COM

Then browse to any drive and use it like this:
A:UPLOAD MYFILE.COM

It is happy for you to give it wildcards in the standard way:
A:UPLOAD MAT?????.COM or 
A:UPLOAD *.* for every file in the directory, as I've done below.
Note that my terminal (minicom) doesn't do wrapping, and it doesn't allow me to select and copy the characters that are off the right-hand side. Those characters seem to be lost. The pkg format doesn't allow for breaks in the stream of hex characters.

The answer to this is to use the capture feature of your terminal program. In the case of minicom it's -C filename.  With that option, everything that comes to the console is also written to the file.
Here is my capture file, after UPLOADing all files in my B: drive. The process happens very quickly, and I was able to make a very convenient backup of every file in every drive on my CF card in a matter of minutes. 
It's very easy to copy and paste any of those files back into CP/M. The first line of the PKG is the instruction to use DOWNLOAD to put the file into the current drive.

UPLOAD.COM for Z80 CP/M - writing the utility

Below is a pkg file. It's easy to see what's going on. Your source file is in hex and padded to the 128-byte boundary. There's a count and checksum at the end. The first line is the instruction to use 'download.com' to put the file into the current directory as the name specified.

I wrote this utility for my Mac some time ago, it allows me to convert any file, paste the result into the Terminal app I'm using to communicate with my RC2014 and have the file copied onto one of the drives.

What has been missing is a way to do the opposite. To grab a file from the RC2014 as the original binary file. I've written about that problem here. The very neat solution would be to have a Z80 program which does this same conversion on the RC2014 and displays a file that you can copy and paste - and either convert back into a binary or store for re-downloading at some later point.

Conveniently, there's an example program in the CP/M Operating System Manual called A Sample File Dump Utility (5.4). This very nearly does what we want. It takes a filename as an argument and dumps that file as hex to the console (formatted neatly with numbering and spaces, which were easy to take out).

It was also easy to add the necessary count and checksum, output A:UPLOAD at the start along with the filename. 

I decided to look up the actual user number, rather than just printing "U0". This is the start of my deep dive into BDOS - a series of operating system calls, documented and available for you to use in your programs. They tend to involve setting up parameters in Z80 registers, setting the C register to the number of the call that you want to make, and then CALLing the base of the BDOS.

I've dabbled in this before, it provides convenient ways to receive keys and output characters to and from the console.

Getting the user number is simple. 

But then we came to parsing the passed argument for wildcards and finding matching files. The example program doesn't do this. It simply OPENs the file that's specified by the user, and if that happens to contain wildcards, the first matching file is opened. 

UPLOAD.COM would be a whole lot more useful if it does pattern matching and spews out all of the matching files. It now does and I've demonstrated this in my guide.

The CP/M documentation isn't bad. It allowed me to discover that you can pass a filename to a 'first' and 'next' functions (17 and 18) which give you the first and subsequent matches for your filename which contains ? or *.

I learned that the system has a 128-character buffer at a known location, which is used for passing results for certain functions. For filenames It's divided into 4 32-byte buffers and you get a number 0-3 in A which you shift-multiply by 32 and add to the base address of the buffer for the result you need.

After calling the 'first match' function, you have to call the 'next match' immediately, and then again, as long as you receive a valid result. (You can use this same process without checking whether your filename contains wildcard characters. If it doesn't, then 'first match' just returns your file, and 'next' will return 255 meaning 'no more results').

It would seem sensible to use the 'first' and 'next' system at the start and make an array of matching filenames. Instead I chose to do something less efficient, but that I thought was simpler to write, which is a 'get_nth_match' function, which passes the original filename to 'first' and then calls 'next' the appropriate number of times.

UPLOAD.COM is available here and included alongside download.com with the CF cards that you can buy for your RC2014.


Thursday, 6 January 2022

Creating a custom template for custom cards in Dendrite

As is sometimes the case, I'm writing this post primarily for my own future reference.

Dendrite is a development environment that allows you to work with tokens for commonly-used blocks of text when hand-coding a site. Further to that, it gives you simple dialogs for special blocks with structured text and images, known as 'cards'. 

It allows me to paste an image and fill in a few fields when I add stories to a page like the one above, this is a newsletter I manage.

There's a block of code near the top of this page, which generates a feature image. This is the code:
It's an image (with 2x and 3x versions) with a caption, all within a div which is floating right.

Each month for some years I've created the resized images and edited this code manually, because that's less effort than remembering how to set up a new template for a custom card in Dendrite. 

So today I've gone to the trouble, and have also gone to the trouble of documenting it here for you and for myself.

This text only appears once on the page, but the real benefit is being able to use the 'insert card' dialog to paste the image and write the caption.  Turning multiple fiddly copy-and-paste operations into a few slick and mindless strokes.

So here's the procedure. You're welcome, future-me.

Adding a new template to a Dendrite website

First copy one of the existing default templates, found in <website folder>/Templates. (use 'News' because that one uses all of the tokens, which will serve as a temporary reference). Change its name to something memorable (In this case, FeatureImage.txt) 
A template file consists of the code for that card, with dendrite-style tokens inserted where information from the card dialog is to appear. The example above should be self-explanatory.

With that in place, Insert Card in Dendrite offers this dialog, which you can use to fill in the information, and Add Image allows you to paste the image (past it once, Dendrite can create the 2x and 3x versions. All you have to do is give it a base name, which also serves as the id for this particular object.)


I use a keystroke to grab a roughly-square image to the clipboard. A paste into the image well in the dialog above crops it to the selected proportions and resizes it with 2x and 3x versions. It's a remarkably efficient way to add 'cards' containing images to a hand-coded web page. (Note that in this example, my new template doesn't use the body, the link text or the link target. So those can be ignored when filling in this card.)

Important things to remember

  • Remember to select your new template before OKing this card dialog (the drop-down button at the very top of the Insert Card dialog.) I think it defaults to 'News' rather than the last one you chose. 
  • If you use a different image format (as I do here, 200w and locked proportions rather than 150x150 for the regular news stories) remember to make sure those settings are right for this special card and future regular ones.