title   'Z-80 Floppy Disk Test'
; Floppy Disk Test for Z-80 CP/M Systems
;
; version 1.0  16 December 1980
;
; The purchaser may freely create any number
; of copies of this program on magnetic media
; as necessary to support his computer system(s).
; Further resale of this program is prohibited.
;
; Copyright (c) 1980 by
; Ray Duncan
; Laboratory Microsystems
; 4147 Beethoven Street
; Los Angeles, CA 90066
;
       org     100h
;
cpm     equ     5       ;references to
wboot   equ     0       ;operating system
;
;                       references to ASCII char
cr      equ     0dh
lf      equ     0ah
ff      equ     0ch
tab     equ     09h
;
;                       parameters for disk
;                       (supplied as single
;                       density, soft sectored)
$drvf   equ     1       ;first drive to allow
                       ;testing (0=A,1=B,etc)
$drvl   equ     3       ;last drive to allow
                       ;testing
$trkf   equ     0       ;first track
$trkl   equ     76      ;last track
$secf   equ     1       ;first sector
$secl   equ     26      ;last sector
$bps    equ     128     ;bytes per sector
$bpt    equ     $bps*$secl ;bytes per track
;
                       ;number of digits to
                       ;accept in track and
                       ;sector assignments
$dig    equ     2       ;(should be set larger
                       ; for devices having
                       ; track or sector
                       ; numbers >99 )
;
;                       program identification
$ver    equ     1       ;version
$rev    equ     0       ;revision
;
       jp      dtst    ;enter from CP/M
       form
; global variables for use by all routines
;
pass    dw      0       ;current pass number
errors  dw      0       ;error count for pass
;
                       ;the following variables
                       ;are used by RDBUF and
                       ;WTBUF to address the
                       ;disk, and by PERR to
                       ;display failing disk
                       ;addresses ---
drv     db      0       ;drive to test
trk     dw      0       ;current track
sec     dw      0       ;current sector
buffer  dw      0       ;current memory address
iolen   dw      0       ;bytes last transferred
;
                       ;the following variables
                       ;define the area to be
                       ;tested on the selected
                       ;disk drive ---
trkf    dw      0       ;first track to test
trkl    dw      0       ;last track to test
secf    dw      0       ;first sector to test
secl    dw      0       ;last sector to test
;
                       ;the following variables
                       ;define the test mode ---
                       ;
bypass  db      0       ;0=do not bypass error
                       ;itemization, 1=bypass
                       ;error itemization,print
                       ;total errors per pass
                       ;only.
                       ;
skew    db      0       ;0=no sector skew
                       ;1=use sector skew for
                       ;  increased test speed
                       ;
list    db      0       ;0=print errors on
                       ;terminal, 1=print errors
                       ;on list device.
                       ;
lockio  db      0       ;0=no lock
                       ;1=lock on read
                       ;2=lock on write
                       ;
restor  db      0       ;0=do not restore original
                       ;data, 1=restore original
                       ;data on diskette
                       ;
lockpt  db      0       ;0=use variable test
                       ;data pattern, 1=lock on
                       ;user supplied data pattern
                       ;
pattrn  db      0       ;contains user supplied
                       ;8 bit data pattern
                       ;
passl   dw      0       ;last pass to do on this
                       ;test run
                       ;
digits  db      $dig    ;maximum number of digits
                       ;to be accepted during
                       ;decimal or hexadecimal
                       ;numeric input.
                       ;
xtran   dw      sectrb  ;address of sector
                       ;translation table
       form
;
; disk test --- main control
;
dtst    equ     $       ;entry from CP/M
       ld      de,dtsta;print program title
       ld      c,9
       call    cpm
       ld      hl,(cpm+1)
       ld      de,buffend
       or      a,a     ;make sure enough user
       sbc     hl,de   ;memory to execute test
       jr      nc,dtst01
       ld      de,dtsts;not enough memory,
       ld      c,9     ;print warning and exit.
       call    cpm
       jp      wboot
