>>>>>>>>>>>>>>>>>>>>> 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...


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++