home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
cpm
/
news
/
cpmnet81.jny
< prev
next >
Wrap
Text File
|
1994-07-13
|
19KB
|
535 lines
>>>>>>>>>>>>>>>>>>>>> CP/M-Net News <<<<<<<<<<<<<<<<<<<<<<<<
============================================================
Number 1 January, 1980 Volume 1, Issue 1
============================================================
Printed monthly (at worst quarterly) to inform user's of
RCPM Systems to the latest software news, information, and
updates of public domain software accessible via
telephone/modem transfer. Yearly subscription for copies of
the CP/M-Net News may be obtained by mailing $18.00 (check
or money orders only) to Kelly Smith, CP/M-Net, 3055 Waco
Avenue, Simi Valley, California 93063. CP/M-Net is a non-
profit orginization and all money received on subscriptions
are utilized for the sustaining and enhancments of the CP/M-
Net System.
If you would like to contribute an article, include a
column containing your area of interest and expertise, or
participate in an open forum for conversation and transfer
of ideas, feel free to send it to the CP/M-Net System and
indicate that you would like it to be included in the CP/M-
Net News...if possible, use WordStar (trademark, MicroPro
International) or Electric Pencil (trademark, Micheal
Shrayer) in 60 column format.
Note: CP/M is a registered trademark of Digital Research
MODEM/XMODEM Protocol Explained
by Kelly Smith, CP/M-Net "SYSOP"
January 8,1980
I thought that it may be of some interest to those of you
that use the MODEM/XMODEM file transfer capability of the
CP/M-Net, to get a little insight as to the communications
protocol (i.e. "handshaking method") used by the system.
Herein lies the details of a very good (not perfect) data
communications protocol that has become the "de facto"
standard for various remote CP/M systems (RCPM's) that are
accessible across the country (refer to RCPMLST5.DOC on all
RCPM's for access numbers and note that the "digit number"
in that list changes as new system are listed). I also wish
to give credit to Ward Christensen (the "original" CBBS) for
writing MODEM.ASM (CPMUG Volume 25.11) and Keith Petersen,
Bruce Ratoff, Dave Hardy, Rod Hart, Tom "C" (we know who you
are Tom!), and others, for enhancements to Ward's original
program that we now call XMODEM (external modem).
Data is sent in 128 byte blocks with sequentially numbered
blocks, and appended by a single checksum at the end of each
block. As the receiving computer acquires the incoming data,
it performs it's own checksum and upon each completion of a
block, it compares it's checksum result with that of the
sending computers. If the receiving computer matches the
checksum of the sending computer, it transmits an ACK (ASCII
code protocol character for ACKNOWLEDGE (04 Hex, Control-F))
back to the sending computer. The ACK therefore means "alls
well on this end, send some more...". Notice in the
following example, that the sending computer will transmit
an "initial NAK" (ASCII protocol character for NEGATIVE
ACKNOWLEDGE (15 Hex, Control-U))...or, "that wasn't quite
right, please send again". Due to the asynchronous nature of
the initial "hook-up" between the two computers, the
receiving computer will "time-out" looking for data, and
send the NAK as the "cue" for the sending computer to begin
transmission. The sending computer knows that the receiving
computer will "time-out", and uses this fact to "get in
sync"...The sending computer responds to the "initial NAK"
with a SOH (ASCII code protocol character for START OF
HEADING (01 Hex, Control-A)), sends the first block number,
sends the 2' complement of the block number (VERY important,
I will discuss this later...), sends 128 bytes of 8 bit data
(thats why we can transfer ".COM" files), and finally a
checksum, where the checksum is calculated by summing the
SOH, the block number, the block number 2's complement, and
the 128 bytes of data.
Receiving Computer:
----/NAK/------------------------/ACK/----------------------
15H 06H
Sending Computer:
--------/SOH/BLK#/BLK#/DATA/CSUM/---/SOH/BLK#/BLK#/DATA/etc.
01H 001H 0FEH 8bit 8bit 01H 002H 0FDH 8bit ....
This process continues, with the next 128 bytes, IF the
block was ACK'ed by the receiving computer, and then the
next sequential block number and it's 2's complement, etc.
But what happens if the block is NAK'ed?...easy, the
sending computer just re-sends the previous block. Now the
hard part...what if the sending computer transmits a block,
the receiving computer gets it and sends an ACK, but the
sender does not see it?...The sending computer thinks that
it has failed and after 10 seconds re-transmits the
block...ARGH!...the receiving computer has "stashed" the
data in memory or on disk (data is written to disk after
receiving 16 blocks), the receiving computer is now 1 block
AHEAD of the transmiting computer! Here comes the operation
of the block numbers...The receiver detects that this is the
last block (all over again), and transmits back an ACK,
throws away the block, and (effectively) "catches
up"...clever! Whats more, the integrity of the block number
is verified by the receiving computer, because it "sums" the
SOH (01 Hex) with the block number plus the 2's complement
of the block number), and the result MUST BE zero for a
proper transfer (e.g. 01+01+FE hex = 00, on the first
block). The sequence of events then, looks like this:
Receiving Computer:
----/ACK/-----------------------/NAK/-----------------------
06H 15H
Sending Computer:
CSUM/---/SOH/BLK#/BLK#/DATA/CSUM/---/SOH/BLK#/BLK#/DATA/etc.
8bit 01H 003H 0FCH 8bit 8bit 01H 003H 0FCH 8bit ....
Normal completion of data transfers will then conclude with
an EOT (ASCII code protocol END OF TRANSMISSION, 04 Hex,
Control-D) from the sending computer, and a final ACK from
the receiving computer. Unfortunately, if the receiving
computer misses the EOT, it will continue to wait for the
next block (sending NAK's every 10 seconds, up to 10 times)
and eventually "time-out". This is rarely the case however,
and although not "bullet-proof", it is a very workable
protocol.
Receiving Computer:
----/ACK/---/ACK/"Transfer Complete"/A>(or B>)
06H 06H ................................
Sending Computer:
CSUM/---/EOT/---/A>(or B>)
8bit 04H .............
In some case, where the telephone transmission is
repeatedly "trashed" (weak signals, multiple noise "hits",
etc.), the receiving computer (and operator) will be
provided the option to quit. Here, the operator enters "R"
or "Q" in response to "Retry or Quit?" (after 10 retries),
and if quit is envoked by the operator, a CAN (ASCII code
protocol CANCEL, 18 Hex, Control-X) is sent by the receiving
computer to cancel the entire transfer session (Note: is is
possible to "garble" an ACK to a CAN, and abort
prematurley):
Receiving Computer:
----/NAK/...NAK's ten times.../"Retry or Quit?"(Q)/CAN/A>...
15H 18H
Sending Computer:
CSUM/---/...Garbled Data....../-----------------------/A>...
8bit
A final considerations when using the MODEM program, is a
timing related problems when transfer status messages and/or
textual data is directed to the screen of a slow (4800 Baud
or less) terminal or to a hard copy printer. This problem is
readily apparent (multiple NAK's) when using MODEM for the
first time, and can usually be "cured" by NOT SPECIFYING the
"V" (video) sub-option when sending or receiving files.
Users of Lifeboat Associates BSTAM encounter the same
problem, but this is easily fixed with the files TQPATCH.ASM
and RQPATCH.ASM (transfer quiet/receive quiet) that Keith
Petersen (Royal Oak CP/M, "call-back" remote system, (313)-
588-7054) wrote to solve the problem of low speed terminal
I/O.
For users of CBBS's that do not have MODEM.ASM (but DO HAVE
a CP/M disk system...ESSENTIAL!), let me suggest that you
"data capture" the file MBOOT3.ASM from one of the RCPM's
(it's a small 8 kilo-byte file that "fits" in most system's
memory) to get the larger MODEM.ASM (40 kilo-bytes). Check
it very carefully for errors using the "data capture" (read
ERROR PRONE method here). Then edit and assemble for your
modem configuration.
If you are tired of buying software where the advertisment
is written better than the program, then the RCPM's are just
what you have been looking for...and FREE!
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Software Tricks for the 8080/Z80
by
Kelly Smith, CP/M-Net "SYSOP"
January 8, 1980
In my travels through the software written by others
(articles, disassemblies, etc.), I occasionaly find some
"tricks" incorporated to either optimize the storage
requirements of the code (typically ROM based) or to attempt
to confuse disassembly...although they are not recommended
routines, I think you may find them of interest...
The LXI Trick
Many large mainframe computers (mini's and maxi's) have a
SKIP instruction...but most micro's are multi-byte
instruction oriented, and therefore it is difficult to
provide a SKIP when the instruction length is
indeterminate. First, an example of "straight" coding:
error1: mvi a,1 ; set-up error code 1
jmp error$handler
error2: mvi a,2 ; set-up error code 2
jmp error$handler
error3: mvi a,3 ; set-up error code 3, and fall into it
;
error$handler: ; all error codes come here
;
lxi d,error$message ; point to error message,
; error code in A reg.
.
.
.
This is easy enough to understand (right?), but consider
this:
lxib equ 1 ; equate first byte of LXI b,nnnn
;
error1: mvi A,1 ; set-up error code 1
db lxib ; first byte of LXI B,nnnn
error2: mvi A,2 ; set-up error code 2
db lxib ; first byte of LXI B,nnnn
error3: mvi A,3 ; set-up error code 3
lxi d,error$message ; point to error message,
; error code in A reg.
.
.
.
If a jump is made to ERROR1, the E Reg. is set-up, then the
LXIB will be executed, the B&C Regs. will be given "garbage"
code that follows it, and finally the program counter will
be incremented past the next instruction...it SKIP's and
falls into the eventual output routine...insidious to
disassemblers! This was one of the "favorite's" at MITS
(remember the Altair?).
The ORI Trick
You might code:
and$function: ; indicate boolean AND
;
mvi a,1 ; set flags to non-zero, this is AND
jmp do$boolean; do boolean function
;
or$function: ; indicate boolean OR
;
xra a ; set flags to zero, this is OR
;
do$boolean: ; boolean functions come here
;
your code... ; do something...anything!
.
.
.
But consider the following:
ori equ 0f6h ; equate first byte of ORI n
;
and$function: ; indicate boolean AND
;
db ori ; set flags with A reg. not zero,
; first byte of ORI
or$function: ; indicate boolean OR
;
xra a ; set flags to zero, this is OR
;
do$boolean: ; boolean functions come here
;
your code... ; is everyone confused?
.
.
.
This one is particulary clever; when entering at
AND$FUCTION, the ORI picks-up the "XRA A" as F6 Hex, and
automatically set's the flags non-zero. Let me suggest that
if you actually use the LXI or ORI trick...comment it WELL
in your source code...you may have to patch your
program YEARS LATER, and will this look strange !?!
Using XTHL
A "cute" (not fast) example for register "swapping" when
all registers are used and must be saved, is shown as
follows:
exchange$bc$with$hl: ; exchange B&C Regs. with H&L Regs.
;
push b ; put B&C Regs. on the stack
xthl ; H&L Regs. = top stack entry = B&C Regs.
pop b ; B&C Regs. = original H&L Regs.
Very often you will code a routine to pass a constant to a
subroutine, such as:
mvi c,1
call dumb$subroutine
.
.
.
mvi c,2
call dumb$subroutine
.
.
.
mvi c,3
call dumb$subroutine
.
.
.
;
dumb$subroutine: ; use argument passed in C reg.
.
.
.
By manipulating the return address, you can save one byte
per CALL as follows:
call trick$subroutine
db 1 ; put constant in "return" location
.
.
.
call trick$subroutine
db 2 ; put constant in "return" location
.
.
.
call trick$subroutine
db 3 ; put constant in "return" location
.
.
.
;
trick$subroutine: ; trick subroutine to get constant
;
xthl ; H&L Regs. = return address
mov c,m ; get constant pointed to by H&L Regs.
inx h ; bump for return address
xthl ; restore the return address and H&L Regs.
;
dumb$subroutine: ; use argument passed in the C Reg.
;
.
.
.
The "Indirect Jump" via the Stack
Try this trick to save a few bytes, by faking a "indirect
jump" via the stack...you might code this routine:
call get$data$word
jmp use$data$word
;
get$data$word: ; get word into H&L regs.
;
lhld my$data$word ; fetch my data word
ret
;
use$data$word: ; use data word in H&L Regs.
But a more "elegant" (though obtuse) method could be coded
as this:
lxi h,use$data$word ; make "indirect address"
push h ; save it on the stack
;
get$data$word: ; get word into H&L Regs.
;
lhld my$data$word ; fetch my data word
ret ; pop stack for address and "jump"
This can lead to even trickier manipulations on the stack
for return address's...everyone (at one time or another) has
coded a routine to "filter" keyboard characters, and it
usual looks like this:
cpi '.' ; period character?
jz filter
cpi ',' ; comma character?
jz filter
cpi ';' ; semicolon character?
jz filter
cpi ':' ; colon character?
jz filter
.
.
.
But we need to save some bytes, so we get tricky with
coding like this:
lxi b,filter ; make "FILTER" address
push b ; put "FILTER" address on the stack
cpi '.' ; period character?
rz ; pop stack and go, if match
cpi ',' ; comma character?
rz ; pop stack and go, if match
cpi ';' ; semicolon character?
rz ; pop stack and go, if match
cpi ':' ; colon character?
rz ; pop stack and go, if match
pop b ; no match...adjust the stack
.
.
.
A popular method for "In line" printing of messages in CP/M
applications program, is as follows:
call start ; go to START, after message
db 'My Junk Program, Version 1$'
;
start: pop d ; get address of message string
mvi c,2 ; CP/M print string function
call 5 ; let CP/M do the work
.
.
.
Register "Moving"
The tricky way to move the D&E Regs. to B&C Regs might be
as follows:
push d
pop b
But the obvious way (and faster) is just:
mov d,b
mov e,c
A really tricky programmer could use the PUSH/POP method to
affect the condition code...this "blows-away" even
experienced programmers when they encounter it in someones
code!...watch this:
mvi c,081h ; the "flags"
.
.
.
push b ; use "cunning set-up" to confuse
.
.
.
pop psw ; do it to it!
.
.
.
This has the effect of moving the B reg. into the A Reg.,
and moving the C Reg. into the PSW (flags), with the carry
and sign bits SET (sign is minus), and all other flags reset
to zero's...this trick causes most programmers to mumble for
hours...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Helpful CP/M Tip-of-the-Month
Have you ever fired-up Digital Research's DDT or SID
program debugger's, specifying the program to debug...It's
loads, then goes '?' , because it can't find the file, and
it's really on the other diskette (let's say B:) in your
system !?! So you Control-C out (back to CP/M) and PIP the
program to the proper diskette...ARGH!
So use DDT or SID, to CHANGE the logged in drive number to
get to the file you want...Let's assume that we are logged
on to the A: drive, and our target file to debug is on
B:...follow along:
A>DDT BUMBFILE.COM<cr>
DDT VERS 2.0
?
-
So here we sit (not the least bit amused), contemplating
our navels...DO THIS!
-IBUMBFILE.COM<cr> {set-up temporary FCB with filename.typ}
-S5C<cr> {Substitute starting at address 5C Hex}
005C 00 02<cr> {set drive number 2 (B: disk)}
005D 42 .<cr> {quit substituting}
-R<cr> {Read BUMBFILE.COM}
As if by magic, the debugger will log on to the B: disk,
grab the file, and read it in for your debug session! All
you have to remember, is that at address 5C Hex is the start
of the temporary file control block, and that:
01 equals A: disk
02 equals B: disk
03 equals C: disk
.
.
.
So on, and so on for up to 16 disk drives...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++