dtst01  ld      c,12    ;check CP/M version
       call    cpm
       ld      a,l     ;make sure 2.x
       and     0f0h
       cp      20h
       jr      z,dtst02
       ld      de,dtstz;not CP/M 2.x, print
       ld      c,9     ;error message and exit
       call    cpm
       jp      wboot
dtst02  xor     a,a     ;initialize variables
       ld      (bypass),a
       ld      (skew),a
       ld      (list),a
       ld      (lockio),a
       ld      (restor),a
       ld      (lockpt),a
       ld      (pass),a
       ld      (pass+1),a
       ld      (errors),a
       ld      (errors+1),a
                       ;now set up test
                       ;configuration
       ld      de,dtstb
       call    getyn   ;itemize errors?
       cp      'y'
       jr      z,dtst03;yes
       ld      a,1     ;no
       ld      (bypass),a
       jr      dtst04  ;skip query for output
                       ;device, since errors
                       ;will not be listed
dtst03  ld      de,dtstc;audit errors on console
       call    getl    ;or line printer?
       cp      'c'
       jr      z,dtst04;c=use console
       cp      'p'
       call    nz,query
       jr      nz,dtst03;no match,try again
       ld      a,1     ;p=use line printer
       ld      (list),a
dtst04  ld      de,dtstd;lock on read or write?
       call    getl
       cp      'n'     ;n=no locks
       jr      z,dtst06
       cp      'r'     ;r=lock on read
       jr      nz,dtst05
       ld      a,1
       ld      (lockio),a
       jr      dtst12  ;bypass querys about
                       ;restore mode and
                       ;data pattern: since
                       ;we are locked in read
                       ;mode, they are
                       ;irrelevant.
                       ;
dtst05  cp      'w'     ;w=lock on write
       call    nz,query
       jr      nz,dtst04 ;no match,try again
       ld      a,2
       ld      (lockio),a
       jr      dtst08  ;bypass restore question,
                       ;since we are locked in
                       ;write mode.
                       ;
dtst06  ld      de,dtste;restore user data?
       call    getyn
       cp      'y'     ;y=restore
       jr      nz,dtst08
       ld      a,1     ;n=do not restore
       ld      (restor),a
dtst08  ld      de,dtstf;lock on data pattern?
       call    getyn
       cp      'n'
       jr      z,dtst12;n=use variable pattern
       ld      a,1     ;y=lock on pattern
       ld      (lockpt),a ;supplied by operator
       ld      de,dtstg;accept data pattern
       call    geth    ;from keyboard
       ld      (pattrn),a
dtst12  ld      de,dtsth;select drive to be
       call    getl    ;used for test
       sub     'a'     ;convert to logical #
       cp      $drvf   ;make sure its legal
       call    c,query
       jr      c,dtst12 ;too small,try again
       cp      $drvl+1
       call    nc,query
       jr      nc,dtst12 ;too large,try again
       ld      (drv),a ;save drive assignment
       add     'A'     ;also format for output
       ld      (dtsti1),a
       ld      de,dtsti;confirm selected drive?
       call    getyn
       cp      'n'
       jr      z,dtst12;not confirmed,try again
                       ;
                       ;initialize track
                       ;limits
       ld      hl,$trkf
       ld      (trkf),hl
       ld      hl,$trkl
       ld      (trkl),hl
dtst15  ld      de,dtstj ;test all tracks?
       call    getyn
       cp      'y'     ;y=use all of them
       jr      z,dtst20;n=user wants to specify
                       ;  range of tracks
dtst17  ld      de,dtstk;enter first track
       call    getn    ;to test
       ld      (trkf),hl ;save it
       ld      de,dtstl;enter last track
       call    getn    ;to test
       ld      (trkl),hl ;save it
       ld      de,(trkf);make sure first
       or      a,a     ;track<=last track
       sbc     hl,de
       call    c,query ;wrong,start over
       jr      c,dtst17
dtst20                  ;initialize sector
                       ;limits
       ld      hl,$secf
       ld      (secf),hl
       ld      hl,$secl
       ld      (secl),hl
dtst22  ld      de,dtstm;use all sectors
       call    getyn   ;of each track?
       cp      'y'
       jr      z,dtst26;y=use all sectors
                       ;n=user wants to specify
                       ;range of sectors
dtst24  ld      de,dtstn;enter first sector
       call    getn    ;to test.
       ld      (secf),hl ;save it.
       ld      de,dtsto;enter last sector
       call    getn    ;to test.
       ld      (secl),hl ;save it.
       ld      de,(secf);make sure first
       or      a,a     ;sector<=last sector
       sbc     hl,de
       call    c,query
       jr      c,dtst24;error,start over
                       ;
                       ;all variables set up
                       ;now --- how many
dtst26  ld      de,dtstp;test passes should be
       call    getn    ;made?
       ld      (passl),hl ;save # of passes
                       ;
                       ;print advisory message
       ld      de,dtstt;as test begins
       ld      c,9
       call    cpm
       ld      de,dtstu;remind user whether he
       ld      a,(restor) ;is using restore
       or      a,a     ;mode
       jr      z,dtst32
       ld      de,dtstv
dtst32  ld      c,9
       call    cpm
                       ;
dtst40                  ;begin a pass
       ld      hl,(trkf)
       ld      (trk),hl;initialize current track
                       ;
dtst42                  ;process next track
       ld      c,6     ;check for interruption
       ld      e,0ffh
       call    cpm     ;from console
       or      a,a
       jp      nz,dtst94 ;break detected,quit
       ld      a,(restor)
       or      a,a     ;is this restore mode?
       jr      z,dtst45;no,jump
       ld      hl,buff3;yes, save current
       ld      de,merr1;disk contents
       call    rdbuf
dtst45  ld      a,(lockio)
       cp      a,1     ;is this lock on read?
       jr      z,dtst47;yes,jump
       ld      hl,buff1;set up test pattern
       ld      de,$bpt
       call    bufpat
       ld      hl,buff1;write test pattern
       ld      de,merr2
       call    wtbuf
dtst47  ld      a,(lockio)
       cp      a,2     ;is this lock on write?
       jr      z,dtst70;yes,jump
       ld      hl,buff2;read back test pattern
                       ;(or just read existing
                       ; data if locked on read)
       ld      de,merr3
       call    rdbuf
dtst50  ld      a,(lockio)
       or      a,a     ;is this lock on
                       ;read or write?
       jr      nz,dtst70 ;yes,jump
                       ;no, compare test data
       ld      hl,buff1;written to data read
       ld      de,buff2;back from disk. If
       ld      bc,merr4;difference found,
       call    bufcmp  ;print error message
dtst70  ld      a,(restor)
       or      a,a     ;using restore mode?
       jr      z,dtst80;no,jump
                       ;yes,write back user's
                       ;data
       ld      hl,buff3
       ld      de,merr6
       call    wtbuf
       ld      hl,buff1;verify that
       ld      de,merr7;it was rewritten ok
       call    rdbuf
       ld      hl,buff1
       ld      de,buff3
       ld      bc,merr5;check restored data
       call    bufcmp
                       ;if difference found,
                       ;print 'data cannot
                       ;be restored'
                       ;
dtst80                  ;advance current track
       ld      de,(trk)
       inc     de
       ld      (trk),de
       ld      hl,(trkl)
       or      a,a     ;done with all tracks?
       sbc     hl,de
       jp      nc,dtst42 ;no,process another
                       ;
dtst90                  ;end of pass
       ld      bc,(pass)
       inc     bc      ;count passes
       ld      (pass),bc
       ld      hl,dtstr1
       call    conv    ;convert pass #
       ld      bc,(errors)
       ld      hl,dtstr2
       call    conv    ;convert error count
       ld      de,dtstr;print pass and errors
       ld      c,9     ;on console
       call    cpm
       ld      a,(list);also using printer?
       or      a,a
       jr      z,dtst92;no,jump
                       ;yes,also send pass
                       ;and error count to
                       ;list device
       ld      hl,dtstr
       call    perr9
dtst92                  ;reset error count
       xor     a,a
       ld      (errors),a
       ld      (errors+1),a
       ld      hl,(pass)
       ld      de,(passl)
       or      a,a     ;are enough passes done?
       sbc     hl,de
       jp      c,dtst40;not yet,loop
