Puzzle: branching on the m and x flags

Programming the 6502 microprocessor and its relatives in assembly and other languages.
User avatar
dclxvi
Posts: 362
Joined: 11 Mar 2004

Puzzle: branching on the m and x flags

Post 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.)
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: Puzzle: branching on the m and x flags

Post 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.
User avatar
BitWise
In Memoriam
Posts: 996
Joined: 02 Mar 2004
Location: Berkshire, UK
Contact:

Re: Puzzle: branching on the m and x flags

Post 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
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
White Flame
Posts: 704
Joined: 24 Jul 2012

Re: Puzzle: branching on the m and x flags

Post 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.
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: Puzzle: branching on the m and x flags

Post by BigEd »

Aha! Testing the behaviour, not the flags themselves. Neat!
White Flame
Posts: 704
Joined: 24 Jul 2012

Re: Puzzle: branching on the m and x flags

Post 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.
whartung
Posts: 1004
Joined: 13 Dec 2003

Re: Puzzle: branching on the m and x flags

Post 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.
User avatar
Alarm Siren
Posts: 363
Joined: 25 Oct 2016

Re: Puzzle: branching on the m and x flags

Post 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.
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.
User avatar
dclxvi
Posts: 362
Joined: 11 Mar 2004

Re: Puzzle: branching on the m and x flags

Post 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.
White Flame
Posts: 704
Joined: 24 Jul 2012

Re: Puzzle: branching on the m and x flags

Post 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 
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Puzzle: branching on the m and x flags

Post 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.
White Flame
Posts: 704
Joined: 24 Jul 2012

Re: Puzzle: branching on the m and x flags

Post 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.
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Puzzle: branching on the m and x flags

Post 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.
User avatar
dclxvi
Posts: 362
Joined: 11 Mar 2004

Re: Puzzle: branching on the m and x flags

Post 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.
White Flame
Posts: 704
Joined: 24 Jul 2012

Re: Puzzle: branching on the m and x flags

Post 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.
Post Reply