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

STREAMS AND CHANNELS
by Toni Baker
part 2 of 5, ZX Computing January 1987

Toni Baker sheds light on windows for all Spectrums.


In the last part we discussed what streams and channels were and what
we could do with them. Also we introduced the concept of "user defined
channels" - that is - new channels created in machine code to do
various tasks not possible in any other way. Last month's episode
included a program to CLOSE a user defined channel. Apologies are due
here as unfortunately it was riddled with bugs, so I've listed a new
version in this episode. You might like to play "Spot the bugs" by
comparing last month's listing to this month's - see how many you can
find! The included version works, however, as does the rest of the
program.


Windows

This article consists of a new user defined channel called a WINDOW.
The concept of a window is very simple. Users of the QL will already
be familiar with them. A window is a rectangular region of the screen
which may be treated as if it were a whole screen - if you print text
to a window it will only appear within the confines of this rectangle,
and once the window gets completely full it will scroll independently
of the rest of the screen. It is also possible to clear a window, in
any colour scheme, without clearing the rest of the screen.

The program given will in fact cater for two different types of
window, which I have called "Fast" and "Slow" windows (although in
practice there seems to be no noticeable difference in speed between
the two). A "Fast" window will do exactly what is described above, and
no more. The standard character set is used (although this may be
altered by changing the system variable (CHARS) at address 5C36) and
all characters are eight pixels wide.

A "Slow" window, however, has two important extras. The first is
left-justification. Put simply this means that words are treated as
whole chunks of characters and will not be split up - thus if a word
is too long to fit at the end of one line then it will be printed as a
whole at the start of the next line. This is achieved by cleverly
storing all of the characters in a buffer until the end of the word is
reached, and then deciding whether or not it will fit on the line.
This means that when you use a Slow window channel you never have to
worry about spacing the words out to fit on the line - the channel
does that all by itself.

The second improvement is that you are not restricted to eight bit
wide characters - you can use seven bit wide; six bit wide; four bit
wide even if you like. This means that you can fit more characters in
each line than would normally be possible.

When a window becomes filled then it will scroll. Normally it will
pause at this point, but it is possible to open a window which has the
scroll pause disabled - in such a case the window will scroll
automatically each time without pausing to wait for you. While a
window is paused the screen will appear to freeze. You may then press
either SPACE (which continues with the scroll) or BREAK (which will
break out giving report message "D BREAK - CONT repeats"). Even if the
scroll pause is disabled it will still be possible to break out at the
point of scrolling by pressing BREAK.

The Spectrum, unlike the QL, does not come equipped with the statement
CLS #N (which clears a window), so I have had to make special
provision to ensure that a window may be cleared easily from BASIC. I
have used CHR$ 0 as a clear-window control, thus to clear a window
attached to stream four it would merely be necessary to use the
command: PRINT #4;CHR$ 0; and the job will be done. You can put other
print items on the same line at course. All of the normal controls are
allowed, including AT and TAB, all of the colour controls (PAPER, INK,
FLASH and BRIGHT) as well as INVERSE. OVER is not incorporated,
however, when used with SLOW windows. OVER 1 will switch to printing
with double-height characters, while OVER 0 will revert to printing
with single- height characters.

As was stated in the last article, all of our new user defined
channels will be identified by the fact that IX+05 will contain the
two-byte value 1234h, where IX points to the channel information
block. Figure one lists the channel information used by a window
channel. Note that if the window is a Fast window then W_SCROLLS
(IX+15) will be the last variable - IX+16 and beyond will not exist.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Figure 1

IX+00  W_OUT      Address of WINDOW output routine (=B4F1)
IX+02  W_IN       Address of WINDOW output routine (=15C4, REPORT_J)
IX+04  W_CHNAME   Name of channel (="W")
IX+05  W_IDEN     New channel identifier (=1234)
IX+07  W_CLOSE    Address of empty buffer routine
                  (=B4D6 for Slow windows or 0052 for Fast windows)
IX+09  W_CHLEN    Length of channel information block
IX+0B  W_FLAGS    Various flags, defined as follows:
       Bit 7:    Reset if leading space required, set otherwise
       Bit 6:    Not used
       Bit 5:    Set if scroll pause enabled, reset otherwise
       Bit 4:    Set for Slow window, reset for Fast window
       Bit 3:    Set for INVERSE 1, reset for INVERSE 0
       Bit 2:    Set if using double height, reset otherwise
       Bits 1,0: Number of control parameters required
