M100 TS-DOS ROM TPDD Protocol

From Bitchin100 DocGarden
Jump to: navigation, search

TS-DOS Model 100/102 ROM TPDD Protocol routines

; =========================================================================================
; Flush the RX output buffer to the TPDD file
; =========================================================================================
46DDH  LHLD FCCEH           ; Get address of FD control        
46E0H  LXI B,0007H          ; Load offset of write buffer length         
46E3H  DAD B                ; Calculate address of write buffer length within FD control   
46E4H  MOV A,M              ; Load the write buffer length     
46E5H  PUSH H               ; Save pointer to write buffer length in FD control on stack    
46E6H  INX H                ; Skip the LSB of the write buffer pointer   
46E7H  INX H                ; Skip the MSB of the write buffer pointer   
46E8H  INX H                ; Point to the actual data in the file write buffer   
46E9H  LXI D,FD04H          ; DE points to the data area in RX buffer         
46ECH  MOV C,A              ; Load buffer length into BC      
46EDH  MVI B,00H            ; Make MSB of BC zero       
46EFH  PUSH PSW             ; Save write buffer length to stack      
46F0H  CALL 4A05H           ; Move BC bytes from M to (DE) with increment        
46F3H  POP PSW              ; Retrieve write buffer length from stack     
46F4H  ANA A                ; Test if write buffer is empty   
46F5H  CNZ 4095H            ; Send TPDD Write File opcode using RX buffer with length in A if not empty       
46F8H  POP H                ; Retrieve pointer to write buffer length in FD control from stack   
46F9H  MVI M,00H            ; Set length of write buffer to zero - it is flushed       
46FBH  INX H                ; Increment to address in FD control of write buffer    
46FCH  MOV D,H              ; Save HL to DE for storage of new buffer pointer address     
46FDH  MOV E,L              ; Save MSB of HL to DE     
46FEH  INX H                ; Increment to MSB of address in FD conrol of write buffer   
46FFH  INX H                ; Increment to actual write buffer address   
4700H  SHLX                 ; Save new write buffer pointer to FD control           
4701H  RET         

; =========================================================================================
; Appears to be unused code
; =========================================================================================
4702H  POP PSW
4703H  RET

; =========================================================================================
; Display or print all disk files and free space
; =========================================================================================
4704H  LXI H,F685H          ; Keyboard buffer  
4707H  SHLD FCDBH           ; Initialize pointer to File Length table       
470AH  CALL 4735H           ; Clear #files displayed, init TPDD and send 1st file dir referene opode       
470DH  JNZ 486AH            ; Communication Error 
4710H  XRA A                ; Prepare to initialize "skip page" count to zero  
4711H  STA FCDAH            ; Load the "skip page" count      
4714H  STA FCD4H            ; Current Page of files being displayed      
4717H  DCR A                ; Set A to 0xFF
4718H  STA FCD7H            ; Save as Page size to print (print 255 files per page)
471BH  MVI A,01H            ; 
471DH  STA F63AH            ; ???
4720H  CALL 4747H           ; Get next page of files from server (NADSBox) with page size = 255
4723H  JMP 4C23H            ; Send CRLF to screen or printer        

; =========================================================================================
; Display Disk free space on LCD at current position
;     NOT CALLED!
;     Old function - reporting of Free space was merged into the printing of the function
;     key display routine.
; =========================================================================================
4726H  CALL 4A10H           ; Multiply TPDD free sectors len x 128 = free space
4729H  CALL 4C3EH           ; Print binary number in HL at current position
472CH  LXI H,4AD1H          ; Point to "0 Bytes free" text
472FH  CALL 4A2CH           ; Send buffer at M to screen
4732H  JMP 4C23H            ; Send CRLF to screen or printer

; =========================================================================================
; Clear #files displayed, init TPDD and send 1st file dir referene opode
; =========================================================================================
4735H  XRA A                ; Indicate zero files being displayed 
4736H  STA FCD6H            ; Count of files on screen
4739H  CALL 4943H           ; Configure baud, test for NADSBox, get NADSBox dir 
473CH  LXI H,0146H          ; Load code for "F" attribute and First File dir reference
473FH  JMP 476FH            ; Send Directory Reference opcode with HL=access mode

; =========================================================================================
; Get next page of files from server (NADSBox / TPDD)
; =========================================================================================
4742H  MVI A,14H            ; Maximum files that will fit on the display      
4744H  STA FCD7H            ; Save for comparison of file count      
4747H  LDA FCCBH            ; NADSBox / Desklink / TPDD2 flag      
474AH  ANA A                ; Test if TPDD  
474BH  JNZ 4757H            ; Skip 1st invocation of "Next file" if NADSBox or TPDD2??      
474EH  LXI H,02F6H          ; Load access mode code for "Next entry" dir reference        
4751H  CALL 476FH           ; Send Directory Reference opcode with HL=access mode       
4754H  JNZ 47F8H            ; Check RX packet for Noraml Response & report errors      
4757H  LDA FD04H            ; 1st filename byte      
475AH  ANA A                ; Test if 1st filename byte is zero  
475BH  RZ                   ; Return if last file retrieved
475CH  LDA FCDAH            ; Load the "skip page" count      
475FH  ANA A                ; Test if this is a "skip page"  
4760H  CZ 4781H             ; Call routine to display file if not a skip page     
4763H  LXI H,FCD6H          ; Count of files on screen        
4766H  INR M                ; Increment #files displayed on LCD  
4767H  LDA FCD7H            ; Load count of max files that will fit on LCD      
476AH  CMP M                ; Compare with current file count          
476BH  JNZ 474EH            ; Jump back to get next file from TPDD if not full      
476EH  RET

; =========================================================================================
; Send Directory Reference opcode with HL=access mode
; =========================================================================================
476FH  SHLD FD00H           ; Save file attrib and reference mode to TX buffer     
4772H  LXI H,1A00H          ; Load code and length for Directory Reference opcode      
4775H  CALL 4822H           ; Send TPDD Command/Len in HL     
4778H  CALL 47CCH           ; Receive a packet     
477BH  LDA FD02H            ; Storage for RX packet - response byte    
477EH  CPI 11H              ; Test if response was 0x11  (valid Dir Ref response)
4780H  RET

; =========================================================================================
; Print filename in RX buffer to LCD and save length info
; =========================================================================================
4781H  MVI C,09H            ; Prepare to draw 9 bytes of filename to LCD     
4783H  LXI H,FD04H          ; Location of filename in RX packet       
4786H  CALL 48E5H           ; Redraw C characters from buffer at HL using current video mode      
4789H  MVI A,20H            ; Prepare to draw a trailing space            
478BH  RST 4                ; Send char in A to LCD    
478CH  LHLD FD1DH           ; Get file size parameter from RX buffer      
478FH  MOV E,H              ; Save file size LSB in E   
4790H  MOV D,L              ; Save file size MSB in D          

; =========================================================================================
; Store next file length to keyboard buffer
; =========================================================================================
4791H  LHLD FCDBH           ; Next storage location for file size info       
4794H  XCHG                 ; HL <--> DE 
4795H  SHLX                 ; Store file length at address of next file save 
4796H  XCHG                 ; HL <--> DE 
4797H  INX H                ; Increment storage address of next file length  
4798H  INX H                ; Increment again (2 byte length)  
4799H  SHLD FCDBH           ; Save new address for lenght storage for next file
479CH  RET

; =========================================================================================
; Receive an RX packet.  This routine gets copied into RAM and executed from there while
;       the Main ROM is selected.  This allows the RX packet to be recieved without
;       switching to and from the Main ROM to receive each individual byte.  The jump
;       addresses represent the RAM execution locations.
; =========================================================================================
479DH  LXI H,FD02H          ; Storage for RX packet         
47A0H  PUSH H               ; Save start of packet address on stack    
47A1H  CALL FDB2H           ; Get next byte from RX into HL        
47A4H  CALL FDB2H           ; Get next byte from RX into HL        
47A7H  MOV C,A              ; Move length byte into C     
47A8H  CALL FDB2H           ; Get next byte from RX into HL        
47ABH  MOV B,C              ; Move length byte to B     
47ACH  MOV A,C              ; Move length byte to A for zero test     
47ADH  ANA A                ; Test if length is zero   
47AEH  JZ FDA4H             ; Skip reading payload if zero length      
47B1H  CALL FDB2H           ; Get next byte from RX into HL        
47B4H  DCR C                ; Decrement length byte   
47B5H  JNZ FD9DH            ; Keep looping until all data received       
47B8H  POP H                ; Restore start of RX packet address   
47B9H  INR B                ; Add opcode byte to length for checksum   
47BAH  INR B                ; Add length byte to length for checksum   
47BBH  XRA A                ; Clear A for checksum calculation   
47BCH  SIM                  ; ?Clear Interrupts 
47BDH  ADD M                ; Add next byte to checksum   
47BEH  INX H                ; Increment RX data pointer   
47BFH  DCR B                ; Decrement length counter   
47C0H  JNZ FDA9H            ; Keep looping until all bytes added       
47C3H  CMA                  ; Compliment the checksum          
47C4H  CMP M                ; Compare with received checksum   
47C5H  RET                  ; Branch if no match - Communication Error

; =========================================================================================
; Get next RX byte and store at HL, test for drive ready & print errors
;    This routine (and the one above) get copied into RAM and then executed
;    when the Main ROM is selected, so a direct call to the Main ROM address
;    is allowed.
; =========================================================================================
47C6H  CALL 6D7EH           ; Get a character from RS232 receive queue
47C9H  MOV M,A              ; Save byte at HL
47CAH  INX H                ; Increment HL
47CBH  RET

; =========================================================================================
; Copy the 2 Receive RX Packet routines above to RAM and execute
; =========================================================================================
47CCH  LXI H,479DH          ; Point to Receive RX Packet routine
47CFH  LXI D,FD89H          ; Point to target destination in RAM
47D2H  LXI B,002FH          ; Length of the routine
47D5H  CALL 4A05H           ; Move BC bytes from M to (DE) with increment      
47D8H  CALL 4862H           ; Test if DSR is set - drive ready, error otherwise
47DBH  CALL 4BDBH           ; Call Main ROM's Set interrupt to 1DH routine.
47DEH  RST 1                ; Switch to Main ROM and execute "Receive RX Pakcet" routine
47DFH  DW   FD89H           ; Address of our RX routine we just copied to RAM
47E1H  JNZ 486AH            ; Communication Error if no /DSR
47E4H  RET

; =========================================================================================
; Get next RX byte and store at HL, test for drive ready & print errors
; =========================================================================================
47E5H  CALL 47EBH           ; Test drive and get byte from RX
47E8H  MOV M,A              ; Save byte at HL
47E9H  INX H                ; Increment HL 
47EAH  RET

; =========================================================================================
; Test drive ready and get byte from RX,  Report error if not ready
; =========================================================================================
47EBH  CALL 4862H           ; Test if DSR is set - Drive Ready
47EEH  CALL 4BE7H           ; Get a character from RS232 receive queue
47F1H  JC 488DH             ; Empty Error Text
47F4H  JNZ 486AH            ; Communication Error
47F7H  RET

; =========================================================================================
; Check RX packet for Normal Response & report errors
; =========================================================================================
47F8H  LDA FD02H            ; Storage for RX packet        
47FBH  CPI 12H              ; Test for normal return code      
47FDH  JNZ 486AH            ; Communication Error        
4800H  LDA FD04H            ; Location of error code in RX packet        
4803H  ANA A                ; Test if error code is zero    
4804H  RZ                   ; Return if no error 
4805H  CPI 10H        
4807H  JZ 487EH             ; File Exists Error       
480AH  CPI 50H        
480CH  JZ 486DH             ; Write Protect Error       
480FH  CPI 60H        
4811H  JZ 4881H             ; Disk Full Error       
4814H  JC 486AH             ; Communication Error       
4817H  JZ 4873H             ; Disk not in drive error       
481AH  CPI 80H        
481CH  JC 4873H             ; Disk not in drive error       
481FH  JMP 4870H            ; Drive Trouble Error        

; =========================================================================================
; Send TPDD Command/Len in HL,  H=len, L=Cmd. 0XED8A already has data
; =========================================================================================
4822H  SHLD FCE6H           ; Store TDD CMD/Len passed in HL        
4825H  LXI H,FCE6H          ; Point to Command/Len storage location         

4828H  PUSH H               ; Save Cmd/Len storage address to stack    
4829H  LDA FCCAH            ; TPDD Bank Numbere       
482CH  ANA A                ; Test if reading from side zero   
482DH  JZ 4837H             ; Skip ahead if reading from side zero      
4830H  ANI 01H              ; Mask off all but LSB (only side 1 allowed)     
4832H  RAR                  ; Move the "1" to C          
4833H  RAR                  ; Move the "1" to bit 7 
4834H  RAR                  ; Move the "1" to bit 6 (could just do MVI 0x40 !!) 
4835H  ORA M                ; OR the 0x40 with Cmd to access Disk Bank 1   
4836H  MOV M,A              ; Save the updated cmd value back at (HL)     
4837H  MOV A,M              ; Get the command value / modified value     
4838H  INX H                ; Point to length parameter passed in   
4839H  ADD M                ; Add the length parameter (checksum)   
483AH  MOV B,A              ; Store checksum in B                                
483BH  MOV A,M              ; Get the Length parameter     
483CH  PUSH PSW             ; Save length on stack      
483DH  INX H                ; Point to data (already loaded before invocation)   
483EH  ANA A                ; Test if length is zero   
483FH  JZ 484BH             ; Skip data checksum if length zero        
4842H  MOV C,A              ; Save current count     
4843H  MOV A,B              ; Get running checksum value     
4844H  ADD M                ; Add this byte to checksum   
4845H  INX H                ; Point to next data byte   
4846H  DCR C                ; Decrement count   
4847H  JNZ 4844H            ; Keep looping until count is zero       
484AH  MOV B,A              ; Save updated checksum in B     
484BH  MOV A,B              ; Get Checksum from B    
484CH  CMA                  ; Compliment the checksum (protocol thing) 
484DH  MOV D,A              ; Save complimented checksum in D     
484EH  LXI B,5A5AH          ; Load "ZZ" header signature into BC         
4851H  CALL 49DBH           ; Send BC to the serial port using XON/XOFF        
4854H  POP B                ; Get the length from stack into BC                     
4855H  MOV C,B              ; Move the length into C     
4856H  MVI B,00H            ; Clear upper MSB of count       
4858H  INX B                ; Add Opcode byte to the TX length    
4859H  INX B                ; Add length byte to the TX length    
485AH  POP H                ; Get address of TX packet from stack    
485BH  CALL 48D9H           ; Send BC bytes to RS-232 from (HL) using XON/XOFF                  
485EH  MOV A,D              ; Get checksum byte for TX       

; =========================================================================================
; Send A to RS-232 using XON/XOFF and check DSR when done
; =========================================================================================
485FH  CALL 4BE3H           ; Call Main ROM's Send A to RS-232 using XON/XOFF routine         

; =========================================================================================
; Test if DSR is set - drive ready, error otherwise
; =========================================================================================
4862H  IN BBH               ; Get I/O port with /DSR bit
4864H  ANI 20H              ; Mask all but the /DSR bit
4866H  RZ                   ; Return if /DSR is asserted (low = asserted)

; =========================================================================================
; Error jump table.  Loads A with error number and prints
; =========================================================================================
4867H  (3EH) MVI A,01H
       DB	0x21            ; Filler - looks like "LXI H,XXXX"
486AH  MVI A,02H            ; Communication Error
       DB	0x21
486DH  MVI A,03H            ; Write Protect Error
       DB	0x21
4870H  MVI A,04H            ; Drive Trouble Error
       DB	0x21
4873H  MVI A,05H            ; Disk not in drive Error

; =========================================================================================
; For any of the errors above, change the RAM/Disk mode back to RAM
; =========================================================================================
4875H  LXI H,FCD9H          ; RAM / Disk mode (0=RAM,  1=DISK)
4878H  MVI M,00H            ; Go back to RAM mode
       DB   0x21            ; Filler - looks like "LXI H,XXXX"

487BH  MVI A,06H            ; Printer Not Ready Error
       DB	0x21
487EH  MVI A,07H            ; File Exists Error
       DB	0x21
4881H  MVI A,08H            ; Disk Full Error
       DB	0x21
4884H  MVI A,09H            ; File Empty Error
       DB	0x21
4887H  MVI A,0AH            ; Directory Full Error
       DB	0x21
488AH  MVI A,0BH            ; Ram Full Error
       DB	0x21
488DH  MVI A,0CH            ; ID Error (No text...runtime error only)
       DB	0x21
4890H  MVI A,0DH            ; Bad File Name Error

; =========================================================================================
; Generate the error loaded in A above
; =========================================================================================
4892H  LXI H,4B02H          ; Pointer to error string table
4895H  MOV C,A              ; Save error number for loop counting   
4896H  DCR C                ; Decrement error counter to test if string found 
4897H  JZ 48A4H             ; String found in table    
489AH  MOV A,M              ; Get next byte of error string   
489BH  INX H                ; Inrcrement pointer 
489CH  CPI 00H              ; Test if end of string   
489EH  JNZ 489AH            ; Loop until end of string found     
48A1H  JMP 4896H            ; Branch back to decrement string counter     

; =========================================================================================
; Print error string at HL from error table
; =========================================================================================
48A4H  LDA FCD2H            ; Get the "Print error messages" flag       
48A7H  ANA A                ; Test if printing allowed   
48A8H  JZ 48D0H             ; Jump to generate system error if not allowed to print      
48ABH  MVI A,07H            ; Print BELL to cause a beep       
48ADH  RST 4                ; Send A to the LCD     
48AEH  INX H                ; Skip the error code   
48AFH  CALL 48F4H           ; Print string at HL to col 1 on the bottom line        
48B2H  CALL 48C7H           ; Display "Press any key" and wait for key     
   
; =========================================================================================
; Perform Long jump
; =========================================================================================
48B5H  LHLD FCCEH           ; Stack pointer upon entry        
48B8H  SPHL                 ; Restore stack frame  
48B9H  LHLD FCD0H           ; Get return vector after error        
48BCH  PUSH H               ; Prepare to return to return vector    
48BDH  RET

; =========================================================================================
; Draw "Press any key" text with pre&post CR and wait for key press
; =========================================================================================
48BEH  CALL 4C23H           ; Send CRLF to screen or printer
48C1H  CALL 48C7H           ; Display "Press any key" and wait for key
48C4H  JMP 4C23H            ; Send CRLF to screen or printer

