• Please review our updated Terms and Rules here

New version of Xmodem.com for CP/M

mfeberhard

Member
Joined
Jan 14, 2017
Messages
39
Location
California
For all you users of my CP/M Xmodem program, I’ve just released a new version, version 2.10. This version corrects a long-standing bug. Here’s a wordy description of the bug:

Xmodem versions prior to 2.10 would fail in some cases during reception with checksums, with a "Can't Sync" error, if XMODEM was started before the sender (e.g. TeraTerm) was started. Xmodem will send a NAK every few seconds to tell the sender to start sending, and to use checksums (rather than CRCs). Xmodem will do this until it receives any byte from the sender. Before sending the file, TeraTerm attempts to flush its serial port to get rid of any stray NAKs by repeatedly reading from its serial port until the serial port indicates that it has no data available. This will normally flush out any accumulated NAKs that Xmodem sent while trying to establish connection for file reception using checksums. However, this fails if the PC hardware or OS does not immediately report that a new character is waiting after TeraTerm's flush loop reads a NAK. The result will be an extra NAK in its serial port buffer, causing TeraTerm to resend the first block. This will cause an Xmodem synchronization error later on, the first time that Xmodem holds off ACKing a block (e.g. to write to disk) because there will be an extra ACK in the queue. The solution (in Xmodem version 2.10) is to wait to see if the serial port is idle for a half second after the very first block. If not, then Xmodem will receive the resent block 1 without having sent an ACK for the first one. From there on, synchronization is correct.

While I was at it, I corrected a typo in that “Can’t Sync” error message, and I replaced my PDEC16 subroutine (which prints a 16-bit value in decimal) with a really slick and extremely compact recursive subroutine that was written by Bill Gates back when he was still writing code. For you assembly language nerds, take a look at that subroutine an see if you can understand how it works :-) I love it!

Note that the file that's called Xmodem.cpm needs to be renamed Xmodem.com once it's on your CP/M machine.

Please try it out, tell me if I broke anything!!!

-Martin
 
Please try it out, tell me if I broke anything!!!
Unfortunately, I currently don't have hardware to test it on.

But I was curious and looked at the source code (very tight code, neat). The patch area for custom I/O routines is quite short, only 12 bytes each. Sufficient for calling into custom BIOS routines, but is that enough to actually write custom routines? Have you never needed more?

In the usage section of XMODEM.ASM, the documentation for /E is:
; /E Specifies an enhanced RDR routine that returns with the
; Z flag set if no character is waiting. Note that this
; option does not actually select the RDR device as the
; transfer port. (/X2 does.)
The RDR device is selected with /X1, not /X2. :)
But the actual help text is different, so this is not a real issue.
 
Unfortunately, I currently don't have hardware to test it on.

But I was curious and looked at the source code (very tight code, neat). The patch area for custom I/O routines is quite short, only 12 bytes each. Sufficient for calling into custom BIOS routines, but is that enough to actually write custom routines? Have you never needed more?

In the usage section of XMODEM.ASM, the documentation for /E is:

The RDR device is selected with /X1, not /X2. :)
But the actual help text is different, so this is not a real issue.
12 bytes seems to be adequate - the /X3 option is intended for calling ROM-based routines, such as those in CUTER. If you want to talk directly to the hardware, then you are better off using the /X2 option, which assumes very typical I/O hardware, see below. (You could do this with /X3, but there's no reason to do that.)

This program's been out there for more than a decade, so far I haven't heard of a real case where 12 bytes was inadequate for /X3. I had worked hard to make this program functional even with a small amount of RAM, so every byte mattered :-)

/X2 routines

Receive routine:
RXBYTE: IN <status port>
CMA or NOP <invert if the ready bit has the wrong polarity>
ANI <mask>
RZ <no character waiting, return with Z flag set>
in <data port>
RET <with Z flag cleared>


Transmit routine:
TXBYTE: IN <status port>
ANI <mask>
jz TXBYTE
mov a,c
out <data port>
ret


