ZX Spectrum - Interface 1 ROM Disassembly


; Disassembly of the file "C:\lab\if1-2.rom"
;
; CPU Type: Z80
;
; Created with d Z80 1.50
;
; on Sunday, 28 of April 2002 at 12:35 PM
;

; ------------------------
; last updated 14-JAN-2004
; ------------------------

#define DEFB .BYTE
#define DEFW .WORD
#define DEFM .TEXT
#define EQU .EQU
#define ORG .ORG

ORG $0000

; ---------------------------------------
; FLAGS3 System Variable - IY+$7C ($5CB6)
; ---------------------------------------
; Bit 0 - set when executing an extended command.
; Bit 1 - set during CRT-VARS and CLEAR #, CLOSE etc.
; Bit 2 - settable by User to force the ERR_SP routine to handle errors.
; Bit 3 - set when networking.
; Bit 4 - set during LOAD and MOVE
; Bit 5 - set during SAVE
; Bit 6 - set during MERGE
; Bit 7 - set during VERIFY
;
; Note. before initialization of FLAGS_3, this is considered to be the first
; byte of channels and so PEEK 23734 gives 244 decimal (%11110100) the high
; order byte of the Main ROM address PRINT-OUT - $09F4.
;
; -------------------------------------------

; --------------------------------
; THE 'RETURN TO MAIN ROM' ROUTINE
; --------------------------------
; The system is initialized by the Main ROM so this address is accessed
; solely by a RST 00H instruction. It is used from five locations to return
; to the Main ROM.

;; MAIN-ROM
L0000: POP HL ; discard the return address in this ROM.
LD (IY+$7C),$00 ; reset all the bits of FLAGS_3.
JP L0700 ; jump forward to UNPAGE address.

; -------------------
; THE 'START' ROUTINE
; -------------------
; An instruction fetch on address $0008 pages in this ROM.
; The three-byte instruction at this location must exist on both sides of
; the looking-glass. The value fetched is immediately discarded.
; It follows that this restart should never be invoked from this ROM.

;; ST-SHADOW
L0008: LD HL,($5C5D) ; fetch character address from CH_ADD.
POP HL ; pop return address to HL register.
PUSH HL ; and save again on machine stack.

JP L009A ; jump forward to continue at START-2.

; -----------------------------
; THE 'CALL A MAIN ROM' ROUTINE
; -----------------------------
; Call an address in the main ROM. The address follows the restart so this
; is as convenient and as brief as a CALL instruction.
; The SBRT routine within the system variables area reads
;
; L5CB9 LD HL,value
; L5C5C CALL addr
; L5C5F LD (L5CB9+1),HL
; L5CC2 RET
;
; By immediately placing the current value of HL in the subroutine, then
; all registers before the call are as they were before the RST
; instruction. The value of HL after the call is stored immediately in
; this now redundant location so that, after this ROM is paged back in,
; the registers, after the RST instruction has executed, are as they were
; immediately after the CALL.
; see START-2.

;; CALBAS
L0010: LD ($5CBA),HL ; insert the current value of HL in the
; Z80 code to be picked up later.

POP HL ; drop the return address - the location
; of address to be called.
PUSH DE ; preserve the DE register contents.

JR L0081 ; forward to continue at CALBAS-2.

DEFB $FF ; unused.

; ---------------------------------------------
; THE 'TEST IF SYNTAX IS BEING CHECKED' ROUTINE
; ---------------------------------------------
; On the ZX80, testing the syntax flag was done with the 4-byte
; instruction that tests the System Variable FLAGS. On the ZX81 and
; ZX Spectrum, a call to SYNTAX-Z reduced the invocation to a three-byte
; CALL. Here it is reduced to a one-byte restart.

;; CHKSYNTAX
L0018: BIT 7,(IY+$01) ; test most significant bit of FLAGS
RET ; return the result.
; (Z = Syntax, NZ = Run-time)

DEFB $FF ; unused.
DEFB $FF ; unused.
DEFB $FF ; unused.

; --------------------------
; THE 'SHADOW-ERROR' ROUTINE
; --------------------------
; This is similar to the Main ROM error handler and the following byte
; indicates the type of error and in runtime the message that should be
; printed. If checking syntax then the error pointer is set before a
; return is made to the Main ROM.

;; SH-ERR
L0020: RST 18H ; checking syntax ?
JR Z,L0068 ; forward, if so, to ST-ERROR

JR L003A ; forward, in run-time, to TEST-SP,
; and then REP-MSG

DEFB $FF ; unused.
DEFB $FF ; unused.
DEFB $FF ; unused.

; ------------------------------------
; THE 'MAIN ROM ERROR RESTART' ROUTINE
; ------------------------------------
; This restart invokes the error handler of the Main 16K ROM. The required
; error number is usually first placed in the System Variable ERR_NR. In
; some cases the error code is already present and this restart is used when
; the error situations handled by this ROM have been eliminated.
; Since the exit from this point is by manipulating the stack, the return
; address is of no importance as that route is never taken. There are also
; three conditional jumps back to this point.

;; ROMERR
L0028: RES 3,(IY+$02) ; update TV_FLAG - signal no change in mode.
JR L0040 ; forward to RMERR-2.

DEFB $FF ; unused.
DEFB $FF ; unused.

; -------------------------------------------------
; THE 'CREATE NEW SYSTEM VARIABLES RESTART' ROUTINE
; -------------------------------------------------
; This restart is used the first time that that the ROM is paged in to
; create the System Variables. This will be either by an instruction
; fetch on $0008 or $1708.

;; NEWVARS
L0030: JP L01F7 ; jump to CRT-VARS

DEFB $FF ; unused.
DEFB $FF ; unused.
DEFB $FF ; unused.
DEFB $FF ; unused.
DEFB $FF ; unused.

; --------------------------------
; THE 'MASKABLE INTERRUPT' ROUTINE
; --------------------------------
; There is no service routine but should the routine be called either
; directly or by straying into a RST $38 instruction, then interrupts are
; enabled.

;; INT-SERV
L0038: EI ; Enable Interrupts
RET ; return.


; ------------------------
; THE 'TEST SYSTEM' BRANCH
; ------------------------
; This branch allows the user to trap errors before this ROM is used to print
; the error report.

;; TEST-SP
L003A: CALL L0077 ; routine CHECK-SP
; usually returns.
JP L0260 ; jump to REP-MSG

; ----------------------------
; THE 'MAIN ROM ERROR' ROUTINE
; ----------------------------
; a continuation of RST 28H.
; This ROM has inserted a Main ROM error code into ERR_NR and the routine in
; the Main ROM is now invoked.
; First a check is made to see if the user wishes to trap errors using a
; custom routine in ERR_SP. This will be used in the syntax path anyway.
;

;; RMERR-2
L0040: RST 18H ; checking syntax ?
JR Z,L0068 ; forward, if so, to ST-ERROR.

CALL L0077 ; routine CHECK-SP allows the user to trap
; run-time errors at this point but normally
; returns here.

CALL L17B7 ; routine RCL-T-CH reclaims any temporary
; channels and stops all Microdrive motors.

BIT 1,(IY+$7C) ; test FLAGS_3.
JR Z,L0068 ; forward, if executing CLOSE, to ST-ERROR.

BIT 4,(IY+$7C) ; test FLAGS_3 - loading filename 'run' ?
JR Z,L0068 ; forward, if not, to ST-ERROR.

; As a security measure, the file 'run' can not be hacked.

LD A,(IY+$00) ; fetch error number from the System Variable
; ERR_NR.
CP $14 ; is it "CODE error" ?
JR NZ,L0068 ; forward, if not, to ST-ERROR.

; The user has pressed BREAK while trying to load the program 'run'.

LD HL,$0000 ; cause a system reset.
PUSH HL ; place address zero on machine stack.
RST 00H ; switch to MAIN-ROM.

;

DEFB $FF ; unused
DEFB $FF ; unused
DEFB $FF ; unused
DEFB $FF ; unused
DEFB $FF ; unused

; ------------------------------------
; THE 'NON-MASKABLE INTERRUPT' ROUTINE
; ------------------------------------
; There is no NMI functionality.

;; NMINT-SRV
L0066: RETN ; return to previous interrupt state.


; --------------------------
; THE 'SYNTAX ERROR' ROUTINE
; --------------------------
; An error has occurred during syntax checking so the position must be
; highlighted when a return is made to the Editor in the Main ROM.

;; ST-ERROR
L0068: LD HL,($5C5D) ; fetch character address from CH_ADD.
LD ($5C5F),HL ; set X_PTR to same to position error cursor.

LD SP,($5C3D) ; set the Stack Pointer from ERR_SP.

LD HL,$16C5 ; prepare address of main SET-STK.
PUSH HL ; push on the machine stack.

RST 00H ; switch to MAIN-ROM where SET-STK will clean up
; the work areas before returning to the Error
; Routine obtained from ERR_SP.

; ---------------------------------------
; THE 'CHECK ERROR STACK POINTER' ROUTINE
; ---------------------------------------
; This allows the user's software to trap any errors at this point by setting
; the otherwise unused bit 2 of FLAGS_3 after inserting a custom error
; handler in the System Variable ERR_SP.
; Both Shadow ROM situations and Main ROM situations can be trapped and the
; routine is called from BOTH RST 20H and RST 28H.

;; CHECK-SP
L0077: BIT 2,(IY+$7C) ; test FLAGS_3 has the user set up a custom
; error handler in Main RAM ?
RET Z ; return if not.

; Otherwise the user, or the third party software, has set up a custom routine
; in the system variable ERR_SP and set bit 2 of FLAGS_3 so that it is invoked
; at this point.

LD SP,($5C3D) ; set stack pointer from ERR_SP.
RST 00H ; switch to MAIN-ROM.

; ----------------------
; THE 'CALBAS-2' ROUTINE
; ----------------------
; A continuation of the code at $0010.
; Continue by picking up the address to be called, located after the RST
; instruction and placing after the CALL instruction in the SBRT sequence.

;; CALBAS-2
L0081: LD E,(HL) ; fetch low byte of called address
INC HL ; advance pointer.
LD D,(HL) ; fetch high byte.

LD ($5CBD),DE ; place in the Z80 code SBRT
INC HL ; increment pointer.

EX (SP),HL ; transfer continuation address to machine
; stack - and the stack value (was DE) to HL.

EX DE,HL ; original DE value now restored.

LD HL,$0000 ; signal CALBAS routine in use.
PUSH HL ; place on stack.

LD HL,$0008 ; address of main ERROR restart
PUSH HL ; place on stack

LD HL,$5CB9 ; address of calling SBRT subroutine.
PUSH HL ; place on stack.

JP L0700 ; jump to UNPAGE

; ---------------------
; THE 'CONTROL' ROUTINE
; ---------------------
; A continuation of code at L0008. The return address has been dropped off
; the machine stack into HL.
;
; First see if this ROM was paged in as a result of the $0008 address
; stacked during the CALBAS routine. (see above)

;; START-2
L009A: PUSH AF ; preserve accumulator and status flags.

LD A,H ; test HL for zero - the CALBAS
OR L ; indicator value.
JR NZ,L00A5 ; forward, if not, to START-3.

POP AF ; restore accumulator and flags.

POP HL ; discard address stacked by RST 08.
LD HL,($5CBA) ; pick up post-CALL HL value from SBRT.
RET ; return.

;------------------------------------------------------------------------------
; Now consider that the address $0008 may have been an input or output
; routine that precedes the letter of one of the new channels. These
; paging addresses ensure that this ROM is paged in so that the real
; input/output addresses can be read from the locations after the
; channel's letter. In this case, the return address is towards the end
; of the CALL-SUB routine in the Main ROM, i.e.
; L15FB CALL $162C ; routine CALL-JUMP (a JP (HL) instr.)
; L15FE POP HL ; return address
;------------------------------------------------------------------------------

;; START-3
L00A5: PUSH DE ; preserve DE.
LD DE,$15FE ; test against possible return address 0x15FE
SBC HL,DE ; subtract (carry is clear)
POP DE ; restore DE.
JR NZ,L00BC ; forward with no match to START-4.

; This ROM has been paged by an attempt to use a stream.

POP AF ; restore accumulator.

LD HL,L0700 ; stack the address UNPAGE to switch to
PUSH HL ; the Main ROM afterwards.

LD HL,$0004 ; the shadow routine is 4 bytes forward
ADD HL,DE ; adjust input/output address pointer.
LD E,(HL) ; pick up low-order byte of I/O routine.
INC HL ; bump pointer.
LD D,(HL) ; pick up high-order byte of routine.
EX DE,HL ; transfer I/O address to HL.

JP (HL) ; jump to routine and then to UNPAGE

; ---

; By elimination, the address $0008 has been reached as a result of a
; RST 08 instruction in the Main ROM. This may be the very first time
; that this ROM has been paged in after startup or NEW.

;; START-4
L00BC: RST 30H ; create new system variables if first time.

LD A,$01 ; %00000001
OUT ($F7),A ;

LD A,$EE ; %11101110
OUT ($EF),A ;

POP AF ; temporarily drop the accumulator.
POP HL ; fetch address of error code/hook code to HL.
PUSH AF ; save accumulator again.

; Note. the address of the code could be anywhere in the 64K address space
; but it is not in this ROM. Luckily in the Main ROM at $007B is the
; sequence ld a,(hl) ; ret which will fetch the unknown error code from
; the known address.

RST 10H ; CALBAS
DEFW $007B ; main TEMP-PTR3

LD ($5C3A),A ; place the error code in sysvar ERR_NR

; The error code at this stage is one less than actual code.

CP $FF ; is it 'OK'
JR NZ,L00E9 ; forward, if not, to TEST-CODE

BIT 1,(IY+$7C) ; test FLAGS_3 - first time ?
JR Z,L00E7 ; forward, if not, to NREPORT-2
; 'Program finished'

BIT 7,(IY+$0C) ; test PPC_hi - a direct command ?
JR Z,L00E7 ; forward, if not, to NREPORT-2

LD HL,($5C59) ; use E_LINE to address the first character of
; the edit buffer.
LD A,(HL) ; searching for RUN without whitespace.

CP $F7 ; is character the token 'RUN' ?
JP Z,L0A99 ; jump forward, if so, to LOAD-RUN

;; NREPORT-2
L00E7: RST 20H ; Shadow Error Restart
DEFB $FF ; 'Program finished'

; ---

; Continue to consider the error code. This may have occurred after the
; Error RESTART in the Main ROM - range $00 (NEXT without FOR) to
; $1A (Tape Loading Error) or a RESTART in RAM which could also include
; the Hook Codes.

;; TEST-CODE
L00E9: SUB $1B ; subtract lowest Hook Code (PAUSE)
JP NC,L1E71 ; jump, if same or higher, to HOOK-CODE

CP $F0 ; was it $0B 'Nonsense in basic'
JR Z,L00FB ; forward to COPYCHADD

CP $F3 ; was it $0D 'Invalid file name'
JR Z,L00FB ; forward to COPYCHADD

CP $FC ; was it $17 'Invalid stream'
JP NZ,L0028 ; jump, if not, to ROMERR

; If one of the above three reports, then this is possibly an extended
; command and further investigation is required. A number of situations
; may apply. The error could have occurred -
;
; 1) In INPUT - just pass control back to Main ROM. This is just a normal
; Nonsense in BASIC and will not be due to anything new.
; 2) While already investigating an error. Too much - just use Main ROM.
; 3) While entering a new or modified line and syntax failed.
; 4) While running the program and an error was encountered.
;
; The character address CH_ADD is not much use as that is the place
; after the command where the standard ROM encountered an error.
; It will be required by the Main ROM if control is passed back so, in
; order that the Main ROM parsing routines can be used, make a copy of the
; error character position. We will have to work forward from the
; beginning of the line if checking syntax or from the start of the
; program in run-time so that the errant command can be found. It may also
; be necessary to remove hidden characters from the BASIC line.

;; COPYCHADD
L00FB: LD HL,($5C5D) ; fetch character address from CH_ADD and
LD ($5CCB),HL ; store in shadow system variable CHADD_

POP AF ; restore accumulator.

BIT 5,(IY+$37) ; test FLAGX - in INPUT mode ?

JP NZ,L0028 ; jump back, if so, to ROMERR

; Continue if in Editing or Run-time Mode.

BIT 0,(IY+$7C) ; test FLAGS_3 - already extended command ?
JP NZ,L0028 ; jump, if so, to ROMERR

; else signal - handling an extended command - so that such a double error
; can be trapped.

SET 0,(IY+$7C) ; update FLAGS_3 - signal executing an
; extended command.

RST 18H ; checking syntax ?

JR NZ,L011B ; skip forward, if not, to RUNTIME

LD (IY+$0C),$FF ; set bit 7 of PPC_hi to indicate a line
; entry situation.

; In both cases, load B with the statement number where the error was
; encountered. Previous validated statements are not to be disturbed.

;; RUNTIME
L011B: LD B,(IY+$0D) ; load B with statement number from SUBPPC
LD C,$00 ; and set C to zero for a quotes flag.

BIT 7,(IY+$0C) ; test PPC_hi - line entry ?
JR Z,L0130 ; forward, if not, to PROG-LINE

; An edit line may have a line number at start and whitespace. We need to
; set CH_ADD at the first command.

PUSH BC ; save BC

RST 10H ; CALBAS
DEFW $19FB ; main E-LINE-NO fetches any line number to
; BC, setting CH_ADD at the command token.

POP BC ; restore BC - discarding line number.

RST 10H ; CALBAS
DEFW $0018 ; main GET-CHAR gets first command of the
; first statement of the errant line.

JR L016F ; forward to statement loop - S-STAT to find
; the errant statement.

; ---

;; PROG-LINE
L0130: LD HL,($5C53) ; set pointer to start of program from PROG.

;; SC-L-LOOP
L0133: LD A,($5C46) ; fetch high byte of errant line from PPC_hi
CP (HL) ; compare with tested high byte.
JR NC,L013B ; forward, if errant line higher or same,
; to TEST-LOW

; else, unusually, the current line is not there so let Main ROM handle.

;; NREPORT-1
L0139: RST 20H ; Shadow Error Restart
DEFB $00 ; Nonsense in BASIC

; ---

;; TEST-LOW
L013B: INC HL ; increment program pointer to address low byte.
JR NZ,L0144 ; forward, if high bytes not same, to LINE-LEN

LD A,($5C45) ; fetch low byte of current line from PPC_lo
CP (HL) ; compare to addressed byte.
JR C,L0139 ; back, if not in program area, to NREPORT-1


;; LINE-LEN
L0144: INC HL ; increment program
LD E,(HL) ; pointer and
INC HL ; pick up the
LD D,(HL) ; length of the BASIC line
INC HL ; resting at the first character.

JR Z,L016F ; forward, if line numbers matched, to S-STAT
; the mid-entry point of the statement loop.

ADD HL,DE ; else add length to current address.
JR L0133 ; loop back to SC-L-LOOP

; --------------------
; THE 'STATEMENT LOOP'
; --------------------
; Entered at mid-point S-STAT with statement counter in B and a quotes
; counter, C, set at an even zero.

;; SKIP-NUM
L014E: LD DE,$0006 ; a hidden floating point number has six bytes.
ADD HL,DE ; add to skip to next character.

; -> The Looping Point.

;; EACH-ST
L0152: LD A,(HL) ; fetch addressed BASIC character.
CP $0E ; is it the hidden number indicator ?
JR Z,L014E ; back to SKIP-NUM to ignore.

INC HL ; else increase pointer.

CP $22 ; is it quotes character '"' ?
JR NZ,L015D ; skip forward, if not, to CHKEND

DEC C ; decrement quotes counter.

;; CHKEND
L015D: CP $3A ; is character ':' ?
JR Z,L0165 ; skip forward to CHKEVEN

CP $CB ; is character 'THEN' ?
JR NZ,L0169 ; skip forward to CHKEND-L


;; CHKEVEN
L0165: BIT 0,C ; are quotes balanced ?
JR Z,L016F ; forward, if so, to S-STAT
; for next statement.

; A carriage return must not appear within quotes.

;; CHKEND-L
L0169: CP $0D ; carriage return ?
JR NZ,L0152 ; back, if not, to EACH-ST

JR L0139 ; back to NREPORT-1
; 'Nonsense in BASIC'


; The Statement Loop Entry Point -->

;; S-STAT
L016F: DJNZ L0152 ; decrement statement counter and loop back
; to EACH-ST.

; The errant statement has been located and CH_ADD is set to start.

DEC HL ; point to start or ':'

LD ($5C5D),HL ; set the Main ROM system variable CH_ADD

RST 18H ; checking syntax ?

JR NZ,L01AA ; forward, if not, to CL-WORK

BIT 7,(IY+$0C) ; test PPC_hi - is it an Edit Line ?
JP Z,L01F0 ; jump forward, if not, to ERR-6.

DEC HL ; prepare to enter loop below.

LD C,$00 ; ??

; It is well to reflect on what has been achieved up to this point. At
; each statement, the first attempt at validation is made by the Main ROM.
; Then if that should encounter something not to its liking, this ROM has
; a bash. There could be ten or more statements before this one and each
; will have been validated by the Main ROM or by this routine. As part of
; that validation process, when a number is parsed, then the integer or
; floating point form of the number is inserted after the digits, rendered
; invisible by a CHR$(14).
;
; Once a statement has passed validation by either ROM, then it is not
; undone. If, say, the Main ROM has failed on the third statement of
;
; 10 PRINT "Hi :" : LET vat = 15 : OPEN# 7, "T" : LET tax = cost * (vat/100)
;
; then it will have already inserted six bytes after the '7' before raising
; the error 'Invalid stream'. This ROM has located the separator before
; the command but needs to remove the hidden numbers before parsing the
; statement as the latter process will put them back in and we can't
; double up. The easiest way to do this is to search for hidden numbers
; right to the end of the line. There won't be any after this statement
; but stopping at a CHR$(13) is easier than considering end of statement
; markers in quotes. It seems that this neat solution was not arrived at
; immediately and the instruction, above, sets C to the quotes flag again
; and it is needlessly preserved on the stack.
;
; The end-user is oblivious to this elegant toing and froing between ROMS
; and the unseen error code generation and cancellation. All that is
; apparent is that when the RETURN key is pressed, the line simply enters
; the program.

;; RCLM-NUM
L0182: INC HL ; increment character pointer
LD A,(HL) ; fetch the character.

CP $0E ; is it the number marker ?
JR NZ,L01A5 ; forward, if not, to NEXTNUM

PUSH BC ; preserve BC (zero)

LD BC,$0006 ; six bytes to reclaim.

RST 10H ; CALBAS
DEFW $19E8 ; main RECLAIM-2

PUSH HL ; preserve character pointer.

LD DE,($5CCB) ; fetch error pointer from CHADD_
AND A ; prepare for true subtraction.
SBC HL,DE ; test if character position less than error.
JR NC,L01A3 ; forward, if not, to NXT-1

EX DE,HL ; transfer CHADD_ value to HL.
LD BC,$0006 ;
AND A ;
SBC HL,BC ; reduce by six.
LD ($5CCB),HL ; store back in system variable CHADD_

;; NXT-1
L01A3: POP HL ; restore character pointer.
POP BC ; and restore BC (zero)

;; NEXTNUM
L01A5: LD A,(HL) ; fetch character.
CP $0D ; carriage return ?
JR NZ,L0182 ; loop back, if not, to RCLM-NUM

; The run-time path rejoins here

;; CL-WORK
L01AA: RST 10H ; CALBAS
DEFW $16BF ; main SET-WORK

CALL L0255 ; routine RES-VARS sets new system variables
; from that following CHADD_ to that preceding
; COPIES to the value $FF.

RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR advances CH_ADD and fetches
; the command character.

SUB $CE ; reduce tokens - why?

CP $01 ; 'CAT' ?
JP Z,L0486 ; jump to CAT-SYN

CP $02 ; 'FORMAT' ?
JP Z,L04B4 ; jump to FRMT-SYN

CP $03 ; 'MOVE' ?
JP Z,L053D ; jump to MOVE-SYN

CP $04 ; 'ERASE' ?
JP Z,L0531 ; jump to ERASE-SYN

CP $05 ; 'OPEN #' ?
JP Z,L04ED ; jump to OPEN-SYN

CP $2A ; 'SAVE' ?
JP Z,L082F ; jump to SAVE-SYN

CP $21 ; 'LOAD' ?
JP Z,L0898 ; jump to LOAD-SYN

CP $08 ; 'VERIFY' ?
JP Z,L08A2 ; jump to VERIF-SYN

CP $07 ; 'MERGE' ?
JP Z,L08AC ; jump to MRG-SYN

CP $2D ; 'CLS' ?
JP Z,L0559 ; jump to CLS#-SYN

CP $2F ; 'CLEAR' ?
JP Z,L057F ; jump to CLR#-SYN

; If none of the new extended commands then load HL from the VECTOR
; system variable which normally points to the error routine below.
; However the user, or a third party software publisher, may have
; altered the vector to point to their own extended BASIC routines.

;; ERR-V
L01EC: LD HL,($5CB7) ; fetch address from system variable VECTOR
JP (HL) ; jump to address.

; ---

;; ERR-6
L01F0: LD HL,($5CCB) ; fetch original character address from
; CHADD_
LD ($5C5D),HL ; and place in standard CH_ADD
RST 28H ; Error Main ROM.

; -----------------------------------------
; THE 'CREATE NEW SYSTEM VARIABLES' ROUTINE
; -----------------------------------------
; A continuation of the restart code at $0030. A check is made to see if
; the 58 variables already exist and the stack is set up to create the
; room using the main ROM routine. If there isn't 58 free bytes available
; then an 'Out of memory' report is generated by the Main ROM.

;; CRT-VARS
L01F7: LD HL,($5C4F) ; system variable CHANS normally $5CB6.
LD DE,$A349 ; add test value $A349.
; ----
ADD HL,DE ; add - if uninitialized will give $FFFF.
JR C,L023D ; forward, if higher, to VAR-EXIST

LD HL,L0224 ; prepare address of DEFAULT routine
PUSH HL ; push on machine stack

LD HL,($5C63) ; use system variable STKBOT
LD ($5C65),HL ; to set system variable STKEND

LD HL,$5C92 ; use system variable MEMBOT
LD ($5C68),HL ; to set system variable MEM

LD HL,$5CB5 ; the last standard system variable.
; P-RAMT_hi - the location before new area.
LD BC,L003A ; 58 bytes to allocate.

; Now call MAKE-ROOM in the Main ROM by placing a sequence of addresses
; on the machine stack as it is not possible to use the CALBAS routine yet.

LD DE,$0000 ; indicator - signals Main ROM has been used.
PUSH DE ; stack word.

LD E,$08 ; form address $0008 in Main ROM.
PUSH DE ; stack word.

LD DE,$1655 ; the Main ROM address MAKE-ROOM.
PUSH DE ; stack word.

; The machine stack now has the hierarchy DEFAULT; $0000; ERROR-1;
; MAKE-ROOM which will be handled in reverse order.

JP L0700 ; jump to UNPAGE.

; After creating room and paging this ROM back in, 'return' to the next
; address which was the first in the sequence pushed on machine stack
; earlier.

;; DEFAULT
L0224: LD HL,L0242 ; default system variable values.
LD BC,$0013 ; nineteen bytes to move.
LD DE,$5CB6 ; old CHANS area, new sysvar FLAGS_3.
LDIR ; copy the bytes.

; Note. So far the value in the accumulator, which may be the number of a
; stream to close, has not been altered. This next instruction is worded
; wrongly and
;
; OPEN #7,"s" : CLOSE #7
;
; may not work.
; The fix would be to use 'ld hl $5cef ; ld (hl), $01' (5 bytes)
; or even 'dec h ; ld ($5cee),hl' (4 bytes)
; The next pair of instructions would have been better if executed using
; the HL register pair also.

LD A,$01 ; set accumulator to 1.
LD ($5CEF),A ; set system variable COPIES.

LD (IY+$77),$50 ; set NMI_ADD_hi to eighty.
LD (IY+$76),$00 ; set NMI_ADD_lo to zero.

RET ; return.

; ---

; The extended System Variables already exist.

;; VAR-EXIST
L023D: RES 1,(IY+$7C) ; reset indicator in FLAGS_3.
RET ; return.


; -------------------------------------------
; THE 'SYSTEM VARIABLES DEFAULT VALUES' TABLE
; -------------------------------------------
; These are the initial values of the first section of the extended System
; Variables that are copied, once only, to a newly opened area following
; the standard 48K Spectrum System Variables. The memory area that was at
; this location (CHANS) is moved upwards to make room.
; The first new location (which was the first byte of CHANS) is now
; FLAGS_3, accessible by the IY register, and normally zero when the Main
; ROM becomes active again. Bit 1 is set when a CLEAR# is active and also
; by the copy itself.

;; SV-DEFS
L0242: DEFB $02 ; FLAGS3 (with bit 1 already set).
DEFW $01F0 ; VECTOR

LD HL,$0000 ; SBRT located at $5CB9
CALL $0000 ;
LD ($5CBA),HL ;
RET ;

DEFW $000C ; BAUD
DEFB $01 ; NTSTAT
DEFB $00 ; IOBORD - black.
DEFW $0000 ; SER_FL


; ----------------------------------------
; THE 'RESET NEW SYSTEM VARIABLES' ROUTINE
; ----------------------------------------
; The central area is filled with $FF bytes.
; This occurs whenever a new extended command is invoked.

;; RES-VARS
L0255: LD HL,$5CCD ; set pointer to NTRESP - start of area.
LD B,$22 ; thirty four bytes to fill.

;; EACH-VAR
L025A: LD (HL),$FF ; insert a default $FF value.
INC HL ; bump the pointer.
DJNZ L025A ; loop back to EACH-VAR.

RET ; return.

; ------------------------------------
; THE 'SHADOW REPORT PRINTING' ROUTINE
; ------------------------------------
; This routine prints the error reports of the Shadow ROM.
; These relate to the code that follows a RST 20H restart. The error code
; is not printed as it would conflict with Main ROM reports. The text of
; the message is printed and then the Main ROM routine is used to print a
; comma and then the line number and statement. For example,
; Program finished, 0:1
; The code is similar to that at MAIN-4 in the Main ROM. Some improvements
; have been made but at least one slight error has been replicated.

;; REP-MSG
L0260: LD (IY+$7C),$00 ; clear FLAGS_3 in preparation for leaving
; this ROM.

EI ; Enable Interrupts.

HALT ; wait for the first interrupt.

CALL L17B7 ; routine RCL-T-CH reclaims any temporary
; channels and stops any running drive motor.

RES 5,(IY+$01) ; update FLAGS - 'Ready for new key'.
BIT 1,(IY+$30) ; test FLAGS2 - is printer buffer empty ?
JR Z,L0276 ; forward, if so, to FETCH-ERR

RST 10H ; CALBAS - call a Base ROM routine.
DEFW $0ECD ; main routine - COPY-BUFF
; Note. the programmer has neglected to
; set bit 1 of FLAGS first.

;; FETCH-ERR
L0276: POP HL ; drop the return address - after RST.
LD A,(HL) ; fetch the error code.
LD (IY+$00),A ; place in system variable ERR_NR.
INC A ; increment setting zero if was $FF.
PUSH AF ; save actual code and status flags.

LD HL,$0000 ; prepare to blank some system variables.
LD (IY+$37),H ; clear all the bits of FLAGX.
LD (IY+$26),H ; blank X_PTR_hi to suppress error marker.
LD ($5C0B),HL ; blank DEFADD to signal that no defined
; function is being evaluated.

INC L ; select offset of 1 (explicit in main ROM ).
LD ($5C16),HL ; update STRMS_00 - inputs from keyboard.

RST 10H ; CALBAS
DEFW $16B0 ; main SET-MIN clears workspace etc.

RES 5,(IY+$37) ; update FLAGX - signal in EDIT mode
; not INPUT mode.
; Note. all the bits were reset earlier.

RST 10H ; CALBAS
DEFW $0D6E ; main CLS-LOWER

SET 5,(IY+$02) ; update TV_FLAG - signal lower screen
; requires clearing.
RES 3,(IY+$02) ; update TV_FLAG - no change in mode.

POP AF ; restore the incremented error code.
LD HL,L02BF ; start search at REP-MSGS table below.
LD B,$04 ; roughly ensure that BC does not limit
; search area as code must be found.
CPIR ; search for code $00 - $17 skipping
; all ASCII text.

; At this point HL addresses first character of message.

;; PR-REP-LP
L02A7: LD A,(HL) ; fetch each character in turn.
CP $20 ; compare to space.
JR C,L02B4 ; forward if less to END-PR-MS

PUSH HL ; save the character pointer
RST 10H ; CALBAS
DEFW $0010 ; main PRINT-A

POP HL ; restore pointer
INC HL ; and increment.
JR L02A7 ; loop back to PR-REP-LP

; ---

