1 ;------------------------------------------------------------------; 2 ; N_CHAN.ASM, network software for the ZX-Spectrum ; 3 ; ; 4 ; Attaches a network to the parallel port of an OPUS Discovery ; 5 ; Cable configuration: ; 6 ; D0 - D3 = nibble to handle ; 7 ; D4 = own togglebit ; 8 ; D5 = server togglebit ; 9 ; D6 = own errorbit ; 10 ; D7 = server errorbit ; 11 ; ACK = halt server-bit ; 12 ; The connectivity should be 100%. ; 13 ; The OPUS needs the optional 2K RAM (or 4K or 8K) to store this ; 14 ; program into, which patches the OPUS ROM. ; 15 ; This version works with nibbles at network level. ; 16 ; Target assembler: Hisoft Devpac Gens 3 (Copyright 1983) ; 17 ; ; 18 ; Copyright (C) 1991 M. van der Heide ThunderWare Research Center. ; 19 ;------------------------------------------------------------------; 20 21 CHADD EQU #5C5D 22 CHANS EQU #5C4F 23 CURCHL EQU #5C51 24 DEFADD EQU #5C0B 25 ERRNR EQU #5C3A 30 ERRSP EQU #5C3D 35 FLAGX EQU #5C71 40 KCUR EQU #5C5B 45 PROG EQU #5C53 50 STKEND EQU #5C65 55 STRMS EQU #5C10 60 TADDR EQU #5C74 65 WORKSP EQU #5C61 70 XPTR EQU #5C5F 75 80 TIMER EQU #0000 85 90 ACKBYT EQU #3001 ; Byte that contains the ACK bit 95 PORTNR EQU #3002 ; Data byte (D0 - D7) 100 CLOCKB EQU #3003 ; Byte that contains the flip-flop clock 105 110 RLOGIN EQU #00 115 RSAVE EQU #01 120 RLOAD EQU #02 125 RERASE EQU #03 130 RMOVE EQU #04 135 140 OKSVR EQU #00 145 FSERR EQU #01 150 OMERR EQU #02 155 PAERR EQU #03 160 165 ORG #2089 ; Just after the tables 170 171 ;------------------------------------------------------------------; 172 ; Starting point (CALL this address to patch the OPUS ROM) ; 175 ; Set pointers ; 178 ;------------------------------------------------------------------; 180 185 SETPNT LD HL,TAB_02 190 LD (#200B),HL ; Command table 195 LD HL,TAB_04 200 LD (#200D),HL ; Channel table 1 205 LD HL,TAB_06 210 LD (#200F),HL ; Channel table 2 215 LD HL,TAB_08 220 LD (#2011),HL ; Device table 225 LD A,#FF ; Signal: Internal 'error' 230 CALL PRERR 235 DEFM "Network mounte" 240 DEFB #E4 ; (Inverted 'd') 245 248 ;------------------------------------------------------------------; 250 ; Start of patched channel table 1 ; 252 ;------------------------------------------------------------------; 255 260 TAB_04 DEFB #20 ; ' ' 265 DEFW #0A75 ; TAB_4:M 270 DEFB #6D ; 'M' 275 DEFW #0A75 ; TAB_4:M 280 DEFB #23 ; '#' 285 DEFW #08EA ; TAB_4:# 290 DEFB #62 ; 'B' 295 DEFW #0954 ; TAB_4:B 300 DEFB #74 ; 'T' 305 DEFW #0948 ; TAB_4:T 310 DEFB #6B ; 'K' 315 DEFW #089C ; TAB_4:KSP 320 DEFB #73 ; 'S' 325 DEFW #089C ; TAB_4:KSP 330 DEFB #70 ; 'P' 335 DEFW #089C ; TAB_4:KSP 340 DEFB #6A ; 'J' 345 DEFW #11BC ; TAB_4:J 350 DEFB #EF ; 'CAT' 355 DEFW #0DC0 ; TAB_4_CAT 360 DEFB #64 ; 'D' 365 DEFW #1032 ; TAB_4:D 370 DEFB #AF ; 'CODE' 375 DEFW #0FFF ; TAB_4:CODE 380 DEFB #6E ; 'N' 385 DEFW TAB_4N 390 DEFB #00 ; Other 395 DEFW #1B4C ; ERR_04 400 402 ;------------------------------------------------------------------; 405 ; Start of patched channel table 2 ; 407 ;------------------------------------------------------------------; 410 415 TAB_06 DEFB #20 ; ' ' 420 DEFW #0C8B ; TAB_6:' ' 425 DEFB #6D ; 'M' 430 DEFW #0C83 ; TAB_6:'M' 435 DEFB #6A ; 'J' 440 DEFW #1B66 ; TAB_6:'J' 445 DEFB #6E ; 'N' 450 DEFW TAB_6N 455 DEFB #00 ; Other 460 DEFW #1B64 ; ERR_06 465 467 ;------------------------------------------------------------------; 470 ; Start of patched device table ; 472 ;------------------------------------------------------------------; 475 480 TAB_08 DEFB #01 ; Drive 1 485 DEFW #123A ; TAB_8:M 490 DEFB #02 ; Drive 2 495 DEFW #123A ; TAB_8:M 500 DEFB #03 ; Drive 3 505 DEFW #123A ; TAB_8:M 510 DEFB #04 ; Drive 4 515 DEFW #123A ; TAB_8:M 520 DEFB #05 ; Ramdisk 48K 525 DEFW #1749 ; TAB_8:5 530 DEFB #06 ; Ramdisk 128K 535 DEFW #178E ; TAB_8:6 540 DEFB #81 ; Printer port 545 DEFW #1814 ; TAB_8:T 550 DEFB #82 ; Joystick port 555 DEFW #11EE ; TAB_8:J 560 DEFB #83 ; Network 565 DEFW TAB_8N 570 DEFB #00 ; Other 575 DEFW #1B89 ; ERR_08 580 582 ;------------------------------------------------------------------; 585 ; Start of patched command table ; 587 ;------------------------------------------------------------------; 590 595 TAB_02 DEFB #F8 ; SAVE 600 DEFW LSVM 605 DEFB #EF ; LOAD 610 DEFW LSVM 615 DEFB #D6 ; VERIFY 620 DEFW LSVM 625 DEFB #D5 ; MERGE 630 DEFW LSVM 635 DEFB #D1 ; MOVE 640 DEFW #0329 ; +++ 645 DEFB #D3 ; OPEN # 650 DEFW #0200 ; OPEN_# 655 DEFB #A9 ; POINT 660 DEFW #03AA ; POINT 665 DEFB #FD ; CLEAR 670 DEFW #03E5 ; CLEAR 675 DEFB #CF ; CAT 680 DEFW #04BF ; +++ 685 DEFB #D0 ; FORMAT 690 DEFW #04DC ; FORMAT 695 DEFB #D2 ; ERASE 700 DEFW #04D8 ; ERASE 705 DEFB #FB ; CLS 710 DEFW #04F2 ; CLS_# 715 DEFB #00 ; Other 720 DEFW #016D ; CONTSIG_2 725 727 ;------------------------------------------------------------------; 730 ; Start of new channel table 1 subtable for 'N' ; 732 ;------------------------------------------------------------------; 735 740 TAB_4N DEFW #0594 ; Read (ERROR_2A) 745 DEFW #0594 ; Write (ERROR_2A) 750 DEFW OPEN_N ; Open 755 DEFW #001C ; Close (return) 760 DEFW #0532 ; Length (USE_M1) 765 DEFW #0BF3 ; Position (REPORT_J) 770 772 ;------------------------------------------------------------------; 775 ; Start of new channel table 2 subtable for 'N' ; 777 ;------------------------------------------------------------------; 780 785 TAB_6N DEFW ERASEN ; Erase 790 DEFW #0594 ; Inquire (REPORT_2A) 795 DEFW #0000 ; Catalog 800 DEFW FORM_N ; Format 805 807 ;------------------------------------------------------------------; 810 ; Start of new device table subtable for 'N' (code #83) ; 812 ;------------------------------------------------------------------; 815 820 TAB_8N DEFW SENDB ; Write 825 DEFW READB ; Read 830 DEFW #001C ; Inquire (return) 835 DEFW SDLOG ; Format (LOGIN) 840 842 ;------------------------------------------------------------------; 845 ; THE 'OPEN THE N-CHANNEL' SUBROUTINE ; 847 ; ; 850 ; This subroutine is used to open a (temporary) N-channel. ; 852 ;------------------------------------------------------------------; 855 860 OPEN_N LD HL,#0012 ; Length of channel 865 CALL #0818 ; Make channel 870 DEFB #80 ; Station number (max 255) 875 DEFB #F6 ; Filename needed and <= 10 chars 880 RET NC 885 JP NZ,#0DE2 ; Report 'Invalid filename' 890 LD A,#FF ; Signal: internal error 895 CALL PRERR 900 DEFM "Invalid station numbe" 905 DEFB #F2 ; (Inverted 'r') 910 912 ;------------------------------------------------------------------; 915 ; A network channel contains 18 bytes: ; 917 ; ; 920 ; Bytes 0 and 1: the output address ; 925 ; Bytes 2 and 3: the input adress ; 930 ; Byte 4 : the devicename (upper case when permanent) ; 935 ; Bytes 5 and 6: the channel length ; 940 ; Byte 7 : the requested station ; 945 ; Bytes 8 to 17: the file-name ; 947 ;------------------------------------------------------------------; 950 952 ;------------------------------------------------------------------; 955 ; THE 'LOAD, SAVE, VERIFY AND MERGE' COMMAND ROUTINES ; 957 ; ; 960 ; The A register holds the first character after the keyword. ; 965 ; (TADDR) holds 0 for SAVE, 1 for LOAD, 2 for VERIFY and 3 for ; 970 ; MERGE. ; 975 ; In case of SAVE, the channel should be opened for output ; 980 ; and create, in all other cases just for input. The routine ; 985 ; uses large parts of the tape-routine in the Spectrum ROM, ; 990 ; so sufficient room for the stack is needed. ; 992 ;------------------------------------------------------------------; 995 1000 LSVM CP #2A ; Is it a '*' ? 1005 JP NZ,#016D ; (CONTSIG_2) 1010 RST #18 ; Runtime or checking ? 1015 JR Z,LSVM_2 ; Step if checking 1020 LD BC,#0022 ; Make room for 2 headers 1025 RST #10 ; Call MAIN ROM 1030 DEFW #0030 ; MAKE_BC_SPACES 1035 LSVM_2 PUSH IX ; Store 128K register 1040 PUSH DE ; Store start of room 1045 RST #20 ; Next char 1050 CALL #053A ; (CHK_CHAN) 1055 CALL #062F ; (CD_TO_STK) 1060 DEFW #0652 ; (SA_DATA) 1065 DEFW #0109 ; Length 1070 LD IX,#0071 ; IX := Start + 113 1075 ADD IX,DE 1080 LD (IX-#26),#C9 ; Put a RET at relative address 069D 1085 LD A,#C3 ; 1FC3 = UNSTACK_Z 1090 CALL #0319 ; STOREADDR (i.s.o. 1BEE,CHECK_END) 1095 INC IX 1100 LD A,#1F 1105 CALL #0319 1110 POP IX ; IX := start of headers 1115 CALL #0656 ; (EXEC_STK) 1120 LSVM_3 EX (SP),IX ; Switch start headers/128K register 1125 PUSH HL ; Store startaddress of data 1130 CALL #050E ; (CHK_END) 1135 POP HL ; Retrieve startaddress of data 1140 LD (KCUR),HL ; KCUR moves too on channel creation 1145 POP IX ; IX := start of headers 1150 PUSH HL ; Store startaddress of data 1155 LD HL,(STKEND) 1160 DEC HL 1165 DEC HL 1170 DEC HL 1175 LD B,(HL) 1180 DEC HL 1185 LD C,(HL) 1190 LD A,(BC) 1195 CP #6E ; Handling the N-channel ? 1200 JP NZ,#02E1 ; Continue in ROM if not 1205 LD B,(IX+#0C) ; BC := length of datablock 1210 LD C,(IX+#0B) 1215 RST #10 ; (STACK_BC) 1220 DEFW #2D2B 1225 LSVM_4 LD A,(TADDR) 1230 AND A ; Saving ? 1235 PUSH AF ; (Store command type) 1240 LD A,#05 ; Signal: output, create 1245 JR Z,LSVM_5 ; Step if saving 1250 LD A,#02 ; Signal: input 1255 LSVM_5 LD HL,#0000 ; HL = record length 1260 CALL #06A9 ; (OPEN_CHAN) 1265 POP AF ; Restore command type 1270 POP DE ; DE := startaddress of data 1275 LD HL,(STKEND) ; Has the startaddress been shifted 1280 SBC HL,DE ; due to the creation of a channel ? 1285 LD HL,(WORKSP) ; HL := start of headers 1290 EX DE,HL 1295 JR C,LSVM_6 1300 LD HL,(KCUR) ; Then use the saved value instead 1305 LSVM_6 CALL SAVE_N ; Now save the channel 1310 JP #0368 ; And close the channel 1315 1317 ;------------------------------------------------------------------; 1320 ; THE 'SAVE THE N-CHANNEL' SUBROUTINE ; 1322 ; ; 1325 ; On entry the A register holds 0 for SAVE, 1 for LOAD, 2 for ; 1330 ; VERIFY and 3 for MERGE. ; 1335 ; IX points to the start of the channel ; 1340 ; HL points to the startaddress of the required datablock ; 1345 ; DE points to the start of the headers ; 1350 ; Depending on A, a different part of the routine is used. ; 1352 ;------------------------------------------------------------------; 1355 1360 SAVE_N PUSH HL 1365 PUSH DE 1370 PUSH IX ; Copy start of channel to HL 1375 POP HL 1380 LD BC,#0008 1385 ADD HL,BC ; HL := filename in channel 1390 INC DE ; DE := filename in header 1395 LD BC,#000A ; BC := length of filename 1400 LDIR 1405 POP DE ; DE := start of header 1410 LD A,(TADDR) ; Saving ? 1415 AND A 1420 JP NZ,NOSAVE ; Step if not 1425 1430 ; First the save-request is sent, together with the header. 1435 1440 DOSAVE LD A,RSAVE 1445 PUSH DE ; Store start of headers 1450 CALL SENDB ; Send request to save 1455 POP DE ; Get start of headers in DE 1460 PUSH DE 1465 LD BC,#0011 ; Send 17 bytes (the header) 1470 CALL SVBYTE 1475 1480 ; Now wait for the server to receive a returncode 1485 1490 CALL SETRD ; Go to read mode 1495 CALL SIGRDY 1500 CALL READB ; Read the returncode 1505 AND A 1510 JP NZ,PRERR ; Report an error if found 1515 1520 ; Now save the datablock. 1525 1530 SVDATA CALL SETWR ; Go to write mode 1535 POP HL ; HL := start of header 1540 POP DE ; DE := start of data 1545 LD BC,#000B 1550 ADD HL,BC 1555 LD C,(HL) ; Read the blocklength in BC 1560 INC HL 1565 LD B,(HL) 1570 CALL SIGRDY 1575 CALL SVBYTE ; Save the datablock 1580 1585 ; A final read is done, to see if the datablock was received ok. 1590 1595 CALL SETRD ; Go to read mode 1600 CALL SIGRDY 1605 CALL READB ; Read the returncode 1610 AND A 1615 JP NZ,PRERR ; Report an error if found 1620 CALL SETWR ; Finish in write mode 1625 JP SIGRDY 1630 1632 ;------------------------------------------------------------------; 1635 ; THE 'SET WRITE MODE' SUBROUTINE ; 1637 ; ; 1640 ; The lower nibble of the port is set to output ; 1642 ;------------------------------------------------------------------; 1645 1650 SETWR CALL SIGBSY ; Signal to the server: switching 1655 LD HL,CLOCKB 1660 LD (HL),#00 ; Clock on 1665 DEC HL 1670 LD (HL),#5F ; %0101 1111 1675 INC HL 1680 LD (HL),#04 ; Clock off 1685 RET 1690 1695 1697 ;------------------------------------------------------------------; 1700 ; THE 'SET READ MODE' SUBROUTINE ; 1702 ; ; 1705 ; The lower nibble of the port is set to input ; 1707 ;------------------------------------------------------------------; 1710 1715 SETRD CALL SIGBSY 1720 LD HL,CLOCKB 1725 LD (HL),#00 1730 DEC HL 1735 LD (HL),#50 ; %0101 0000 1740 INC HL 1745 LD (HL),#04 1750 RET 1755 1757 ;------------------------------------------------------------------; 1760 ; THE 'SEND BYTES TO SERVER' SUBROUTINE ; 1762 ; ; 1765 ; BC bytes are saved from DE onward. A parity matching byte ; 1770 ; is built like in the taperoutines. This byte is sent as last ; 1775 ; byte ; 1777 ;------------------------------------------------------------------; 1780 1785 SVBYTE EXX ; Use alternate registers while sending 1790 LD D,#00 ; Initialize the parity byte 1795 EXX 1800 SVBYT2 LD A,B ; Last byte sent ? 1805 OR C 1810 JR Z,SVBYT3 ; Then send the parity 1815 LD A,(DE) ; (The byte to be sent) 1820 EXX 1825 LD B,A ; Make a copy 1830 XOR D ; Include into parity byte 1835 LD D,A 1840 LD A,B 1845 CALL SENDB ; Send the current byte 1850 EXX 1855 INC DE ; Point to next byte 1860 DEC BC ; One less to go 1865 JR SVBYT2 ; Send next 1870 SVBYT3 EXX 1875 LD A,D ; Fetch parity byte into A 1880 CALL SENDB ; And send it 1885 EXX 1890 RET 1895 1897 ;------------------------------------------------------------------; 1900 ; THE 'SEND A BYTE TO THE SERVER' SUBROUTINE ; 1902 ; ; 1905 ; This subroutine is used to send the byte held in A to the ; 1910 ; server. First a check is made to see if the network state ; 1915 ; is ok, then the byte is sent nibble by nibble. ; 1917 ;------------------------------------------------------------------; 1920 1925 SENDB DI ; No interrupts while timing 1930 LD HL,PORTNR 1935 LD E,A ; Make a copy of the byte 1940 LD BC,TIMER ; Init timer 1945 LD A,(HL) ; Read network state 1950 AND %10100000 ; Test error and togglebit of server 1955 XOR %10000000 ; Error in togglebit if reset 1960 JP NZ,HNDERR ; Handle any error 1965 SEND2 LD A,E ; Fetch byte 1970 RRCA 1975 RRCA 1980 RRCA 1985 RRCA 1990 AND #0F ; Send high nibble first 1995 LD (HL),A ; Own togglebit now low 2000 SNIBB1 BIT 7,(HL) ; Wait for server togglebit to go low 2005 JR Z,SEND3 2010 BIT 5,(HL) ; Timeout by server ? 2015 JR NZ,HNDERR ; Then handle the error 2020 DEC BC 2025 LD A,B 2030 OR C 2035 JR NZ,SNIBB1 ; (Timer circuit) 2040 JR TIMOUT ; Report the timeout 2045 SEND3 LD BC,TIMER ; Re-init timer 2050 LD A,E ; Fetch byte again 2055 AND #0F ; Prepare for low nibble 2060 OR %01000000 ; Own togglebit will go high 2065 LD (HL),A 2070 SNIBB2 BIT 7,(HL) ; Wait for server togglebit to go high 2075 JR NZ,SEND4 2080 BIT 5,(HL) ; Timeout by server ? 2085 JR NZ,HNDERR ; Then handle the error 2090 DEC BC 2095 LD A,B 2100 OR C 2105 JR NZ,SNIBB2 ; (Timer circuit) 2110 JR TIMOUT ; Report the timeout 2115 SEND4 EI ; Enable interrupts before returning 2120 RET 2125 2127 ;------------------------------------------------------------------; 2130 ; THE 'TIMEOUT HANDLER' ROUTINE ; 2132 ; ; 2135 ; Timeout has occured while sending a nibble to the server. ; 2140 ; Set the errorbit and wait for the errorbit from the server ; 2145 ; After this, reset it and wait for the server to reset too ; 2150 ; If no new timeout is found, then resend the byte, otherwise ; 2155 ; report the error in BASIC ; 2157 ;------------------------------------------------------------------; 2160 2165 TIMOUT SET 4,(HL) ; Set own errorbit 2170 LD BC,TIMER ; Set timer 2175 TIME2 BIT 5,(HL) ; Wait for server errorbit to go high 2180 JR NZ,TIME3 2185 DEC BC 2190 LD A,B 2195 OR C 2200 JR NZ,TIME2 ; (Timer circuit) 2205 2210 ; A new timeout occured. Print the error in BASIC 2215 2220 TIMERR LD A,#FF ; Signal: internal error 2225 CALL PRERR 2230 DEFM "Contact lost with serve" 2235 DEFB #F2 ; (Inverted 'r') 2240 2245 TIME3 RES 4,(HL) ; Reset errorbit 2250 LD BC,TIMER ; Init timer 2255 TIME4 BIT 5,(HL) ; Wait for error server to go low again 2260 JR Z,REPTO ; Report the error 2265 DEC BC 2270 LD A,B 2275 OR C 2280 JR NZ,TIME4 ; (Timer circuit) 2285 JR TIMERR ; Report the error if timeout again 2290 REPTO LD A,#01 ; Signal: message from server 2295 JP PRERR 2300 2302 ;------------------------------------------------------------------; 2305 ; THE 'HANDLE ERROR IN NETWORK STATE' SUBROUTINE ; 2307 ; ; 2310 ; The server errorbit is set, or the togglebit reset. ; 2315 ; Set own errorbit to reset the server. ; 2317 ;------------------------------------------------------------------; 2320 2325 HNDERR BIT 5,(HL) ; Errorbit set? 2330 SET 6,(HL) ; Ensure own togglebit is high 2335 JR Z,HNDTGE ; Step if not 2340 SET 4,(HL) ; Set own errorbit too 2345 LD BC,TIMER ; Init timer 2350 ETIME1 BIT 5,(HL) ; Wait for the server error to go low 2355 JR Z,HNDTG2 ; Step if before timeout 2360 DEC BC 2365 LD A,B 2370 OR C 2375 JR NZ,ETIME1 ; (Timer circuit) 2380 2385 ; The server does not respond. This means that the server is 2390 ; not connected. Report the error from BASIC 2395 2400 LD A,#FF ; Signal: internal error 2405 CALL PRERR 2410 DEFM "Server not onlin" 2415 DEFB #E5 ; (Inverted 'e') 2420 2425 ; The togglebit is reset. Report the error to the server 2430 ; and wait for a reset 2435 2440 HNDTGE SET 4,(HL) ; Set errorbit 2445 LD BC,TIMER ; Init timer 2450 ETIME2 BIT 5,(HL) ; Wait for the server error to go high 2455 JR NZ,HNDTG2 2460 DEC BC 2465 LD A,B 2470 OR C 2475 JR NZ,ETIME2 ; (Timer circuit) 2480 2485 ; The server does not respond before timeout 2490 ; Report the error from BASIC 2495 2500 TGERR LD A,#FF ; Signal: internal error 2505 CALL PRERR 2510 DEFM "Invalid network stat" 2515 DEFB #E5 ; (Inverted 'e') 2520 RET 2525 2530 HNDTG2 RES 4,(HL) ; Reset own errorbit 2535 LD BC,TIMER ; Init timer 2540 ETIME3 BIT 5,(HL) ; Wait for server error to go low too 2545 JR Z,TGERR2 2550 DEC BC 2555 LD A,B 2560 OR C 2565 JR NZ,ETIME3 ; (Timer circuit) 2570 JR TGERR ; Report the error if timeout occured 2575 TGERR2 BIT 7,(HL) ; Test again if togglebit now set 2580 JR Z,TGERR ; Report the error if not 2585 2590 ; The handshake is done. Read the error from the server 2595 2600 HNDER2 LD A,#01 ; Signal: message from server 2605 JP PRERR 2610 2612 ;------------------------------------------------------------------; 2615 ; THE 'READ A BYTE FROM THE SERVER' SUBROUTINE ; 2617 ; ; 2620 ; One byte is read from the server and returned in A ; 2622 ;------------------------------------------------------------------; 2625 2630 READB DI ; No interrupts while timing 2635 LD HL,PORTNR 2640 LD BC,TIMER ; Init the timer 2645 BIT 5,(HL) ; Handle any error first 2650 JP NZ,HNDERR 2655 RNIBB1 LD A,(HL) ; Read a nibble 2660 BIT 7,A ; Only valid if togglebit low 2665 JR Z,READ2 2670 BIT 5,A ; Timeout by server ? 2675 JP NZ,HNDERR ; Then report the error 2680 DEC BC 2685 LD A,B 2690 OR C 2695 JR NZ,RNIBB1 ; (Timer circuit) 2700 JP TIMOUT ; Report the error 2705 READ2 RLCA 2710 RLCA 2715 RLCA 2720 RLCA 2725 AND #F0 ; Keep only nibblepart 2730 LD E,A ; Store high nibble in E 2735 RES 6,(HL) ; Make own toggelbit low 2740 LD BC,TIMER ; Init timer 2745 RNIBB2 LD A,(HL) ; Read next nibble 2750 BIT 7,A ; Only valid if togglebit high 2755 JR NZ,READ3 2760 BIT 5,A ; Timeout by server ? 2765 JP NZ,HNDERR ; Then report the error 2770 DEC BC 2775 LD A,B 2780 OR C 2785 JR NZ,RNIBB2 ; (Timer circuit) 2790 JP TIMOUT ; Handle timeout 2795 READ3 AND #0F ; Keep only nibblepart 2800 OR E ; Include high nibble 2805 SET 6,(HL) ; Make togglebit high again 2810 EI ; Enable interrupts before returning 2815 RET 2820 2822 ;------------------------------------------------------------------; 2825 ; THE 'READ BYTES FROM SERVER' SUBROUTINE ; 2827 ; ; 2830 ; BC bytes are read and stored from DE onward ; 2835 ; While reading, a parity matching byte is built as in the ; 2840 ; taperoutine. The last read byte should match this byte. ; 2842 ;------------------------------------------------------------------; 2845 2850 LDBYTE EXX ; Use alternate registers 2855 LD D,#00 ; Initialize the parity 2860 EXX 2865 LDBYT2 LD A,B ; Last byte read ? 2870 OR C 2875 JR Z,LDBYT3 ; Then step 2880 EXX 2885 CALL READB ; Read a byte 2890 LD B,A ; Make a copy 2895 XOR D ; Include into parity 2900 LD D,A 2905 LD A,B ; Read byte into A again 2910 EXX 2915 LD (DE),A ; Store the byte 2920 INC DE ; Point to next byte 2925 DEC BC ; One less to go 2930 JR LDBYT2 ; Read next 2935 LDBYT3 EXX 2940 CALL READB ; Read the parity byte 2945 XOR D ; Match with own parity 2950 EXX 2955 RET 2960 2962 ;------------------------------------------------------------------; 2965 ; THE 'REPORT AN ERROR IN BASIC' SUBROUTINE ; 2967 ; ; 2970 ; This subroutine is CALLed on two occasions: when an internal ; 2975 ; error occured (A=#FF); immediately after the CALL is the ; 2980 ; message, or from the server (A<>#FF); the message will have ; 2985 ; to be read byte by byte from the server. ; 2987 ;------------------------------------------------------------------; 2990 2995 PRERR EI ; Ensure interrupts are enabled 3000 HALT 3005 HALT ; Wait for server 3010 HALT 3015 HALT 3020 HALT 3025 PUSH AF ; Store errortype 3030 CALL SIGBSY ; Halt the server 3035 RES 3,(IY+#02) ; Signal: mode not changed 3040 RST #10 ; Clear all work areas 3045 DEFW #16B0 ; (SET_MIN) 3050 CALL #05FB ; Clear temporary channels 3055 RES 5,(IY+#01) ; Signal: ready for a new key 3060 LD HL,#0000 ; The system variables FLAGX, XPTR-hi 3065 XOR A ; and DEFADD are all set to zero 3070 LD (FLAGX),A 3075 LD (XPTR),A 3080 LD (DEFADD),HL 3085 INC HL ; Ensure that stream #00 points to 3090 LD (STRMS+6),HL ; channel 'K' 3095 RST #10 ; Clear the lower screen 3100 DEFW #0D6E ; (CLS_LOWER) 3105 SET 5,(IY+#02) ; Signal: lower screen requires clearing 3110 POP AF ; Read errortype 3115 INC A ; Internal error ? 3120 JR Z,INTERR ; Then step 3125 SVERR CALL SETRD ; Go to read mode 3130 CALL PRTEXT ; Read the message (and print it) 3135 JR CONTE ; And step 3140 INTERR POP HL ; Fetch start of text 3145 CALL #05EF ; (PRINT_MSG) 3150 CALL SETWR ; Ensure port is in write mode 3155 CALL SIGRDY 3160 CONTE LD A,#40 ; Togglebit high, error low 3165 LD (PORTNR),A 3170 LD SP,(ERRSP) ; Clear the stack 3175 JP #01BB ; Finish error in Spectrum ROM 3180 3182 ;------------------------------------------------------------------; 3185 ; THE 'FORMAT THE N-CHANNEL' SUBROUTINE ; 3187 ; ; 3190 ; Formatting the 'N' channel means setting the port to ; 3195 ; the right format and logging to the server ; 3197 ;------------------------------------------------------------------; 3200 3205 FORM_N PUSH DE ; DE = parameter start 3210 CALL #0863 ; Test parameters: 3215 DEFB #0F ; - Status < 16 3220 DEFB #00 ; - No filename allowed 3225 JR NC,FRM_N2 ; Step if no errors 3230 LD A,#FF ; Signal: internal error 3235 JR NZ,SECOND ; Step if in second parameter 3240 FIRST CALL PRERR 3245 DEFM "Invalid statu" 3250 DEFB #F3 ; (Inverted 's') 3255 SECOND CALL PRERR 3260 DEFM "Too many parameter" 3265 DEFB #F3 3270 FRM_N2 POP HL ; HL := parameter start 3275 INC HL 3280 LD H,(HL) ; H := status 3285 LD B,#06 3290 LD A,#83 ; Signal: network port 3295 JP #0FE5 ; Login 3300 3302 ;------------------------------------------------------------------; 3305 ; THE 'LOGIN TO THE SERVER' SUBROUTINE ; 3307 ; ; 3310 ; On entry H holds the status ; 3312 ;------------------------------------------------------------------; 3315 3320 SDLOG PUSH HL ; Store status byte 3325 LD HL,ACKBYT ; Init ACKbyte as output 3330 SET 4,(HL) 3335 RES 5,(HL) ; Init ACK as active low (inverted!) 3340 CALL SETWR ; Now set the correct bits for I/O 3345 LD A,#40 ; Ensure togglebit high, error low 3350 LD (PORTNR),A 3355 CALL SIGRDY 3360 LD A,RLOGIN ; Now send a login code to the server 3365 CALL SENDB 3370 POP AF ; A holds the status 3375 CALL SENDB ; Send status too 3380 CALL SETRD ; Go to read mode 3385 RST #10 3390 DEFW #0DAF ; Clear the main screen 3395 3400 ; The server wil send a string of bytes, followed by a 0. 3405 ; Print each byte in the screen. 3410 3415 PRTEXT CALL SIGRDY 3420 RDMESS CALL READB ; Read each byte in turn 3425 AND A ; Last byte ? 3430 JR Z,MESEND ; Step out through SETWR 3435 RST #10 ; Print the byte 3440 DEFW #0010 3445 JR RDMESS ; Loop back for the next byte 3450 MESEND CALL SETWR ; End in write mode 3455 JP SIGRDY 3460 3462 ;------------------------------------------------------------------; 3465 ; THE 'SIGNAL "READY" TO SERVER' SUBROUTINE ; 3467 ; ; 3470 ; This short subroutine sets the ACK line (inverted!) to signal ; 3475 ; to the server that I/O can be continued. ; 3477 ;------------------------------------------------------------------; 3480 3485 SIGRDY PUSH HL ; Do not disturb HL 3490 LD HL,ACKBYT 3495 RES 5,(HL) ; Make ACK high 3500 POP HL 3505 RET 3510 3512 ;------------------------------------------------------------------; 3515 ; THE 'SIGNAL "BUSY" TO THE SERVER' SUBROUTINE ; 3517 ; ; 3520 ; The ACK line is made low (inverted!) to signal to the server ; 3525 ; that I/O is halted. ; 3527 ;------------------------------------------------------------------; 3530 3535 SIGBSY PUSH HL ; Do not disturn HL 3540 LD HL,ACKBYT 3545 SET 5,(HL) ; Make ACK low 3550 POP HL 3555 RET 3560 3562 ;------------------------------------------------------------------; 3563 ; The 'SAVE THE N-CHANNEL' subroutine continues here ; 3565 ; The current request is not save. First send the load request and ; 3570 ; the header as received from the BASIC system. ; 3572 ;------------------------------------------------------------------; 3575 3580 NOSAVE LD A,RLOAD 3585 PUSH DE ; Store start of 1st header 3590 CALL SENDB ; Send request to load 3595 POP DE 3600 LD BC,#0011 ; Send 17 bytes (the 1st header) 3605 CALL SVBYTE 3610 PUSH DE ; DE points to the 2nd header 3615 3620 ; Now wait for the server to receive a returncode 3625 3630 CALL SETRD ; Go to read mode 3635 CALL SIGRDY 3640 CALL READB ; Read the returncode 3645 AND A 3650 JP NZ,PRERR ; Report an error if found 3655 3660 ; First read the header from the server as found on disk. 3665 3670 POP DE ; Fetch start of 2nd header in DE 3675 LD BC,#0011 ; Read 17 bytes 3680 PUSH DE 3685 CALL LDBYTE 3690 LD A,PAERR ; Prepare for parity error 3695 JP NZ,REPSVR ; Send the error if found 3700 CALL SIGBSY ; Halt the server 3705 EX (SP),IX ; IX := start of 2nd header 3710 POP HL ; HL := start of channel 3715 EX (SP),HL ; HL := startaddress, stack back both 3720 PUSH HL 3725 SERRSP LD HL,(ERRSP) ; Store current value of ERRSP below 3730 EX (SP),HL ; Start of data 3735 PUSH HL 3740 LD HL,REPBER ; Store new error handling routine start 3745 EX (SP),HL ; in between 3750 PUSH HL 3755 LD HL,#1708 ; Ensure that the OPUS is paged in when 3760 EX (SP),HL ; handling an error in BASIC 3765 LD (ERRSP),SP ; Reinitialize the ERRSP 3770 PUSH HL ; Store start of data 3775 PUSH HL ; Add 2 more numbers to the stack, 3780 PUSH HL ; to avoid corruption by CD_TO_STK 3785 3790 ; The stack is now set up as follows: 3795 ; start_of_channel, old_errsp, new_errsp, page_in, start_of_data 3800 3805 LD A,(IX+#00) ; Fetch file-type in A 3810 CP #03 ; CODE ? 3815 JR Z,VRCTRL ; Then use the verify routine 3820 LD A,(TADDR) ; Fetch request 3825 DEC A ; LOAD ? 3830 JR Z,LDCTRL ; Step if so 3835 DEC A ; VERIFY ? 3840 JP NZ,MGCTRL ; Step if not 3845 3847 ;------------------------------------------------------------------; 3848 ; THE 'VERIFY CONTROL' SUBROUTINE ; 3849 ; ; 3850 ; The header is verified and a jump made to the LOAD routine ; 3852 ;------------------------------------------------------------------; 3855 3860 VRCTRL CALL #062F ; Copy code to stack: 3865 DEFW #07CB ; VERIFY control 3870 DEFW #003D ; (Length) 3875 DEC HL 3880 LD (HL),#E1 ; Place 'POP HL' at relative address 0806 3885 DEC HL 3890 DEC HL 3895 DEC HL 3900 LD (HL),#C9 ; 'RET' at address 0803 3905 DEC HL 3910 LD (HL),#3C ; and 'INC A' at address 0802 3915 POP HL ; Get startaddress 3920 CALL #0656 ; Execute the code 3925 JR Z,LDCTR2 ; Skip if no errors 3930 VRERR LD A,FSERR ; Report the error to the server 3935 CALL REPSTK ; Repair error stack 3940 JP REPSVR 3945 3947 ;------------------------------------------------------------------; 3948 ; THE 'LOAD CONTROL' SUBROUTINE ; 3950 ;------------------------------------------------------------------; 3952 3953 LDCTRL CALL #062F ; Copy code to stack: 3955 DEFW #0808 ; LOAD control 3960 DEFW #00AC ; (Length) 3965 LD HL,#0068 3970 ADD HL,DE ; Plave a 'RET' at relative address 0870 3975 LD (HL),#C9 3980 POP HL ; HL := startaddress 3985 CALL #0656 ; Execute the code 3990 LDCTR2 LD B,D ; BC := length 3995 LD C,E 4000 CALL REPSTK ; Repair the error stack 4005 PUSH IX ; IX = startaddress 4010 CALL SENDOK ; Send the 'OK' byte 4015 POP DE ; DE := startaddress 4020 JR NC,GOVER ; Step in verifying 4025 GOLOAD CALL SIGRDY 4030 CALL LDBYTE ; Load the datablock 4035 CONTLD JR Z,LDEND ; Finish in write mode if no error occured 4040 LD A,#FF ; Signal: internal error 4045 LDERR CALL PRERR ; Report the error 4050 DEFM "Parity erro" 4055 DEFB #F2 4060 4065 LDEND CALL SETWR 4070 CALL SIGRDY 4075 POP IX ; IX := start of channel 4080 RET 4085 4090 GOVER CALL SIGRDY 4095 CALL VEBYTE ; Verify the block 4100 JR NZ,LDERR ; Report parity error if found 4105 LD A,(ERRNR) 4110 INC A ; Error in verifying ? 4115 JR NZ,LDEND ; Finish if verification OK 4120 DEC A 4125 CALL PRERR ; Print the error 4130 DEFM "Verification faile" 4135 DEFB #E4 ; (Inverted 'd') 4140 4142 ;------------------------------------------------------------------; 4143 ; THE 'MERGE CONTROL' SUBROUTINE ; 4144 ; ; 4145 ; Room is created to hold the data to be MERGEd and the data is ; 4150 ; loaded into it. The program and its variables are merged with ; 4155 ; the current program. The Spectrum ROM routine is used, so all ; 4160 ; the tape-protections will still work ; 4162 ;------------------------------------------------------------------; 4165 4170 MGCTRL CALL #062F ; Copy code to stack: 4175 DEFW #08B6 ; MERGE control 4180 DEFW #000E ; (Length) 4185 POP HL ; HL := startaddress 4190 CALL #0656 ; Execute the code 4195 LD B,D ; BC := length 4200 LD C,E 4205 CALL REPSTK ; Repair error stack 4210 PUSH DE 4215 CALL SENDOK ; Send 'OK' byte 4220 POP DE 4225 CALL SIGRDY 4230 CALL LDBYTE ; Load the block 4235 JR NZ,LDERR ; Report an error if found 4240 POP HL ; HL := startaddress 4245 RST #10 ; Now merge the program and its variables 4250 DEFW #08CE ; (ME_NEW_LP-6) 4255 JR LDEND ; Restore the stack and network state 4260 4262 ;------------------------------------------------------------------; 4265 ; THE 'REPORT AN ERROR TO THE SERVER' SUBROUTINE ; 4267 ;------------------------------------------------------------------; 4270 4275 REPSVR PUSH AF ; Store errorcode 4280 CALL SETWR ; Go to write mode 4285 CALL SIGRDY 4290 POP AF 4295 CALL SENDB ; Send errorcode 4300 LD A,#01 ; Signal: message from server 4305 JP PRERR ; Read the error message 4310 4312 ;------------------------------------------------------------------; 4315 ; THE 'SIGNAL "OK" TO THE SERVER' SUBROUTINE ; 4317 ;------------------------------------------------------------------; 4320 4325 SENDOK PUSH AF 4330 PUSH BC 4335 CALL SETWR 4340 CALL SIGRDY 4345 LD A,OKSVR 4350 CALL SENDB 4355 CALL SETRD 4360 POP BC 4365 POP AF 4370 RET 4375 4377 ;------------------------------------------------------------------; 4380 ; THE 'REPORT BASIC ERROR' ROUTINE ; 4382 ; ; 4385 ; An error was detected by the BASIC system. ; 4390 ; Rearrange the stack and report the error to the server ; 4392 ;------------------------------------------------------------------; 4395 4400 REPBER LD A,(ERRNR) ; Read the error-code 4405 CP #03 ; Out of memory ? 4410 JR NZ,SDBER ; Send if not 4415 LD A,OMERR ; Prepare to send this code 4420 SDBER POP HL ; The 'old' ERRSP 4425 LD (ERRSP),HL ; Restore it 4430 JR REPSVR ; Pass the error to the server 4435 4437 ;------------------------------------------------------------------; 4440 ; THE 'VERIFY BYTES' SUBROUTINE ; 4442 ; ; 4445 ; BC bytes are verified from DE onward ; 4447 ;------------------------------------------------------------------; 4450 4455 VEBYTE EXX ; Use alternate registers 4460 LD D,#00 ; Initialize the parity 4465 EXX 4470 VEBYT2 LD A,B ; Last byte done ? 4475 OR C 4480 JP Z,LDBYT3 ; Then step 4485 EXX 4490 CALL READB ; Read a byte 4495 LD B,A ; Make a copy 4500 XOR D ; Include into parity 4505 LD D,A 4510 LD A,B ; Read byte into A again 4515 EXX 4520 EX DE,HL 4525 CP (HL) ; The actual comparison 4530 EX DE,HL 4535 JR Z,VEBYT3 ; Step if they match 4540 LD A,#FF ; Signal via ERRNR 4545 LD (ERRNR),A 4550 VEBYT3 INC DE ; Point to next 4555 DEC BC ; One less to go 4560 JR VEBYT2 ; Read next 4565 4567 ;------------------------------------------------------------------; 4570 ; THE 'ERASE FROM THE "N" CHANNEL' COMMAND ROUTINE ; 4572 ; ; 4575 ; The channel is opened, the erase request sent, together with ; 4580 ; the filename ; 4582 ;------------------------------------------------------------------; 4585 4590 ERASEN LD HL,#0000 ; Recordlength 4592 XOR A ; Signal: input, output 4595 CALL #06A9 ; (OPEN_CHAN) 4600 PUSH IX 4605 POP HL ; Make HL point to the filename 4610 LD DE,#0008 4615 ADD HL,DE 4620 PUSH HL 4625 LD A,RERASE ; Send the erase request 4630 CALL SENDB 4635 POP DE ; Start of name in DE 4640 LD BC,#000A ; 10 bytes to send 4645 CALL SVBYTE ; Send the name 4650 4655 ; Now wait for the server to receive a return code 4660 4665 CALL SETRD ; Go to read mode 4670 CALL SIGRDY 4675 CALL READB ; Read the returncode 4680 AND A ; OK ? 4685 JP NZ,PRERR ; Else read the error from the server 4690 CALL SETWR ; Go to write mode 4695 CALL SIGRDY 4700 JP #0368 ; Close the channel 4705 4707 ;------------------------------------------------------------------; 4710 ; THE 'REPAIR ERROR STACK' SUBROUTINE' ; 4712 ; ; 4715 ; The stack is restored. ; 4717 ;------------------------------------------------------------------; 4720 4725 REPSTK POP DE ; DE := return address 4730 EX (SP),HL ; Destroy dummies 4735 POP HL 4740 EX (SP),HL 4745 POP HL 4750 EX (SP),HL ; Destroy PAGE_IN address 4755 POP HL 4760 EX (SP),HL ; Destroy error return address 4765 POP HL 4770 EX (SP),HL ; Restore 'old' ERRSP 4775 LD (ERRSP),HL 4780 POP HL ; HL := start of code 4785 EX DE,HL ; Pass to DE 4790 JP (HL) ; Return directly