wearmouth:version_2
; Disassembly of the file "C:\lab\if1-2.rom"
;
; CPU Type: Z80
;
; Created with dZ80 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.
; If the system variables were already created then it is possible to
; derive the stream from the accumulator by reversing the operations
; that were performed on the value in the main ROM.
SRL A ; Shift Right Logical halves the value
SUB $03 ; Subtraction produced the original stream.
RES 1,(IY+$7C) ; set FLAGS_3 to indicate a CLOSE operation.
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
; Now perform the check that should have taken place in the original ROM.
LD A,C ; Test offset for zero.
OR B ;
RET Z ; Return if the stream is already closed.
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 23737 $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.
; Chris Born for documentation.
wearmouth/version_2.txt · Last modified: 2022/03/22 09:33 by 127.0.0.1