; =========================================================================================
; Draw "Press any key" text and wait for key press
; =========================================================================================
48C7H  LXI H,4BBBH          ; Point to "Press any key" text
48CAH  CALL 4A2CH           ; Send buffer at M to screen
48CDH  JMP 4BEBH            ; Wait for key from keyboard

; =========================================================================================
; Generate system error from code saved at (HL).  This is called in liu of printing when
;      the print/generate system error flag is set.  The 1st byte of each error entry
;      contains the system error number to generate.
; =========================================================================================
48D0H  MOV E,M              ; Get the error code
48D1H  LHLD FCCEH           ; Stack pointer upon entry
48D4H  MVI M,00H            ; Indicate all arguments (none) processed
48D6H  JMP 4C37H            ; Generate error in E

; =========================================================================================
; Send BC bytes to RS-232 from (HL) using XON/XOFF
; =========================================================================================
48D9H  MOV A,M              ; Get next byte from (HL)
48DAH  CALL 485FH           ; Send A to RS-232 using XON/XOFF
48DDH  INX H                ; Increment poiner 
48DEH  DCX B                ; Decrement byte count 
48DFH  MOV A,C              ; Get C for zero comparison
48E0H  ORA B                ; Or in MSB of count
48E1H  JNZ 48D9H            ; Jump to send next byte if not zero
48E4H  RET

; =========================================================================================
; Draw C characters to LCD from buffer at HL using current video mode
; =========================================================================================
48E5H  MOV A,M              ; Get next byte from LCD buffer 
48E6H  CPI 20H              ; Test if it is a space 
48E8H  JNC 48EDH            ; Test if it's less than space, jump if not to redraw 
48EBH  MVI A,20H            ; Draw a space for "illegal" values 
48EDH  RST 4                ; Draw byte with current regular/inverse video mode 
48EEH  INX H                ; Increment to next byte in LCD buffer 
48EFH  DCR C                ; Decrement the loop counter 
48F0H  JNZ 48E5H            ; Branch if not done to process next character 
48F3H  RET                  ; Done

; =========================================================================================
; Print string at HL to col 1 on the bottom line
; =========================================================================================
48F4H  PUSH H               ; Save pointer to error text on stack  
48F5H  LXI H,0108H          ; Position cursor on col 1 of row 16
48F8H  SHLD F639H           ; Cursor row (1-8)  
48FBH  CALL 4C2BH           ; Erase from cursor to end of line      
48FEH  POP H                ; Restore pointer to error text from stack 
48FFH  JMP 4A2CH            ; Send buffer at M to screen

; =========================================================================================
; Copy filename at (HL) to current BASIC program
; =========================================================================================
4902H  PUSH H               ; Save pointer to filename on stack    
4903H  MVI A,20H            ; Load A with code for SPACE       
4905H  LXI H,FC93H          ; Filename of current BASIC program            
4908H  MVI B,08H            ; Prepare to fill current BASIC program with 8 spaces       
490AH  CALL 4A34H           ; Fill (HL) with B bytes of A        
490DH  POP H                ; Retrieve filename pointer from stack   
490EH  LXI D,FC93H          ; Filename of current BASIC program             
4911H  MVI B,06H            ; Prepare to copy 6 bytes of filename before ext       
4913H  MOV A,M              ; Get next byte of filename from source     
4914H  CPI 41H              ; Compare byte with 'A' to test validity     
4916H  JC 4890H             ; Bad Filename Error if invalid char      
4919H  MOV A,M              ; Get next byte again     
491AH  CPI 2EH              ; Compare with '.'     
491CH  JZ 492BH             ; Branch if '.' to fill remaining with ' '      
491FH  ORA A                ; Test for zero (NULL term)   
4920H  RZ                   ; Return if done copying filename (NULL term)
4921H  STAX D               ; Store next filename byte at (DE)    
4922H  INX H                ; Increment source pointer   
4923H  INX D                ; Increment dest pointer   
4924H  DCR B                ; Decrement counter   
4925H  JNZ 4919H            ; Keep looping until all bytes copied       
4928H  JMP 4933H            ; Jump to copy 2 extension bytes       

; =========================================================================================
; Fill remainder of filename at (DE) with spaces,  B has count
; =========================================================================================
492BH  MVI A,20H            ; Load code for ' '        
492DH  STAX D               ; Save next byte as ' '     
492EH  INX D                ; Increment pointer    
492FH  DCR B                ; Decrement count    
4930H  JNZ 492DH            ; Keep looping until full 
       
; =========================================================================================
; Copy 2 extention bytes from (HL) to (DE)
; =========================================================================================
4933H  MVI B,02H            ; Setup count = 2        
4935H  INX H                ; Skip the '.' in the source name    
4936H  MOV A,M              ; Load next extension byte from source      
4937H  ORA A                ; Test for NULL termination    
4938H  RZ                   ; Return if NULL terminated 
4939H  STAX D               ; Store in destination     
493AH  INX D                ; Increment destination    
493BH  DCR B                ; Decrement the counter    
493CH  JNZ 4935H            ; Keep looping until done        
493FH  MVI A,00H            ; Load NULL termination byte        
4941H  STAX D               ; Add NULL termination to filename     
4942H  RET        

; =========================================================================================
; Configure baud, test for NADSBox, get NADSBox dir
; =========================================================================================
4943H  CALL 4A94H           ; Configure baud (19200 for NADSBox/Desklink, 9600 otherwise)        
4946H  CALL 49A2H           ; Send M1 and test for NADSBox/Desklink response        
4949H  CALL 47CCH           ; Receive a packet        
494CH  CALL 47F8H           ; Check RX packet for Normal Response & report errors        
494FH  LDA FCCBH            ; NADSBo / Desklink / TPDD2 flag       
4952H  ANA A                ; Test if server is NADSBox / Desklink   
4953H  RZ                   ; Return if not NADSBox / Desklink
4954H  LXI H,0008H          ; Load opcode 0x08 - FDC Emulation mode         
4957H  CALL 4822H           ; Send TPDD Command/Len in HL        
495AH  CALL 47CCH           ; Receive a packet - should be the current directory name        
495DH  LDA FD03H            ; Load length byte from the RX payload area       
4960H  CPI 01H              ; Test if length 1     
4962H  JZ 4977H             ; Jump to send mystery opcodes if length = 1      
4965H  DCR A                ; Subtract 1 from length (skip reserved 0x00 in filename)   
4966H  MOV C,A              ; Save count in C     
4967H  MVI A,02H            ; Prepare to indicate we have a NADSBox / Desklink directory name       
4969H  STA FCCBH            ; NADSBox / Desklink flag = HAVE_NADS_DIR       
496CH  MVI B,00H            ; Clear MSB of count       
496EH  LXI H,FD05H          ; Point to filename in RX payload (skip 1st byte)         
4971H  LXI D,FCC0H          ; Storage area for NADSBox directory           
4974H  JMP 4A05H            ; Move BC bytes from M to (DE) with increment       