dtst94                  ;done with all passes
       ld      de,dtstw;ask whether to exit
       call    getl    ;or to continue test
       cp      'c'     ;c=continue
       jp      z,dtst
       cp      'e'     ;e=exit
       jr      nz,dtst94;if no match,try again
       ld      de,dtstx;print goodbye
       ld      c,9
       call    cpm     ;and return control
       jp      wboot   ;to CP/M
                       ;
       form
;
; routines to read and write up to one track
;
rdbuf                   ;read current track from
                       ;secf to secl
                       ;
                       ;call hl=buffer base addr
                       ;     de=error msg addr
       ld      (rdbufa),de ;save message address
       ld      (buffer),hl ;save buffer address
       ld      hl,0    ;initialize transfer byte
       ld      (iolen),hl ;count
       call    seldsk  ;select disk
       ld      hl,(secf)
       ld      (sec),hl;initialize current sector
rdbuf1  call    setio   ;set up track,sector,memory
       call    read    ;now request transfer
       or      a,a     ;was i/o successful?
       jr      z,rdbuf2;no error,jump
       ld      de,(rdbufa)
       call    perr    ;i/o error, audit it
rdbuf2  call    rwadv   ;advance sector address
       jr      nc,rdbuf1;not done,read another
       ret             ;back to caller
rdbufa  dw      0       ;address of error message
;
wtbuf                   ;write current track
                       ;from secf to secl
                       ;
                       ;call de=error msg addr
                       ;     hl=buffer base addr
                       ;
       ld      (wtbufa),de ;save message addr
       ld      (buffer),hl ;save memory addr
       ld      hl,0    ;initialize transfer
       ld      (iolen),hl ;byte count
       call    seldsk  ;select disk drive
       ld      hl,(secf)
       ld      (sec),hl;initialize current sector
wtbuf1  call    setio   ;set track,sector,memory
       call    write   ;request disk write
       or      a,a     ;any i/o errors?
       jr      z,wtbuf2;no,jump
       ld      de,(wtbufa)
       call    perr    ;error, audit it
wtbuf2  call    rwadv   ;advance sector address
       jr      nc,wtbuf1;not done,write another
       ret             ;back to caller
wtbufa  equ     rdbufa  ;save address of error
                       ;message
                       ;
rwadv                   ;advance sector and
                       ;memory addresses
                       ;
       ld      de,$bps ; de <- bytes per sector
       ld      hl,(buffer)
       add     hl,de   ;update buffer address
       ld      (buffer),hl
       ld      hl,(iolen)
       add     hl,de   ;count bytes transferred
       ld      (iolen),hl
       ld      de,(sec)
       inc     de      ;advance current sector
       ld      (sec),de
       ld      hl,(secl)
       or      a,a     ;done with all sectors?
       sbc     hl,de   ;exit with carry set if
                       ;done
       ret
       form
;
; set up buffer with test pattern
;
bufpat                  ;call hl=address of base
                       ;        of buffer
                       ;     de=byte length of
                       ;        area to set up
       ld      a,(lockpt)
       or      a,a     ;are we locked on user
                       ;specified data pattern?
       jr      nz,bufpa2;yes,jump
bufpa1  ld      a,r     ;read refresh register
       xor     a,h
       add     a,l
                       ;make data a function of
                       ;memory address
       ld      (hl),a  ;and store it
       inc     hl      ;advance buffer address
       dec     de      ;count bytes stored
       ld      a,d     ;done yet?
       or      a,e
       jr      nz,bufpa1 ;no,loop
       ret
                       ;user specified pattern
bufpa2  ld      a,(pattrn)
       ld      (hl),a  ;store one byte
       inc     hl      ;advance buffer address
       dec     de      ;count bytes stored
       ld      a,d     ;done yet?
       or      a,e
       jr      nz,bufpa2 ;not done,loop
       ret             ;exit
                       ;
       form
