-5

Can someone explain to me if there is a way to get one decimal value (0-9)? I need it for a project I'm working on. I wanted to do MOV SI, [CX:DX], but then I had a feeling that would not work. So I decided to search up online, but the subject I wanted wouldn't show up. So can someone tell me if there is a way to get 1 decimal value from 0 to 9?

I have tried: MOV BL, 1000 MOV AH, 00h DIV DX

Is there something I'm doing wrong?

Using compiler NASM with 16-Bit code.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Get from what? Anyway, generally speaking, use the remainder from dividing by 10. – Jester Dec 20 '20 at 00:25
  • Is it decimal or hex? What do you mean "get"? Get from where, and do what with it? – Nate Eldredge Dec 20 '20 at 00:25
  • What I meant was: Get 1 decimal value from CX, and convert it into its hex value. Example: CX has value 1. Converting it into hex would be 01h. – Hornet's Channel Dec 20 '20 at 00:26
  • 1
    That's not much more clear. 1 decimal digit 0-9 has the same value in hex, so there is nothing to convert. And to get a digit, use modulo 10. You might want to provide sample input in `cx` and expected output in `si` so we understand better. – Jester Dec 20 '20 at 00:28
  • 1 is 1, in any base. 0-9 is 0-9 in any base >= 10, so in hex too. – Jester Dec 20 '20 at 00:29
  • Yeah, but I am trying to get the clock ticks from midnight, and get the first digit of that. I know how to get the clock ticks, but I don't know how to get the first digit. – Hornet's Channel Dec 20 '20 at 00:29
  • Provide a better example, not cx=1 because you don't need to do anything with that. What do you want if, say, cx=1234? – Jester Dec 20 '20 at 00:30
  • The returned value would be 1, because 1 is the first digit of that value. The next digit would be 2, and so on. – Hornet's Channel Dec 20 '20 at 00:30
  • 1
    So you want the most significant decimal digit? One option would be to divide input by 10000. If the quotient is not zero, that is your result. Otherwise divide the remainder by 1000, then 100, then 10. Note that working in reverse is simpler, if you are happy with that you only need to divide by 10 every time but that gives you digits in reverse order. – Jester Dec 20 '20 at 00:32
  • That is giving me an Invalid Opcode error. Do you mean `DIV DX, 1000' or: – Hornet's Channel Dec 20 '20 at 00:40
  • `MOV CX, 1000` and then `DIV DX, CX`? – Hornet's Channel Dec 20 '20 at 00:41
  • I'll try `DIV DX`. – Hornet's Channel Dec 20 '20 at 00:43
  • All I'm seeing here is a black screen. – Hornet's Channel Dec 20 '20 at 00:45
  • Consult an instruction set reference about how to use `DIV`. It takes dividend in `DX`,`AX` pair so you will need to arrange that. – Jester Dec 20 '20 at 00:58
  • 2
    Sure you'll see a black screen unless you actually output the result. But that's a whole separate question. – Nate Eldredge Dec 20 '20 at 00:59
  • I did try to output the result. – Hornet's Channel Dec 20 '20 at 01:02
  • I think there is something wrong with the print function. – Hornet's Channel Dec 20 '20 at 01:07
  • 1
    Re: the title question: [How to convert a binary integer number to a hex string?](https://stackoverflow.com/q/53823756), or [Printing hex values in x86 assembly](https://stackoverflow.com/q/18879672) (16-bit). But that seems *not* to be what you're doing? [How do I print an integer in Assembly Level Programming without printf from the c library?](https://stackoverflow.com/a/46301894) is for extracting base 10 digits from integers in registers, or [Displaying numbers with DOS](https://stackoverflow.com/q/45904075) – Peter Cordes Dec 20 '20 at 02:44
  • `MOV SI, [CX:DX]` will definitely not work. Did you mean `cs:`, to load from an address in the code segment? DX is also not valid as a base register in a 16-bit addressing mode. So that syntax is nonsense, and loading from memory doesn't match the text of what you're talking about. From the text of your question, I have no idea what you want. Your code using `div dx` also makes no sense, except as a guaranteed way to raise a divide-exception interrupt regardless of the value of DX. See [this](//stackoverflow.com/q/12231784) and [this](https://stackoverflow.com/q/63273843). – Peter Cordes Dec 20 '20 at 02:45
  • *"So can someone tell me if there is a way to get 1 decimal value from 0 to 9?"* are you asking how to get the ASCII character value for a digit `0-9`? If so, See [ASCII Table & Description](http://www.asciitable.com/). You simply `add` or `or` with `0x30` to go from decimal to ASCII and `sub` to go from ASCII to decimal. – David C. Rankin Dec 20 '20 at 04:42
  • This all seems like an XY problem. When you get the 32-bit value (clock ticks from midnight) what are you intending to do with the digits? Print them and if it is to print them what do you want them to look like on the screen? I guess I am curious what your final objective is with the timer ticks. – Michael Petch Dec 20 '20 at 06:17
  • I am intending to print the digits by comparing them with the hexadecimal values of digits. Like 1 becomes 0x31 (hexadecimal value for the number '1'), 2 becomes 0x32 (hexadecimal value for the number '2'), and so on. – Hornet's Channel Dec 21 '20 at 23:42
  • @Peter Cordes What is your point? I just want to grab one hexadecimal value from `[CX:DX]`, For example: 1234h becomes 01h, next value is 02h, and so on. – Hornet's Channel Dec 21 '20 at 23:44
  • Then explain it that way in your question, instead of using existing syntax that has a different meaning (`[]` means memory operand, `:` separates segment from offset in an address), with a destination register that definitely can't hold what you want (SI is only a 16-bit register). My point is that `MOV SI, [CX:DX]` doesn't tell us anything about what you want to do, because you haven't explained the meaning for the syntax you're inventing. Anyway, now that you explained in comments, we can point this question at already-answered ones. – Peter Cordes Dec 22 '20 at 00:31
  • Since 32 and 16 are powers of 2, you can convert CX and DX separately to 4 hex digits each. Bits in one register don't affect the digits for the other register even when viewing the whole thing as a 32-bit number. (Unlike converting to base 10). [How to convert a binary integer number to a hex string?](https://stackoverflow.com/q/53823756) has more detail about that. – Peter Cordes Dec 22 '20 at 00:36

1 Answers1

1

From comments, it seems like what you want is something like:

;Input
; cx    Clock ticks since midnight
;
;Output
; ax    Most significant non-zero decimal digit of clocks since midnight (unless
;       clocks since midnight is zero, where zero will be returned)
;
;Trashed
; none (contents of all registers not used for outputs are preserved)

get_most_significant_decimal_digit:
    mov ax,cx           ;ax = numerator = clocks since midnight
    cmp ax,10           ;Is it already small enough?
    jb .done            ; yes

    push bx
    push dx

    mov bx,10           ;bx = divisor (constant value 10)
.continue:
    xor dx.dx           ;dx:ax = numerator zero extended to 32 bits
    div bx              ;ax = numerator / 10

    cmp ax,10           ;Is it small enough now?
    jae .continue       ; no, keep going

    pop dx
    pop bx
.done:
    ret

Note: For maximum performance it'd probably be better to use a different divisor for each division, with divisors chosen to minimize the number of divisions. E.g. "if not smaller than 10000 then divide by 10000" then "if not smaller than 100 then divide by 100" then "if not smaller than 10 then divide by 10". It's hard to tell if the performance improvement is worth the extra complexity (and worse code readability) that would be required.

On second thought; it's not really that messy (untested, NASM syntax):

    section .data
const10000:  dw 10000
const100:    dw 100
const10:     dw 10
    section .text

;Input
; cx    Clock ticks since midnight
;
;Output
; ax    Most significant non-zero decimal digit of clocks since midnight (unless
;       clocks since midnight is zero, where zero will be returned)
;
;Trashed
; none (contents of all registers not used for outputs are preserved)

get_most_significant_decimal_digit:
    mov ax,cx           ;ax = numerator = clocks since midnight
    push dx

    cmp ax,10000
    jb .l1
    xor dx,dx
    div word [const10000]
.l1:
    cmp ax,100
    jb .l2
    xor dx,dx
    div word [const100]
.l2:
    cmp ax,10
    jb .l3
    xor dx,dx
    div word [const10]
.l3:
    pop dx
    ret

For this alternative, "worse case" is 3 divisions and 3 branches (instead of 4 divisions and 5 branches for previous approach).

Brendan
  • 35,656
  • 2
  • 39
  • 66
  • For performance you'd only divide once, after either a chain of compare/branch. Or with a loop that multiplies a value by 10 (starting with 1 or 10) and comparing, stopping when you find a power of 10 that's greater than the input and using the previous one. (Or the multiply by 10 overflows.) – Peter Cordes Dec 20 '20 at 04:40
  • In your update, you can hoist `xor dx,dx` ahead of the branch chain. Interesting tradeoff of code-size vs. number of `div` executions; that could be ok if `div` isn't vastly slower than mul or code-fetch, and is good for a lot of cases. – Peter Cordes Dec 20 '20 at 04:42
  • @PeterCordes: You could have a tree of branches with each "end node" in the tree doing one division; but that would be "extra complexity (and worse code readability)" and (depending on how often it's called) might even make performance worse (e.g. mispredicted branches costing more than the 2 divisions did). – Brendan Dec 20 '20 at 04:52
  • @PeterCordes: For absolute maximum performance (if it's extremely frequently executed); you could have a 64 KiB table (like "`result_byte = table[ticks];`") but that has its own problems in real mode (would need to a whole segment for the table). – Brendan Dec 20 '20 at 04:56
  • Yeah true. For 16-bit code, it's sometimes interesting to consider how it would perform on a real 8086 or 8088, where division was absolutely horrible and there was no branch prediction. That's partly what I had in mind, or to strike a balance across the spectrum of history? Or avoid the worst case on the slowest CPUs that could run it. If you had 32-bit code, you could BSR to find the leading set bit, and use that to index a table (and fixup if below that value), like adapt something from [Fastest way to compute order of magnitude in x86 assembly](//codegolf.stackexchange.com/q/47290) – Peter Cordes Dec 20 '20 at 05:01
  • A chain of branches could be good if the distribution of numbers is uniform: most are large. And BTW, [How do I determine the number of digits of an integer in C?](https://stackoverflow.com/a/18027868) has a kind of hacky example of an idea I remembered seeing with a table, with benchmarks for that vs. branchy binary search, for some machine. I didn't look to see how well-crafted the benchmark is, but I think an array of random numbers so it shouldn't be just training the branch predictor. The lookup table will stay hot in cache, though. – Peter Cordes Dec 20 '20 at 05:10
  • 1
    @PeterCordes: Yes, plenty of alternatives (another is something like "`if(ticks < 10) return ticks; else return table[ticks/2 - 5];`" to reduce table size); but mostly I'm just waiting for the original asker to realize their question doesn't reflect what they actually want (either because they want "integer to decimal ASCII" for all digits, or because they remember that BIOS "ticks since midnight" is actually a 32-bit value in `cx:dx` and not 16-bit). ;) – Brendan Dec 20 '20 at 05:32
  • I don't think a table larger than a couple cache lines is worth considering, if you're thinking about modern CPUs. Simply not worth it to miss in cache for most calls, probably even if it costs a branch miss or two per call. Especially not when a cheap `bsr` to start with can shrink the table tremendously. (Except we can't do that in code that has to still work on ancient pre-386 CPUs.) – Peter Cordes Dec 20 '20 at 07:13
  • What happened to `const1000: dw 1000` ? This stage is missing from the code. Also since the ticks from midnight is actually a dword stored in `CX:DX`, the calculation will be even more complex (and most certainly for the OP). – Sep Roland Dec 20 '20 at 14:37
  • 2
    @SepRoland: For that code, `const1000: dw 1000` isn't needed (values from 1000 to 9999 would be divided by 100, then divided by 10). – Brendan Dec 20 '20 at 15:26