This month’s utility gives you a new WINDOW command that will place a user defined 3-D window at any location and any size on the screen using any of the eight available colors.
The user can define the line, column, width, height, and color of each window. When using this utility in your own home-grown programs, WINDOW will provide an appropriate background to print text, prompts, or instructions. If used together with my GETPUT utility published previously, you can SAVE, then replace the area of the screen covered-up by the window.
This program provides a graphic background only and doesn’t open a new channel, so PRINT will work in its usual way. Error trapping is minimal for incorrectly sized windows, but is provided for the BASIC syntax.
The syntax of this new command must be followed correctly or you will get a “Nonsense in Basic” error message.
Windows that go off the screen will generate an “Integer out ofrange” error message. The syntax is as follows:
PRINT USRwindow;AT L,C;W,H;A
Note the commas and semi-colons. Variable “L” is the line (1-20) and “C” is the column (1-30) where you want the upper left corner of the window to appear. Variable “W” is the width (1-30) and “H” is the height (1-20) (in characters) of the window that you want to define. “A” is the attribute color (0-7) that you want to paint the window. The window shadow is always black.
The CODE must be initialized by RUNning line 200 first.
The CODE is not relocatable and is 323 bytes long. Study the following demo to understand your new utility command.
Machine Code Disassembly
; ---------------------------------------------------------------------------
; WINDOW - screen window routine for the Timex/Sinclair 2068
;
; Called from BASIC as:
; PRINT USR window;AT line,column;width,height;colour
;
; Draws a filled rectangle in the given PAPER colour, outlines it, and adds a
; three-pixel drop shadow on the right and bottom edges.
;
; Source: BASIC loader DATA lines 250-290. 323 bytes, 49856-50178
; ($C2C0-$C402).
;
; VERIFICATION
; Bytes: DATA value count matches the loader's FOR bounds (323) and the
; values sum to the loader's own checksum (32454).
; Symbols: every ROM entry point below confirmed against a TS2068 home ROM
; disassembly. None of them match the 48K Spectrum ROM, so this
; will not run on a Spectrum.
; ---------------------------------------------------------------------------
; --- TS2068 home ROM entry points ------------------------------
PRINT_END EQU $2175 ; in K_PRIN: return address of the CALL P_SEQ at $2172,
; i.e. "print items done, now CALL ENDQ and return"
SELECT EQU $1230 ; select the stream whose number is in A
DYADIC EQU $1BDC ; NEXT-CHAR, then evaluate two comma-separated
; numeric expressions onto the calculator stack
EXPR_1 EQU $1BE4 ; NEXT-CHAR, then evaluate one numeric expression
; (the RST $20 that falls through into TEM6)
GET_XY EQU $2660 ; pop two stacked values: C = first argument,
; B = second, E = sign of first, D = sign of second
FP2BC EQU $3160 ; pop one stacked value into BC (A = copy of C)
SUB_HL_DE EQU $1745 ; BC := HL - DE, HL and DE returned swapped.
; Lives in the LIST module; borrowed here as a
; general-purpose subtract
PLOTBC EQU $263E ; plot at C (x), B (y)
DRAWLN EQU $2813 ; draw a line, BC magnitudes, DE direction signs
; --- system variables ------------------------------------------------------
MASKT EQU $56 ; IY+$56 = $5C90, temporary attribute mask.
; A set bit means "take this attribute bit from what
; is already on screen". The ROM's own PLOT setup
; uses OR $F8 here; this routine uses $38.
; --- constants -------------------------------------------------------------
CH_AT EQU 22 ; AT control code
CH_PAPER EQU 17 ; PAPER control code
TK_AT EQU $AC ; BASIC token for AT
STREAM_S EQU 2 ; main screen stream
ERR_NONSENSE EQU $0B ; "C Nonsense in BASIC"
ORG $C2C0
; ---------------------------------------------------------------------------
; Entry. USR is evaluated deep inside the ROM's PRINT item handler, so the
; stack is carrying the expression evaluator's return addresses. Discard them
; until $2175 surfaces - that is the return address of the CALL P_SEQ inside
; K_PRIN - then push it back.
;
; Two things fall out of this. The routine can now read the rest of the BASIC
; line itself instead of letting the ROM print it, and the closing RET lands
; at $2175, where the ROM does CALL ENDQ and returns from PRINT with the
; statement already consumed.
; ---------------------------------------------------------------------------
window: ; $C2C0
LD DE,PRINT_END
unwind: ; $C2C3
POP HL
AND A
SBC HL,DE
JR NZ,unwind
PUSH DE ; restore it for the closing RET
; --- parse the rest of the statement ---------------------------------------
; GET_XY returns the first argument in C and the second in B, because the
; calculator stack is popped last-in first. So C takes line and width, B takes
; column and height.
RST $18 ; get current char
CP ";"
JR NZ,error
RST $20 ; next char
CP TK_AT
JR NZ,error
CALL DYADIC ; line,column
CALL GET_XY
LD ($C300),BC ; C -> line, B -> column
RST $18
CP ";"
JR NZ,error
CALL DYADIC ; width,height
CALL GET_XY
LD ($C302),BC ; C -> width, B -> height
RST $18
CP ";"
JR Z,getcol
error: ; $C2F2
RST $08
DEFB ERR_NONSENSE
getcol: ; $C2F4
CALL EXPR_1 ; steps past the ";" and takes colour
CALL FP2BC
LD A,C
LD (colour+1),A ; self-modify: poke it into LD A,n
JR fill
; --- parameter block, stepped over by the JR above -------------------------
; Held here rather than in BASIC variables so the routine stays self-contained.
; Re-used later to hold the same four values converted to pixel coordinates.
params: ; $C300
p_line: DEFB 0 ; row, then y in pixels
p_column: DEFB 0 ; column, then x in pixels
p_width: DEFB 0 ; width in chars, then pixels
p_height: DEFB 0 ; height in chars, then pixels
; ---------------------------------------------------------------------------
; Fill the rectangle with spaces in the requested PAPER colour.
; ---------------------------------------------------------------------------
fill: ; $C304
LD A,STREAM_S
CALL SELECT
LD A,CH_PAPER
RST $10
colour: ; $C30C
LD A,3 ; operand overwritten at $C30D above
RST $10
LD DE,(params) ; E = line, D = column
LD A,(p_height)
LD B,A ; B = rows remaining
row: ; $C317
LD A,(p_width)
LD C,A ; C = columns remaining
LD A,CH_AT
RST $10
LD A,E
RST $10 ; row
LD A,D
RST $10 ; column
space: ; $C322
LD A," "
RST $10
DEC C
JR NZ,space
INC E ; next screen row
DJNZ row
; ---------------------------------------------------------------------------
; Convert the four character coordinates to pixel coordinates in place.
;
; PLOT measures y upward from the bottom of the screen while AT counts rows
; downward from the top, so the row is flipped first. SUB_HL_DE is a memory
; management helper from the LIST module, pressed into service as an arbitrary
; subtract: it is handed DE holding both coordinate bytes at once and only the
; low byte of the result is kept, so the column sitting in D does not matter.
; ---------------------------------------------------------------------------
topixels: ; $C32B
LD (IY+MASKT),$38 ; %00111000 - keep each cell's existing
; PAPER when plotting, so the border and
; shadow do not repaint the fill or the
; background. INK, BRIGHT and FLASH still
; come from the temporary attributes
LD HL,22
LD DE,(params)
CALL SUB_HL_DE
LD A,C
LD (p_line),A ; line := 22 - line
LD A,(p_line) ; y := line * 8
ADD A,A
ADD A,A
ADD A,A
LD (p_line),A
LD A,(p_column) ; x := column * 8 - 1
ADD A,A
ADD A,A
ADD A,A
DEC A
LD (p_column),A
LD A,(p_width) ; width := width * 8 + 1
ADD A,A
ADD A,A
ADD A,A
INC A
LD (p_width),A
LD A,(p_height) ; height := height * 8 + 1
ADD A,A
ADD A,A
ADD A,A
INC A
LD (p_height),A
; ---------------------------------------------------------------------------
; Outline the window. PLOT the top-left corner, then four DRAWs clockwise.
; DRAWLN takes magnitudes in B and C with their signs in D and E, in the
; pairing GET_XY established: C with E (horizontal), B with D (vertical).
; ---------------------------------------------------------------------------
border: ; $C364
LD A,(p_column)
LD C,A
LD A,(p_line)
LD B,A
CALL PLOTBC ; top-left corner
LD A,(p_width) ; top edge: E = +1, right
LD C,A
LD B,0
LD DE,$0101
CALL draw
LD C,0 ; right edge: D = -1, down
LD A,(p_height)
LD B,A
LD E,1
LD D,$FF
CALL draw
LD A,(p_width) ; bottom edge: E = -1, left
LD C,A
LD B,0
LD E,$FF
LD D,1
CALL draw
LD C,0 ; left edge: D = +1, up
LD A,(p_height)
LD B,A
LD DE,$0101
CALL draw
JR shadow ; step over the shim below
; ---------------------------------------------------------------------------
; DRAW shim. DRAWLN corrupts HL', which the caller is using, so save and
; restore it around the call.
; ---------------------------------------------------------------------------
draw: ; $C3A3
EXX
PUSH HL ; HL'
EXX
CALL DRAWLN
EXX
POP HL
EXX
RET
; ---------------------------------------------------------------------------
; Drop shadow: three horizontal lines below the window, then three vertical
; lines to its right, each inset three pixels from the corner.
; ---------------------------------------------------------------------------
shadow: ; $C3AD
LD A,(p_height)
LD B,A
LD A,(p_line)
SBC A,B ; see note 1 - carry is not cleared
DEC A
LD B,A ; y of the first shadow line
LD L,3 ; three lines
shad_h: ; $C3B9
PUSH HL
LD A,(p_column)
INC A
INC A
INC A ; inset 3 pixels from the left corner
LD C,A
PUSH BC
CALL PLOTBC
LD A,(p_width)
LD C,A
LD B,0
LD DE,$0101
CALL draw
POP BC
DEC B ; one pixel further down
POP HL
DEC L
JR NZ,shad_h
LD A,(p_column)
LD B,A
LD A,(p_width)
ADD A,B
INC A
LD C,A ; x of the first shadow column
LD L,3
shad_v: ; $C3E3
PUSH HL
LD A,(p_line)
DEC A
DEC A
DEC A ; inset 3 pixels from the top corner
LD B,A
PUSH BC
CALL PLOTBC
LD C,0
LD A,(p_height)
LD B,A
LD D,$FF
LD E,1
CALL draw
POP BC
INC C ; one pixel further right
POP HL
DEC L
JR NZ,shad_v
RET ; $C402 - returns to PRINT_END
; ---------------------------------------------------------------------------
; Notes on the original
;
; 1. $C3B4 SBC A,B
; The carry flag is not cleared first, and the instruction before it is a
; call into DRAWLN, so its state is not predictable. If carry comes back
; set, the shadow sits one pixel higher than intended. AND A before the SBC
; would settle it. Reported, not applied.
;
; 2. The routine never validates its arguments. A column near the right edge
; with a large width runs the border off the screen; the BASIC demo guards
; against this in lines 60-80 rather than the machine code doing it.
;
; 3. SELECT is called again at $C304 even though K_PRIN already selected
; stream 2 at $2166, before the USR expression was evaluated.
; ---------------------------------------------------------------------------
Source Code
10 LET window=49856
20 PRINT USR window;AT 5,4;25,4;5
30 PRINT PAPER 5; INK 2;AT 6,5;"This is the window DEMO!"; FLASH 1;AT 7,5;"Press a key to continue."
40 PAUSE 0
50 LET line=INT (1+RND*19)
60 LET column=INT (1+RND*29)
70 LET width=INT (1+RND*(31-column))
80 LET height=INT (1+RND*(21-line))
90 LET color=INT (1+RND*7)
100 PRINT USR window;AT line,column;width,height;color
110 GO TO 50
200 CLEAR 49855: LET t=0
210 FOR n=49856 TO 50178
220 READ a: POKE n,a: LET t=t+a
230 NEXT n: IF t<>32454 THEN PRINT FLASH 1;"Data Error! Recheck DATA lines": STOP
240 RUN
250 DATA 17,117,33,225,167,237,82,32,250,213,223,254,59,32,35,231,254,172,32,30,205,220,27,205,96,38,237,67,0,195,223,254,59,32,15,205,220,27,205,96,38,237,67,2,195,223,254,59,40,2,207,11,205,228,27,205,96,49,121,50,13,195
260 DATA 24,4,0,0,0,0,62,2,205,48,18,62,17,215,62,3,215,237,91,0,195,58,3,195,71,58,2,195,79,62,22,215,123,215,122,215,62,32,215,13,32,250,28,16,236
270 DATA 253,54,86,56,33,22,0,237,91,0,195,205,69,23,121,50,0,195,58,0,195,135,135,135,50,0,195,58,1,195,135,135,135,61,50,1,195,58,2,195,135,135,135,60,50,2,195,58,3,195,135,135,135,60,50,3,195
280 DATA 58,1,195,79,58,0,195,71,205,62,38,58,2,195,79,6,0,17,1,1,205,163,195,14,0,58,3,195,71,30,1,22,255,205,163,195,58,2,195,79,6,0,30,255,22,1,205,163,195,14,0,58,3,195,71,17,1,1,205,163,195,24,10,217,229,217,205,19,40,217,225,217,201
290 DATA 58,3,195,71,58,0,195,152,61,71,46,3,229,58,1,195,60,60,60,79,197,205,62,38,58,2,195,79,6,0,17,1,1,205,163,195,193,5,225,45,32,226,58,1,195,71,58,2,195,128,60,79,46,3,229,58,0,195,61,61,61,71,197,205,62,38,14,0,58,3,195,71,22,255,30,1,205,163,195,193,12,225,45,32,225,201Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.