A new way to distinguish 6502 variants in software

Programming the 6502 microprocessor and its relatives in assembly and other languages.
Post Reply
Chromatix
Posts: 1462
Joined: 21 May 2018

A new way to distinguish 6502 variants in software

Post by Chromatix »

I spent a bit of time today cross-referencing opcode maps to come up with the following routine, 20 bytes long and with no branches whatsoever, which produces a (vaguely) human-readable ASCII code in the accumulator indicating the type of core it's running on:

Code: Select all

.P65C02
TestCPU:
LDA #0
STA $84
STA $85
LDA #$1D ; 'N' EOR 'S'
STA $83
LDA #$6B ; 'N' EOR 'S' EOR '8'
STA $1D
LDA #$4E ; 'N'
RMB4 $83 ; magic $47 opcode
EOR $83

; accumulator now contains ASCII:
; N - NMOS 6502
; S - 65SC02
; C - 65C02 or 65CE02
; 8 - 68C816 or 65C802

; output routine for BBC Micro
JSR $FFEE ; OSWRCH
JSR $FFE7 ; OSNEWL
RTS
Further work may be needed to identify sub-types within those four families, but the above will usually be enough to identify a mis-labelled CPU from China, provided you can get it to boot in the first place. The old NMOS 6502s with missing ROR and the 65CE02 are rare enough that you're unlikely to encounter them outside of a well-curated collection, and distinguishing a Rockwell 65C02 from a WDC example is tricky in generic code that doesn't know where to find a source of interrupts and timing.
Screenshot 2020-01-30 01.20.19.png
I thought it might be instructive to explain how this code works. As the comments hint, the key is in the "magic" $47 opcode, which behaves differently on all four families. Other opcodes with a similar class of behaviour could have been used, but $47 has some nice properties that make it work particularly well here.

The first 8 instructions, immediately preceding the "magic opcode", set up some zero-page locations with "magic data". These are needed to cause some of the CPUs to behave in useful ways once given the $47 opcode. Address $83 was chosen because it is available to user programs on the BBC Micro, and is a 1-byte NOP on the 65SC02. Any other 1-byte NOP opcode could be substituted for this address, as long as the zeroes stored at $84 and $85 are also moved to follow it. Address $1D is important for a different reason and should not casually be moved; on the BBC Micro it harmlessly clobbers BASIC's DATA pointer. It can be moved out of zero-page if the data stored at $84 is changed to match, but $83 must contain $1D.

An NMOS 6502 interprets $47 $83 as a concatenation of LSR $83 and EOR $83, in that effective order. Since that instruction is immediately followed by another EOR $83, the accumulator is left unchanged from its initial value of ASCII 'N'.

A 65SC02 interprets both $47 and $83 as 1-byte NOPs. The following EOR $83 changes the accumulator to ASCII 'S' using the magic value $1D stored there earlier.

A 65C02 interprets $47 $83 as RMB4 $83 (as written in the listing), which clears bit 4 of that memory location. The subsequent EOR now produces ASCII 'C' in the accumulator; it's a useful coincidence that 'S' and 'C' differ by only one bit. The rare 65CE02 is backwards compatible with the 65C02, and will be identified as one by this routine.

A 65816 (or 65802) interprets $47 $83 as EOR [$83], indirecting through the 3-byte long pointer we set up there earlier. The magic value stored there combines with the $1D magic value to convert 'N' into '8' in ASCII.

I'm sure some of you are now thinking of the "Story of Mel"… :mrgreen:
Last edited by Chromatix on Thu Jan 30, 2020 6:18 pm, edited 1 time in total.
User avatar
BillO
Posts: 1038
Joined: 12 Dec 2008
Location: Canada

Re: A new way to distinguish 6502 variants in software

Post by BillO »

Nice! Thanks for this.
Bill
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: A new way to distinguish 6502 variants in software

Post by Chromatix »

And I just found a bug in jsbeeb - it doesn't support the 65C02 that should be in the "parasite" side of the Master Turbo, and in fact seems to have put an NMOS 6502 there. Oops!
User avatar
cjs
Posts: 759
Joined: 01 Dec 2018
Location: Tokyo, Japan
Contact:

Re: A new way to distinguish 6502 variants in software

Post by cjs »

This is so truly clever that I am overcome with awe.

Also, it turns out that cheap Chinese vendors are not the only ones doing remarking. It apparently also goes the other way around: py65 claims that its mpu6502 class "simulates the original NMOS 6502 microprocessor," but it's actually a re-marked 65SC02 emulator, at least according to this test. (In case it's not clear, test_chromatix_id is the function that links to. Here is the code under test.)

Oh well. At least he said "simulate" instead of "emulate." :-)
Curt J. Sampson - github.com/0cjs
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: A new way to distinguish 6502 variants in software

Post by Chromatix »

It's pretty common for 6502 sims to implement only the documented NMOS opcodes, and leave the undocumented ones as NOPs. Indeed from the py65 README: "At this time, all of the documented opcodes are supported. Support for the illegal opcodes is planned for the future." That would be detected as 65SC02 behaviour, since I don't exercise any of the opcodes that are new on the 65SC02 in this code. So you should mostly use this code on real hardware or accurate sims.

ETA: to give a better result on py65 and similar, try appending the following after EOR $83:

Code: Select all

CMP #$53 ; ASCII 'S'
BNE *+6
BRA *+4
LDA #$6E ; ASCII 'n'
A real NMOS 6502 will take the BNE branch. A real 65SC02 will take the BRA. But a sim treating undocumented instructions as NOPs will fall through the BRA (possibly executing the $02 operand byte as another NOP) and end up replacing the accumulator value.
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: A new way to distinguish 6502 variants in software

Post by Chromatix »

Here's how my 65C02 sim (which tests correctly with Klaus' test suite) runs the extended code above:

Code: Select all

FFC0: LDA #$00        ; A=00 X=00 Y=00 S=00    I  ; 0 cyc	fetch FFC0 -> A9	fetch FFC1 -> 00
FFC2: STA  $84        ; A=00 X=00 Y=00 S=00    IZ ; 2 cyc	fetch FFC2 -> 85	fetch FFC3 -> 84	store 0084 <- 00
FFC4: STA  $85        ; A=00 X=00 Y=00 S=00    IZ ; 5 cyc	fetch FFC4 -> 85	fetch FFC5 -> 85	store 0085 <- 00
FFC6: LDA #$1D        ; A=00 X=00 Y=00 S=00    IZ ; 8 cyc	fetch FFC6 -> A9	fetch FFC7 -> 1D
FFC8: STA  $83        ; A=1D X=00 Y=00 S=00    I  ; 10 cyc	fetch FFC8 -> 85	fetch FFC9 -> 83	store 0083 <- 1D
FFCA: LDA #$25        ; A=1D X=00 Y=00 S=00    I  ; 13 cyc	fetch FFCA -> A9	fetch FFCB -> 25
FFCC: STA  $1D        ; A=25 X=00 Y=00 S=00    I  ; 15 cyc	fetch FFCC -> 85	fetch FFCD -> 1D	store 001D <- 25
FFCE: LDA #$4E        ; A=25 X=00 Y=00 S=00    I  ; 18 cyc	fetch FFCE -> A9	fetch FFCF -> 4E
FFD0: RMB4  $83       ; A=4E X=00 Y=00 S=00    I  ; 20 cyc	fetch FFD0 -> 47	fetch FFD1 -> 83	fetch 0083 -> 1D	store 0083 <- 0D
FFD2: EOR  $83        ; A=4E X=00 Y=00 S=00    I  ; 25 cyc	fetch FFD2 -> 45	fetch FFD3 -> 83	fetch 0083 -> 0D
FFD4: CMP #$53        ; A=43 X=00 Y=00 S=00    I  ; 28 cyc	fetch FFD4 -> C9	fetch FFD5 -> 53
FFD6: BNE *+6         ; A=43 X=00 Y=00 S=00 N  I  ; 30 cyc	fetch FFD6 -> D0	fetch FFD7 -> 04
FFDC: BRA *+0         ; A=43 X=00 Y=00 S=00 N  I  ; 33 cyc	fetch FFDC -> 80	fetch FFDD -> FE
FFDC: A=43 X=00 Y=00 S=00 N  I  
It correctly produces ASCII 'C' in the accumulator.

And here is a Visual6502 demonstration!
Last edited by Chromatix on Thu Jan 30, 2020 6:21 pm, edited 1 time in total.
User avatar
drogon
Posts: 1671
Joined: 14 Feb 2018
Location: Scotland
Contact:

Re: A new way to distinguish 6502 variants in software

Post by drogon »

And when it outputs a lower case 'v' ?


This is on my Ruby 816 board. code is running in 8-bit register + memory mode.

Code: Select all

000273r 1  A9 00                LDA #0
000275r 1  85 84                STA $84
000277r 1  85 85                STA $85
000279r 1  A9 1D                LDA #$1D ; 'N' EOR 'S'
00027Br 1  85 83                STA $83
00027Dr 1  A9 25                LDA #$25 ; 'N' EOR 'S' EOR '8'
00027Fr 1  85 1D                STA $1D
000281r 1  A9 4E                LDA #$4E ; 'N'
000283r 1               
000283r 1                       .setcpu "65C02"
000283r 1  47 83                RMB4 $83 ; magic $47 opcode
000285r 1                       .setcpu "65816"
000285r 1               
000285r 1  45 83                EOR $83
000287r 1               
000287r 1               ; accumulator now contains ASCII:
000287r 1               ; N - NMOS 6502
000287r 1               ; S - 65SC02
000287r 1               ; C - 65C02 or 65CE02
000287r 1               ; 8 - 68C816 or 65C802
000287r 1               
000287r 1               ; output routine for BBC Micro
000287r 1               
000287r 1  20 93 FF             JSR osWrch
00028Ar 1  20 8C FF             JSR osNewl
00028Dr 1  60                   RTS
The .setcpu is needed as the assembler is called in 816 mode. That just lets the RMB4 opcode assemble. I tried it in emulated 6502 mode and got the same result. Not tried it in 16-bit mode yet.

-Gordon
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: A new way to distinguish 6502 variants in software

Post by Chromatix »

It seems that I goofed on the magic constant to stash in $1D. Instead of $25, it should be $6B. Fixing that should make it work as expected with an '816. I've made a couple of judicious edits above to reflect that.

Don't bother trying it in 16-bit mode. Although it'll work in native mode with M set (8-bit accumulator), the immediate operands to LDA will be the wrong size with M clear. It's really intended to be run in emulation mode, on the assumption that you're trying to figure out what a chip is from scratch.
Last edited by Chromatix on Thu Jan 30, 2020 6:23 pm, edited 1 time in total.
User avatar
drogon
Posts: 1671
Joined: 14 Feb 2018
Location: Scotland
Contact:

Re: A new way to distinguish 6502 variants in software

Post by drogon »

Chromatix wrote:
It seems that I goofed on the magic constant to stash in $1D. Instead of $25, it should be $6B. Fixing that should make it work as expected with an '816.
Yup - got "8" :)

-Gordon
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/
wyattwong
Posts: 5
Joined: 20 Jan 2021

Re: A new way to distinguish 6502 variants in software

Post by wyattwong »

Chromatix wrote:
I spent a bit of time today cross-referencing opcode maps to come up with the following routine, 20 bytes long and with no branches whatsoever, which produces a (vaguely) human-readable ASCII code in the accumulator indicating the type of core it's running on:

Code: Select all

.P65C02
TestCPU:
LDA #0
STA $84
STA $85
LDA #$1D ; 'N' EOR 'S'
STA $83
LDA #$6B ; 'N' EOR 'S' EOR '8'
STA $1D
LDA #$4E ; 'N'
RMB4 $83 ; magic $47 opcode
EOR $83

; accumulator now contains ASCII:
; N - NMOS 6502
; S - 65SC02
; C - 65C02 or 65CE02
; 8 - 68C816 or 65C802

