6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Nov 22, 2024 5:13 am

All times are UTC




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: Feature Tests
PostPosted: Thu Apr 29, 2021 10:14 am 
Offline
User avatar

Joined: Tue Aug 11, 2020 3:45 am
Posts: 311
Location: A magnetic field
Subject: Feature Tests

After devising a tortuous byte sequence to determine 65816 mode and automatic host identification, I've considered various sequences to automatically determine host processor. This would allow, for example, block copy instructions to be used, if present, and alternative functionality, if not. Indeed, I hope that it is possible to provide a bitmask of processor bugs, bitmask of processor features, outline memory map and platform ABI revision. In the worst case, it should be possible to switch to an extended 65C02 virtual machine or similar. In this topic, I concentrate on differentiating NMOS 6502, 65C02, 65CE02 and 65816, with and without Rockwell bit tests. Apologies if I omit other useful cases. Likewise, I am likely to make errors. Apologies in advance and thank you for back-checking.

On 65CE02 and 65816, opcode $F4 pushes 16 bit immediate on stack. This is a three byte sequence. On other processor variants, opcode $F4 is usually a two byte NOP. Therefore, LDY #0 // TSX // $F4 // INY // INY // INY // INY // INY // TXS should restore stack while counting the length of the opcode. On 65CE02 and 65816, RegY should be three. In all other known cases, RegY should be four.

To distinguish between 65CE02 and 65816, opcode $BB either performs LDZ abs,X or TYX. Therefore, LDX #1 // LDY #0 // $BB // INX // INX // INX // INX // INX leaves RegY as zero on 65CE02 or sets ReyX to one on 65816. (A similar test is described in the Transputer Handbook.) As a further back-check, RegX should be four or six on known variants.

To distinguish between NMOS 6502 and 65C02, merely use unconditional branch (opcode $80). On NMOS 6502, this is a two byte NOP.

So far, these three tests distinguish NMOS 6502, 65C02, 65CE02 and 65816. However, this is only the basics. It does not cover processor bugs, individual features (such as Rockwell bit test extension) or other processor variants.

It is preferable to avoid random stack manipulation or random writes. Therefore, a load opcode is preferable. In this case, to test unrelated functionality including Rockwell extension, use opcode $A7 and a spare location in zero page. Opcode $A7 has very distinct action on different classes of 6502 processor architecture:-

  • On NMOS 6502, $A7 is an illegal instruction which simultaneously loads RegA and RegX. Specifically, LAX zp.
  • On an [url=XXX]architecture where illegal instructions are routed to a second accumulator[/url], opcode $A7 performs LDB zp.
  • On CMOS 6502 without Rockwell extension, it is a two byte NOP.
  • On CMOS 6502 with Rockwell extension, it sets bit two in a zero page location. Specifically, SMB2 zp.
  • On 65816, it specifies 24 bit indirection via zero page. Specifically, LDA [d].
  • On other variants, such as 32 bit extensions, it is likely to have unrelated functionality, such as instruction prefix.

My technique to distinguish variations in functionality is to start with a candidate sequence, such as LDA #0 // STA <TEMP // $A7 <TEMP // ORA <TEMP and then assemble and disassemble fuzzed sequences until something useful emerges. In this case, LDY #0 // LDA #4 // STA <TEMP // LDA #7 // LDB <TEMP // INY // INY // INY // INY // INY // AND #2 // STA <TEMP // STB <TEMP // INY // INY // INY // INY // INY should be suitably perverse. (Feel free to devise smaller versions but the gains are minimal.)

  • On NMOS 6502 this will be ... // LDA #4 // STA <TEMP // LAX <TEMP // ... // AND #2 // ... // SAX <TEMP // ... which will store zero.
  • On a processor with second accumulator, this will store four, as expected.
  • On CMOS 6502 without Rockwell extension, unknown instructions are NOPs of the appropriate length and it will store two.
  • On CMOS 6502 with Rockwell extension (and 65CE02), the sequence ends with STA <TEMP // SMB2 <TEMP // ... which will store six.
  • On 65816, operations intended for RegB are substituted with 24 bit indirection. Suitable set-up can be used to handle an unrelated value.
  • On other variants, RegY can be used to determine the length of the opcode. Other tests may be required.

As an example of a 64 bit extension test, 65CE02 and XR1600 can be distinguished by testing opcode $17 to determine bit operation or prefix extension. Specifically, LDA #0 // $17 $A9 // $17 $A9 only leaves RegA zero on 65CE02. It is either executed as LDA #0 // RMB1 <$A9 // RMB1 <$A9 or it is executed as LDA #0 // LDA.W #$A917. Tests for competing architecture extensions are most likely to test one or more $x7 or $xF opcodes.