; =========================================================================================
; Send mystery opcodes
; =========================================================================================
4977H  LXI H,0023H          ; Load mystery opcode 23    
497AH  CALL 4822H           ; Send TPDD Command/Len in HL   
497DH  CALL 47CCH           ; Receive a packet   
4980H  LXI H,4AE7H          ; Point to mystery opcode 0x31    
4983H  CALL 498FH           ; Send 9-byte protocol packet at HL   
4986H  LXI H,4AF0H          ; Point to 2nd mystery opcode 0x31    
4989H  CALL 498FH           ; Send 9-byte protocol packet at HL   
498CH  LXI H,4AF9H          ; Point to 3rd mystery opcode 0x31    

; =========================================================================================
; Send 9-byte protocol packet at HL
; =========================================================================================
498FH  LXI B,0009H          ; Load byte counter to send 9 bytes    
4992H  CALL 48D9H           ; Send BC bytes at (HL) to RS-232 using XON/XOFF   
4995H  JMP 47CCH            ; Receive a packet  

; =========================================================================================
; Flush the RX queue & discard
; =========================================================================================
4998H  CALL 4BDFH           ; Check RS232 queue for pending characters
499BH  RZ                   ; Return if RS232 queue is empty
499CH  CALL 47EBH           ; Test drive ready and get byte from RX.
499FH  JMP 4998H            ; Branch to get next byte from RX

; =========================================================================================
; Send M1 and test for NADSBox / Desklink response
; =========================================================================================
49A2H  CALL 4862H           ; Test if DSR set - drive ready      
49A5H  CALL 49CDH           ; Send "M1",0x0D to TX and delay about 3ms      
49A8H  CALL 4998H           ; Flush the RX queue & discard      
49ABH  CALL 419BH           ; Send Opcode 0x08 - FDC emulation mode      
49AEH  CALL 49D3H           ; Send 0x0D to RS-232 and Delay 3ms      
49B1H  CALL 4BDFH           ; Check RS232 queue for pending characters      
49B4H  MVI A,00H            ; NADSBox / Desklink / TPDD2 flag     
49B6H  JNZ 49BAH            ; Skip setting NADSBox / Desklink flag if no response     
49B9H  INR A                ; Indicate response from "M1" command = NADSBox / Desklink 
49BAH  STA FCCBH            ; Store NADSBox / Desklink / TPDD2 flag     
49BDH  CALL 4998H           ; Flush the RX queue & discard      
49C0H  CALL 49CDH           ; Send "M1" to TX and delay about 3ms      
49C3H  CALL 4998H           ; Flush the RX queue & discard      
49C6H  LXI H,0007H          ; Load opcode for Drive Status       
49C9H  CALL 4822H           ; Send TPDD Command/Len in HL      
49CCH  RET

; =========================================================================================
; Send "M1",0x0D to RS-232 and delay about 3ms
; =========================================================================================
49CDH  LXI B,4D31H          ; Load code for "M1"
49D0H  CALL 49DBH           ; Send BC to RS-232 using XON/XOFF
49D3H  MVI A,0DH            ; Load code for CR (0x0D)
49D5H  CALL 485FH           ; Send A to RS-232 using XON/XOFF
49D8H  JMP 49E3H            ; Delay routine - about 3ms

; =========================================================================================
; Send BC to RS-232 using XON/XOFF
; =========================================================================================
49DBH  MOV A,B              ; Get first byte to send
49DCH  CALL 485FH           ; Send A to RS-232 using XON/XOFF
49DFH  MOV A,C              ; Get 2nd byte to send
49E0H  JMP 485FH            ; Send A to RS-232 using XON/XOFF

; =========================================================================================
; Delay routine - about 72000 cycles = 28.9ms
; =========================================================================================
49E3H  MVI H,14H            ; 7 Cycles
49E5H  MVI L,FFH            ; 7 Cycles
49E7H  DCR L                ; 4 Cycles  
49E8H  JNZ 49E7H            ; 10 Cycles to Jump back. 14 * 255 = 3570 cycles inner loop
49EBH  DCR H                ; 4 Cycles.  But we save 3 cycles from the final JNZ, so 1 cycle  
49ECH  JNZ 49E5H            ; 10 Cycles to jump back. (3570+1+7)*20+7 = 71567 cycles
49EFH  RET                  ; 71567 * 403.877ns = 28.904ms

; =========================================================================================
; Wait for drive to be ready for 20480 counts of 28.9ms = 9.8 minutes???.
; Report error if no data received.
; =========================================================================================
49F0H  LXI B,FF50H          ; Get count of delay times to wait
49F3H  CALL 49E3H           ; Delay 28.9ms      
49F6H  CALL 4BDFH           ; Check RS232 queue for pending characters             
49F9H  RNZ                  ; Return if data available
49FAH  DCR B                ; Decrement inner loop count 
49FBH  JNZ 49F3H            ; Branch to delay and test again     
49FEH  DCR C                ; Decrement outer loop count 
49FFH  JNZ 49F3H            ; Branch to delay and test again     
4A02H  JMP 4867H            ; No data received - Drive Not Ready error     

; =========================================================================================
; Move BC bytes from M to (DE) with increment
; =========================================================================================
4A05H  MOV A,M              ; Get next byte from (HL)
4A06H  STAX D               ; Store next byte at (DE)
4A07H  INX H                ; Increment source pointer
4A08H  INX D                ; Increment destination poiner
4A09H  DCX B                ; Decrement loop control
4A0AH  MOV A,B              ; Prepare to test for zero
4A0BH  ORA C                ; OR in LSB to test for zero
4A0CH  JNZ 4A05H            ; Keep looping until done
4A0FH  RET

; =========================================================================================
; Multiply TPDD free sectors len x 128 = free space.  A '0' is added to LCD to convert this
;      multiplication to x1280 (the TPDD sector size).
; =========================================================================================
4A10H  LDA FD1FH            ; Get number of free sectors reported from server (TPDD)
4A13H  MOV L,A              ; Move #free sectors into HL for multiply
4A14H  MVI H,00H            ; Clear MSB of HL
4A16H  LXI D,0080H          ; Prepare to multiply #sectors x 128 
4A19H  JMP 4C1FH            ; Signed integer muliply (FAC1=HL*DE)

; =========================================================================================
; Compare (HL) with (DE), C bytes long
; =========================================================================================
4A1CH  PUSH D               ; Save pointer to 1st buffer   
4A1DH  PUSH H               ; Save pointer to 2nd buffer  
4A1EH  LDAX D               ; Load next byte from 1st buffer  
4A1FH  SUB M                ; Compare with nex byte from 2nd buffer 
4A20H  JNZ 4A29H            ; Jump to exit if no match     
4A23H  INX H                ; Increment 2nd buffer pointer 
4A24H  INX D                ; Increment 1st buffer pointer 
4A25H  DCR C                ; Decrement length 
4A26H  JNZ 4A1EH            ; Jump to compare next byte if not at end     
4A29H  POP H                ; Restore pointer to 2nd buffer 
4A2AH  POP D                ; Restore pointer to 1st buffer 
4A2BH  RET

; =========================================================================================
; Send buffer at M to screen
; =========================================================================================
4A2CH  (7EH) MOV A,M        ; Get next byte to send to LCD
4A2DH  (A7H) ANA A          ; Test for end of string
4A2EH  (C8H) RZ             ; Return if end of string (NULL terminated)
4A2FH  (E7H) RST 4          ; Send character in A to screen/printer
4A30H  (23H) INX H          ; Increment to next byte
4A31H  (C3H) JMP 4A2CH      ; Branch to test next byte for zero and print

; =========================================================================================
; Fill (HL) with B bytes of A
; =========================================================================================
4A34H  MOV M,A              ; Load next byte at (HL) with A
4A35H  INX H                ; Increment pointer
4A36H  DCR B                ; Decrement counter
4A37H  JNZ 4A34H            ; Keep looping until B bytes copied
4A3AH  RET

; =========================================================================================
; Initialize blank filename and attribute byte
; =========================================================================================
4A3BH  MVI A,20H            ; Prepare to fill TX packet with spaces
4A3DH  MVI B,18H            ; Fill TX buffer with 24 spaces
4A3FH  LXI H,FCE8H          ; Address of TX packet data
4A42H  CALL 4A34H           ; Fill (HL) with B bytes of A
4A45H  MVI A,46H            ; Load "F" TPDD file attribute flag
4A47H  STA FD00H            ; Store in TX packet data location
4A4AH  RET

; =========================================================================================
; Get RAM directory address of selected file into HL
; =========================================================================================
4A4BH  LHLD FCE4H           ; LCD Buffer address of current file selection
4A4EH  CALL 4902H           ; Copy filename at (HL) to current BASIC program

; =========================================================================================
; Get RAM directory address of current BASIC program
; =========================================================================================
4A51H  CALL 4C0FH           ; Update system pointers
4A54H  JMP 4BFBH            ; Find catalog entry address of current BASIC program

; =========================================================================================
; Send Dir Reference for filename at (HL)
; =========================================================================================
4A57H  PUSH H               ; Save filename address on stack  
4A58H  CALL 4A3BH           ; Initialize blank filename and attribute byte      
4A5BH  POP H                ; Restore filename address 
4A5CH  LXI D,FCE8H          ; Address of TX packet data       
4A5FH  LXI B,0006H          ; Prepare to copy 6 bytes of TX data       
4A62H  CALL 4A05H           ; Move BC bytes from M to (DE) with increment      
4A65H  MVI A,2EH            ; Load code for '.'     
4A67H  STAX D               ; Add '.' between name and extension  
4A68H  INX D                ; Increment TX pkt address pointer 
4A69H  LXI B,0002H          ; Prepare to copy extension from filename
4A6CH  CALL 4A05H           ; Move BC bytes from M to (DE) with increment      
4A6FH  CALL 4943H           ; Configure baud, test for NADSBox, get NADSBox dir      

; =========================================================================================
; Send Dir Reference opcode
; =========================================================================================
4A72H  MVI A,00H            ; Set dir reference type to "reference" for open     
4A74H  STA FD01H            ; Save in dir reference location in TX buffer     
4A77H  LXI H,1A00H          ; Load opcode for directory reference
4A7AH  CALL 4822H           ; Send TPDD Command/Len in HL      
4A7DH  CALL 47CCH           ; Receive a packet      
4A80H  LDA FD1CH            ; Load attribute byte     
4A83H  ANA A                ; Test attribute byte for zero 
4A84H  RET

; =========================================================================================
; Save OS's baud rate string to storage area, A = "Print error messages" flag
; =========================================================================================
4A85H  STA FCD2H            ; Store "Print error messages" flag
4A88H  LXI D,F9A8H          ; Storage area for previous baud setting
4A8BH  LXI H,F65BH          ; RS232 parameter setting table   
4A8EH  LXI B,0005H          ; Copy 7 bytes of parameter data
4A91H  JMP 4A05H            ; Move BC bytes from M to (DE) with increment

; =========================================================================================
; Configure baud, check for Desklink/NADSBox.  Set to 9600 if not Desklink/NADSBox
; =========================================================================================
4A94H  CALL 4BD7H           ; Wait for TX empty and Reset UART to accept mode bits         
4A97H  LXI H,4ADEH          ; Baud rate string for "98N1D"          
4A9AH  LXI D,F65BH          ; RS232 parameter setting table
4A9DH  LXI B,0005H          ; Prepare to copy 5 bytes to Baud Parameter table
4AA0H  CALL 4A05H           ; Move BC bytes from M to (DE) with increment
4AA3H  LXI H,F65BH          ; Point to RS232 parameter setting table
4AA6H  STC                  ; Indicate Baud rate is part of string
4AA7H  CALL 4BD3H           ; Set RS232 parameters from string at M
4AAAH  CALL 4998H           ; Flush the RX queue & discard
4AADH  CALL 49A2H           ; Send M1 and test for NADSBox / Desklink response
4AB0H  CALL 49E3H           ; Delay routine - about 3ms
4AB3H  CALL 4BDFH           ; Check RS232 queue for pending characters
4AB6H  JNZ 4998H            ; Jump to flush RX and keep 19200 if NADSBox / Desklink found       
4AB9H  CALL 4BD7H           ; Wait for TX empty and Reset UART to accept mode bits        
4ABCH  LXI H,F65BH          ; Baud rate string for "98N1D"   
4ABFH  DCR M                ; Set baud rate to '8' = 9600 baud   
4AC0H  STC                  ; Indicate Baud rate is part of string 
4AC1H  JMP 4BD3H            ; Set RS232 parameters from string at M - change to 9600 Baud       