;; END-PR-MS
L02B4: LD SP,($5C3D) ; set machine stack pointer from ERR_SP
INC SP ; prepare to overwrite the MAIN-4
INC SP ; address $1303.
LD HL,$1349 ; substitute with the part that prints
; the comma and line statement.
PUSH HL ; push address to base of stack.
RST 00H ; return to MAIN-ROM.

; Note. at this stage we have, say, "Program finished" on the screen and
; the Main ROM routine at $1349 will complete the ", 0:1" part looping
; back to MAIN-2 to put $1303 on the stack again.

; ------------------------------------
; THE 'SHADOW REPORT MESSAGES' ROUTINE
; ------------------------------------
; These are the Shadow Error Reports. Note. that the never used
; "Header mismatch error" has been largely reclaimed. Each error code,
; which must be less than a space, serves to delimit the preceding text.
; The final delimiter might just as well be $18.

;; REP-MSGS
L02BF DEFB $00
DEFM "Program finished"
DEFB $01
DEFM "Nonsense in BASIC" ; Duplicate of a Main ROM error
DEFB $02
DEFM "Invalid stream number"
DEFB $03
DEFM "Invalid device expression"
DEFB $04
DEFM "Invalid name"
DEFB $05
DEFM "Invalid drive number"
DEFB $06
DEFM "Invalid station number"
DEFB $07
DEFM "Missing name"
DEFB $08
DEFM "Missing station number"
DEFB $09
DEFM "Missing drive number"
DEFB $0A
DEFM "Missing baud rate"
DEFB $0B
DEFM "er mismatch e" ; Note. remnants of unused text.
DEFB $0C
DEFM "Stream already open"
DEFB $0D
DEFM "Writing to a 'read' file"
DEFB $0E
DEFM "Reading a 'write' file"
DEFB $0F
DEFM "Drive 'write' protected"
DEFB $10
DEFM " Microdrive full"
DEFB $11
DEFM " Microdrive not present"
DEFB $12
DEFM "File not found"
DEFB $13
DEFM "Hook code error" ; not listed in manual.
DEFB $14
DEFM "CODE error"
DEFB $15
DEFM "MERGE error"
DEFB $16
DEFM "Verification has failed"
DEFB $17
DEFM "Wrong file type"
DEFB $18 ; end-marker

; *********************************************
; ** T H E S Y N T A X R O U T I N E S **
; *********************************************

; --------------------------------
; THE 'CAT COMMAND SYNTAX' ROUTINE
; --------------------------------
; e.g. CAT 3
; Without the syntax tables of the Main ROM, checking syntax is quite
; laborious. Although the Main ROM allowed CAT without a parameter, a
; single expression in the range 1 - 8 is now required. By default, CAT
; outputs to the upper screen but output may be directed to any stream in
; the range 0 to 15 decimal. The subroutines used to evaluate the numeric
; expressions use the SCANNING routine, in Main ROM, which inserts the
; hidden five-byte numbers after any numeric arguments.

;; CAT-SYN
L0486: LD HL,$5CD8 ; address system variable S_STR1.
LD (HL),$02 ; default to stream 2 the screen.

RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR

CP $0D ; carriage return ?
JR Z,L0494 ; forward, if so, to MISSING-D

CP $3A ; is character ':' ?

;; MISSING-D
L0494: JP Z,L0683 ; jump if no parameter to NREPORT-9

CP $23 ; is character '#' ?
JR NZ,L04A6 ; forward to CAT-SCRN

; Output is directed at a specific stream.

CALL L064E ; routine EXPT-STRM checks for number in range.
CALL L05B1 ; routine SEPARATOR checks for ',' or ';'.
JR NZ,L04B2 ; forward, if not present, to OREPORT-1
; 'Nonsense in BASIC'

RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR

;; CAT-SCRN
L04A6: CALL L061E ; routine EXPT-NUM
CALL L05B7 ; routine ST-END
CALL L066D ; routine CHECK-M-2 checks that drive is in
; range 1 - 8.

JP L1AB5 ; jump forward to CAT-RUN

; ---

;; OREPORT-1
L04B2: RST 20H ; Shadow Error Restart
DEFB $00 ; Nonsense in BASIC

; -----------------------------------
; THE 'FORMAT COMMAND SYNTAX' ROUTINE
; -----------------------------------
; e.g.

;; FRMT-SYN
L04B4: CALL L05F2 ; routine EXPT-SPEC
CALL L05B1 ; routine SEPARATOR
JR NZ,L04BF ; forward to NO-FOR-M

CALL L062F ; routine EXPT-NAME

;; NO-FOR-M
L04BF: CALL L05B7 ; routine ST-END
LD A,($5CD9) ; sv L_STR1 device letter.
CP $54 ; is character "T" ?
JR Z,L04CD ; forward to FOR-B-T

CP $42 ; is character "B" ?
JR NZ,L04D3 ; forward to NOT-FOR-B


;; FOR-B-T
L04CD: CALL L06B0 ; routine TEST-BAUD
JP L0ACD ; jump to SET-BAUD

;; NOT-FOR-B
L04D3: CP $4E ; is character "N" ?
JR NZ,L04E7 ; forward to FOR-M

CALL L068F ; routine TEST-STAT
LD A,($5CD6) ; sv D_STR1 drive number
AND A
JP Z,L069F ; jump to NREPORT-6
LD ($5CC5),A ; sv NTSTAT
JP L05C1 ; jump to END1

;; FOR-M
L04E7: CALL L0685 ; routine TEST-MNAM
JP L1ABA ; jump to FOR-RUN

; ---------------------------------
; THE 'OPEN COMMAND SYNTAX' ROUTINE
; ---------------------------------
;

;; OPEN-SYN
L04ED: CALL L064E ; routine EXPT-STRM
CALL L05B1 ; routine SEPARATOR
JR NZ,L04B2 ; back to OREPORT-1
; 'Nonsense in BASIC'

CALL L05F2 ; routine EXPT-SPEC
CALL L05B1 ; routine SEPARATOR
JR NZ,L0500 ; forward to NOT-OP-M

CALL L062F ; routine EXPT-NAME

;; NOT-OP-M
L0500: CALL L05B7 ; routine ST-END
LD A,($5CD8) ; sv D_STR1

RST 10H ; CALBAS
DEFW $1727 ; main STR-DATA1

LD HL,$0011 ;
AND A ;
SBC HL,BC ;
JR C,L052F ; forward to NREPORT-C

LD A,($5CD9) ; sv L_STR1 device letter.
CP $54 ; "T" ?
JR Z,L051C ; forward to OPEN-RS

CP $42 ; "B" ?
JR NZ,L051F ; forward to NOT-OP-B


;; OPEN-RS
L051C: JP L0B4E ; jump to OP-RSCHAN

; ---

;; NOT-OP-B
L051F: CP $4E ; is character "N" ?
JR NZ,L0529 ; forward to OP-M-C

CALL L068F ; routine TEST-STAT
JP L0F40 ; jump to OPEN-N-ST

; ---

;; OP-M-C
L0529: CALL L0685 ; routine TEST-MNAM
JP L1ABF ; jump to OP-RUN

; ---

;; NREPORT-C
L052F: RST 20H ; Shadow Error Restart
DEFB $0B ; Stream already open

; ----------------------------------
; THE 'ERASE COMMAND SYNTAX' ROUTINE
; ----------------------------------
;

;; ERASE-SYN
L0531: CALL L06A3 ; routine EXPT-EXPR
CALL L05B7 ; routine ST-END
CALL L0685 ; routine TEST-MNAM
JP L1AAB ; jump to ERASE-RUN

; ---------------------------------
; THE 'MOVE COMMAND SYNTAX' ROUTINE
; ---------------------------------
;

;; MOVE-SYN
L053D: CALL L06B9 ; routine EXPT-EXP1
CALL L059F ; routine EX-D-STR
RST 10H ; CALBAS
DEFW $0018 ; main GET-CHAR

CP $CC ; 'TO' ?
JR NZ,L0584 ; forward to NONSENSE

CALL L06B9 ; routine EXPT-EXP1
CALL L059F ; routine EX-D-STR
RST 10H ; CALBAS
DEFW $0018 ; main GET-CHAR

CALL L05B7 ; routine ST-END
JP L1AB0 ; jump to MOVE-RUN

; --------------------------
; THE 'CLS# COMMAND' ROUTINE
; --------------------------
;

;; CLS#-SYN
L0559: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR

CP $23 ; is the character '#' ?
JR NZ,L0584 ; forward, if not, to NONSENSE

RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR

CALL L05B7 ; routine ST-END

LD HL,L0038 ; prepare a zero and black ink on white paper.
LD ($5C8D),HL ; set system variables ATTR_P and MASK_P.
LD ($5C8F),HL ; set system variables ATTR_T and MASK_T.
; Note. not really necessary as done by CLS.
LD (IY+$0E),L ; set system variable BORDCR to colour scheme.
LD (IY+$57),H ; set system variable P_FLAG to zero.

LD A,$07 ; load A with white.
OUT ($FE),A ; directly change border colour.

RST 10H ; CALBAS
DEFW $0D6B ; main CLS clears screen and sets colours.

JP L05C1 ; jump forward to END1.

; ----------------------------
; THE 'CLEAR# COMMAND' ROUTINE
; ----------------------------
;

;; CLR#-SYN
L057F: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


CP $23 ; '#' ?

;; NONSENSE
L0584: JP NZ,L04B2 ; jump to OREPORT-1
; 'Nonsense in BASIC'

RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


CALL L05B7 ; routine ST-END


XOR A ;

;; ALL-STRMS
L058E: PUSH AF ;
SET 1,(IY+$7C) ; sv FLAGS_3
CALL L1718 ; routine CLOSE
POP AF ;
INC A ;
CP $10 ;
JR C,L058E ; back to ALL-STRMS

JP L05C1 ; jump to END1


; -----------------------------------------------------
; THE 'EXCHANGE FILE SPECIFIERS DSTRI AND STR2' ROUTINE
; -----------------------------------------------------
; This routine is used by the MOVE routines to bring one of the two 8-byte
; file specifiers into context. There were two similar routines in the
; first Interface 1 ROM and this, the most efficient, has survived.

;; EX-D-STR
L059F: LD HL,$5CD6 ; sv D_STR1. drive number
LD DE,$5CDE ; sv D_STR2.
LD B,$08 ; eight bytes to swap.

;; ALL-BYTES
L05A7: LD A,(DE) ; fetch byte 1.
LD C,(HL) ; fetch byte 2.
LD (HL),A ; place byte 1.
LD A,C ; byte 2 to accumulator.
LD (DE),A ; place byte 2.

INC HL ; increment the
INC DE ; two pointers.
DJNZ L05A7 ; loop back, for all eight, to ALL-BYTES.

RET ; return.


; -----------------------
; THE 'SEPARATOR' ROUTINE
; -----------------------
; This routine returns with zero flag set if the current character is
; either a comma or semi-colon.

;; SEPARATOR
L05B1: CP $2C ; is character ',' ?
RET Z ; return with zero set if so.

CP $3B ; is character ';' ?
RET ; return.

; ------------------------------
; THE 'END OF STATEMENT' ROUTINE
; ------------------------------
;

;; ST-END
L05B7: CP $0D ; is character carriage return ?
JR Z,L05BF ; forward, if so, to TEST-RET

CP $3A ; is character a ':' ?
JR NZ,L0584 ; back, if not, to NONSENSE


;; TEST-RET
L05BF: RST 18H ; checking syntax ?
RET NZ ; return if not.


; --------------------------------------------
; THE 'RETURN TO THE MAIN INTERPRETER' ROUTINE
; --------------------------------------------
;

;; END1
L05C1: LD SP,($5C3D) ; sv ERR_SP
LD (IY+$00),$FF ; sv ERR_NR

LD HL,$1BF4 ; Main ROM address STMT-NEXT

RST 18H ; checking syntax ?
JR Z,L05E0 ; forward, if so, to RETAD-SYN

LD A,$7F ;
IN A,($FE) ;
RRA ;
JR C,L05DD ; forward to RETAD-RUN

LD A,$FE ;
IN A,($FE) ;
RRA ;
JR NC,L05E2 ; forward to BREAK-PGM


;; RETAD-RUN
L05DD: LD HL,$1B7D ; Main ROM address STMT-R-1

;; RETAD-SYN
L05E0: PUSH HL ;
RST 00H ; to MAIN-ROM

; ---

;; BREAK-PGM
L05E2: LD (IY+$00),$14 ; insert error code in system variable ERR_NR.
RST 28H ; Error Main ROM
; 'BREAK into program'

; ----------------------------------------
; THE 'EVALUATE STRING EXPRESSION' ROUTINE
; ----------------------------------------
;

;; EXPT-STR
L05E7: RST 10H ; CALBAS
DEFW $1C8C ; main EXPT-EXP
RST 18H ; checking syntax ?
RET Z

PUSH AF
RST 10H ; CALBAS
DEFW $2BF1 ; main STK-FETCH
POP AF
RET


; -----------------------------------------
; THE 'EVALUATE CHANNEL EXPRESSION' ROUTINE
; -----------------------------------------
;

;; EXPT-SPEC
L05F2: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


;; EXP-SPEC2
L05F5 CALL L05E7 ; routine EXPT-STR evaluates a string e.g. "m"
; start in DE, length in BC.

; one of the main tenets of Sinclair BASIC is that a value can be replaced
; by an expression of the same type at any time, so this routine must allow
; something like "tomato"(3) as well as the more conventional "m" specifier.
; Only in runtime when the expression is evaluated can a single character be
; insisted upon.

JR Z,L060C ; forward, if checking syntax, to TEST-NEXT.

PUSH AF ; save following character.

LD A,C ; in runtime check
DEC A ; immediately for
OR B ; a single character.

JR NZ,L062D ; forward, if not, to NREPORT-3
; 'Invalid device expression'

LD A,(DE) ; fetch the addressed character.

RST 10H ; CALBAS
DEFW $2C8D ; main ALPHA

JR NC,L062D ; forward, if not alphabetic, to NREPORT-3

AND $DF ; convert to uppercase with 'AND %11011111'

LD ($5CD9),A ; place in system variable L_STR1 device letter.

POP AF ; restore the following character.

;; TEST-NEXT
L060C: CP $0D ; test for carriage return.
RET Z ; return if so.

CP $3A ; is character ':' ?
RET Z ; return if so.

CP $A5 ; RND
RET NC ; return with a token??

CALL L05B1 ; routine SEPARATOR tests for both ';' and ','.

JP NZ,L04B2 ; jump back, if not, to OREPORT-1
; 'Nonsense in BASIC'

RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR

; -----------------------------------------------
; THE 'EVALUATE NUMERIC DRIVE EXPRESSION' ROUTINE
; -----------------------------------------------
; This routine is called once only to evaluate the numeric expression
; following a 'CAT' command token or is used from above to check a numeric
; expression following for example "M"; .

;; EXPT-NUM
L061E: RST 10H ; CALBAS
DEFW $1C82 ; main EXPT-1NUM
RST 18H ; checking syntax ?
RET Z ; return if checking syntax.

PUSH AF ; save NZ not syntax flag

RST 10H ; CALBAS
DEFW $1E99 ; main FIND-INT2

LD ($5CD6),BC ; set system variable D_STR1 drive number

POP AF ; restore NZ not syntax flag

RET ; return.

; ---

;; NREPORT-3
L062D: RST 20H ; Shadow Error Restart
DEFB $02 ; 'Invalid device expression'

; -------------------------------
; THE 'EVALUATE FILENAME' ROUTINE
; -------------------------------
;

;; EXPT-NAME
L062F: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


CALL L05E7 ; routine EXPT-STR
RET Z

PUSH AF
LD A,C
OR B
JR Z,L064C ; forward to NREPORT-4

LD HL,$000A
SBC HL,BC
JR C,L064C ; forward to NREPORT-4

LD ($5CDA),BC ; sv N_STR1
LD ($5CDC),DE ; sv D_STR1
POP AF
RET

; ---

;; NREPORT-4
L064C: RST 20H ; Shadow Error Restart
DEFB $03 ; Invalid name

; ------------------------------------
; THE 'EVALUATE STREAM NUMBER' ROUTINE
; ------------------------------------
;

;; EXPT-STRM
L064E: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


RST 10H ; CALBAS
DEFW $1C82 ; main EXPT-1NUM
RST 18H ; checking syntax ?
RET Z ;

PUSH AF ;
RST 10H ; CALBAS
DEFW $1E94 ; main FIND-INT1
CP $10 ;
JR NC,L0663 ; forward to NREPORT-2

LD ($5CD8),A ; sv D_STR1
POP AF ;
RET ;

; ---

;; NREPORT-2
L0663: RST 20H ; Shadow Error Restart
DEFB $01 ; Invalid stream number


; ----------------------------------
; THE 'CHECK "M" PARAMETERS' ROUTINE
; ----------------------------------
; called once from TEST-MNAM

;; CHECK-M
L0665: LD A,($5CD9) ; fetch system variable L_STR1 device letter.
CP $4D ; is character "M" ?
JP NZ,L062D ; jump back, if not, to NREPORT-3
; Error: 'Invalid device expression'.

;; CHECK-M-2
L066D: LD DE,($5CD6) ; fetch system variable D_STR1 drive number.
LD A,E ; test for
OR D ; zero.
JR Z,L0681 ; forward, if so, to NREPORT-5
; 'Invalid drive number'

INC DE ; also test that
LD A,E ; location does not hold
OR D ; the default $FFFF value.
JR Z,L0683 ; forward, if so, to NREPORT-9
; 'Missing drive number'.

DEC DE ; restore to initial value.
LD HL,L0008 ; and test that
SBC HL,DE ; drive is in range 1 - 8.
RET NC ; return if so.


;; NREPORT-5
L0681: RST 20H ; Shadow Error Restart
DEFB $04 ; Invalid drive number
; ---

;; NREPORT-9
L0683: RST 20H ; Shadow Error Restart
DEFB $08 ; Missing drive number

; -----------------------------------------------
; THE 'CHECK "M" PARAMETERS AND FILENAME' ROUTINE
; -----------------------------------------------
; This routine checks that the device expression is "M", that the drive is in
; the range 1 - 8 and that the filename is not null.

;; TEST-MNAM
L0685: CALL L0665 ; routine CHECK-M checks for "M" and valid
; drive number.

LD A,($5CDB) ; load A with D_STR1 the high byte of length
; of filename.
AND A ; test for zero.
RET Z ; return if so.

; else system default $FF.

RST 20H ; Shadow Error Restart
DEFB $06 ; Missing name


; ----------------------------------
; THE 'CHECK STATION NUMBER' ROUTINE
; ----------------------------------
;

;; TEST-STAT
L068F: LD DE,($5CD6) ; sv D_STR1 drive number
INC DE
LD A,E
OR D
JR Z,L06A1 ; forward to NREPORT-8

DEC DE
LD HL,L0040
SBC HL,DE
RET NC


;; NREPORT-6
L069F: RST 20H ; Shadow Error Restart
DEFB $05 ; Invalid station number

;; NREPORT-8
L06A1: RST 20H ; Shadow Error Restart
DEFB $07 ; Missing station number

; -----------------------------------
; THE 'EVALUATE "X";N;"NAME"' ROUTINE
; -----------------------------------
;

;; EXPT-EXPR
L06A3: CALL L05F2 ; routine EXPT-SPEC
CALL L05B1 ; routine SEPARATOR
JP NZ,L04B2 ; jump to OREPORT-1
; 'Nonsense in BASIC'

CALL L062F ; routine EXPT-NAME
RET ; return...


; -----------------------------
; THE 'CHECK BAUD RATE' ROUTINE
; -----------------------------
;

;; TEST-BAUD
L06B0: LD HL,($5CD6) ; sv D_STR1 drive number
INC HL
LD A,L
OR H
RET NZ

RST 20H ; Shadow Error Restart
DEFB $09 ; Missing baud rate

; -------------------------------------------
; THE 'EVALUATE STREAM OR EXPRESSION' ROUTINE
; -------------------------------------------
;

;; EXPT-EXP1
L06B9: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


CP $23 ; is character '#' ?
JP Z,L064E ; jump to EXPT-STRM

CALL L05F5 ; routine EXP-SPEC2
CALL L05B1 ; routine SEPARATOR
JR NZ,L06CC ; forward to ENDHERE

CALL L062F ; routine EXPT-NAME

;; ENDHERE
L06CC: RST 18H ; checking syntax ?
RET Z

LD A,($5CD9) ; sv L_STR1 device letter.
CP $54 ; is character "T" ?
RET Z ;

CP $42 ; is character "B" ?
RET Z ;

CP $4E ; is character "N" ?
JP Z,L068F ; jump, if so, to TEST-STAT

JP L0685 ; jump to TEST-MNAM

; ---

DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF

; --------------------
; THE 'UNPAGE' ROUTINE
; --------------------
;

;; UNPAGE
L0700: RET


; ---------------------------------
; THE 'EVALUATE PARAMETERS' ROUTINE
; ---------------------------------
;

;; EXPT-PRMS
L0701: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


CP $2A ; is character '*'
JR NZ,L073C ; forward, if not, to OREP-1-2

RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


CALL L05F5 ; routine EXP-SPEC2
CALL L05B1 ; routine SEPARATOR
JR NZ,L0716 ; forward to NO-NAME

CALL L062F ; routine EXPT-NAME

;; NO-NAME
L0716: PUSH AF
LD A,($5CD9) ; sv L_STR1 device letter.

CP $4E ; is character "N" ?
JR NZ,L0722 ; forward, if not, to NOT-NET

SET 3,(IY+$7C) ; update FLAGS_3 signal networking.

;; NOT-NET
L0722: POP AF
CP $0D ; is character carriage return ?
JR Z,L0750 ; forward to END-EXPT

CP $3A ; is character ':' ?
JR Z,L0750 ; forward to END-EXPT

CP $AA ; is character the token 'SCREEN$' ?
JR Z,L0771 ; forward to SCREEN$

CP $AF ; is character the token 'CODE' ?
JR Z,L0789 ; forward to CODE

CP $CA ; is character the token 'LINE' ?
JR Z,L073E ; forward to LINE

CP $E4 ; is character the token 'DATA' ?
JP Z,L07D2 ; jump to DATA

;; OREP-1-2
L073C: RST 20H ; Shadow Error Restart
DEFB $00 ; Nonsense in BASIC

; ---

;; LINE
L073E: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR

RST 10H ; CALBAS
DEFW $1C82 ; main EXPT-1NUM

CALL L05B7 ; routine ST-END

RST 10H ; CALBAS
DEFW $1E99 ; main FIND-INT2

LD ($5CED),BC ; sv HD_11
JR L0753 ; forward to PROG

; ---

;; END-EXPT
L0750: CALL L05B7 ; routine ST-END

; the 'PROGRAM' SUBROUTINE is used when loading 'run'.

;; PROG
L0753: XOR A ;
LD ($5CE6),A ; sv HD_00
LD HL,($5C59) ; sv E_LINE
LD DE,($5C53) ; sv PROG
LD ($5CE9),DE ; sv HD_0D
SCF ;
SBC HL,DE ;
LD ($5CE7),HL ; sv HD_0B
LD HL,($5C4B) ; sv VARS
SBC HL,DE ;
LD ($5CEB),HL ; sv HD_0F
RET

; ---

;; SCREEN$
L0771: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR

CALL L05B7 ; routine ST-END
LD HL,$1B00
LD ($5CE7),HL ; sv HD_0B
LD HL,$4000
LD ($5CE9),HL ; sv HD_0D
LD A,$03
LD ($5CE6),A ; sv HD_00
RET

; ---

;; CODE
L0789: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR

CP $0D ; is character a carriage return ?
JR Z,L079A ; forward to DEFLT-0

CP $3A ; is character a ':' ?
JR NZ,L079F ; forward to PAR-1

BIT 5,(IY+$7C) ; sv FLAGS_3
JR NZ,L073C ; back to OREP-1-2


;; DEFLT-0
L079A: RST 10H ; CALBAS
DEFW $1CE6 ; main USE-ZERO
JR L07A7 ; forward to TEST-SAVE

; ---

;; PAR-1
L079F: RST 10H ; CALBAS
DEFW $1C82 ; main EXPT-1NUM
CALL L05B1 ; routine SEPARATOR
JR Z,L07B2 ; forward to PAR-2


;; TEST-SAVE
L07A7: BIT 5,(IY+$7C) ; sv FLAGS_3
JR NZ,L073C ; back to OREP-1-2

RST 10H ; CALBAS
DEFW $1CE6 ; main USE-ZERO
JR L07B8 ; forward to END-CODE

; ---

;; PAR-2
L07B2: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


RST 10H ; CALBAS
DEFW $1C82 ; main EXPT-1NUM

;; END-CODE
L07B8: RST 10H ; CALBAS
DEFW $0018 ; main GET-CHAR

CALL L05B7 ; routine ST-END

RST 10H ; CALBAS
DEFW $1E99 ; main FIND-INT2
LD ($5CE7),BC ; sv HD_0B

RST 10H ; CALBAS
DEFW $1E99 ; main FIND-INT2
LD ($5CE9),BC ; sv HD_0D

LD A,$03
LD ($5CE6),A ; sv HD_00
RET ; return.

; ---
;
; ---


;; DATA
L07D2: BIT 6,(IY+$7C) ; sv FLAGS_3
JR Z,L07DA ; forward to NO-M-ARR

RST 20H ; Shadow Error Restart
DEFB $14 ; MERGE error

;; NO-M-ARR
L07DA: RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


RST 10H ; CALBAS
DEFW $28B2 ; main LOOK-VARS

SET 7,C
JR NC,L07F2 ; forward to EXISTING

LD HL,$0000
BIT 4,(IY+$7C) ; sv FLAGS_3
JR NZ,L080E ; forward to LD-DATA

LD (IY+$00),$01 ; sv ERR_NR to '2 Variable not found'
RST 28H ; Error Main ROM

; ---

;; EXISTING
L07F2: JR Z,L07F6 ; forward to G-TYPE


;; NONS-BSC
L07F4: RST 20H ; Shadow Error Restart
DEFB $00 ; Nonsense in BASIC

; ---

;; G-TYPE
L07F6: RST 18H ; checking syntax ?
JR Z,L081C ; forward to END-DATA

BIT 5,(IY+$7C) ; sv FLAGS_3
JR Z,L0803 ; forward to VR-DATA

BIT 7,(HL)
JR Z,L07F4 ; back to NONS-BSC


;; VR-DATA
L0803: INC HL
LD A,(HL)
LD ($5CE7),A ; sv HD_0B
INC HL
LD A,(HL)
LD ($5CE8),A ; sv HD_0B_hi
INC HL

;; LD-DATA
L080E: LD A,C
LD ($5CEB),A ; sv HD_0F
LD A,$01
BIT 6,C
JR Z,L0819 ; forward to NUM-ARR

INC A

;; NUM-ARR
L0819: LD ($5CE6),A ; sv HD_00

;; END-DATA
L081C: EX DE,HL
RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


CP $29 ; is character ')' ?
JR NZ,L07F4 ; back to NONS-BSC

RST 10H ; CALBAS
DEFW $0020 ; main NEXT-CHAR


CALL L05B7 ; routine ST-END
LD ($5CE9),DE ; sv HD_0D
RET ; return.


; ---------------------------------
; THE 'SAVE COMMAND SYNTAX' ROUTINE
; ---------------------------------
;

;; SAVE-SYN
L082F: SET 5,(IY+$7C) ; sv FLAGS_3
CALL L0701 ; routine EXPT-PRMS

LD A,($5CD9) ; sv L_STR1 device letter.

CP $42 ; is character 'B' ?
JR Z,L084F ; forward to SA-HEADER

CP $4E ; is character 'N' ?
JR NZ,L0849 ; forward to SAVE-M

CALL L068F ; routine TEST-STAT
CALL L0F46 ; routine OP-TEMP-N
JR L084F ; forward to SA-HEADER

; ---

;; SAVE-M
L0849: CALL L0685 ; routine TEST-MNAM
JP L1AC4 ; jump to SAVE-RUN

; ---

;; SA-HEADER
L084F: LD B,$09
LD HL,$5CE6 ; sv HD_00

;; HD-LOOP
L0854: CALL L0884 ; routine SA-BYTE
INC HL
DJNZ L0854 ; back to HD-LOOP

LD HL,($5CE9) ; sv HD_0D
BIT 3,(IY+$7C) ; sv FLAGS_3
JR Z,L086E ; forward to SA-BLOCK

LD A,($5CE6) ; sv HD_00
CP $03 ; compare with three - type CODE
JR NC,L086E ; forward to SA-BLOCK

LD DE,$0114 ;
ADD HL,DE ;

;; SA-BLOCK
L086E: LD BC,($5CE7) ; sv HD_0B

;; SA-BLK-LP
L0872: LD A,C ;
OR B ;
JR Z,L0881 ; forward to S-BLK-END

PUSH IX ;;;

CALL L0884 ; routine SA-BYTE

POP IX ;;;

DEC BC ;
INC HL ;
JR L0872 ; back to SA-BLK-LP

; ---

;; S-BLK-END
L0881: JP L098C ; jump to TST-MR-M

; --------------------------------------------------
; THE 'SAVE A BYTE TO NETWORK OR RS232 LINK' ROUTINE
; --------------------------------------------------
;

;; SA-BYTE
L0884: PUSH HL ;
PUSH BC ;
BIT 3,(IY+$7C) ; sv FLAGS_3
LD A,(HL) ;
JR NZ,L0892 ; forward to SA-NET

CALL L0D07 ; routine BCHAN-OUT
JR L0895 ; forward to SA-B-END

; ---

;; SA-NET
L0892: CALL L0E09 ; routine NCHAN-OUT

;; SA-B-END
L0895: POP BC ;
POP HL ;
RET ;


; ---------------------------------
; THE 'LOAD COMMAND SYNTAX' ROUTINE
; ---------------------------------
;

;; LOAD-SYN
L0898: SET 4,(IY+$7C) ; sv FLAGS_3
CALL L0701 ; routine EXPT-PRMS
JP L08B3 ; jump to LD-VF-MR

; -----------------------------------
; THE 'VERIFY COMMAND SYNTAX' ROUTINE
; -----------------------------------
;

;; VERIF-SYN
L08A2: SET 7,(IY+$7C) ; sv FLAGS_3
CALL L0701 ; routine EXPT-PRMS
JP L08B3 ; jump to LD-VF-MR

; ----------------------------------
; THE 'MERGE COMMAND SYNTAX' ROUTINE
; ----------------------------------
;

;; MRG-SYN
L08AC: SET 6,(IY+$7C) ; sv FLAGS_3
CALL L0701 ; routine EXPT-PRMS

; ----------------------------------------
; THE 'LOAD-VERIFY-MERGE COMMANDS' ROUTINE
; ----------------------------------------
;

;; LD-VF-MR
L08B3: LD HL,$5CE6 ; set source to HD_00
LD DE,$5CDE ; set destination to D_STR2
LD BC,$0007 ; seven bytes to copy.
LDIR ; copy type, start, length, length of program.

LD A,($5CD9) ; sv L_STR1 device letter.

CP $4E ; "N" ?

JR Z,L08D1 ; forward to TS-L-NET

CP $42 ; "B" ?

JR Z,L08D7 ; forward to TS-L-RS

; proceed with Microdrive device.

CALL L0685 ; routine TEST-MNAM return without error if
; device is "M" and drive and filename are OK.

CALL L1971 ; routine F-M-HEAD loads the header type
; record for the above filename and populates
; the locations HD_00 to HD_11.

JR L08F6 ; forward to TEST-TYPE which tests that file
; types agree and then loads rest of records.

; ---

;; TS-L-NET
L08D1: CALL L068F ; routine TEST-STAT
CALL L0F46 ; routine OP-TEMP-N

;; TS-L-RS
L08D7: LD HL,$5CE6 ; sv HD_00
LD B,$09 ;

;; LD-HEADER
L08DC: PUSH HL
PUSH BC
BIT 3,(IY+$7C) ; sv FLAGS_3
JR Z,L08EB ; forward to LD-HD-RS


;; LD-HD-NET
L08E4: CALL L0DAF ; routine NCHAN-IN
JR NC,L08E4 ; back to LD-HD-NET

JR L08F0 ; forward to LD-HDR-2

; ---

;; LD-HD-RS
L08EB: CALL L0B88 ; routine BCHAN-IN
JR NC,L08EB ; back to LD-HD-RS


;; LD-HDR-2
L08F0: POP BC
POP HL
LD (HL),A
INC HL
DJNZ L08DC ; back to LD-HEADER

; -->

;; TEST-TYPE
L08F6: LD A,($5CDE) ; sv D_STR2
LD B,A
LD A,($5CE6) ; sv HD_00
CP B
JR NZ,L0906 ; forward to NREPORT-N

CP $03 ; compare with three - type CODE
JR Z,L0915 ; forward to T-M-CODE

JR C,L0908 ; forward to TST-MERGE

;; NREPORT-N
L0906: RST 20H ; Shadow Error Restart
DEFB $16 ; Wrong file type

; ---

;; TST-MERGE
L0908: BIT 6,(IY+$7C) ; sv FLAGS_3
JR NZ,L096B ; forward to MERGE-BLK

BIT 7,(IY+$7C) ; sv FLAGS_3
JP Z,L09A7 ; jump to LD-PR-AR

;; T-M-CODE
L0915: BIT 6,(IY+$7C) ; sv FLAGS_3
JR Z,L091D ; forward to LD-BLOCK

RST 20H ; Shadow Error Restart
DEFB $14 ; MERGE error

; ---

;; LD-BLOCK
L091D: LD HL,($5CDF) ; sv D_STR2 (+1) length of data
LD DE,($5CE7) ; sv HD_0B
LD A,H ;
OR L ;
JR Z,L0936 ; forward to LD-BLK-2

SBC HL,DE ;
JR NC,L0936 ; forward to LD-BLK-2

BIT 4,(IY+$7C) ; sv FLAGS_3
JR Z,L0934 ; forward to NREPORT-L

RST 20H ; Shadow Error Restart
DEFB $13 ; Code Error

; ---

;; NREPORT-L
L0934: RST 20H ; Shadow Error Restart
DEFB $15 ; Verification has failed

; ---

;; LD-BLK-2
L0936: LD HL,($5CE1) ; sv L_STR2
LD A,(IX+$04) ; channel letter
CP $CD ; 'M' +$80 ?
JR NZ,L0945 ; forward to LD-BLK-3

LD HL,($5CE4) ; sv D_STR2 ********
JR L0956 ; forward to LD-BLK-4

; ---

;; LD-BLK-3
L0945: BIT 3,(IY+$7C) ; sv FLAGS_3
JR Z,L0956 ; forward to LD-BLK-4

LD A,($5CE6) ; sv HD_00
CP $03 ; compare with three - type CODE
JR Z,L0956 ; forward to LD-BLK-4

LD BC,$0114 ;
ADD HL,BC ;

;; LD-BLK-4
L0956: LD A,H ;
OR L ;
JR NZ,L095D ; forward to LD-BLK-5

LD HL,($5CE9) ; sv HD_0D

;; LD-BLK-5
L095D: LD A,($5CE6) ; sv HD_00
AND A ;
JR NZ,L0966 ; forward to LD-NO-PGM

LD HL,($5C53) ; sv PROG

;; LD-NO-PGM
L0966: CALL L0A60 ; routine LV-ANY
JR L098C ; forward to TST-MR-M

; ---


;; MERGE-BLK
L096B: LD A,($5CEE) ; sv HD_11_hi
AND $C0 ;
JR NZ,L0977 ; forward to NO-AUTOST

CALL L17B7 ; routine RCL-T-CH


RST 20H ; Shadow Error Restart
DEFB $14 ; MERGE error

; ---

;; NO-AUTOST
L0977: LD BC,($5CE7) ; sv HD_0B
PUSH BC ;
INC BC ;

RST 10H ; CALBAS
DEFW $0030 ; main BC-SPACES

LD (HL),$80 ;
EX DE,HL ;
POP DE ;
PUSH HL ;
CALL L0A60 ; routine LV-ANY
POP HL ;

RST 10H ; CALBAS
DEFW $08CE ; main ME-CTRLX

; ---

;; TST-MR-M
L098C: LD A,(IX+$04) ; channel letter
CP $CD ; 'M' + $80 ?
JR NZ,L0998 ; forward to TST-MR-N

CALL L138E ; routine CLOSE-M2
JR L09A4 ; forward to MERGE-END

; ---

;; TST-MR-N
L0998: BIT 3,(IY+$7C) ; sv FLAGS_3
JR Z,L09A4 ; forward to MERGE-END

CALL L0FAE ; routine SEND-NEOF
CALL L17B7 ; routine RCL-T-CH

;; MERGE-END
L09A4: JP L05C1 ; jump to END1

; ---

;; LD-PR-AR
L09A7: LD DE,($5CE7) ; sv HD_0B
LD HL,($5CE1) ; sv L_STR2
PUSH HL ;
LD A,H ;
OR L ;
JR NZ,L09B9 ; forward to LD-PROG

INC DE ;
INC DE ;
INC DE ;
EX DE,HL ;
JR L09C2 ; forward to TST-SPACE

; ---


;; LD-PROG
L09B9: LD HL,($5CDF) ; sv D_STR2 (+1) length of data
EX DE,HL ;
SCF ;
SBC HL,DE ;
JR C,L09CB ; forward to TST-TYPE


;; TST-SPACE
L09C2: LD DE,$0005 ;
ADD HL,DE ;
LD B,H ;
LD C,L ;

RST 10H ; CALBAS
DEFW $1F05 ; main TEST-ROOM

; Note. that before the above call, interrupts are disabled and the motor
; of the Microdrive is running. If there should be insufficient room,
; then the processor stops at the HALT instruction at address $1303
; (MAIN-4), in the main ROM, while trying to output the "Out of Memory"
; report. This could be corrected by replacing the above 3 bytes to a
; call to a 6-byte subroutine which carries out the same instructions
; between an EI/DI pair. In the production of the "Out of Memory" report
; this ROM will be paged again by the instruction fetch at 0008. The
; motors are stopped at START-4 and then Control will then pass to the
; other ROM to execute the "LD A,(HL)", then back to this ROM to eliminate
; the "OK" message before a final switch to the Main ROM for the actual
; message text.

;; TST-TYPE
L09CB: POP HL
LD A,($5CE6) ; sv HD_00
AND A
JR Z,L0A19 ; forward to SET-PROG

LD A,H
OR L
JR Z,L09F7 ; forward to CRT-NEW

LD A,(IX+$04) ; channel letter
CP $CD ; is character an inverted "M" ?
JR NZ,L09E2 ; forward to T-LD-NET

LD HL,($5CE4) ; sv D_STR2
JR L09EC ; forward to RCLM-OLD

; ---

;; T-LD-NET
L09E2: BIT 3,(IY+$7C) ; sv FLAGS_3
JR Z,L09EC ; forward to RCLM-OLD

LD DE,$0114
ADD HL,DE

;; RCLM-OLD
L09EC: DEC HL
LD B,(HL)
DEC HL
LD C,(HL)
DEC HL
INC BC
INC BC
INC BC
RST 10H ; CALBAS
DEFW $19E8 ; main RECLAIM-2

;; CRT-NEW
L09F7: LD HL,($5C59) ; sv E_LINE
DEC HL
LD BC,($5CE7) ; sv HD_0B
PUSH BC
INC BC
INC BC
INC BC
LD A,($5CE3) ; sv D_STR2
PUSH AF
RST 10H ; CALBAS
DEFW $1655 ; main MAKE-ROOM
INC HL
POP AF
LD (HL),A
POP DE
INC HL
LD (HL),E
INC HL
LD (HL),D
INC HL

;; END-LD-PR
L0A13: CALL L0A60 ; routine LV-ANY

JP L098C ; jump to TST-MR-M

; ---

;; SET-PROG
L0A19: RES 1,(IY+$7C) ; sv FLAGS_3
LD DE,($5C53) ; sv PROG
LD HL,($5C59) ; sv E_LINE
DEC HL
RST 10H ; CALBAS
DEFW $19E5 ; main RECLAIM-1
LD BC,($5CE7) ; sv HD_0B
LD HL,($5C53) ; sv PROG
RST 10H ; CALBAS
DEFW $1655 ; main MAKE-ROOM
INC HL
LD BC,($5CEB) ; sv HD_0F
ADD HL,BC
LD ($5C4B),HL ; sv VARS
LD A,($5CEE) ; sv HD_11_hi
LD H,A
AND $C0
JR NZ,L0A52 ; forward to NO-AUTO

SET 1,(IY+$7C) ; sv FLAGS_3
LD A,($5CED) ; sv HD_11
LD L,A
LD ($5C42),HL ; sv NEWPPC
LD (IY+$0A),$00 ; sv NSPPC

;; NO-AUTO
L0A52: LD HL,($5C53) ; sv PROG
LD DE,($5CE7) ; sv HD_0B
DEC HL
LD ($5C57),HL ; sv DATADD
INC HL
JR L0A13 ; back to END-LD-PR


; ----------------------------
; THE 'LOAD OR VERIFY' ROUTINE
; ----------------------------
; This routine is able to either LOAD or VERIFY a block of bytes, from any
; of the three possible binary sources, A Microdrive cartridge, the Binary
; "B" RS232 channel or the Network "N" channel.
; The block could be a program, code bytes or an array and the first
; receiving location is in HL and the length in DE.

;; LV-ANY
L0A60: LD A,D ; test the length
OR E ; for zero.
RET Z ; return if so.

LD A,(IX+$04) ; fetch channel letter
CP $CD ; is letter "M" + $80 ?

JR NZ,L0A6E ; forward, if not, to LV-BN to load from
; the B channel or network.

; else is a temporary "M" channel so load or verify and then return.

CALL L199A ; routine LV-MCH loads or verifies a block
; of code from Microdrive.

RET ; return after called routine.

; ---

; Load or Verify from B channel or Network.

;; LV-BN
L0A6E: PUSH HL ; save address.
PUSH DE ; save byte count.
BIT 3,(IY+$7C) ; test FLAGS_3 - using network ?
JR Z,L0A7D ; forward, if not, to LV-B

; Load or Verify from "N" channel.

;; LV-N
L0A76: CALL L0DAF ; routine NCHAN-IN
JR NC,L0A76 ; back to LV-N

JR L0A82 ; forward to LV-BN-E

; ---

; Load or Verify from "B" channel.

;; LV-B
L0A7D: CALL L0B88 ; routine BCHAN-IN
JR NC,L0A7D ; back to LV-B

; Load or Verify "B","N" end test.

;; LV-BN-E
L0A82: POP DE ; restore code length.
DEC DE ; and decrement.
POP HL ; restore load address.

BIT 7,(IY+$7C) ; test FLAGS_3 - verify operation.
JR NZ,L0A8E ; forward, if so missing load, to VR-BN

LD (HL),A ; load the byte into memory.
JR L0A93 ; forward to LVBN-END

; ---

; Verify "B" or "N" bytes.

;; VR-BN
L0A8E: CP (HL) ; compare the received byte with the byte in
; memory.
JR Z,L0A93 ; forward, with match, to LVBN-END.

RST 20H ; Shadow Error Restart
DEFB $15 ; 'Verification has failed'

; ---

; Load or Verify "B","N" end.

;; LVBN-END
L0A93: INC HL ; increment the address.
LD A,E ; test the byte
OR D ; counter for zero.
JR NZ,L0A6E ; back, if not, to LV-BN

RET ; return.


; --------------------------------
; THE 'LOAD "RUN" PROGRAM' ROUTINE
; --------------------------------
;

;; LOAD-RUN
L0A99: LD BC,$0001 ; set drive to one.
LD ($5CD6),BC ; update D_STR1 drive number.

LD BC,$0003 ; length of "run" is three.
LD ($5CDA),BC ; update N_STR1 length of filename.

LD BC,L0ACA ; addr: NAME-RUN (below)
LD ($5CDC),BC ; update A_STR1 - address of filename.

SET 4,(IY+$7C) ; update FLAGS_3 signal a LOAD operation.

CALL L0753 ; routine PROG sets up the first seven header
; bytes for a program.

LD HL,$5CE6 ; set start to HD_00
LD DE,$5CDE ; set destination to D_STR2
LD BC,$0009 ; nine bytes are copied.
; Note. should be seven but is mostly harmless.

LDIR ; block copy.

SET 7,(IY+$0A) ; update Main NSPPC - signal no jump to be made.

CALL L1971 ; routine F-M-HEAD loads the header type
; record for the 'run' file and populates
; the nine locations HD_00 to HD_11.

JP L08F6 ; jump back to TEST-TYPE to test that type is
; 'program' and load the rest.

; ---

;; NAME-RUN
L0ACA: DEFM "run" ; the filename "run"

; *******************************************
; ** T H E R S 2 3 2 R O U T I N E S **
; *******************************************

; ----------------------------------------
; THE 'SET "BAUD" SYSTEM VARIABLE' ROUTINE
; ----------------------------------------
;

;; SET-BAUD
L0ACD: LD BC,($5CD6) ; sv D_STR1 drive number
LD HL,L0AF3 ; RS-CONSTS

;; NXT-ENTRY
L0AD4: LD E,(HL)
INC HL
LD D,(HL)
INC HL
EX DE,HL
LD A,H
CP $4B ;
JR NC,L0AE8 ; forward to END-SET

AND A
SBC HL,BC
JR NC,L0AE8 ; forward to END-SET

EX DE,HL
INC HL
INC HL
JR L0AD4 ; loop back to NXT-ENTRY

; ---

;; END-SET
L0AE8: EX DE,HL
LD E,(HL)
INC HL
LD D,(HL)
LD ($5CC3),DE ; sv BAUD
JP L05C1 ; jump to END1

; ------------------------------------
; THE 'RS232 TIMING CONSTANTS' ROUTINE
; ------------------------------------
;

;; RS-CONSTS
L0AF3: DEFW $0032 ;
DEFW $0A82 ;
DEFW $006E ;
DEFW $04C5 ;
DEFW $012C ;
DEFW $01BE ;
DEFW $0258 ;
DEFW $00DE ;
DEFW $04B0 ;
DEFW $006E ;
DEFW $0960 ;
DEFW $0036 ;
DEFW $12C0 ;
DEFW $001A ;
DEFW $2580 ;
DEFW $000C ;
DEFW $4B00 ;
DEFW $0005 ;


; ----------------------------------------------
; THE 'OPEN RS232 CHANNEL IN CHANS AREA' ROUTINE
; ----------------------------------------------
;

;; OP-RS-CH
L0B17: LD HL,($5C53) ; use system variable PROG to address the
; location following the Channels area.
DEC HL ; step back to the end-marker.
LD BC,$000B ; eleven bytes of room required.
PUSH BC ; save bytes

RST 10H ; CALBAS
DEFW $1655 ; main routine MAKE-ROOM opens up the space.
; register HL points to location before room.

POP BC ; bring back the eleven bytes.

PUSH DE ; save DE briefly
CALL L1A82 ; routine REST-N-AD adjusts the dynamic memory
; pointers to filenames in D_STR1 and D_STR2.
POP DE ; restore DE.

LD HL,L0B76 - 1 ; last byte of T-Channel info.
LD BC,$000B ; eleven bytes to copy.
LDDR ; block copy downwards.

INC DE ;
LD A,($5CD9) ; sv L_STR1 device letter.
CP $42 ; is it "B" ?
RET NZ ; return as must be "T".

; but if this is to be a binary channel then overwrite the letter and the output
; and input routines.

PUSH DE ;

LD HL,$0004 ;
ADD HL,DE ;

LD (HL),$42 ; 'B'
INC HL ;

LD DE,L0D07 ; address B-CHAN-OUT
LD (HL),E ;
INC HL ;
LD (HL),D ;
INC HL ;

LD DE,L0B7C ; address B-INPUT
LD (HL),E ;
INC HL ;
LD (HL),D ;

POP DE ;
RET ; return.


; ----------------------------------------
; THE 'ATTACH CHANNEL TO A STREAM' ROUTINE
; ----------------------------------------
;

;; OP-RSCHAN
L0B4E: CALL L0B17 ; routine OP-RS-CH

;; OP-STREAM
L0B51: LD HL,($5C4F) ; sv CHANS
DEC HL
EX DE,HL
AND A
SBC HL,DE
EX DE,HL
LD HL,$5C16 ; sv STRMS_00
LD A,($5CD8) ; sv D_STR1
RLCA
LD C,A
LD B,$00
ADD HL,BC
LD (HL),E
INC HL
LD (HL),D
JP L05C1 ; jump to END1

; ----------------------
; THE '"T" CHANNEL DATA'
; ----------------------
; the eleven-byte "T" CHANNEL descriptor.

;; TCHAN-DAT
L0B6B: DEFW $0008 ; main ERROR-1
DEFW $0008 ; main ERROR-1
DEFB $54 ; character "T"
DEFW L0C3A ; TCHAN-OUT
DEFW L0B76 ; T-INPUT
DEFW $000B ; channel length - 11 bytes.

; -------------------------------
; THE '"T" CHANNEL INPUT' ROUTINE
; -------------------------------
;

;; T-INPUT
L0B76: LD HL,L0B82 ; address of routine TCHAN-IN
JP L0D5A ; jump to CALL-INP

; -------------------------------
; THE '"B" CHANNEL INPUT' ROUTINE
; -------------------------------
;

;; B-INPUT
L0B7C: LD HL,L0B88 ; address of routine BCHAN-IN
JP L0D5A ; jump to CALL-INP

; ---------------------------------------
; THE '"T" CHANNEL INPUT SERVICE' ROUTINE
; ---------------------------------------
;

;; TCHAN-IN
L0B82: CALL L0B88 ; routine BCHAN-IN
RES 7,A
RET


; ---------------------------------------
; THE '"B" CHANNEL INPUT SERVICE' ROUTINE
; ---------------------------------------
; (Hook Code: $1D)

;; BCHAN-IN
L0B88: LD HL,$5CC7 ; sv SER_FL
LD A,(HL) ;
AND A ;
JR Z,L0B95 ; forward to REC-BYTE

LD (HL),$00 ;
INC HL ;
LD A,(HL) ;
SCF ;

RET ; Return.

; ---

;; REC-BYTE
L0B95: CALL L163E ; routine TEST-BRK

DI ; Disable Interrupts

LD A,($5CC6) ; sv IOBORD
OUT ($FE),A

LD DE,($5CC3) ; sv BAUD
LD HL,$0320 ; 800d.
LD B,D ;
LD C,E ;
SRL B ;
RR C ;
LD A,$FE ;
OUT ($EF),A ;

;; READ-RS
L0BAF: IN A,($F7) ; bit 7 is input serial data (txdata)
RLCA ;
JR NC,L0BC3 ; forward to TST-AGAIN

IN A,($F7) ;
RLCA ;
JR NC,L0BC3 ; forward to TST-AGAIN

IN A,($F7) ;
RLCA ;
JR NC,L0BC3 ; forward to TST-AGAIN

IN A,($F7) ;
RLCA ;
JR C,L0BCF ; forward to START-BIT


;; TST-AGAIN
L0BC3: DEC HL ;
LD A,H ;
OR L ;
JR NZ,L0BAF ; back to READ-RS

PUSH AF ;
LD A,$EE ;
OUT ($EF),A ;
JR L0BEE ; forward to WAIT-1

; ---

;; START-BIT
L0BCF: LD H,B ; Load HL with halved BAUD value.
LD L,C ;

LD B,$80 ; Load B with the start bit.

DEC HL ; Reduce the counter by the time for the four
DEC HL ; tests.
DEC HL ;

;; SERIAL-IN
L0BD6: ADD HL,DE ; Add the BAUD value.
NOP ; (4) a timing value.

;; BD-DELAY
L0BD8: DEC HL ; (6) Delay for 26 * BAUD
LD A,H ; (4)
OR L ; (4)
JR NZ,L0BD8 ; (12) back to BD-DELAY

ADD A,$00 ; (7) wait
IN A,($F7) ; Read a bit.
RLCA ; Rotate bit 7 to carry.
RR B ; pick up carry in B
JR NC,L0BD6 ; back , if no start bit, to SERIAL-IN

LD A,$EE ; Send CTS line low (comms data 0 also)
OUT ($EF),A ; send to serial port

LD A,B ; Transfer the received byte to A.
CPL ; Complement.
SCF ; Set Carry to signal success.
PUSH AF ; (*) push the success flag.

; The success and failure (time out) paths converge here with the HL register
; holding zero.

;; WAIT-1
L0BEE: ADD HL,DE ; (11) transfer DE (BAUD) to HL.

;; WAIT-2
L0BEF: DEC HL ; ( 6) delay for stop bit.
LD A,L ; ( 4)
OR H ; ( 4)
JR NZ,L0BEF ; (12/7) back to WAIT-2

; Register HL is now zero again.

ADD HL,DE ; HL = 0 + BAUD
ADD HL,DE ; HL = 2 * BAUD
ADD HL,DE ; HL = 3 * BAUD

; The device at the other end of the cable (not a Spectrum) may send a
; second byte even though CTS (Clear To Send) is low.

;; T-FURTHER
L0BF7: DEC HL ; Decrement counter.
LD A,L ; Test for
OR H ; zero.
JR Z,L0C34 ; forward, if no second byte, to END-RS-IN

IN A,($F7) ; Read TXdata.
RLCA ; test the bit read.
JR NC,L0BF7 ; back, if none, to T-FURTHER

; As with first byte, TXdata must be high four four tests.

IN A,($F7) ;
RLCA ;
JR NC,L0BF7 ; back to T-FURTHER

IN A,($F7) ;
RLCA ;
JR NC,L0BF7 ; back to T-FURTHER

IN A,($F7) ;
RLCA ;
JR NC,L0BF7 ; back to T-FURTHER


; A second byte is on its way and is received exactly as before.

LD H,D ;
LD L,E ;
SRL H ;
RR L ;
LD B,$80 ;
DEC HL ;
DEC HL ;
DEC HL ;

;; SER-IN-2
L0C1B: ADD HL,DE ;
NOP ; timing.

;; BD-DELAY2
L0C1D: DEC HL ;
LD A,H ;
OR L ;
JR NZ,L0C1D ; back to BD-DELAY2

ADD A,$00 ;
IN A,($F7) ;
RLCA ;
RR B ;
JR NC,L0C1B ; back to SER-IN-2

; The start bit has been pushed out and B contains the second received byte.

LD HL,$5CC7 ; Address the SER_FL System Variable.

LD (HL),$01 ; Signal there is a byte in the next location.
INC HL ; Address that location.
LD A,B ; Transfer the byte to A.
CPL ; Complement
LD (HL),A ; and insert in the second byte of serial flag.

;; END-RS-IN
L0C34: CALL L0D4D ; routine BORD-REST

POP AF ; ( either 0 and NC or the first received byte
; and the carry flag set )

EI ; Enable Interrupts

RET ; Return.

; --------------------------------
; THE '"T" CHANNEL OUTPUT' ROUTINE
; --------------------------------
; The text channel output routine is able to list programs and, when
; printing, takes correct action with TAB values etc.

;; TCHAN-OUT
L0C3A: CP $A5 ; 'RND' - first token
JR C,L0C44 ; forward, if less, to NOT-TOKEN

SUB $A5 ; reduce to range $00-5A
RST 10H ; CALBAS
DEFW $0C10 ; main PO-TOKENS
RET ; return.

; ---

;; NOT-TOKEN
L0C44: LD HL,$5C3B ; Address the FLAGS System Variable.
RES 0,(HL) ; update FLAGS - allow for leading space.
CP $20 ; compare to space
JR NZ,L0C4F ; forward, if not, to NOT-LEAD

SET 0,(HL) ; update FLAGS - signal suppress leading space.

;; NOT-LEAD
L0C4F: CP $7F ; compare to copyright symbol. (DEL in ASCII)
JR C,L0C55 ; forward, if less, to NOT-GRAPH

LD A,$3F ; output CHR$(127) and graphics as '?'

;; NOT-GRAPH
L0C55: CP $20 ; compare against space.
JR C,L0C70 ; forward to CTRL-CD

PUSH AF ; Preserve character.

INC (IY+$76) ; Increment width NMI_ADD_lo
LD A,($5CB1) ; Load A with limit from NMI_ADD_hi
CP (IY+$76) ; Compare to width NMI_ADD_lo

JR NC,L0C6C ; forward, if less or equal, to EMIT-CH

CALL L0C74 ; routine TAB-SETZ emits CR/LF.

LD (IY+$76),$01 ; Set width to one NMI_ADD_lo

;; EMIT-CH
L0C6C: POP AF ; Restore the unprinted character.

JP L0D07 ; jump to BCHAN-OUT

; ---

;; CTRL-CD
L0C70: CP $0D ; carriage return ?
JR NZ,L0C82 ; forward to NOT-CR


;; TAB-SETZ
L0C74: LD (IY+$76),$00 ; sv NMI_ADD_lo
LD A,$0D ; output a CR carriage return.
CALL L0D07 ; routine BCHAN-OUT
LD A,$0A ; output a LF line feed.
JP L0D07 ; jump to BCHAN-OUT

; ---

;; NOT-CR
L0C82: CP $06 ;
JR NZ,L0CA5 ; forward to NOT-CMM

LD BC,($5CB0) ; sv NMI_ADD
LD E,$00 ;

;; SPC-COUNT
L0C8C: INC E
INC C
LD A,C
CP B
JR Z,L0C9A ; forward to CMM-LP2


;; CMM-LOOP
L0C92: SUB $08
JR Z,L0C9A ; forward to CMM-LP2

JR NC,L0C92 ; back to CMM-LOOP

JR L0C8C ; back to SPC-COUNT


;; CMM-LP2
L0C9A: PUSH DE
LD A,$20
CALL L0C3A ; routine TCHAN-OUT
POP DE
DEC E
RET Z

JR L0C9A ; back to CMM-LP2

; ---

;; NOT-CMM
L0CA5: CP $16
JR Z,L0CB5 ; forward to TAB-PROC

CP $17
JR Z,L0CB5 ; forward to TAB-PROC

CP $10
RET C

LD DE,$0CD0
JR L0CB8 ; forward to STORE-COD

; ---

;; TAB-PROC
L0CB5: LD DE,L0CC8 ; addr: TAB-SERV

;; STORE-COD
L0CB8: LD ($5C0E),A ; sv TVDATA

;; ALTER-OUT
L0CBB: LD HL,($5C51) ; sv CURCHL
PUSH DE
LD DE,$0005
ADD HL,DE
POP DE
LD (HL),E
INC HL
LD (HL),D
RET

; ---

;; TAB-SERV
L0CC8: LD DE,L0CD0 ; addr: TAB-SERV2
LD ($5C0F),A ; sv TVDATA
JR L0CBB ; back to ALTER-OUT

; ---

;; TAB-SERV2
L0CD0: LD DE,L0C3A ; addr: TCHAN-OUT
CALL L0CBB ; routine ALTER-OUT
LD D,A
LD A,($5C0E) ; sv TVDATA
CP $16 ; AT control code ?
JR Z,L0CE6 ; forward to TST-WIDTH

CP $17 ; TAB control code ?
CCF
RET NZ

LD A,($5C0F) ; sv TVDATA
LD D,A

;; TST-WIDTH
L0CE6: LD A,($5CB1) ; sv NMI_ADD
CP D
JR Z,L0CEE ; forward to TAB-MOD

JR NC,L0CF4 ; forward to TABZERO


;; TAB-MOD
L0CEE: LD B,A
LD A,D
SUB B
LD D,A
JR L0CE6 ; back to TST-WIDTH

; ---

;; TABZERO
L0CF4: LD A,D
OR A
JP Z,L0C74 ; jump to TAB-SETZ

;; TABLOOP
L0CF9: LD A,($5CB0) ; sv NMI_ADD_lo
CP D ;
RET Z ;

PUSH DE ;
LD A,$20 ;
CALL L0C3A ; routine TCHAN-OUT
POP DE ;
JR L0CF9 ; back to TABLOOP


; --------------------------------
; THE '"B" CHANNEL OUTPUT' ROUTINE
; --------------------------------
; (Hook Code: $1E)
; The bits of a byte are sent inverted. They are fixed in length and heralded
; by a start bit and followed by two stop bits.

;; BCHAN-OUT
L0D07: LD B,$0B ; Set bit count to eleven - 1 + 8 + 2.

CPL ; Invert the bits of the character.
LD C,A ; Copy the character to C.

LD A,($5CC6) ; Load A from System Variable IOBORD
OUT ($FE),A ; Change the border colour.

LD A,$EF ; Set to %11101111
OUT ($EF),A ; Make CTS (Clear to Send) low.

CPL ; reset bit 0 (other bits of no importance)
OUT ($F7),A ; Make RXdata low. %00010000

LD HL,($5CC3) ; Fetch value from BAUD System Variable.
LD D,H ; Copy BAUD value to DE for count.
LD E,L ;

;; BD-DEL-1
L0D1C: DEC DE ; ( 6) Wait 26 * BAUD cycles
LD A,D ; ( 4)
OR E ; ( 4)
JR NZ,L0D1C ; (12) back to BD-DEL-1

;; TEST-DTR
L0D21: CALL L163E ; routine TEST-BRK allows user to stop.
IN A,($EF) ; Read the communication port.
AND $08 ; isolate DTR (Data Terminal Ready) bit.
JR Z,L0D21 ; back, until DTR found high, to TEST-DTR

SCF ; Set carry flag as the start bit.
DI ; Disable Interrupts.

;; SER-OUT-L
L0D2C: ADC A,$00 ; Set bit 0 76543210 <- C
OUT ($F7),A ; Send RXdata the start bit.

LD D,H ; Transfer the BAUD value to DE for count.
LD E,L ;

;; BD-DEL-2
L0D32: DEC DE ; ( 6) Wait for 26 * BAUD
LD A,D ; ( 4)
OR E ; ( 4)
JR NZ,L0D32 ; (12) back to BD-DEL-2

DEC DE ; ( 6)
XOR A ; ( 4) clear rxdata bit
SRL C ; shift a bit of output byte to carry.
DJNZ L0D2C ; back for 11 bits to SER-OUT-L

; Note the last two bits will have been sent reset as C is exhausted.

EI ; Enable Interrupts.

LD A,$01 ; Set RXdata

LD C,$EF ; prepare port address.
LD B,$EE ; prepare value %11101110
OUT ($F7),A ; Send RXdata high.
OUT (C),B ; Send CTS and comms data low - switch off RS232

;; BD-DEL-3
L0D48: DEC HL ; ( 6) The final 26 * BAUD delay
LD A,L ; ( 4)
OR H ; ( 4)
JR NZ,L0D48 ; (12) back to BD-DEL-3


; -----------------------------------
; THE 'BORDER COLOUR RESTORE' ROUTINE
; -----------------------------------
;

;; BORD-REST
L0D4D: PUSH AF ; Preserve the accumulator value throughout.

LD A,($5C48) ; Fetch System Variable BORDCR
AND $38 ; Mask off the paper bits.
RRCA ; Rotate to the range 0 - 7
RRCA ;
RRCA ;
OUT ($FE),A ; Change the border colour.

POP AF ; Restore accumulator and flags.

RET ; Return.


; ----------------------
; THE 'CALL-INP' ROUTINE
; ----------------------
; If the extended streams e.g. #7 are being used for input then this ROM
; will be paged in as a result of the $0008 address in the normal INPUT
; channel position. Since 'INPUT #7' or 'INKEYS #7' could have been used
; it is the purpose of this routine to determine which has been used.
; Note also that 'MOVE #7 TO #2' could also invoke this routine and that MOVE
; operations are further differentiated in the INKEY$ branch.

;; CALL-INP
L0D5A: RES 3,(IY+$02) ; update TV_FLAG - The mode is to be considered
; unchanged.
; Note. this should have been done by the Main
; ROM before entering the EDITOR.

PUSH HL ; (*) Preserve HL the address of the actual
; service routine - either NCHAN_IN, MCHAN_IN,
; BCHAN_IN ot T_CHAN_IN.

LD HL,($5C3D) ; Fetch address of Error Stack Pointer ERR_SP

LD E,(HL) ; Extract the address of the error handler
INC HL ; If INPUT is being used this will be
LD D,(HL) ; address $107F in the Main ROM.

AND A ; Prepare to subtract.

LD HL,$107F ; address of ED-ERROR in the Main ROM

SBC HL,DE ; subtract from test value.

JR NZ,L0D98 ; forward if not in EDITOR to INKEY$

; continue to handle INPUT from a stream.

POP HL ; (*) POP service routine to HL e.g. NCHAN_IN

LD SP,($5C3D) ; set Stack Pointer from System Variable ERR_SP
POP DE ; discard the known ED-ERROR address $107F.
POP DE ; POP the next value in hierarchy - MAIN-4
; (usually).
LD ($5C3D),DE ; and set the system variable ERR_SP

;; IN-AGAIN.
L0D78: PUSH HL ; Push the address of the service routine
; e.g. NCHAN_IN on the machine stack.

LD DE,L0D7E ; addr: IN-AG-RET (below)
PUSH DE ; push this address

JP (HL) ; jump to the service routine either MCHAN_IN,
; NCHAN_IN, BCHAN_IN or TCHAN_IN and then return
; to the next address IN-AG-RET.

; ---

;; IN-AG-RET
L0D7E JR C,L0D8A ; forward with acceptable codes to ACC-CODE

JR Z,L0D87 ; forward with time-out to NO-READ

; Otherwise Iris has closed her channel or the Microdrive file was exhausted.

;; OREPORT-8
L0D82: LD (IY+$00),$07 ; set ERR_NR to '8 End of file'
RST 28H ; Error Main ROM.

; ---

;; NO-READ
L0D87: POP HL ; Retrieve the address of teh service routine
; and try again as always for INPUT.
JR L0D78 ; back to IN-AGAIN.

