It is currently Tue Oct 16, 2018 5:26 am

 All times are UTC - 7 hours

 Page 2 of 2 [ 26 posts ] Go to page Previous  1, 2
 Print view Previous topic | Next topic
Author Message
 Post subject: Re: Hex to DecimalPosted: Tue Jun 17, 2014 7:25 pm

Joined: Sun Jan 02, 2011 11:50 am
Posts: 522
Sure, tepples. Right now I am (slowly) working on something in C that will combine 6502 assembly and C in the same source file and call ca65 to build the assembly. I think my idea will work, but I would be interested to see how others solved this problem.

Top

 Post subject: Re: Hex to DecimalPosted: Tue Jun 17, 2014 7:48 pm

Joined: Tue Jun 10, 2014 8:15 pm
Posts: 35
Movax12 wrote:
Omegamatrix, nice code, but what I am more interested in at the moment is how you determine the following:

Code:
;--------------------------------
;0-99 conversion stats
;--------------------------------
;cycles  occurances
;28  -  10
;31  -  10
;34  -  10
;37  -  10
;39  -  10
;42  -  10
;45  -  10
;48  -  10
;50  -  10
;56  -  10

;average execution is 41 cycles

What method/tools do you use to create that information?

I'm running a 2600 program I wrote, because I don't know much about the NES yet. The 2600 has a 1 cycle timer which I'm loading before the subroutine and checking afterward. From there I run some code to check for the best times and worst times. The difference between best and worse times is small enough that I can use some zero page ram to record how many times each cycle scenario occurs. I subtract the best time to offset it to zero, and also 4 cycles for loading the 2600 timer, and an additional 3 cycles for saving the value left in A at the end of the routine (I do that before I load the timer in A).

The program I'm using scrolls through all possible values, and checks if they are all correct. If it's good I get a green passed screen, or a red fail screen. Here's some of the code:

Code:
.innerLoop:
dec    hexValue
lda    hexValue              ; A = 0-255

IF DETERMINE_BEST_WORSE_TIME
ldy    #255
sty    TIM1T
ENDIF
jsr    HexToDec255

;... routine runs here

sta    decOnes              ; save routine value
lda    INTIM                ; get timer value
sty    decHundreds
stx    decTens

eor    #\$FF
sec
sbc    #4+3                 ; take away 3 cycles for STA decOnes, and 4 cycles for LDA INTIM
cmp    worstTime            ; save best/worse time
bcc    .lessThen
sta    worstTime            ; worseTime begins the routine loaded with 0, and bestTime begins with \$FF
.lessThen:
cmp    bestTime
bcs    .moreThen
sta    bestTime
.moreThen:

sec
sbc    #35                   ; You have run the routine once to get the best time (35 in this case),
tax                          ; and run the routine again afterward with that value.
clc
lda   #1
adc   occurances,X           ; update count for each routine time occurance...
sta   occurances,X

ldy    hexValue              ; verify routine values against tables stored in rom
lda    decOnes
cmp    OnesTab,Y
lda    decTens
cmp    TensTab,Y
lda    decHundreds
cmp    HundredsTab,Y

lda    #\$FF

dey
cpy    #\$FF
bne    .innerLoop

Here in Stella's debugger you can see the cycles cases begin at \$B0 and continues from there. This snapshot is for the results of 0-255.
Attachment:

DebuggerValues.jpg [ 72.54 KiB | Viewed 2054 times ]

Top

 Post subject: Re: Hex to DecimalPosted: Tue Jun 17, 2014 7:54 pm

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20656
Location: NE Indiana, USA (NTSC)
Movax12 wrote:
tepples wrote:
I wrote a 6502 simulator in Python so that I could run automated unit tests on 6502 code. [...] Want to see my simulator?

Sure, tepples.

Movax12 wrote:
Right now I am (slowly) working on something in C that will combine 6502 assembly and C in the same source file and call ca65 to build the assembly. I think my idea will work, but I would be interested to see how others solved this problem.