;
;
; compare specified buffer and print error
; message if difference found
;
bufcmp                  ;compare buffers
                       ;
                       ;call bc=address of
                       ;        error message
                       ;     de=address 1st buffer
                       ;     hl=address 2nd buffer
                       ;
       ld      (bufcma),bc ;save msg address
       ld      (bufcmb),hl ;save base of buffer
       ld      bc,(iolen)  ;length to compare
bufcm1  ld      a,(de)  ;fetch byte from 1st buffer
       cp      a,(hl)  ;compare it to 2nd buffer
       jr      nz,bufcm3 ;difference found,jump
bufcm2  inc     hl      ;advance buffer addresses
       inc     de
       dec     bc      ;count bytes
       ld      a,b     ;done yet?
       or      a,c
       jr      nz,bufcm1 ;no,loop
       ret             ;back to caller
                       ;
bufcm3                  ;difference found, print
                       ;error audit trail
       push    bc      ;first save registers
       push    de
       push    hl
       ld      de,(bufcmb)
       or      a,a
       sbc     hl,de   ;find a buffer offset
       push    hl      ;now divide by bytes per
       pop     bc      ;sector to find relative
       ld      de,$bps ;sector number
       call    div
       ld      hl,(secf)
       add     hl,bc   ;add relative sector to
                       ;first sector to find
                       ;actual address for use
                       ;by PERR
       ld      (sec),hl
       ld      de,(bufcma)
       call    perr    ;now audit error
       pop     hl      ;restore registers
       pop     de
       pop     bc
bufcm4                  ;advance memory address
                       ;out of this sector where
                       ;an error was found.
       inc     hl      ;bump buffer addresses
       inc     de
       dec     bc      ;done with all data area?
       ld      a,b
       or      a,c
       ret     z       ;yes,exit compare routine
       ld      a,l     ;check if on new sector
       and     $bps-1  ;boundary
       jr      z,bufcm1;found it, go compare
                       ;more data
       jr      bufcm4  ;keep advancing until
                       ;sector boundary.
                       ;
bufcma  dw      0       ;address of error message
bufcmb  dw      0       ;base buffer address
       form
;
perr                    ;error printing routine,
                       ;prints pass,drive,track,
                       ;sector, and message
                       ;specified by caller on
                       ;console or list device.
                       ;
                       ; call with de=address
                       ;   of message giving
                       ;   type of error
                       ;
       ld      a,(bypass)
       or      a,a     ;is error itemization
                       ;bypass flag set?
       jr      nz,perr2;yes,skip printing
                       ;and go count errors
       ld      (perra),de ;save message addr.
       ld      bc,(pass)
       inc     bc
       ld      hl,perrc;convert current pass
       call    conv
       ld      a,(drv) ;form drive name
       add     'A'
       ld      (perrd),a
       ld      bc,(trk);convert current track
       ld      hl,perre
       call    conv
       ld      bc,(sec);convert current sector
       ld      a,(skew);is skew in effect?
       or      a,a
       jr      z,perr0 ;no
       call    sectran ;yes, translate sector
perr0   ld      hl,perrf
       call    conv
       ld      a,(list);should output be on
       or      a,a     ;console or printer?
       jr      nz,perr3;jump,use printer
                       ;fall thru,use console
       ld      hl,(errors)
       ld      a,h     ;is this first error?
       or      a,l
       jr      nz,perr1;no,jump
       ld      de,dtstq;print title for errors
       ld      c,9
       call    cpm
perr1   ld      de,perrb;print disk address
       ld      c,9
       call    cpm
       ld      de,(perra)
       ld      c,9     ;print error type
       call    cpm
                       ;
perr2                   ;count errors
       ld      hl,(errors)
       inc     hl
       ld      (errors),hl
       ret             ;back to caller
                       ;
perr3                   ;errors to printer
       ld      hl,(errors)
       ld      a,h     ;is this 1st error to
       or      a,l     ;be printed this pass?
       jr      nz,perr4;no,jump
       ld      hl,dtstq;yes,print title
       call    perr9
perr4   ld      hl,perrb;print disk address
       call    perr9
       ld      hl,(perra)
       call    perr9   ;print error type
       jr      perr2   ;go count errors
                       ;
