.........1.........2.........3.........4.........5.........6.........7.........8

LIGHT SCREEN DESIGNER
ZX Computing, June/July 1984
Part 01 of 13

We welcome back Toni Baker with the first in another
superb series on the delights of machine code.

[This transcription of the series incorporates all corrections to printing ]
[and programming errors, both those reported by the author and readers and ]
[those never reported in the magazine but which I discovered myself while  ]
[transcribing the text and checking the program listings. Minor corrections]
[have been incorporated into the article in which the errors occurred;     ]
[whereas whole rewrites of routines just appear in the article in which the]
[rewrite was published, with a comment by the original routine.            ]
[All comments in [] have been added by me.                             JimG]

[Save: SAVE "LSD" CODE 56064,3863    ]
[Load: CLEAR 49151: LOAD "LSD" CODE  ]
[Run : RANDOMIZE USR 56789           ]
[The author said to save from DB00 (56064), although the code doesn't start]
[until DB42 (56130); the intervening space is used for program variables.  ]
[The area from C000 (49152) to DAFF is used for a copy of the screen.      ]


Well, hello everyone, and welcome to the first in a brand new series
of articles. Throughout this series I intend to list and explain one
program - only one, mind - not one per issue but one program
altogether throughout the series! Why? Well it's a bit long, that's
all.

One problem you get with long programs, I've found, is that lots of
errors seem to crop up in the listing, so we'll have to see what we
can do about that. [Not much, as it turned out. JimG] For those of you
who've got the word processor program, WordSheep, from the Feb/Mar
issue, but haven't managed to get it going yet, here are the
corrections you need to make:

-----------------------------------------------------------------------------
Page 45: In COMPRESS:	ED80 should read EDB0
         In ADJUST:	010060 should read 016000
Page 46: In A_RAND0M:	20FD should read 30FD
         In TRANSFER_U:	013080 should read 010380
         In WIPE:	23 should read 13
Page 47: In DELETE	MISSING INSTRUCTION: C9 (RET) from
			end of subroutine.
Page 48: In PROCESS:	2100EA should read 21C0EA
Page 49: In REFORM:	MISSING INSTRUCTION: 09 (ADD HL,BC)
			between 2AAF5C and 012000
-----------------------------------------------------------------------------

Anyway - back to the present. Light Screen Designer is for people
who've got 48K knocking around inside their Spectrum. If you've only
got 16K then bad luck - you can't use this program - not because it
isn't relocatable, but because you just haven't got enough memory -
this really is a long program!

It's a program to help you design pictures on the screen, a grand
artwork program incorporating just about every feature you could
possibly desire to help you draw pictures. You can do straight lines
and curves and various geometrical shapes. In fact the program will
even do the colouring in for you once you've drawn the outline - in
any colour too!

If you hunt around these pages you'll soon come across a picture
labelled Fig.1 [PART01.GIF - although the diagram was superseded in
Part 13]. You have to MAKE one of these. It's a keyboard overlay and
tells you what each of the keys do when the program is running. To
make an overlay, get a piece of card the same size as a Spectrum
keyboard, cut holes out for the keys to go through, and write all the
appropriate words above the holes. Alternatively, if you can't be
bothered to do all that, just keep a copy of Fig.1 handy by your
Spectrum when running the program and refer to it as and when you need
to. I shall explain what each key actually does later on, when we
actually get down to writing the individual parts.

Let's get down to it then shall we? The memory used by the program
starts at address C000, although the program itself doesn't actually
start until we reach address DB42, so here's what's what:

-----------------------------------------------------------------------------
C000-D7FF	DFC	Display File Copy - used to record pictures.
D800-DAFF	AFC	Attributes File Copy - used to record pictures.
DB00-DB41		System Variables used by the program.
-----------------------------------------------------------------------------

You don't have to worry about any of this for the time being. Now for
some work. The addresses from DB42 to DB8F store a table of subroutine
addresses. The table will be called CMD_ADDRS. Since no subroutines
exist at present then you should, for the time being, set every entry
in this table to 0052 (an address in the ROM at which a RET
instruction is stored). To do this you must POKE 52h into address
DB42, 00 into DB43, 52h into DB44, 00 into DB45, ... and so on up to
..., 52h into DB8E and 00 into DB8F. Got all that? Right, now for some
more data - a table call- ed INP_TABLE. Feed this in:

-----------------------------------------------------------------------------
DB90 59CE3831 INP_TABLE DEFM "Y","N*","8","1"
DB94 B00D3938           DEFM "0*","enter","9","8"
DB98 37363534           DEFM "7","6","5","4"
DB9C 333231B0           DEFM "3","2","1","0*"
-----------------------------------------------------------------------------

It won't make a lot of sense to you at present, but all will become
clear in a moment, so don't go away. Note that some of the bytes have
80h added, and so I've marked these with an asterisk in the right hand
column. Some more data coming up next, but in a slightly different
form. What we have are eighteen messages which will later need to be
printed at the bottom of the screen.

Each message is followed by a coded byte which will tell the program
if anything needs to be input. To enter this, note that the first
column contains the address to which the message is to be written, the
second column contains the text of the message which must be loaded
character by character from the specified address, and the third
column contains one byte of data which must be POKEd immediately after
the text of the message (note that this data is given in hex).

-----------------------------------------------------------------------------
ADDRESS   TEXT                          DATA BYTE
DBA0      Light Screen Designer         80
DBB6      Paint Colour                  98      *
DBC3      WARNING - Screen Memory Wipe  90
DBE0      Store Memory                  95
DBED      Recall Memory                 95
DBFB      Clear Screen                  90
DC08      Number of Radians?            80
DC1C      Now in Text Mode              80
DC2D      Machine Code Address?         80
DC44      Copy                          90
DC49      Ink Colour                    96
DC54      Paper Colour                  96
DC61      Flash Status                  92
DC6E      Border Colour                 98
DC7C      Over Status                   9E
DC88      Inverse Status                9E
DC97      Bright Status                 92
DCA5      Return to BASIC               90
["*" was incorrectly printed as 96; corrected in Part 11. JimG]
-----------------------------------------------------------------------------

Now so far I haven't really given you anything useful except a lot of
promises for the future, so I'd now like to implement just two
subroutines in the program. The techniques involved are quite clever,
although not difficult to follow, so I imagine you should be able to
learn from them. Firstly, we have a subroutine called GET_CHR which
merely waits for any key other than "CAPS SHIFT" to be pressed, and
then returns with DE containing a current keyboard scan.

-----------------------------------------------------------------------------
DCB5 CD8E02   GET_CHR   CALL KEY_SCAN      ;DE= keyboard scan
DCB8 7B                 LD   A,E
DCB9 FE27               CP   #27
DCBB 2803               JR   Z,GET_CHR_2   ;Jump if "caps shift" only pressed
DCBD 3C                 INC  A
DCBE 20F5               JR   NZ,GET_CHR    ;Jump unless no keys at
                                           ;all are pressed
DCC0 CD8E02   GET_CHR_2 CALL KEY_SCAN      ;DE= keyboard scan
DCC3 7B                 LD   A,E
DCC4 FE27               CP   #27
DCC6 28F8               JR   Z,GET_CHR_2   ;Jump if "caps shift" only pressed
DCC8 3C                 INC  A
DCC9 28F5               JR   Z,GET_CHR_2   ;Jump if no keys at
                                           ;all are pressed
DCCB C9                 RET
-----------------------------------------------------------------------------

Notice that there was not one but two loops in the above routine. The
first loop waits until either "caps shift" only or no keys at all are
pressed, and the second loop then waits for any key or key combination
other than "caps shift only" or no keys at all. The purpose of having
two loops instead of one is that the first loop waits until the human
finger is removed from the previous key depression and the second loop
waits for a new key. If this were not done you would have an awfully
fast "repeat" facility (not desired).

In order to understand the subroutine below, I ought to explain what
the data bytes in the message table were all about. 80 means "no input
is required", 90 means "input Y or N" (for YES or NO), 92 means "input
0, 1 or 8", 95 means "input any digit or enter", 96 means "input any
digit", 98 means "input any digit between 0 and 7"' and 9E means
"input either 0 or 1".

The subroutine below must be called in a rather special way - CALL
MESSAGE / DEFB xx - where xx is a byte between 01 and 12. The choice
of byte determines which message is printed. Here's the subroutine:

-----------------------------------------------------------------------------
DCCC AF       MESSAGE   XOR  A             ;A= 00
DCCD CD0116             CALL CHAN_OPEN     ;Use stream number zero
                                           ;(lower part of screen)
DCD0 E1                 POP  HL            ;HL= address of data byte
DCD1 46                 LD   B,(HL)        ;B= number of message to print
DCD2 23                 INC  HL            ;HL= subroutine return address
DCD3 E5                 PUSH HL
DCD4 219FDB             LD   HL,MESSAGES-1
DCD7 7E       ME_SEARCH LD   A,(HL)
DCD8 23                 INC  HL
DCD9 FE80               CP   #80
DCDB 38FA               JR   C,ME_SEARCH   ;Find start of next message
DCDD 10F8               DJNZ ME_SEARCH     ;Search to find appropriate message
DCDF 7E       ME_PRINT  LD   A,(HL)        ;A= next character of message
DCE0 23                 INC  HL
DCE1 FE80               CP   #80
DCE3 C8                 RET  Z             ;Return if data byte 80 found
DCE4 3003               JR   NC,ME_INPUT   ;Jump if other data byte found
DCE6 D7                 RST  #10           ;Print character
DCE7 18F6               JR   ME_PRINT
DCE9 6F       ME_INPUT  LD   L,A
DCEA 26DB               LD   H,INP_TABLE hi;HL points to allowed inputs
DCEC 3E3F               LD   A,"?"
DCEE D7                 RST  #10           ;Print a question mark
DCEF E5       ME_I_LOOP PUSH HL
DCF0 CDB5DC             CALL GET_CHR       ;Wait for key to be pressed
DCF3 CD1E03             CALL K_TEST        ;Convert to character code
DCF6 E1                 POP  HL
DCF7 FE20               CP   #20
DCF9 2811               JR   Z,ME_EXIT     ;Jump if "ESCAPE" pressed
                                           ;(Note that the stack is
                                           ;deliberately unbalanced)
DCFB E5                 PUSH HL
DCFC 4F                 LD   C,A           ;C= character just input
DCFD 7E       ME_CHECK  LD   A,(HL)        ;A= one of the allowed characters
DCFE 23                 INC  HL
DCFF 47                 LD   B,A
DD00 E67F               AND  #7F           ;Disregard bit 7
DD02 B9                 CP   C
DD03 2807               JR   Z,ME_EXIT     ;Jump if allowed character was input
DD05 CB10               RL   B
DD07 30F4               JR   NC,ME_CHECK   ;Jump if there are more
                                           ;characters to check
DD09 E1                 POP  HL
DD0A 18E3               JR   ME_I_LOOP     ;Try again
DD0C E1       ME_EXIT   POP  HL            ;Balance the stack
DD0D C36E0D             JP   CLS_LOWER     ;Clear lower part of screen
                                           ;and return
[**** Correction from Part 2]
DD0D F5                 PUSH AF
DD0E CD6E0D             CALL CLS_LOWER
DD11 F1                 POP  AF
DD12 C9                 RET
-----------------------------------------------------------------------------

You can test this program out by writing the machine code CALL MESSAGE
/ DEFB ?? / RET to any address and then calling it [Run "test01" in
LSD.TAP]. If you put a PAUSE 0 instruction after the USR instruction
then you will see the message at the bottom of the screen for as long
as you need. Notice that "Light Screen Designer", "Number of Radians?
" and "Machine Code Address?" will stay on the screen during PAUSE 0,
but all the others will have been erased by the machine code as soon
as the correct input was made.

In the next instalment of this program I shall actually start doing
something useful in the way of drawing pictures. Till then, SAVE all
the material we've covered so far and we'll add to it with each new
instalment.

Toni Baker
