6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Nov 22, 2024 4:01 pm

All times are UTC




Post new topic Reply to topic  [ 27 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Thu May 03, 2018 4:55 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
Here is a 65C816 puzzle I found interesting: how to copy the m or x flag into a flag that you can branch on (i.e. the n, v, z, or c flag). In fact, it can be thought of as four sub-puzzles in one, which I have arranged (roughly) in order of increasing difficulty.

The rules are:

A. BPL, BVC, BNE, or BCC must branch to the 16-bit case, i.e. n,v,z, or c is 0 when m or x is 0. (Therefore, BMI, BVS, BEQ, or BCS would branch to the 8-bit case). The other way around is not a valid solution. (From a practical standpoint, it shouldn't matter which branch instruction covers which case, but this restriction makes the puzzles slightly more challenging.)

B. It must produce a correct result no matter what the initial values of the registers and flags are. In particular, you may not assume that the DBR bank is RAM (it could be ROM); in other words, STA ABS does not necessarily store A in a RAM location (unless your code specifically sets the DBR to a RAM bank). Likewise, you may not assume that D register would reference RAM (it might reference I/O); in other words STA DP does not necessarily store in RAM location (unless your code specifically sets the D register to a RAM location). However, you may, if you wish, assume that the S register will reference RAM; in other words, you may use PHA and PLA (or LDA 1,S) if you like.

The (sub)puzzles are:

1. What is the smallest code that will copy the m flag into a flag you can branch on? For a minimal size solution, what is the fewest number of flags, registers, and RAM locations that must be overwritten?

2. Same as #1 (both questions), except copying the x flag instead of the m flag.

3. Same as #1 (both questions), except that all registers (except the P register), all RAM locations, and all flags that can't be branched on (i.e. the m, x, b, d, i, and e flags) must be preserved; in other words, if you are copying the m flag to the v flag you may overwrite the n, z, and c flags, but you must preserve the d flag.

4. Same as #3, except copying the x flag instead of the m flag.

There are multiple answers for each (sub)puzzle. The best I could do was 3 bytes for #1, and #2, and 4 bytes for #3 and #4. (Note that this byte count does not include any subsequent BCC etc. instruction.)


Top
 Profile  
Reply with quote  
PostPosted: Thu May 03, 2018 5:20 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
Bit of a challenge! Quick recap from your doc:
Quote:
The P register contains 9 (yes, 9) of the flags.
    P register bit 7: n flag
    P register bit 6: v flag
    P register bit 5: m flag (native mode)
    P register bit 4: x flag (native mode), b flag (emulation mode)
    P register bit 3: d flag
    P register bit 2: i flag
    P register bit 1: z flag
    P register bit 0: c flag
The e flag is separate from the P register but is accessed via the c flag using the XCE instruction (details below).


As a starting point, I'd probably think of
Code:
PHP
PHP
PLA
<something>
PLP
but then of course the PLP has restored everything, as opposed to 'everything else' - and I've used quite a few bytes.


Top
 Profile  
Reply with quote  
PostPosted: Thu May 03, 2018 8:29 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
The sequence PHP/PLA is dangerous as P always 8 bits and A may be either 8 or 16. Any BIT or AND instruction could fail unless you set M to known state as part of the test.

So you probably need to do something like:
Code:
PHP      ; Save MX
.longa off
SEP #$20 ; Ensure A is 8-bits
PHA      ; Save A
LDA 2,S  ; Fetch Flags
LSR A    ; Shift MX down to ZC bits
LSR A
LSR A
LSR A
EOR 2,S  ; Difference with current P
AND #$03 ; Limit to ZC bits
EOR 2,S  ; And update P
STA 2,S
PLA      ; Restore A
PLP      ; Restore flags with MX in ZC

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Thu May 03, 2018 9:44 am 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 679
Code:
ldx #$ea00


In 8-bit index mode, that's ldx #$00 nop, so Z and /N.

In 16-bit index mode, that's ldx #$ea00, so /Z and N.

Switch to lda to test the m bit. Blows a register, though.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Thu May 03, 2018 10:01 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
Aha! Testing the behaviour, not the flags themselves. Neat!


Top
 Profile  
Reply with quote  
PostPosted: Thu May 03, 2018 11:14 am 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 679
Hmm, I've never actually programmed the 816 before, but if BIT # does 16-bit immediates, then a similar trick could be used as well, without sacrificing the accumulator, if the high 2 bits of the immediate value get shoved into N and V. Ruins Z, though.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Thu May 03, 2018 5:27 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
White Flame wrote:
Code:
ldx #$ea00


In 8-bit index mode, that's ldx #$00 nop, so Z and /N.

In 16-bit index mode, that's ldx #$ea00, so /Z and N.

Switch to lda to test the m bit. Blows a register, though.

Heh, yea, that's really clever.


Top
 Profile  
Reply with quote  
PostPosted: Thu May 03, 2018 6:10 pm 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
White Flame wrote:
Code:
ldx #$ea00


In 8-bit index mode, that's ldx #$00 nop, so Z and /N.

In 16-bit index mode, that's ldx #$ea00, so /Z and N.

Switch to lda to test the m bit. Blows a register, though.


You have officially blown my mind. So sneaky.

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 04, 2018 4:44 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
White Flame wrote:
Code:
ldx #$ea00

Well done!

The first solution I stumbled upon was A9 00 3A which copies m to n. But since DEX is CA and DEY is 88, I didn't immediately get a solution for the x flag, unlike your solution which makes the solution for both obvious. I wasn't able to find any 3 byte solution that overwrote less than 1 register and 2 flags.

For #3 and #4, as mentioned above, 4 bytes is the smallest solution I found when preserving the A and X (or Y) registers. However, the minimum number of flags overwritten was different for the m flag solutions and x flag solutions I found.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 04, 2018 8:55 am 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 679
m -> c, clobbers z:
Code:
clc
bit #$3800 ; sec hidden in 16-bit mode


x -> c, clobbers n & z
Code:
e0 00 24 18

; when x flag = 1
cpx #$00   ; always sets carry, clobbers n & z
bit #$18   ; clobbers z

; when x flag = 0
cpx #$2400 ; clobbers n, z, c
clc        ; always clears c

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Fri May 04, 2018 2:48 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1949
Location: Sacramento, CA, USA
Ah! I somehow knew that the shortest solutions would use BIT# and CPX#, but I lacked the raw talent to connect the last few dots on my own!

Very impressive.

Mike B.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 04, 2018 11:24 pm 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 679
I was actually trying to mask a SEP/REP instruction to directly fiddle one of the branchable status bits, but that always ended up 1 byte longer. Having cpx #$00 set the carry was the key, combining in flag behavior that wouldn't be clobbered easily by the next instructions, as opposed to N or Z.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Sat May 05, 2018 12:43 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1949
Location: Sacramento, CA, USA
Code:
e0 00 24 18

; when x flag = 1
cpx #$00   ; always sets carry, clobbers n & z
bit #$18   ; clobbers z

; when x flag = 0
cpx #$2400 ; clobbers n, z, c
clc        ; always clears c

$24 is the opcode for BIT d, not BIT #, but it works out more reliably with BIT d anyway, because it's always a two-byte instruction.

Mike B.


Top
 Profile  
Reply with quote  
PostPosted: Sat May 05, 2018 2:31 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
Technically, using BIT DP is a correct solution. However, recall rule B:
Quote:
...Likewise, you may not assume that D register would reference RAM (it might reference I/O); in other words STA DP does not necessarily store in RAM location (unless your code specifically sets the D register to a RAM location)...

So there is the possibility of a side effect since BIT DP could access an I/O location. A solution without any side effects exists. (And it's faster too.) BIT # is not correct as it assumes m=1 when x=1.


Top
 Profile  
Reply with quote  
PostPosted: Sat May 05, 2018 10:39 am 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 679
Yeah, I goofed on that. I originally had BIT dp, but remembered about the I/O clause, flipping it to immediate, and didn't have a lot of time to prove it before posting. Again, I've never coded for the 65816 yet, so this is fun to learn anyway. ;)

Now, let's see, one reference I saw had WDM without any parameters, but now I see it could be a 2-byte no-op instruction. If it's the latter, then that would be a good substitution for the BIT. Not sure how many cycles it would be.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 27 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

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