Here's the second major routine needed to make working with decimal numerical input possible.
Get_Number parses through the text input buffer looking for numerical ascii codes that fall in the range 0x30 to 0x39.
These correspond to the digits 0 to 9, so the first thing you must do is subtract 0x30 (decimal 48) from the incoming character to turn it into a decimal digit. It is convenient to preload one of the working registers with 0x30 (and keep it there) and in this application I choose R3 to contain 0x30.
Once you have your first decimal digit in the accumulator, you need to multiply it by 10. Then you take your second digit, add it to the sum in the accumulator and multiply by 10 again. You continue this process until you have added the last digit to the total, and the accumulator will hold the decimal equivalent of the numerical string in the text buffer.
However, you have to know where to stop multiplying by 10, so that the last digit in the string is just added . To do this you have to continuously look ahead at the next character in the input buffer and determine if it is a numerical character or not. If it's not a number, you skip the times 10 multiplication and jump to a final addition.
Here is the resulting code - hand assembled for Suite -16.
// 0x0090 --------------------------------NUMBER------------------------------------- 0x1300, // SET R3 0x30 Preload R3 with decimal 48 0x0030, 0x1200, // SET R2, 0x0A Preload R2 with decimal 10 0x000A, 0x1100, // SET R1, 0x0200 text buffer start 0x0200, 0x4100, // LD AC, @R1 get first character from buffer 0x3400, // Store R0 in R4 0xE100, // INC R1 0x4100, // LD AC, @R1 get next character - and test to see if it is a number 0xB300, // Subtract R3 Is it bigger than 0x30? 0x02AA, // BLT Not a Number 0xB200, // Subtract R2 0x0A 0x03AA, // BGE Not a Number 0x2400, // Get original character R0 back from R4 0xB300, // Subtract R3 to form a digit // 0x00A0 --------------------------------------------------------------------- 0xA700, // get the accumulating total from R7 - ready to multiply 0x3500, // Store R0 in R5 Digit is now in R5 0xA500, // ADD R5 Accumulator R0 = (2 * digit) 0x3600, // Store R0 in R6 - this is the "Times 10" routine 0xA600, // ADD R6 4 X 0xA600, // ADD R6 6 X 0xA600, // ADD R6 8 X 0xA600, // ADD R6 10 X 0x3700, // Store R0 in R7 R7 is accumulation of all the digits multiplied by powers of 10 0x0096, // BRA 0x0096 Get the next digit 0x0F00, // Not a number address = 0xAA 0x2400, // Get last digit R0 back from R4 0xB300, // Subtract R3 0xA700, // Add the accumulated sum from R7 - decimal number is now it R0 0x1700, // Don't forget to clear R7 0x0000, // 0x00B0 --------------------------------------------------------------------- 0x0802, // CALL 0x0002 PRINTNUM 0x0000, // BRA START
Incidentally, a functionally similar routine coded in MSP430 assembly language took 25 16-bit words (although in MSP430 ASM it's only 16 instructions). I'm quite happy with this implementation by comparison - as I use 8 words to initialise registers.
One thing that would be useful in the instruction set - would be immediate operations on the accumulator with the short-constant held in the Payload-byte.
There are several occasions when handling ascii characters, that you want to test against some value and modify the contents of the accumulator. Being able to add or subtract an 8-bit literal from the accumulator without having to use another register would be a big advantage.
Whilst some might think that hand-assembly must be some form of mental masochism, it's actually not that difficult.
Actually writing the code takes surprisingly little time. What does take the time is thinking how to write the algorithm efficiently using the instructions and registers available, and the time spent recompiling and testing the code.
You will see that I am listing my code in little blocks of 4 instructions, with a commented dividing line between every 16 instructions.
This allows me to arrange the code into easily manageable blocks and keep tally of exactly what address the instruction will be coded at.
Routines must for the moment be placed at fixed addresses ( non-relocatable) as I only have absolute branching. It's a minor inconvenience for the moment, and it will probably be changed when I revisit the branching mechanism.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.