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