Anything like Leushenko's x86-to-C static recompiler?

Omegamatrix wrote:
I'm running a 2600 program I wrote [describes unit test harness]

Neat. It'd be harder to do that on an NES without mapper support.

Top

 Post subject: Re: Hex to DecimalPosted: Tue Jun 17, 2014 7:59 pm

Joined: Sun Jan 02, 2011 11:50 am
Posts: 522
tepples wrote:
Anything like Leushenko's x86-to-C static recompiler?.

I considered that idea, but i want to be able to use actual ca65 compatible source, so I am going to load the binary output of ca65 into an open-source 6502 emulator's RAM and hook back to C code when needed.

Omegamatrix, interesting solution. I am going to have to look at that more than once to really get what you are doing.

Top

 Post subject: Re: Hex to DecimalPosted: Tue Jun 17, 2014 8:02 pm

Joined: Tue Jun 10, 2014 8:15 pm
Posts: 35
tepples wrote:
But so long as the cart doesn't put something with read side effects into \$6xxx, BIT \$6469 should work. (The addresses with read side effects on the NES are \$2002, \$2007, \$4015-\$4017, and \$4020-\$5FFF for the Vs. System's credit acknowledge.) The only mapper that I can think of that has read side effects there is Bandai boards with an I²C EEPROM.

I have another option I could try. If I substitute:
Code:
.byte \$2C                    ; BIT \$6469
.use100
.done

With this:
Code:
.byte \$2C                    ; BIT \$9BE9
.use100
sbc    #-101
.done

The function is the same. It's a little more convoluted and certainly harder to read, but if it doesn't conflict with anything I would certainly switch over to it.

If that still potentially breaks something I'm going to stick a branch in there with some comments that a byte can be saved with BIT, but the user must check for these odd cases.

Top

 Post subject: Re: Hex to DecimalPosted: Tue Jun 17, 2014 8:07 pm

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20656
Location: NE Indiana, USA (NTSC)
I don't think any NES mapper has read side effects in \$8000-\$FFFF because that's where the program itself is, except perhaps for really freaking complicated copy protection schemes.

Top

 Post subject: Re: Hex to DecimalPosted: Tue Jun 17, 2014 8:22 pm

Joined: Sun Apr 13, 2008 11:12 am
Posts: 7648
Location: Seattle
tepples wrote:
Mapper 234 triggers bankswitches on reads from \$FF80-\$FFF7. (This doesn't pose a problem with using BIT here)

Top

 Post subject: Re: Hex to DecimalPosted: Tue Jun 17, 2014 9:30 pm

Joined: Mon Feb 07, 2011 12:46 pm
Posts: 1017
thefox wrote:
In fact, all of \$4020..\$FFFF. It could be RAM, ROM, mapper registers or nothing (= open bus).
Actually, I think it could even map things to the rest of the address space too if it wanted to (I have discussed deliberately interfering with mirrors of RAM and PPU registers, in order to allow writes to write two things at once; for example, a mapper may be designed to mirrors its write-only registers at \$1xxx, \$3xxx, \$5xxx, \$7xxx, \$9xxx, \$Bxxx, \$Dxxx, \$Fxxx).

Even if such a thing existed it still won't interfere with using BIT here (which only reads), although changing it to SBC #-101 like you have would in fact fix it with whatever mapper you might want to use (probably; I don't know if there are other cases).

Like they said there are some mappers that have side-effects on read from some registers, but \$9BE9 is not in that range, and I am not sure why anyone would make one where there are side-effects on read at that address.

Maybe you can put a comment explaining it; if needed, it can then be changed between ADD #100 or SBC #-101 or a branch instead of BIT, depending on the mapper, if it is necessary to do so.

_________________
.

Top

 Post subject: Re: Hex to DecimalPosted: Tue Jun 17, 2014 9:54 pm

Joined: Tue Jun 10, 2014 8:15 pm
Posts: 35
zzo38 wrote:
Maybe you can put a comment explaining it; if needed, it can then be changed between ADD #100 or SBC #-101 or a branch instead of BIT, depending on the mapper, if it is necessary to do so.

