.........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