; ---

;; ACC-CODE
L0D8A: CP $0D ; Is the acceptable code ENTER?
JR Z,L0D94 ; forward, if so, to END-INPUT

RST 10H ; CALBAS - Call the Base ROM.
DEFW $0F85 ; main ADD-CHRX
; A special entry point within ADD-CHAR to add
; the character to WORKSPACE.

POP HL ; Retrieve the address of the saved service
; routine.
JR L0D78 ; back for another character to IN-AGAIN.

; ---

;; END-INPUT
L0D94: POP HL ; Discard the service routine.
JP L0700 ; jump to UNPAGE

; -------------------
; THE 'INKEY$' BRANCH
; -------------------

;; INKEY$
L0D98: POP HL ; (*) POP service routine to HL e.g. NCHAN_IN
LD DE,L0D9E ; ret addr. INK-RET (below)
PUSH DE ; push this address for the return address.

JP (HL) ; jump to the service routine either MCHAN_IN,
; NCHAN_IN, BCHAN_IN or TCHAN_IN and then return
; to the next address IN-AG-RET.

; ---

;; INK-RET
L0D9E RET C ; Return with acceptable character.

RET Z ; Return with no character.

BIT 4,(IY+$7C) ; sv FLAGS_3 MOVE?
JR Z,L0D82 ; back to OREPORT-8

OR $01 ;
RET ; return with zero and carry reset.


; ***********************************************
; ** T H E N E T W O R K R O U T I N E S **
; ***********************************************

; -------------------------------
; THE '"N" CHANNEL INPUT' ROUTINE
; -------------------------------
;

;; N-INPUT
L0DA9: LD HL,L0DAF ; Address: NCHAN-IN
JP L0D5A ; jump to CALL-INP

; ---------------------------------------
; THE '"N" CHANNEL INPUT SERVICE' ROUTINE
; ---------------------------------------
;

;; NCHAN-IN
L0DAF: LD IX,($5C51) ; sv CURCHL
LD A,(IX+$10) ; NCOBL
AND A
JR Z,L0DBB ; forward to TEST-BUFF

RST 20H ; Shadow Error Restart
DEFB $0D ; Reading a 'write' file

; ---

;; TEST-BUFF
L0DBB: LD A,(IX+$14) ; NCIBL
AND A
JR Z,L0DD5 ; forward to TST-N-EOF

LD E,(IX+$13) ; NCCUR
DEC A
SUB E
JR C,L0DD5 ; forward to TST-N-EOF

LD D,$00
INC E
LD (IX+$13),E ; NCCUR
ADD IX,DE
LD A,(IX+$14) ;
SCF
RET

; ---

;; TST-N-EOF
L0DD5: LD A,(IX+$0F) ; NCTYPE
AND A
JR Z,L0DDC ; forward to GET-N-BUF

RET

; ---

;; GET-N-BUF
L0DDC: LD A,($5CC6) ; sv IOBORD
OUT ($FE),A
DI

;; TRY-AGAIN
L0DE2: CALL L0FD3 ; routine WT-SC-E
JR NC,L0DFC ; forward to TIME-OUT

CALL L0EB5 ; routine GET-NBLK
JR NZ,L0DFC ; forward to TIME-OUT

EI

CALL L0D4D ; routine BORD-REST

LD (IX+$13),$00 ; NCCUR
LD A,($5CD2) ; sv NTTYPE
LD (IX+$0F),A ; NCTYPE
JR L0DBB ; back to TEST-BUFF

; ---

;; TIME-OUT
L0DFC: LD A,(IX+$0B) ; NCIRIS
AND A ;
JR Z,L0DE2 ; back to TRY-AGAIN

EI ;
CALL L0D4D ; routine BORD-REST
AND $00 ;
RET ;


; --------------------------------
; THE '"N" CHANNEL OUTPUT' ROUTINE
; --------------------------------
;

;; NCHAN-OUT
L0E09: LD IX,($5C51) ; sv CURCHL
LD B,A
LD A,(IX+$14) ; NCIBL
AND A
LD A,B
JR Z,L0E17 ; forward to TEST-OUT

RST 20H ; Shadow Error Restart
DEFB $0C ; Writing to a 'read' file

;; TEST-OUT
L0E17: LD E,(IX+$10) ; NCOBL
INC E ;
JR NZ,L0E25 ; forward to ST-BF-LEN

PUSH AF ;
XOR A ;
CALL L0E48 ; routine S-PACK-1
POP AF ;
LD E,$01 ;

;; ST-BF-LEN
L0E25: LD (IX+$10),E ; NCOBL
LD D,$00 ;
ADD IX,DE ;
LD (IX+$14),A ; NCIBL
RET ;


; -----------------------
; THE 'OUT-BLK-N' ROUTINE
; -----------------------
;

;; OUT-BLK-N
L0E30: CALL L1082 ; routine OUTPAK
LD A,(IX+$0B) ; NCIRIS
AND A ;
RET Z ;

LD HL,$5CCD ; sv NTRESP
LD (HL),$00 ;
LD E,$01 ;
CALL L104F ; routine INPAK
RET NZ ;

LD A,($5CCD) ; sv NTRESP
DEC A ;
RET ;


; ----------------------
; THE 'S-PACK-1' ROUTINE
; ----------------------
;

;; S-PACK-1
L0E48: CALL L0E4F ; routine SEND-PACK
RET NZ ;

JP L0EAC ; jump to BR-DELAY

; -----------------------
; THE 'SEND-PACK' ROUTINE
; -----------------------
; (Hook Code: $30)

;; SEND-PACK
L0E4F: LD (IX+$0F),A ; NCTYPE
LD B,(IX+$10) ; NCOBL
LD A,($5CC6) ; sv IOBORD
OUT ($FE),A ;

PUSH IX ;
POP DE ;

LD HL,$0015 ;
ADD HL,DE ;
XOR A ;

;; CHKS1
L0E62: ADD A,(HL) ;
INC HL ;
DJNZ L0E62 ; back to CHKS1

LD (IX+$11),A ; NCDCS
LD HL,$000B ;
ADD HL,DE ;
PUSH HL ;
LD B,$07 ;
XOR A ;

;; CHKS2
L0E71: ADD A,(HL) ;
INC HL ;
DJNZ L0E71 ; back to CHKS2

LD (HL),A ;
DI ;

;; SENDSCOUT
L0E77: CALL L101E ; routine SEND-SC
POP HL ;
PUSH HL ;
LD E,$08 ;
CALL L0E30 ; routine OUT-BLK-N
JR NZ,L0E77 ; back to SENDSCOUT

PUSH IX ;
POP HL ;

LD DE,$0015 ;
ADD HL,DE ;
LD E,(IX+$10) ; NCOBL
LD A,E ;
AND A ;
JR Z,L0E9A ; forward to INC-BLKN

LD B,$20 ;

;; SP-DL-1
L0E93: DJNZ L0E93 ; back to SP-DL-1

CALL L0E30 ; routine OUT-BLK-N
JR NZ,L0E77 ; back to SENDSCOUT


;; INC-BLKN
L0E9A: INC (IX+$0D) ; NCNUMB
JR NZ,L0EA2 ; forward to SP-N-END

INC (IX+$0E) ; NCNUMB_hi

;; SP-N-END
L0EA2: POP HL ;
CALL L0D4D ; routine BORD-REST
EI ;
LD A,(IX+$0B) ; NCIRIS
AND A ;
RET ;


; ----------------------
; THE 'BR-DELAY' ROUTINE
; ----------------------
;

;; BR-DELAY
L0EAC: LD DE,$1500 ;

;; DL-LOOP
L0EAF: DEC DE ;
LD A,E ;
OR D ;
JR NZ,L0EAF ; back to DL-LOOP

RET ;


; ---------------------------------------------
; THE 'HEADER AND DATA BLOCK RECEIVING' ROUTINE
; ---------------------------------------------
;

;; GET-NBLK
L0EB5: LD HL,$5CCE ; sv NTDEST
LD E,$08
CALL L104F ; routine INPAK
RET NZ

LD HL,$5CCE ; sv NTDEST
XOR A
LD B,$07

;; CHKS3
L0EC4: ADD A,(HL)
INC HL
DJNZ L0EC4 ; back to CHKS3

CP (HL)
RET NZ

LD A,($5CCE) ; sv NTDEST
AND A
JR Z,L0EDD ; forward to BRCAST

CP (IX+$0C) ; NCSELF
RET NZ

LD A,($5CCF) ; sv NTSRCE
CP (IX+$0B) ; NCIRIS
RET NZ

JR L0EE2 ; forward to TEST-BLKN

; ---

;; BRCAST
L0EDD: LD A,(IX+$0B) ; NCIRIS
OR A
RET NZ


;; TEST-BLKN
L0EE2: LD HL,($5CD0) ; sv NTNUMB
LD E,(IX+$0D) ; NCNUMB_lo
LD D,(IX+$0E) ; NCNUMB_hi
AND A
SBC HL,DE
JR Z,L0F02 ; forward to GET-NBUFF

DEC HL
LD A,H
OR L
RET NZ

; Note. The return status of the next routine should really be checked.

CALL L0F02 ; routine GET-NBUFF

; Note. The DEC instruction does not affect the carry flag.

DEC (IX+$0D) ; NCNUMB_lo
JR NC,L0EFF ; forward, with no carry, to GETNB-END !!

DEC (IX+$0E) ; NCNUMB_hi

;; GETNB-END
L0EFF: OR $01
RET


;; GET-NBUFF
L0F02: LD A,($5CCE) ; sv NTDEST
OR A
CALL NZ,L107B ; routine SEND-RESP
LD A,($5CD3) ; sv NTLEN
AND A
JR Z,L0F30 ; forward to STORE-LEN

PUSH IX
POP HL

LD DE,$0015
ADD HL,DE
PUSH HL
LD E,A
CALL L104F ; routine INPAK
POP HL
RET NZ

LD A,($5CD3) ; sv NTLEN
LD B,A
LD A,($5CD4) ; sv NTDCS

;; CHKS4
L0F24: SUB (HL)
INC HL
DJNZ L0F24 ; back to CHKS4

RET NZ

LD A,($5CCE) ; sv NTDEST
AND A
CALL NZ,L107B ; routine SEND-RESP

;; STORE-LEN
L0F30: LD A,($5CD3) ; sv NTLEN
LD (IX+$14),A ; NCIBL
INC (IX+$0D) ; NCNUMB_lo
JR NZ,L0F3E ; forward to GETBF-END

INC (IX+$0E) ; NCNUMB_hi

;; GETBF-END
L0F3E: CP A
RET


; --------------------------------------
; THE 'OPEN "N" CHANNEL COMMAND' ROUTINE
; --------------------------------------
;

;; OPEN-N-ST
L0F40: CALL L0F52 ; routine OP-PERM-N
JP L0B51 ; jump to OP-STREAM

; ----------------------------------------
; THE 'OPEN TEMPORARY "N" CHANNEL' ROUTINE
; ----------------------------------------
; (Hook Code: $2D)
;

;; OP-TEMP-N
L0F46: CALL L0F52 ; routine OP-PERM-N
LD IX,($5C51) ; sv CURCHL
SET 7,(IX+$04) ; channel letter
RET


; ----------------------------------------
; THE 'OPEN PERMANENT "N" CHANNEL' ROUTINE
; ----------------------------------------
;

;; OP-PERM-N
L0F52: LD HL,($5C53) ; sv PROG
DEC HL

LD BC,$0114
PUSH BC
PUSH HL
PUSH BC
LD HL,($5C65) ; sv STKEND
ADD HL,BC
JP C,L0F9E ; jump to OUTMEM

LD BC,$0050
ADD HL,BC
JP C,L0F9E ; jump to OUTMEM

SBC HL,SP
JP NC,L0F9E ; jump to OUTMEM

POP BC
POP HL

RST 10H ; CALBAS
DEFW $1655 ; main MAKE-ROOM

INC HL
POP BC
CALL L1A82 ; routine REST-N-AD
LD ($5C51),HL ; sv CURCHL

EX DE,HL ;
LD HL,L0FA3 ; NCHAN-DAT
LD BC,$000B ; eleven bytes.
LDIR ;

LD A,($5CD6) ; sv D_STR1 drive number
LD (DE),A
INC DE
LD A,($5CC5) ; sv NTSTAT
LD (DE),A
INC DE
XOR A
LD (DE),A
LD H,D
LD L,E
INC DE
LD BC,$0106
LDIR
LD DE,($5C51) ; sv CURCHL
RET

; ---


;; OUTMEM
L0F9E: LD (IY+$00),$03 ; sv ERR_NR
RST 28H ; Error Main ROM


; ------------------------------
; THE '"N" CHANNEL DATA' ROUTINE
; ------------------------------
;

;; NCHAN_DAT
L0FA3: DEFW $0008 ; main ERROR-1
DEFW $0008 ; main ERROR-1
DEFB $4E ; character "N"
DEFW L0E09 ; NCHAN-OUT
DEFW L0DA9 ; N-INPUT
DEFW $0114 ; length


; ---------------------------------------
; THE 'SEND EOF BLOCK TO NETWORK' ROUTINE
; ---------------------------------------
;

;; SEND-NEOF
L0FAE: LD IX,($5C51) ; sv CURCHL
LD A,(IX+$10) ; NCOBL
AND A ;
RET Z ;

LD A,$01 ;
JP L0E48 ; jump to S-PACK-1

; ---------------------------
; THE 'NETWORK STATE' ROUTINE
; ---------------------------
;

;; NET-STATE
L0FBC: LD A,R ;
OR $C0 ;
LD B,A ;
CALL L0FC7 ; routine CHK-REST
JR C,L0FBC ; back to NET-STATE

RET ;


; ---------------------------
; THE 'CHECK-RESTING' ROUTINE
; ---------------------------
;

;; CHK-REST
L0FC7: CALL L163E ; routine TEST-BRK

;; MAKESURE
L0FCA: PUSH BC ;
POP BC ;
IN A,($F7) ;
RRCA ;
RET C ;

DJNZ L0FCA ; back to MAKESURE

RET ;


; ------------------------
; THE 'WAIT-SCOUT' ROUTINE
; ------------------------
;

;; WT-SC-E
L0FD3: CALL L163E ; routine TEST-BRK
LD HL,$01C2 ;

;; CLAIMED
L0FD9: LD B,$80 ;
CALL L0FC7 ; routine CHK-REST
JR NC,L0FED ; forward to WT-SYNC

DEC HL ;
DEC HL ;
LD A,H ;
OR L ;
JR NZ,L0FD9 ; back to CLAIMED

LD A,(IX+$0B) ; NCIRIS
AND A ;
JR Z,L0FD9 ; back to CLAIMED

RET ;


;; WT-SYNC
L0FED: IN A,($F7) ;
RRCA ;
JR C,L1013 ; forward to SCOUT-END

LD A,$7F ;
IN A,($FE) ;
OR $FE ;
IN A,($FE) ;
RRA ;
CALL NC,L163E ; routine TEST-BRK
DEC HL ;
LD A,H ;
OR L ;
JR NZ,L0FED ; back to WT-SYNC

LD A,(IX+$0B) ; NCIRIS
AND A ;
JR Z,L0FED ; back to WT-SYNC

RET ;


; --------------------------------------
; THE 'BREAK INTO I/O OPERATION' ROUTINE
; --------------------------------------
; Note. an obsolete duplicate.

;; E-READ-N
L100A: EI ;
CALL L0D4D ; routine BORD-REST
LD (IY+$00),$14 ; sv ERR_NR
RST 28H ; Error Main ROM

; ----------------------
; THE 'SCOUT END' BRANCH
; ----------------------
;

;; SCOUT-END
L1013: LD L,$09

;; LP-SCOUT
L1015: DEC L
SCF
RET Z

LD B,$0E

;; DELAY-SC
L101A: DJNZ L101A ; back to DELAY-SC

JR L1015 ; back to LP-SCOUT


; ------------------------
; THE 'SEND-SCOUT' ROUTINE
; ------------------------
;

;; SEND-SC
L101E: CALL L0FBC ; routine NET-STATE
LD C,$F7
LD HL,$0009
LD A,($5CC5) ; sv NTSTAT
LD E,A
IN A,($F7)
RRCA
JR C,L101E ; back to SEND-SC


;; ALL-BITS
L102F: OUT (C),H
LD D,H
LD H,$00
RLC E
RL H


LD B,$08

;; S-SC-DEL
L103A: DJNZ L103A ; back to S-SC-DEL

IN A,($F7)
AND $01
CP D
JR Z,L101E ; back to SEND-SC

DEC L
JR NZ,L102F ; back to ALL-BITS

LD A,$01
OUT ($F7),A
LD B,$0E

;; END-S-DEL
L104C: DJNZ L104C ; back to END-S-DEL

RET


; -------------------
; THE 'INPAK' ROUTINE
; -------------------
;

;; INPAK
L104F: LD B,$FF

;; N-ACTIVE
L1051: IN A,($F7)
RRA
JR C,L105A ; forward to INPAK-2

DJNZ L1051 ; back to N-ACTIVE

INC B
RET

; ---

;; INPAK-2
L105A: LD B,E

;; INPAK-L
L105B: LD E,$80
LD A,$CE
OUT ($EF),A
NOP
NOP
INC IX
DEC IX
INC IX
DEC IX

;; UNTIL-MK
L106B: LD A,$00
IN A,($F7)
RRA
RR E
JP NC,L106B ; jump to UNTIL-MK
LD (HL),E
INC HL
DJNZ L105B ; back to INPAK-L

CP A
RET


; --------------------------------
; THE 'SEND RESPONSE BYTE' ROUTINE
; --------------------------------
;

;; SEND-RESP
L107B: LD A,$01
LD HL,$5CCD ; sv NTRESP
LD (HL),A
LD E,A

; --------------------
; THE 'OUTPAK' ROUTINE
; --------------------
;

;; OUTPAK
L1082: XOR A
OUT ($F7),A
LD B,$04

;; DEL-0-1
L1087: DJNZ L1087 ; back to DEL-0-1


;; OUTPAK-L
L1089: LD A,(HL)
CPL
SCF
RLA
LD B,$0A

;; UNT-MARK
L108F: OUT ($F7),A
RRA
AND A
DEC B
LD D,$00
JP NZ,L108F ; jump to UNT-MARK
INC HL
DEC E
PUSH HL
POP HL
JP NZ,L1089 ; jump to OUTPAK-L
LD A,$01
OUT ($F7),A
RET

; *****************************************************
; ** T H E M I C R O D R I V E R O U T I N E S **
; *****************************************************
; The shadow ROM uses the alternate HL register solely in connection with the
; Microdrive maps. This does not conflict with the Main ROM use in the
; calculator. When used as a Hook Codes, then the calculator is implicitly in
; use by the user and so HL' should be preserved throughout.

; -----------------------------------------
; THE 'SET A TEMPORARY "M" CHANNEL' ROUTINE
; -----------------------------------------
; (Hook Code: $2B)
; This routine is used to create all Microdrive channels. The routine that
; creates a permanent channel (as used by a print file) uses this routine and
; then converts the temporary channel to a permanent one.
; Temporary channels are created by LOAD, SAVE, CAT etc. and last just as long
; as required. They are deleted before returning to the Main ROM by the next
; routine DEL-M-BUF.

;; SET-T-MCH
L10A5: EXX ; exx
LD HL,$0000 ; set HL' to zero as the default no-map-exists
; condition.
EXX ; exx

LD IX,($5C4F) ; set IX from system variable CHANS.
LD DE,$0014 ; skip over the twenty bytes of the standard
ADD IX,DE ; channels to point to the next or end-marker.

; now enter a search of existing "M" channels to see if any use the same drive.

;; CHK-LOOP
L10B3: LD A,(IX+$00) ; fetch the next byte.
CP $80 ; compare to end-marker.
JR Z,L10F1 ; forward, if so, to CHAN-SPC.

LD A,(IX+$04) ; fetch the letter of the extended channel.
AND $7F ; reset bit 7.
CP $4D ; is it character 'M' ?
JR NZ,L10E7 ; forward, if not, to NEXT-CHAN.

; an existing Microdrive Channel has been found.

LD A,($5CD6) ; fetch drive number from system variable D_STR1
CP (IX+$19) ; compare to CHDRIV the drive associated with
; this channel.
JR NZ,L10E7 ; forward, if not the same, to NEXT-CHAN.

; a Microdrive Channel has been found that matches the current drive.
; It will not be necessary to create a new map for the temporary channel.

EXX ; -
LD L,(IX+$1A) ; load address of the associated Microdrive.
LD H,(IX+$1B) ; map into the HL' register.
EXX ; -

LD BC,($5CDA) ; load BC with length of filename from N_STR1.
LD HL,($5CDC) ; load HL with address of filename.

CALL L1403 ; routine CHK-NAME checks name in channel
; against name addressed by HL.

JR NZ,L10E7 ; forward, with name mismatch, to NEXT-CHAN.

BIT 0,(IX+$18) ; test CHFLAG.
JR Z,L10E7 ; forward to NEXT-CHAN.

RST 20H ; Shadow Error Restart.
DEFB $0D ; Reading a 'write' file.

;; NEXT-CHAN
L10E7: LD E,(IX+$09) ; fetch length of channel.
LD D,(IX+$0A) ; to the DE register pair.
ADD IX,DE ; add to point to the following location.
JR L10B3 ; loop back to CHK-LOOP until end-marker found.

; ---

; Now create the space for the channel.

;; CHAN-SPC
L10F1: LD HL,($5C53) ; set pointer from system variable PROG.
DEC HL ; now points to channels end-marker (as does IX)

PUSH HL ; * save a copy of new location.
LD BC,$0253 ; set amount of bytes required.

; Note. interrupts are disabled so on the original shadow ROM, which launched
; straight into the MAKE-ROOM routine, the system hung if there was
; insufficient free memory, at the HALT instruction in the Main error report.
; The solution here is to perform the same checks that will be performed by
; the Main MAKE-ROOM routine.

PUSH HL ; save first location
PUSH BC ; and amount while free memory is checked.

LD HL,($5C65) ; fetch start of free memory from STKEND
ADD HL,BC ; add bytes required producing carry if
; result is higher than 65535
JP C,L119A ; jump, if so, to OUTMEM2

LD BC,$0050 ; now allow for overhead of eighty bytes.
ADD HL,BC ; and perform same test.
JP C,L119A ; jump, if too high, to OUTMEM2

SBC HL,SP ; finally test that result is less than the
; stack pointer at the other side of free memory.
JP NC,L119A ; jump, if higher, to OUTMEM2.

POP BC ; restore the new room
POP HL ; parameters.

; now call the MAKE-ROOM routine in the certain knowledge that nothing can
; go wrong.

RST 10H ; CALBAS
DEFW $1655 ; main MAKE-ROOM

POP DE ; * restore pointer to first new location.
PUSH DE ; * and save on machine stack again.

LD HL,L14B1 ; the default "M" CHANNEL DATA.
LD BC,$0019 ; twenty five bytes to copy including blank
LDIR ; filename to start of new channel.

LD A,($5CD6) ; fetch drive number from D_STR1.
LD (IX+$19),A ; insert at CHDRIV.

LD BC,$0253 ; set BC to amount of room that was created.

PUSH IX ; move start of channel
POP HL ; to HL register.

CALL L1A82 ; routine REST-N-AD corrects filename pointers
; leaving DE at first filename D_STR1.

EX DE,HL ; transfer filename pointer to HL.

LD BC,($5CDA) ; set BC to length of filename from N_STR1.

BIT 7,B ; test for the default $FF bytes.
JR NZ,L1143 ; forward, with no name, to TEST-MAP

; now enter a loop to transfer the filename to CHNAME, counting BC down to zero.
; The filename could be in ROM with 'run' or more usually in string workspace
; with its parameters on the calculator stack as with
; LOAD * "m";1;"crapgame"
; SAVE * "M";7; CHR$0 + "secret".


;; T-CH-NAME
L1135: LD A,B ; check length
OR C ; for zero.
JR Z,L1143 ; forward, if so, to TEST-MAP.

LD A,(HL) ; fetch character of filename.
LD (IX+$0E),A ; transfer to same position in CHNAME.

INC HL ; increment
INC IX ; both pointers.
DEC BC ; decrement length.
JR L1135 ; loop back to T-CH-NAME.

; ---

;; TEST-MAP
L1143: POP IX ; * restore pointer to first location of channel.

EXX ; exchange set - no need now to keep balanced.
LD A,H ; test map address for zero .
OR L ; indicating that this drive has no map.
JR NZ,L1168 ; forward, if map exists, to ST-MAP-AD.

; a Microdrive map is now created for this drive.

LD HL,($5C4F) ; set pointer from system variable CHANS.
PUSH HL ; save this pointer to the new area.
DEC HL ; set HL to location before new room.
LD BC,$0020 ; thirty two bytes are required.

RST 10H ; CALBAS
DEFW $1655 ; main MAKE-ROOM.

; now handle dynamic pointers outside the control of the Main ROM

POP HL ; restore pointer to first location.
LD BC,$0020 ; thirty two bytes were created.
ADD IX,BC ; channel was moved up so adjust that pointer.

CALL L1A82 ; routine REST-N-AD corrects filename pointers.

; fill map with $FF bytes

LD A,$FF ; the fill byte.
LD B,$20 ; thirty two locations.
PUSH HL ; save map address pointer.

;; FILL-MAP
L1163: LD (HL),A ; insert the byte
INC HL ; next location.
DJNZ L1163 ; loop back to FILL-MAP

POP HL ; restore address.

;; ST-MAP-AD
L1168: LD (IX+$1A),L ; place map address in
LD (IX+$1B),H ; channel at CHMAP.

; now make DE point to IX+$19 the header preamble and copy ROM preamble bytes.

PUSH IX ; push start of channel
POP HL ; pop to HL

LD DE,$001C ; the offset is $1C
ADD HL,DE ; add to point to start of header preamble.
EX DE,HL ; transfer this destination to DE.

LD HL,L14CA ; point HL to PREAMBLE data in this ROM.
LD BC,$000C ; twelve bytes to copy to channel.
LDIR ; in they go.

; now use the same technique to copy the same 12 bytes of ROM preamble
; to IX+$37, the data block preamble in the channel.
; A little long-winded as the destination only requires adjustment.

PUSH IX ;
POP HL ;
LD DE,$0037 ;
LD BC,$000C ;

ADD HL,DE ;
EX DE,HL ;
LD HL,L14CA ; the PREAMBLE data.
LDIR ;

; now form the offset from CHANS to this channel for a return value to be
; inserted in the STRMS area.

PUSH IX ; transfer
POP HL ; pointer.

LD DE,($5C4F) ; fetch start of CHANS area from CHANS
OR A ; clear carry for subtraction.
SBC HL,DE ; the true offset.

INC HL ; add one as the offset is to second location.

RET ; return. >>>

; ---

;; OUTMEM2
L119A: LD (IY+$00),$03 ; set ERR_NR for '4 Out of memory'
RST 28H ; Error Main ROM

; ---------------------------------
; THE 'RECLAIM "M" CHANNEL' ROUTINE
; ---------------------------------
; (Hook Code: $2C)
; This routine is used to reclaim a temporary "M" channel such as that created
; by the routine above and to reclaim a permanent "M" channel by the CLOSE
; command routines.

;; DEL-M-BUF
L119F: LD L,(IX+$1A) ; fetch map address.
LD H,(IX+$1B) ; from CHMAP.
PUSH HL ; and save.
LD A,(IX+$19) ; fetch drive number from CHDRIV.
PUSH AF ; and save also.

PUSH IX ; transfer channel base address
POP HL ; to the HL register pair.

LD BC,$0253 ; set BC to bytes to reclaim.

RST 10H ; CALBAS
DEFW $19E8 ; main RECLAIM-2 reclaims the channel.

PUSH IX ; transfer channel
POP HL ; base address again.

LD DE,($5C4F) ; set DE to start of channels from CHANS
OR A ; clear carry.
SBC HL,DE ; subtract to form the offset.
INC HL ; add 1 as points to second byte.

LD BC,$0253 ; set the number of bytes reclaimed.

CALL L1444 ; routine REST-STRM corrects all stream offsets
; in the standard systems variables area
; reducing them if they followed the deleted
; channel.

POP AF ; restore drive number
POP HL ; and old map address.

; now consider deleting the map if it was used only by the reclaimed channel.

LD B,A ; transfer drive to B
LD IX,($5C4F) ; set IX from CHANS
LD DE,$0014 ; prepare to step over the twenty standard bytes
ADD IX,DE ; to address next channel or end-marker.

;; TEST-MCHL
L11D0: LD A,(IX+$00) ; fetch current byte.
CP $80 ; compare to end-marker.
JR Z,L11EF ; forward, with match, to RCLM-MAP

LD A,(IX+$04) ; fetch the channel letter.
AND $7F ; cancel any inverted bit.
CP $4D ; is character "M" ?
JR NZ,L11E5 ; forward, if not, to NXTCHAN

LD A,(IX+$19) ; fetch this channel drive number.
CP B ; compare to that of deleted channel.
RET Z ; return with match - the Microdrive map is
; still in use. >>

; else continue search.

;; NXTCHAN
L11E5: LD E,(IX+$09) ; fetch length of channel
LD D,(IX+$0A) ; to DE register.
ADD IX,DE ; add to address next channel.
JR L11D0 ; loop back to TEST-MCHL

; ---

; the branch was here when the end-marker was encountered without finding a
; channel that uses the map.

;; RCLM-MAP
L11EF: LD BC,$0020 ; thirty two bytes to reclaim.
PUSH HL ; save pointer to start.
PUSH BC ; save the 32 bytes.

RST 10H ; CALBAS
DEFW $19E8 ; main RECLAIM-2 reclaims the Microdrive map.

POP BC ; restore 32 counter.
POP HL ; restore map address.

CALL L1476 ; routine REST-MAP adjusts all channel map
; addresses.

RET ; return.

; -------------------------------
; THE '"M" CHANNEL INPUT' ROUTINE
; -------------------------------
;

;; M-INPUT
L11FD: LD IX,($5C51) ; sv CURCHL
LD HL,L1207 ; addr: MCHAN-IN
JP L0D5A ; jump to CALL-INP

; ---------------------------------------
; THE '"M" CHANNEL INPUT SERVICE' ROUTINE
; ---------------------------------------
;

;; MCHAN-IN
L1207: BIT 0,(IX+$18) ; test CHFLAG
JR Z,L120F ; forward, if reset, to TEST-M-BF

;; rwf-err
L120D: RST 20H ; Shadow Error Restart
DEFB $0D ; Reading a 'write' file

; ---

;; TEST-M-BF
L120F: LD E,(IX+$0B) ; load DE with the offset from CHDATA of the
LD D,(IX+$0C) ; next byte to be received from CHBYTE.

LD L,(IX+$45) ; load HL with the number of data bytes
LD H,(IX+$46) ; in CHDATA from RECLEN.

SCF ; set carry to include
SBC HL,DE ; subtract the two relative positions.
JR C,L1233 ; forward to CHK-M-EOF

INC DE ; else increment pointer.
LD (IX+$0B),E ; store back
LD (IX+$0C),D ; in CHBYTE.
DEC DE ; decrement pointer.

PUSH IX ; save start of channel.
ADD IX,DE ; add the offset within CHDATA first.
LD A,(IX+$52) ; now apply offset of CHDATA from start of
; channel to character.
POP IX ; restore channel start.
SCF ; set carry flag.
RET ; return.

; ---

;; CHK-M-EOF
L1233: BIT 1,(IX+$43) ; bit 1 of RECFLG is set if this is the last
; record in this file.
JR Z,L123D ; forward, if not EOF, to NEW-BUFF.

XOR A ; set accumulator to zero.
ADD A,$0D ; add to carriage return clearing the
; carry flag and resetting the zero flag.
RET ; return.

; ---

;; NEW-BUFF
L123D: LD DE,$0000 ; set next byte offset to zero.
LD (IX+$0B),E ; and update the
LD (IX+$0C),D ; pointer CHBYTE.

INC (IX+$0D) ; increment record number CHREC.
CALL L1252 ; routine GET-RECD gets the record specified
; by CHREQ matching filename CHNAME from the
; cartridge in the drive CHDRIV which is
; started.

XOR A ; signal stop all motors.
CALL L1532 ; routine SEL-DRIVE.

JR L120F ; back to TEST-M-BF.

; --------------------------
; THE 'GET A RECORD' ROUTINE
; --------------------------
; This routine is used to read a specific record from a PRINT type file.
; It is called twice -
; 1) From the "M" input routine when the current record is exhausted and the
; next record is to be read in.
; 2) From Hook Code $27 READ-RANDOM.

;; GET-RECD
L1252: LD A,(IX+$19) ; get drive number from CHDRIV.
CALL L1532 ; routine SEL-DRIVE starts the motor.

; ->

;; GET-R-2
L1258: LD BC,$04FB ; set sector counter to 1275 = 255*5
LD ($5CC9),BC ; update system variable SECTOR

;; GET-R-LP
L125F: CALL L1280 ; routine G-HD-RC reads in the next header and
; matching record to pass the tape head.

JR C,L1279 ; forward, with name mismatch, to NXT-SCT

JR Z,L1279 ; forward, if not in use, to NXT-SCT

LD A,(IX+$44) ; fetch the record number 0-n from RECNUM
CP (IX+$0D) ; compare with that required in CHREC
JR NZ,L1279 ; forward, if no number match, to NXT-SCT

PUSH IX ; transfer address of Microdrive channel
POP HL ; from the IX to HL registers.

LD DE,$0052 ; offset to CHDATA
ADD HL,DE ; add to form address of start of 512 byte data
CALL L142B ; routine CHKS-BUFF
RET Z ; return if checksums match.


;; NXT-SCT
L1279: CALL L13F7 ; routine DEC-SECT
JR NZ,L125F ; loop back, if not zero, to GET-R-LP

; else produce the Error Report.

RST 20H ; Shadow Error Restart
DEFB $11 ; File not found


; ---------------------------------------
; THE 'GET HEADER AND DATA BLOCK' ROUTINE
; ---------------------------------------
; This routine fetches at random a header and matching record and sets the
; flags to indicate three possible outcomes.
;
; Zero flag set - record is not in use.
; Carry flag set - name does not match required
; Both flags reset - the name matches required.

;; G-HD-RC
L1280: CALL L13A9 ; routine GET-M-HD2 reads in and checksums
; the next 14 byte header to pass tape heads.

LD DE,$001B ; prepare the offset from header to RECFLG and
ADD HL,DE ; add to address the start of 528 byte RECORD

CALL L15EB ; routine GET-M-BUF reads in the record
; descriptor and data.
; register HL addresses RECFLG

CALL L1426 ; routine CHKS-HD-R checksums the 14 bytes
; of the record descriptor.

JR NZ,L12B1 ; forward, with error, to G-REC-ERR

BIT 0,(IX+$43) ; check RECFLG - should be reset.
JR NZ,L12B1 ; forward, if not, to G-REC-ERR

; now test descriptor for an unused record.

LD A,(IX+$43) ; load A with RECFLG - bit 1 indicates EOF
OR (IX+$46) ; combine with RECLEN_hi bit 1 set if full.
AND $02 ; test for either full record or EOF.
RET Z ; return if not with zero set and carry reset
; signaling that record is unused.

; the record is a contender for a header record.

PUSH IX ; transfer start of channel
POP HL ; to the HL register pair.

LD DE,$0047 ; offset to 10 characters of filename.
ADD HL,DE ; add so HL addresses the start of RECNAM.
LD BC,$000A ; ten bytes to compare against required CHNAME.

CALL L1403 ; routine CHK-NAME

JR NZ,L12B1 ; forward, with name mismatch, to G-REC-ERR

; else set flags to signal success before returning.

LD A,$FF ; prepare to reset zero flag
OR A ; also reset carry
RET ; return with zero reset and carry reset.

; ---

; else set carry to signal names do not match.

;; G-REC-ERR
L12B1: SCF ; set carry flag to signal failure and
; instigate another search.
RET ; return with zero reset and carry set.


; --------------------------------
; THE '"M" CHANNEL OUTPUT' ROUTINE
; --------------------------------
; labeled MWRCH in source code.

;; MCHAN-OUT
L12B3: LD IX,$FFFA
ADD IX,DE
BIT 0,(IX+$18) ; ???? CHFLAG
JR NZ,L12C1 ; forward to NOREAD

RST 20H ; Shadow Error Restart
DEFB $0C ; Writing to a 'read' file

;; NOREAD
L12C1: LD E,(IX+$0B) ; CHBYTE


LD D,(IX+$0C) ; CHBYTE_hi
PUSH IX
ADD IX,DE
LD (IX+$52),A ; indexed
POP IX
INC DE
LD (IX+$0B),E ; CHBYTE
LD (IX+$0C),D ; CHBYTE_hi
BIT 1,D ; is CHBYTE the maximum $0200 ?
RET Z ; return if not.


; ------------------------------------------
; THE 'WRITE RECORD ONTO Microdrive' ROUTINE
; ------------------------------------------
; (Hook Code: $26)
;

;; WR-RECD
L12DA: LD A,(IX+$19) ; fetch drive number.
CALL L1532 ; routine SEL-DRIVE

LD BC,$32C8 ; set BC to 13000 decimal
CALL L1652 ; routine DELAY-BC

CALL L12EE ; routine WRITE-PRC

XOR A ; signal stop motor
CALL L1532 ; routine SEL-DRIVE

RET ; return.

; -----------------------------
; THE 'WRITE RECORD' SUBROUTINE
; -----------------------------
;
;

;; WRITE-PRC
L12EE: CALL L1349 ; routine CHK-FULL.

JR NZ,L12FC ; forward, if not, to NOFULL.

CALL L119F ; routine DEL-M-BUF reclaims the buffer.

XOR A ; set accumulator to zero.
CALL L1532 ; routine SEL-DRIVE stops the motor.

RST 20H ; Shadow Error Restart.
DEFB $0F ; ' Microdrive full'

; ---

;; NOFULL
L12FC: PUSH IX ; save the pointer to channel base.
LD B,$0A ; count ten characters.

;; CP-NAME
L1300: LD A,(IX+$0E) ; copy a character of CHNAME
LD (IX+$47),A ; to RECNAM
INC IX ; increment the index pointer.
DJNZ L1300 ; loop back for all ten characters to CP-NAME

POP IX ; restore base of "M" channel.

LD C,(IX+$0B) ; fetch CHBYTE_lo
LD (IX+$45),C ; update RECLEN_lo

LD A,(IX+$0C) ; fetch CHBYTE_hi
LD (IX+$46),A ; update RECLEN_hi

LD A,(IX+$0D) ; fetch CHREC
LD (IX+$44),A ; update RECNUM

RES 0,(IX+$43) ; reset RECFLG indicating a record.

PUSH IX ; transfer channel base address
POP HL ; to the HL register.

LD DE,$0043 ; prepare offset to point to RECFLG
ADD HL,DE ; and add to address the record descriptor.

CALL L1426 ; routine CHKS-HD-R checksums the 14 bytes.

LD DE,$000F ; add extra offset to CHDATA
ADD HL,DE ; the 512 bytes of data.

CALL L142B ; routine CHKS-BUFF checksums the buffer.

PUSH IX ; Note. this code is redundant and erroneous.
POP HL ; the three registers are set up properly
LD DE,$0047 ; in the next routine.

CALL L135A ; routine SEND-BLK writes block to Microdrive
; cartridge as indicated my Microdrive map
; which is updated.

; now prepare channel for next record. accumulator could be used to set CHBYTE.

LD DE,$0000 ; set DE to zero.
LD (IX+$0B),E ; set CHBYTE_lo to zero
LD (IX+$0C),D ; set CHBYTE_hi to zero
INC (IX+$0D) ; increment the record counter CHREC

RET ; return.


; ----------------------
; THE 'CHK-FULL' ROUTINE
; ----------------------
; Check the thirty two bytes of a Microdrive map for a reset bit.

;; CHK-FULL
L1349: LD L,(IX+$1A) ; load the address of the Microdrive map
LD H,(IX+$1B) ; from CHMAP to HL.
LD B,$20 ; set counter to thirty two.

;; NXT-B-MAP
L1351: LD A,(HL) ; fetch each byte in turn.
CP $FF ; compare to the all-full indicator.
RET NZ ; return if there is a spare sector >>

INC HL ; next address.
DJNZ L1351 ; loop back to NXT-B-MAP

XOR A ; set the zero flag for failure.
RET ; return.


; ----------------------
; THE 'SEND-BLK' ROUTINE
; ----------------------
; This important routine is called from the FORMAT routine and the WRITE-PRC
; routine to write the record to the cartridge at the next available free
; sector as indicated by the Microdrive map.

;; SEND-BLK
L135A: PUSH IX ; transfer the channel
POP HL ; address to HL.

LD DE,$0037 ; offset to data preamble.
ADD HL,DE ; add to address using HL
PUSH HL ; save pointer to data block

; now enter a loop to find the header of an available record on Microdrive.
; This SEND-BLK routine is only called when there is known to be a record
; available on the tape.

;; FAILED
L1362: CALL L13A9 ; routine GET-M-HD2 gets any old header.
CALL L13C4 ; routine CHECK-MAP checks if sector is free
; on the Microdrive map.
JR NZ,L1362 ; back, if not, to FAILED.

; A usable sector has been found on the drive. HL addresses byte within map.

EX (SP),HL ; map address to stack, bring back data pointer.
PUSH BC ; preserve B the map byte mask.

IN A,($EF) ; test the drive.
AND $01 ; examine 'write protect' bit.
JR NZ,L1374 ; forward, if not protected, to NO-PRT.

RST 20H ; Shadow Error Restart.
DEFB $0E ; Drive 'write' protected

;; NO-PRT
L1374: LD A,$E6 ; xx100110
OUT ($EF),A ; enable writing.

LD BC,$0168 ; a delay value of 360 decimal.
CALL L1652 ; routine DELAY-BC pauses briefly as the
; record now approaches the tape heads.

CALL L15B3 ; routine OUT-M-BUF writes descriptor and
; data buffer.

LD A,$EE ; xx101110
OUT ($EF),A ; disable writing.

POP BC ; restore the map bit.
POP HL ; and the address of the byte within Microdrive
; map.
LD A,B ; transfer masked bit to A.
OR (HL) ; combine with status of other 7 sectors.
LD (HL),A ; update the map to show this sector is now
; used.

RET ; return.


; ------------------------
; THE 'CLOSE FILE' ROUTINE
; ------------------------
; Note. The first entry point is not used.

;; close-m
L138B: PUSH HL ;
POP IX ;

; (Hook Code: $23)

;; CLOSE-M2
L138E: BIT 0,(IX+$18) ; CHFLAG
JR Z,L139B ; forward to NOEMP

SET 1,(IX+$43) ; RECFLG
CALL L12DA ; routine WR-RECD

;; NOEMP
L139B: XOR A ;
CALL L1532 ; routine SEL-DRIVE
CALL L119F ; routine DEL-M-BUF
RET ; return after subroutine.

; ------------------------------------------
; THE 'MAIN ERROR RESTART EMULATION' ROUTINE
; ------------------------------------------

;; ERR-RS
L13A3: POP HL ;
LD A,(HL) ;
LD ($5C3A),A ; sv ERR_NR
RST 28H ; Error Main ROM

; ------------------------------------------
; THE 'FETCH HEADER FROM Microdrive' ROUTINE
; ------------------------------------------
; This routine fetches the next valid 14-byte header to pass the tape heads
; ensuring that it is a header as opposed to a record descriptor.

;; GET-M-HD2
L13A9: PUSH IX ; transfer start of channel
POP HL ; to the HL register pair.

LD DE,$0028 ; offset to HDFLAG
ADD HL,DE ; add to form first receiving location.

CALL L15E2 ; routine GET-M-HD reads 15 bytes from
; Microdrive - last is a checksum byte.

CALL L1426 ; routine CHKS-HD-R checksums the bytes.

JR NZ,L13A9 ; back, with error, to GET-M-HD2

BIT 0,(IX+$28) ; test HDFLAG should be set.
JR Z,L13A9 ; back, if not a header, to GET-M-HD2

RET ; return - with HL addressing start of header.


; ---------------------------------
; THE 'CHECK MAP BIT STATE' ROUTINE
; ---------------------------------
;

;; CHK-MAP-2
L13BF: LD E,(IX+$44) ; pick up record from RECNUM
JR L13C7 ; forward to ENTRY

; ---

;; CHECK-MAP
L13C4: LD E,(IX+$29) ; pick up sector from HDNUMB

; ->

;; ENTRY
L13C7: LD L,(IX+$1A) ; fetch address of associated
LD H,(IX+$1B) ; Microdrive map from CHMAP

; the pseudo-map routine enters here with a temporary map address.

;; ENTRY-2
L13CD: XOR A ; clear accumulator is one way to
LD D,A ; clear D in preparation for addition.
LD A,E ; transfer sector to A.
AND $07 ; and mask off lower 8 bits for later

SRL E ; returning to E,
SRL E ; divide the
SRL E ; sector or record by eight.
ADD HL,DE ; add to map base to give address of map bit.
LD B,A ; now load sector mod 8 to B and
INC B ; increment to form counter 1 - 8.

XOR A ; clear A
SCF ; and set carry bit ready to rotate in.

;; ROTATE
L13DD: RLA ; rotate left A
DJNZ L13DD ; back, while counter not zero, to ROTATE

LD B,A ; return sector bit in B.
AND (HL) ; AND accumulator with map sector byte.
RET ; return - Z = free, NZ = occupied.


; -----------------------------------
; THE 'RESET BIT IN MAP AREA' ROUTINE
; -----------------------------------
; This routine is called when opening a channel and by FORMAT, CAT and ERASE
; to mark a map bit representing a sector as available.

;; RES-B-MAP
L13E3: CALL L13C4 ; routine CHECK-MAP fetches bit mask for map
; location addressed by HL into B register.

LD A,B ; fetch sector mask with one bit set.
CPL ; complement - seven bits set and one bit reset.
AND (HL) ; combine with other sector bits.
LD (HL),A ; and update map byte resetting the bit.
RET ; return.


; ------------------------------------------
; THE 'CHECK 'PSEUDO-MAP' BIT STATE' ROUTINE
; ------------------------------------------
;

;; TEST-PMAP
L13EB: PUSH IX ;
POP HL ;

LD DE,$0052 ;
ADD HL,DE ;
LD E,(IX+$29) ; HDNUMB
JR L13CD ; back to ENTRY-2


; -------------------------------------
; THE 'DECREASE SECTOR COUNTER' ROUTINE
; -------------------------------------
;

;; DEC-SECT
L13F7: LD BC,($5CC9) ; sv SECTOR
DEC BC ;
LD ($5CC9),BC ; sv SECTOR
LD A,B ;
OR C ;
RET ;


; ------------------------
; THE 'CHECK-NAME' ROUTINE
; ------------------------
;

;; CHK-NAME
L1403: PUSH IX ; preserve original channel base address.

LD B,$0A ;

;; ALL-CHARS
L1407: LD A,(HL) ;
CP (IX+$0E) ; CHNAME
JR NZ,L1423 ; forward to CKNAM-END

INC HL ;
INC IX ;
DEC B ;
DEC C ;
JR NZ,L1407 ; back to ALL-CHARS

LD A,B ;
OR A ;
JR Z,L1423 ; forward to CKNAM-END


;; ALLCHR-2
L1418: LD A,(IX+$0E) ; CHNAME
CP $20 ;
JR NZ,L1423 ; forward to CKNAM-END

INC IX ;
DJNZ L1418 ; back to ALLCHR-2


;; CKNAM-END
L1423: POP IX ;
RET ;


; -----------------------------------------
; THE 'CALCULATE/COMPARE CHECKSUMS' ROUTINE
; -----------------------------------------
; Used for Microdrive channels only.
; While the two checksums within a Network buffer are simple 8-bit sums of
; the data, the algorithm used for the Microdrive channels is a little more
; sophisticated as it avoids the formation of the result $FF. While across the
; network a byte is as good as its neighbour, with Microdrives the value $FF
; might arise as a result of a failed read.
; The same routine is used both to prepare the checksum prior to saving and to
; calculate and compare the checksum after reading.
; The first entry point is used for the 14 bytes of HDCHK and DESCHK
; and the second entry point is used for the 512 bytes of DCHK.

;; CHKS-HD-R
L1426: LD BC,$000E ; fourteen bytes
JR L142E ; forward to CHKS-ALL

; ---
; ->

;; CHKS-BUFF
L142B: LD BC,$0200 ; 512 bytes.

; common code.

;; CHKS-ALL
L142E: PUSH HL ; save pointer to first address.
LD E,$00 ; initialize checksum to zero

;; NXT-BYTE
L1431: LD A,E ; fetch running sum
ADD A,(HL) ; add to current location.
INC HL ; point to next location.


ADC A,$01 ; avoid the value $FF.
JR Z,L1439 ; forward to STCHK

DEC A ; decrement.

;; STCHK
L1439: LD E,A ; update the 8-bit sum.

DEC BC ; reduce counter
LD A,B ; and check
OR C ; for zero.
JR NZ,L1431 ; back, if not, to NXT-BYTE

LD A,E ; fetch running sum
CP (HL) ; compare to checksum contents
LD (HL),A ; before inserting the byte.

POP HL ; restore pointer to first address.
RET ; return - with zero flag set if sums agree.


; ---------------------------------
; THE 'RESTORE STREAM DATA' ROUTINE
; ---------------------------------
; When a channel is deleted, then the streams that point to channels beyond
; that one have to have their offsets reduced by the deleted amount.
; Also a stream that exactly matches the offset to the deleted channel, and
; there could be several, will have its entry set to zero.
; On entry, HL = offset, BC = $0253

;; REST-STRM
L1444: PUSH HL ; save the offset
LD A,$10 ; maximum streams + 1
LD HL,$5C16 ; the start of the user streams area STRMS_00

;; NXT-STRM
L144A: LD ($5C5F),HL ; save stream pointer temporarily in X_PTR

LD E,(HL) ; fetch low byte of offset.
INC HL ; bump address.
LD D,(HL) ; fetch high byte of streams offset.

POP HL ; retrieve the
PUSH HL ; supplied offset.

OR A ; clear carry.
SBC HL,DE ; subtract looking for an exact match
JR NZ,L145C ; forward, if not, to NOTRIGHT

LD DE,$0000 ; else set displacement to zero.
JR L1463 ; forward to STO-DISP to close the stream.

; ---

;; NOTRIGHT
L145C: JR NC,L1469 ; forward, if entry lower, to UPD-POINT ->

; else this stream entry is to be reduced by $0253 bytes.

EX DE,HL ; streams offset to HL
OR A ; clear carry
SBC HL,BC ; reduce by 595 decimal bytes
EX DE,HL ; transfer reduced entry to DE.

;; STO-DISP
L1463: LD HL,($5C5F) ; fetch stream address from X_PTR
LD (HL),E ; and insert
INC HL ; the updated
LD (HL),D ; offset.

; ->

;; UPD-POINT
L1469: LD HL,($5C5F) ; fetch stream address from X_PTR.
INC HL ; bump - each stream entry
INC HL ; is two bytes.
DEC A ; decrement the loop counter.
JR NZ,L144A ; back, if not zero, to NXT-STRM

; else clean up and return.

LD ($5C5F),A ; set X_PTR_hi to zero resting value.
POP HL ; balance stack.
RET ; return.


; -----------------------------------
; THE 'RESTORE MAP ADDRESSES' ROUTINE
; -----------------------------------
; When a Microdrive map is reclaimed, then all the addresses of the Microdrive
; maps in the "M" channels are examined and if higher than the deleted map, the
; address is reduced by thirty two bytes.
; On entry, HL = map address, BC = $0020.

;; REST-MAP
L1476: LD BC,$0020 ; set BC to thirty two. Already done.
LD IX,($5C4F) ; load IX from system variable CHANS.
LD DE,$0014 ; there are 20 bytes of the standard 4 channels
ADD IX,DE ; add to skip these.

; now enter a loop.

;; LCHAN
L1482: LD A,(IX+$00) ; fetch first byte.
CP $80 ; is it the channels area end-marker ?
RET Z ; return if so - all maps adjusted. >>

PUSH HL ; save map address.
LD A,(IX+$04) ; fetch channel letter.
AND $7F ; reset bit 7.
CP $4D ; compare to "M"
JR NZ,L14A6 ; forward, if not, to LPEND

; a Microdrive channel has been found so compare the address of the map.

LD E,(IX+$1A) ; fetch address of the Microdrive
LD D,(IX+$1B) ; map for this channel from CHMAP.
SBC HL,DE ; subtract from that of deleted map.
JR NC,L14A6 ; forward, if is lower, to LPEND

; address of this Microdrive map is higher than the one deleted.

EX DE,HL ; transfer address to HL.
OR A ; clear carry.
SBC HL,BC ; subtract thirty two.
LD (IX+$1A),L ; and place back
LD (IX+$1B),H ; in CHMAP.

;; LPEND
L14A6: POP HL ; restore address of deleted map.
LD E,(IX+$09) ; fetch length of channel
LD D,(IX+$0A) ; to DE.
ADD IX,DE ; add to address next channel.
JR L1482 ; loop back to LCHAN.

; ------------------------------
; THE '"M" CHANNEL DEFAULT' DATA
; ------------------------------
;

;; MCH-DAT
L14B1: DEFW $0008 ; main ERROR-1
DEFW $0008 ; main ERROR-1
DEFB $CD ; inverted "M" character
DEFW L12B3 ; MCHAN-OUT
DEFW L11FD ; M-INPUT
DEFW $0253 ; length
DEFW $0000 ;
DEFB $00 ;
DEFM " " ; 10 spaces
DEFB $FF ; CHFLAG

; -------------------
; THE 'PREAMBLE DATA'
; -------------------
; The PREAMBLE consists of twelve distinctive bytes that are saved to a
; Microdrive cartridge before the data. They are not read back but allow
; the ULA of the Microdrive to recognize the start of a saved data block.

;; PREAMBLE
L14CA: DEFB $00, $00, $00
DEFB $00, $00, $00
DEFB $00, $00, $00
DEFB $00, $FF, $FF

; -------------------------------
; THE 'NOT-USED TOOLKIT' ROUTINES
; -------------------------------
; The following four routines are for debugging
; purposes during development.

; ----------------------
; THE 'DISP-HEX' ROUTINE
; ----------------------
; display a byte as two hex characters.

;; DISP-HEX
L14D6: PUSH AF ;
RRA ;
RRA ;
RRA ;
RRA ;
CALL L14DF ; routine DISP-NIB
POP AF ;

;; DISP-NIB
L14DF: AND $0F ;

CP $0A ;

JR C,L14E7 ; forward to CONV-1

ADD A,$07 ;

;; CONV-1
L14E7: ADD A,$30 ;

CALL L14F8 ; routine DISP-CH
RET ;


; -----------------------
; THE 'DISP-HEX2' ROUTINE
; -----------------------
; display a byte in hexadecimal followed by a space

;; DISP-HEX2
L14ED: PUSH AF ;
CALL L14D6 ; routine DISP-HEX
LD A,$20 ;
CALL L14F8 ; routine DISP-CH
POP AF ;
RET ;


; ---------------------
; THE 'DISP-CH' ROUTINE
; ---------------------
;

;; DISP-CH
L14F8: PUSH HL ;
PUSH DE ;
PUSH BC ;
PUSH AF ;
EXX ;
PUSH HL ;
PUSH DE ;
PUSH BC ;
PUSH AF ;
LD HL,($5C51) ; sv CURCHL
PUSH HL ;
PUSH AF ;
LD A,$02 ;
RST 10H ; CALBAS
DEFW $1601 ; main CHAN-OPEN
POP AF ;
RST 10H ; CALBAS
DEFW $0010 ; main PRINT-A


POP HL ;
LD ($5C51),HL ; sv CURCHL
POP AF ;
POP BC ;
POP DE ;
POP HL ;
EXX ;
POP AF ;
POP BC ;
POP DE ;
POP HL ;
RET ;


; ----------------------
; THE 'HEX-LINE' ROUTINE
; ----------------------
; The Master routine which displays ten bytes of memory, addressed by HL,
; in Hexadecimal followed by a CR. The thirty output characters sit
; comfortably within the 32 character display of the Spectrum.

;; HEX-LINE
L151D: PUSH HL ;
PUSH BC ;
PUSH AF ;
LD B,$0A ;

;; HEX-LINE2
L1522: LD A,(HL) ;
CALL L14ED ; routine DISP-HEX2
INC HL ;
DJNZ L1522 ; back to HEX-LINE2

LD A,$0D ;
CALL L14F8 ; routine DISP-CH
POP AF ;
POP BC ;
POP HL ;
RET ; return.


; --------------------------------
; THE 'SELECT DRIVE MOTOR' ROUTINE
; --------------------------------
; (Hook Code: $21)
; This important routine is called on over twenty occasions to activate a
; Microdrive whose number is in the accumulator, or with a parameter of
; zero, to stop all motors. It is the sole means of controlling the real
; or virtual bank of eight Microdrives.
; It is called with interrupts disabled and this condition should be in
; force when the Hook Code is used.

;; SEL-DRIVE
L1532: PUSH HL ; preserve the original HL value throughout.

CP $00 ; is the parameter zero ?
JR NZ,L153D ; forward, if not, to TURN-ON.

; The requirement is to ensure that all eight drives are switched off.

CALL L1565 ; routine SW-MOTOR with A holding zero.

EI ; Enable Interrupts.

POP HL ; restore original HL value.
RET ; return. >>


; --------------------
; THE 'TURN ON' BRANCH
; --------------------
; This route turns on a drive in the range 1 - 8. If the Hook Code has
; been erroneously invoked with a higher value, then this will be treated
; in much the same way as with zero. See later.

;; TURN-ON
L153D: DI ; Disable Interrupts.

CALL L1565 ; routine SW-MOTOR

LD HL,$1388 ; prepare decimal 5,000 delay value.

;; TON-DELAY
L1544: DEC HL ; a simple
LD A,H ; delay loop to
OR L ; let things settle down.
JR NZ,L1544 ; back, if not zero, to TON-DELAY

LD HL,$1388 ; load with five thousand again.

; Now enter another 5000 loop testing for break and searching for a GAP on
; the tape at each iteration.

;; REPTEST
L154C: LD B,$06 ; six consecutive reads required to register
; as a gap.

;; CHK-PRES
L154E: CALL L163E ; routine TEST-BRK allows the user to stop.

IN A,($EF) ; read the Microdrive port.
AND $04 ; test for the gap bit
JR NZ,L155B ; forward, if not, to NOPRES

DJNZ L154E ; loop back six times to CHK-PRES

; A gap has been found - a formatted cartridge is in the drive.

POP HL ; restore original HL value.
RET ; return with motor running, interrupts
; disabled. >>

; -------------------
; THE 'NO GAP' BRANCH
; -------------------
; If no gap signal found on drive so far then continue counting down from
; 5000 and looping back to test for six gaps.

;; NOPRES
L155B: DEC HL ; decrement the counter
LD A,H ; test for
OR L ; zero.
JR NZ,L154C ; back, if not, to REPTEST

CALL L1532 ; routine SEL-DRIVE with accumulator zero
; stops the drive motor.

RST 20H ; Shadow Error Restart
DEFB $10 ; ' Microdrive not present'

; -----------------------------
; THE 'SWITCH MOTOR' SUBROUTINE
; -----------------------------
; The main developer of the Microdrives and acknowledged co-inventor was
; the late Ben Cheese, 14-Jul-1954 - 15-Jan-2001.
;
; This ROM software always handles the switching of Microdrives as if
; there were eight drives connected. There is no short cut to directly
; switch on a drive and they must be handled as an array of eight devices.
; Each Microdrive includes a D-flip flop, capable of holding logic state
; one or zero. When the flip-flop is set at logic one then the
; recording/playback device is switched on.
;
; The first Microdrive has the D-input terminal of the flip-flop connected
; to the comms data line of the Interface 1 and the clock-input terminal
; connected to the clock-output terminal of Interface 1. Subsequent
; Microdrives have the D-input terminal connected to the Q-output terminal
; of the next innermost drive/flip-flop and the CLOCK-input terminal
; connected to the CLOCK-input terminal of the same adjacent
; drive/flip-flop.
;
; The eight Microdrives thus behave as a shift register allowing a logic 1
; condition, originating at the Interface 1 control device, to be loaded
; into the first flip-flop by a single clock pulse and to be shifted out
; to the appropriate flip-flop by a series of further clock pulses.
;
; As eight pulses will be required, then the logic state of drive eight is
; considered first and drive one is the last to be considered.
;
; By negating the drive number and adding nine, the routine below begins
; by effecting this reversal and, by converting zero to nine, it ensures
; that eight logic zeros are shifted out for this case and for the case
; of any out-of-range parameter, which can arise in the case of a User
; experimenting with Hook Codes.
; The limit of eight Microdrives is set in the routine below and not in
; hardware.
;
; As Ben pointed out on his patent from which some of these details are
; taken, "it will be appreciated that the control device may be used to
; select associated devices other than recording/playback devices and that
; any number of associated devices may be accommodated by use of the
; technique described."

;; SW-MOTOR
L1565: PUSH DE ; preserve the original DE value throughout.

LD DE,$0100 ; load DE with the constants logic one and
; logic zero.

NEG ; negate the supplied drive number 0 - n
ADD A,$09 ; add 9 so that 0 = 9, -1 = 8, -8 = 1, -10 = -1
LD C,A ; place the reversed parameter in C.
LD B,$08 ; set clock shift counter to eight.

;; ALL-MOTRS
L1570: DEC C ; decrement the drive selector.
JR NZ,L1586 ; forward, if not in position, to OFF-MOTOR.

; The time has come to send out a signal to start this drive.

LD A,D ; select logic one.
OUT ($F7),A ; output to data port.

LD A,$EE ; select comms clock 1, comms data 0
OUT ($EF),A ; output to D-flip flop.

CALL L15A2 ; routine DEL-S-1 holds for a millisecond.

LD A,$EC ; select comms clock 0, comms data 0
OUT ($EF),A ; output to D-flip flops.

CALL L15A2 ; routine DEL-S-1 holds for a millisecond.

JR L1597 ; forward to NXT-MOTOR

; ---

;; OFF-MOTOR
L1586: LD A,$EF ; select comms clock 1, comms data 1
OUT ($EF),A ; output to D-flip flop.

LD A,E ; select logic 0.
OUT ($F7),A ; output to data port.

CALL L15A2 ; routine DEL-S-1 holds for a millisecond.

LD A,$ED ; select comms clock 0, comms data 1
OUT ($EF),A ; output to Microdrive port.

CALL L15A2 ; routine DEL-S-1 holds for a millisecond.

;; NXT-MOTOR
L1597: DJNZ L1570 ; back, for all eight drives, to ALL-MOTRS.

LD A,D ; select logic one.
OUT ($F7),A ; output to data port.
LD A,$EE ; select comms clock 1, comms data 0.
OUT ($EF),A ; output to Microdrive port.

POP DE ; restore original DE value.
RET ; return.


; ---------------------------------
; THE '1 MILLISECOND DELAY' ROUTINE
; ---------------------------------
; This subroutine is used to time the transitions of the Delay-flip-flops
; used, above, to control the array of Microdrives attached to Interface 1.
; Delay flip flops become unstable if transitions are too close together
; and this routine provides a 1 millisecond delay between clock pulses.

;; DEL-S-1
L15A2: PUSH BC ; preserve counters.
PUSH AF ;

LD BC,$0087 ; 135 decimal.
CALL L1652 ; routine DELAY-BC

POP AF ;
POP BC ; restore counters

RET ; return.


; ---------------------------------------------
; THE 'SEND HEADER BLOCK TO Microdrive' ROUTINE
; ---------------------------------------------
; Routine is called once from the FORMAT routine.

;; OUT-M-HD
L15AD: PUSH HL ;
LD DE,$001E ; 30 bytes.
JR L15B7 ; forward to OUT-M-BLK ->

; -------------------------------------------
; THE 'SEND DATA BLOCK TO Microdrive' ROUTINE
; -------------------------------------------
;

;; OUT-M-BUF
L15B3: PUSH HL ;
LD DE,$021F ; 543 bytes.

; -> Common code.

;; OUT-M-BLK
L15B7: IN A,($EF) ;
AND $01 ; isolate write prot. bit.
JR NZ,L15BF ; forward to NOT-PROT

RST 20H ; Shadow Error Restart
DEFB $0E ; Drive 'write' protected

; ---

;; NOT-PROT
L15BF: LD A,($5CC6) ; sv IOBORD
OUT ($FE),A ;
LD A,$E2 ;
OUT ($EF),A ;
INC D ;
LD A,D ;
LD B,E ;
LD C,$E7 ;

NOP ;
NOP ;
NOP ;

;; OUT-M-BYT
L15D0: OTIR ;
DEC A ;
JR NZ,L15D0 ; back to OUT-M-BYT

LD A,$E6 ;
OUT ($EF),A ;
CALL L0D4D ; routine BORD-REST
POP HL ;
RET ; return.

; -----------------------------
; THE 'SIGNAL ERROR' EXIT POINT
; -----------------------------
; This exit point is used twice from the next routines when the required
; header or record block is not found within the requisite time.

;; SIGN-ERR
L15DE: POP BC ; balance the stack.
POP HL ; first byte of destination.
INC (HL) ; increment RECFLG or HDFLAG.
RET ; return.


; --------------------------------------------------
; THE 'RECEIVE BLOCK FROM Microdrive HEADER' ROUTINE
; --------------------------------------------------
;

