Description

PHP is a mnemonic for an instruction that pushes the lowest 8 bits of the of the P register to the stack. An immediate consequence of this is that there is no specific instruction for pushing the emulation (9th) bit of P. XCE is the only instruction capable of switching back and forth between native and emulation mode.

Uses

Insulation

PHP and PLP can be used to insulate a subroutine from its caller as well as from subroutines that it calls. The following code snippets illustration this application of PHP.

Insulating your subroutine for the benefit of callers

FirstRoutine insulates itself from its own caller in this example by issuing a php instruction at the very beginning. Because we have cached the state of the P register on the stack, we can feel free to alter P to fit our programming needs throughout the duration of the routine. Indeed the very first thing we have done here is set the accumulator to 8-bits. We eventually call SecondRoutine. Note that when SecondRoutine has returned we are assuming that SecondRoutine cleaned up after itself and we can go on using 8-bit code.

FirstRoutine:
    
    php
    
    ; Set accumulator to 8-bits
    sep #$20

    lda #$10
    sta $c0
    
    jsr SecondRoutine

    lda #$11
    sta $c1
    
    plp
    
    rts

Insulating your code from other subroutines

SecondRoutine is also insulated from FirstRoutine by being "wrapped" in PHP / PLP. Notice how the call to ThirdRoutine is similarly wrapped with PHP / PLP instructions. This a very similar technique that should be used when there is no certainty that the subroutine you call will be able to clean up the state of P after it's done executing.

SecondRoutine:
    
    php
    
    ; Set accumulator to 16-bits
    rep #$20

    lda #$0404
    sta $d0
    
    ; Insulating a routine from one of its own subroutines can be avoided
    ; by writing subroutines that also insulate themselves.
    ; Using php and plp here is something you ideally shouldn't have to do.
    php
    jsr ThirdRoutine
    plp

    lda #$0408
    sta $d2
    
    plp
    
    rts

ThirdRoutine executes a loop using 8-bit registers and very inconsiderately modifies the state of P without saving it first.

ThirdRoutine:

    ; Set accumulator and index registers to 8-bit mode.
    sep #30
    
    lda #$15
    ldx #$20

.loop

    sta $20, x
    
    bpl .loop

    ; leaving without cleaning up the state of P is kind of rude,
    ; but some people, you can't tell em anything, now can you?
    rts

It is worth acknowledging that ubiquitously using PHP and PLP in this fashion increases code size marginally, but it has three major benefits that tend to outweigh that:

  1. The programmer can focus more on what each particular routine does and less on how the other routines may be affecting it.
  2. The programmer can freely change register sizes throughout the course of a routine without having to do a bunch of mental or other type of bookkeeping on what the sizes are supposed to be at the beginning and end.
  3. Some of the cost of adding PHP and PLP wrappings is offset by the fact that when a subroutine returns, the caller will not have to issue a SEP or REP instruction to insure the register sizes are correct before proceeding.

Caching a logical result

There are times when a programmer just doesn't have enough resources immediately available.

Move this to another page discussing register size concerns

The fact that the size of the Accumulator and Index Registers can be independently altered programmatically this presents a potentially disastrous model for 65c816 assembly language programmers. The most obvious hazard can be seen in the usage of Immediate instructions.

; address $7E000 = 0x1000
lda #$1000
sta $7E0000

It's clear from the way this code is written in text form that the programmer intends to take a 16-bit number, 0x1000, and store it to the addresses $7E0000 (low byte) and $7E0001 (high byte). This seems simple enough, but here's where the trouble starts. The opcode that corresponds to

lda #immediate

is 0xA9, without any regard to whether the M Flag in the P Register is set. If the programmer doesn't use a special marker indicating the width of the immediate argument to lda, and that's assuming the assembler even has this capability, the assembler will be forced to use an algorithm to guess the width of the argument. Since the assembler can't read the programmer's mind, it has to use information it gathers in the course of processing the files to make that determination. Thus, depending on how the assembler determines this, the output for the above code could be converted to machine code in one of two ways.

It could treat it as a 16-bit argument as the coder probably intended.

A9 **00 10** 8F 00 00 7E

And if it somehow determines that the Accumulator is supposed to be 8 bits wide at this location it will truncate the upper bits of the argument, leaving us with just 0x00.

A9 **00 8F** 00 00 7E

Some assemblers provide the programmer with a way to define variables or text replacement identifiers. Here's an example in xkas (version v0.06):

; Note that 99 is a decimal representation, not hexadecimal
**!someNumber** = 99

lda #**!someNumber**
sta $7E0000

Using a text based identifier can often be great for the programmer's sanity, but it obfuscates to a certain degree the width of the immediate argument. And this is especially true when the argument is a decimal number rather than hexadecimal. One can often just count the number of digits of a hex constant to determine the desired width of the argument, but this is not the case in the above example. After text replacement the lines will read as follows:

lda #**99**
sta $7E0000

This begs a question about the intended width of 99 in this code: "Is it the 8-bit version of 99 (0x63) or is it the 16-bit version of 99 (0x0063)"? It is often best practice, especially when register widths are being changed within your code a lot, or where it may be ambiguous, to specify the width of the argument to an immediate mode instruction.

How to specify an 8-bit argument in xkas (v0.06):

; assembles to 'A9 63 8F 00 00 7E
lda.b #!someNumber
sta $7E0000

How to specify a 16-bit argument in xkas (v0.06):

; assembles to 'A9 63 00 8F 00 00 7E
lda.w #!someNumber
sta $7E0000