perr9                   ;send a string
                       ;terminated by '$'
                       ;to list device
       ld      a,(hl)  ;fetch next char
       cp      a,'$'   ;is it terminator?
       ret     z       ;yes,exit
       push    hl      ;save string addr.
       ld      e,a     ;send this character
       ld      c,5
       call    cpm
       pop     hl      ;restore string addr
       inc     hl      ;and increment it
       jr      perr9   ;check next char.
                       ;
perra   dw      0       ;addr of message
                       ;describing error type
perrb   db      cr,lf
perrc   db      'nnnn    ' ;pass #
perrd   db      'n     '   ;drive
perre   db      'nnnn   '  ;track
perrf   db      'nnnn   $' ;sector
;
       form
;
;
; disk interface to CP/M BIOS
;
seldsk  ld      a,(drv) ;select disk drive
       ld      c,a
       ld      de,24
                       ;this routine links
                       ;to the desired routine
                       ;through the standard
                       ;CP/M BIOS jump table
jpbios  ld      hl,(wboot+1)
       add     hl,de
       jp      (hl)
;
settrk  ld      bc,(trk);select track
       ld      de,27
       jr      jpbios
;
setsec  ld      bc,(sec);select sector
       ld      de,30
       ld      a,(skew);use sector skew?
       or      a,a
       jr      z,jpbios;no
       call    sectran ;translate sector addr.
       jr      jpbios
;
setdma  ld      bc,(buffer) ;set memory addr.
       ld      de,33
       jr      jpbios
;
setio   call    settrk  ;set up track,sector,
       call    setsec  ;and memory address
       call    setdma  ;for subsequent read
       ret             ;or write
;
read                    ;read one disk sector
       ld      de,36
       jr      jpbios
;
write                   ;write one disk sector
       ld      de,39
       jr      jpbios
;
sectran                 ;translate logical to
                       ;physical sector number
                       ;
                       ;call bc=logical sector
                       ;return bc=physical sector
       push    hl
       ld      hl,sectrb-1
       add     hl,bc
       ld      c,(hl)
       pop     hl
       ret
sectrb  db      1,4,7,10,13     ;table built
       db      16,19,22,25,2
;with skew
       db      5,8,11,14,17,20 ;factor =3
       db      23,26,3,6,9,12
       db      15,18,21,24
       form
;
; messages for test initialization and
; error printing
;
dtsta   db      cr,lf,lf
       db      'Z-80 Floppy Disk '
       db      'Test version '
       db      $ver+'0','.'
       db      $rev+'0',cr,lf
       db      '(c) 1980 Laboratory '
       db      'Microsystems',cr,lf,'$'
dtstb   db      cr,lf,'Itemize '
       db      'errors?    $'
dtstc   db      cr,lf,'Use '
       db      'console or printer'
       db      '? (C/P) $'
dtstd   db      cr,lf,'Lock on read '
       db      'or write? (N/R/W) $'
dtste   db      cr,lf,'Restore '
       db      'original data? $'
dtstf   db      cr,lf,'Lock on '
       db      'data pattern? $'
dtstg   db      cr,lf,'Enter data '
       db      'pattern, hex 00-FF$'
dtsth   db      cr,lf,'Drive '
       db      'to be tested '
       db      '(',$drvf+'A','-'
       db      $drvl+'A',') $'
dtsti   db      cr,lf,'Confirm: test drive '
dtsti1  db      'X ? $'
dtstj   db      cr,lf,'Test all '
       db      'tracks?    $'
dtstk   db      cr,lf,'First '
       db      'track to test      $'
dtstl   db      cr,lf,'Last '
       db      'track to test      $'
dtstm   db      cr,lf,'Test all '
       db      'sectors?    $'
dtstn   db      cr,lf,'First '
       db      'sector to test    $'
dtsto   db      cr,lf,'Last '
       db      'sector to test     $'
dtstp   db      cr,lf,'How many '
       db      'test passes?    $'
dtstq   db      cr,lf,lf,'Pass  '
       db      'Drive  Track  '
       db      'Sector  Error-type'
       db      cr,lf,'$'