IX+0C  W_XCOORD  Current x coordinate of print position
IX+0D  W_WIDTH   Number of characters per line
IX+0E  W_YCOORD  Current y coordinate of print position
IX+0F  W_HEIGHT  Height of window, in squares
IX+10  W_PRPOS   Address within screen of current print position
IX+12  W_HOME    Address within screen of top left-hand corner of window
IX+14  W_ATTR    Colours currently being used for window
IX+15  W_SCROLLS Counts number of scrolls allowed before scroll pause, +1
IX+16  W_PIX     Position within square of current print position
IX+17  W_CH_WID  Width of characters, in pixels
IX+18  W_CHARS   Address of character set (20-7F) minus 100h
IX+1A  W_UDG     Address of character set (80-A4)
IX+1C  W_WIDTH_8 Width of window, in squares
IX+1D  W_LEN     Number of characters stored in buffer
IX+1E  W_BUFFER  The buffer itself
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


UDGs

The program commences with a few routines suitable for all user
defined channels, not just windows. At address B000 the routine
CLOSE_NEW will close the new channel which is attached to stream A. At
address B061 is a routine which will close all user defined channels,
but any data stored in buffers will be lost if this routine is used.
At address B06D the routine OPEN_NEW will open any new user defined
channel and attach it to a stream. To use this sub-routine the
registers must be pre-assigned with the required values, as specified
in the notes above the routine.

Then there are a couple of routines which will work with most user
defined channels, though not necessarily all of them. CHR_TYPE
(address B0B7) will expand keywords, and will count incoming control
parameters. It will also set or reset the "Leading Space" bit (see
Figure One) according to whether or not the character is a space. If,
on return from this sub- routine the sign flag is set, it means that
there is no more work to be done for this character. CHR_TYPE_2
(address B12F) will also deal with the comma control and the TAB
function, and in a similar manner will also return with the sign flag
set if work on the character is finished.

And then we come to the program itself. The program will work both on
16K/48K Spectrums, and on the Spectrum 128, in either mode - but when
the program is run on a Spectrum 128 in 128K mode then it will be
possible to define a window either on screen zero (the normal screen)
or screen one. Note that the Spectrum 128 has no built-in software
which may print onto screen one, so this program will make up for one
of the few flaws in the new machine. Users of the Spectrum 128 must
ensure that the machine stack resides below address BFFF otherwise the
program will crash.

To open a window onto screen one it is only necessary to set bit 7 of
the B register (the co-ordinate of the window) when opening the
channel.

Here's how you open a window channel. Firstly you must assign the
registers as defined in the notes above OPEN_WINDOW (address B212),
and then all you have to do is call the sub-routine OPEN_WINDOW
itself. Voila, the job will be done, and you can use the channel in
BASIC just as easy (if not easier) as you can in machine code.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Listing 1

To close a new channel. On entry the A register must contain the stream number
to be closed. If called from the label CLOSE_CLR then the Carry flag must be
set if data in buffers is to be sent, or reset if such data is to be lost.

B000 37       CLOSE_NEW SCF                ;Signal "Data in buffer to be sent"
B001 F5       CLOSE_CLR PUSH AF            ;Stack stream number
B002 08                 EX   AF,AF'        ;F' stores the Carry flag
B003 F1                 POP  AF            ;A= stream number to close
B004 CD2117             CALL STR_DATA_A    ;BC= stream data for given stream;
                                           ;HL points to appropriate STRMS var
B007 E5                 PUSH HL            ;Stack pointer to STRMS variable
B008 21EBFF             LD   HL,#FFEB      ;HL= -15h
B00B 09                 ADD  HL,BC
B00C E1                 POP  HL            ;HL points to STRMS variable
B00D D0                 RET  NC            ;Return with channels "K", "S", "R"
                                           ;and "P", and also with streams which
                                           ;are already  closed
B00E DD2A4F5C           LD   IX,(CHANS)    ;IX points to channel info area
B012 DD09               ADD  IX,BC
B014 DD2B               DEC  IX            ;IX points to channel information
                                           ;block for the given channel
