6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Apr 19, 2024 10:52 am

All times are UTC




Post new topic Reply to topic  [ 10 posts ] 
Author Message
PostPosted: Sun Aug 26, 2012 6:37 am 
Offline

Joined: Sat Sep 04, 2004 4:17 am
Posts: 30
Location: Last Ninja 2: Basement
I just spent a few hours researching some of the differences between 6500 family variants and writing a short piece of code that should be able to detect what kind of CPU you're running on top of. I've not tested it thoroughly! It works as expected on a C64 (result of $0c meaning normal NMOS 6502 core) and an NES emulator ($04 meaning NMOS 6502 with no decimal mode) but that's the extent of my testing as I either lack supporting code, knowledge, or access to the platforms in question to test every possibility.

It currently supports detection of these 6500 variants, without crashing (hopefully) during any part of detection:

"Stock" NMOS 6502
WDC 65C02 (and clones)
65816
Very early 6502 with the "ROR bug"
Ricoh 2A03 (NES CPU)
Rockwell R65C02 series (with RMB/SMB opcodes)
Hudson Soft HuC6280 (PC Engine CPU)
Mitsubishi/Renesas 740 MPU/MCU
Renesas 740 MUL/DIV instruction support

It's designed to be built with the ACME assembler, version 0.93 or up, and builds to 96 bytes in size. The result of the tests is stored as flags in a zero-page location which can be easily modified, and testing requires at most two zero-page locations beyond the flags storage location. I hope that someone here finds this little code snippet useful.

Code:
!to "detect6502.o",plain
!cpu 65816
*=$4000

zp0=$fb
zp1=$fc
zp2=$fd

; Detects the CPU in use
; 76543210
; ||||||''-- 0=NMOS 6502 (or 65816 if bit 1 ON), 1=65C02 or variant
; ||||||'--- 0=6502 series, 1=65816 series
; |||||'---- 0=Early 6502 w/bad ROR instruction, 1=ROR is normal
; ||||'----- 0=No decimal mode (Ricoh 2A03), 1=decimal mode OK
; |||'------ 1=Hudson Soft HuC6280 CPU
; ||'------- 1=Rockwell R65C02 series CPU
; |'-------- 1=Mitsubishi/Renesas 740 CPU
; '--------- 1=Renesas 740 with MUL/DIV instructions

detect6502
   lda #$02   ; Initialize for 740 MUL/DIV test
   sta zp2
; Test for NMOS/CMOS and 6502/65816
   lda #$00
   sta zp1      ; Initialize for 740 series test
   inc
   cmp #$01
   bmi +
   xba
   dec
   xba
   inc
+   sta zp0      ; 0=6502, 1=65C02, 2=65C816
; Test for very early 6502s with bad ROR instruction
   lda #$08
   clc
   ror
   cmp #$04
   bne +      ; 6502 with bad ROR instruction
   ora zp0
   sta zp0      ; 4 = good ROR
; Test for Ricoh 2A03 (NES) which has no decimal mode
+   cli      ; C64 KERNAL can't handle D set on IRQ
   sed
   lda #$09
   clc
   adc #$01
   cmp #$10
   bne +      ; Decimal mode failed
   lsr      ; $10 -> $08
   ora zp0
   sta zp0
; Test for Hudson Soft HuC6280 CPU
+   cld
   sei
   and #$01   ; Skip remaining tests if not CMOS
   beq ++
   php
   ldy #$01
   rep #$08   ; HuC6280 sees "CLY,PHP" instead
   cpy #$00
   bne +      ; Not a HuC6280 CPU
   plp      ; Undo the PHP instruction
   lda #$20
   ora zp0
   sta zp0      ; Set HuC6280 flag
; Test for Rockwell R65C00 series
; This is pitifully simple: it sets the flag bit for us!
+   !byte $d7   ; SMB5 opcode on R65C00, ignored on 65C02
; Test for Mitsubishi/Renesas 740 series CPU
   !byte $3c   ; LDM on 740, BIT on 65C02
   !byte $40,zp1   ; 740 sets zp1 to $40
   lda zp1
   beq ++      ; If not a 740, end testing
   ora zp0      ; Should already contain $40
   sta zp0
   lda #$02
   !byte $62,zp2   ; MUL zp2
   cmp #$04   ; If MUL is supported, 2*2=4
   bne ++
   lda #$80   ; Set 740 MUL/DIV flag
   ora zp0
   sta zp0
++   rts


Last edited by daivox on Tue Nov 22, 2022 5:09 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 26, 2012 7:04 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8138
Location: Midwestern USA
Detecting a 65C816 would be problematic because at reset, it comes up as a (mostly) 65C02 and can only be switched to native mode with the XCE instruction. The XCE opcode is $FB, which would behave as a one byte NOP with a 65C02 but would be undefined on all other eight bit MPUs and might result in a crash.

So I think the path to determining if the MPU is an '816 would be to first determine if it is a 65C02. If it is then you could proceed as follows:

Code:
         clc
         xce
         bcs is_816
         ...

Of course, you'd need an actual 65C816 machine to try out this code.

Code:
; Test for Rockwell R65C00 series
; This is pitifully simple: it sets the flag bit for us!
+   !byte $d7   ; SMB5 opcode on R65C00, ignored on 65C02

$D7 is valid on the WDC 65C02 as SMB 5,ZP.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Last edited by BigDumbDinosaur on Sun Aug 27, 2023 8:01 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 26, 2012 7:49 pm 
Offline

Joined: Sat Sep 04, 2004 4:17 am
Posts: 30
Location: Last Ninja 2: Basement
The code already checks for 65816 by first checking for 65C02, and only then using XBA instructions to determine if it's a 65816. As for the SMB5 instruction, you are indeed correct; I've modified the comments to indicate that the detection finds "old" vs. "new" type 65C02 chips. Apparently some 65C02 chips exist without SMB/RMB/BBS/BBR opcodes, and the test checks that. R65C02 and WDC 65C02 appear to have identical opcode matrices, but a datasheet on this site for a California Micro Devices G65SC02 has a matrix with these opcode slots empty. Also, I found a bug where I didn't put zp0 in the SMB5 instruction, and fixed it. This changes the code size to 97 bytes.

I also added a warning, because zp0 will be executed as a 65C02 opcode if the SMB5 opcode test fails. Here's the modified code:

Code:
!to "detect6502.o",plain
!cpu 65816
*=$4000

; WARNING: the value in zp0 may be executed as an opcode on CMOS 6502
; chips during the bit-op test. Storing the result in an address that
; consitutes the wrong opcode could result in a crash (for example, if
; $40 is used, a 65C02 without bit-op extensions will execute RTI!)
; Two- or three-byte opcodes in zp0 will cause trouble as well!
; Anything ending in $x3 or $xB will be interpreted as NOP and should
; be safe.
zp0=$fb
zp1=$fc
zp2=$fd

; Detects the CPU in use
; 76543210
; ||||||''-- 0=NMOS 6502 (or 65816 if bit 1 ON), 1=65C02 or variant
; ||||||'--- 0=6502 series, 1=65816 series
; |||||'---- 0=Early 6502 w/bad ROR instruction, 1=ROR is normal
; ||||'----- 0=No decimal mode (Ricoh 2A03), 1=decimal mode OK
; |||'------ 1=Hudson Soft HuC6280 CPU
; ||'------- 1=Bit-op capable 65C02 (RMB/SMB/BBR/BBS opcodes available)
; |'-------- 1=Mitsubishi/Renesas 740 CPU
; '--------- 1=Renesas 740 with MUL/DIV instructions

detect6502
   lda #$02   ; Initialize for 740 MUL/DIV test
   sta zp2
; Test for NMOS/CMOS and 6502/65816
   lda #$00
   sta zp1      ; Initialize for 740 series test
   inc
   cmp #$01
   bmi +
   xba
   dec
   xba
   inc
+   sta zp0      ; 0=6502, 1=65C02, 2=65C816
; Test for very early 6502s with bad ROR instruction
   lda #$08
   clc
   ror
   cmp #$04
   bne +      ; 6502 with bad ROR instruction
   ora zp0
   sta zp0      ; 4 = good ROR
; Test for Ricoh 2A03 (NES) which has no decimal mode
+   cli      ; C64 KERNAL can't handle D set on IRQ
   sed
   lda #$09
   clc
   adc #$01
   cmp #$10
   bne +      ; Decimal mode failed
   lsr      ; $10 -> $08
   ora zp0
   sta zp0
; Test for Hudson Soft HuC6280 CPU
+   cld
   sei
   and #$01   ; Skip remaining tests if not CMOS
   beq ++
   php
   ldy #$01
   rep #$08   ; HuC6280 sees "CLY,PHP" instead
   cpy #$00
   bne +      ; Not a HuC6280 CPU
   plp      ; Undo the PHP instruction
   lda #$20
   ora zp0
   sta zp0      ; Set HuC6280 flag
; Test for 65C02 with bit-op instructions
; This is pitifully simple: it sets the flag bit for us!
+   !byte $d7,zp0   ; WARNING: zp0 becomes opcode if SMB5 not usable!
; Test for Mitsubishi/Renesas 740 series CPU
   !byte $3c   ; LDM on 740, BIT on 65C02
   !byte $40,zp1   ; 740 sets zp1 to $40
   lda zp1
   beq ++      ; If not a 740, end testing
   ora zp0      ; Should already contain $40
   sta zp0
   lda #$02
   !byte $62,zp2   ; MUL zp2
   cmp #$04   ; If MUL is supported, 2*2=4
   bne ++
   lda #$80   ; Set 740 MUL/DIV flag
   ora zp0
   sta zp0
++   rts


Top
 Profile  
Reply with quote  
PostPosted: Mon Aug 27, 2012 5:08 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8138
Location: Midwestern USA
daivox wrote:
The code already checks for 65816 by first checking for 65C02, and only then using XBA instructions to determine if it's a 65816.

Yes, I can see where that would work, and it does have the advantage of not switching modes if the MPU is a 65C816—it stays in emulation mode following the test, although switching it back following an
XCE is a one byte operation.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Last edited by BigDumbDinosaur on Sun Aug 27, 2023 8:03 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 06, 2019 6:10 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
Performing thread necromancy to point out possibly the single easiest way to distinguish NMOS 6502s from anything later:
Code:
 BRA cmos
nmos:
 [...]
cmos:
This works because $80 (BRA) is an undocumented 2-byte NOP on NMOS. Now, whether emulators implement that quirk or not is an open question, but if you *know* you're running on real hardware…


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 03, 2021 12:12 pm 
Offline

Joined: Sun Feb 22, 2004 9:01 pm
Posts: 78
Digging up this thread, I've been trying to make so code to detect if I'm running on a 65816 or not. I don't need the whole suite of finding everything just is-65816 vs not-65816 (but with a complication that it could be a 6502 or a 65C02/12).

With some experimenting, I've come up with this. Does it look robust enough?

Code:
          ; 6502  A   65C02  A   65816  B   A
 CLC
 LDA #$00 ;       00         00         zz  00
 DB  #$EB ; SBC       NOP    00  XBA    00  zz
 DB  #$3A ; #$3A  C5  DEC A  FF  DEC A  00  yy
 DB  #$EB ; SBC       NOP    FF  XBA    yy  00
 DB  #$EA ; #$EA  DA  NOP    FF  NOP    yy  00

Distinct values for is-65816 and not-65816 (with a added 6502/65C02 that I don't need for this task).

So I can then BEQ m816:BNE m02.

_________________
--
JGH - http://mdfs.net


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 22, 2022 2:10 pm 
Offline

Joined: Sun Dec 15, 2019 12:08 am
Posts: 19
What about detecting a Sally 6502? I imagine it would test as a stock 6502 since MOS made it for Atari. Really, that just adds back a feature that MOS removed since the original die left room for multiplexers to take the 6502 off the bus. That also explains one of the NC pins. I guess they didn't feel like anyone needed to use bus-mastering DMA.

Sally was a modified 6502, B revision, despite it being listed as a C revision in Atari literature.


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 21, 2022 11:45 am 
Offline
User avatar

Joined: Tue Aug 11, 2020 3:45 am
Posts: 311
Location: A magnetic field
In response to jgharston, testing NMOS/CMOS/65816 is very easy. It is only complicated if you wish to cover obscure cases. As noted by Chromatix, NMOS is unable to follow BRA and this separates it from everything else. 65816 in emulation mode is only faithful to 65C02 if 65C02 instructions are executed. The extended instructions are always available. Although, register sizes are restricted in emulation mode. In response to jeffythedragonslayer (and being needlessly lambasted again by BigDumbDinosaur), and after considering the use of 65816 REP to clear multiple flags simultaneously (decimal, carry, interrupt), I find that REP can also be used to determine execution on 65816. This works in emulation mode or native mode.

SEC // REP #1 will clear carry on 65816 but leave carry set on 65C02. Unfortunately, while BRA ... SEC // REP #1 // BCC ... splits NMOS/CMOS/65816 into three chronological cases, it fails horribly on obscure processor variants, such as 65CE02. In this case, 65CE02 follows BRA. However, REP and SEP are interpreted as direct page, 16 bit increment or decrement (DEW and INW). The immediate value of REP is interpreted as the lower byte of the 16 bit decrement. Thankfully, carry is unaffected. However, DEW $01 may be incompatible with your operating system's allocation of direct page. In this case, REP can be used with higher values. On 65816, it will clear additional flags. On 65CE02, it will decrement at a higher address. Specifically: +$80 to clear negative flag, +$40 to clear overflow flag, +$08 to clear decimal flag. Or you can get fancy with +$04 to begin an atomic operation. Unfortunately, it is highly inadvisable to use +$20 to set accumulator to 16 bit or +$10 to set index registers to 16 bit because 65816 native mode cannot be assumed.

Do not abbreviate cases by omitting BRA. NMOS/65816 test seems very concise and convenient but it will cause chaos on 6510 and 6509 where REP #1 is illegal instruction LAX (dp,X) and simultaneously loads RegA and RegX. This leaves carry unchanged but may use memory location $0001 as the lower byte of a source address. On 6510 and 6509, LAX ($01,X) may strobe a bank latch before possibly strobing I/O. It is quicker and easier to test BRA rather than guard LAX (dp,X). It also preserves RegA and RegX across all three cases.

The obvious 65816 test is to invoke XCE. That is great if the test passes. However, XCE on NMOS becomes the three byte illegal instruction ISC abs,Y which is INC followed by SBC. In this case, XCE // NOP // NOP will become ISC $EAEA,Y. Worse, XCE on 65CE02 becomes PLZ (pull top of stack to RegZ). Overcoming that side-effect requires PHA // TSX // CLC // XCE // NOP // NOP // TXS // PLA // BCS which is longer, slower, requires two byte instruction sequence which is also a safe address region, destroys the contents of RegX, fails to distinguish NMOS/CMOS (which is partly why we do not use PHX/PLX and preserve RegX) and lacks the option to clear flags on 65816. XCE is otherwise a good choice.

Detecting CMOS/65816 with XBA // DEC // XBA is cunning but it doesn't generalize well. XBA on NMOS becomes the three byte illegal instruction DCP abs,Y which is DEC followed by CMP. This requires two instances of two byte instruction sequence which are also safe address regions. XBA on 65CE02 becomes an abs, 16 bit shift operation.

_________________
Modules | Processors | Boards | Boxes | Beep, Beep! I'm a sheep!


Top
 Profile  
Reply with quote  
PostPosted: Wed May 17, 2023 8:07 pm 
Offline

Joined: Wed May 17, 2023 10:30 am
Posts: 2
Hello,

I have designed a small PCB containing a bit of RAM, ROM and a LED interface to run this nice chunk of code checking what 6502 CPU type might disguise in Chinese re-labelled 6502 fake CPUs.

See details on https://www.forum64.de/index.php?thread ... 6502-cpus/

The forum is in German, but the images and the ZIP release file (Gerbers, code, docs, etc.) will be of use for English speaking individuals, too. :-)

After a long while, I had a look at the code again, and I am wondering if the part regarding the detection of Hudson Hu6280 is correct:

Code:
                    ldy #$01
                    rep #$08        ; HuC6280 sees "CLY,PHP" instead
                    cpy #$00
                    bne test_740    ; Not a HuC6280 CPU
                    plp             ; Undo the PHP instruction
                    lda #$20
                    ora zp0
                    sta zp0         ; Set HuC6280 flag
                                    ; Test for 65C02 with bit-op instructions
                                    ; This is pitifully simple: it sets the flag bit for us!