Good idea. I added in the comment with some choices.

I also added some ASCII support. Cost was just 2 bytes and 2 cycles.

Top

 Post subject: Re: Hex to DecimalPosted: Wed Jun 18, 2014 11:00 pm

Joined: Tue Jun 10, 2014 8:15 pm
Posts: 35
I took a look and realized there is a easy way to chop another couple of bytes out of the routine by eliminating a branch. This will bring the total size down to 41 bytes, which is not too bad. The downside is that doing so will add an extra 3 cycles to some cases, including the worst case. To me that seemed like no gain at all. In the end I decided to to leave the routine as is and just post the changes here for user to make their own decision.

Old:
Code:
HexToDec99; SUBROUTINE
ldx    #0 + ASCII_OFFSET
cmp    #50                   ; A = 0-99
bcc    .try20
sbc    #50
ldx    #5 + ASCII_OFFSET
bne    .try20                ;always branch

.div20:
inx
inx
sbc    #20
.try20:

New (save 2 bytes by eliminating branch, but add 3 more cycles):
Code:
HexToDec99; SUBROUTINE
ldx    #0 + ASCII_OFFSET
cmp    #50                   ; A = 0-99
bcc    .try20
sbc    #30
ldx    #3 + ASCII_OFFSET
.div20:
inx
inx
sbc    #20
.try20:

Top

 Post subject: Re: Hex to DecimalPosted: Sat Jun 28, 2014 10:54 am

Joined: Tue Jun 10, 2014 8:15 pm
Posts: 35
I've wrote a couple more 16 bit number (0-65535) to decimal routines. My old, original routine took 258 bytes, and 150-162 cycles. That routine however was not set up for JSR. To do so would requires 3 more bytes, and 15 cycles. That brings it true cost to 261 bytes, and 165-177 cycles.

The first new routine I made takes only 234 bytes, and 157-162 cycles (including the JSR, RTS). Its worse case time is 3 cycles faster then the old routines best case, and it saves 27 bytes. The second new routine saves even more taking only 174 bytes, but takes longer requiring 178-186 cycles to execute. Still, those cycles are not too bad, and I'm pretty happy about the overall performance of both routines.

The basic approach of both new routines is different to the original. Now I'm going after the 10,000's and 1,000's digit by shifting the high byte to the 1024 value bit. This gives a pretty close approximation to an integer divide by 1000. I start with that and correct the result. The fast routine uses a multiply by 24 table to correct the result. I doubled that table up storing the high byte value (0-6) in the bits 0-2. Since the low byte value only ever uses bits 3-7 this was easy to do.

Support has also been added for entries into the routine to do 0-999, 0-255, and 0-99 conversions. Adding support for HexToDec255 and HexToDec999 requires 9 more bytes. Ascii support is also available at a cost of 2 bytes and 2 cycles for each digit.

Stats:
Code:
;slow routine - 174 bytes, 183 bytes with HexToDec255 and HexToDec999
;HexToDec99     ; 37 cycles
;HexToDec255    ; 52-57 cycles
;HexToDec999    ; 72-77 cycles
;HexToDec65535  ; 178-186 cycles

;Fast routine - 234 bytes, 243 bytes with HexToDec255 and HexToDec999
;HexToDec99     ; 37 cycles
;HexToDec255    ; 52-57 cycles
;HexToDec999    ; 72-77 cycles
;HexToDec65535  ; 157-162 cycles

;-------------------------------------------------------------------------------

;HexToDec99
; start in A
; end with A = 10's, decOnes

;HexToDec255
; start in A
; end with Y = 100's, A = 10's, decOnes

;HexToDec999
; end with Y = 100's, A = 10's, decOnes
; requires 1 extra temp register on top of decOnes, could combine
; these two if HexToDec65535 was eliminiated...

;HexToDec65535
; end with decTenThousand, decThousand, Y = 100's, A = 10's, decOnes
; requires 2 extra temp registers on top of decTenThousand, decThousand, decOnes