B016 DD7E05             LD   A,(IX+#05)
B019 FE34               CP   #34
B01B C0                 RET  NZ
B01C DD7E06             LD   A,(IX+#06)
B01F FE12               CP   #12
B021 C0                 RET  NZ            ;Return if this is not one of our new
                                           ;user-defined channels
B022 3600               LD   (HL),#00
B024 23                 INC  HL
B025 3600               LD   (HL),#00      ;Reset the STRMS variable, thus
                                           ;closing the stream
B027 C5                 PUSH BC            ;Stack displacement into channel
                                           ;information area
B028 DD6E07             LD   L,(IX+#07)
B02B DD6608             LD   H,(IX+#08)    ;HL= address of "Send data in buffer"
                                           ;subroutine
B02E 08                 EX   AF,AF'        ;Retrieve Carry flag
B02F DC2C16             CALL C,CALL_JUMP   ;Send data if required
B032 DDE5               PUSH IX
B034 E1                 POP  HL            ;HL points to channel info block
B035 DD4E09             LD   C,(IX+#09)
B038 DD460A             LD   B,(IX+#0A)    ;BC= length of channel info block
B03B C5                 PUSH BC
B03C CDE819             CALL RECLAIM_2     ;Reclaim the memory used by block
B03F C1                 POP  BC
B040 3E10               LD   A,#10         ;A= number of streams to consider
B042 21165C             LD   HL,STRMS_00   ;HL points to stream zero variable
B045 5E       CLOSELOOP LD   E,(HL)
B046 23                 INC  HL
B047 56                 LD   D,(HL)        ;DE= stream variable for next stream
B048 E3                 EX   (SP),HL       ;HL= stream variable for stream
                                           ;just closed
B049 A7                 AND  A
B04A ED52               SBC  HL,DE
B04C 19                 ADD  HL,DE
B04D 300B               JR   NC,CLOSENEXT  ;Jump unless the channel information
                                           ;block for this stream has moved
B04F EB                 EX   DE,HL
B050 A7                 AND  A
B051 ED42               SBC  HL,BC
B053 EB                 EX   DE,HL         ;DE= updated stream variable for
                                           ;this stream
B054 E3                 EX   (SP),HL       ;HL points to 2nd byte of STRMS var
B055 2B                 DEC  HL            ;HL points to 1st byte of STRMS var
B056 73                 LD   (HL),E
B057 23                 INC  HL
B058 72                 LD   (HL),D        ;Store new value of streams variable
B059 E3                 EX   (SP),HL
B05A E3       CLOSENEXT EX   (SP),HL       ;HL points to 2nd byte of STRMS var
B05B 23                 INC  HL            ;HL points to next STRMS variable
B05C 3D                 DEC  A
B05D 20E6               JR   NZ,CLOSELOOP  ;Loop back to consider remaining
                                           ;streams
B05F F1                 POP  AF            ;Balance the stack
B060 C9                 RET                ;Return

To clear all new channels. Any data held in buffers will be lost, and the memory
used by all of the new user-defined channels will be reclaimed.

B061 3E10     CLEAR_NEW LD   A,#10         ;A= number of streams to consider
B063 3D       CLEARLOOP DEC  A             ;A= stream number of next stream
B064 F5                 PUSH AF
B065 A7                 AND  A             ;Signal "Data in buffers to be lost"
B066 CD01B0             CALL CLOSE_CLR     ;Clear the channel associated with
                                           ;this stream
B069 F1                 POP  AF
B06A 20F7               JR   NZ,CLEARLOOP  ;Loop back until all streams cleared
B06C C9                 RET                ;Return

To open a new channel. On entry A' must contain the designated stream number;
A must contain the name of the channel (an ASCII character code); BC must
contain the length of the required channel information block; DE must contain
the input address; HL must contain the output address; and IX must contain the
address of a subroutine to send any data stored in buffers (or 0052h if this
does not apply).

B06D C5       OPEN_NEW  PUSH BC            ;Stack length of channel info block
B06E DDE5               PUSH IX            ;Stack close buffer address
B070 F5                 PUSH AF            ;Stack name of channel
B071 D5                 PUSH DE            ;Stack input address
B072 E5                 PUSH HL            ;Stack output address
B073 C5                 PUSH BC            ;Stack length of info block (again)
B074 08                 EX   AF,AF'        ;A= stream number to attach
B075 CD2117             CALL STR_DATA_A    ;BC= stream data for given stream
B078 78                 LD   A,B
B079 B1                 OR   C
;[This next command was missed out of the listing in part 2.]
;[A correction wasn't printed until part 5.             JimG]
B07A C1                 POP  BC
B07B 2802               JR   Z,OPEN_NEW2   ;Jump unless stream already open
B07D CF                 RST  #08           ;"O Invalid stream" error report
B07E 17                 DEFB #17
B07F E5       OPEN_NEW2 PUSH HL            ;Stack pointer to stream variable
B080 2A535C             LD   HL,(PROG)
B083 2B                 DEC  HL            ;HL points to 80h byte at end of
                                           ;channel information area
B084 CD5516             CALL MAKE_ROOM     ;Make room for new channel info
B087 23                 INC  HL            ;HL points to new channel info block
B088 22515C             LD   (CURCHL),HL   ;Make this the current channel
B08B E5                 PUSH HL
B08C DDE1               POP  IX            ;IX points to channel info block
B08E 23                 INC  HL            ;HL points to second byte of info
B08F ED4B4F5C           LD   BC,(CHANS)    ;BC points to start of CHANS area
B093 A7                 AND  A
B094 ED42               SBC  HL,BC         ;HL= required stream value
B096 EB                 EX   DE,HL         ;DE= required stream value
B097 E1                 POP  HL            ;HL points to stream variable
B098 73                 LD   (HL),E
B099 23                 INC  HL
B09A 72                 LD   (HL),D        ;Assign stream variable with required
                                           ;value, thus opening the stream
B09B DDE5               PUSH IX
B09D E1                 POP  HL            ;HL points to channel info block
B09E D1                 POP  DE            ;DE= output address
B09F C1                 POP  BC            ;BC= input address
B0A0 CDAEB0             CALL OPENSTORE     ;Store these addresses in info block
B0A3 F1                 POP  AF            ;A= name of channel
B0A4 77                 LD   (HL),A        ;Store name of channel
B0A5 23                 INC  HL
B0A6 3634               LD   (HL),#34
B0A8 23                 INC  HL
B0A9 3612               LD   (HL),#12      ;Signal "User-defined channel"
B0AB 23                 INC  HL
B0AC D1                 POP  DE            ;DE= close buffer address
B0AD C1                 POP  BC            ;BC= length of channel info block
B0AE 73       OPENSTORE LD   (HL),E
B0AF 23                 INC  HL
B0B0 72                 LD   (HL),D        ;Store output or close address
B0B1 23                 INC  HL
B0B2 71                 LD   (HL),C
B0B3 23                 INC  HL
B0B4 70                 LD   (HL),B        ;Store input address or block length
B0B5 23                 INC  HL
B0B6 C9                 RET                ;Return

To deal with parameters of control codes, and keyword tokens. This subroutine
may be used with any user-defined channel provided that (IX+0B) contains flags.
Bits 1 and 0 count incoming parameters for use with colour controls, AT and TAB;
while bit 7 is set if a space has just been printed to the channel. On return
the Sign flag will be set if no more needs to be done, otherwise the Zero flag
will be set if the character is a graphics character, and the Carry flag will be
set for block graphics and control codes.

B0B7 4F       CHR_TYPE  LD   C,A           ;C= character to "print"
B0B8 DD7E0B             LD   A,(IX+#0B)    ;A contains various flags
B0BB 57                 LD   D,A           ;D contains various flags
B0BC E603               AND  #03           ;A= number of parameters expected
B0BE 2816               JR   Z,CTYP_GO     ;Jump if no parameters expected
B0C0 15                 DEC  D
B0C1 7A                 LD   A,D
B0C2 DD770B             LD   (IX+#0B),A    ;Decrement parameter count
B0C5 E603               AND  #03
B0C7 2805               JR   Z,CTYP_LAST   ;Jump if this is the last parameter
B0C9 FD71D5             LD   (TVDATA)hi,C  ;Store this (middle) parameter
B0CC 185E               JR   CTYP_DONE     ;Jump to exit
B0CE 3A0E5C   CTYP_LAST LD   A,(TVDATA)lo  ;A= original control code
B0D1 FD46D5             LD   B,(TVDATA)hi  ;B= middle parameter (if one exists)
B0D4 1847               JR   CTYP_CTRX     ;Jump to exit
B0D6 213B5C   CTYP_GO   LD   HL,FLAGS      ;HL points to system flags
B0D9 1EA3               LD   E,"SPECTRUM"  ;E= the 1st Spectrum 128K keyword
B0DB CB66               BIT  4,(HL)
B0DD 2002               JR   NZ,CTYP_MODE  ;Jump if in 128K mode
B0DF 1EA5               LD   E,"RND"       ;E= the 1st Spectrum 16K/48K keyword
B0E1 79       CTYP_MODE LD   A,C           ;A= character to print
B0E2 BB                 CP   E
B0E3 380F               JR   C,CTYP_NTOK   ;Jump unless this is a keyword
B0E5 DD7E0B             LD   A,(IX+#0B)    ;A contains various flags
B0E8 07                 RLCA               ;Bit 0 = leading space bit
B0E9 AE                 XOR  (HL)
B0EA E601               AND  #01
B0EC AE                 XOR  (HL)
B0ED 77                 LD   (HL),A        ;Assign leading space bit as reqd
B0EE 79                 LD   A,C           ;A= character to print
B0EF CD520B             CALL P_T&UDG       ;Expand this keyword
B0F2 1838               JR   CTYP_DONE     ;Jump to exit
B0F4 FE80     CTYP_NTOK CP   #80
B0F6 380D               JR   C,CTYP_NGRA   ;Jump unless this is a graphics char
B0F8 DDCB0BBE           RES  7,(IX+#0B)    ;Signal "Leading space will be reqd"
B0FC FE90               CP   #90
B0FE 3003               JR   NC,CTYP_UDG   ;Jump if this is a UDG
B100 BF                 CP   A             ;Set the Zero flag
B101 37                 SCF                ;Set the Carry flag
B102 C9                 RET                ;Return
B103 BF       CTYP_UDG  CP   A             ;Set Zero flag; reset Carry flag
B104 C9                 RET                ;Return
B105 FE20     CTYP_NGRA CP   #20
B107 380C               JR   C,CTYP_CTRL   ;Jump with control characters
B109 DDCB0BBE           RES  7,(IX+#0B)    ;Signal "Leading space will be reqd"
B10D 2004               JR   NZ,CTYP_SPCE  ;Jump unless character is SPACE
B10F DDCB0BFE           SET  7,(IX+#0B)    ;Signal "Leading space not reqd"
B113 A7       CTYP_SPCE AND  A             ;Reset Zero flag; reset Carry flag
B114 C9                 RET                ;Return
B115 FE10     CTYP_CTRL CP   #10
B117 3804               JR   C,CTYP_CTRX   ;Jump to exit with codes 00 to 0F
B119 FE18               CP   #18
B11B 3803               JR   C,CTYP_PARA   ;Jump with codes 10 to 18
B11D FEFF     CTYP_CTRX CP   #FF           ;Reset Zero flag; set Carry flag
B11F C9                 RET                ;Return
B120 320E5C   CTYP_PARA LD   (TVDATA)lo,A  ;Store control code
B123 14                 INC  D             ;Signal "One parameter required"
B124 FE16               CP   #16
B126 3801               JR   C,CTYP_SET    ;Jump with codes 10 to 16
B128 14                 INC  D             ;Signal "Two parameters required"
B129 DD720B   CTYP_SET  LD   (IX+#0B),D    ;Store number of reqd parameters
B12C F6FF     CTYP_DONE OR   #FF           ;Set the Sign flag
B12E C9                 RET                ;Return

To deal with the comma control, and the TAB function as well. This subroutine
may be used with any user-defined channel, provided that (IX+0B) contains flags
as above; that (IX+0C) contains the x coordinate (or column number) of the
current print position; and that (IX+0D) contains the width of the line (or
total number of columns allowed). Flags on return are as above, except that the
Sign flag will be set if comma or TAB have been dealt with.

B12F CDB7B0   CHR_TYPE2 CALL CHR_TYPE      ;Deal with ctrl params and keywords
B132 DD5E0D   CHRTYPE2A LD   E,(IX+#0D)    ;E= width of line
B135 F5                 PUSH AF            ;Stack the flags
B136 FE06               CP   #06
B138 2806               JR   Z,CTYP_COMM   ;Jump with comma control
B13A FE17               CP   #17
B13C 2810               JR   Z,CTYP_TAB    ;Jump with TAB control
B13E F1                 POP  AF            ;Restore the flags
B13F C9                 RET                ;Return
B140 DD7E0C   CTYP_COMM LD   A,(IX+#0C)    ;A= column number of print position
B143 3C                 INC  A
B144 E6F8               AND  #F8
B146 C608               ADD  A,#08         ;A= column number of next field
B148 BB                 CP   E
B149 3811               JR   C,CTYP_SPCS   ;Jump if column number is in range
B14B 7B                 LD   A,E
B14C 180E               JR   CTYP_SPCS     ;Else tab past end of current line
B14E 61       CTYP_TAB  LD   H,C
B14F 68                 LD   L,B           ;HL= TAB parameter
B150 1600               LD   D,#00         ;DE= width of line
B152 ED52     CTYP_LOOP SBC  HL,DE
B154 30FC               JR   NC,CTYP_LOOP
B156 19                 ADD  HL,DE         ;Reduce modulo line length
B157 7D                 LD   A,L           ;A= column number to TAB to
B158 A7                 AND  A
B159 2001               JR   NZ,CTYP_SPCS  ;Jump unless column zero is required
B15B 7B                 LD   A,E           ;Tab past end of current line
B15C DDBE0C   CTYP_SPCS CP   (IX+#0C)
B15F 2807               JR   Z,CTYP_EXIT   ;Jump if required column reached
B161 F5                 PUSH AF
B162 CD390C             CALL PO_SPACE      ;Print a space
B165 F1                 POP  AF
B166 18F4               JR   CTYP_SPCS     ;Jump back to see if finished
B168 F1       CTYP_EXIT POP  AF            ;A= control just dealt with
B169 F6FF               OR   #FF           ;Set the Sign flag
B16B C9                 RET                ;Return

The following two entry points PAGE_0 and PAGE_7 will page in RAM page zero and
RAM page seven respectively. The routines will have no effect unless called from
a Spectrum 128 in 128K mode.

B16C C5       PAGE_0    PUSH BC
B16D 0600               LD   B,#00         ;B= required page number
B16F 1803               JR   PAGE_B        ;Jump forward
B171 C5       PAGE_7    PUSH BC
B172 0607               LD   B,#07         ;B= required page number
B174 F5       PAGE_B    PUSH AF
B175 FDCB0166           BIT  4,(FLAGS)
B179 280E               JR   Z,PAGE_EXIT   ;Jump if not in 128K mode
B17B 3A5C5B             LD   A,(BANK_M)    ;A= current page flags
B17E E6F8               AND  #F8
B180 B0                 OR   B
B181 01FD7F             LD   BC,#7FFD      ;BC= port no reqd to change page
B184 325C5B             LD   (BANK_M),A    ;Signal required RAM page
B187 ED79               OUT  (C),A         ;Actually change page
B189 F1       PAGE_EXIT POP  AF
B18A C1                 POP  BC
B18B C9                 RET                ;Return

The following three subroutines require that initially HL points to a byte on
the screen, and will each adjust the value of HL in a different way. The first
will point HL to the pixel immediately below the given one; the second will
point HL down one line (ie. down eight pixels); and the third will find the
address of the corresponding attribute byte.

B18C 24       DOWN_1    INC  H             ;Assume within a character square
B18D 7C                 LD   A,H
B18E E607               AND  #07
B190 C0                 RET  NZ            ;Return if this is so
B191 7D                 LD   A,L
B192 C620               ADD  A,#20
B194 6F                 LD   L,A           ;Assume crossing screen thirds
B195 D8                 RET  C             ;Return if this is so
B196 7C                 LD   A,H
B197 D608               SUB  #08
B199 67                 LD   H,A           ;Adjust for within screen third
B19A C9                 RET                ;Return

B19B 7D       DOWN_8    LD   A,L
B19C C620               ADD  A,#20
B19E 6F                 LD   L,A           ;Assume within screen third
B19F D0                 RET  NC            ;Return if this is so
B1A0 7C                 LD   A,H
B1A1 C608               ADD  A,#08
B1A3 67                 LD   H,A           ;Adjust for crossing screen thirds
B1A4 C9                 RET                ;Return

B1A5 7C       ATTR_ADDR LD   A,H
B1A6 1F                 RRA
B1A7 1F                 RRA
B1A8 E606               AND  #06           ;Isolate screen third number
B1AA F6B0               OR   #B0
B1AC CB14               RL   H             ;Carry flag = screen number
B1AE 1F                 RRA
B1AF 67                 LD   H,A           ;HL= attribute byte address
B1B0 C9                 RET                ;Return

The remainder of the program in this article is concerned exclusively with
WINDOW channels. The following subroutine will move the print position to the
start of the (A+1)th line of the current window.

B1B1 DDBE0F   LINE_A    CP   (IX+W_HEIGHT)
B1B4 D29F1E             JP   NC,REPORT_B   ;Give error if line number too big
B1B7 DD360C00           LD   (IX+W_XCOORD),#00 ;Reset x coordinate
B1BB DD770E             LD   (IX+W_YCOORD),A   ;Assign new y coordinate
B1BE DD6E12             LD   L,(IX+W_HOME)lo
B1C1 DD6613             LD   H,(IX+W_HOME)hi   ;HL= address of top LH corner
B1C4 A7                 AND  A
B1C5 2806               JR   Z,LINEFOUND   ;Jump if line zero required
B1C7 47                 LD   B,A           ;B= line number required
B1C8 CD9BB1   LINE_LOOP CALL DOWN_8        ;HL= address of next line
B1CB 10FB               DJNZ LINE_LOOP     ;HL= address of required line
B1CD CD9AB3   LINEFOUND CALL STOREADDR     ;Store this address as print position
B1D0 DDCB0BFE           SET  7,(IX+W_FLAGS);Signal "Leading space not required"
B1D4 DDCB0B66           BIT  4,(IX+W_FLAGS)
B1D8 C8                 RET  Z             ;Return if this is a "Fast" channel
B1D9 DD361600           LD   (IX+W_PIX),#00;Reset print pos within char square
B1DD C9                 RET                ;Return

The next subroutine will clear one line of a window, given that A contains the
attribute byte with which to clear; that HL contains the address of the line
within the screen; and that BC contains the width of the line in squares, less
one.

B1DE C5       CLW_LINE  PUSH BC            ;Stack length of line, less one
B1DF E5                 PUSH HL            ;Stack address of line
B1E0 F5                 PUSH AF            ;Stack attribute byte
B1E1 CDA5B1             CALL ATTR_ADDR     ;HL= address of attribute line
B1E4 F1                 POP  AF            ;A= attribute byte
B1E5 54                 LD   D,H
B1E6 5D                 LD   E,L
B1E7 13                 INC  DE            ;DE points to second attribute byte
B1E8 77                 LD   (HL),A        ;Store first attribute byte
B1E9 EDB0               LDIR               ;Store remaining attribute bytes
B1EB E1                 POP  HL            ;HL= address of line
B1EC C1                 POP  BC            ;BC= length of line, less one
B1ED 3E08               LD   A,#08         ;A= number of rows per line
B1EF 54                 LD   D,H
B1F0 5D                 LD   E,L
B1F1 13                 INC  DE            ;DE points to second byte in line
B1F2 E5       CLWL_LOOP PUSH HL            ;Stack address of 1st byte in row
B1F3 D5                 PUSH DE            ;Stack address of 2nd byte in row
B1F4 C5                 PUSH BC            ;Stack length of line, less one
B1F5 3600               LD   (HL),#00      ;Reset first byte
B1F7 EDB0               LDIR               ;Reset remaining bytes
B1F9 C1                 POP  BC            ;BC= length of line, less one
B1FA D1                 POP  DE            ;DE= address of 2nd byte in row
B1FB E1                 POP  HL            ;HL= address of 1st byte in row
B1FC F5                 PUSH AF            ;Stack loop counter
B1FD CD8CB1             CALL DOWN_1        ;HL= addr of 1st byte in next row
B200 F1                 POP  AF            ;A= loop counter
B201 14                 INC  D             ;DE= address of 2nd byte in row
B202 3D                 DEC  A
B203 20ED               JR   NZ,CLWL_LOOP  ;Repeat for all eight rows
B205 C9                 RET                ;Return

This very short subroutine will fetch the width of the current window, measured
in squares, into the C register.

B206 DD4E0D   GET_WIDTH LD   C,(IX+W_WIDTH);C= width of window, in characters
B209 DDCB0B66           BIT  4,(IX+W_FLAGS)
B20D C8                 RET  Z             ;Return with "Fast" windows
B20E DD4E1C             LD   C,(IX+W_WIDTH_8)  ;C= width of window, in squares
B211 C9                 RET

To open a WINDOW channel and attach it to a stream. This subroutine will open
either a "Fast" or a "Slow" window channel. The registers must be assigned on
entry as follows:

For both types of channel:
A'   The stream number to which the channel is to be attached
A    The attribute byte used initially by the window
B    The number of lines between the top of the window and the top of the screen
C    The number of squares between the left of the window and the left of the
     screen
D    The height of the window, in squares
E    The width of the window, in squares
H    00 if the scroll pause is disabled; FF if the scroll pause is enabled
L    00 for a "Fast" window; FF for a "Slow" window

For SLOW windows only:
BC'  Address of normal character set, minus 100h
DE'  Address of graphics character set (beginning at CHR$ 80h)
H'   The width of these characters, in pixels

B212 F5       OPEN_WIND PUSH AF            ;Stack attribute byte
B213 C5                 PUSH BC            ;Stack coordinates of window
B214 D5                 PUSH DE            ;Stack size of window
B215 E5                 PUSH HL            ;Stack flags
B216 011600             LD   BC,#0016      ;BC= length of "Fast" chan info block
B219 DD215200           LD   IX,#0052      ;Signal "No buffer", presuming
                                           ;"Fast" channel
B21D 2C                 INC  L
;[The next line was printed as 2021 in the magazine,]
;[which jumped to the wrong address.            JimG]
B21E 2020               JR   NZ,OPWN_CREA  ;Jump with "Fast" channels
B220 1600               LD   D,#00         ;DE= window width, in squares
B222 62                 LD   H,D
B223 6B                 LD   L,E           ;HL= window width, in squares
B224 29                 ADD  HL,HL
B225 29                 ADD  HL,HL
B226 29                 ADD  HL,HL         ;HL= window width, in pixels
B227 D9                 EXX
B228 7C                 LD   A,H           ;A= character width in pixels
B229 D9                 EXX
B22A 5F                 LD   E,A           ;E= character width in pixels
B22B 3EFF               LD   A,#FF         ;A= -1
B22D 3C       OPWN_LOOP INC  A
B22E ED52               SBC  HL,DE
B230 30FB               JR   NC,OPWN_LOOP  ;A= width of window, in characters
B232 D9                 EXX
B233 6F                 LD   L,A
B234 D9                 EXX                ;L'= width of window, in characters
B235 5F                 LD   E,A           ;DE= width of window, in characters
B236 211E00             LD   HL,#001E      ;HL= length of "Slow" channel info
                                           ;block, excluding buffer
B239 19                 ADD  HL,DE         ;HL= total length of chan info block
B23A 44                 LD   B,H
B23B 4D                 LD   C,L           ;BC= total length of chan info block
B23C DD21D6B4           LD   IX,EMPTY_2    ;IX points to empty buffer subrt
B240 21F1B4   OPWN_CREA LD   HL,WINDOW     ;HL points to output subroutine
B243 11C415             LD   DE,REPORT_J   ;DE points to input error routine
B246 3E57               LD   A,"W"         ;A= name of this channel
B248 CD6DB0             CALL OPEN_NEW      ;Open the channel
B24B E1                 POP  HL            ;HL= flags
B24C D1                 POP  DE            ;DE= size of window
B24D C1                 POP  BC            ;BC= coordinates of window
B24E F1                 POP  AF            ;A= attribute byte for window
B24F DD7714             LD   (IX+W_ATTR),A ;Store attribute byte
B252 AF                 XOR  A             ;A= 00
B253 CB1D               RR   L
B255 1F                 RRA
;[The next line was printed as CB17  RR H in the magazine,]
;[but CB17 is RL A, and CB1C is RR H. As this is setting  ]
;[the "Scroll pause" and "Fast/Slow" flags in bits 5 & 4  ]
;[of W_FLAGS, CB1C  RR H must be correct.            JimG ]
B256 CB1C               RR   H
B258 1F                 RRA
B259 1F                 RRA
B25A 1F                 RRA                ;Construct flags byte
B25B DD770B             LD   (IX+W_FLAGS),A;Store these flags
B25E DD730D             LD   (IX+W_WIDTH),E;Store window width
B261 DD720F             LD   (IX+W_HEIGHT),D  ;Store window height
B264 78                 LD   A,B           ;A= y coordinate of window
B265 E698               AND  #98
B267 F640               OR   #40
B269 DD7713             LD   (IX+W_HOME)hi,A  ;Store high part of address of top
                                              ;left-hand corner of window
B26C 78                 LD   A,B           ;A= y coordinate of window
B26D 0F                 RRCA
B26E 0F                 RRCA
B26F 0F                 RRCA

;[There was a chunk of the ENTER subroutine at B2BB from the next issue]
;[printed in the magazine at this point - which I've omitted.      JimG]

;[The next line was printed as E6B0  AND E0 in the magazine,]
;[but E6B0 is AND B0, and E6E0 is AND E0. The latter command]
;[gives the correct result.                             JimG]
B270 E6E0               AND  #E0
B272 B1                 OR   C
B273 DD7712             LD   (IX+W_HOME)lo,A  ;Store low part of address of top
                                              ;left-hand corner of window
B276 DDCB0B66           BIT  4,(IX+W_FLAGS)
B27A 2816               JR   Z,CLSWINDOW   ;Jump with "Fast" window
B27C DD731C             LD   (IX+W_WIDTH_8),E ;Store window width, in squares
B27F D9                 EXX
B280 DD750D             LD   (IX+W_WIDTH),L;Store window width, in characters
B283 DD7417             LD   (IX+W_CH_WID),H  ;Store character width, in pixels
B286 DD7318             LD   (IX+W_CHARS)lo,E
B289 DD7219             LD   (IX+W_CHARS)hi,D ;Store addr of character set -100h
B28C DD711A             LD   (IX+W_UDG)lo,C
B28F DD701B             LD   (IX+W_UDG)hi,B;Store addr of graphics char set

Control does not return immediately from the above routine, but continues into
the next subroutine, whose purpose it is to clear a window and move the print
position to the top left-hand corner. It is the WINDOW equivalent to CLS.

B292 AF       CLSWINDOW XOR  A             ;A= 00
B293 CDB1B1             CALL LINE_A        ;Move print pos to top left corner
B296 CD06B2             CALL GET_WIDTH     ;C= width of window, in squares
B299 2804               JR   Z,CLSW_CONT   ;Jump with "Fast" windows
B29B DD361D00           LD   (IX+W_LEN),#00;Clear buffer, abandoning contents
B29F DD460F   CLSW_CONT LD   B,(IX+W_HEIGHT)  ;B= height of window, in squares
B2A2 DD7E14             LD   A,(IX+W_ATTR) ;A= attribute byte
B2A5 DD361501           LD   (IX+W_SCROLLS),#01  ;Reset scroll count
B2A9 CD71B1             CALL PAGE_7        ;Use RAM page 7 in case scr 1 in use
B2AC 0D                 DEC  C             ;C= width of window, less one
B2AD C5       CLSW_LOOP PUSH BC
B2AE 0600               LD   B,#00         ;BC= width of window, less one
B2B0 F5                 PUSH AF
B2B1 CDDEB1             CALL CLW_LINE      ;Clear next line of window
B2B4 F1                 POP  AF
B2B5 C1                 POP  BC
B2B6 10F5               DJNZ CLSW_LOOP     ;Clear whole window
B2B8 C36CB1             JP   PAGE_0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The Windows program will continue next month