;; GET-M-HD
L15E2: PUSH HL ; save destination
LD DE,$000F ; set fifteen bytes to load.
LD HL,$0000 ; set large delay when waiting for a header.
JR L15F2 ; forward to GET-M-BLK

; --------------------------------------------------
; THE 'RECEIVE BLOCK FROM Microdrive RECORD' ROUTINE
; --------------------------------------------------
;

;; GET-M-BUF
L15EB: PUSH HL ; save destination.
LD DE,$0210 ; set 528d bytes to load.
LD HL,$01F4 ; set delay counter to 500d.

; -->

;; GET-M-BLK
L15F2: LD B,E ; load B register for first INIR load.
LD C,D ; load C register with count of further loads.
INC C ; adjust to count down to zero.
PUSH BC ; save the INIR counters.

;

;; CHK-AGAIN
L15F6: LD B,$08 ; set gap counter to eight.

DEC HL ;
LD A,H ;
OR L ;

JR Z,L15DE ; back to SIGN-ERR

;; CHKLOOP
L15FD: CALL L163E ; routine TEST-BRK

IN A,($EF) ;
AND $04 ; isolate gap bit.
JR Z,L15F6 ; back to CHK-AGAIN

DJNZ L15FD ; back to CHKLOOP

;; CHK-AG-2
L1608: LD B,$06 ;
DEC HL ;
LD A,H ;
OR L ;
JR Z,L15DE ; back to SIGN-ERR


;; CHK-LP-2
L160F: CALL L163E ; routine TEST-BRK
IN A,($EF) ;
AND $04 ; isolate gap bit.
JR NZ,L1608 ; back to CHK-AG-2

DJNZ L160F ; back to CHK-LP-2

LD A,$EE ;
OUT ($EF),A ;

LD B,$3C ; set count 60 decimal.

;; DR-READY
L1620: IN A,($EF) ;
AND $02 ; isolate sync bit.
JR Z,L162A ; forward to READY-RE

DJNZ L1620 ; back to DR-READY

JR L15F6 ; back to CHK-AGAIN

; ---

;; READY-RE
L162A: POP BC ; retrieve counters from the stack.
POP HL ; retrieve the destination
PUSH HL ; and stack again.
CALL L163E ; routine TEST-BRK.
LD A,C ; transfer repeat counter to A.
LD C,$E7 ; set port to $E7.

; Now the INIR (INput to memory Increment and Repeat) instruction is used.

;; IN-M-BLK
L1633: INIR ; read B bytes from port C to destination HL.

; B (zero) will now count 256 bytes if first block was not the total.

DEC A ; decrement repeat counter.
JR NZ,L1633 ; back, if not zero, to IN-M-BLK

; All bytes, 15 or 528 have now been read.

LD A,$EE ;
OUT ($EF),A ;

POP HL ; restore pointer to first byte.
RET ; return.

; ----------------------
; THE 'TEST-BRK' ROUTINE
; ----------------------
; Note. used more consistently in this ROM.

;; TEST-BRK
L163E: LD A,$7F ; read port $7FFE - keys B, N, M, SYM, SPACE.
IN A,($FE) ;
RRA ; test for SPACE key.
RET C ; return if not pressed.

LD A,$FE ; read port $FEFE - keys SHIFT, Z, X, C, V.
IN A,($FE) ;
RRA ; test for SHIFT key.
RET C ; return if not pressed.

CALL L0D4D ; routine BORD-REST.

LD (IY+$00),$14 ; set ERR_NR to main 'L BREAK into program'
RST 28H ; invoke the Main ROM error routine.

; ----------------------
; THE 'DELAY-BC' ROUTINE
; ----------------------
;

;; DELAY-BC
L1652: PUSH AF ;

;; DELAY-BC1
L1653: DEC BC ;
LD A,B ;
OR C ;
JR NZ,L1653 ; back to DELAY-BC1

POP AF ;
RET ;

; ------------------------
; THE 'READ BLOCK' ROUTINE
; ------------------------
; Note. new in this ROM.
; Used by format routine.

;; READ-BLK
L165A: PUSH HL
PUSH BC

;; RDLOOP1
L165C: LD B,$08

;; RDLOOP2
L165E: CALL L163E ; routine TEST-BRK

IN A,($EF)
AND $04 ; isolate gap bit.

JR Z,L165C ; back to RDLOOP1

DJNZ L165E ; back to RDLOOP2


;; RDLOOP3
L1669: LD B,$06

;; RDLOOP4
L166B: CALL L163E ; routine TEST-BRK

IN A,($EF)
AND $04 ; isolate gap bit.

JR NZ,L1669 ; back to RDLOOP3

DJNZ L166B ; back to RDLOOP4

LD A,$EE
OUT ($EF),A

LD B,$3C ; set counter to 60d.

;; SYNC-RD
L167C: IN A,($EF)
AND $02 ; isolate sync bit.
JR Z,L1686 ; forward to READY-R2

DJNZ L167C ; back to SYNC-RD

JR L165C ; back to RDLOOP1

; ---


;; READY-R2
L1686: POP BC
POP HL
PUSH HL
CALL L163E ; routine TEST-BRK

LD C,$E7 ; port
LD E,$FC ; required test byte
LD B,$0F ; initial counter.
LD D,$64 ; final counter.
INIR

;; RD-BYT-1
L1696: IN A,(C)
CP E
JR NZ,L16AD ; forward to ENDRD

DJNZ L1696 ; back to RD-BYT-1


;; RD-BYT-2
L169D: IN A,(C)
CP E
JR NZ,L16AD ; forward to ENDRD

DJNZ L169D ; back to RD-BYT-2

LD B,D ; final counter is $64

;; RD-BYT-3
L16A5: IN A,(C)
CP E
JR NZ,L16AD ; forward to ENDRD

DJNZ L16A5 ; back to RD-BYT-3

XOR A ; set zero flag to signal successful read

;; ENDRD
L16AD: POP HL
RET ; return.

; -------------------------
; THE 'WRITE BLOCK' ROUTINE
; -------------------------
; Note. new in this ROM.
; Called once from the FORMAT routine.

;; WR-BLK
L16AF: PUSH HL ; preserve HL throughout.

LD A,($5CC6) ; fetch the value of IOBORD
OUT ($FE),A ; and change the border colour.

LD A,$E2
OUT ($EF),A ; enable writing

LD E,$66
LD C,$E7
LD B,$1B
LD A,$FC ; test byte written
NOP ;
OTIR ;

;; WR-BYT-1
L16C4: OUT (C),A
DJNZ L16C4 ; back to WR-BYT-1


;; WR-BYT-2
L16C8: OUT (C),A
DJNZ L16C8 ; back to WR-BYT-2

LD B,E ; load counter with $66

;; WR-BYT-3
L16CD: OUT (C),A
DJNZ L16CD ; back to WR-BYT-3

LD A,$E6
OUT ($EF),A

CALL L0D4D ; routine BORD-REST

POP HL ; restore initial HL value.

RET ; return.

; --------------------
; THE 'UNUSED' SECTION
; --------------------
; Contains copyright holder and initials of the main programmer. The rest
; is set to $FF. This section is situated before the fixed-position CLOSE
; rectification routine.

DEFB $7F ; copyright ©
DEFM " 1983 Sinclair"
DEFM " Research Ltd"
DEFM " MJB " ; Martin Brennan

DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF
DEFB $FF

; --------------------------
; THE 'CLOSE STREAM' ROUTINE
; --------------------------
; Note. An instruction fetch on main address L1708 pages in this ROM.

;; CLOSE-CH
L1708: INC HL ; Same instruction as in Main ROM
RST 30H ; Create the new system variables

; Note. If extra System Variables were created then the accumulator will
; now hold the value 1 from the setting of COPIES.
; A command like OPEN #7,"s" : CLOSE #7 will not work.


SRL A ;
SUB $03 ;

RES 1,(IY+$7C) ; set FLAGS_3
CALL L1718 ; routine CLOSE (below)
JP L05C1 ; jump back to normal command exit at END1

; ---------------------------
; THE 'CLOSE COMMAND' ROUTINE
; ---------------------------
; This command is called from above and also from ALL-STRMS as part of the
; CLEAR # command execution.

;; CLOSE
L1718: RST 10H ; CALBAS
DEFW $1727 ; main STR-DATA1

LD A,C ;
OR B ;
RET Z ;

PUSH BC ;
PUSH HL ;

LD HL,($5C4F) ; sv CHANS
DEC HL ;
ADD HL,BC ;
EX (SP),HL ;
RST 10H ; CALBAS
DEFW $16EB ; main CLOSEX
LD HL,($5C4F) ; sv CHANS
LD DE,$0014 ;
ADD HL,DE ;

POP DE ;
SCF ;
SBC HL,DE ;
POP BC ;


RET NC ;

PUSH BC ;
PUSH DE ;
EX DE,HL ;
LD ($5C51),HL ; sv CURCHL
INC HL ;


INC HL ;
INC HL ;
INC HL ;

LD A,(HL) ; fetch the letter.

; Now mark the channel as temporary so that if anything goes wrong, such
; as the user pressing BREAK, then the channel can be reclaimed by CLEAR #.

L1741: SET 7,(HL) ; As suggested by Andrew Pennell 1983.

LD DE,$0005 ;
ADD HL,DE ;
LD E,(HL) ;
INC HL ;
LD D,(HL) ;
PUSH DE ;
CP $54 ; compare to "T"
JR NZ,L175C ; forward to CL-N-CH

BIT 1,(IY+$7C) ; sv FLAGS_3
JR NZ,L177D ; forward to RCLM-CH

LD A,$0D
CALL L0D07 ; routine BCHAN-OUT
JR L177D ; forward to RCLM-CH

; ---

;; CL-N-CH
L175C: CP $4E ; character "N" ?
JR NZ,L176B ; forward to CL-M-CH

BIT 1,(IY+$7C) ; sv FLAGS_3
JR NZ,L177D ; forward to RCLM-CH

CALL L0FAE ; routine SEND-NEOF
JR L177D ; forward to RCLM-CH

; ---


;; CL-M-CH
L176B: CP $4D ; character "M"
JR NZ,L177D ; forward to RCLM-CH

POP DE ;
POP IX ;
POP DE ;
BIT 1,(IY+$7C) ; sv FLAGS_3
JP Z,L138E ; jump to CLOSE-M2

JP L119F ; jump to DEL-M-BUF

; ---

;; RCLM-CH
L177D: POP BC ;
POP HL ;
PUSH BC ;
RST 10H ; CALBAS
DEFW $19E8 ; main RECLAIM-2
XOR A ;
LD HL,$5C16 ; sv STRMS_00

;; UPD-STRM
L1787: LD E,(HL) ;
INC HL ;
LD D,(HL) ;
DEC HL ;
LD ($5C5F),HL ; sv X_PTR
POP BC ;
POP HL ;
PUSH HL ;
PUSH BC ;
AND A ;
SBC HL,DE ;
JR NC,L17A2 ; forward to UPD-NXT-S

EX DE,HL ;
AND A ;
SBC HL,BC ;
EX DE,HL ;
LD HL,($5C5F) ; sv X_PTR
LD (HL),E ;
INC HL ;
LD (HL),D ;

;; UPD-NXT-S
L17A2: LD HL,($5C5F) ; sv X_PTR
INC HL ;
INC HL ;
INC A ;
CP $10 ;
JR C,L1787 ; back to UPD-STRM

LD (IY+$26),$00 ; sv X_PTR_hi
POP HL ;
POP HL ;
RES 1,(IY+$7C) ; sv FLAGS_3
RET ; return.

; ----------------------------------------
; THE 'RECLAIM TEMPORARY CHANNELS' ROUTINE
; ----------------------------------------
;

;; RCL-T-CH
L17B7: LD IX,($5C4F) ; sv CHANS
LD DE,$0014
ADD IX,DE

;; EX-CHANS
L17C0: LD A,(IX+$00) ; first character of channel
CP $80 ; is it the end-marker ?
JR NZ,L17D0 ; forward to CHK-TEMPM

LD A,$EE
OUT ($EF),A

XOR A
JP L1532 ; jump to SEL-DRIVE

; ---

RET ; unused - the above JP was probably once a CALL.

; ---

;; CHK-TEMPM
L17D0: LD A,(IX+$04) ; channel letter
CP $CD ; is it an inverted "M" ?
JR NZ,L17DC ; forward to CHK-TEMPN

CALL L119F ; routine DEL-M-BUF
JR L17B7 ; back to RCL-T-CH


;; CHK-TEMPN
L17DC: CP $CE ; is channel letter an inverted "N" ?
JR NZ,L17EB ; forward to PT-N-CHAN

LD BC,$0114
PUSH IX
POP HL
RST 10H ; CALBAS
DEFW $19E8 ; main RECLAIM-2
JR L17B7 ; back to RCL-T-CH


;; PT-N-CHAN
L17EB: LD E,(IX+$09) ; length of
LD D,(IX+$0A) ; channel
ADD IX,DE
JR L17C0 ; back to EX-CHANS


; --------------------------
; THE 'MOVE COMMAND' ROUTINE
; --------------------------
;

;; MOVE
L17F5: SET 4,(IY+$7C) ; update FLAGS_3 to indicate a MOVE is in
; progress - see INKEY$

CALL L1859 ; routine OP-STRM
LD HL,($5C4F) ; sv CHANS
PUSH HL
CALL L059F ; routine EX-D-STR
CALL L1859 ; routine OP-STRM
CALL L059F ; routine EX-D-STR
POP DE
LD HL,($5C4F) ; sv CHANS
OR A
SBC HL,DE
LD DE,($5CDA) ; sv N_STR1
ADD HL,DE
LD ($5CDA),HL ; sv N_STR1

;; M-AGAIN
L1818: LD HL,($5CDA) ; sv N_STR1
LD ($5C51),HL ; sv CURCHL

;; I-AGAIN
L181E: RST 10H ; CALBAS
DEFW $15E6 ; main INPUT-AD
JR C,L1827 ; forward to MOVE-OUT

JR Z,L181E ; back to I-AGAIN

JR L1832 ; forward to MOVE-EOF


;; MOVE-OUT
L1827: LD HL,($5CE2) ; sv D_STR2
LD ($5C51),HL ; sv CURCHL
RST 10H ; CALBAS
DEFW $0010 ; main PRINT-A

JR L1818 ; back to M-AGAIN

;; MOVE-EOF
L1832: RES 4,(IY+$7C) ; sv FLAGS_3
LD HL,($5C4F) ; sv CHANS
PUSH HL
CALL L059F ; routine EX-D-STR
CALL L18A8 ; routine CL-CHAN
CALL L059F ; routine EX-D-STR
POP DE
LD HL,($5C4F) ; sv CHANS
OR A
SBC HL,DE
LD DE,($5CDA) ; sv N_STR1
ADD HL,DE
LD ($5CDA),HL ; sv N_STR1
CALL L18A8 ; routine CL-CHAN
CALL L17B7 ; routine RCL-T-CH

RET ; RETURN


; ---------------------------------------------
; THE 'USE STREAM OR TEMPORARY CHANNEL' ROUTINE
; ---------------------------------------------
;

;; OP-STRM
L1859: LD A,($5CD8) ; sv D_STR1
INC A
JR Z,L186A ; forward to OP-CHAN

DEC A
RST 10H ; CALBAS
DEFW $1601 ; main CHAN-OPEN
LD HL,($5C51) ; sv CURCHL
LD ($5CDA),HL ; sv N_STR1
RET


;; OP-CHAN
L186A: LD A,($5CD9) ; sv L_STR1 device letter.

CP $4D ; is character "M" ?
JR NZ,L1883 ; forward to CHECK-N

CALL L1B05 ; routine OP-TEMP-M creates a temporary
; Microdrive channel, starts motor, and
; fetches record zero of named file.
XOR A
CALL L1532 ; routine SEL-DRIVE
LD ($5CDA),IX ; sv N_STR1
BIT 2,(IX+$43) ; RECFLG
RET Z

RST 20H ; Shadow Error Restart
DEFB $16 ; Wrong file type


;; CHECK-N
L1883: CP $4E ; is character "N" ?
JR NZ,L188F ; forward to CHECK-R

CALL L0F46 ; routine OP-TEMP-N
LD ($5CDA),IX ; sv N_STR1
RET

; ---

; Finally, check for the RS232 channel before producing an error.

;; CHECK-R
L188F: CP $54 ; is character "T" ?
JR Z,L1899 ; forward to USE-R

CP $42 ; is character "B" ?
JR Z,L1899 ; forward to USE-R

RST 20H ; Shadow Error Restart
DEFB $00 ; Nonsense in BASIC

; ---

;; USE-R
L1899: CALL L0B17 ; routine OP-RS-CH
LD ($5CDA),DE ; sv N_STR1
PUSH DE ;
POP IX ;
SET 7,(IX+$04) ; channel letter
RET ; return.


; ----------------------------------
; THE 'CLOSE 'MOVE' CHANNEL' ROUTINE
; ----------------------------------
;

;; CL-CHAN
L18A8: LD A,($5CD8) ; sv D_STR1
INC A
RET NZ

LD A,($5CD9) ; sv L_STR1 device letter.
CP $4D ; is character "M" ?
JR NZ,L18BC ; forward to CL-CHK-N

LD IX,($5CDA) ; sv N_STR1
CALL L138E ; routine CLOSE-M2
RET ;


;; CL-CHK-N
L18BC: CP $4E ; is character "N" ?
RET NZ ;

LD IX,($5CDA) ; sv N_STR1
LD ($5C51),IX ; sv CURCHL
CALL L0FAE ; routine SEND-NEOF
RET


; ---------------------------------------------
; THE 'SAVE DATA BLOCK INTO Microdrive' ROUTINE
; ---------------------------------------------
;

;; SA-DRIVE
L18CB: LD A,($5CD6) ; fetch drive number from D_STR1
CALL L1532 ; routine SEL-DRIVE starts motor.

IN A,($EF) ; read Microdrive port.
AND $01 ; isolate 'write protect' bit.
JR NZ,L18D9 ; forward, if not low, to STAR-SA

RST 20H ; Shadow Error Restart
DEFB $0E ; 'Drive 'write' protected'

; ---

;; STAR-SA
L18D9: LD HL,($5CE9) ; sv HD_0D
LD ($5CE4),HL ; sv D_STR2

CALL L1B05 ; routine OP-TEMP-M creates a temporary
; Microdrive channel, starts motor, and
; attempts to fetch record zero of named file.

BIT 0,(IX+$18) ; test CHFLAG
JR NZ,L18ED ; forward, with no existing file, to NEW-NAME

CALL L138E ; routine CLOSE-M2 closes temporary channel
; and stops the motor.

RST 20H ; Shadow Error Restart
DEFB $0C ; Writing to a 'read' file

; ---

;; NEW-NAME
L18ED: SET 2,(IX+$43) ; update RECFLG signal not a PRINT type file.

; Note. the Microdrive motor has been left running by OP-TEMP-M so the next
; two lines are not necessary. Redundant code elsewhere suggests that
; OP-TEMP-M once stopped the drive.

LD A,(IX+$19) ; fetch drive from CHDRIV.
CALL L1532 ; routine SEL-DRIVE stops and then restarts the
; motor.

PUSH IX ; transfer the channel base address
POP HL ; to the HL register pair.

LD DE,$0052 ; prepare offset to data buffer.
ADD HL,DE ; add to address start of data.
EX DE,HL ; transfer this destination to DE.

LD HL,$5CE6 ; set source to the nine byte header at HD_00
LD BC,$0009 ; nine bytes to copy.
LD (IX+$0B),C ; update CHBYTE_lo with length saved so far.

LDIR ; block move the header info into the buffer.

PUSH DE ; save destination.

; Now calculate the number of sectors required using a similar method to
; the one used for calculating the number of records to load.
; Note. there is an error in the calculation as one byte should be subtracted
; from the total bytes to ensure that there is at least one byte in the EOF
; record. The next instruction should be to load HL with eight.

L190B: LD HL,$0009 ; start with the nine header bytes. ??
LD BC,($5CE7) ; fetch data length from HD_0B.
ADD HL,BC ; add to give total size of block.

SRL H ; halve MSB to convert to 512 byte chunks.
INC H ; increment to include EOF block. Wrong.

; Note.
; 511 bytes = 502 bytes + 9 header = $01FF, SRL=$00, INC=$01 sectors OK.
; 512 bytes = 503 bytes + 9 header = $0200, SRL=$01, INC=$02 sectors WRONG!!
; 513 bytes = 504 bytes + 9 header = $0201, SRL=$01, INC=$02 sectors OK.


PUSH HL ; preserve register H the sector counter.

CALL L1D43 ; routine FREESECT calculates free sectors on
; cartridge.

POP HL ; bring back the sector estimate in H.
LD A,E ; load accumulator with actual sectors.
CP H ; compare with estimate
JR NC,L1921 ; forward, if equal or greater, to SA-DRI-2

RST 20H ; Shadow Error Restart
DEFB $0F ; ' Microdrive full'

; ---

;; SA-DRI-2
L1921: POP DE ; bring back destination.
LD HL,($5CE4) ; fetch start from D_STR2
LD BC,($5CE7) ; fetch data length from HD_0B

;; SA-DRI-3
L1929: LD A,B ; test for
OR C ; zero bytes.
JR Z,L194F ; forward, if all chunks saved, to SA-DRI-4

LD A,(IX+$0C) ; fetch high byte of byte counter from CHBYTE_hi
CP $02 ; compare to 2 which would indicate 512 bytes.
JR NZ,L1943 ; forward, if less, to SA-DRI-WR

; a sector is written to Microdrive.

PUSH HL ; preserve start of data.
PUSH BC ; preserve length.

CALL L12EE ; routine WRITE-PRC.

POP BC ; restore length.

PUSH IX ; transfer the channel base address
POP HL ; to the HL register pair.

LD DE,$0052 ; add offset to
ADD HL,DE ; point to data buffer.
EX DE,HL ; transfer this destination to DE.

POP HL ; restore the start of data.

;; SA-DRI-WR
L1943: LDI ; transfer one byte at a time decrementing BC
; the total byte counter.

; now increment the channel byte counter which started at zero and has a
; limit of 512 bytes.

INC (IX+$0B) ; increment CHBYTE_lo
JR NZ,L1929 ; back, if not 256, to SA-DRI-3

INC (IX+$0C) ; increment CHBYTE_hi
JR L1929 ; back to SA-DRI-3 to check high byte.

; ---

;; SA-DRI-4
L194F: SET 1,(IX+$43) ; update RECFLG mark this as EOF record.

CALL L12EE ; routine WRITE-PRC writes last record in set.

LD A,($5CEF) ; fetch user-alterable system variable COPIES
DEC A ; decrement
JR Z,L196A ; forward, if zero, to END-SA-DR

LD ($5CEF),A ; place decremented value back in COPIES

RES 1,(IX+$43) ; update RECFLG - signal not the EOF record.
LD A,$00 ; prepare to start saving at record zero again.
LD (IX+$0D),A ; update the channel record counter CHREC.

JR L18ED ; back to NEW-NAME

; ---

;; END-SA-DR
L196A: XOR A ; set accumulator to zero.
CALL L1532 ; routine SEL-DRIVE stops the motor.

JP L119F ; jump to DEL-M-BUF

; ----------------------------------------------------
; THE 'GET HEADER INFORMATION FROM Microdrive' ROUTINE
; ----------------------------------------------------
; this routine extracts the nine bytes of global header information that
; is prepended to the data saved on Microdrive. This relates to the type -
; Basic, Code and length etc. and is the equivalent of a tape header without
; the name which, in contrast, does have to be saved to every record.
; It is obtained therefore from the start of data at record zero.
;
; Note. the destination for this data, (program area or variable location),
; has already been calculated and since opening a channel will move this
; destination up in memory, the "Start of data" is transferred to the D_STR2
; location, otherwise used for the second filename during moves, so that its
; value is adjusted by REST-N-AD during OP-TEMP-M.

;; F-M-HEAD
L1971: LD HL,($5CE1) ; copy start of data from D_STR2(+3)
LD ($5CE4),HL ; to dynamic location D_STR2(+6)

CALL L1B05 ; routine OP-TEMP-M creates a temporary
; Microdrive channel, starts motor, and
; fetches record zero of named file.

BIT 0,(IX+$18) ; test CHFLAG for valid first record.
JR Z,L1982 ; forward, if OK, to F-HD-2

RST 20H ; Shadow Error Restart
DEFB $11 ; 'File not found'

; ---

;; F-HD-2
L1982: BIT 2,(IX+$43) ; test RECFLG is it a print file
JR NZ,L198A ; forward, if not, to F-HD-3

RST 20H ; Shadow Error Restart
DEFB $16 ; 'Wrong file type'

; ---

;; F-HD-3
L198A: PUSH IX ; transfer the channel base address
POP HL ; to the HL register pair.

LD DE,$0052 ; offset to CHDATA
ADD HL,DE ; add to address start of data.
LD DE,$5CE6 ; set destination to nine system variables
; starting at location HD_00.
LD BC,$0009 ; nine bytes to copy.

LDIR ; block move to HD_00 - HD_11.

RET ; return.

; --------------------------------------------------
; THE 'LOAD OR VERIFY BLOCK FROM Microdrive' ROUTINE
; --------------------------------------------------
; This subroutine is called once only from LV-ANY to load a block of code,
; previously SAVED to a number of sectors, from Microdrive.
; At this stage a temporary channel has already been created and it holds
; the first 512 byte record containing at the start the nine header bytes.
; There will be an accurate Microdrive map for the drive which has its
; motor running.
; The block could be a program, code bytes or an array and the first
; receiving location is in HL and the length in DE.

;; LV-MCH
L199A: LD ($5CE9),HL ; save start in system variable HD_0D

; now directly read the header values at the start of the data buffer.

LD E,(IX+$53) ; directly read the saved length
LD D,(IX+$54) ; from the data buffer into DE.

; now calculate how many 512 byte Microdrive records need to be read in
; by taking the total minus one to ensure an EOF record.
; e.g.
; 1023 bytes = 1014 bytes + 9 header - 1 = $03FE, SRL=$01, INC=$02 sectors
; 1024 bytes = 1015 bytes + 9 header - 1 = $03FF, SRL=$01, INC=$02 sectors
; 1025 bytes = 1016 bytes + 9 header - 1 = $0400, SRL=$02, INC=$03 sectors

LD HL,$0008 ; add eight in effect +9 for header -1.
ADD HL,DE ; add the program/code length.

; the MSB is the number of 256 chunks.

SRL H ; shift right to halve and give 512 byte
; chunks.
INC H ; increment to include the extra sector.

LD A,H ; use accumulator to store record count
LD ($5CE7),A ; in the temporary system variable HD_0B

; the Microdrive map is now saved on the machine stack, for later recall,
; and at the same time the current map locations are all set to zero.
; The new map is to be used for records rather than sectors.

CALL L1A04 ; routine SA-MAP saves the thirty two bytes
; of the map on the machine stack safely
; dipping into the 80 bytes of spare memory.

; now, since this is record zero, subtract the nine header bytes from the
; current record length and put back.

LD DE,$0009 ;

LD L,(IX+$45) ; RECLEN_lo
LD H,(IX+$46) ; RECLEN_hi

OR A ; clear carry

SBC HL,DE ;

LD (IX+$45),L ; RECLEN_lo
LD (IX+$46),H ; RECLEN_hi

;

PUSH IX ; transfer the channel base address
POP HL ; to the HL register pair.

LD DE,$005B ; prepare offset $0052 to data and then an
ADD HL,DE ; extra nine bytes. Add to skip the header.

LD DE,($5CE9) ; set destination from HD_0D

JR L19EA ; forward to LOOK-MAP to enter the record
; loading loop at the mid-point as record
; zero is already in the channel.

; ---

; The record loading loop loads records in random order. Consider that
; multiple copies of a filename may have been saved so there may be several
; sectors with the same record number.

;; USE-REC
L19D0: CALL L1A5D ; routine F-REC2 fetches only a header and
; record that matches the name specified
; in CHNAME and only if the map bit is reset
; indicating no sector with this record number
; has already been loaded.

LD A,(IX+$44) ; re-fetch record number from RECNUM.

; Note. the next test is a nonsense as a record zero has already been marked
; so no sector with record zero could be reloaded.

OR A ; test for a record zero.
JR Z,L19D0 ; back, if so, to USE-REC.

; now calculate the destination if this 512 byte sector.

RLA ; double recnum to give 512 byte chunks
DEC A ; decrement to adjust for nine bytes of header.
LD D,A ; place in MSB of offset

LD E,$F7 ; set LSB of offset to $00 - $09 for header.
LD HL,($5CE9) ; fetch start of data from HD_0D

ADD HL,DE ; add to calculate destination for this sector.
EX DE,HL ; transfer destination to DE.

PUSH IX ; transfer the channel base address
POP HL ; to the HL register pair.

LD BC,$0052 ; prepare offset to start of 512 byte buffer
ADD HL,BC ; add so that HL addresses start of data.

; -> The mid loop entry point.

;; LOOK-MAP
L19EA: EXX ; preserve HL and DE by using alternate
; registers.

CALL L13BF ; routine CHK-MAP-2 sets HL to the map byte
; and B to the mask.

; Note. the routine also resets the zero flag if this record has previously
; been loaded but this is not possible.

JR NZ,L19D0 ; back, if already loaded, to USE-REC.

; since this is the first time for this record mark so that not loaded again.

LD A,(HL) ; mark the record bit
OR B ; by setting it so that it is not
LD (HL),A ; considered for loading again.

EXX ; restore HL (source) and DE (destination).

CALL L1A39 ; routine LD-VE-M loads or verifies a
; data record.

; now decrement the record count which is beyond reach of IY register.

LD A,($5CE7) ; fetch count of records to be loaded HD_0B
DEC A ; decrement
LD ($5CE7),A ; and place back in system variable HD_0B

JR NZ,L19D0 ; back, if not finished to USE-REC

; the block is loaded

CALL L1A1E ; routine RE-MAP restores the true Microdrive
; map from the stack.

RET ; return.


; ------------------------------------------
; THE 'SAVE Microdrive MAP CONTENTS' ROUTINE
; ------------------------------------------
; This routine saves the sector-mapped Microdrive map on the machine stack
; at the same time setting each of the 32 vacated locations to zero.

;; SA-MAP
L1A04: POP HL ; drop the return address into HL
LD ($5CC9),HL ; and save in unused system variable SECTOR

LD L,(IX+$1A) ; fetch address of Microdrive map from CHMAP
LD H,(IX+$1B) ; fetch address of Microdrive map from CHMAP
LD BC,$1000 ; set word counter B to sixteen and C to zero.

; now enter a loop stacking two bytes at a time.

;; SA-MAP-LP
L1A11: LD E,(HL) ; fetch first byte to E.
LD (HL),C ; set location to zero.
INC HL ; bump address.
LD D,(HL) ; fetch second byte to D.
LD (HL),C ; set location to zero.
INC HL ; bump address.

PUSH DE ; save DE on machine stack.

DJNZ L1A11 ; back, for 16 pairs, to SA-MAP-LP

LD HL,($5CC9) ; restore return address from SECTOR
JP (HL) ; and jump to location.

; ---------------------------------------------
; THE 'RESTORE Microdrive MAP CONTENTS' ROUTINE
; ---------------------------------------------
; This routine is the opposite of the above and restores the sector-mapped
; Microdrive map from the machine stack back to its original location
; overwriting the now redundant record-indicating map.

;; RE-MAP
L1A1E: POP HL ; drop the subroutine return address.
LD ($5CC9),HL ; store in the multi-purpose variable SECTOR.

LD L,(IX+$1A) ; fetch address of Microdrive map from CHMAP.
LD H,(IX+$1B) ; fetch address of Microdrive map from CHMAP.
LD DE,$001F ; thirty one locations are added.
ADD HL,DE ; to address the last location.
LD B,$10 ; set the pop counter to sixteen.

;; RE-MAP-LP
L1A2E: POP DE ; pop two bytes of the map from the stack.

LD (HL),D ; insert a map byte.
DEC HL ; decrement the address.
LD (HL),E ; insert second map byte.
DEC HL ; decrement the address again.

DJNZ L1A2E ; back, sixteen times, to RE-MAP-LP.

LD HL,($5CC9) ; restore the return address from SECTOR.
JP (HL) ; and jump to address.

; ---------------------
; THE 'LD-VE-M' ROUTINE
; ---------------------
; The Load or Verify from Microdrive routine.
; This routine loads or verifies up to 512 bytes of data currently in the
; Microdrive channel data buffer.

;; LD-VE-M
L1A39: LD C,(IX+$45) ; RECLEN_lo
LD B,(IX+$46) ; RECLEN_hi

; now test if a VERIFY operation by performing the equivalent of bit 7,(iy+$7c)

LD A,($5CB6) ; load system variable FLAGS_3 to accumulator.
BIT 7,A ; test FLAGS_3 value - performing VERIFY ?

JR NZ,L1A49 ; forward, if so, to VE-M-E

; the operation is a LOAD.

LDIR ; block copy the bytes.
RET ; return.

