.........1.........2.........3.........4.........5.........6.........7.........8



STREAMS AND CHANNELS

by Toni Baker

part 4 of 5, ZX Computing March 1987



If you thought that you couldn't use the ZX Printer

with your new 128 then Toni Baker can prove you wrong.



[Note that the code in STREAMS.TAP "PART4"]

[includes the code from "PART3".      JimG]





This article contains not one, but two new channels which you can use

on your Spectrum. The first is a channel which enables users of the

Spectrum 128 (or the 128+2) to be able to use the ZX Printer - or some

other compatible printer such as an Alphacom - even in 128K mode. If

you don't have a Spectrum 128 then you don't need this new channel of

course, because you can use the ZX Printer anyway.



The second is not really a new channel at all, but a modification to

an existing channel. It enables the Spectrum to be able to communicate

freely with a QL via the Local Area Network available from the ZX

Interface One. Of course communication between these computers via the

network is already possible, and David Nowotnik has recently been

doing an excellent series in ZX Computing on that very task. It is not

my intention to duplicate any of his material, but merely to remove a

couple of deficiencies in the channel itself. We'll return to this

later, meanwhile back to the ZX Printer.





Channel Z



The program as such begins at address B544. If this seems a rather

arbitrary address to you then I should explain that the reason it

starts at B544 is that all the code follows on from the stuff in last

month's article (which ran from B000 to B543). Indeed, some of the

subroutines developed last month will be used in the routines given here.



The trick to using the ZX Printer on the 128 is to avoid any

possibility of erasing or corrupting the OLD printer buffer (addresses

5B00 to 5BFF) - this is because the memory in this range is used by

the 128 to store various paging subroutines, system variables, and an

alternative machine stack for use when accessing the vast banks of

paged memory. If we can arrange things so that we can avoid corrupting

this memory then the good old ZX Printer can still work normally. You

see, all of the ZX Printer software in the ROM was written before the

advent of the 128 and is not compatible with the requirements of the

new machine.



All we really need to do then is to create a NEW printer buffer

somewhere in memory, and use the new buffer instead of the old one.

The best possible place for this buffer would be as part of the

channel information block for the channel - that would mean that the

area occupied by the new printer buffer would be reclaimed whenever

you closed the channel.



We shall call this new channel "Z", for ZX-Printer. Take a look at

Figure One - it shows the structure of the channel information block

for the new channel. As you can see, the first eleven bytes store

completely standard information, using the standard which was

developed throughout this series. (IX+05/06) contains the constant

1234h, which identifies this as being a user-defined channel. This

means that it may be opened or closed using some of the software

listed last month (IX+0B), (IX+0C) and (IX+0D) also store information

in the same format as the "W" channel given last month - this is so

that we can exploit more of last month's subroutines. As you shall

see, a collection of new channels is far more advantageous than a

single new channel.



Finally, the new printer buffer itself runs from (IX+0E) upwards, and

is 100h bytes in length (same as the old printer buffer). The variable

Z_XCOORD (IX+0C) will be zero if the printer buffer is completely

empty, or non-zero otherwise. If the new buffer is full it will

contain 20h. Z_WIDTH (IX+0D) remains constant at 20h - the width of

the buffer. This is so that the TAB and comma-control routines from

last month may be exploited successfully.



The program begins at the label Z_CLOSE, which is the routine to

perform all of the peripheral tasks necessary to close the channel. In

fact this merely consists of testing whether or not the buffer is

empty, and printing a newline if it isn't.



- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure 1



IX+00  (2 bytes)     Z_PRINT   Address of channel "Z" output

IX+02  (2 bytes)     REPORT_J  Address of channel "Z" input

IX+04  (1 byte)      Z_NAME    Name of channel (ie. "Z")

IX+05  (2 bytes)     Z_IDEN    Constant 1234h identifies new channel

IX+07  (2 bytes)     Z_CLOSE   Address of close buffer routine

IX+09  (2 bytes)     Z_LEN     Length of channel info (ie. 010E)

