Convert numbers to gfx

Page 1/2
| 2

By Daemos

Paragon (2044)

Daemos's picture

30-11-2014, 19:43

So here is the deal. I want to convert a number into loose digits.

So for example I have the number 123 I want this number converted into the byte 1, 2 and 3 in RAM.

So if the input is db 123
output should be db 1,2,3

This has to happen in assembly so no basic excel or any other tool. I allready have some ideas but they suck the life out of RAM or CPU.

Login or register to post comments

By Grauw

Ascended (10699)

Grauw's picture

25-02-2020, 01:53

In Synthesix I do it the crappy way :).

There’s a divide by 10 routine on MSX Banzai.

Lemme see if I can think of another way.

By Daemos

Paragon (2044)

Daemos's picture

30-11-2014, 19:58

the divide by 10 is way over my head. The crappy way looks less mathematic so more understandable. Lets see if I can figure the crappy way out.

By Grauw

Ascended (10699)

Grauw's picture

30-11-2014, 21:32

Essentially you just want to do two divisions by 10.

a)
Divide value by 10, remainder -> right digit
Divide quotient by 10, remainder -> middle digit, quotient -> left digit

Or,

b)
Divide value by 100, quotient -> left digit
Divide remainder by 10, quotient -> middle digit, remainder -> right digit

In Synthesix I use b), because it is more convenient for writing the output left-to-right. It’s crappy because I do this division-by-10 by just subtracting 10 in a loop. Dividing using shifts like shown on MSX Banzai should be faster on average. (see post below)

Probably the fastest way is to use a 768-byte lookup table.

An alternative is to store your numbers in memory using BCD encoding, and use the daa instruction to help adjust to BCD after doing additions / subtractions.

p.s. in Synthesix I use 1’s complement numbers so I don’t have a loop to subtract 100. Also I generate an ASCII string so I add "0" here and there or write spaces / "-". Makes it a bit harder to read for your case, but it should illustrate the general concept.

By Grauw

Ascended (10699)

Grauw's picture

30-11-2014, 20:39

Note for the first division, the shift-division is probably fastest (on average), whereas for the second one the number will never be higher than 25 so just doing sub 10 twice and checking the carry is faster.

By Grauw

Ascended (10699)

Grauw's picture

30-11-2014, 20:57

Grauw wrote:

Note for the first division, the shift-division is probably fastest (on average), whereas for the second one the number will never be higher than 25 so just doing sub 10 twice and checking the carry is faster.

Looks like I was mistaken there, and the loop-division I use in Synthesix is not actually as crappy as it seems.

Knowing in advance that the dividend is never higher than 99, as is the case in approach b), on average the loop-division runs a little faster (132 cycles, worst case 240 cycles) than the shift-division on MSX Banzai (153 cycles). Especially when there is a bias to lower numbers, as is the case here (value is either 0-99, 0-99 or 0-55).

The shift-division does run in constant time though, which is probably valuable in a real-time application like a game.

By Daemos

Paragon (2044)

Daemos's picture

30-11-2014, 23:12

Thank you very much for the info. I will see what I come up with and let you know how it works out.

This is the variant of the crappy method I have come up with and it works like a charm. Its fast enough for my stuff Smile

;heldenpoging in = a het getal out = de digits in numbertable
convertnumbertodigit:

  ;find first digit
  ld hl,numbertable
  ld b,0
.loop100:
  sub 100
  jp c,.done100
  inc b
  ld c,a ;save current value
  jp .loop100

;write data and set values for next digit  
.done100:
  ld a,b
  ld (hl),a
  inc hl ;next digit
  ld b,0
  ld a,c
.loop10:
  sub 10
  jp c,.done10
  inc b
  ld c,a
  jp .loop10

.done10:
  ld a,b
  ld (hl),a
  inc hl
  ld b,0
  ld a,c
  inc a
.loop1:
  dec a
  jp z,.done
  inc b
  ld c,a
  jp .loop1
  
.done:
   ld a,b
   ld (hl),a


  ret
  

By hit9918

Prophet (2927)

hit9918's picture

30-11-2014, 23:25

But what is it about? Display points?
I thought of having the points ready in ascii.
The thing done every frame is just the straight copy to vram in ascii.
And every now and then an enemy is shot and then do an ascii add to the points.
In this style:

add (hl)
if akku <= ascii9 then I am finished. store to (de).
else sub (ascii9+1) and go to higher letter and continue loop.

the display starts with
db ascii0, ascii0, ascii0

100 points is
db 1,0,0

storing big endian because vram transfer likes to go forward.

By Grauw

Ascended (10699)

Grauw's picture

01-12-2014, 00:14

Looks good. The loops could be a little faster by removing the jp c,.done10 and doing a jp nc,.loop10 at the end of the loop in stead, like I did in Synthesix. Initialise b with -1 and add a,10 after the loop to compensate for the extra subtraction.

(I just replaced that code in Synthesix with a lookup table by the way. I’m sure I’ll come to regret it later when I run out of ROM space Tongue.)

Did you consider BCD or hit9918’s approach (similar to BCD)?

By freshness79

Supporter (9)

freshness79's picture

01-12-2014, 01:56

I think you can do it a bit faster this way:

print3digit:
	ld hl,numbertable
	ld c,0			// Put $30 for ASCII char
	ld b,9			// Put $39 for ASCII char
.loop100:
	sub a,100
	jr C,.loop10
	inc c
	jr .loop100

.loop10:
	add a,10
	jr C,.done
	dec b
	jr .loop10
.done:
				// Put add a,$30 for ASCII char
	ld (hl),c
	inc hl
	ld (hl),b
	inc hl
	ld (hl),a
	ret

By freshness79

Supporter (9)

freshness79's picture

01-12-2014, 11:39

Even faster (thanks to Graw's hint):

print3digit:
	ld hl,numbertable
	ld c,-1			// Put $2f for ASCII char
	ld b,10			// Put $3a for ASCII char
.loop100:
	inc c
	sub a,100
	jr NC,.loop100
.loop10:
	dec b
	add a,10
	jr NC,.loop10
.done:
				// Put add a,$30 for ASCII char
	ld (hl),c
	inc hl
	ld (hl),b
	inc hl
	ld (hl),a
	ret

And, btw, if you save B after the first loop you can avoid clobbering C.

Page 1/2
| 2