Here's the fast routine:
Code:
;Hex to Decimal (0-65535) conversion
;by Omegamatrix
;
;HexToDec99     ; 37 cycles
;HexToDec255    ; 52-57 cycles
;HexToDec999    ; 72-77 cycles
;HexToDec65535  ; 157-162 cycles

ASCII_OFFSET = \$00
temp         = decOnes
hexHigh      = temp2
hexLow       = temp3

Mult24Tab:
.byte \$00,\$18,\$30,\$48,\$60,\$78,\$90,\$A8,\$C0,\$D8
.byte \$F0,\$09,\$21,\$39,\$51,\$69,\$81,\$99,\$B1,\$C9
.byte \$E1,\$F9,\$12,\$2A,\$42,\$5A,\$72,\$8A,\$A2,\$BA
.byte \$D2,\$EA,\$03,\$1B,\$33,\$4B,\$63,\$7B,\$93,\$AB
.byte \$C3,\$DB,\$F3,\$0C,\$24,\$3C,\$54,\$6C,\$84,\$9C
.byte \$B4,\$CC,\$E4,\$FC,\$15,\$2D,\$45,\$5D,\$75,\$8D
.byte \$A5,\$BD,\$D5,\$ED,\$06,\$1E

Mod100Tab:
.byte 0,56,12,56+12

ShiftedBcdTab
.byte \$00,\$01,\$02,\$03,\$04,\$08,\$09,\$0A,\$0B,\$0C
.byte \$10,\$11,\$12,\$13,\$14,\$18,\$19,\$1A,\$1B,\$1C
.byte \$20,\$21,\$22,\$23,\$24,\$28,\$29,\$2A,\$2B,\$2C
.byte \$30,\$31,\$32,\$33,\$34,\$38,\$39,\$3A,\$3B,\$3C
.byte \$40,\$41,\$42,\$43,\$44,\$48,\$49,\$4A,\$4B,\$4C

HexToDec65535; SUBROUTINE
sta    hexHigh               ;3  @9
stx    hexLow                ;3  @12
tax                          ;2  @14
lsr                          ;2  @16
lsr                          ;2  @18   integer divide 1024 (result 0-63)

cpx    #\$A7                  ;2  @20   account for overflow of multiplying 24 from 43,000 (\$A7F8) onward,
adc    #0                    ;2  @22   we can just round it to \$A700, and the divide by 1024 is fine...
tay                          ;2  @24
lda    Mult24Tab+1,Y         ;4  @28   could use LAX...
tax                          ;2  @30
and    #\$F8                  ;2  @32
txa                          ;2  @37
and    #\$07                  ;2  @39
ror                          ;2  @44
lsr                          ;2  @46
tay                          ;2  @48   integer divide 1,000 (result 0-65)

lsr                          ;2  @50   split the 1,000 and 10,000 digit
tax                          ;2  @52
lda    ShiftedBcdTab,X       ;4  @56
tax                          ;2  @58
rol                          ;2  @60
and    #\$0F                  ;2  @62
IF ASCII_OFFSET
ora    #ASCII_OFFSET
ENDIF
sta    decThousands          ;3  @65
txa                          ;2  @67
lsr                          ;2  @69
lsr                          ;2  @71
lsr                          ;2  @73
IF ASCII_OFFSET
ora    #ASCII_OFFSET
ENDIF
sta    decTenThousands       ;3  @76

;at this point we have a number 0-65 that we have to times by 24,
;add to original sum, and Mod 1024 to get the remainder 0-999

lda    Mult24Tab,Y           ;4  @80   could use LAX...
tax                          ;2  @82
and    #\$F8                  ;2  @84
clc                          ;2  @86
sta    temp                  ;3  @92
txa                          ;2  @94
Start100s:
and    #\$03                  ;2  @99
tax                          ;2  @101   0,1,2,3
cmp    #2                    ;2  @103
rol                          ;2  @105   0,2,5,7
IF ASCII_OFFSET
ora    #ASCII_OFFSET
ENDIF
tay                          ;2  @107   Y = Hundreds digit