IX+0B  (1 byte)      Z_FLAGS   Various flags

        Bit 7: Set if leading space not reqd. for keywords, reset otherwise

        Bit 6: Not used

        Bit 5: Not used

        Bit 4: Not used

        Bit 3: Set if INVERSE status is ON, reset otherwise

        Bit 2: Set if OVER status is ON, reset otherwise

        Bit 1: Set if exactly two additional parameters reqd., reset otherwise

        Bit 0: Set if exactly one additional parameter reqd., reset otherwise

IX+0C  (1 byte)      Z_XCOORD  X coordinate of print position in buffer

IX+0D  (1 byte)      Z_WIDTH   Width of buffer (ie. 20h)

IX+0E  (100h bytes)  Z_BUFFER  New printer buffer

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -





Newline



The Z_NEWLINE routine may be compared to the ROM routine COPY_BUFF at

address 0ECD. Its purpose is to transfer the contents of the buffer to

the ZX Printer itself, before finally erasing the previous buffer

contents. The routine Z_EMPTY does the actual erasing. You will notice

a couple of differences between the ROM routine COPY_BUFF and my

routine Z_NEWLINE. Firstly, the new printer buffer is used instead of

the old one, and secondly the subroutine Z_COPY_LINE is called instead

of the original COPY_LINE.



In fact Z_COPY_LINE is itself very similar to the ROM's COPY_LINE

routine (at 0EFA). The only difference in evidence is the effect of

pressing the BREAK key - in my routine Z_EMPTY is called instead of

CLEAR_PRB. This removes any possible chance of corrupting the old

printer buffer area.



A general purpose subroutine is included next - SEARCH_CH_ALL. It is

entered with the A register containing the name of a channel. The

routine will search through the channel information area looking for a

channel with this name. If it finds one it will return with IX

pointing to the channel information block and the carry reset,

otherwise the carry will be set. We shall make use of this subroutine

later.





Z_PRINT



Z_PRINT is the routine which "prints" a character held in the A

register onto the new printer buffer. Note that the subroutine at

address B12F comes from last month's article and will sort out all

keywords, control parameters, and will deal with both the

comma-control and the TAB function. On return from this subroutine the

C register will contain any INVERSE or OVER parameters, or the x

coordinate of any AT parameters. The Z_PRINT routine itself is really

quite simple, bearing in mind that it has to deal with ASCII

characters, block graphics, and UDGs, as well as OVER, INVERSE, AT and

ENTER. Follow it through to see how all the various cases are dealt with.



Finally (for this channel) we have Z_OPEN, which opens the channel and

attaches it to a stream. On entry, the A register must contain the

stream number to which the channel is to be attached and the

subroutine will do the rest. It makes use of another subroutine from

last article - the OPEN_NEW routine which will open user defined channels.





Channel Q



When communicating with the QL via the normal network channel you will

notice a couple of problems. The first is that on the Spectrum the

code for "enter" is 0D (thirteen), whereas on the QL the code for

"enter" is 0A (ten). This means that, for instance, PRINT A$ on the QL

cannot be matched by INPUT A$ on the Spectrum, nor vice versa. It is

normally necessary to use PRINT A$;CHR$(13); on the QL in order that

the Spectrum may use INPUT A$. Conversely, INPUT A$ on the QL needs to

be matched by PRINT A$;CHR$ 10; on the Spectrum.



What I intend to do is to modify the "N" channel attached to a

particular stream so that the deficiencies are removed. In a modified

"N" channel these problems disappear. PRINT A$ on the Spectrum may be

matched by INPUT A$ on the QL, and vice versa. Other surprising

advantages turn up; if you LIST a Spectrum program over a modified

network channel then the QL will be able to LOAD the program with the

single command LOAD NET1_2 (assuming that the Spectrum is station

number 2) - or to transfer the program directly onto a QL microdrive

ready for loading later with the single QL command COPY NET1_2 TO

MDV1_PROG.



The second problem is a little more complex, but not much so. The

Spectrum command INPUT A$ actually expects to receive a string

expression, not the contents of an actual string (although this is not

the case with INPUT LINE). Fortunately for us, we seldom notice this