; ---

; the operation is a VERIFY.

;; VE-M-E
L1A49: LD A,(DE) ; fetch a byte from the destination.
CP (HL) ; compare to that of source
JR NZ,L1A55 ; forward, with mismatch, to VE-FAIL

INC HL ; increment source address.
INC DE ; increment destination address.
DEC BC ; decrement byte count.
LD A,B ; test for
OR C ; zero.
JR NZ,L1A49 ; back, if not, to VE-M-E

RET ; return.

; ---

;; VE-FAIL
L1A55: RST 20H ; Shadow Error Restart
DEFB $15 ; 'Verification has failed'

; ------------------------------------------
; THE 'FETCH RECORD FROM Microdrive' ROUTINE
; ------------------------------------------
; Entered at F-REC2,
; Note. the first entry point f-rec1 is unused.

;; f-rec1
L1A57: LD A,(IX+$19) ; fetch drive number.
CALL L1532 ; routine SEL-DRIVE starts motor.

; -->

;; F-REC2
L1A5D: LD BC,$04FB ; Set sector counter to 5 * 255 = 1275
LD ($5CC9),BC ; Update System Variable SECTOR

;; UNTILFIVE
L1A64: CALL L1280 ; routine G-HD-RC fetches the next header and
; matching record to pass tape head.

JR C,L1A7B ; forward, with name mismatch, to F-ERROR

JR Z,L1A7B ; forward, with unused record, to F-ERROR

CALL L13BF ; routine CHK-MAP-2 checks RECORD.

JR NZ,L1A7B ; forward, if already loaded, to F-ERROR

PUSH IX ; transfer the channel base address
POP HL ; to the HL register pair.

LD DE,$0052 ;
ADD HL,DE ;
CALL L142B ; routine CHKS-BUFF
RET Z ;


;; F-ERROR
L1A7B: CALL L13F7 ; routine DEC-SECT
JR NZ,L1A64 ; back to UNTILFIVE

RST 20H ; Shadow Error Restart
DEFB $11 ; File not found


; -----------------------------------------
; THE 'RESTORE ADDRESS OF FILENAME' ROUTINE
; -----------------------------------------
; This subroutine performs a similar function to the Main ROM POINTERS routine
; by adjusting the extra system variables that point to filenames within
; the sliding, dynamic areas.
; On entry HL points to the start of the New Room and BC holds the number of
; bytes created.

;; REST-N-AD
L1A82: PUSH HL ; Preserve HL throughout.

PUSH HL ; Preserve HL for second call.

LD DE,($5CE4) ; Fetch D_STR2 - start of 2nd filename.
CALL L1A9D ; routine TST-PLACE may adjust fetched value.
LD ($5CE4),DE ; Store in System Variable D_STR2

POP HL ; Restore HL for second call.

LD DE,($5CDC) ; Fetch D_STR1 - start of 1st filename.
CALL L1A9D ; routine TST-PLACE
LD ($5CDC),DE ; Store in System Variable D_STR1

POP HL ; Restore original HL value.

RET ; return.

; ---------------------------
; THE 'TEST PLACE' SUBROUTINE
; ---------------------------
; This subroutine is used twice from above to test if the filename address
; is within the Spectrum's dynamic RAM area.
; HL = location before new room.
; DE = address of filename.
; BC = amount of room just created.

;; TST-PLACE
L1A9D: SCF ; adjust for one before.
SBC HL,DE ; subtract filename address from start of room
RET NC ; and if before new room then return.

LD HL,($5C65) ; fetch STKEND and if the filename is above
SBC HL,DE ; then it is not in dynamic memory.
RET C ;

EX DE,HL ; add the number of bytes created
ADD HL,BC ; to the filename address
EX DE,HL ; to bring it into line.

RET ; return.


; -----------------------------------
; THE 'CALLS TO THE COMMANDS' ROUTINE
; -----------------------------------
;

;; ERASE-RUN
L1AAB: CALL L1D79 ; routine ERASE
JR L1AC9 ; forward to ENDC

; ---

;; MOVE-RUN
L1AB0: CALL L17F5 ; routine MOVE
JR L1AC9 ; forward to ENDC

; ---

;; CAT-RUN
L1AB5: CALL L1C52 ; routine CAT
JR L1AC9 ; forward to ENDC

; ---

;; FOR-RUN
L1ABA: CALL L1B5D ; routine FORMAT
JR L1AC9 ; forward to ENDC

; ---

;; OP-RUN
L1ABF: CALL L1ACC ; routine OP-M-STRM
JR L1AC9 ; forward to ENDC

; ---

;; SAVE-RUN
L1AC4: CALL L18CB ; routine SA-DRIVE
JR L1AC9 ; forward to ENDC

; ---

;; ENDC
L1AC9: JP L05C1 ; jump to END1

; ------------------------------------------
; THE 'OPEN A PERMANENT "M" CHANNEL' ROUTINE
; ------------------------------------------
;

;; OP-M-STRM
L1ACC: LD A,($5CD8) ; sv D_STR1
ADD A,A ;
LD HL,$5C16 ; sv STRMS_00
LD E,A ;
LD D,$00 ;
ADD HL,DE ;
PUSH HL ;
CALL L1B05 ; routine OP-TEMP-M creates a temporary
; Microdrive channel, starts motor, and
; fetches record zero of named file.
BIT 0,(IX+$18) ; CHFLAG
JR Z,L1AE9 ; forward to MAKE-PERM

IN A,($EF) ;
AND $01 ; isolate write prot.
JR NZ,L1AE9 ; forward to MAKE-PERM

RST 20H ; Shadow Error Restart
DEFB $0E ; Drive 'write' protected

; ---

;; MAKE-PERM
L1AE9: RES 7,(IX+$04) ; channel letter
XOR A ;
CALL L1532 ; routine SEL-DRIVE
BIT 0,(IX+$18) ; CHFLAG
JR NZ,L1AFF ; forward to STORE-DSP

BIT 2,(IX+$43) ; RECFLG
JR Z,L1AFF ; forward to STORE-DSP

RST 20H ; Shadow Error Restart
DEFB $16 ; Wrong file type

; ---

;; STORE-DSP
L1AFF: EX DE,HL ;
POP HL ;
LD (HL),E ;
INC HL ;
LD (HL),D ;
RET ;


; ------------------------------------------
; THE 'OPEN A TEMPORARY "M" CHANNEL' ROUTINE
; ------------------------------------------
; (Hook Code: $22)
;

;; OP-TEMP-M
L1B05: CALL L10A5 ; routine SET-T-MCH creates a temporary channel
; using either an existing Microdrive map from
; a channel also using this drive or allocating
; a new one initialized to $FF bytes.
; fields CHREC etc. are set to zero.

PUSH HL ; preserve the offset to this channel from CHANS

LD A,(IX+$19) ; fetch drive number 1 - 8 from CHDRIV
CALL L1532 ; routine SEL-DRIVE starts motor and disables
; interrupts.

LD BC,$0032 ; now set temporary unused
LD ($5CC9),BC ; system variable SECTOR_lo to fifty
; and set SECTOR_hi to zero.

; now enter a loop

;; OP-F-L
L1B16: CALL L1280 ; routine G-HD-RC fetches any header and
; matching record

PUSH AF ; preserve return status flags.

; maintain the 'maximum sectors to visit' so only one rotation of tape occurs.

LD A,(IX+$29) ; fetch sector from HDNUMB
ADD A,$03 ; add 3
LD HL,$5CC9 ; address current (max+3) in SECTOR_lo
CP (HL) ; compare
JR C,L1B26 ; forward, if less, to OP-F-X

LD (HL),A ; update with new max sectors to visit.

;; OP-F-X
L1B26: POP AF ; restore status flags.

JR C,L1B49 ; forward, with no name match, to OP-F-4

JR Z,L1B46 ; forward, if unused, to OP-F-3
; to reset map bit.

; the fetched record is one from the file named in CHNAME

RES 0,(IX+$18) ; update CHFLAG to indicate success.

LD A,(IX+$44) ; fetch the record number within file RECNUM
OR A ; test for zero - first record.
JR NZ,L1B41 ; forward, if not, to OP-F-2

PUSH IX ; transfer the channel base address
POP HL ; to the HL register pair.

LD DE,$0052 ; prepare offset to data and
ADD HL,DE ; add to address start of the 512 byte buffer

CALL L142B ; routine CHKS-BUFF checks that checksum agrees.
JR Z,L1B5B ; forward, if OK, to DP-F-5

;; OP-F-2
L1B41: CALL L1258 ; routine GET-R-2 repeatedly calls the
; subroutine G-HD-RC (as at start of loop)
; until the validated record matching CHREC
; (zero) is loaded.

JR L1B5B ; forward, with success, to DP-F-5.

; ---

;; OP-F-3
L1B46: CALL L13E3 ; routine RES-B-MAP resets bit for unused
; sectors.

; the branch was here

;; OP-F-4
L1B49: LD HL,$5CCA ; address visited sector count SECTOR_hi

LD A,(HL) ; fetch sector counter.
INC A ; increment
LD (HL),A ; and put back in SECTOR_hi.
DEC HL ; address the max sector value.
CP (HL) ; compare.
JR C,L1B16 ; back, if less than one revolution, to OP-F-L

; else a full revolution occurred without finding the record.

RES 1,(IX+$43) ; RECFLG
RES 2,(IX+$43) ; RECFLG

; the branch was here with record zero of named file.

;; DP-F-5
L1B5B: POP HL ; restore the offset from CHANS.

RET ; return.

; --------------------------------
; THE 'FORMAT "M" COMMAND' ROUTINE
; --------------------------------
; e.g. FORMAT "m";1;"demos"


;; FORMAT
L1B5D: CALL L10A5 ; routine SET-T-MCH creates a temporary
; Microdrive channel with name of cartridge.

LD A,(IX+$19) ; fetch drive number from CHDRIV
CALL L1565 ; routine SW-MOTOR starts the motor.

LD BC,$32C8 ; decimal 1300
CALL L1652 ; routine DELAY-BC

DI ; Disable Interrupts.

IN A,($EF) ; read Microdrive port.
AND $01 ; isolate write prot. bit.
JR NZ,L1B75 ; forward, if not low, to FORMAT-1

RST 20H ; Shadow Error Restart
DEFB $0E ; Drive 'write' protected

; ---

;; FORMAT-1
L1B75: LD A,$E6 ; enable writing.
OUT ($EF),A ; update Microdrive port.

LD BC,$00FF ; assume 255 sectors will fit on a tape.
LD ($5CC9),BC ; set system variable SECTOR.

PUSH IX ; transfer the channel base address
POP HL ; to the HL register pair.

LD DE,$002C ; offset to HDNAME
ADD HL,DE ;
EX DE,HL ; make destination HDNAME
LD HL,$FFE2 ;
ADD HL,DE ; make source CHNAME

LD BC,$000A ; ten bytes to copy.
LDIR ; copy - C is now zero.

; now prepare an 'unusable' record.

XOR A ; make accumulator zero.

LD (IX+$47),A ; set first character of RECNAM to zero.
SET 0,(IX+$28) ; mark HDFLAG indicate a header.
RES 0,(IX+$43) ; mark RECFLG indicate a record.
SET 1,(IX+$43) ; mark RECFLG indicate an EOF record.

PUSH IX ; transfer the channel base address
POP DE ; to the DE register pair for a change.

LD HL,$0043 ; offset to RECFLG - start of record descriptor.
ADD HL,DE ; add offset to start of record descriptor.

CALL L1426 ; routine CHKS-HD-R inserts 14 byte checksum.

; Now enter a loop to write the blocks to the cartridge

;; WR-F-TEST
L1BAB: CALL L13F7 ; routine DEC-SECT decrements sector originally
; set to $FF
JR Z,L1BDF ; forward, if BC is zero, to TEST-SCT ->

LD (IX+$29),C ; insert reduced sector number in HDNUMB

PUSH IX ; transfer the base channel address
POP HL ; to the HL register pair.

LD DE,L0028 ; offset to the header
ADD HL,DE ; add to address HDFLAG.

CALL L1426 ; routine CHKS-HD-R inserts 14 byte checksum
; preserving the HL value.

LD DE,$FFF4 ; subtract twelve
ADD HL,DE ; to address the header PREAMBLE.

CALL L15AD ; routine OUT-M-HD writes the header to tape.

LD BC,$01B2 ; set timer for gap - 434 decimal.
CALL L1652 ; routine DELAY-BC

PUSH IX ; transfer start of channel
POP HL ; to HL register pair.

LD DE,$0037 ; adjust HL to point to PREAMBLE at
ADD HL,DE ; start of record descriptor.
CALL L16AF ; routine WR-BLK writes record to tape.

LD BC,$0100 ; a short delay.
CALL L1652 ; routine DELAY-BC

CALL L163E ; routine TEST-BRK

JR L1BAB ; loop back to WR-F-TEST for sectors 254 - 1.

; ---

; -> the branch was to here when all sectors from 254 down to 1 have been
; written.

;; TEST-SCT
L1BDF: LD BC,$0087 ; use value 35 decimal.
CALL L1652 ; routine DELAY-BC

LD A,$EE ; signal disable writing.
OUT ($EF),A ; output to Microdrive port.

LD A,(IX+$19) ; select drive number from CHDRIV.
CALL L1532 ; routine SEL-DRIVE.

LD BC,$0032 ; set max sector to fifty, read sectors to zero.
LD ($5CC9),BC ; insert both values in SECTOR

;; CHK-SCT
L1BF6: CALL L13A9 ; routine GET-M-HD2 reads the next valid header
; to pass the tape head.
LD A,(IX+$29) ; fetch the unique sector number from HDNUMB
ADD A,$03 ; add three to value.
LD HL,$5CC9 ; address system variable SECTOR
CP (HL) ; and compare to total of sectors to visit.
JR C,L1C05 ; forward if less to CHK-SCT2

LD (HL),A ; else insert new value for sectors to visit.

;; CHK-SCT2
L1C05: CALL L13C4 ; routine CHECK-MAP checks if sector is free
; on the Microdrive map.
JR Z,L1C1E ; forward, if so, to CHK-NSECT

PUSH IX ; transfer channel base address
POP HL ; to the HL register pair.

LD DE,$0043 ; offset to the start of record descriptor.
ADD HL,DE ; add to address RECFLG.

CALL L165A ; routine READ-BLK reads in a block.

JR NZ,L1C1E ; forward, with bad read, to CHK-NSECT
; leaving map bit set.

CALL L1426 ; routine CHKS-HD-R check the header checksum
JR NZ,L1C1E ; forward, with error, to CHK-NSECT

CALL L13E3 ; routine RES-B-MAP resets the map bit marking
; the sector as usable.

;; CHK-NSECT
L1C1E: LD HL,$5CCA ; address SECTOR_hi the visited sector counter.
LD A,(HL) ; fetch the value.
INC A ; increment
LD (HL),A ; and place back.

DEC HL ; decrement to address max sectors to visit.
CP (HL) ; compare counter to limit.
JR C,L1BF6 ; back, if counter is less, to CHK-SCT

LD L,(IX+$1A) ; load L from CHMAP_lo
LD H,(IX+$1B) ; load H from CHMAP_hi

; Register HL now addresses the Microdrive maps which at this stage have
; sectors 0 and 255 marked as unusable. If as is usual, the lower numbered
; sectors have overwritten the higher numbered sectors then typically
; the top seventy sectors, or so, will be marked as unusable though not on an
; emulated machine which at this stage will only have 0 and 255 marked
; unusable. On a real machine the splice will show up as an unusable sector
; and there may be some other sectors unusable due to dirt on the recording
; film.
; What happens next is unique to this ROM and is no doubt due to extensive
; testing and analysis of the Microdrives by Sinclair Research.
; Microdrive sectors are encountered in descending order, as they are
; written, and the following routine marks any sector following a bad sector
; as bad also. One can conclude that Sinclair Research's test programme
; revealed that the first sectors to fail were those adjacent to contaminated
; or damaged sectors.
; This perhaps explains why my use of the Microdrives with ROM 2 has been
; more reliable than early reviews, no doubt with ROM 1, suggested.

LD DE,$001F ; add thirty one to start at the end of the map
ADD HL,DE ; - the byte that refers to sector 255.
LD B,$20 ; count the thirty two bytes of a map.

SCF ; set carry flag to ensure that sector 255
; is unusable - but it is already marked so ??

;; PREP-MARK
L1C35: LD A,(HL) ; fetch a byte representing eight sectors.

LD C,A ; and store it in C - Note. unnecessary.

RRA ; rotate right accumulator C->76543210->C

OR C ; combine with original value. Why not OR (HL) ?

LD (HL),A ; store the modified byte back in the map.

DEC HL ; point to the next byte for lower-numbered
; sectors.

L1C3B: DJNZ L1C35 ; loop back to PREP-MARK for all 32 map bytes.

; Note. the above routine is untidy. There is no need to set the carry flag
; and no need to store the original value in C. While it achieves it's aims,
; if sector one is bad it has no effect on the next sector to be encountered.
; That would be hard to implement but the first sector that is marked bad,
; the highest numbered sector, is marked so solely because it is adjacent to
; the overwritten section.

; Note. from details of addresses Andrew Pennell gave in the magazine "Your
; Sinclair" it can be deduced that the unpublished ROM 3 had two extra
; instruction bytes at this point and together with a cleanup, this may have
; addressed the above issue.

; Now prepare to overwrite the unusable sectors (which are mapped as usable)
; with record descriptors which are usable.

CALL L1E49 ; routine IN-CHK marks the channel record
; descriptor fields as usable by blanking
; both RECFLG and RECLEN and then inserting
; the descriptor checksum.

; A loop is now entered to write usable datablocks to every sector indicated
; as usable in the Microdrive map.

;; MARK-FREE
L1C40: CALL L1349 ; routine CHK-FULL checks if there is still a
; usable sector on the cartridge.
JR NZ,L1C4D ; forward, if so, to MK-BLK.

; The FORMAT operation is now complete.

XOR A ; select no motor
CALL L1532 ; routine SEL-DRIVE stops the Microdrive motor.

CALL L119F ; routine DEL-M-BUF deletes the Microdrive
; buffer and the Microdrive map.

RET ; return. >>>>>>>

; ---

;; MK-BLK
L1C4D: CALL L135A ; routine SEND-BLK writes block to Microdrive
; cartridge as indicated by the Microdrive map
; which is then updated by the routine.

JR L1C40 ; loop back to MARK-FREE


; -------------------------
; THE 'CAT COMMAND' ROUTINE
; -------------------------
;

;; CAT
L1C52: LD A,($5CD8) ; fetch output stream from S_STR1

RST 10H ; CALBAS
DEFW $1601 ; main CHAN-OPEN

CALL L10A5 ; routine SET-T-MCH sets a temporary channel.

LD A,(IX+$19) ; fetch drive number from CHDRIV.
CALL L1532 ; routine SEL-DRIVE starts the motor.

LD BC,$0032 ; set maximum sector to 50 and initialize
; value of sectors read to zero.
LD ($5CC9),BC ; update system variable SECTOR

; On the original Interface 1 ROM operations like CAT and ERASE were quite
; slow as the routines assumed the theoretical maximum number of sectors was
; 256. In reality, the maximum number of sectors on a Microdrive is
; approximately 180, so the original routines spent the last 3 seconds
; reading about 75 sectors for the second time. The improved algorithm above
; is to keep a record of the maximum sector + 3 and when the number of
; visited sectors is equal to this number then a complete revolution of the
; tape has been made and the operation can cease. The overhead of three is
; to ensure that bad sectors or the tape splice do not cause the operation to
; end prematurely.
; Happily, this algorithm also works with emulators which usually provide the
; full 256 sectors.

;; CAT-LP
L1C68: CALL L13A9 ; routine GET-M-HD2 reads in 14 byte header.

LD A,(IX+$29) ; fetch value of sector from HDNUMB

ADD A,$03 ; add 3 to value.

LD HL,$5CC9 ; address system variable SECTOR_lo
CP (HL) ; compare to contents
JR C,L1C77 ; forward if A is less to CAT-LP-E

LD (HL),A ; else update SECTOR_lo with higher value.

;; CAT-LP-E
L1C77: CALL L1E5E ; routine G-RDES loads only a
; 14 byte record descriptor.
JR NZ,L1C68 ; back, with error or mismatch, to CAT-LP

; a record can be considered in use if either the RECLEN is maximum $0200 or
; the RECFLG indicates that it is the seldom full EOF record.

LD A,(IX+$43) ; RECFLG
OR (IX+$46) ; RECLEN_hi
AND $02 ;
JR NZ,L1C8B ; forward, if used, to IN-NAME

; else mark sector free in Microdrive map and find next sector.

CALL L13E3 ; routine RES-B-MAP
JR L1CF4 ; forward to F-N-SCT

; a name is to be inserted in the 512 byte data buffer workspace, if it is not
; there already. Secret files are not listed.

;; IN-NAME
L1C8B: LD A,(IX+$47) ; take first character of RECNAM
OR A ; test for zero.
JR Z,L1CF4 ; forward, if CHR$ 0, to F-N-SCT

PUSH IX ; transfer base address
POP HL ; to HL register.

LD DE,$0052 ; offset to start of data buffer.
ADD HL,DE ; add to address names.
LD DE,$000A ; set DE to ten, the length of a name.
LD B,$00 ; set high byte to zero.
LD C,(IX+$0D) ; fetch name total from CHREC initially zero.

;; SE-NAME
L1CA0: LD A,C ; test name count for zero
OR A ;
JR Z,L1CDA ; forward, with first name, to INS-NAME

PUSH HL ; save buffer address.
PUSH IX ; save channel base address.
PUSH BC ; save name total.

LD B,$0A ; set character counter to ten.

;; T-NA-1
L1CAA: LD A,(HL) ; take letter of buffered name.
CP (IX+$47) ; compare to that in RECNAM
JR NZ,L1CB5 ; forward, with mismatch, to T-NA-2

INC HL ; increment
INC IX ; both pointers.
DJNZ L1CAA ; back, for all ten, to T-NA-1


;; T-NA-2
L1CB5: POP BC ; restore
POP IX ; all
POP HL ; pointers.

; if all ten characters match then find next sector.

JR Z,L1CF4 ; forward to F-N-SCT

; if buffered name is higher than new name then re-order to create a slot.

JR NC,L1CC1 ; forward to ORD-NAM

; else add ten to buffer address and compare with following name performing
; a simple insert if the end of the list is reached.

ADD HL,DE ; add ten to address.
DEC C ; decrement name counter.
JR L1CA0 ; back to SE-NAME

; ---


;; ORD-NAM
L1CC1: PUSH HL ; save pointer to start of name slot.
PUSH DE ; save the value ten.
PUSH BC ; save the buffered name counter.

PUSH HL ; save address of name slot again.
SLA C ; double name count.
LD H,B ; set H to zero.
LD L,C ; HL = 2 * count
ADD HL,BC ; HL = 4 * count
ADD HL,BC ; HL = 6 * count Note. add hl,hl doubles.
ADD HL,BC ; HL = 8 * count c.f. Main ROM
ADD HL,BC ; HL = 10 * count

LD B,H ; transfer number of bytes
LD C,L ; to be moved to BC register.

POP HL ; restore address of insertion point.
DEC HL ; decrement and then add
ADD HL,BC ; bytes to be moved to point to end of block.

EX DE,HL ; now make DE
ADD HL,DE ; the destination
EX DE,HL ; ten bytes higher.

LDDR ; slide the block of higher names upwards.

POP BC ; restore name count.
POP DE ; restore ten value.
POP HL ; restore insertion point.

;; INS-NAME
L1CDA: PUSH IX ; save channel base address.
LD B,$0A ; set character count to ten.

;; MOVE-NA
L1CDE: LD A,(IX+$47) ; fetch a character from new name at RECNAM
LD (HL),A ; insert into buffer.
INC IX ; increment both
INC HL ; pointers.
DJNZ L1CDE ; loop back to MOVE-NA

POP IX ; restore channel base address.

LD A,(IX+$0D) ; fetch count of names from CHREC
INC A ; increment
LD (IX+$0D),A ; and store back in CHREC

CP $32 ; compare to maximum of 50.
JR Z,L1CFF ; forward, if buffer filled, to BF-FILLED


;; F-N-SCT
L1CF4: LD HL,$5CCA ; sv SECTOR_hi
LD A,(HL) ; fetch actual count of used sectors.
INC A ; and increment.
LD (HL),A ; update SECTOR_hi
DEC HL ; address system variable SECTOR_lo
CP (HL) ; compare
JP C,L1C68 ; jump to CAT-LP

;; BF-FILLED
L1CFF: PUSH IX ;

XOR A ; clear accumulator
CALL L1532 ; routine SEL-DRIVE stops the motor.

PUSH IX ; transfer the channel base address
POP HL ; to the HL register pair.

LD DE,$002C ; offset to cartridge name HDNAME.
ADD HL,DE ; add the offset to address the name.

CALL L1D5B ; routine PRNAME prints name and a carriage
; return.

LD A,$0D ; prepare an extra carriage return.
CALL L1D71 ; routine PRCHAR outputs it.

PUSH IX ;
POP HL ;

LD DE,$0052 ; offset to CHDATA - the 512 byte data buffer.
ADD HL,DE ; add to address list of up to fifty names.

LD B,(IX+$0D) ; load B with count of names from CHREC
LD A,B ; test for
OR A ; zero.
JR Z,L1D27 ; forward, if so, to NONAMES


;; OT-NAMS
L1D22: CALL L1D5B ; routine PRNAME
DJNZ L1D22 ; loop back to OT-NAMS


;; NONAMES
L1D27: CALL L1D43 ; routine FREESECT

LD A,E ;
SRL A ;

RST 10H ; CALBAS
DEFW $2D28 ; main STACK-A

LD A,$0D ;
CALL L1D71 ; routine PRCHAR

RST 10H ; CALBAS
DEFW $2DE3 ; main PRINT-FP

LD A,$0D ;
CALL L1D71 ; routine PRCHAR

POP IX ;

CALL L119F ; routine DEL-M-BUF

RET ; return.


; ----------------------
; THE 'FREESECT' ROUTINE
; ----------------------
; This routine is called from SAVE and CAT to calculate the number of free
; sectors that are present on a Microdrive from the map information.
; The count of free sectors is returned in the E register.

;; FREESECT
L1D43: LD L,(IX+$1A) ; address of Microdrive map.
LD H,(IX+$1B) ; for channel transferred to HL.

LD E,$00 ; initialize sector count to zero.
LD C,$20 ; there are thirty two bytes to examine.

;; FR-SC-LP
L1D4D: LD A,(HL) ; fetch a byte from the map.
INC HL ; address next map location.

LD B,$08 ; count eight bits.

;; FR-S-LPB
L1D51: RRA ; rotate right.
JR C,L1D55 ; forward, with carry, to FR-S-RES.

INC E ; increment the free sector count.

;; FR-S-RES
L1D55: DJNZ L1D51 ; loop back for all eight bits to FR-S-LPB.

DEC C ; decrement byte count.
JR NZ,L1D4D ; loop back for thirty two bytes to FR-SC-LP.

RET ; return.

; --------------------
; THE 'PRNAME' ROUTINE
; --------------------
; This routine outputs a ten character name, followed by a carriage return,
; and is used by the CAT command to first print the cartridge name and then
; the filenames on the cartridge.
; Note. For a routine that can output to any stream, it seems straightforward
; until one notices the call to TEMPS at the end. This applies the permanent
; colour screen attributes to the temporary set and has been placed within
; the routine as a security measure to ensure that if the cartridge name
; or filename contains a string of colour control codes that render filenames
; invisible then their effect does not last beyond the current name.
;
; On the other hand, colour control codes can be used in the cartridge name
; without affecting the cartridge contents display.

;; PRNAME
L1D5B: PUSH BC ; preserve name count.

LD B,$0A ; ten characters per name.

;; PRNM-LP
L1D5E: LD A,(HL) ; fetch a character.

CALL L1D71 ; routine PRCHAR

INC HL ; point to next character.
DJNZ L1D5E ; loop back for all ten to PRNM-LP

LD A,$0D ; prepare a carriage return.
CALL L1D71 ; routine PRCHAR

PUSH HL ; preserve character address.

RST 10H ; CALBAS
DEFW $0D4D ; main TEMPS restores temporary colours from
; the permanent colours after each name.

POP HL ; restore character address.
POP BC ; restore name count.

RET ; return.


; --------------------
; THE 'PRCHAR' ROUTINE
; --------------------
; The PRINT CHARACTER routine utilizes the output restart in the main ROM
; which outputs to any stream and so a stream such as the "T" channel
; could be sent output. The IX register has therefore to be preserved.

;; PRCHAR
L1D71: PUSH IX ; preserve this ad hoc channel address.

RST 10H ; CALBAS
DEFW $0010 ; main PRINT-A

POP IX ; restore this channel address.

RET ; return.


; ---------------------------
; THE 'ERASE COMMAND' ROUTINE
; ---------------------------
; (Hook Code: $24)
; The ERASE command is in two stages and uses the first 32 bytes of the
; otherwise unused data buffer to map out the sectors to be marked clear.
; The first stage performs this mapping and in one revolution of the tape
; it should find all sectors that have the specified name. It should also
; find the EOF record, which all files have, and which contains in the
; RECNUM field the maximum record number. For example with four records the
; numbers will be 0, 1, 2, 3.
; Once the number of marked records equals the max record plus one then the
; second stage can begin which is to mark free all the records.
;
; There are two circumstances under which the procedure might not be so
; straightforward.
; The first is if the user has pressed BREAK during a previous ERASE
; operation after a few records were marked free.
; The second is if the file has been saved with the System Variable COPIES
; holding a value larger than 1. For example with a value of 5, there will
; be five EOF records and five records with RECNUM equal to zero etc.
;
; For the first case the command will make five revolutions of the tape
; before marking all found sectors free.
; This may happen in the second case also if more multi records were found
; before the first EOF record was encountered.
; It is more likely that the ERASE command will have to be invoked several
; times to erase the file. It is simpler to issue the command within a
; loop. Multiple copy files are usually saved as part of a well-considered
; scheme and are seldom subsequently erased.

;; ERASE
L1D79: CALL L10A5 ; routine SET-T-MCH creates a temporary channel
; using either an existing Microdrive map from
; a channel also using this drive or allocating
; a new one initialized to $FF bytes.

LD A,(IX+$19) ; fetch drive number from CHDRIV.
CALL L1532 ; routine SEL-DRIVE starts motor.

IN A,($EF) ; read Microdrive port.
AND $01 ; isolate 'write prot.' bit.
JR NZ,L1D8A ; forward, if not zero, to ERASE-1

RST 20H ; Shadow Error Restart
DEFB $0E ; Drive 'write' protected


;; ERASE-1
L1D8A: PUSH IX ; transfer address of start of channel.
POP HL ; to the HL register.

LD DE,$0052 ; prepare offset to data buffer.
ADD HL,DE ; add to address start.

; A pseudo Microdrive map will also be created within the buffer conserving
; memory. This is initialized to $00 bytes.

PUSH HL ; transfer buffer address
POP DE ; from HL to DE register
INC DE ; and increment address.

LD BC,$001F ; set counter to 31 and B to zero.
XOR A ; set A to zero.
LD (HL),A ; insert zero in first location.
LDIR ; copy to other 31 addresses

LD A,$FF ; prepare, as a default, to examine every sector.
LD (IX+$0D),A ; update CHREC with max record number.

LD BC,$04FB ; prepare decimal 1275 (5+ revolutions)
LD ($5CC9),BC ; update system variable SECTOR

; Note. if the EOF record is not found, or if the number of found sectors
; doesn't equal the maximum record then 5+ revolutions of the tape will
; occur after which all mapped sectors will be erased. Normally with a
; simple file it's all over in less than two revolutions.

;; ERASE-LP
L1DA7: CALL L13F7 ; routine DEC-SECT decrements the 1275 counter.
JR Z,L1E03 ; forward, if zero, to ERASE-MK

CALL L13A9 ; routine GET-M-HD2 reads the next 14-byte
; header to pass the tape heads.

CALL L1E5E ; routine G-RDES reads the corresponding
; 14-byte record descriptor for this sector.

JR NZ,L1DE5 ; forward, with read error, to TST-NUM

; now check if sector is in use. Considered it so if next position is
; at $0200 or if it is the EOF record.

LD A,(IX+$43) ; RECFLG
OR (IX+$46) ; RECLEN_hi
AND $02
JR NZ,L1DC3 ; forward, if in use, to ERASE-2
; to consider for erasure.

; the sector is not used so reset the REAL Microdrive map bit.

CALL L13E3 ; routine RES-B-MAP resets sector bit on
; the REAL Microdrive map.

JR L1DE5 ; forward to TST-NUM

; ---

; consider for erasure if filename matches.

;; ERASE-2
L1DC3: PUSH IX ; transfer channel base address
POP HL ; to the HL register.