Yeah, you are right about that comment about the RDR device. Funny how I can look at the same bit of code for years and not see a glaring typo like that! My only guarantee is that is not the only typo in the comments!

For fun, go read Bill Gate's PDEC16 subroutine in that code!

Thanks,
Martin
 
12 bytes seems to be adequate - the /X3 option is intended for calling ROM-based routines, such as those in CUTER. If you want to talk directly to the hardware, then you are better off using the /X2 option, which assumes very typical I/O hardware, see below.
Some modern CP/M implementations run on top of another operating system and use its API to access hardware directly, so I was thinking about systems using I/O request packets. For example, the CP/M-65 port to the BBC Micro family uses such a hosted environment, and when I was considering running CP/M on top of CAOS on the KC85/2 family (which turned out to impractical).

These interfaces use shared data structures, and setting up and maintaining those probably takes more than 12 bytes.

My only guarantee is that is not the only typo in the comments!
There were a few others, but I really did not want to be too annoying. ;-)

For fun, go read Bill Gate's PDEC16 subroutine in that code!
I did, that's why I started reading the code in the first place. Implementing division by repeated subtraction is going to be veeeery slow for large numbers... but it surely is tight code.
 
I did, that's why I started reading the code in the first place. Implementing division by repeated subtraction is going to be veeeery slow for large numbers... but it surely is tight code.
True it is a slow routine - more so the more digits. But (at least for 16 bits), it's still plenty faster than the serial port to the console! This routine is only used for a few messages at the conclusion of the transfer, so even if the console port is super fast, a few hundred milliseconds is not even noticeable to the user :-) In this case (as with Bill Gates's Basic), compactness trumps speed.
 
Thinking aloud, just some unrelated reflections of the early days (daze?) :)
Modem7 from which xmodem originates was written in the days of 300 baud modems, polled serial routines and no handshaking. No handshaking over a telephone line!
We never had any problems at all.
Since then almost everyone uses interrupt-driven receive (& possible also transmit) which has messed up the timing, so a byte received by the program could have been sent several byte-lengths of time ago.
With polled tx/rx, when the receiver sees a Nak or Ack it assumed that character had just been sent right then in real time. Timings were based on immediate. non-buffered exchanges.
Not sure where I'm going with this but my mind was drifting back to running a 300 baud BBS in the late 70s/early 80s... xmodem was everything and seldom failed. Then came Y, Z etc...
Sorry I'm rambling... :)
Cheers
Phil
 
True it is a slow routine - more so the more digits. But (at least for 16 bits), it's still plenty faster than the serial port to the console!
In a separate thread, we had Kermit transfers failing because the video console output was extremely slow. The Commodore 128 console output is also famous for being slow.

Since then almost everyone uses interrupt-driven receive (& possible also transmit) which has messed up the timing,
CP/M does not require interrupt-capable hardware, so many systems do not implement any. XModem is fully driven by the receiver, so buffer overruns are unlikely. With at most one data packet in flight, the protocol is quite latency-sensitive. The timeout mechanism also does not handle intermediate buffering and jitter well. All these modem protocols suffer greatly when trying to be run over a TCP/IP connection.

The biggest issues with XModem are the lack of proper file name and file size tracking, but CP/M being unable to do better, it's a good match. Especially now, where most transfers are done locally, from a modern terminal to a vintage computer or SBC.
 
Modem7 from which xmodem originates was written in the days of 300 baud modems, polled serial routines and no handshaking. No handshaking over a telephone line!
In those days, we were required to let the phone company know that we had a modem on the phone line and they would send a tech out to make sure that the line was "data quality."

I remember a friend of mine in college getting free local phone service for 10 months because when he put his modem on his phone line, he called the phone company and got charged a fee for the tech to verify his line. But he got line noise calling the college's computer, therefore the phone company wasn't providing him what they promised.
 
Back
Top