The .PH directive will switch over to the patch mode. The current program counter is saved during patch mode. The saved program counter will still count up for every byte that is saved to the target file in the background. At the same time a new, temporary, program counter is selected. The .EP directive switches the patch mode off and replaces the temporary program counter with the original one again.
In Version 3 of the SB-Assembler this directive will perform a boundary sync.
The expression may not contain forward referenced labels otherwise a fatal Undefined label error will be reported.
A previous patch mode will be automatically closed when a new patch mode is initiated. This allows you to write multiple patch routines in a row, where each patch routine gets its own address space.
Please note that the .OR directive also terminates an active patch mode automatically.
The patch mode is typically used to generate pieces of code that are moved to a different memory location by the processor before it is used as code. All labels that are defined during the patch mode automatically get the appropriate values as if the code really was generated at the intended address. In the mean time the code is saved to the target file in a consecutive order as if the program counter was not changed.
Originally the patch mode was intended to write some patch routines that should be copied into an existing program by a simple move routine, hence it's name.
Another useful application for the patch mode is writing code that is supposed to start at e.g. address $8000 of the target system, but should be burned to an EPROM starting at address $0000.
You can't simply use .OR $0000 because that would produce erratic destination addresses for jumps, calls and memory access instructions.
The patch mode can solve this little dilemma by first setting .OR $0000 and then set .PH $8000. Code will be saved to the target file starting at address $0000. But the SB-Assembler pretends to generate code that originates at address $8000.
The patch mode can also be used to write programs that should run in bank switched memory.
Imagine a system that has 4 banks of memory mapped in the address space from $0800 to $0FFF. For the first bank you can simply set the .OR to $0800. The code is sent to the target file starting at address $0800. But if you want to program the next memory bank you can't just use .OR $0800 again because that would effectively overwrite the code of the previous block of memory, which also started at $0800.
Example 2 explains how this can be solved by using the .PH directive.
A label declared on the line containing the .PH directive will get the value of the real program counter, not the modified program counter.
This simplifies calculation of the beginning and the length of the piece of patch code.
Thus when a patch mode is active and a new .PH line is found, with a label in the label field, the label will get the real program counter's value, not the value it would get from the previous patch code block.
Version 3 of the SB-Assembler has the more convenient .TA directive which can be used for mapping code to the appropriate ROM locations.
Warning The .PH directive can only be used in CODE memory in SB-Assembler Version 3. Using it in RAM or EEPROM memory will result in the *** Error: Only allowed in Code memory error to be reported.
Imagine a piece of 6502 program that should be copied to RAM memory to allow direct changing of addresses used by instructions.
8000- .OR $8000 Begin of program 8000-A0 0A INSTALL LDY #NEXT-PATCH1 Calc. length of patch 8002-B9 0C 80 .LOOP LDA PATCH,Y Get byte from ROM 8005-99 00 20 STA RAM,Y and put it in RAM 8008-88 DEY Decrement length counter 8009-10 F7 BPL .LOOP Repeat while Y>=0 800B-60 RTS 800C- 800C- PATCH .PH $2000 Destination of patch routine 2000-48 RAM PHA Save Accu on stack 2001-AD 00 00 LDA $0000 Address may be changed ; dynamically because it's ; stored in RAM now 2004-38 .LOOP NOP Any other code goes here 2005-68 PLA Restore Accu 2006-60 RTS 2007- .EP End of patch routine 8013- 8013-EA NEXT NOP The rest of ROM code
The INSTALL routine copies the patch routine to RAM location starting at address $2000.
In this case the patch routine may not be longer than 127 bytes to simplify the copy loop.
The first thing that's done is calculating the length of the routine that has to be moved by subtracting the end address of the patch routine in ROM ($8013) from the begin address of the patch routine in ROM ($800C, and not $2000!).
Then the patch routine is copied to RAM byte by byte.
The labels defined in the patch routine get the value as if the routine was already copied to its destination in RAM. The label RAM gets the value of $2000 and the Local label .LOOP is assigned the value $2004. In reality however the object bytes of the patch routine generated by the assembler are stored in the normal sequence at the addresses $800C to $8012, in ROM.
Imagine you want to create a program for the 8048 processor which can address only 4 kb of ROM memory. But you want to increase to ROM addressing range to 8 kb by using bank switching. The memory range $0000 to $07FF (2 kb) is always available. The memory range from $0800 to $0FFF is available 3 times in 3 different banks. Two I/O lines control what bank is selected.
0000- .OR $000 Begin of bank 0 ; Here come universal ; routines, the interrupt ; service routines and bank ; switch routines. .NO $800,$FF Fill bank 0 with $FF 0800- NOP Bank 1.1 starts at $0800, ; No special attention is ; required yet to do that. ; Here comes the code ; of bank 1.1 .NO $FFF,$FF Fill bank 1.1 with $FF 1000- .PH $800 Bank 1.2 again starts at 0800- ; address $0800. But this time ; the target address in the ; object file continues normally ; from $1000 where we are now. ; Here comes the code of bank 1.2 .NO $FFF,$FF Fill bank 1.2 with $FF 1000- .EP (.EP is optional here) 1800- .PH $800 Bank 1.3 also starts at 0800- ; address $0800. The target ; address still continues which ; is $1800 now. ; Here comes the code of bank 1.3 .NO $FFF,$FF Fill bank 1.3 with $FF .EP End of bank 1.3
Bank 0 of the program is nothing special from the SB-Assembler's point of view.
It contains some universal routines, interrupt service routines and the bank switch routines.
Bank 0 is always available to the target processor.
Bank 1.1 is a normal bank 1 which can be coded as usual. It starts at address $0800, immediately following bank 0. The bank continues to address $0FFF. The .NO directive fills the unused portion of bank 1.1 with $FF.
Bank 1.2 should also start at address $0800, at least the 8048 must think it does. But the address space from $0800 to $0FFF is already used in the EPROM. But by using the .PH directive we can pretend to start at address $0800 again, while in reality the code continues to be stored from address $1000 onward.
The procedure for bank 1.3 is equal to that for bank 1.2, with the only difference being the target address in EPROM which now has grown to $1800.
Please note that switching from bank 1.2 to bank 1.3 does not necessarily require the .EP directive.
The .PH directive would have automatically executed an .EP directive.
The last bank does not have to be filled completely because there are no more banks following it. Also the last .EP is optional. It is allowed to end the program without ending the patch mode first.
This example works for formatted and unformatted target files because all banks are filled completely by using the .NO directives at the end of every bank.
It is also possible to use the .OR directive instead of the .NO directive to start the new bank. In that case we can't use unformatted files however.