6502/65816 CPU variant detection code
Posted: Sun Aug 26, 2012 6:37 am
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.
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: Select all
!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