← Back to Courses
module
9

Reading a whole line

Why this matters

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.

Somewhere to put it

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.

Reading a line into it

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.

What actually lands in the buffer

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.

Make the buffer a full line wide

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

Reading it back

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.

The whole program

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.

Change one thing

  • Delete the first 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?
  • Print A and B just before the read, keeping the fresh line. Do they turn up in the echo? Why not?
  • Change 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.
  • Change 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?
  • Put 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?

Exercises

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.

When it goes wrong

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.

Summary

  • 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.
  • Call 157 reads a line into the address in DE, echoing as it goes.
  • It hands back the screen row from column 0, not your keystrokes. Print 13 and 10 first so that row is empty.
  • The row is padded with spaces and the zero comes after the padding, so a full line gets written however little is typed. Reserve 64 bytes.
  • 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.

Next

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.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.