dtstr   db      cr,lf,lf,'Pass '
dtstr1  db      'nnnn complete, '
dtstr2  db      'nnnn errors.'
       db      cr,lf,'$'
dtsts   db      cr,lf,'Not enough '
       db      'memory to execute.'
       db      cr,lf,'$'
dtstt   db      cr,lf,lf,'Beginning '
       db      'disk test - push '
       db      'any key to abort '
       db      'program.',cr,lf,'$'
dtstu   db      'Warning: user '
       db      'data will not be '
       db      'restored.',cr,lf,'$'
dtstv   db      'User data will be '
       db      'restored.',cr,lf,'$'
dtstw   db      cr,lf,'Continue or '
       db      'exit test? (C/E)$'
dtstx   db      cr,lf,lf
       db      'Goodbye.',cr,lf,'$'
dtsty   db      cr,lf,'Use sector '
       db      'skew?  $'
dtstz   db      cr,lf,'Need CP/M 2.x '
       db      'to execute.',cr,lf,'$'
;
;
merr1   db      'read error - original data$'
merr2   db      'write error - test pattern$'
merr3   db      'read error - test pattern$'
merr4   db      'compare error - test pattern$'
merr5   db      'original data cannot '
       db      'be restored$'
merr6   db      'write error - restore phase$'
merr7   db      'read error - restore phase$'
;
       form
;
;
; utility and console input routines
;
getyn                   ;get y or n response
                       ;from operator.
                       ;
                       ;call de=address of cue
                       ;return acc=y or n
       push    de      ;save cue address
       ld      c,9     ;print cue message
       call    cpm
       ld      de,getyna
       ld      c,9     ;print possible answers
       call    cpm
       call    getchar ;get a character
                       ;from console
       or      a,20h   ;fold to lower case
       pop     de      ;restore cue address
                       ;in case needed again
       cp      'y'     ;make sure response
                       ;is ok
       ret     z       ;exit if y
       cp      'n'
       ret     z       ;exit if n
       push    de
       call    query   ;print question mark if
       pop     de      ;not y or n, try again
       jr      getyn   ;
getyna  db      '(Y/N) ',tab,'> $'
;
;
getl                    ;get any letter response
                       ;from operator.
                       ;
                       ;call de=address of cue
                       ;return acc=ASCII char.
       ld      c,9     ;print cue message
       call    cpm
       ld      de,getla;tab and print
       ld      c,9     ;cue mark
       call    cpm
       call    getchar ;read console
       or      a,20h   ;fold to lower case
       ret
getla   db      tab,'> $'
;
;
getn                    ;get a decimal number
                       ;from the console.
                       ;
                       ;call de=address of cue
                       ;return hl=number
       push    de      ;save cue message address
                       ;in case needed later
       ld      c,9
       call    cpm     ;print cue message
       ld      de,getna;print tab and cue mark
       ld      c,9
       call    cpm
       ld      hl,0    ;initialize forming
                       ;answer
       ld      a,(digits)
       ld      b,a     ;total characters allowed
                       ;to be input
getn1   push    hl      ;save answer
       push    bc      ;save char. count
       call    getchar ;read console
       pop     bc      ;restore char. count
       pop     hl      ;restore forming answer
       cp      cr      ;is this return?
       jr      z,getn9 ;yes,exit with answer
       cp      '0'     ;is this legal char.?
       jr      c,getn3 ;no, jump
       cp      '9'+1   ;is this legal char.?
       jr      nc,getn3;no,jump
       and     0fh     ;isolate bottom 4 bits
                       ;previous data * 10
       push    hl
       pop     de
       add     hl,hl   ;(*2)
       add     hl,hl   ;(*4)
       add     hl,de   ;(*5)
       add     hl,hl   ;(*10)
       ld      e,a     ;now add in this digit
       ld      d,0
       add     hl,de
       djnz    getn1   ;count characters accepted
       jr      getn9   ;enough accepted,exit
getn3                   ;illegal character detected.
       call    query   ;print question mark and
       pop     de      ;restart input
       jr      getn
getn9                   ;input complete,clean
                       ;stack and exit with
                       ;answer in (hl)
       pop     de
       ret
