unsigned to ascii

The place for codemasters or beginners to talk about programming any language for the Spectrum.
dfzx
Microbot
Posts: 170
Joined: Mon Nov 13, 2017 6:55 pm
Location: New Forest, UK
Contact:

unsigned to ascii

Post by dfzx » Thu Oct 04, 2018 7:07 pm

I'm slowly learning Z88DK C and writing a game using it. I've recently added a score to the game which basically involves keeping a 16 bit value which is decremented on an interrupt. (i.e. the score goes down as you play, meaning the quicker you complete the level the better the score.) This score is printed on screen and is currently updated 10 times a second.

The problem is that the code to convert the value to an ASCII string for printing is slow. Slower than I expected, certainly. Z88DK has a utoa() function in the library ("unsigned to ASCII") which is hand coded assembler, but it takes a radix and uses a subroutine to do the division so it can handle big numbers. It's all a bit heavyweight for what I need.

So I thought I'd write my own, which I did. For those who know C:

Code: Select all

void my_utoa( uint16_t i, uint8_t* next_char )
{
  do
  {
    *next_char = (i % 10) + '0';
    next_char--;
    i = i / 10;
  } while(i);

  return;
}
But that compiles to rather slow and heavyweight assembly too. The modulo operator is a subroutine call, as is the division.

I was wondering if this utoa() operation is just bound to be slow on a Z80, or maybe there's a faster, lighter way to do it in assembly language? The requirement is to convert a number which I'm happy to restrict to the range 0-32767 into a 6 character string (including null terminator byte) with zeroes padding to the left. e.g. input of 320 would result in "00320\0". Speed is more important than code size at this point in time.

What approach would I take to do that operation in assembly language?
0 x

AndyC
Manic Miner
Posts: 251
Joined: Mon Nov 13, 2017 5:12 am

Re: unsigned to ascii

Post by AndyC » Fri Oct 05, 2018 8:28 am

One common solution is to use BCD to store the number, as it's a lot quicker to convert a BCD number into ASCII because you don't have to deal with any divides (just shifts) and the Z80 can do BCD arithmetic almost as fast as native numbers.
1 x

User avatar
R-Tape
Site Admin
Posts: 2578
Joined: Thu Nov 09, 2017 11:46 am

Re: unsigned to ascii

Post by R-Tape » Fri Oct 05, 2018 8:34 am

Is it an option to avoid the conversion altogether by only handling it as a string? It would be much quicker to check when a string is ‘00000’.
0 x

dfzx
Microbot
Posts: 170
Joined: Mon Nov 13, 2017 6:55 pm
Location: New Forest, UK
Contact:

Re: unsigned to ascii

Post by dfzx » Fri Oct 05, 2018 12:37 pm

Thanks for the input guys. I think it's dawned on me why so many games have non-numerical countdown or scoring systems. Numbers are tricky to deal with, as well as dull to look at. Gauges, sliders and half eaten turkeys have been deployed for more than one reason.

The BCD thing rang all sorts of 30 year old bells. :) I've not touched BCD since I moved on from the Speccy back in the 80s. I'll bear that one in mind, but for the game I think a bit more imagination in the scoring will make the problem go away.
0 x

spectron
Berk
Posts: 22
Joined: Thu Mar 29, 2018 2:27 pm

Re: unsigned to ascii

Post by spectron » Fri Oct 05, 2018 1:13 pm

As above, if it's a simple score counting down, just store it in consecutive bytes as ascii chars (48-57 decimal). To decrement the score just decrement the bottom char and if it goes to 47 ('/' char), set it back to 57 then go to the next char and decrement that. That way the score is always in the right format for printing.
0 x

User avatar
Metalbrain
Dizzy
Posts: 81
Joined: Thu Feb 15, 2018 2:14 pm

Re: unsigned to ascii

Post by Metalbrain » Fri Oct 05, 2018 1:22 pm

dfzx wrote:
Thu Oct 04, 2018 7:07 pm
The requirement is to convert a number which I'm happy to restrict to the range 0-32767 into a 6 character string (including null terminator byte) with zeroes padding to the left. e.g. input of 320 would result in "00320\0". Speed is more important than code size at this point in time.

What approach would I take to do that operation in assembly language?
I'd say the easiest solution would be to subtract powers of 10, something like this (not tested):

Code: Select all

	ld hl,NUMBER
	ld bc,result_location

	ld de,-10000
	ld a,47
loop1:
	inc a
	add hl,de
	jr c,loop1
	sbc hl,de
	ld (bc),a
	inc bc

	ld de,-1000
	ld a,47
loop2:
	inc a
	add hl,de
	jr c,loop2
	sbc hl,de
	ld (bc),a
	inc bc

	ld de,-100
	ld a,47
loop3:
	inc a
	add hl,de
	jr c,loop3
	sbc hl,de
	ld (bc),a
	inc bc

	ld de,-10
	ld a,47
loop4:
	inc a
	add hl,de
	jr c,loop4
	sbc hl,de
	ld (bc),a
	inc bc

	ld a,48
	add a,l
	ld (bc),a
	inc bc

	xor a
	ld (bc),a
1 x

Bizzley
Microbot
Posts: 124
Joined: Thu Nov 16, 2017 10:47 am

Re: unsigned to ascii

Post by Bizzley » Fri Oct 05, 2018 3:01 pm

Here's another version of the 'powers of 10' method. This one is rolled up with CALLs. Enter with HL holding number to convert.

Code: Select all

conv16bit	ld bc,mess16bit
		ld de,10000
		call conv16bit1
		ld de,1000
		call conv16bit1
		ld de,100
		call conv16bit1
		ld de,10
		call conv16bit1
		ld a,l
		jr conv16bit3
conv16bit1	ld a,-1
conv16bit2	inc a
		sub hl,de
		jr nc,conv16bit2
		add hl,de
conv16bit3	or 48
		ld (bc),a
		inc bc
		ret
mess16bit	ds 5,0
0 x
"He made eloquent speeches to an audience consisting of a few depressed daffodil roots, and sometimes the cat from next door."

AndyC
Manic Miner
Posts: 251
Joined: Mon Nov 13, 2017 5:12 am

Re: unsigned to ascii

Post by AndyC » Fri Oct 05, 2018 3:17 pm

spectron wrote:
Fri Oct 05, 2018 1:13 pm
As above, if it's a simple score counting down, just store it in consecutive bytes as ascii chars (48-57 decimal). To decrement the score just decrement the bottom char and if it goes to 47 ('/' char), set it back to 57 then go to the next char and decrement that. That way the score is always in the right format for printing.
Or even just bytes holding 0-9, so you can use the carry flag to spot a number rolling past 0 - it's not like your print routine has to use ASCII codes for 0-9 after all.
0 x

User avatar
Metalbrain
Dizzy
Posts: 81
Joined: Thu Feb 15, 2018 2:14 pm

Re: unsigned to ascii

Post by Metalbrain » Fri Oct 05, 2018 3:20 pm

Bizzley wrote:
Fri Oct 05, 2018 3:01 pm

Code: Select all

		sub hl,de
Unfortunately, that one doesn't exist.
0 x

Bizzley
Microbot
Posts: 124
Joined: Thu Nov 16, 2017 10:47 am

Re: unsigned to ascii

Post by Bizzley » Fri Oct 05, 2018 3:43 pm

Metalbrain wrote:
Fri Oct 05, 2018 3:20 pm
Bizzley wrote:
Fri Oct 05, 2018 3:01 pm

Code: Select all

		sub hl,de
Unfortunately, that one doesn't exist.
It does if you use the SjASMPLus Assembler. It's one of the "fake" instructions the assembler supports and then internally converts to an equivalent. I'm so used to using them now I forget that other assemblers don't support them so my bad. Replace it with :

or a
sbc hl,de

for compatibilty with other assemblers. Actually you don't really need the 'or a' in this case.
0 x
"He made eloquent speeches to an audience consisting of a few depressed daffodil roots, and sometimes the cat from next door."

Post Reply