Search code examples
assemblyx86bcd6502

6502 assembly binary to bcd - is that possible on x86?


I have a few questions regarding this code:

; Convert an 16 bit binary value to BCD
;
; This function converts a 16 bit binary value into a 24 bit BCD. It
; works by transferring one bit a time from the source and adding it
; into a BCD value that is being doubled on each iteration. As all the
; arithmetic is being done in BCD the result is a binary to decimal
; conversion. All conversions take 915 clock cycles.
;
; See BINBCD8 for more details of its operation.
;
; Andrew Jacobs, 28-Feb-2004

        .ORG $0200

BINBCD16:   SED     ; Switch to decimal mode
        LDA #0      ; Ensure the result is clear
        STA BCD+0
        STA BCD+1
        STA BCD+2
        LDX #16     ; The number of source bits

CNVBIT:     ASL BIN+0   ; Shift out one bit
        ROL BIN+1
        LDA BCD+0   ; And add into result
        ADC BCD+0
        STA BCD+0
        LDA BCD+1   ; propagating any carry
        ADC BCD+1
        STA BCD+1
        LDA BCD+2   ; ... thru whole result
        ADC BCD+2
        STA BCD+2
        DEX     ; And repeat for next bit
        BNE CNVBIT
        CLD     ; Back to binary

        BRK     ; All Done.

; A test value to be converted

        .ORG $0300

BIN     .DW  12345
BCD     .DS  3

from this site.

I don't understand what exactly this line does:

ROL BIN+1

Does it perform right shift on the second byte of BIN? If so, what exactly is in this byte?

Also is it possible to write something similar for x86? Is it possible to use BCD in order to print number in decimal with x86 in some elegant way? Or better stick with division by 10? I know something about AAA, AAM instructions but I don't know if they're really useful.


Solution

  • ROL = rotate left. Yes, that is the second byte. The ASL+ROL together shift the 16 bit number in BIN and BIN+1 left by one bit. The ROL is used to propagate the MSB of the low byte into the LSB of the high byte while the MSB of the high byte is moved to the carry flag which is used by the ADC instruction.

    Note this code uses packed BCD, so on x86 you'd need to use the DAA not the AAA instruction. Also BCD stuff has been deprecated and are not available in 64 bit mode. Nevertheless, here is equivalent x86 code, with added text conversion and printing. GNU assembler at&t, 32 bit linux:

    .globl main
    main:
        sub $8, %esp
        mov $12345, %edx
        mov $16, %ecx
    repeat:
        shl %dx
        mov bcd, %al
        adc %al, %al
        daa
        mov %al, bcd
        mov bcd+1, %al
        adc %al, %al
        daa
        mov %al, bcd+1
        mov bcd+2, %al
        adc %al, %al
        daa
        mov %al, bcd+2
        dec %ecx
        jnz repeat
    
    # print
        lea bcd+2, %esi
        lea txt, %edi
        call unpack
        call unpack
        call unpack
        push $txt
        call puts
        call exit
    
    unpack:
        mov (%esi), %al
        dec %esi
        mov %al, %ah
        shr $4, %al
        and $15, %ah
        add $0x3030, %ax
        stosw
        ret
    
    .lcomm bcd, 3
    .lcomm txt, 7
    

    The above is not the recommended way to do generic int-to-string, it's just translation of the 6502 code in the question.