Classic Computer Magazine Archive COMPUTE! ISSUE 34 / MARCH 1983 / PAGE 198


Jim Butterfield, Associate Editor

Part I
Numeric Input

It's relatively easy to input strings in machine language. You must receive the characters and put them away neatly. But numbers are a different problem: the ASCII characters must be changed to binary and gathered into a single number.

It's usually best to gather the digits into a buffer rather than to try to process them as they arrive; in this way, you can cope with special characters such as delete and backspace. When the user signals that the input is complete (usually by pressing RETURN), your program can go to the buffer and work out the whole number.

Single Digits

One-digit numbers are fairly easy. If we understand that values coming in from keyboard or file are in ASCII, we're well on the way to doing the job.

ASCII represents the character zero as hexadecimal 30, decimal 48. To print the character zero in BASIC, you'd need to say PRINT CHR$(48). This may seem confusing to beginners (PRINT CHR$(0) doesn't print anything), but it works out well when you get used to it. So hex 30 represents zero; and, if we wish to do arithmetic on it, we must change it to binary zero. The easiest way to do this is with an AND command: AND #$0F will knock out the unwanted high bits.

This works on all the decimal digits: zero, hex 30, up to nine, hex 39. We should check each input character to insure that it is indeed a legitimate digit – otherwise, we may be converting a nonsense character, such as a comma.

Before we output, we must convert our binary value back to ASCII. If its value may be printed as a single digit (0 to 9), the job is once again easy. We simply use the ORA function to insert the missing bits back in: ORA #$30 changes binary to an ASCII digit.

Let's write a simple program to accept a single numeric digit. We'll use $FFE4 for GET, and $FFD2 for PRINT – this will work on all PET/CBM machines, VIC, and Commodore 64. Our coding goes:

TOP JSR $FFE4 (get a character)
    CMP #$30  (less than zero ASCII?)
    BCC TOP   (it's less, go back)
    CMP #$3A  (greater than nine ASCII?)
    BCS TOP   (it's greater, go back)
    JSR $FFD2 (echo to screen)
    RTS       (return to BASIC)

We have not converted our number to binary – just checked it to insure that it's in the right range. If our program were to continue, it might perform AND #$0F to convert to binary, and then store the value in A.

As a matter of amusement, let's convert the above program to BASIC POKEs and run it. Our BASIC equivalent goes:

100 DATA 32, 228, 255, 201, 48, 144, 249
110 DATA 201, 58, 176, 245, 32, 210, 255,
120 DATA 96
200 FOR J = 848 TO 862 : READ X : POKE J, X : NEXT J
300 FOR J = 1 TO 10 : SYS 848 : NEXT J

The first three lines give the machine language program in decimal. The individual instructions have been separated by spaces to make them more visible. Line 200 POKEs the program into the cassette area. Finally, line 300 invokes the machine language program ten times; you will be required to type ten numeric digits. If you try to type other keys, alphabetic or punctuation, the computer will ignore you.

Hexadecimal Input

Hex input is fairly easy. Since each digit is weighted at 16 times the following one, we need to multiply by 16, and that's easy to do, since 16 is a power of two. For example: to convert hex 1234, we must start with the one, multiply by 16, add the two, multiply by 16, add the three, multiply by 16, and finally add the four. If we did this on a calculator, we'd get 4660 as the result. Even though we're working in binary, we must do the same kind of calculation. Let's input four digits and convert them to a binary value. First, a subroutine to get a hex digit in ASCII and convert to binary 0-15:

HEXIN JSR $FFE4 (get a digit)
      TAX       (save a copy)
      CMP #$3A  (less than 9?)
      BCS BIG   (no, skip next)
      SBC #$2F  (convert 0-9)
  BIG CMP #$41  (A or more?)
      BCC SMALL (no, skip next)      SBC #$37  (convert A-F)
SMALL CMP #$10  (result too big?)
      BCS HEXIN (yes, try again)
      TAY       (copy value to Y)
      TXA       (restore ASCII)
      JSR $FFD2 (print digit)

There are some "tricks" to the above coding. The first subtraction seems to be taking off 47 ($2F), but it really subtracts the correct value of 48 since the carry is clear, which is a "borrow" in subtraction. The carry is set for the second subtraction, so the stated value of $37 (55) is used. All illegal characters are excluded, although it may take a little hand calculation to work out why.

That's the hard part. Now let's do the easy part — the actual hex input of four digits:

FOUR  JSR  HEXIN get character
      TYA        move value to A
      ASL   A 2..
      ROL   A 4..
      ROL   A 8..
      ROL   A 16
      STA  VALUE put result somewhere
      JSR  HEXIN fourth character
      ORA  VALUE combine with previous
      STA  VALLO store in low byte
      JSR  HEXIN get third character
      TYA        move value to A
      ASL   A    mult, by 2..
      ROL   A 4..
      ROL   A 8..
      ROL   A 16
      STA VALUE  put result somewhere
      JSR HEXIN  fourth character
      ORA VALUE  combine with previous
      STA VALLO  store in low byte

Not too hard? Multiplying by 16 is performed by four short instructions: a shift and three rotates. The rest involves combining the digits and putting them away.

Next time, we'll discuss decimal number input, which requires a somewhat more difficult multiplication by ten.