.........1.........2.........3.........4.........5.........6.........7.........8 STREAMS AND CHANNELS by Toni Baker part 3 of 5, ZX Computing February 1987 The concluding part of Toni Baker's Windows program. [Note that the code in STREAMS.TAP "PART3"] [includes the code from "PART2". JimG] Last month we began experimenting with windows. The listing continues ... As a demonstration, the extra program WIND_DEMO which I've tagged on at the end at address B544 will open a window twenty-four squares wide by eight squares high, positioned AT 2,1 (relative to the whole screen) with yellow paper and blue ink. Furthermore, the window will be a SLOW window, so words won't ever be cut in half, and although the standard character set is used, they are defined to be seven bits wide, not eight, so you get more characters than you would normally. Running the program once will open the window and attach it to stream four. Thereafter PRINT #4 will print onto the window. Follow through For those of you who wish to follow the program through and understand how it all works, I'll tell you that the program starts running from location WINDOW (address B4F1) with the A register containing the character to be printed, whenever RST 10 is used with this channel. Oh - incidentally - while we're talking about the WINDOW routine, take a look at the four instructions following the label WIND_CTRL. The CALL instruction carries out the control code function, the POP instruction restores the control character to the A register, and the RET instruction terminates everything - the routine has finished - control will then pass back to the RST 10 sub-routine itself, and then back to the PRINT statement which caused the RST 10 to be used. But ... what's this AND A instruction doing just before the RET? The comment beside the instruction reads "Reset the carry flag". Why? - Surely everything's finished now. We shouldn't need to worry about flags should we? Unfortunately we do. You see when a control code such as PAPER 4 or INVERSE 1 is used in a PRINT statement then the appropriate control codes are sent to RST 10 to be carried out. The PRINT routine expects the carry flag to be reset on return from such a routine, and will produce the error message "C Nonsense in BASIC" if this is not the case. Next month I'll give you no less than two new channels: a modified network channel for owners of the ZX Interface 1 which will successfully communicate with a QL, and a channel which will allow users of the Spectrum 128 to use the standard ZX Printer whilst in 128K mode, saving a lot of money in the process. See you then. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The following subroutine prints a newline; ie. it moves the print position to the left-hand edge of the next line down. B2BB CDC3B2 ENTER CALL ENTER_1 ;Print a single newline once B2BE DDCB0B56 BIT 2,(IX+W_FLAGS) B2C2 C8 RET Z ;Return if using single height B2C3 DD7E0E ENTER_1 LD A,(IX+W_YCOORD) ;A= current y coordinate B2C6 3C INC A ;A= new y coordinate B2C7 DDBE0F CP (IX+W_HEIGHT) B2CA C2B1B1 JP NZ,LINE_A ;Jump if in range to move print pos B2CD DDCB0B6E BIT 5,(IX+W_FLAGS) B2D1 2812 JR Z,SCROLL ;Jump if scroll pause disabled B2D3 DD3515 DEC (IX+W_SCROLLS);Decrement scroll count B2D6 200D JR NZ,SCROLL ;Jump unless scroll pause required B2D8 DD7E0F LD A,(IX+W_HEIGHT) B2DB DD7715 LD (IX+W_SCROLLS),A ;Re-initialise scroll count B2DE 3E7F SCR_PAUSE LD A,#7F B2E0 DBFE IN A,(#FE) ;Scan part of the keyboard B2E2 1F RRA B2E3 38F9 JR C,SCR_PAUSE ;Pause until SPACE pressed B2E5 CD541F SCROLL CALL BREAK_KEY B2E8 D2000D JP NC,REPORT_D ;Give error report if BREAK pressed B2EB DD7E0E LD A,(IX+W_YCOORD) ;A= y coordinate of bottom line B2EE F5 PUSH AF B2EF CDB1B1 CALL LINE_A ;Move print pos to start of bottom ;line of window B2F2 C1 POP BC ;B= number of lines to copy B2F3 CD06B2 CALL GET_WIDTH ;C= width of window in squares B2F6 DD6E12 LD L,(IX+W_HOME)lo B2F9 DD6613 LD H,(IX+W_HOME)hi ;HL= address of top left corner B2FC CD71B1 CALL PAGE_7 ;Use RAM page 7 in case scr 1 used B2FF C5 SCR_LOOP1 PUSH BC ;Stack loop counter B300 0600 LD B,#00 ;BC= width of line B302 C5 PUSH BC B303 E5 PUSH HL B304 CDA5B1 CALL ATTR_ADDR ;HL= addr of this attribute line B307 112000 LD DE,#0020 B30A EB EX DE,HL ;DE= addr of this attribute line B30B 19 ADD HL,DE ;HL= addr of next attribute line B30C EDB0 LDIR ;Copy one attribute line B30E E1 POP HL B30F C1 POP BC B310 E5 PUSH HL B311 CD9BB1 CALL DOWN_8 ;HL= address of next line B314 D1 POP DE ;DE= address of this line B315 E5 PUSH HL B316 3E08 LD A,#08 ;A= number of rows per line B318 C5 SCR_LOOP2 PUSH BC B319 D5 PUSH DE B31A E5 PUSH HL B31B EDB0 LDIR ;Copy one row from line B31D E1 POP HL B31E D1 POP DE B31F C1 POP BC B320 24 INC H ;HL= addr of next row of next line B321 14 INC D ;DE= addr of next row of this line B322 3D DEC A B323 20F3 JR NZ,SCR_LOOP2 ;Copy whole line B325 E1 POP HL B326 C1 POP BC B327 10D6 DJNZ SCR_LOOP1 ;Transfer all required lines B329 0B DEC BC ;BC= length of line, less one B32A DD7E14 LD A,(IX+W_ATTR) ;A= attribute byte B32D CDDEB1 CALL CLW_LINE ;Clear bottom line B330 C36CB1 JP PAGE_0 ;Restore RAM page zero and return The following subroutine deals with all control codes except for comma-control and TAB (these are dealt with by CHR_TYPE2 at address B12F). On entry the A register will contain the control code itself, while any parameters required will be stored in B (middle parameter, if one exists) and C (last parameter). B333 FE0D CONTROLS CP "enter" B335 2884 JR Z,ENTER ;Jump to deal with "enter" B337 D610 SUB #10 B339 D8 RET C ;Return with codes 00 to 0F B33A FE06 CP #06 B33C 2827 JR Z,CTRL_AT ;Jump to deal with AT B33E D0 RET NC ;Return with codes 17 to 1F B33F C65F ADD A,CTRL_INFO lo B341 6F LD L,A B342 26B3 LD H,CTRL_INFO hi;HL points to control info table B344 46 LD B,(HL) ;B= bit mask for this control B345 111400 LD DE,#0014 ;DE= IX displacement to W_ATTR B348 FE63 CP #63 B34A 3802 JR C,CTRL_CONT ;Jump unless ctrl is INVERSE/OVER B34C 1E0B LD E,#0B ;DE= IX displacement to W_FLAGS B34E DDE5 CTRL_CONT PUSH IX B350 E1 POP HL ;HL points to channel info block B351 19 ADD HL,DE ;HL points to variable to alter B352 79 LD A,C ;A= control parameter B353 48 LD C,B ;C= bit mask B354 0F RRCA B355 07 CTRL_LOOP RLCA B356 CB18 RR B B358 30FB JR NC,CTRL_LOOP ;A= ctrl parameter in correct posn B35A AE XOR (HL) B35B A1 AND C B35C AE XOR (HL) ;Mix in reqd bits according to mask B35D 77 LD (HL),A ;Store variable B35E C9 RET B35F 07 CTRL_INFO DEFB #07 ;Bit mask for INK B360 38 DEFB #38 ;Bit mask for PAPER 80 DEFB #80 ;Bit mask for FLASH B362 40 DEFB #40 ;Bit mask for BRIGHT B363 08 DEFB #08 ;Bit mask for INVERSE B364 04 DEFB #04 ;Bit mask for OVER The next subroutine is the WINDOW version of the AT function. It performs the function AT B,C for the current window. B365 C5 CTRL_AT PUSH BC B366 78 LD A,B ;A= proposed y coordinate B367 CDB1B1 CALL LINE_A ;Move print pos to start of this line B36A C1 POP BC B36B 79 LD A,C ;A= proposed x coordinate B36C A7 AND A B36D C8 RET Z ;Return if task already done B36E DDBE0D CP (IX+W_WIDTH) B371 D29F1E JP NC,REPORT_B ;Give error report if out of range B374 DD770C LD (IX+W_XCOORD),A ;Store new x coordinate B377 0600 LD B,#00 ;BC= x coordinate B379 DDCB0B66 BIT 4,(IX+W_FLAGS) B37D 281A JR Z,AT_EXIT ;Jump with "Fast" channels B37F E5 PUSH HL ;Stack address of start of line B380 DD4E17 LD C,(IX+W_CH_WID) ;BC= character width in pixels B383 60 LD H,B B384 68 LD L,B ;HL= 0000 B385 09 AT_LOOP_1 ADD HL,BC B386 3D DEC A B387 20FC JR NZ,AT_LOOP_1 ;HL= no of pixels to start of char B389 0603 LD B,#03 B38B CB3C AT_LOOP_2 SRL H B38D CB1D RR L B38F 1F RRA B390 10F9 DJNZ AT_LOOP_2 ;HL= no of squares to start of char B392 07 RLCA B393 07 RLCA B394 07 RLCA ;A= pixel position within char square B395 DD7716 LD (IX+W_PIX),A ;Store in variable B398 C1 POP BC ;BC= address of start of line B399 09 AT_EXIT ADD HL,BC ;HL= new print position address B39A DD7510 STOREADDR LD (IX+W_PRPOS)lo,L B39D DD7411 LD (IX+W_PRPOS)hi,H ;Store new print position B3A0 C9 RET ;Return The following subroutine is very short and simple. It merely sets the attribute byte corresponding to the screen address in HL. B3A1 E5 SET_ATTR PUSH HL B3A2 CDA5B1 CALL ATTR_ADDR ;HL= address of attribute byte B3A5 DD7E14 LD A,(IX+W_ATTR) ;A= current colours B3A8 77 LD (HL),A ;Store attribute byte B3A9 E1 POP HL B3AA C9 RET ;Return This subroutine is intended for use with "Slow" windows only. It will plot one row of a character onto the screen. B3AB CDA1B3 PLOT_ROW CALL SET_ATTR ;Set attribute byte B3AE 7E LD A,(HL) ;A= byte from screen B3AF AA XOR D B3B0 A0 AND B B3B1 AA XOR D ;Mix in bits from character B3B2 77 LD (HL),A ;Store in screen B3B3 79 LD A,C ;A= low byte of mask B3B4 3C INC A B3B5 280A JR Z,PLRW_EXIT ;Exit if all bits stored on screen B3B7 23 INC HL ;HL points to next screen byte B3B8 CDA1B3 CALL SET_ATTR ;Set this attribute byte as well B3BB 7E LD A,(HL) ;A= byte from screen B3BC AB XOR E B3BD A1 AND C B3BE AB XOR E ;Mix in bits from character B3BF 77 LD (HL),A ;Store in screen B3C0 2B DEC HL ;HL points to original screen byte B3C1 C38CB1 PLRW_EXIT JP DOWN_1 ;Point HL one pixel down, and return This subroutine will calculate 8*A+DE, and will also collect the current print position. B3C4 6F PREPARE LD L,A B3C5 2600 LD H,#00 ;HL= character code B3C7 29 ADD HL,HL B3C8 29 ADD HL,HL B3C9 29 ADD HL,HL ;HL= eight times character code B3CA 19 ADD HL,DE B3CB EB EX DE,HL ;DE= address of pixel expansion B3CC DD6E10 LD L,(IX+W_PRPOS)lo B3CF DD6611 LD H,(IX+W_PRPOS)hi ;HL= address of print position B3D2 C9 RET ;Return This subroutine will collect one byte from the pixel expansion pointed to by DE and will invert it if necessary. B3D3 1A GET_ROW LD A,(DE) ;A= next row of pixel expansion B3D4 13 INC DE ;DE points to next row B3D5 DDCB0B5E BIT 3,(IX+W_FLAGS) B3D9 C8 RET Z ;Return unless INVERSE 1 in operation B3DA 2F CPL ;Otherwise invert the row B3DB C9 RET This next and very important subroutine will actually print a character, specified in the A register, onto the current window. B3DC F5 PRINT_CHR PUSH AF ;Stack character to print B3DD F5 PUSH AF ;Stack character to print (again) B3DE DD7E0C LD A,(IX+W_XCOORD) ;A= current X coordinate B3E1 DDBE0D CP (IX+W_WIDTH) B3E4 CCBBB2 CALL Z,ENTER ;Print newline if at end of line B3E7 DDCB0B66 BIT 4,(IX+W_FLAGS) B3EB 2034 JR NZ,PCHRSLOW ;Jump with "Slow" window B3ED F1 POP AF ;A= character to print B3EE ED5B365C LD DE,(CHARS) ;DE= addr of normal char set - 100h B3F2 2012 JR NZ,PCHR_OK_1 ;Jump with ASCII character B3F4 300A JR NC,PCHR_UDG ;Jump with user-defined graphics B3F6 47 LD B,A B3F7 CD380B CALL PO_GR_1 ;Construct block graphic B3FA 11925C LD DE,MEMBOT ;DE points to pixel expansion B3FD AF XOR A B3FE 1806 JR PCHR_OK_1 ;Jump forward B400 D690 PCHR_UDG SUB #90 B402 ED5B7B5C LD DE,(UDG) ;DE points to user-defined graphics B406 CDC4B3 PCHR_OK_1 CALL PREPARE ;DE= address of pixel expansion ;HL= address of print position B409 CDA1B3 CALL SET_ATTR ;Set attribute for this square B40C E5 PUSH HL B40D 0608 LD B,#08 ;B= number of rows per line B40F CDD3B3 PCHRLOOP1 CALL GET_ROW ;A= next row from expansion B412 CD71B1 CALL PAGE_7 ;Use RAM page 7 in case scrn 1 in use B415 77 LD (HL),A ;Store row in screen B416 24 INC H ;HL points to next row B417 CD6CB1 CALL PAGE_0 ;Restore RAM page zero B41A 10F3 DJNZ PCHRLOOP1 ;Print whole character B41C E1 POP HL B41D 23 INC HL ;HL= new print position B41E C3B2B4 JP PCHR_EXIT ;Jump to exit B421 DDCB0B56 PCHRSLOW BIT 2,(IX+W_FLAGS) B425 281E JR Z,PCHRSLOW2 ;Jump unless using double height B427 DD7E0F LD A,(IX+W_HEIGHT) ;A= height of window B42A 3D DEC A ;A= y coordinate of bottom line B42B DDBE0E CP (IX+W_YCOORD) B42E 2015 JR NZ,PCHRSLOW2 ;Jump unless at bottom line B430 DD4E0C LD C,(IX+W_XCOORD) ;C= current x coordinate B433 47 LD B,A ;B= current y coordinate B434 05 DEC B ;B= y coordinate after scroll B435 DD7E0B LD A,(IX+W_FLAGS) B438 F5 PUSH AF ;Stack flags B439 C5 PUSH BC ;Stack coordinates B43A CDC3B2 CALL ENTER_1 ;Scroll the screen once B43D C1 POP BC ;BC= coordinates B43E CD65B3 CALL CTRL_AT ;Move print position back where it ;belongs B441 F1 POP AF B442 DD770B LD (IX+W_FLAGS),A ;Restore the flags B445 F1 PCHRSLOW2 POP AF ;A= character to print B446 DD5E18 LD E,(IX+W_CHARS)lo B449 DD5619 LD D,(IX+W_CHARS)hi;DE= addr of normal chr set - 100h B44C 2008 JR NZ,PCHR_OK_2 ;Jump with ASCII characters B44E D680 SUB #80 B450 DD5E1A LD E,(IX+W_UDG)lo B453 DD561B LD D,(IX+W_UDG)hi;DE points to graphics chr set B456 CDC4B3 PCHR_OK_2 CALL PREPARE ;DE= address of pixel expansion ;HL= address of print position B459 E5 PUSH HL ;Stack address of print position B45A 01FFFF LD BC,#FFFF B45D DD7E17 LD A,(IX+W_CH_WID) ;A= width of chr in pixels B460 CB38 PCHRMASK1 SRL B B462 CB19 RR C B464 3D DEC A B465 20F9 JR NZ,PCHRMASK1 ;BC= mask, not yet in position B467 DD7E16 LD A,(IX+W_PIX) ;A= pixel posn within chr square B46A A7 AND A B46B 2808 JR Z,PCHRMASK3 ;Jump if mask OK B46D 37 PCHRMASK2 SCF B46E CB18 RR B B470 CB19 RR C B472 3D DEC A B473 20F8 JR NZ,PCHRMASK2 ;Otherwise rotate mask into place B475 3E08 PCHRMASK3 LD A,#08 ;A= number of rows per line B477 F5 PCHRLOOP2 PUSH AF ;Stack loop counter B478 CDD3B3 CALL GET_ROW ;A= next row from expansion B47B D5 PUSH DE ;Stack pointer into expansion B47C 57 LD D,A B47D DD7E16 LD A,(IX+W_PIX) ;A= pixel posn within chr square B480 A7 AND A B481 2807 JR Z,PCHR_ROW ;Jump if pixels correctly aligned B483 CB1A PCHRSHIFT RR D B485 CB1B RR E B487 3D DEC A B488 20F9 JR NZ,PCHRSHIFT ;Shift pixels into position B48A CD71B1 PCHR_ROW CALL PAGE_7 ;Set RAM page 7 in case scrn 1 in use B48D CDABB3 CALL PLOT_ROW ;Plot row onto screen B490 DDCB0B56 BIT 2,(IX+W_FLAGS) B494 C4ABB3 CALL NZ,PLOT_ROW ;And again if using double height B497 CD6CB1 CALL PAGE_0 ;Restore RAM page zero B49A D1 POP DE ;DE points to pixel expansion B49B F1 POP AF ;A= loop counter B49C 3D DEC A B49D 20D8 JR NZ,PCHRLOOP2 ;Print whole character B49F E1 POP HL ;HL= original print position B4A0 DD7E16 LD A,(IX+W_PIX) ;A= original position within square B4A3 DD8617 ADD A,(IX+W__CH_WID) ;Allow for width of character B4A6 FE08 PCHR_POS CP #08 B4A8 3805 JR C,PCHR_POS2 ;Jump if print position OK B4AA D608 SUB #08 B4AC 23 INC HL ;Otherwise amend it B4AD 18F7 JR PCHR_POS ;Loop back to try again B4AF DD7716 PCHR_POS2 LD (IX+W_PIX),A ;Store new position within square B4B2 CD9AB3 PCHR_EXIT CALL STOREADDR ;Store new print position B4B5 DD340C INC (IX+W_XCOORD) ;Increment x coordinate B4B8 F1 POP AF ;A= character just printed B4B9 C9 RET ;Return The following subroutine will empty the buffer ("Slow" windows only), printing the former contents onto the window. B4BA D4BBB2 EMPTY CALL NC,ENTER ;Print a newline if required B4BD DDE5 PUSH IX B4BF E1 POP HL ;HL points to channel info block B4C0 011D00 LD BC,#001D B4C3 09 ADD HL,BC ;HL points to variable W_LEN B4C4 7E LD A,(HL) ;A= number of characters in buffer B4C5 A7 AND A B4C6 C8 EMPTYLOOP RET Z ;Return if finished B4C7 23 INC HL ;HL points to next char in buffer B4C8 7E LD A,(HL) B4C9 E5 PUSH HL B4CA CDF4B0 CALL CTYP_NTOK ;Flags indicate type of character B4CD CDDCB3 CALL PRINT_CHR ;Print the character B4D0 E1 POP HL B4D1 DD351D DEC (IX+W_LEN) B4D4 18F0 JR EMPTYLOOP The following subroutine will empty the buffer ("Slow") or do nothin ("Fast"). It will decide whether or not a newline is required, and print one if so. B4D6 DDCB0B66 EMPTY_2 BIT 4,(IX+W_FLAGS) B4DA C8 RET Z ;Return with "Fast" windows B4DB 57 LD D,A ;D= next character to print B4DC DD7E1D LD A,(IX+W_LEN) ;A= length of word in buffer B4DF DD860C ADD A,(IX+W_XCOORD) ;A= potential x coordinate after ;the buffer has been emptied B4E2 DDBE0D CP (IX+W_WIDTH) B4E5 2002 JR NZ,EMPTY_OUT ;Jump unless word exactly fills line B4E7 53 LD D,E ;D= alternative char to print B4E8 37 SCF ;Signal "Newline not needed" B4E9 D5 EMPTY_OUT PUSH DE B4EA C5 PUSH BC B4EB CDBAB4 CALL EMPTY ;Empty buffer with newline if needed B4EE C1 POP BC B4EF F1 POP AF ;A= next character to print B4F0 C9 RET ;Return And now at last we have the window output subroutine itself. B4F1 DD2A515C WINDOW LD IX,(CURCHL) ;IX points to channel info block B4F5 CDB7B0 CALL CHR_TYPE ;Deal with keywords, etc. B4F8 F8 RET M ;Return if tasks completed B4F9 F5 PUSH AF ;Stack character to print B4FA 2824 JR Z,WIND_ABLE ;Jump with graphics characters B4FC 3013 JR NC,WINDASCII ;Jump with ASCII characters B4FE A7 AND A B4FF CC92B2 CALL Z,CLSWINDOW ;Clear window for CHR$ 0 B502 F1 POP AF B503 F5 PUSH AF B504 5F LD E,A ;Signal "Char not to be changed" B505 CDD6B4 CALL EMPTY_2 ;Empty buffer B508 CD32B1 CALL CHRTYPE2A ;Deal with TAB and comma control B50B CD33B3 WIND_CTRL CALL CONTROLS ;Deal with remaining ctrl characters B50E F1 POP AF B50F A7 AND A ;Reset Carry flag B510 C9 RET ;Return B511 FE20 WINDASCII CP "space" B513 200B JR NZ,WIND_ABLE ;Jump with all chars except "space" B515 1E0D LD E,"enter" ;Use "enter" as alternative char B517 CDD6B4 CALL EMPTY_2 ;Empty buffer B51A FE0D CP "enter" B51C 28ED JR Z,WIND_CTRL ;Jump if newline now required B51E 1806 JR WINDPRINT ;Jump to print "space" on window B520 DDCB0B66 WIND_ABLE BIT 4,(IX+W_FLAGS) B524 2004 JR NZ,#B52A ;Jump with "Slow" windows B526 F1 WINDPRINT POP AF B527 C3DCB3 JP PRINT_CHR ;Jump to print the character B52A 2A515C WIND_SLOW LD HL,(CURCHL) ;HL points to channel info block B52D 011D00 LD BC,#001D B530 09 ADD HL,BC ;HL points to variable W_LEN B531 7E LD A,(HL) ;A= number of chars in buffer B532 DDBE0D CP (IX+W_WIDTH) B535 2005 JR NZ,WINDSLOW2 ;Jump unless buffer full B537 E5 PUSH HL B538 CDBAB4 CALL EMPTY ;Empty buffer B53B E1 POP HL B53C F1 WINDSLOW2 POP AF B53D 34 INC (HL) ;Increment length of buffer B53E 4E LD C,(HL) B53F 0600 LD B,#00 ;BC= new length of buffer B541 09 ADD HL,BC B542 77 LD (HL),A ;Store char in buffer B543 C9 RET ;Return ;[The next subroutine gets overwritten by the code in Part 4. JimG] And finally - just a quick demonstration program to open a window and attach it to stream four. If you RUN this you'll be able to use PRINT #4; to print onto the window. B544 3E04 WIND_DEMO LD A,#04 B546 08 EX AF,AF' ;A'= stream number B547 2A7B5C LD HL,(UDG) ;HL= address of user-defined graphics B54A 0180FF LD BC,#FF80 B54D 09 ADD HL,BC B54E 44 LD B,H B54F 4D LD C,L ;BC'= address of UDGs - 80h B550 ED5B365C LD DE,(CHARS) ;DE'= addr of normal char set - 100h B554 2607 LD H,#07 ;H'= width of characters B556 D9 EXX B557 3E31 LD A,#31 ;A= attribute byte for window B559 010102 LD BC,#0201 ;BC= position of window B55C 111808 LD DE,#0818 ;DE= size of window B55F 21FFFF LD HL,#FFFF ;HL signals "Slow window" and ;"Scroll pause enabled" B562 CD12B2 CALL OPEN_WIND ;Open the window channel B565 215827 LD HL,#2758 B568 D9 EXX ;HL'= value required by BASIC B569 C9 RET ;Return - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -