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. This is known as a phantom bit. XCE is the only instruction capable of switching back and forth between native and emulation mode.
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
PLP. Notice how the call to
ThirdRoutine is similarly wrapped with
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:
- The programmer can focus more on what each particular routine does and less on how the other routines may be affecting it.
- 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.
- 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
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
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