test_740            !byte $d7,zp0   ; WARNING: zp0 becomes opcode if SMB5 not usable!


This sets bit 5 to 1 which is not correct, I think. That bit is set later via SMB5 on if a R65C02 CPU is detected. The "Hudson Bit" is bit #4, so the parameter for the LDA should be #$10, not #$20 - or am I missing something?

Regards
kinzi


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 27, 2023 2:27 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
Also relevant to readers of this thread: http://forum.6502.org/viewtopic.php?f=2&t=5931

Since the HuC6280 and 65CE02 both interpret opcode $47 the same way as the Rockwell-extended 65C02, they will be identified as one by my code from that thread (returning ASCII 'C'). If you have reasonably free use of zero-page, I recommend running my code first, using the magic $47 opcode to distinguish between NMOS 'N', CMOS 'S', Rockwell-extended 'C', and 65816 '8' variants. If it comes out as CMOS, run the further test using BRA to check for emulators which treat all undocumented NMOS opcodes as 1-byte NOPs. If it comes out as Rockwell-extended, perform additional tests for the HuC6280 and/or 65CE02. You can then omit the additional test for the Rockwell instructions at the end of your routine.

Provided the presence of Rockwell instructions has already been verified, using REP #$08 to distinguish the HuC6280 looks like it would work. This instruction is harmless on the 65CE02, which interprets it as CPZ #imm. A convenient way to test for the 65CE02 as well would be to insert an CPY #1 between the LDY and REP, and a BNE between the REP and CPY. The 65CE02 initialises its extra Z register to zero, so comparing it to 8 will clear the Zero flag, whereas both the alternatives will have the Zero flag set at that point.

However, REP is opcode $C2 which a Renesas 740 would interpret as WIT (wait for interrupt), and furthermore the Rockwell opcodes are rearranged so that $47 in my first-line test would be interpreted as a 3-byte instruction BBS2 zp,rel. This is highly inconvenient, and implies that I have to put the test for a 740-series first of all.

Putting all of this together:
Code:
.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

; detect 6502 clone cores without (patented) Decimal arithmetic mode
  CMP #$4E ; ASCII 'N'
  BNE @naive
  CLI    ; avoid tripping up interrupt handlers with Decimal mode
  SED
  LDA #$90
  ADC #$10 ; in Decimal mode, result will be 0 or 1 with Carry set - in Binary mode, $A0 or $A1 with Carry clear
  CLD
  SEI
  LDA #$64 ; ASCII 'd'
  BCC @end
  LDA #$4E ; ASCII 'N'
; detect early NMOS 6502 without working ROR
; according to Michael Steil <https://www.pagetable.com/?p=406> faulty ROR behaves like ASL but doesn't update Carry
  ROL A
  ROR A
; with working ROR, Carry flag and accumulator would be unchanged
; with faulty ROR, accumulator is shifted left two places and Carry flag is cleared
  BCS @naive
  LDA #$4F ; ASCII 'O'

@naive:
; detect naive NMOS simulators
  CMP #$53 ; ASCII 'S'
  BNE @advanced
  BRA @advanced
  LDA #$6E ; ASCII 'n'

; detect 65CE02 and HuC6280
@advanced:
.P65816
  CMP #$43 ; ASCII 'C'
  BNE @end
  LDY #1 ; load Y with non-zero value
  CPY #1 ; set Zero flag
  REP #8 ; magic $C2 opcode: HuC6280 interprets as CLY,PHP - 65CE02 interprets as CPZ #8 - normal 65C02 interprets as NOP #imm
  BNE @ce02
  CPY #0
  BNE @end
  PLY
  LDA #$48 ; ASCII 'H'
  BRA @end
@ce02:
  LDA #$45 ; ASCII 'E'
@end:

; accumulator now contains ASCII:
; O - old NMOS 6502 (faulty ROR)
; N - real NMOS 6502
; n - NMOS simulator with 1-byte NOPs
; d - NMOS without Decimal mode (eg. Ricoh 2A03)
; S - 65SC02
; C - 65C02
; E - 65CE02
; H - HuC6280
; 8 - 65C816 or 65C802
This doesn't yet check for the Renesas 740. This check would probably have to be inserted at the top of the code.


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

All times are UTC


Who is online

Users browsing this forum: No registered users and 2 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: