6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Sep 29, 2024 2:23 pm

All times are UTC




Post new topic Reply to topic  [ 4 posts ] 
Author Message
PostPosted: Mon Apr 07, 2014 2:32 pm 
Offline

Joined: Sat Aug 21, 2010 7:52 am
Posts: 231
Location: Arlington VA
Somewhere around here I found this handy binary-to-ASCII single digit translator which I put in my '#' word for printing numbers. It doesn't work for number bases greater than 16, (e.g. both $0B and $11 will return 'B' instead of 'B' and 'H') but if I ever need base 36 or whatever, I'll deal with it then.
Code:
the business end of the # word, replace binary number on TOS with an ASCII digit
        lda tos
        sed
        cmp #10
        adc #'0'
        cld
        sta tos
I needed a corresponding DIGIT word for the ASCII-to-binary round trip, and wondered if there was a corresponding trick that uses decimal mode? I couldn't figure it out.
The requirement is to convert an ASCII digit (input as 2OS) into a binary value (returned as 2OS) using the curent number base (input as TOS).
If successful, return the binary value at 2OS and a true flag at TOS
if the character is not a digit (in the current number base), leave a false flag (0) at TOS
Put another way, here's the stack diagram
DIGIT ( char base -- digit true | false )

Here's the best I could come up with. (X is the data stack pointer)
Code:
A0 FF     digit   ldy #$ff      ; assume success
B5 20             lda stackl,x  ; get the character
38                sec
E9 30             sbc #'0'
90 0D             bcc digit02   ; char was < '0'? not a digit, fail
C9 0A             cmp #10
90 02             bcc digit01   ; digit is in the range 00-09
E9 07             sbc #7        ; no? alphabet adjust 'A' --> $0a
C5 08     digit01 cmp tos       ; compare to current number base
B0 03             bcs digit02   ; not in current number base? fail
95 20             sta stackl,x  ; digit in the current number base
2C C8 E8         .byt $2c      ; BIT xxyy, skip the next two bytes
          digit02 iny           ; fail, set flag to 0
                  inx           ; drop the stack

98                tya           ; ya = flag word $0000 or $ffff
4C F6 08          jmp put       ; next!


Top
 Profile  
Reply with quote  
PostPosted: Tue Apr 08, 2014 12:40 am 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 674
I don't think decimal mode is going to help you here. You're neither going from or to a BCD value, nor checking for BCD overflow. The overflow being checked here is for particular ASCII ranges, not 0-9/10-15 related.

I believe even in your current code, the characters between '9' and 'A' (:;<=>?@) will get subtracted down into digit range and be false positives as numbers. There's really no way around this I can see besides doing 2 separate subtractions & range-checks.

You can elide the <'0' check, if you later use unsigned branch comparisons. Just let the number wrap around to the high end, and the result will be too big for the base (assuming bases 2-36 are the only valid ones). I learned that shortcut in simplifying code for checking ranges; align your range with zero, then perform a single unsigned greater-than check to catch both less than (wrapped) and greater than cases.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Tue Apr 08, 2014 1:19 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
Here's the one from my 65m32 Forth (still translating...)

It's ( char base -- digit true | false ) just like yours.
Code:
00000080:           157 _digit:
00000080: 52020000  158         ldb  ,x+        ;nip potential digit into b
00000081: d809ffd0  159         dec  #-'0',b    ;clear carry and set ^N if b < '0'
00000082: 4ca9fff6  160         tst  [pl]#-10,b ;clear ^N if b > '9'
00000083: f0a7fff9  161         adb  [pl]#-7    ;ascii correction for b > '9'
00000084: 90a6000a  162         cpb  [pl]#10    ;clear carry if '9' < b < 'A'
00000085: 80580001  163         cmp  [cs]#1,b   ;check for base > digit
00000086: b3520000  164         stb  [cs],-x    ;if valid digit, tuck it into 2OS
00000087: 34000000  165         cdc  #,a        ;clear TOS if valid
00000088: 5c0e0045  166         bra  _zequ      ;exit through 0=

It makes some sneaky use of conditional execution and condition-code side-effects. Hopefully, the comments are enough to make it clear what's going on, but I don't know if it would help to translate it 'back' to 6502 ... I'll give it a try later and update this post.

Mike


Last edited by barrym95838 on Tue Apr 08, 2014 3:43 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Tue Apr 08, 2014 3:11 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8521
Location: Southern California
Some of what I wrote here is for the non-Forth-literate, since it's not in the Forth sub-forum and the same method can be used without Forth.

Because of how/where >DIGIT gets used, there's no need to check to see if it's valid, regardless of the desired base. When turning a number into ASCII for human output, you divide it by the contents of variable BASE and take the remainder for each digit as you build the number right to left, repeatedly, until the input number is 0. The remainder will always be less than the base, so it is guaranteed to be valid, without error checking. The same routine works for any base you could ever want, and in fact BASE will sometimes change in the process, like when forming a time string where the minutes' and seconds' ones' place goes 0-9 but the tens' place only goes 0-5. You might also want to put in punctuation (using HOLD), like the ":" in the case of the time string (for example, 3:27:49.1), or the decimal point, comma, / (for dates), - (the minus sign at the left end), or other character.

What I have for >DIGIT in my '816 Forth is:
Code:
          HEADER ">DIGIT", NOT_IMMEDIATE ; ( n -- ASCII_CHR ) ( b -- ASCII_CHR )
TO_DIGIT: PRIMITIVE         ; Turn number (0-15) into ASCII character.
          LDA     0,X       ; We'll do it in 16-bit to avoid changing from
          CMP     #$A       ; 16 to 8 and back.  It's only meaningful for
          BCC     ja30      ; bytes though.
          ADC     #6        ; Add only 6, not 7, because C flag was set.
 ja30:    ADC     #$30      ; Carry flag is always clear when getting here.
          STA     0,X
          GO_NEXT
 ;-------------------


For going the other direction, you do need to see if it's valid, as you're starting with ASCII. Take each digit, turn it into a number if possible, multiply the number you're building by the contents of BASE, and add the quantity specified by the new ASCII digit. Loop until you reach an ASCII value that's neither a valid digit in the chosen base nor a specified-valid character (like - . , / :). Mine uses CONVERT and NUMBER? which are not primitives. I'll add them later if someone wants them.

I highly recommend keeping it such that the BASE can be anything and is kept in BASE, and the same routines get used for all conversions regardless of base.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 21 guests


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 post attachments in this forum

Search for:
Jump to: