Times Square on Your ZX81
How would you like to be able to create a “Times Square” display which moves from right to left without upsetting other items on your ZX81 (or 8K ROM ZX80) screen? Would you like to display graphs or functions such as a sine wave on your screen when they need more than 32 columns of display? Or what about moving animated characters or graphs across the screen with great speed? You can easily do these and more with LSCROLL, a lateral, or horizontal, scrolling routine for your computer.
Moving graphics around is not extremely difficult with the 8K ROM by using the SCROLL, PAUSE, PRINT AT, PRINT TAB, and UNPLOT statements. Speed becomes important, though, even in the FAST mode. The program described here, written especially for 16K RAM (because of machine language coding), adds to the graphics capabilities of your computer by allowing pictures to move horizontally across the screen without changing their shapes.
Lateral SCROLLing may be obtained by writing a Basic program which uses the string handling capabilities of the 8K ROM. Again, speed is important. Owners of the ZX80 would be staring at a blank screen during execution of the program, and the ZX81 users would wait even longer if the SLOW mode is used. LSCROLL solves these problems. Execution of the lateral scrolling program shown here is extremely fast—under certain conditions even faster than the SCROLL statement.
Here are some of LSCROLL’s features:
1) Machine Language written—fast in both the SLOW and FAST modes and protected by the RAMTOP.
2) 172 bytes long.
3) Beginning line to scroll is selectable by the programmer.
4) Range of lines to be scrolled is selectable by the programmer.
5) Fill characters to be placed in newly opened columns are selectable by the programmer.
6) Totally dynamic—works even if SCROLL is used in your program.
7) 22 lines scrolled laterally, maximum.
8) Scrolling is from right to left—ideal for text windows.
Before writing LSCROLL, I needed to understand the format of the display file within the computer. It is comprised of 24 lines of text, each beginning with an ENTER character, code 118. Some trailing blanks of lines of text are eliminated to save space in the RAM under certain conditions during program execution of SCROLL, or even when typing is being performed such as when a program is being written into the computer. The LSCROLL program takes these conditions into account to avoid crashing the system by upsetting the display file.
Figure 1: LSCROLL Listing.
4 LET C=0
5 REM ALL OS IN THIS LISTING
6 REM ARE ZEROS.
10 LET D$="3AEE7FFE16DA517F3E0032EE7F3AEF7FD600CA617FFE15D2617FC3667F3E1632EF7FF53AEF7F47F13AEE7F80FE16DA857F26002E163AEE7F4F0600ED427D32EF7FED5B0C400EFFF53AEE7F6FF1260006001AFE76CA9F7F13C3957F0CA7E5ED42E1CAAC7F13C3957F0600F53AEF7F4FF1C5D5E1237EFE76C2B77FE5A7ED522B7D4DD600E1C2D07FE5D1C3E57F3DC2D87F13C3E17F06000B13D5E123EDB03E001213C10B79D600C2B47FC90000"
20 FOR X=2 TO 344 STEP 2
30 LET P=(CODE D$(X-1)-28)*16
40 LET P=P+CODE D$(X)-28
50 POKE 32579+INT (X/2),P
60 LET C=C+P
70 NEXT X
80 PRINT C
To use LSCROLL, shown in Figure 1, do the following:
1) Carefully type in the program. You may want to execute a line 10 as a command—EDIT, DELETE, DELETE, ENTER, and then PRINT LEN D$. The value printed after this should be 344.
2) SAVE your proofread program on tape.
3) Enter NEW.
4) Enter POKE 16388,68 and POKE 16389,127. Enter NEW. This will protect LSCROLL from being written over by the Basic system.
5) LOAD your saved program, and RUN it. If within several seconds, the number 22093 appears, enter NEW. If this did not happen, go back to step 1.
Other ways of loading LSCROLL into your computer will be discussed later. For now, since you have LSCROLL in your computer, let us do some experimenting.
To begin scrolling the screen at line XX (0 is the top line, 21 is the last line you can PRINT) just POKE 32750,XX. To scroll for YY lines after and including line number XX, POKE 32751,YY. To execute scrolling, try RAND USR 32580.
As an example, let’s begin with a problem which requires scrolling the third line of the screen. We might also wish to scroll the remainder of the screen. The top two lines of the screen, numbered 0 and 1, are not to be changed (note that LSCROLL uses the same line numbering system as Sinclair). Enter the program shown in Figure 2 and RUN it. You will want to BREAK after scrolling is complete.
Figure 2: Illustrative Use of LSCROLL.
10 PRINT AT 0,13;"TEST";AT 5,13;"HELLO";AT 20,13;"GOODBYE"
20 POKE 32750,2
30 POKE 32751,20
40 PAUSE 20
50 RAND USR 32580
60 GOTO 40
Note that each execution of line 50 in the program will scroll from right to left. Of course, you want to scroll only a certain number of columns. There are several ways to do this. One way is to set up a FOR/NEXT loop around lines 40 and 50. Another way is to change line 50 to something like 50 LET X=X+1+USR 32580. The USR routine always returns the value zero, so the effect of this statement is to increment X for every scroll. The value of X can then be tested with an IF statement for appropriate action.
Also notice that blanks are “filled” into newly opened columns on the screen from the right as characters are LSCROLLed to the left. To “fill” with a different character, just type POKE 32738,CC where CC is the code of the character to be used for filling. For example, to fill with an inverse video blank, enter POKE 32738,128 or even POKE 32738,CODE “■”.
There is another thing interesting about LSCROLL. Look at Figure 2. Values of XX, YY and CC mentioned above need not be POKEd each time LSCROLL is used. When any changes are to be made to these values, only the individual POKE statement needs to be used first. If XX and YY are never POKEd, LSCROLL assumes that the entire screen is to be LSCROLLed. The chart in Figure 3 shows values of XX, YY and CC, along with their default (assumed by the program) values.
Figure 3: Values of XX, YY, and CC.
POKE DEFAULTED
VALUE ADDRESS VALUE FUNCTION
XX 32750 0 if XX> 21 Specifies the line number
to begin scrolling.
(0-21)
YY 32751 22 (all lines) Specifies the number
if XX+YY> 22 of lines after and
or if YY =0 including line XX to be
scrolled. (1-22)
CC 32738 0 if not POKEd Specifies the CODE of
but will not the character for
be altered "filling" in newly
or checked scrolled columns.
To execute LSCROLL, use USR(32580) in your program.
To load LSCROLL, NEW/ POKE 16388,68/POKE 16389,127/NEW/ read LSCROLL in/RUN
LSCROLL is executed by using the sequence of characters “USR 32580” in certain places in your program. The RAND statement, as shown in line 50 of Figure 2, can be used. This makes LSCROLL very convenient to call only when random numbers are used, USR 32580 serves as an “argument” in a dummy LET statement such as LET ZZ=USR 32580.
Figure 4 shows several applications of LSCROLL. Try putting 95 SCROLL into the first program and see how that runs. With this, it looks as though your ZX80 or ZX81 could cost ten times what it does. Try running all of these programs. This may give you some ideas for more applications of LSCROLL. Be sure to NEW the computer between each of the programs. Do not worry. RAMTOP was modified and LSCROLL is protected. The line pointer and range remain as last POKEd, so each program shown ensures that they are selected and POKEd properly. If you ever wonder what values are POKEd in, just PEEK them out with commands. Remember, though, that LSCROLL may have modified any invalid numbers you POKEd, according to the rules in Figure 3.
Figure 4: Applications of LSCROLL.
# indicates a blank
10 REM PROGRAM 1
11 REM FOR ZX81 SLOW MODE,
12 REM REMOVE LINE 70.
20 POKE 32750,10
30 POKE 32751,1
40 LET S$="TIMES SQUARE DISPLAY#####"
50 FOR X=1 TO 25
60 PRINT AT 10,31;S$(X)
70 PAUSE 10
80 RAND USR 32580
90 NEXT X
100 GOTO 50
10 REM PROGRAM 2
20 PRINT AT 5,0;
30 LIST
40 POKE 32750,5
50 POKE 32751,100
60 PAUSE 10
70 RAND USR 32580
80 GOTO 60
10 REM PROGRAM 3
20 LET S$="##NUMBER#"
30 POKE 32750,10
40 POKE 32751,1
50 FOR X=1 TO 20
60 LET D$=S$+STR$ X
70 FOR I=1 TO LEN D$
80 PRINT AT 10,10;"#";AT 10,20;D$(I)
90 PAUSE 10
100 RAND USR 32580
110 NEXT I
120 NEXT X
10 REM PROGRAM 4
20 POKE 32750,0
30 POKE 32751,22
40 PLOT RND*63,RND*43
50 PAUSE 10
60 LET X=USR 32580
70 GOTO 40
10 REM PROGRAM 5
20 POKE 32750,0
30 POKE 32751,50
40 POKE 32738,8
50 FOR Y=21 TO RND*21 STEP -1
60 PRINT AT Y,31;"■"
70 NEXT Y
80 LET X=USR 32580
90 PAUSE 10
100 GOTO 40
10 REM PROGRAM 6
11 REM MODIFIED FROM THE ZX81
12 REM BASIC MANUAL,
13 REM SINCLAIR RESEARCH, LTD
20 POKE 32750,0
30 POKE 32751,0
40 POKE 32738,0
50 FOR X=1 TO 200
60 PLOT 63,22+20*SIN (X/32*PI)
70 PLOT 63,22+20*COS (X/32*PI)
80 PAUSE 8
90 RAND USR 32580
100 NEXT X
How does LSCROLL work? Figure 5 shows the standard Z80 source listing of LSCROLL. Since the text of the display file may change in length and format under certain conditions, LSCROLL takes great care in checking the status of the display file’s area in RAM.
The LSCROLL program begins by checking both the line pointer and the range for scrolling as to the validity of these values. Adjustments are made according to the rules in Figure 3 to insure that LSCROLL does not crash the system. The fill character is not checked.
Each line of text in the display file begins with an ENTER character. The LSCROLL program scans the file and counts ENTER characters until the first line to be scrolled according to the POKEd line pointer is found.
Figure 5: Z80 Source Listing of LSCROLL.
Object
Address Code Ref Instruction Comments
32580 3AEE7F LD A,(LBL21) ;get line pointer in acc.
32583 FE16 CP 22 ;compare with 21.
32585 DA517F JP C,LBL1 ;≤21 go to lbl1.
32588 3E00 LD A,0 ;clear acc.
32590 32EE7F LD (LBL21),A ;make line pointer zero.
32593 7F51 3AEF7F LBL1 LD A,(LBL22) ;get range in acc.
32596 D600 SUB A,0 ;test acc.
32598 CA617F JP Z,LBL2 ;range=0? yes-go to lbl2.
32601 FE15 CP 21 ;compare with 22.
32603 D2617F JP NC,LBL2 ;> 21 go to lbl2.
32606 C3667F JP LBL3 ;go to lbl2.
32609 7F61 3E16 LBL2 LD A,22 ;load acc with 22.
32611 32EF7F LD (LBL22),A ;make range=22.
32614 F5 PUSH AF ;load range in
32615 7F66 3AEF7F LBL3 LD A,(LBL22) ;B and line
32618 47 LD B,A ;pointer in acc.
32619 F1 POP AF ;for final validity
32620 3AEE7F LD A,(LBL21) ;comparison.
32623 80 ADD A,B ;add the two.
32624 FE16 CP 22 ;compare the sum with 22.
32626 DA857F JP C,LBL4 ;sum ≤ 22 go to lbl4.
32629 2600 LD H,0 ;otherwise,
32631 2E16 LD L,22 ;make the
32633 3AEE7F LD A,(LBL21) ;range
32636 4F LD C,A ;equal to
32637 0600 LD B,0 ;the difference
32639 ED42 SBC HL,BC ;(22-line pointer)
32641 7D LD A,L ;to insure the
32642 32EF7F LD (LBL22),A ;correct end of scroll.
32645 7F85 ED5B0C40 LBL4 LD DE,(16396) ;load D_FILE in DE pair.
32649 0EFF LD C,255 ;set counter to -1.
32651 F5 PUSH AF ;load the
32652 3AEE7F LD A,(LBL21) ;line pointer
32655 6F LD L,A ;into
32656 F1 POP AF ;the HL
32657 2600 LD H,0 ;register pair.
32659 0600 LD B,0 ;make BC pair a counter.
32661 7FA5 1A LBL5 LD A,(DE) ;compair display file
32662 FE76 CP 118 ;character with 118.
32664 CA9F7F JP Z,LBL6 ;not 118(ENTER) go to lbl6.
32667 13 INC DE ;inc. search address.
32668 C3957F JP LBL5 ;loop back to lbl5.
32671 7F9F 0C LBL6 INC C ;ENTER found, inc. counter.
32672 A7 AND A ;clear carry flag.
32673 E5 PUSH HL ;subtract counter
32674 ED42 SBC HL,BC ;from
32676 E1 POP HL ;line pointer.
32677 CAAC7F JP Z,LBL11 ;result=0, go to lbl11.
32680 13 INC DE ;inc. search address.
32681 C3957F JP LBL5 ;loop back to lbl5.
32684 7FAC 0600 LBL11 LD B,0 ;load BC
32686 F5 PUSH AF ;register
32687 3AEF7F LD A,(LBL22) ;pair
32690 4F LD C,A ;with
32691 F1 POP AF ;range.
32692 7FC4 C5 LBL12 PUSH BC ;save BC pair in stack.
32693 D5 PUSH DE ;put addr. of first line
32694 E1 POP HL ;to scroll in HL pair.
32695 7FB7 23 LBL13 INC HL ;point to next char.
32696 7E LD A,(HL) ;get char. in acc.
32697 FE76 CP 118 ;compare with 118.
32699 C2B77F JP NZ,LBL13 ;not ENTER go to lbl13.
32702 E5 PUSH HL ;save end of line in stack.
32703 A7 AND A ;clear carry flag.
32704 ED52 SBC HL,DE ;put length of line in HL
32706 2B DEC HL ;pair less one for ENTER.
32707 7D LD A,L ;put length in acc.
32708 4D LD C,L ;also save length in BC pair.
32709 D600 SUB A,0 ;test acc.
32711 E1 POP HL ;get line addr. back.
32712 C2D07F JP NZ,LBL15 ;acc not zero go to lbl15
32715 E5 PUSH HL ;put address of line in
32716 D1 POP DE ;DE pair (no scroll)
32717 C3E57F JP LBL23 ;go to lbl23
32720 7FD0 3D LBL15 DEC A ;decrease acc. if acc.
32721 C2D87F JP NZ,LBL18 ;was < 1 go to lbl18.
32724 13 INC DE ;increment pointer, then
32725 C3E17F JP LBL19 ;go to lbl19 for fill.
32728 7FD8 0600 LBL18 LD B,0 ;clear B.
32730 0B DEC BC ;decrement counter.
32731 13 INC DE ;set up scroll pointer.
32732 D5 PUSH DE ;place pointer in
32733 E1 POP HL ;HL pair and increment
32734 23 INC HL ;for fetching address.
32735 EDB0 LDIR ;scroll the line
32737 7FE1 3E00 LBL19 LD A,0 ;fill character into acc.
32739 12 LD (DE),A ;acc. into last char. pos.
32740 7FE5 13 LBL23 INC DE ;point to next line.
32741 C1 POP BC ;decrement range counter.
32742 0B DEC BC ;to ready next scroll.
32743 79 LD A,C ;load acc. with count.
32744 D600 SUB A,0 ;test acc. range not
32746 C2B47F JP NZ,LBL12 ;satisfied, go to lbl12
32749 7FED C9 LBL20 RET ;return to BASIC BC is 0.
32750 7FEE 00 LBL21 NOP ;storage for line pointer.
32751 7FEF 00 LBL22 NOP ;storage for range.
Before any line is scrolled to the left, LSCROLL counts the length of that line by looking for the next ENTER character. If the length is less than one character, no scrolling takes place.
LSCROLL scrolls until all lines specified via the range value have been shifted. Actual shifting is performed by the Z80 LDIR, “Repeating Block Load with Increment,” instruction. The contents of the memory location whose address is in the computer’s HL register pair are loaded into the memory location whose address is in the computer’s DE register pair. Then, DE and HL are both incremented by one, and BC is decremented by one. When BC, holding the length of the line of text to be shifted, is zero. LDIR allows the computer to execute the next instruction.
You can use LSCROLL in your programs either by entering LSCROLL and NEWing it, as described above, or by writing a program around the listing in Figure 1. Of course, LSCROLL should be on tape to save a lot of typing.
If a program is written around the listing, all that must be done before you LOAD it is the two POKE commands that modify RAMTOP described earlier. It would be a good idea to put the statement:
XXXX IF PEEK 16388+PEEK 16389*256 <>32580 THEN STOP
somewhere near the beginning. This will make sure you did your POKE commands before execution continues. Do not forget that your program cannot do these POKEs because NEW must be executed immediately afterwards. You could spell out these two POKE commands in a REM statement for reference later.
Use your imagination for finding uses for LSCROLL. How about bar charts more than 32 characters wide? Or even objects racing across the screen in games? Try laterally scrolling prompts to users before INPUT statements to break monotony. Also think about this: When LSCROLL and SCROLL are used alternately, an effective diagonal movement of screen graphics or characters results. Virtually every program imaginable could benefit from the frills of the 172-byte LSCROLL program.
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.