; output routine for BBC Micro
JSR $FFEE ; OSWRCH
JSR $FFE7 ; OSNEWL
RTS
Further work may be needed to identify sub-types within those four families, but the above will usually be enough to identify a mis-labelled CPU from China, provided you can get it to boot in the first place. The old NMOS 6502s with missing ROR and the 65CE02 are rare enough that you're unlikely to encounter them outside of a well-curated collection, and distinguishing a Rockwell 65C02 from a WDC example is tricky in generic code that doesn't know where to find a source of interrupts and timing.
Screenshot 2020-01-30 01.20.19.png
I thought it might be instructive to explain how this code works. As the comments hint, the key is in the "magic" $47 opcode, which behaves differently on all four families. Other opcodes with a similar class of behaviour could have been used, but $47 has some nice properties that make it work particularly well here.

The first 8 instructions, immediately preceding the "magic opcode", set up some zero-page locations with "magic data". These are needed to cause some of the CPUs to behave in useful ways once given the $47 opcode. Address $83 was chosen because it is available to user programs on the BBC Micro, and is a 1-byte NOP on the 65SC02. Any other 1-byte NOP opcode could be substituted for this address, as long as the zeroes stored at $84 and $85 are also moved to follow it. Address $1D is important for a different reason and should not casually be moved; on the BBC Micro it harmlessly clobbers BASIC's DATA pointer. It can be moved out of zero-page if the data stored at $84 is changed to match, but $83 must contain $1D.

An NMOS 6502 interprets $47 $83 as a concatenation of LSR $83 and EOR $83, in that effective order. Since that instruction is immediately followed by another EOR $83, the accumulator is left unchanged from its initial value of ASCII 'N'.

A 65SC02 interprets both $47 and $83 as 1-byte NOPs. The following EOR $83 changes the accumulator to ASCII 'S' using the magic value $1D stored there earlier.

A 65C02 interprets $47 $83 as RMB4 $83 (as written in the listing), which clears bit 4 of that memory location. The subsequent EOR now produces ASCII 'C' in the accumulator; it's a useful coincidence that 'S' and 'C' differ by only one bit. The rare 65CE02 is backwards compatible with the 65C02, and will be identified as one by this routine.

A 65816 (or 65802) interprets $47 $83 as EOR [$83], indirecting through the 3-byte long pointer we set up there earlier. The magic value stored there combines with the $1D magic value to convert 'N' into '8' in ASCII.

I'm sure some of you are now thinking of the "Story of Mel"… :mrgreen:
What assembler recognize the RMB4 mnemonic ?
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: A new way to distinguish 6502 variants in software

Post by GARTHWILSON »

wyattwong wrote:
What assembler recognize the RMB4 mnemonic ?
Any 65c02 assembler should. RMBx, SMBx, BBSx, and BBRx (where "x" is the bit number) are standard instructions for Rockwell 65c02's and for WDC W65C02's (which is what all current production ones are).

For the 65c02's many improvements over the original NMOS 6502, see http://wilsonminesco.com/NMOS-CMOSdif/ .
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?
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: A new way to distinguish 6502 variants in software

Post by BigDumbDinosaur »

GARTHWILSON wrote:
wyattwong wrote:
What assembler recognize the RMB4 mnemonic ?
Any 65c02 assembler should. RMBx, SMBx, BBSx, and BBRx (where "x" is the bit number) are standard instructions for Rockwell 65c02's and for WDC W65C02's (which is what all current production ones are).

For the 65c02's many improvements over the original NMOS 6502, see http://wilsonminesco.com/NMOS-CMOSdif/ .
Some assemblers expect RMB x,<operand> instead of RMBx <operand>. The Kowalski assembler takes the former syntax, also for SMB, BBR and BBS.

Incidentally, I refer to these four instructions as the "Rockwell extensions" in my writings, since Rockwell devised them for use in their modem chipsets. They were not part of the original 65C02 assembly language as promulgated by WDC and only appeared in the WDC parts some time after Rockwell was licensed to produce the 65C02.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
Post Reply