LD DE,$0047 ; offset to 10 characters of filename.
ADD HL,DE ; add so HL addresses the start of RECNAM.
LD BC,$000A ; ten bytes to compare against required CHNAME.

CALL L1403 ; routine CHK-NAME

JR NZ,L1DE5 ; forward, with no match, to TST-NUM

; the name matches so sector is marked free.

CALL L13EB ; routine TEST-PMAP obtains address of sector
; bit in HL and bit mask in B.

LD A,B ; transfer mask to B
OR (HL) ; combine with addressed byte
LD (HL),A ; and update setting the sector bit.

BIT 1,(IX+$43) ; test RECFLG is this an EOF record.
JR Z,L1DE5 ; forward, if not, to TST-NUM

; All files should have an EOF record and, if this is it, then the endpoint
; can be reduced from $FF to record number plus one as range starts at 1.

LD A,(IX+$44) ; fetch record number from RECNUM
INC A ; increment as CHREC value starts at one not
; zero.
LD (IX+$0D),A ; update the endpoint CHREC

;; TST-NUM
L1DE5: PUSH IX ; transfer the channel base address
POP HL ; to the HL register.

LD DE,$0052 ; add offset to data
ADD HL,DE ; to address the pseudomap.

LD E,$00 ; initialize E to zero.
LD C,$20 ; and C counter to thirty two.

;; LP-P-MAP
L1DF0: LD A,(HL) ; fetch a byte from pseudomap
INC HL ; and increment the address.

LD B,$08 ; set bit counter to eight.

;; LP-B-MAP
L1DF4: RRA ; rotate end bit to carry.
JR NC,L1DF8 ; forward, with no carry, to NOINC-C

INC E ; increment recno

;; NOINC-C
L1DF8: DJNZ L1DF4 ; back to LP-B-MAP for all eight bits.

DEC C ; decrement byte counter.
JR NZ,L1DF0 ; back to LP-P-MAP for all 32 bytes.

; now E holds the number of records marked for erasure in range 1 to NR.

LD A,(IX+$0D) ; fetch records to be erased from CHREC
CP E ; compare to records marked for erasure.
JR NZ,L1DA7 ; back, if not exact match, to ERASE-LP

; Now the second stage begins. Since the pseudomap has a representation of
; all the records to be erased we can load the headers one by one, and
; rewrite the corresponding records with a clear one in the channel.
; The same record is written after all the appropriate headers. Fields
; like RECNUM only have relevance when the record is in use.
;
; First prepare a clear record descriptor. The actual data buffer does not
; have to be clear and in fact contains the pseudomap. Note also that the
; checksum for the data need not be calculated but the checksum for the
; record descriptor is required to be accurate.

;; ERASE-MK
L1E03: CALL L1E49 ; routine IN-CHK marks the channel record
; descriptor fields as usable by blanking
; both RECFLG and RECLEN and then inserting
; the descriptor checksum.

; now enter a loop for all marked records.

;; ERASE-MK2
L1E06: CALL L13A9 ; routine GET-M-HD2 reads the next header
; to pass the tape heads.
CALL L13EB ; routine TEST-PMAP checks if the sector,
; (in HDNUMB) is marked to be erased in the
; pseudomap.
JR Z,L1E31 ; forward, if not, to T-OTHER

; this record is marked for erasure.

PUSH HL ; save pseudomap sector bit address.
PUSH BC ; save pseudomap bit mask which has one set bit.

LD A,$E6 ; enable writing.
OUT ($EF),A ; output to Microdrive port.

LD BC,$0168 ; set counter to 360 decimal.
CALL L1652 ; routine DELAY-BC pauses briefly as the
; record now approaches the tape heads.

PUSH IX ; transfer channel base address
POP HL ; to the HL register pair.

LD DE,$0037 ; offset to record PREAMBLE.
ADD HL,DE ; add to form start of save address.

CALL L15B3 ; routine OUT-M-BUF rewrites descriptor and
; data buffer. The descriptor is checksummed,
; the data is not.

LD A,$EE ; disable writing
OUT ($EF),A ; output to Microdrive port

; now update bit the real Microdrive map and the pseudomap.

CALL L13E3 ; routine RES-B-MAP resets appropriate bit
; for the now free sector in the REAL
; Microdrive map.

POP BC ; restore the pseudomap bit mask.
POP HL ; restore the pseudomap sector bit address.

LD A,B ; transfer bitmask to B.
CPL ; the set bit is now reset and the other seven
; bits are set.
AND (HL) ; reset the bit in the pseudomap
LD (HL),A ; and update.

; now check if there are any more sectors to do.

;; T-OTHER
L1E31: PUSH IX ; transfer channel base address
POP HL ; to the HL register.

LD DE,$0052 ; prepare offset to the pseudomap
ADD HL,DE ; and add to address start of map.

LD B,$20 ; set byte count to thirty two.

;; CHK-W-MAP
L1E3A: LD A,(HL) ; fetch a byte representing eight sectors.
OR A ; test for zero.
JR NZ,L1E06 ; back, if a byte is not zero, to ERASE-MK2

INC HL ; increment the map address
DJNZ L1E3A ; loop back to CHK-W-MAP for all 32 bytes.

; at this point all records have been erased and it only remains to clear up.

XOR A ; select no motor
CALL L1532 ; routine SEL-DRIVE stops the motor.

CALL L119F ; routine DEL-M-BUF deletes the adhoc buffer.

RET ; return.


; -----------------------------------
; THE 'PREPARE 'FREE SECTOR'' ROUTINE
; -----------------------------------
; The two indicators within the current channel are marked clear and the
; RECORD DESCRIPTOR is checksumed in preparation for writing to each sector
; to be marked free.

;; IN-CHK
L1E49: XOR A ; clear accumulator A.
LD (IX+$43),A ; blank RECFLG.
LD (IX+$45),A ; blank RECLEN_lo.
LD (IX+$46),A ; blank RECLEN_hi.

PUSH IX ; transfer the start of channel
POP HL ; to the HL register pair.

LD DE,$0043 ; prepare the offset to RECFLG.
ADD HL,DE ; add to form start of record descriptor.

CALL L1426 ; routine CHKS-HD-R inserts 14-byte checksum.

RET ; return.

; --------------------------------------
; THE 'OBTAIN RECORD DESCRIPTOR' ROUTINE
; --------------------------------------
; This routine is used by CAT, ERASE and the GET-DESC Hook Code $33.
; It loads and verifies the 14 byte record descriptor from RECFLG to RECNAM.
; This is normally loaded with the following data block
; or with the header block.
; The Zero Flag is set upon successful completion.

;; G-RDES
L1E5E: PUSH IX ; transfer channel address
POP HL ; to HL register.

LD DE,$0043 ; offset to RECFLG
ADD HL,DE ; add to form first receiving location.
CALL L15E2 ; routine GET-M-HD reads in 15 bytes.
CALL L1426 ; routine CHKS-HD-R checksums the first 14 bytes
RET NZ ; return with checksum error.

BIT 0,(IX+$43) ; test bit 0 of RECFLAG - should be zero
RET ; return.

; -----------------------
; THE 'HOOK-CODE' ROUTINE
; -----------------------
; This accesses the twenty six hook codes now reduced to the range $00 - $19.

;; HOOK-CODE
L1E71: CP $1A ; compare to upper limit.
JR C,L1E77 ; forward, if valid, to CLR-ERR.

RST 20H ; Shadow Error Restart.
DEFB $12 ; Hook code error.

; ---

;; CLR-ERR
L1E77: LD (IY+$00),$FF ; set ERR_NR to one less than zero - no error.

SET 2,(IY+$01) ; update FLAGS signal 'L' mode.

INC HL ; step past the hook code location in RAM.
EX (SP),HL ; make this the return address.
PUSH HL ; push back what was at stack pointer - the
; preserved value of AF on entry.

ADD A,A ; double the code.
LD D,$00 ; set D to zero for indexing.
LD E,A ; transfer the code to E.

LD HL,L1E99 ; address: HOOK-TAB the base of the Hook Codes.
ADD HL,DE ; index into this table.

LD E,(HL) ; low byte to E.
INC HL ; increment pointer.
LD D,(HL) ; high byte to D.

POP AF ; restore AF from machine stack.

LD HL,L0700 ; push the address UNPAGE
PUSH HL ; on the machine stack.

EX DE,HL ; transfer address to HL.
JP (HL) ; jump to Hook Code routine.


; ---------------------------
; THE 'HOOK CODE +32' ROUTINE
; ---------------------------
; (Hook Code: $32)
; This allows the user to call any address in the shadow ROM.

;; HOOK-32
L1E94: LD HL,($5CED) ; sv HD_11
JP (HL) ; jump to routine.


; ---------------------------
; THE 'HOOK CODE +31' ROUTINE
; ---------------------------
; (Hook Code: $31)
; This Hook Code ensures that the extra System Variables are created. Since
; this has already occurred, as is the case with all Hook Codes, then all that
; remains to do is to return to the address on the stack - the UNPAGE location.

;; HOOK-31
L1E98: RET ; return.


; -------------------------------
; THE 'HOOK CODE ADDRESSES' TABLE
; -------------------------------
; The addresses of the Hook Codes. The last two are new to this ROM.

;; HOOK-TAB
L1E99: DEFW L1ECD ; $1B - CONS-IN
DEFW L1EE0 ; $1C - CONS-OUT
DEFW L0B88 ; $1D - BCHAN-IN
DEFW L0D07 ; $1E - BCHAN-OUT
DEFW L1EF0 ; $1F - PRT-OUT
DEFW L1EF5 ; $20 - KBD-TEST
DEFW L1532 ; $21 - SEL-DRIVE
DEFW L1B05 ; $22 - OP-TEMP-M
DEFW L138E ; $23 - CLOSE-M2
DEFW L1D79 ; $24 - ERASE
DEFW L1EFD ; $25 - READ-SEQ
DEFW L12DA ; $26 - WR-RECD
DEFW L1F0B ; $27 - RD-RANDOM
DEFW L1F3F ; $28 - RD-SECTOR
DEFW L1F7A ; $29 - RD-NEXT
DEFW L1F85 ; $2A - WR-SECTOR
DEFW L10A5 ; $2B - SET-T-MCH
DEFW L119F ; $2C - DEL-M-BUF
DEFW L0F46 ; $2D - OP-TEMP-N
DEFW L1F18 ; $2E - CLOSE-NET
DEFW L1F25 ; $2F - GET-PACK
DEFW L0E4F ; $30 - SEND-PACK
DEFW L1E98 ; $31 - HOOK-31
DEFW L1E94 ; $32 - HOOK-32

DEFW L1FE4 ; $33 - GET-DESC
DEFW L1FF6 ; $34 - OP-B-CHAN


; ---------------------------
; THE 'CONSOLE INPUT' ROUTINE
; ---------------------------
; (Hook Code: $1B)

;; CONS-IN
L1ECD: EI ; enable interrupts.
RES 5,(IY+$01) ; update FLAGS signal no new key pressed.

;; WTKEY
L1ED2: HALT ; wait for an interrupt.

RST 10H ; CALBAS
DEFW $02BF ; main KEYBOARD

BIT 5,(IY+$01) ; test FLAGS - new key ?
JR Z,L1ED2 ; loop back, if not, to WTKEY

LD A,($5C08) ; place decoded key in system variable LASTK
RET ; return.


; ----------------------------
; THE 'CONSOLE OUTPUT' ROUTINE
; ----------------------------
; (Hook Code: $1C)
; outputs a character to the unalterable system stream for the console.

;; CONS-OUT
L1EE0: PUSH AF ; save character to be output.
LD A,$FE ; use system stream $FE - upper screen.

; ->

;; OUT-CODE
L1EE3: LD HL,$5C8C ; address system variable SCR_CT.
LD (HL),$FF ; load with a high number to suppress scroll
; prompt.

RST 10H ; CALBAS
DEFW $1601 ; main CHAN-OPEN opens selected stream.

POP AF ; fetch the preserved print character.

RST 10H ; CALBAS
DEFW $0010 ; main PRINT-A prints character in accumulator.


RET ; return.


; ----------------------------
; THE 'PRINTER OUTPUT' ROUTINE
; ----------------------------
; (Hook code: $1D)
; outputs a character to stream 3.

;; PRT-OUT
L1EF0: PUSH AF ; preserve character to be printed
LD A,$03 ; select stream 3
JR L1EE3 ; back to OUT-CODE


; ---------------------------
; THE 'KEYBOARD TEST' ROUTINE
; ---------------------------
; ( Hook Code: $20 )
; Normally a single reset bit in A determines which half row is read but by
; resetting all bits the entire keyboard is read. A pressed key will cause
; a bit to be reset. Routine returns with zero flag set if no keys pressed,
; NZ otherwise.

;; KBD-TEST
L1EF5: XOR A ; reset all eight high-order bits.
IN A,($FE) ; read the entire keyboard.
AND $1F ; retain any unpressed keys - will be $1F if
; no key.
SUB $1F ; subtract to give zero if no keys.
RET ; return.


; -------------------------------
; THE 'READ SEQUENTIAL' HOOK CODE
; -------------------------------
; (Hook Code: $25)
;

;; READ-SEQ
L1EFD: BIT 1,(IX+$43) ; RECFLG
JR Z,L1F08 ; forward to INCREC

LD (IY+$00),$07 ; set ERR_NR to '8 End of file'
RST 28H ; Error Main ROM

; ---

;; INCREC
L1F08: INC (IX+$0D) ; increment the required record in CHREC
; and continue into next routine...

; ---------------------------
; THE 'READ RANDOM' HOOK CODE
; ---------------------------
; (Hook Code: $27)
; reads a PRINT record randomly.

;; RD-RANDOM
L1F0B: CALL L1252 ; routine GET-RECD gets the record specified
; by CHREQ matching filename CHNAME from the
; cartridge in the drive CHDRIV which is
; started.

BIT 2,(IX+$43) ; test RECFLG - is it a PRINT type file.
RET Z ; return if so.

CALL L119F ; routine DEL-M-BUF reclaims the permanent
; channel thus losing the buffer contents.

RST 20H ; Shadow Error Restart
DEFB $16 ; 'Wrong file type'

; -------------------------------------
; THE 'CLOSE NETWORK CHANNEL' HOOK CODE
; -------------------------------------
; (Hook Code: $2E)
; Hook Code Only

;; CLOSE-NET
L1F18: CALL L0FAE ; routine SEND-NEOF

PUSH IX ; pick up start address
POP HL ; of the channel.

LD BC,$0114 ; bytes to reclaim.

RST 10H ; CALBAS.
DEFW $19E8 ; main RECLAIM-2.

RET ; return.


; -------------------------------------
; THE 'GET PACKET FROM NETWORK' ROUTINE
; -------------------------------------
; (Hook Code: $2F)

;; GET-PACK
L1F25: LD A,($5CC6) ; sv IOBORD
OUT ($FE),A ;
DI
CALL L0FD3 ; routine WT-SC-E
JR NC,L1F3A ; forward to GP-ERROR

CALL L0EB5 ; routine GET-NBLK
JR NZ,L1F3A ; forward to GP-ERROR

EI ;
AND A ;
JP L0D4D ; jump to BORD-REST

; ---

;; GP-ERROR
L1F3A: SCF ;
EI ;
JP L0D4D ; jump to BORD-REST


; ---------------------------
; THE 'READ SECTOR' HOOK CODE
; ---------------------------
; (Hook Code: $28)
; fetches header from sector specified by CHREC.
; If the sector is from a PRINT type file then it returns with success.
; Otherwise if a program or code file the data area is 'cleared'.

;; RD-SECTOR
L1F3F: LD HL,$00FF ; ensure every sector is tried.
; Note. was $F0 (240) in original ROM which
; would not be compatible with emulators.
LD ($5CC9),HL ; update temporary variable SECTOR

;; NO-GOOD
L1F45: CALL L13A9 ; routine GET-M-HD2 reads the next header
; to pass the tape heads.

LD A,(IX+$29) ; fetch sector number from HDNUMB
CP (IX+$0D) ; compare with required sector in CHREC
JR Z,L1F57 ; forward, with match, to USE-C-RC

CALL L13F7 ; routine DEC-SECT decrements the counter.
JR NZ,L1F45 ; loop back, if not zero, to NO-GOOD

RST 20H ; Shadow Error Restart
DEFB $11 ; 'File not found'

; ---

;; USE-C-RC
L1F57: PUSH IX ; transfer channel base address
POP HL ; to the HL register.

LD DE,$0043 ; offset to RECFLG
ADD HL,DE ; add to address start of record descriptor.

CALL L15EB ; routine GET-M-BUF reads in the record
; descriptor and the 512 bytes of data.

CALL L1426 ; routine CHKS-HD-R checksums the descriptor.

JR NZ,L1F75 ; forward, with error, to DEL-B-CT

LD DE,$000F ; additional offset to data.
ADD HL,DE ; add to address data.

CALL L142B ; routine CHKS-BUFF checksums the data buffer.
JR NZ,L1F75 ; forward, with error, to DEL-B-CT

OR A ; clear carry
BIT 2,(IX+$43) ; test RECFLG - is this a PRINT file ?
RET Z ; return if so.

;; DEL-B-CT
L1F75: CALL L1FD4 ; routine CLR-BUFF sets descriptor and data
; contents to same values.
SCF ; signal error.
RET ; return from hook-code.


; --------------------------------
; THE 'READ NEXT SECTOR' HOOK CODE
; --------------------------------
; (Hook Code: $29)
; This hook code just reads the next header to pass the tape head and then,
; without further qualification, reads the corresponding data using the
; routine above. If not a PRINT file then the data is cleared.
; It needlessly sets up a sector counter in the System Variable SECTOR.

;; RD-NEXT
L1F7A: LD HL,$00FF ; set count to 255. Note. not used.
LD ($5CC9),HL ; insert in system variable SECTOR.

CALL L13A9 ; routine GET-M-HD2 reads the next header
; to pass the tape heads.

JR L1F57 ; back to USE-C-RC to read and validate the
; corresponding descriptor and data.

; ----------------------------
; THE 'WRITE SECTOR' HOOK CODE
; ----------------------------
; (Hook Code: $2A)
; writes to Microdrive the sector in CHREC.

;; WR-SECTOR
L1F85: LD HL,$00FF ; set counter to ensure at least one revolution
LD ($5CC9),HL ; of the tape and update SECTOR

PUSH IX ; transfer base address
POP HL ; of channel to HL.

LD DE,$0037 ; offset to header preamble
ADD HL,DE ; add and
PUSH HL ; preserve location on machine stack.

LD DE,$000C ; offset past preamble to RECFLG
ADD HL,DE ; the start of the record descriptor.

CALL L1426 ; routine CHKS-HD-R insert checksum byte.

LD DE,$000F ; 15 byte offset to start of data.
ADD HL,DE ; add to address first of 512 bytes.
CALL L142B ; routine CHKS-BUFF inserts buffer checksum.

;; WR-S-1
L1FA1: CALL L13A9 ; routine GET-M-HD2 reads any header.
LD A,(IX+$29) ; fetch sector from HDNUMB
CP (IX+$0D) ; compare to required sector in CHREC
JR Z,L1FB3 ; forward, with match, to WR-S-2

CALL L13F7 ; routine DEC-SECT decrements the counter
JR NZ,L1FA1 ; back, if not zero, to WR-S-1

; else the header was not found after a complete tape revolution.

RST 20H ; Shadow Error Restart
DEFB $11 ; File not found

; ---

;; WR-S-2
L1FB3: IN A,($EF) ; read Microdrive port.
AND $01 ; isolate 'write prot.' bit.
JR NZ,L1FBB ; forward, if not, to WR-S-3

RST 20H ; Shadow Error Restart
DEFB $0E ; Drive 'write' protected

; ---

;; WR-S-3
L1FBB: LD A,$E6 ; enable writing
OUT ($EF),A ; output to port.

LD BC,$0168 ; set delay to 360
CALL L1652 ; routine DELAY-BC pauses briefly as the
; record now approaches the tape heads.

POP HL ; restore pointer to RECFLG
CALL L15B3 ; routine OUT-M-BUF writes descriptor and
; data buffer.

LD A,$EE ; disable writing
OUT ($EF),A ; output to port.

CALL L13C4 ; routine CHECK-MAP fetches bit mask for map
; location addressed by HL into B register.

LD A,B ; transfer mask to accumulator
OR (HL) ; combine with any set bits already there.
LD (HL),A ; update map marking sector used.

RET ; return.

; -----------------------------------
; THE 'CLEAR BUFFER CONTENTS' ROUTINE
; -----------------------------------
; This routine sets the contents of the 14 byte record descriptor and
; the 512 byte data buffer to the same value so that they are unreadable.
; This is invoked when the possibility that a secret file, whose name begins
; with CHR$ 0 has been read.

;; CLR-BUFF
L1FD4: PUSH IX ; transfer the channel base
POP HL ; address to HL.

LD DE,$0028 ; offset to HDFLAG.
ADD HL,DE ; add to base address.

LD D,H ; transfer same
LD E,L ; address to DE and
INC DE ; make one higher.

LD BC,$0229 ; set counter to 553 bytes.
LDIR ; fill with HDFLAG contents.

RET ; return.

; ---------------------------------------
; THE 'FETCH RECORD DESCRIPTOR' HOOK CODE
; ---------------------------------------
; (Hook Code: $33)
; Note. new in this ROM.
; This Hook Code reads the next header and corresponding record descriptor
; returning with carry flag set with header mismatch or if the name starts
; with CHR$ 0 and should therefore be secret.

;; GET-DESC
L1FE4: CALL L13A9 ; routine GET-M-HD2 reads the next 14-byte
; header to pass the tape heads.
CALL L1E5E ; routine G-RDES reads the corresponding
; 14-byte record descriptor for this sector.
JR NZ,L1FF1 ; forward, with checksum error, to NOT-RECV

; a valid header and matching descriptor has been read.

LD A,(IX+$47) ; fetch first character of RECNAM.
OR A ; test for CHR$ 0.
RET NZ ; return if not a secret file.

; but if a secret file then ensure that the 14 descriptor bytes (read) and
; the 512 buffer bytes (not read) are cleared to the same value.

;; NOT-RECV
L1FF1: CALL L1FD4 ; routine CLR-BUFF (above).
SCF ; signal error.
RET ; return from hook code.

; --------------------------------
; THE 'OPEN "B" CHANNEL' HOOK CODE
; --------------------------------
; (Hook Code: $34)
; New in this ROM.

;; OP-B-CHAN
L1FF6: LD A,$42 ; letter "B"
LD ($5CD9),A ; place in system variable L_STR1
CALL L0B17 ; routine OP-RS-CH opens an RS232 channel.
RET ; return.

; ---

DEFB $FF ; spare

; ---

.end

; -----------------------------
; THE 'SHADOW' SYSTEM VARIABLES
; -----------------------------
;
; X1 23734 $5CB6 FLAGS3 ; IY+$7C - Flags
; X2 23735 $5CB7 VECTOR ; Address used to extend BASIC.
; X10 23732 $5CB9 SBRT ; 10 bytes of Z80 code to Page ROM.
; 2 23747 $5CC3 BAUD ; BAUD=(3500000/(26*baud rate)) -2
; 1 23749 $5CC5 NTSTAT ; Own network station number.
; 1 23750 $5CC6 IOBORD ; Border colour during I/O
; N2 23751 $5CC7 SER_FL ; 2 byte workspace used by RS232
; N2 23753 $5CC9 SECTOR ; 2 byte workspace used by Microdrive.
; N2 23755 $5CCB CHADD_ ; Temporary store for CH_ADD
; 1 23757 $5CCC NTRESP ; Store for network response code.
; -- ----- ----- ------ ; ---------------------------------
; 1 23758 $5CCD NTDEST ; Destination station number 0 - 64.
; 1 23759 $5CCE NTSRCE ; Source station number.
; X2 23760 $5CD0 NTNUMB ; Network block number 0 - 65535
; N1 23762 $5CD2 NTTYPE ; Header type block.
; X1 23763 $5CD3 NTLEN ; Data block length 0 - 255.
; N1 23764 $5CD4 NTDCS ; Data block checksum.
; N1 23765 $5CD5 NTHDS ; Header block checksum.
; -- ----- ----- ------ ; ---------------------------------
; N2 23766 $5CD6 D_STR1 ; 2 byte drive number 1 - 8.
; N1 23768 $5CD8 S_STR1 ; Stream number 1 - 15. [ also 0 ]
; N1 23769 $5CD9 L_STR1 ; Device type "M", "N", "T" or "B"
; N2 23770 $5CDA N-STR1 ; Length of filename.
; N2 23772 $5CDC (dynamic) ; Address of filename.
; -- ----- ----- ------ ; ---------------------------------
; N1 23774 $5CDE D_STR2 ; 2 byte drive ; File type.
; N1 23775 $5CDF ; number. ; Length of
; N1 23776 $5CE0 S_STR2 ; Stream number. ; Data.
; N1 23777 $5CE1 L_STR2 ; Device type. ; Start of
; N1 23778 $5CE2 N-STR2 ; Length of ; data. \
; N1 23779 $5CE3 ; filename. ; Program \
; N1 23780 $5CE4 (dynamic) ; Address of ; length. ; Start of
; N1 23781 $5CE5 (dynamic) ; filename ; ; data.
; -- ----- ----- ------ ; ---------------------------------
; N1 23782 $5CE6 HD_00 ; File type . _
; N2 23783 $5CE7 HD_0B ; Length of data. /\
; N2 23785 $5CE9 HD_0D ; Start of data. /
; N2 23787 $5CEB HD_0F ; Program length. /
; N2 23789 $5CED HD_11 ; Line number.
; -- ----- ----- ------ ; ---------------------------------
; 1 23791 $5CEF COPIES ; Number of copies made by SAVE.
; -- ----- ----- ------ ; ---------------------------------

; Note. the System Variables HD_00 to HD_11 take their names from their
; position in the standard audio tape header. The ten bytes HD_01 to HD_0A
; would be the tape filename and are not held within the above area.
; The area D_STR2 is multipurpose and sometimes the HD_?? variables are
; copied to this region and sometimes the D_STR1 variables are copied there.

; ----------------------------
; THE ' Microdrive MAPS' FORMAT
; ----------------------------
;
; The creation of the extra system variables moves the start of CHANS up to
; address 23792. It is at this location that the first of a possible eight
; Microdrive Maps will be created. Each map is 32 bytes in size containing
; 256 bits for each possible sector and as each map is created, CHANS moves
; up by another 32 bytes.

; 10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
; 00000000 00000000 00000000 00000000 00010101 01010100 00000000 00000000
; 00000000 01100000 00000000 00000000 00000000 00000000 00000111 11111111
; 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111

; Note. The continuous loop tape is formatted in such a way that sector $FE
; is written first and sector $01 is the last to be written. Sectors $00 and
; $FF are therefore always unavailable. As there is only room for about 180
; sectors on a 100 foot long tape, the higher numbered sectors are later
; overwritten by the lower numbered sectors.
; Where the tape is spliced together one or two bad sectors will appear.
; When saving bytes there isn't enough time to copy the next 512 bytes from
; the program/code area to the buffer between sectors so a program or
; code/data block is written to alternating sectors as with the 3K example
; above. As the tape cartridge fills up it becomes more difficult to find
; usable sectors and LOAD/SAVE operations take longer.
; A growing number of Spectrum emulators feature the Microdrives and they
; usually make available all 254 sectors so a typical cartridge will hold 126
; Kilobytes compared to say 92 K on real hardware.
;
; During a LOAD operation the entire sector map is pushed on the machine stack
; and the Microdrive map is used to map loaded records after which the previous
; map is 'popped' of the stack and reverts to mapping sectors again.
;
; ------------------------------
; THE 'STANDARD CHANNELS' FORMAT
; ------------------------------
; The twenty bytes of the standard channels as set up my Main ROM.
;
; CHANS $09F4 ; PRINT-OUT
; $10A8 ; KEY-INPUT
; $4B ; 'K'
;
; $09F4 ; PRINT-OUT
; $15C4 ; REPORT-J
; $53 ; 'S'
;
; $0F81 ; ADD-CHAR
; $15C4 ; REPORT-J
; $52 ; 'R'
;
; $09F4 ; PRINT-OUT
; $15C4 ; REPORT-J
; $50 ; 'P'
;
; $80 ; End Marker

; -------------------------------
; THE ' Microdrive CHANNEL' FORMAT
; -------------------------------
;
; 2 IX+$00 $0008 ; main ERROR-1
; 2 IX+$02 $0008 ; main ERROR-1
; 1 IX+$04 $CD ; inverted or regular "M" character
; 2 IX+$05 $12B3 ; MCHAN-OUT
; 2 IX+$07 $11FD ; M-INPUT
; 2 IX+$09 $0253 ; length of channel.
; 2 IX+$0B CHBYTE $0000 ; position of next byte rec'd/stored
; 1 IX+$0D CHREC $00 ; record number, also temporary sector
; 10 IX+$0E CHNAME " " ; filename with trailing spaces.
; 1 IX+$18 CHFLAG %0000000x ; bit 0 used
; 1 IX+$19 CHDRIV ; drive number 0 - 7.
; 2 IX+$1A CHMAP ; address of MAP for this Microdrive.
; ------------------------------------------------------------------------------
; 12 IX+$1C ; 12 bytes of header preamble
; ------------------------------------------------------------------------------
; 1 IX+$28 HDFLAG ; Flag byte.
; ; bit 0 set indicates a header.
; 1 IX+$29 HDNUMB ; Sector number. [1-254]
; 2 IX+$2A ; Two unused bytes.
; 10 IX+$2C HDNAME ; Cartridge name with trailing spaces.
; 1 IX+$36 HDCHK ; Header checksum.
; ------------------------------------------------------------------------------
; 12 IX+$37 ; 12 bytes of data block preamble.
; ------------------------------------------------------------------------------
; 1 IX+$43 RECFLG ; Flag byte.
; ; bit 0 reset indicates a record.
; ; bit 1 reset no EOF, set EOF
; ; bit 2 reset indicates a PRINT FILE
; 1 IX+$44 RECNUM ; Record number in the range 0-255
; 2 IX+$45 RECLEN ; Number of databytes in record 0-512.
; 10 IX+$47 RECNAM ; Filename with trailing spaces.
; 1 IX+$51 DESCHK ; Checksum of the preceding 14 bytes
; ------------------------------------------------------------------------------
; 512 IX+$52 CHDATA ; the 512 bytes of data.
; 1 +$0252 DCHK ; Checksum of preceding 512 bytes.

; ----------------------------
; THE 'NETWORK CHANNEL' FORMAT
; ----------------------------
;
; 2 IX+$00 $0008 ; main ERROR-1
; 2 IX+$02 $0008 ; main ERROR-1
; 1 IX+$04 $4E ; "N" character
; 2 IX+$05 $0E09 ; NCHAN-OUT
; 2 IX+$07 $0DA9 ; N-INPUT
; 2 IX+$09 $0114 ; Length of channel 276 bytes.
; 1 IX+$0B NCIRIS ; The destination station number.
; 1 IX+$0C NCSELF ; This Spectrum's station number.
; 2 IX+$0D NCNUMB ; The block number.
; 1 IX+$0F NCTYPE ; The packet type code . 0 data, 1 EOF
; 1 IX+$10 NCOBL ; Number of bytes in data block.
; 1 IX+$11 NCDCS ; The data checksum.
; 1 IX+$12 NCHCS ; The header checksum.
; 1 IX+$13 NCCUR ; The position of last buffer char taken
; 1 IX+$14 NCIBL ; Number of bytes in the input buffer.
; 1 IX+$15 NCB ; A 255 byte data buffer.

; ------------------------------
; THE 'RS232 "T" CHANNEL' FORMAT
; ------------------------------
;
; 2 IX+$00 $0008 ; main ERROR-1
; 2 IX+$02 $0008 ; main ERROR-1
; 1 IX+$04 $54 ; "T" character
; 2 IX+$05 $0C3A ; TCHAN-OUT
; 2 IX+$07 $0B76 ; T-INPUT
; 2 IX+$09 $000B ; length of channel.

; ------------------------------
; THE 'RS232 "B" CHANNEL' FORMAT
; ------------------------------
; created by overwriting a "T" channel
;
; 2 IX+$00 $0008 ; main ERROR-1
; 2 IX+$02 $0008 ; main ERROR-1
; 1 IX+$04 $42 ; "B" character
; 2 IX+$05 $0D07 ; BCHAN-OUT
; 2 IX+$07 $0B7C ; B-INPUT
; 2 IX+$09 $000B ; length of channel.


; Acknowledgements
; ----------------
; Dr Ian Logan for main ROM labels ( and half on Interface 1)
; Dr Frank O'Hara for main ROM labels.
; Gianluca Carri for Interface 1 v1.2 labels

; Credits
; -------
; Jonathan Needle for requesting said labels on comp.sys.sinclair
; thereby kick-starting this project.
; Also for the Interface1-aware Spectaculator emulator
; and help with PORTS.
; Paul Dunn for help with PORTS and the SPIN emulator.