getna   db      tab,'> $'
getnb   db      '?$'
;
;
geth                    ;get $dig hex digits
                       ;from keyboard
                       ;
                       ;call de=addr of cue
                       ;return acc=lower 8 bits
                       ;     of entered number,
                       ;     hl=entire 16 bit no.
                       ;
       push    de      ;save cue address
                       ;in case needed again
       ld      c,9
       call    cpm     ;print cue message
       ld      de,getha;print tab and cue mark
       ld      c,9
       call    cpm
       ld      hl,0    ;initialize forming
                       ;answer
       ld      a,(digits)
       ld      b,a     ;max digits to accept
geth1   push    bc      ;save registers
       push    hl
       call    getchar ;read console
       pop     hl
       pop     bc      ;restore registers
       cp      cr      ;if carriage return exit
       jr      z,geth25
       cp      '0'     ;make sure its legal
       jr      c,geth3 ;no,jump
       cp      '9'+1   ;if alpha fold to
       jr      c,geth15;lower case
       or      20h
geth15  cp      'f'+1   ;make sure its legal
       jr      nc,geth3;no,jump
       cp      'a'     ;check if alpha
       jr      c,geth2 ;jump if 0-9
       add     9       ;add correction
geth2   and     0fh
       add     hl,hl   ;previous data *16
       add     hl,hl   ;(left shift 4 bits)
       add     hl,hl
       add     hl,hl
       add     a,l     ;add this char. to
       ld      l,a     ;forming result
       djnz    geth1   ;keep reading console
geth25  pop     de      ;clean up stack
       ld      a,l     ;put lower 8 bits
                       ;of answer in acc.
                       ;(in case exit by
                       ; carriage return)
       ret
geth3   call    query   ;print question mark
       pop     de      ;then restart input
       jr      geth
getha   db      tab,'> $'
;
;
query   push    af      ;save flags
       ld      c,9     ;print question mark
       ld      de,querya
       call    cpm
       pop     af      ;restore flags
       ret
querya  db      ' ?$'
;
;
getchar                 ;get 1 character from
                       ;console via raw input
                       ;mode.  do not echo a
                       ;carriage return.
       ld      e,0ffh
       ld      c,6
       call    cpm     ;read console
       or      a,a     ;anything there?
       jr      z,getchar ;no,try again
       cp      cr      ;is it a carriage return?
       ret     z       ;yes
       push    af      ;no,echo it
       ld      e,a
       ld      c,6
       call    cpm
       pop     af      ;restore acc. and exit
       ret
;
;
conv                    ;convert binary to
                       ;decimal ascii
                       ;
                       ;call bc=binary data, in
                       ;        range 0000-9999.
                       ;     hl=first byte addr
                       ;        to store output
                       ;
       ld      de,1000
       call    div
       call    conv9   ;thousands digit
       ld      de,100
       call    div
       call    conv9   ;hundreds digit
       ld      de,10
       call    div
       call    conv9   ;tens digit
       call    conv9   ;units
       ret             ;back to caller
conv9   ld      a,c     ;turn quotient into
       add     '0'     ;ASCII character
       ld      (hl),a  ;and store it
       inc     hl      ;bump output pointer
       push    de      ;bc <- remainder
       pop     bc
       ret
;
;
div                     ;single precision divide
                       ;call bc=numerator
                       ;     de=divisor
                       ;return bc=quotient
                       ;     de=remainder
       push    hl
       ld      hl,0
       or      a,a
       sbc     hl,de
       ex      de,hl
       ld      hl,0
       ld      a,17
div0    push    hl
       add     hl,de
       jr      nc,div1
       ex      (sp),hl
div1    pop     hl
       push    af
       rl      c
       rl      b
       rl      l
       rl      h
       pop     af
       dec     a
       jr      nz,div0
       or      a,a
       rr      h
       rr      l
       ex      de,hl
       pop     hl
       ret
;
;
buff1   equ     1000h   ;disk buffers
buff2   equ     $bpt*2+buff1
buff3   equ     $bpt*2+buff2
buffend equ     $bpt*2+buff3
;
;
       end     100h