lda    temp                  ;3  @110
adc    Mod100Tab,X           ;4  @114  adding remainder of 256, 512, and 256+512 (all mod 100)
bcs    .doSub200             ;2³ @116/117

.try200:
cmp    #200                  ;2  @118
bcc    .try100               ;2³ @120/121
.doSub200:
iny                          ;2  @122
iny                          ;2  @124
sbc    #200                  ;2  @126
.try100:
cmp    #100                  ;2  @128
bcc    HexToDec99            ;2³ @130/131
iny                          ;2  @132
sbc    #100                  ;2  @134
HexToDec99; SUBROUTINE
lsr                          ;2  @136
tax                          ;2  @138
lda    ShiftedBcdTab,X       ;4  @142
tax                          ;2  @144
rol                          ;2  @146
and    #\$0F                  ;2  @148
IF ASCII_OFFSET
ora    #ASCII_OFFSET
ENDIF
sta    decOnes               ;3  @151
txa                          ;2  @153
lsr                          ;2  @155
lsr                          ;2  @157
lsr                          ;2  @159
IF ASCII_OFFSET
ora    #ASCII_OFFSET
ENDIF
rts                          ;6  @165   A = tens digit

HexToDec255; SUBROUTINE
IF ASCII_OFFSET
ldy    #ASCII_OFFSET         ;2  @8
bne    .try200               ;3  @11    always branch
ELSE
ldy    #0                    ;2  @8
beq    .try200               ;3  @11    always branch
ENDIF

HexToDec999; SUBROUTINE
stx    temp                  ;3  @9
jmp    Start100s             ;3  @12

Here's the slower routine, which saves a lot of bytes:
Code:
;Hex to Decimal (0-65535) conversion
;by Omegamatrix
;
;HexToDec99     ; 37 cycles
;HexToDec255    ; 52-57 cycles
;HexToDec999    ; 72-77 cycles
;HexToDec65535  ; 178-186 cycles

ASCII_OFFSET = \$00
temp         = decOnes
hexHigh      = temp2
hexLow       = temp3

Mod100Tab:
.byte 0,56,12,56+12

ShiftedBcdTab
.byte \$00,\$01,\$02,\$03,\$04,\$08,\$09,\$0A,\$0B,\$0C
.byte \$10,\$11,\$12,\$13,\$14,\$18,\$19,\$1A,\$1B,\$1C
.byte \$20,\$21,\$22,\$23,\$24,\$28,\$29,\$2A,\$2B,\$2C
.byte \$30,\$31,\$32,\$33,\$34,\$38,\$39,\$3A,\$3B,\$3C
.byte \$40,\$41,\$42,\$43,\$44,\$48,\$49,\$4A,\$4B,\$4C

HexToDec65535; SUBROUTINE
sta    hexHigh               ;3  @9
stx    hexLow                ;3  @12
tax                          ;2  @14
lsr                          ;2  @16
lsr                          ;2  @18   integer divide 1024 (result 0-63)

cpx    #\$A7                  ;2  @20   account for overflow of multiplying 24 from 43,000 (\$A7F8) onward,
adc    #1                    ;2  @22   we can just round it to \$A700, and the divide by 1024 is fine...

;at this point we have a number 1-65 that we have to times by 24,
;add to original sum, and Mod 1024 to get a remainder 0-999

sta    temp                  ;3  @25
asl                          ;2  @27
tay                          ;2  @32
lsr                          ;2  @34
lsr                          ;2  @36
lsr                          ;2  @38
lsr                          ;2  @40
lsr                          ;2  @42
tax                          ;2  @44
tya                          ;2  @46
asl                          ;2  @48
asl                          ;2  @50
asl                          ;2  @52
clc                          ;2  @54
sta    hexLow                ;3  @60
txa                          ;2  @62
sta    hexHigh               ;3  @68
ror                          ;2  @70
lsr                          ;2  @72
tay                          ;2  @74    integer divide 1,000 (result 0-65)

