Page 1 of 2

Puzzle: branching on the m and x flags

Posted: Thu May 03, 2018 4:55 am
by dclxvi
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.)

Re: Puzzle: branching on the m and x flags

Posted: Thu May 03, 2018 5:20 am
by BigEd
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: Select all

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.

Re: Puzzle: branching on the m and x flags

Posted: Thu May 03, 2018 8:29 am
by BitWise
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: Select all

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

Re: Puzzle: branching on the m and x flags

Posted: Thu May 03, 2018 9:44 am
by White Flame

Code: Select all

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.

Re: Puzzle: branching on the m and x flags

Posted: Thu May 03, 2018 10:01 am
by BigEd
Aha! Testing the behaviour, not the flags themselves. Neat!

Re: Puzzle: branching on the m and x flags

Posted: Thu May 03, 2018 11:14 am
by White Flame
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.

Re: Puzzle: branching on the m and x flags

Posted: Thu May 03, 2018 5:27 pm
by whartung
White Flame wrote:

Code: Select all

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.

Re: Puzzle: branching on the m and x flags

Posted: Thu May 03, 2018 6:10 pm
by Alarm Siren
White Flame wrote:

Code: Select all

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.

Re: Puzzle: branching on the m and x flags

Posted: Fri May 04, 2018 4:44 am
by dclxvi
White Flame wrote:

Code: Select all

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.

Re: Puzzle: branching on the m and x flags

Posted: Fri May 04, 2018 8:55 am
by White Flame
m -> c, clobbers z:

Code: Select all

clc
bit #$3800 ; sec hidden in 16-bit mode
x -> c, clobbers n & z

Code: Select all

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 

Re: Puzzle: branching on the m and x flags

Posted: Fri May 04, 2018 2:48 pm
by barrym95838
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.

Re: Puzzle: branching on the m and x flags

Posted: Fri May 04, 2018 11:24 pm
by White Flame
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.

Re: Puzzle: branching on the m and x flags

Posted: Sat May 05, 2018 12:43 am
by barrym95838

Code: Select all

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.

Re: Puzzle: branching on the m and x flags

Posted: Sat May 05, 2018 2:31 am
by dclxvi
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.

Re: Puzzle: branching on the m and x flags

Posted: Sat May 05, 2018 10:39 am
by White Flame
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.