because the Spectrum provides the surrounding quotes - this converts

the string text into a string expression and everything is hunky dory

- with one exception. If the string printed over the network by the QL

contains a quotes character then the Spectrum will be unable to

evaluate the result as a string expression and will halt with error "C

Nonsense in BASIC". The cure to this problem is very simple. If,

during Spectrum INPUT (as opposed to INKEY$ or INPUT LINE) a quotes

character is received from a modified network channel THEN IT MUST BE

CONVERTED TO TWO CONSECUTIVE QUOTES CHARACTERS in order that the

expression will still evaluate.



Let's take a look at the subroutines now. We begin with SHADOW_DE

which will call a subroutine in the Shadow ROM whose address is DE. It

achieves this by fooling the Spectrum into thinking that we are

returning to the Shadow ROM from a Spectrum subroutine, and then

simply paging out the Shadow ROM on completion.



Q_PRINT is the routine which "prints" a character over the modified

network. As you can see it is extremely simple, merely testing for an

"enter" character and converting such to 0A, before using the original

Shadow-PRINT subroutine to print the character.



Q_INPUT is more complicated, though not much so. Firstly, the routine

tests the error return address to see whether the character input is

due to an INPUT command, or due to an INKEY$ function. If it turns out

to be merely INKEY$ then things are just as simple as for Q_PRINT. The

shadow input routine is called to read a character, and if that

character is 0A then it is converted to "enter".



If, however, we are dealing with INPUT (as opposed to INKEY$) then the

first thing we must do is abandon altogether use of the EDITOR routine

in the ROM (which we do by emptying the machine stack as far as the

return address from the EDITOR call) and then supplying a new routine

to replace EDITOR. We can call Q_INKEY$ as a sub- routine to fetch a

single character from the network (converting 0A to 0D) and the flags

will tell us if the character is valid or not. Then we can call a ROM

subroutine ADD_CHAR_1 to actually insert the character into the input

line. Quote characters must be inserted twice, not once (except during

INPUT LINE). Finally, if an "enter" character is received then we may

return, as if from the EDITOR routine, with the finished string

expression in the INPUT area of RAM.



MODIFY_N is the routine which will convert a "N" channel to do as it's

told. On entry the A register must contain the stream number of an

existing "N" channel which must have been opened normally. In such a

channel (IX+00/01) and (IX+02/03) will normally contain the value 0008

which, if called, will page in the Shadow ROM and then call the actual

output or input subroutine addressed by (IX+05/06) and (IX+07/08)

respectively.



The MODIFY_N subroutine will merely convert these 0008s to the

addresses of Q_PRINT and Q_INPUT, leaving the addresses of the Shadow

routines unchanged. The Shadow routines will then only be called when

required by Q_PRINT or by Q_INPUT.



Finally, a five byte demonstration of the "Z" channel. Simply calling

Z_DEMO from BASIC will open a new channel "Z", and attach it to stream

number four.





Channel R



In the next and final part of this series we shall examine the use of

the existing channel "R", which may only ever be used in machine code.

We shall also be creating a new channel, also called "R" (R for

RAM-disc) which will enable you to create READ and WRITE files in the

Spectrum 128's RAM-disc area, just as you can on the microdrives. Till

then, keep smiling, and don't take anything seriously.



- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

B544 DD7E0C   Z_CLOSE   LD   A,(Z_XCOORD)  ;A= x coordinate within buffer

B547 A7                 AND  A

B548 C8                 RET  Z             ;Return immediately if buffer empty



B549 DDE5     Z_NEWLINE PUSH IX

B54B E1                 POP  HL            ;HL= address of channel info

B54C 010E00             LD   BC,#000E

B54F 09                 ADD  HL,BC         ;HL points to new printer buffer

B550 F3                 DI                 ;Disable interrupts

B551 0608               LD   B,#08

B553 C5       Z_NL_LOOP PUSH BC

B554 CD73B5             CALL ZCOPYLINE     ;Output next row to ZX Printer

B557 C1                 POP  BC

