.png)
One key at a time is enough to steer a game, and no use at all for a name, a command or an answer. For those you need a whole line - and a line is more than one byte, which means it has to live somewhere.
That somewhere is the real subject of this section. Reading text is the excuse; storing and walking through data is the skill, and every list of sprites, every level map and every score table you write later is the same idea.
A register holds one byte. Text needs a row of them, and you reserve a row
with DEFS:
buffer: DEFS 64 ; 64 bytes, all zero, named buffer
buffer is a label, exactly like the ones you have been jumping to - but this
one names storage rather than code. Nothing is ever executed there. It is a
place to keep things.
Two rules come with it.
Put it where execution cannot reach. Same rule as subroutines in S7.
After the RET that ends your program is the safe place.
Make it big enough. More on that below, and it is less obvious than it sounds.
Call 157, officially named ZGETLN, reads a line. You tell it where to put
the text by loading the address into DE first:
LD DE,buffer ; where to put it
RST 8
DEFB 157 ; ZGETLN - read a line
It waits until Enter is pressed, and echoes what is typed as it goes, so you get a working input prompt for nothing.
Here is the part that will bite you, and it is worth getting straight before you write anything.
The routine gives you the whole screen line, not the keys that were pressed. It copies the row the cursor is sitting on, starting from column zero, whatever happens to be on it.
Type HI at the prompt and the buffer starts:
'>' 'H' 'I' ' ' ' ' ' ' ...
That > is the prompt. You did not type it. It is on the row, so it is in
your buffer.
Print something yourself before reading, and that arrives too. A program that
prints AB and then reads a line gets '>' 'A' 'B' 'H' 'I' - your own output
handed back to you as though the user had typed it.
And the line is padded with spaces to its full width. The text does not end where you stopped typing; it runs on as spaces to the end of the row. The zero byte that marks the end sits after that, about forty bytes in, however little was typed.
So two characters typed means forty bytes written. Which brings us to the buffer size.
Reserve 64 bytes and you are fine. Reserve 16 and the routine writes past the end of it - not because the user typed too much, but because the routine always writes a full line.
That overrun is silent. Nothing warns you. It simply overwrites whatever you had after the buffer, and the bug shows up somewhere else entirely, later, looking like something unrelated.
buffer: DEFS 64 ; a full line, with room to spare
Walking through stored bytes is the same three instructions every time:
LD HL,buffer ; point at the first byte
loop: LD A,(HL) ; fetch it
CP 0 ; end?
JR Z,done
RST 8
DEFB 158 ; ZOUTC - do something with it - here, print it
INC HL ; move to the next byte
JR loop
done:
HL holds an address and LD A,(HL) fetches the byte at it - the brackets
mean "the contents of", as in S4. INC HL steps to the next byte. That is a
pointer, and this loop is how you will walk every list of data in the rest of
the course.
Printing 13 and 10 before reading puts the cursor at column 0 of a blank row, so the buffer starts with the typed text and nothing else. That single detail is the difference between this working and not:
ORG 256
LD A,13 ; start a fresh, empty line
RST 8
DEFB 158 ; ZOUTC
LD A,10
RST 8
DEFB 158 ; ZOUTC
LD DE,buffer ; read the line into our buffer
RST 8
DEFB 157 ; ZGETLN
LD A,13 ; another fresh line for the echo
RST 8
DEFB 158 ; ZOUTC
LD A,10
RST 8
DEFB 158 ; ZOUTC
LD HL,buffer ; walk the buffer and print it
loop: LD A,(HL)
CP 0
JR Z,done
RST 8
DEFB 158 ; ZOUTC
INC HL
JR loop
done: RET
buffer: DEFS 64
Type HELLO and press Enter, and HELLO comes back on the line below -
followed by the rest of the line as spaces, which you cannot see but are
there.
Note what the fresh line buys you: no > in the buffer, and nothing of your
own output either. If a program prints anything on the row it then reads
from, it gets it back.
LD A,13 / LD A,10 pair, so the read happens on the
prompt's row. What appears in the echo that you did not type?A and B just before the read, keeping the fresh line. Do they turn
up in the echo? Why not?DEFS 64 to DEFS 8 and put DEFB 255 after it. Run it, type two
characters, and then work out what happened to that 255. This is worth doing
once so the silent overrun stops being an abstraction.CP 0 to CP 32 - stop at the first space instead of the zero. What
does the echo look like now, and is that better or worse?LD C,5 before the read and check C in the register line afterwards.
Did the call leave it alone? Would you rely on that?9.1 - Label the echo. Print You typed: and then the line, on separate
rows. The catch is in this section: work out why the message must not be on
the row you read from, and where it has to go instead.
9.2 - How long was it? Count the characters typed and print the total. Watch out - the padding means counting to the zero gives you the line width every time. Stop at the first space instead, and say in a comment why.
9.3 - Backwards. Echo the typed line in reverse. You will need to find the
end first, then walk back with DEC HL.
9.4 - Just the letters. Echo the line but skip anything that is not a
capital letter. Codes 65 to 90 are the letters; you have CP and two
conditional jumps.
9.5 - A command. Read a line and print YES if the first character is
Y. Remember from S8 that letter keys arrive as capitals. Then extend it to
check the first two characters - and notice how much more work each extra
character is, which is why comparing whole words gets a section of its own.
| What you see | What it means |
|---|---|
A > at the start of your echoed text |
The read happened on the prompt's row. Print 13 and 10 first. |
| Your own output echoed back as if typed | Same cause. Anything on that row is part of the line. |
| The echo runs on for ages after your text | You are printing the padding. The zero is at the end of the line, not the end of the text. Stop at the first space if you want just the words. |
| Something elsewhere in the program has changed for no reason | The buffer was too small and the routine wrote past it. Give it a full line - 64 bytes. |
| The program prints nothing and waits | Call 157 is waiting for Enter. |
DEFS n reserves n bytes. A label on it names storage, not code, and it
must sit where execution cannot reach - after the final RET.DE, echoing as it goes.LD A,(HL) fetches the byte HL points at; INC HL moves on. That pair
walks any run of bytes in memory, and you will use it constantly.S10. Numbers bigger than a byte. A register holds 0 to 255, which is not enough for a screen coordinate. Next you use registers in pairs, move 16-bit values in and out of memory - and draw a line, which is the first thing in this course that is not made of characters.