Arguably, this is not feature testing but merely cleaving processor architectures into known cases. Therefore, I include a fairly basic feature test around PHX/PLX. If you are really pedantic, you can repeat it on PHY/PLY. On NMOS 6502, these are one byte NOPs. Therefore, LDX #1 // PHX // LDX #0 // PLX will correctly distinguish the presence of this basic CMOS functionality.

These tests generally destroy every available register. Regardless, I believe these tests and others yet to be devised would be useful if they were collected together and used to set bitmasks held within zero page. If this action is statically bound to an application and occurs at start-up, it may add less than 200 bytes to an application but allow guards which perform simultaneous feature tests before invoking one or more fall-backs. The only limit to this scheme is that a Rockwell bit test should not be used to determine the presence of Rockwell bit tests!


Attachments:
feature-test-flowchart0-0-2.pdf [101.07 KiB]
Downloaded 76 times
feature-test-flowchart0-0-1.odg [6.61 KiB]
Downloaded 63 times

_________________
Modules | Processors | Boards | Boxes | Beep, Beep! I'm a sheep!
Top
 Profile  
Reply with quote  
 Post subject: Re: Feature Tests
PostPosted: Sun May 09, 2021 12:20 pm 
Offline
User avatar

Joined: Tue Aug 11, 2020 3:45 am
Posts: 311
Location: A magnetic field
It is possible to perform processor architecture tests ahead of processor feature tests. This allows, for example, meta-data in Acorn or Ruby filing systems, such as load address and execution address to work across multiple processor architectures.

I have concentrated on 6502 interoperability with 6800, 6809 and Z80 although techniques may be extended to 8088, MC68008 or similar. In particular, anyone wishing to explore the 6800 fork of Apple I may switch processor without necessarily switching ROM.

The main problem that I've encountered is choice of opcode which simultaneously meets the criteria of not halting NMOS 6502 while not incurring memory cycle to uninitialized address on Z80 while not incurring illegal instruction exception on 6809.

6800 and 6809 re not binary compatible. However, many of the flow control instructions retain binary compatibility and this is sufficient to allow 6800/6809 to be considered as one case during the initial stage of processor architecture identification. In particular, opcode $7E is jump on 6800 and 6809 while on 6502 it is ROR abs,X. Unfortunately, ROR abs,X with uninitialized RegX does not scribble over the now useless 6800/6809 section of a fat binary. This is due to reverse endian representations.

Cleaving into 6502/Z80 and 6800/6809 is not necessarily the best technique. It may be preferable to split out 6502 first using opcode $4C which is JMP on 6502, a register increment on 6809 and a register transfer on Z80. Alternatively, opcode $2C and opcode $2D have the particularly helpful property of being a one byte instruction on Z80, two byte flow control on 6800/6809 and three byte read operations on 6502. This quickly teases apart representation and ensures that one processor's opcode is another processor's operand.

6502 shares vector locations with 6800 and 6809. Endian representations allow arbitrary choice of page to constrain page offset in the other representation. Unfortunately, there is the further complication that comparable vectors may be shuffled. For example, 6502's IRQ vector is 6809's reset vector. Regardless, vectors to, for example, $FAFB and $FBFA may have appropriate functionality.

Despite ROM vectors being distinct, there remain circumstances where 6502 and 6800/6809 should be distinguished without disrupting registers or flags. For example, where upper and lower byte of ROM vector matches. I found that the sequence $20, $02, $XX, $40 on 6502 is JSR $XX02 // RTI and on 6800/6809 unconditionally branches forward to the end of the sequence. This subroutine-or-skip construct is sufficient for a processor agnostic system.

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


Top
 Profile  
Reply with quote  
 Post subject: Re: Feature Tests
PostPosted: Thu May 27, 2021 12:34 pm 
Offline
User avatar

Joined: Tue Aug 11, 2020 3:45 am
Posts: 311
Location: A magnetic field
BillG provides an example test to distinguish 6800/6809. This is for the purpose of providing a meaningful error message on the FLEX operating system and exiting when a stray binary is run on the wrong system. This does not cover all cases but, in common cases, it is preferable to a less helpful error message or hang. We may be able to use this sequence and similar sequences for 8080 or Z80 CP/M. The minimum requirement is to output "This is a 6502 binary." and exit. This can be applied to all binaries with no further effort. In a minority of cases, an alternative bytecode interpreter may be very welcome functionality.

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


Top
 Profile  
Reply with quote  
 Post subject: Re: Feature Tests
PostPosted: Thu May 27, 2021 7:06 pm 
Offline

Joined: Sat Jan 02, 2016 10:22 am
Posts: 197
Opcode $18 might be a good choice, its CLC on the 6502 family and a short range branch on the Z80/8080. If it's followed by an instruction from the first half of the 6502 opcode list, that would translate to a forward branch on the 8080/Z80 and hopefully something harmless on a 6502.

So for example the 6502 sequence:
Code:
CLC
ROL A

When seen by a Z80/8080 is:
Code:
JR +2A


Top
 Profile  
Reply with quote  
 Post subject: Re: Feature Tests
PostPosted: Fri May 28, 2021 11:56 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 704
Location: North Tejas
Martin A wrote:
Opcode $18 might be a good choice, its CLC on the 6502 family and a short range branch on the Z80/8080.

$18 is one of the new Z80 instructions; it is undefined in the 8080 and 8085.


Top
 Profile  
Reply with quote  
 Post subject: Re: Feature Tests
PostPosted: Fri May 28, 2021 12:15 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 704
Location: North Tejas
In the modern emulation scene, execution of machine code from an arbitrary foreign processor can be forced.

In the real world, with wildly incompatible binary file, disk and cartridge formats, there are few cases in which code can be accidentally run on a "wrong" system. Unless the operating system expressly supports loading only the appropriate portions of "fat" binaries or if the hardware contains multiple processors sharing common memory, there is a substantial penalty for loading the code for an alien processor which will never be used.

In the name of intellectual curiosity, this is some code to prevent running 6502 code on a 680x processor:

Code:
 2000                     00001          org    $2000
                          00002
 2000 4C 2043         [3] 00003          jmp    CPUOK     ; 680x sees inca, coma, bra next instruction
                          00004
 2003 00                  00005          fcb    0
                          00006
 2004 4F                  00007          fcb    $4F       ; 680x clra ; Clear carry flag
                          00008
 2005 0D 0D               00009          fcb    $D,$D     ; 6800 set carry twice, 6809 tst $D
                          00010
 2007 24 09               00011          fcb    $24,$09   ; bcc CPU6809
                          00012
 2009 CE                  00013          fcb    $CE       ; 6800 ldx #BadCPU
 200A 20                  00014          fcb    >BadCPU
 200B 1B                  00015          fcb    <BadCPU
                          00016
 200C BD                  00017          fcb    $BD       ; 6800 jsr PSTRNG
 200D AD                  00018          fcb    $AD
 200E 1E                  00019          fcb    $1E
                          00020
 200F 7E                  00021          fcb    $7E       ; 6800 jmp WARMS
 2010 AD                  00022          fcb    $AD
 2011 03                  00023          fcb    $03
                          00024
 2012                     00025 CPU6809
 2012 8E                  00026          fcb    $8E       ; 6809 ldx #BadCPU
 2013 20                  00027          fcb    >BadCPU
 2014 1B                  00028          fcb    <BadCPU
                          00029
 2015 BD                  00030          fcb    $BD       ; 6809 jsr PSTRNG
 2016 CD                  00031          fcb    $CD
 2017 1E                  00032          fcb    $1E
                          00033
 2018 7E                  00034          fcb    $7E       ; 6809 jmp WARMS
 2019 CD                  00035          fcb    $CD
 201A 03                  00036          fcb    $03
                          00037
 201B 546869732070726F    00038 BadCPU   fcc    'This program requires a 6502 processor.'
 2023 6772616D20726571
 202B 7569726573206120
 2033 363530322070726F
 203B 636573736F722E
 2042 04                  00039          fcb    4
                          00040
 2043                     00041 CPUOK


Edit: fixed endian and conditional branch. Test before, not after posting...


Top
 Profile  
Reply with quote  
 Post subject: Re: Feature Tests
PostPosted: Fri May 28, 2021 5:02 pm 
Offline

Joined: Sat Jan 02, 2016 10:22 am
Posts: 197
BillG wrote:
Martin A wrote:
Opcode $18 might be a good choice, its CLC on the 6502 family and a short range branch on the Z80/8080.

$18 is one of the new Z80 instructions; it is undefined in the 8080 and 8085.

damn!

Serves me right for only checking in Zak's Z80 book. Can I be excused on the grounds I've not written any 8080 code in 30+ years 8)

Anyhow, attempt 2 then since the 8080 doesn't have any relative branches then maybe using on the 6502 JMP would work ?

Opcode 4C is a harmless register move on both Z80 and 8080.

so 6502
Code:
JMP &xx3E
becomes Z80
Code:
LD  C,H
LD  A,&xx
and 8080
Code:
MOV C,H
MVI A,&xx
Setting xx to the code's origin page means the &3E offset leaves enough space for the Z80/8080 code to report an error and quit sensibly or launch a different program.

One system where CPU detection like this would be useful a BBC/master with a Z80 co-processor, and a shared hard drive. You could have the boot sequence detect the CPU and setup directories etc appropriately. (Though of course the other code would work there too)


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

All times are UTC


Who is online

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