B558 10F9               DJNZ Z_NL_LOOP     ;Output all eight rows

B55A 3E04               LD   A,#04

B55C D3FB               OUT  (#FB),A       ;Switch off printer motor

B55E FB                 EI                 ;Enable interrupts



B55F DDE5     Z_EMPTY   PUSH IX

B561 E1                 POP  HL            ;HL= address of channel info

B562 010E00             LD   BC,#000E

B565 09                 ADD  HL,BC         ;HL points to new printer buffer

B566 AF                 XOR  A             ;A= 00

B567 77       ZEMPTLOOP LD   (HL),A        ;Zero next byte of buffer

B568 23                 INC  HL            ;HL points to next byte in buffer

B569 10FC               DJNZ ZEMPTLOOP     ;Zero entire buffer

B56B DDCB0BFE           SET  7,(Z_FLAGS)   ;Signal "Leading space not required"

B56F DD770C             LD   (Z_XCOORD),A  ;Reset x coordinate in buffer

B572 C9                 RET                ;Return



B573 78       ZCOPYLINE LD   A,B           ;A= row number to output

B574 FE03               CP   #03

B576 9F                 SBC  A,A           ;A= FF (last 2 rows); 00 (otherwise)

B577 E602               AND  #02           ;A= 02 (last 2 rows); 00 (otherwise)

B579 D3FB               OUT  (#FB),A       ;Switch on printer motor, but with

                                           ;slow speed for last two rows

B57B 57                 LD   D,A           ;D= "last 2 rows" flag

B57C CD541F   Z_CL_LOOP CALL BREAK_KEY     ;Test BREAK key

B57F 380A               JR   C,Z_CL_NEXT   ;Jump forward unless BREAK pressed

B581 3E04               LD   A,#04

B583 D3FB               OUT  (#FB),A       ;Switch off printer motor

B585 FB                 EI                 ;Enable interrupts

B586 CD5FB5             CALL Z_EMPTY       ;Empty the new printer buffer

B589 CF                 RST  #08           ;Report "D, BREAK - CONT repeats"

B58A 0C                 DEFB #0C

B58B DBFE     Z_CL_NEXT IN   A,(#FE)       ;A= status of printer

B58D 87                 ADD  A,A

B58E F8                 RET  M             ;Return if printer not connected

B58F 30EB               JR   NC,Z_CL_LOOP  ;Wait until printer stylus is ready

B591 C3120F             JP   COPY_L_2A     Jump to print next row from buffer



B594 DD2A4F5C SEARCHALL LD   IX,(CHANS)    ;IX points to base of chan info area

B598 011400             LD   BC,#0014

B59B 57                 LD   D,A           ;D= name of channel to search for

B59C DD09     SCHCHLOOP ADD  IX,BC         ;IX points to next chan info area

B59E DD7E00   SEARCH_CH LD   A,(IX+#00)

B5A1 FE80               CP   #80

B5A3 37                 SCF

B5A4 C8                 RET  Z             ;Return with Carry set if channel

                                           ;not found

B5A5 DD7E04             LD   A,(IX+#04)    ;A= name of channel pointed to

B5A8 BA                 CP   D

B5A9 C8                 RET  Z             ;Return with IX pointing to channel

                                           ;information area, and Carry reset,

                                           ;if search successful

B5AA DD4E09   SEARCHNXT LD   C,(IX+#09)

B5AD DD460A             LD   B,(IX+#0A)    ;BC= length of channel info

B5B0 18EA               JR   SCHCHLOOP     ;Loop back to continue search



B5B2 DD2A515C Z_PRINT   LD   IX,(CURCHL)   ;IX points to channel info area

B5B6 CD2FB1             CALL CHR_TYPE2     ;Deal with keywords, control params,

                                           ;TAB and comma control

B5B9 F8                 RET  M             ;Return if tasks complete

B5BA F5                 PUSH AF

B5BB 283B               JR   Z,ZGRAPHICS   ;Jump with graphics characters

B5BD 3033               JR   NC,Z_ASCII    ;Jump with ASCII characters

B5BF FE0D               CP   #0D

B5C1 2005               JR   NZ,Z_PRCTRLS  ;Jump unless char is "enter"

B5C3 CD49B5             CALL Z_NEWLINE     ;Print a newline

B5C6 187F               JR   Z_EXIT        ;and jump to exit

B5C8 D614     Z_PRCTRLS SUB  #14

B5CA 387B               JR   C,Z_EXIT      ;Jump with controls 00 to 13

B5CC 0608               LD   B,#08         ;B has bit 3 set

B5CE 2805               JR   Z,Z_INVOVER   ;Jump with "INVERSE control"

B5D0 3D                 DEC  A

B5D1 0604               LD   B,#04         ;B has bit 2 set

B5D3 200F               JR   NZ,Z_AT       ;Jump unless char is "OVER control"

B5D5 CB19     Z_INVOVER RR   C

B5D7 9F                 SBC  A,A           ;A= 00 (INVERSE 0 or OVER 0)

                                           ;or FF (INVERSE 1 or OVER 1)

B5D8 DDAE0B             XOR  (Z_FLAGS)     ;A= flags byte, but complemented if

                                           ;parameter is one

B5DB A0                 AND  B             ;A all bits reset, except that bit

                                           ;3 (INVERSE) or bit 2 (OVER) will be

                                           ;taken from flags byte, and

                                           ;complemented if parameter is one

B5DC DDAE0B             XOR  (Z_FLAGS)

B5DF DD770B             LD   (Z_FLAGS),A   ;Bit 3 (INVERSE) or bit 2 (OVER) of

                                           ;flags byte will be assigned with

                                           ;parameter

B5E2 1863               JR   Z_EXIT        ;Jump to exit

B5E4 3D       Z_AT      DEC  A

B5E5 2060               JR   NZ,Z_EXIT     ;Jump unless control is "AT control"

B5E7 79                 LD   A,C           ;A= required x coordinate

B5E8 FE20               CP   #20

B5EA D29F1E             JP   NC,REPORT_B   ;Error if out of range

B5ED DD770C             LD   (Z_XCOORD),A  ;Assign x coordinate as required

B5F0 1855               JR   Z_EXIT        ;Jump to exit

B5F2 ED5B365C Z_ASCII   LD   DE,(CHARS)    ;DE= addr of character set - 100h

B5F6 1811               JR   Z_CHR_1       ;Jump forward

B5F8 D690     ZGRAPHICS SUB  #90

B5FA 3009               JR   NC,Z_UDG      ;Jump with user-defined graphics

B5FC 47                 LD   B,A

B5FD CD380B             CALL PO_GR_1       ;Construct graphic in MEMBOT area

B600 21925C             LD   HL,MEMBOT     ;HL points to graphic form

B603 180B               JR   Z_CHR_2       ;Jump forward

B605 ED5B7B5C Z_UDG     LD   DE,(UDG)      ;DE= address of user-defined graphics

B609 6F       Z_CHR_1   LD   L,A

B60A 2600               LD   H,#00

B60C 29                 ADD  HL,HL

B60D 29                 ADD  HL,HL

B60E 29                 ADD  HL,HL

B60F 19                 ADD  HL,DE         ;HL points to required graphic form

B610 DD7E0C   Z_CHR_2   LD   A,(Z_XCOORD)  ;A= current x coordinate in buffer

B613 FE20               CP   #20

B615 E5                 PUSH HL

B616 CC49B5             CALL Z,Z_NEWLINE   ;Print a newline if buffer full

B619 D1                 POP  DE            ;DE points to graphic form

B61A DD7E0C             LD   A,(Z_XCOORD)  ;A= current x coordinate in buffer

B61D 3C                 INC  A             ;A= new x coordinate

B61E DD770C             LD   (Z_XCOORD),A  ;Store in system variable

B621 C60D               ADD  A,#0D

B623 4F                 LD   C,A

B624 0600               LD   B,#00         ;BC= displacement to posn in buffer

B626 DDE5               PUSH IX

B628 E1                 POP  HL            ;HL points to channel information

B629 09                 ADD  HL,BC         ;HL points to current posn in buffer

B62A 0608               LD   B,#08

B62C EB       Z_CHRLOOP EX   DE,HL         ;DE= current buffer position

                                           ;HL points to graphic form

B62D 1A                 LD   A,(DE)        ;A= byte from buffer

B62E DDCB0B56           BIT  2,(Z_FLAGS)

B632 2001               JR   NZ,Z_CHROVER  ;Jump if OVER off

B634 AF                 XOR  A

B635 DDCB0B5E Z_CHROVER BIT  3,(Z_FLAGS)

B639 2801               JR   Z,Z_CHR_INV   ;Jump if INVERSE on

B63B 2F                 CPL

B63C AE       Z_CHR_INV XOR  (HL)          ;A= byte from graphic form, with

                                           ;OVER and INVERSE taken into account

B63D 12                 LD   (DE),A        ;Store in buffer

B63E E5                 PUSH HL            ;Stack pointer into graphic form

B63F 212000             LD   HL,#0020

B642 19                 ADD  HL,DE         ;HL points to appropriate byte in

                                           ;buffer for next row of character

B643 D1                 POP  DE            ;DE points into graphic form

B644 13                 INC  DE            ;DE points to next byte to use

B645 10E5               DJNZ Z_CHRLOOP     ;Print whole character into buffer

B647 F1       Z_EXIT    POP  AF            ;A= character just printed

B648 A7                 AND  A             ;Reset the Carry flag

B649 C9                 RET                ;Return



B64A 08       Z_OPEN    EX   AF,AF'        ;A'= stream number to attach channel

B64B 3E5A               LD   A,"Z"

B64D CD94B5             CALL SEARCHALL     ;Search for another "Z" channel

B650 D2C415             JP   NC,REPORT_J   ;Error if one exists

B653 DD2144B5           LD   IX,Z_CLOSE    ;IX= close buffer address

B657 21B2B5             LD   HL,Z_PRINT    ;HL= output address

B65A 11C415             LD   DE,REPORT_J   ;DE= input address

B65D 010E01             LD   BC,#010E      ;BC= length of channel info

B660 CD6DB0             CALL OPEN_NEW      ;Open the channel

B663 DD360B00           LD   (Z_FLAGS),#00 ;Reset the flags

B667 DD360D20           LD   (Z_WIDTH),#20 ;Specify buffer width = 20h chars

B66B C35FB5             JP   Z_EMPTY       ;Empty the buffer, and return



B66E 210007   SHADOW_DE LD   HL,UNPAGE

B671 E5                 PUSH HL            ;Stack UNPAGE address in Shadow ROM

B672 D5                 PUSH DE            ;Stack Shadow subroutine address

B673 65                 LD   H,L           ;HL= 0000

B674 E5                 PUSH HL            ;Stack 0000 signalling "Return to

                                           ;Shadow ROM address"

B675 C30800             JP   #0008         ;Jump to call Shadow subroutine



B678 DD2A515C Q_PRINT   LD   IX,(CURCHL)   ;IX points to channel information

B67C F5                 PUSH AF            ;Stack character to print

B67D FE0D               CP   "enter"

B67F 2002               JR   NZ,Q_PRINT_2  ;Jump unless character is "enter"

B681 3E0A               LD   A,#0A         ;A= QL's code for "enter"

B683 DD5E05   Q_PRINT_2 LD   E,(IX+#05)

B686 DD5606             LD   D,(IX+#06)    ;DE= Shadow output address

B689 CD6EB6             CALL SHADOW_DE     ;Call network output in Shadow ROM

B68C F1                 POP  AF            ;A= character just printed

B68D A7                 AND  A             ;Reset the Carry flag

B68E C9                 RET                ;Return



B68F DD2A515C Q_INPUT   LD   IX,(CURCHL)   ;IX points to channel information

B693 2A3D5C             LD   HL,(ERR_SP)   ;HL points to error return address

B696 5E                 LD   E,(HL)

B697 23                 INC  HL

B698 56                 LD   D,(HL)        ;DE= error return address

B699 217F10             LD   HL,ED_ERROR

B69C A7                 AND  A

B69D ED52               SBC  HL,DE

B69F 2814               JR   Z,Q_INPUT_2   ;Jump if in an INPUT channel

B6A1 DD5E07   Q_INKEY$  LD   E,(IX+#07)

B6A4 DD5608             LD   D,(IX+#08)    ;DE= Shadow input address

B6A7 CD6EB6             CALL SHADOW_DE     ;Call network input from Shadow ROM

B6AA 47                 LD   B,A           ;B= character just input

B6AB F5                 PUSH AF            ;Stack the flags

B6AC FE0A               CP   #0A

B6AE 2002               JR   NZ,Q_INKEY$2  ;Jump unless this is a QL "enter"

B6B0 060D               LD   B,#0D         ;B= Spectrum "enter" character

B6B2 F1       Q_INKEY$2 POP  AF            ;Restore the flags

B6B3 78                 LD   A,B           ;A= character to return

B6B4 C9                 RET                ;Return

B6B5 ED7B3D5C Q_INPUT_2 LD   SP,(ERR_SP)   ;Empty the machine stack down as

                                           ;far as ED_ERROR

B6B9 E1                 POP  HL            ;Drop the address ED_ERROR

B6BA E1                 POP  HL            ;HL= normal error return address ptr

B6BB 223D5C             LD   (ERR_SP),HL   ;Restore pointer to error return addr

B6BE CDA1B6   Q_INPLOOP CALL Q_INKEY$      ;Read next character from network

B6C1 3804               JR   C,QINPSTORE   ;Jump if character OK

B6C3 28F9               JR   Z,Q_INPLOOP   ;Jump if still waiting

B6C5 CF                 RST  #08           ;Report "8, End of file"

B6C6 07                 DEFB #07

B6C7 FE0D     QINPSTORE CP   #0D

B6C9 C8                 RET  Z             ;Return (INPUT is now finished) if

                                           ;character is "enter"

B6CA CD850F             CALL ADD_CHAR_1    ;Store the char in the INPUT line

B6CD FDCB377E           BIT  7,(FLAG_X)

B6D1 20EB               JR   NZ,QINPLOOP   ;Loop back if doing INPUT LINE

B6D3 FE22               CP   #22

B6D5 CC850F             CALL Z,ADD_CHAR_1  ;Double-up quote characters

B6D8 18E4               JR   Q_INPLOOP     ;Go back for rest of INPUT



B6DA CD2117   MODIFY_N  CALL STR_DATA1     ;BC= STRMS data for stream supplied

B6DD 78                 LD   A,B

B6DE B1                 OR   C

B6DF 280C               JR   Z,MOD_ERROR   ;Error if stream not in use

B6E1 2A4F5C             LD   HL,(CHANS)    ;HL points to base of chan info area

B6E4 09                 ADD  HL,BC         ;HL points to 2nd byte of chan info

B6E5 23                 INC  HL

B6E6 23                 INC  HL

B6E7 23                 INC  HL            ;HL points to 5th byte of chan info

B6E8 7E                 LD   A,(HL)        ;A= name of channel

B6E9 FE4E               CP   "N"

B6EB 2802               JR   Z,MOD_QL      ;Jump only if channel is "N"

B6ED CF       MOD_ERROR RST  #08           ;Report "O, Invalid stream"

B6EE 17                 DEFB #17

B6EF 2B       MOD_QL    DEC  HL

B6F0 36B6               LD   (HL),#B6

B6F2 2B                 DEC  HL

B6F3 368F               LD   (HL),#8F      ;Store new input address

B6F5 2B                 DEC  HL

B6F6 36B6               LD   (HL),#B6

B6F8 2B                 DEC  HL

B6F9 3678               LD   (HL),#78      ;Store new output address

B6FB C9                 RET



;[This last bit of code gets overwritten by the code in Part 5.  JimG]



B6FC 3E04     Z_DEMO    LD   A,#04         ;A= stream number to attach

B6FE C34AB6             JP   Z_OPEN

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

