Hi, so I'm learning how to do multiprecision math and stuff, and I can usually figure that out, but I just realized, I have no idea how to actually display those dword answers. I can't seem to find the answer here or on google either, so could someone please share how to do it?
You mean 32-bit numbers? It's not that complicated, but you'll have to convert the numbers into decimal. The easiest way is just to subtract the digits you want to display. For example, if you have 924,332,503, you'll start with 1,000,000,000 (the highest possible 32-bit digit). I assume you already have routines to handle 32-bit math? If not, it's not terribly complicated. So first you'll count how many times you can subtract 1 billion before getting a carry. Here it's a 0, so you'll store a 0. Next count how many times you can subtract 100 million before a carry, then 10 million, etc. You might want to add $80 (EDIT: see ASCII codes) to the value before you store it, that way you can just use _vPuts or whatever you want to display the string (make sure to put a 0 at the end).
Well to be honest, I'm confused by what you said... How do you convert a 32-bit number in hexadecimal into decimal? Lets say i had $EA18A644, how do you do that? and another thing, for doing dword rotations, asm28days says:
The code to do a rotation depends on the type of rotation wanted.
For RL-type rotation:
Code:
But doesn't that just screw up the LSB of each byte? I thought that you should start with the LSB ($B3), and increase to the MSB ($32), so your carry contains the rotated MSB from the previous byte. could you explain how that works?
The code to do a rotation depends on the type of rotation wanted.
For RL-type rotation:
Code:
LD HL, dword+3
RL (HL)
DEC HL
RL (HL)
DEC HL
RL (HL)
DEC HL
RL (HL)
dword: .DB $B3, $90, $12, $32
But doesn't that just screw up the LSB of each byte? I thought that you should start with the LSB ($B3), and increase to the MSB ($32), so your carry contains the rotated MSB from the previous byte. could you explain how that works?
-
chickendude
- Expert (Posts: 502)
- 18 Apr 2013 01:01:08 am
- Last edited by chickendude on 04 Feb 2016 11:01:14 pm; edited 1 time in total
Yeah, that depends on how you're storing your numbers. If you're following the usual z80 little-endian pattern, you're right, you'd start at the left (dword, the LSB) and work your way up (inc hl) to the MSB. Their 32-bit RL seems to be for big-endian numbers.
Let's start with something a little simpler, converting a 16-bit hex number into decimal. Say we have $A644. The maximum 16-bit number only goes up into the 10s of thousands, so first we'll subtract 10,000 until we get a carry. 10,000 = $2710. So:
Code:
So the ten thousands digit is 4, now we repeat subtracting 1,000 ($3E8):
Code:
So now we have 42,XXX. Repeat with the 100s, 10s, and 1s.
A simple code to do this might look like:
Code:
If you're going to work with 32-bit numbers, it'll be a little more complicated since you'll need 32-bit addition/subtraction. I've only ever bothered with 24-bit arithmetic, but 32-bit shouldn't be much different. Also, you'd start with the billions, then hundred millions, ten millions, etc. down to the ones/single digits. One billion in hex is "$3B9ACA00", so you'd subtract $3B9ACA00 from $EA18A644 until you got a carry. The 4th subtraction would give you a carry, so you would store 3.
For a 32-bit subtraction, you could try:
Code:
You should be able to use this in the little routine i gave above. I hope i haven't confused you even more, if you've still got questions please ask. Also, have you worked with the shadow registers before? (exx/ex af,af') If you're going to use the shadow registers, make sure you DI (disable interrupts) first, otherwise you'll likely get incorrect values/crash, 'cuz the interrupts make use of the shadow registers. exx swaps the values of hl, bc, and de with their shadow registers, ex af,af' swaps af with af'. Both instructions only use 4 t-states and take up 1 byte, so they're really quick and a nice way to get extra registers when you need them.
Let's start with something a little simpler, converting a 16-bit hex number into decimal. Say we have $A644. The maximum 16-bit number only goes up into the 10s of thousands, so first we'll subtract 10,000 until we get a carry. 10,000 = $2710. So:
Code:
$A46A 0
-$2710
------
$7D5A 1 no carry, so repeat
-$2710
------
$564A 2 ...
-$2710
------
$2F3A 3 ...
-$2710
------
$ 82A 4 ...
-$2710
------
-$1EE6 4 carry, so don't add 1
Code:
$82A 0 ...
-$3E8
-----
$442 1 no carry
-$3E8
-----
$ 5A 2 no carry
-$3E8
-----
-$38E 2
A simple code to do this might look like:
Code:
num2String:
exx
ld hl,table ;where we will store the result
exx
ld de,-10000 ;check how many 10,000s units there are
call dN_b2d
ld de,-1000 ;check how many 1,000s units there are
call dN_b2d
ld de,-100 ;hundreds
call dN_b2d
ld de,-10 ;tens
call dN_b2d
ld de,-1 ;single digits
call dN_b2d
exx
ld (hl),$FF
ld hl,table ;where the string is stored
ret
dN_b2d:
ld a,-1 ;if there is no carry the first run through, # = 0
dN_b2dloop:
inc a ;each iteration increase accumulator by 1
add hl,de
jr c,dN_b2dloop
or a
sbc hl,de ;225 - 100 = 125 - 100 = 25 - 100 = -75. This adds 100 again so we can check the next digits.
exx
ld (hl),a
inc hl
exx
ret
If you're going to work with 32-bit numbers, it'll be a little more complicated since you'll need 32-bit addition/subtraction. I've only ever bothered with 24-bit arithmetic, but 32-bit shouldn't be much different. Also, you'd start with the billions, then hundred millions, ten millions, etc. down to the ones/single digits. One billion in hex is "$3B9ACA00", so you'd subtract $3B9ACA00 from $EA18A644 until you got a carry. The 4th subtraction would give you a carry, so you would store 3.
For a 32-bit subtraction, you could try:
Code:
;h'l'hl - b'c'bc
;h'l'hl = $EA18A644
;b'c'bc = $3B9ACA00 (1,000,000,000)
ld hl,$A644 ;LSB into HL
ld bc,1000000000 & $FFFF ;LSB into BC
exx
ld hl,$EA18 ;MSB into H'L'
ld bc,1000000000 >> 16 ;MSB into B'C'
exx
;now subtract
or a
sbc hl,bc
exx
sbc hl,bc
exx
That's a great explanation, chickendude. If I get around to updating ASM in 28 Days this summer, perhaps I'll ask if I can incorporate a version of that explanation (and code) into the tutorial.
That is a really good explanation! I haven't used shadow registers yet, (I'm a super noob) but the theory, and top examples make perfect sense. I just wish there was a faster way, haha. Thank you very much!
It's really not that slow, though the 32-bit routine would be quite a bit slower than the 16-bit one. But if you code your own number displaying routine (ie, instead of using _vPuts/one of the bcalls) the final code might even be faster than drawing a normal string with _vPuts. But if you want a quicker way (for example, if you're going to be drawing the high score to the screen every frame) you might want to look into storing the string directly in decimal, either using one byte per digit or using half-bytes (nibbles), ie 1553923 would be stored as:
.db 3,2,9,3,5,5,1 (or .db 1,5,5,3,9,2,3)
or:
.db $32,$93,$55,$10 (or $01,55,39,23)
Either way, it'll all happen in the blink of an eye.
@Kerm: i just hope whatever i've written is useful. The more information we can share, the better (ie. do whatever you want with the code/explanations!)
.db 3,2,9,3,5,5,1 (or .db 1,5,5,3,9,2,3)
or:
.db $32,$93,$55,$10 (or $01,55,39,23)
Either way, it'll all happen in the blink of an eye.
@Kerm: i just hope whatever i've written is useful. The more information we can share, the better (ie. do whatever you want with the code/explanations!)
Here's a 24-bit number-to-string routine:
Code:
I chose to add negative numbers, but you could just as easily subtract positive numbers. You can see how the routine's starting to get a bit larger If you had a multiplication routine, maybe you could just divide by 10 and loop however many digits you wanted to check
EDIT: Oops, left my test values in
EDIT2: Made some small optimizations/fixes.
Code:
;#######################
;#NUM2STR
;# Convert number in AHL to a string
;# input: ahl = number to display
;# output: hl = pointer to converted string
;#######################
num2str:
ld ix,numberString ;where we will store the result
;-10,000,000
ld c,$67
ld de,$6980
call b2d ;count how many 10,000,000s there are
;-1,000,000
ld c,$F0
ld de,$BDC0
call b2d
;-100,000
ld c,$FE
ld de,$7960
call b2d
;-10,000
inc c ;ld c,$FF
ld de,-10000
call b2d
;-1,000
ld de,-1000
call b2d
;-100
ld de,-100
call b2d
;-10
ld e,-10
call b2d
;-1
ld e,-1
call b2d
ld (ix),a ;put the terminating zero
ld hl,numberString-1 ;where the string is stored
inc hl
ld a,(hl)
cp '0' ;if it's a 0, skip it
jr z,$-4 ;repeat until we find a non-zero number
or a
jr nz,$+3 ;if all numbers are zeros, move hl back one
dec hl ;.. to display one zero
ret
;#######################
;#B2D
;# Convert the units of a 24-bit binary
;# number in AHL into decimal
;# input: ahl = number to display
;# cde = negative value of units to check (if you
;# want to check 10s, cde should equal -10)
;# ix = string pointer to store the unit
;#######################
b2d:
ld b,-1 ;if there is no carry the first run through, # = 0
b2dLoop:
inc b ;each iteration increase accumulator by 1
add hl,de
adc a,c
jr c,b2dLoop
sbc hl,de ;225 - 100 = 125 - 100 = 25 - 100 = -75. This adds 100 again so we can check the next digits.
sbc a,c
set 4,b
set 5,b ;b+$30
ld (ix),b
inc ix
ret
EDIT: Oops, left my test values in
EDIT2: Made some small optimizations/fixes.
Another method is to divide by 10 and display the remainder and keep repeating this until your number becomes 0. The tricky part with this then becomes that each digit you extract is in reverse order! So the first digit is 1s place, second is 10s place, et cetera. If you need the string stored somewhere, though, that makes this problem very easy to get around by just storing it in reverse in memory. The nice advantage to this is that you can very easily display the number in any base that you want:
Code:
Hopefully there are no typos and I am pretty sure there is a tricky optimisation that I left out using DAA, but I am not sure how to implement it.
Code:
DrawNumber:
dec c
ret z
inc c
ret z
dec ix
ld (ix+1),0
DispNumBase32:
;Inputs:
; C is the base (use 2 to 36)
; DEHL is the number to display
; IX is the end of where to store the number string
;
ld b,32
xor a
add hl,hl
rl e \ rl d
rla
cp c
jr c,$+4
inc l
sub c
djnz $-11
add a,30h
cp 3Ah
jr c,$+4
add a,7
dec ix
ld (ix),a
ld a,h
or l \ or d \ or e
jr nz,DispNumBase32
push ix
pop hl
bcall(_PutS)
ret
Hopefully there are no typos and I am pretty sure there is a tricky optimisation that I left out using DAA, but I am not sure how to implement it.
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.
» Go to Registration page
» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
Advertisement