lsr                          ;2  @76    split the 1,000 and 10,000 digit
tax                          ;2  @78
lda    ShiftedBcdTab,X       ;4  @82
tax                          ;2  @84
rol                          ;2  @86
and    #\$0F                  ;2  @88
IF ASCII_OFFSET
ora    #ASCII_OFFSET
ENDIF
sta    decThousands          ;3  @91
txa                          ;2  @93
lsr                          ;2  @95
lsr                          ;2  @97
lsr                          ;2  @99
IF ASCII_OFFSET
ora    #ASCII_OFFSET
ENDIF
sta    decTenThousands       ;3  @102

lda    hexLow                ;3  @105
cpy    temp                  ;3  @108
bmi    .doSubtract           ;2³ @110/111
beq    useZero               ;2³ @112/113
adc    #23 + 24              ;2  @114
.doSubtract:
sbc    #23                   ;2  @116
sta    hexLow                ;3  @119
useZero:
lda    hexHigh               ;3  @122
sbc    #0                    ;2  @124

Start100s:
and    #\$03                  ;2  @126
tax                          ;2  @128   0,1,2,3
cmp    #2                    ;2  @130
rol                          ;2  @132   0,2,5,7
IF ASCII_OFFSET
ora    #ASCII_OFFSET
ENDIF
tay                          ;2  @134   Y = Hundreds digit

lda    hexLow                ;3  @137
adc    Mod100Tab,X           ;4  @141    adding remainder of 256, 512, and 256+512 (all mod 100)
bcs    .doSub200             ;2³ @143/144

.try200:
cmp    #200                  ;2  @145
bcc    .try100               ;2³ @147/148
.doSub200:
iny                          ;2  @149
iny                          ;2  @151
sbc    #200                  ;2  @153
.try100:
cmp    #100                  ;2  @155
bcc    HexToDec99            ;2³ @157/158
iny                          ;2  @159
sbc    #100                  ;2  @161

HexToDec99; SUBROUTINE
lsr                          ;2  @163
tax                          ;2  @165
lda    ShiftedBcdTab,X       ;4  @169
tax                          ;2  @171
rol                          ;2  @173
and    #\$0F                  ;2  @175
IF ASCII_OFFSET
ora    #ASCII_OFFSET
ENDIF
sta    decOnes               ;3  @178
txa                          ;2  @180
lsr                          ;2  @182
lsr                          ;2  @184
lsr                          ;2  @186
IF ASCII_OFFSET
ora    #ASCII_OFFSET
ENDIF
rts                          ;6  @192   A = tens digit

HexToDec255; SUBROUTINE
IF ASCII_OFFSET
ldy    #ASCII_OFFSET         ;2  @8
bne    .try200               ;3  @11    always branch
ELSE
ldy    #0                    ;2  @8
beq    .try200               ;3  @11    always branch
ENDIF

HexToDec999; SUBROUTINE
stx    hexLow                ;3  @9
jmp    Start100s             ;3  @12

Top

 Display posts from previous: All posts1 day7 days2 weeks1 month3 months6 months1 year Sort by AuthorPost timeSubject AscendingDescending
 Page 2 of 2 [ 26 posts ] Go to page Previous  1, 2

 All times are UTC - 7 hours

#### Who is online

Users browsing this forum: No registered users and 5 guests

 You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum

Search for:
 Jump to:  Select a forum ------------------ NES / Famicom    NESdev    NESemdev    NES Graphics    NES Music    Homebrew Projects       2018 NESdev Competition       2017 NESdev Competition       2016 NESdev Competition       2014 NESdev Competition       2011 NESdev Competition    Newbie Help Center    NES Hardware and Flash Equipment       Reproduction    NESdev International       FCdev       NESdev China       NESdev Middle East Other    General Stuff    Membler Industries    Other Retro Dev       SNESdev       GBDev    Test Forum Site Issues    phpBB Issues    Web Issues    nesdevWiki