; =========================================================================================
; Restore original baud settings
; =========================================================================================
4AC4H  LXI H,F9A8H          ; Storage for OS's baud settings
4AC7H  MOV A,M              ; Get 1st byte of setting   
4AC8H  SUI 3AH              ; Test if 1st byte is numeric   
4ACAH  JC 4BD3H             ; Set RS232 parameters from string at M    
4ACDH  INX H                ; Skip setting baud if 1st byte is "M"odem 
4ACEH  JMP 4BD3H            ; Set RS232 parameters from string at M     

; =========================================================================================
; Strings and table data
; =========================================================================================
4AD1H  DB	"0 Bytes free",0x00  ; Old Text - NOT USED in ROM version, only RAM version
4ADEH  DB	"98N1DNN", 0x00      ; Parameters for setting baud rate
4ADFH  DB   "8N1D",0x00
4AE4H  DB	0x02,0x03,0x01       ; RST 7,30h open mode to TPDD Open mode mapping

4AE9H  DB	"ZZ1",0x04,0x01,0x00,0x84,0xFF,"F"
4AF0H  DB	"ZZ1",0x04,0x01,0x00,0x96,0x0F,0x24
4AF9H  DB	"ZZ1",0x04,0x01,0x00,0x94,0x0F,0x26

4B02H  DB	0x12,"Drive Not Ready",0x00
       DB	0x12,"Communication Error",0x00
       DB	0x12,"Write Protect",0x00
       DB	0x12,"Drive Trouble",0x00
       DB	0x12,"Disk not in drive",0x00
       DB	0x34,"Printer not ready",0x00
       DB	0x05,"File Exists",0x00
       DB	0x39,"Disk Full",0x00
       DB	0x1A,"File Empty",0x00
       DB	0x39,"Directory Full",0x00
       DB	0x07,"Ram Full",0x00
       DB	0x36,0x00
       DB	0x37,"Bad File Name",0x00
4BBBH  DB	" Press any key.",0x00


; =========================================================================================
; RST 1 Vectors to Main ROM.  At places in the code where a call is needed to the Main ROM,
;       a RST 1 followed by a 2-byte address is issued.  The routines will jump to one of
;       the handler functions below to perform that RST 1 operation.
; =========================================================================================

4BCBH  RST 1                ; Call main ROM at address in following 2 bytes
4BCCH  DW    340AH
4BCEH  RET

; =========================================================================================
; Call Main ROM's Evaluate expressino at M-1 routine
; =========================================================================================
4BCFH  RST 1                ; Call main ROM at address in following 2 bytes
4BD0H  DW    1113H
4BD2H  RET

; =========================================================================================
; Call Main ROM's Set RS232 parameters from string at M routine
; =========================================================================================
4BD3H  RST 1                ; Call main ROM at address in following 2 bytes
4BD4H  DW    17E6H
4BD6H  RET

; =========================================================================================
; Call Main ROM's Wait for TX empty and Reset UART routine
; =========================================================================================
4BD7H  RST 1                ; Call main ROM at address in following 2 bytes
4BD8H  DW    6ECBH
4BDAH  RET

; =========================================================================================
; Call Main ROM's Set interrupt to 1DH routine.
;    Seems like it would be easy enough to just do it in this ROM!
; =========================================================================================
4BDBH  RST 1                ; Call main ROM at address in following 2 bytes
4BDCH  DW    765CH
4BDEH  RET

; =========================================================================================
; Call Main ROM's Check RS232 queue for pending characters routine
; =========================================================================================
4BDFH  RST 1                ; Call main ROM at address in following 2 bytes
4BE0H  DW    6D6DH
4BE2H  RET

; =========================================================================================
; Call Main ROM's Send A to RS-232 using XON/XOFF routine
; =========================================================================================
4BE3H  RST 1                ; Call main ROM at address in following 2 bytes
4BE4H  DW    6E32H
4BE6H  RET

; =========================================================================================
; Call Main ROM's Get a character from RS232 receive queue routine
; =========================================================================================
4BE7H  RST 1                ; Call main ROM at address in following 2 bytes
4BE8H  DW    6D7EH
4BEAH  RET

; =========================================================================================
; Call Main ROM's Wait for key from keyboard routine
; =========================================================================================
4BEBH  RST 1                ; Call main ROM at address in following 2 bytes
4BECH  DW    12CBH
4BEEH  RET

; =========================================================================================
; Call Main ROM's Scan keyboard for character (CTRL-BREAK ==> CTRL-C) routine
; =========================================================================================
4BEFH  RST 1                ; Call main ROM at address in following 2 bytes
4BF0H  DW    7242H
4BF2H  RET

; =========================================================================================
; Call Main ROM's Power Down routine
; =========================================================================================
4BF3H  RST 1                ; Call main ROM at address in following 2 bytes
4BF4H  DW    13B5H
4BF6H  RET

; =========================================================================================
; Call Main ROM's Renew automatic power-off counter routine
; =========================================================================================
4BF7H  RST 1                ; Call main ROM at address in following 2 bytes
4BF8H  DW    1BB1H
4BFAH  RET

; =========================================================================================
; Call Main ROM's Find Catalog Entry Address of current BASIC program routine
; =========================================================================================
4BFBH  RST 1                ; Call main ROM at address in following 2 bytes
4BFCH  DW    20AFH
4BFEH  RET

; =========================================================================================
; Call Main ROM's Kill .DO file routine
; =========================================================================================
4BFFH  RST 1                ; Call main ROM at address in following 2 bytes
4C00H  DW    1FBFH
4C02H  RET

; =========================================================================================
; Call Main ROM's Kill .BA file routine
; =========================================================================================
4C03H  RST 1                ; Call main ROM at address in following 2 bytes
4C04H  DW    2017H
4C06H  RET

; =========================================================================================
; Call Main ROM's Kill .CO file routine
; =========================================================================================
4C07H  RST 1                ; Call main ROM at address in following 2 bytes
4C08H  DW    1FD9H
4C0AH  RET

; =========================================================================================
; Call Main ROM's Insert BC spaces at M routine
; =========================================================================================
4C0BH  RST 1                ; Call main ROM at address in following 2 bytes
4C0CH  DW    6B6DH
4C0EH  RET

; =========================================================================================
; Call Main ROM's Update system pointers routine
; =========================================================================================
4C0FH  RST 1                ; Call main ROM at address in following 2 bytes
4C10H  DW    2146H
4C12H  RET

; =========================================================================================
; Call Main ROM's Find BASIC end of program routine
; =========================================================================================
4C13H  RST 1                ; Call main ROM at address in following 2 bytes
4C14H  DW    05F4H
4C16H  RET

; =========================================================================================
; Call Main Rom's Write new entry to catalog routine
; =========================================================================================
4C17H  RST 1                ; Call main ROM at address in following 2 bytes
4C18H  DW    2239H
4C1AH  RET

; =========================================================================================
; Call Main ROM's Signed integer divide (FAC1=DE/HL) routine
; =========================================================================================
4C1BH  RST 1                ; Call main ROM at address in following 2 bytes
4C1CH  DW    377EH
4C1EH  RET

; =========================================================================================
; Call Main ROM's Signed integer muliply (FAC1=HL*DE) routine
; =========================================================================================
4C1FH  RST 1                ; Call main ROM at address in following 2 bytes
4C20H  DW    3725H
4C22H  RET

; =========================================================================================
; Call Main ROM's Send CRLF to LCD routine
; =========================================================================================
4C23H  RST 1                ; Call main ROM at address in following 2 bytes
4C24H  DW    4222H
4C26H  RET

; =========================================================================================
; Call Main ROM's CLS statement routine
; =========================================================================================
4C27H  RST 1                ; Call main ROM at address in following 2 bytes
4C28H  DW    4231H
4C2AH  RET

; =========================================================================================
; Call Main ROM's Erase from cursor to end of line routine
; =========================================================================================
4C2BH  RST 1                ; Call main ROM at address in following 2 bytes
4C2CH  DW    425DH
4C2EH  RET

; =========================================================================================
; Call Main ROM's Start inverse character mode routine
; =========================================================================================
4C2FH  RST 1                ; Call main ROM at address in following 2 bytes
4C30H  DW    4269H
4C32H  RET

; =========================================================================================
; Call Main ROM's Cancel inverse video routine
; =========================================================================================
4C33H  RST 1                ; Call main ROM at address in following 2 bytes
4C34H  DW    426EH
4C36H  RET

; =========================================================================================
; Call Main ROM's Generate error in E routine
; =========================================================================================
4C37H  LXI H,045DH          ; Load address of Main ROM's Generate Error in E routine
4C3AH  PUSH H               ; Push routine to the stack
4C3BH  JMP 00A6H            ; Jump to Main ROM

; =========================================================================================
; Call Main ROM's Print binary number in HL at current position routine
; =========================================================================================
4C3EH  RST 1                ; Call main ROM at address in following 2 bytes
4C3FH  DW    39D4H
4C41H  RET

; =========================================================================================
; Call Main ROM's Print A to printer, expanding tabs if necessary routine
; =========================================================================================
4C42H  RST 1                ; Call main ROM at address in following 2 bytes
4C43H  DW    4B55H
4C45H  RET

; =========================================================================================
; Call Main ROM's Input and display (no "?") line and store routine
; =========================================================================================
4C46H  RST 1                ; Call main ROM at address in following 2 bytes
4C47H  DW    4644H
4C49H  RET

; =========================================================================================
; Call Main ROM's Check keyboard queue for pending characters routine
; =========================================================================================
4C4AH  RST 1                ; Call main ROM at address in following 2 bytes
4C4BH  DW    13DBH
4C4DH  RET

; =========================================================================================
; Call Main ROM's Move BC bytes from M to (DE) with decrement routine
; =========================================================================================
4C4EH  RST 1                ; Call main ROM at address in following 2 bytes
4C4FH  DW    6BE6H
4C51H  RET

; =========================================================================================
; Call Main ROM's LCOPY statement routine
; =========================================================================================
4C52H  RST 1                ; Call main ROM at address in following 2 bytes
4C53H  DW    1E5EH
4C55H  RET

; =========================================================================================
; Call Main ROM's Stop automatic scrolling routine
; =========================================================================================
4C56H  RST 1                ; Call main ROM at address in following 2 bytes
4C57H  DW    423FH
4C59H  RET

; =========================================================================================
; Call Main ROM's Resume Automatic Scrolling routine
; =========================================================================================
4C5AH  RST 1                ; Call main ROM at address in following 2 bytes
4C5BH  DW    4244H
4C5DH  RET

; =========================================================================================
; Call Main ROM's Get char at M and convert to uppercase routine
; =========================================================================================
4C5EH  RST 1                ; Call main ROM at address in following 2 bytes
4C5FH  DW    0FE8H
4C61H  RET

; =========================================================================================
; Call Main ROM's Get file descriptor for file in A routine
; NOT CALLED
; =========================================================================================
4C62H  RST 1                ; Call main ROM at address in following 2 bytes
4C63H  DW    4C81H
4C65H  RET

; =========================================================================================
; Call Main ROM's Update line addresses for current BASIC program routine
; =========================================================================================
4C66H  RST 1                ; Call main ROM at address in following 2 bytes
4C67H  DW    05F0H
4C69H  RET

; =========================================================================================
; Call Main ROM's Copy CO header bytes (6) from (HL) to "active" CO Header storage area routine
; =========================================================================================
4C6AH  RST 1                ; Call main ROM at address in following 2 bytes
4C6BH  DW    253DH
4C6DH  RET

; =========================================================================================
; Call Main ROM's print CO address, length and invocation address to LCD routine
; =========================================================================================
4C6EH  RST 1                ; Call main ROM at address in following 2 bytes
4C6FH  DW    25A4H
4C71H  RET

; =========================================================================================
; Call Main ROM's Process SAVEM arguments for address, length and entry point routine
; =========================================================================================
4C72H  RST 1                ; Call main ROM at address in following 2 bytes
4C73H  DW    2346H
4C75H  RET

Navigate to: