SPONSORED LINKS
 
 
Google
 
N-WAY BRANCHING
 N-WAY BRANCHING

There are several different means for branching to sections of code determined or selected at run time. (The single destination address incorporated into conditional and unconditional jumps are, of course, fixed at assembly time). Each has advantages for different applications.

In. a typical N-way' branch situation, the potential destinations are generally known at assembly time. One of a number of small routines is selected according to the value of an index variable determined while the program is running. The most efficient way to solve this problem is with the MOVC and an indirect jump instruction, using a short table of offset values in ROM to indicate the relative starting addresses of the several routines.

JMP@A+DTPR is an instruction which performs an indirect jump to an address determined during program execution. The instruction adds the eight-bit unsigned accumulator contents with the contents of the sixteen-bit data pointer, just like MOVC, A,@A+DPTR. The resulting sum is loaded in to the program counter and is used as the address for subsequent instruction fetches. Again, a sixteen-bit addition is performed; a carry-out from the low-order eight-bits may propagate through the higher-order bits. In this case, neither the accumulator contents nor the data pointer is altered.

The example subroutine below reads a byte of RAM into the accumulator from one of four alternate address spaces, as selected by the contents of the variable MEMSEL. The address of the byte to be read in determined by the contents of R0 (and optionally R I). It might find use in a printing terminal application, where four different model printers all use the same ROM code but use different types (and sizes) of buffer memory for different speeds and options.

To use this approach, the size of the jump table plus the length of the alternate routines must be less than 256 bytes. The jump table and routines may be located anywhere in program memory.

For applications where up to 128 destinations must be selected, all residing in the same 2k page of program memory, the following technique may be used in the printing terminal example, this sequence could process 128 different codes for ASCII characters arriving via the 8051 serial port.

The destinations in the jump table (PROC00-PROC7F) are not all necessarily unique
routines: A large number of special control codes could each be processed with their own unique routine, with the remaining printing characters all causing a branch to a common routine for entering the character into the output queue.

COMPUTING BRANCH DESTINATIONS AT RUN TIME

Under certain circumstances 128 options are insufficient, the destination routes may cross a 2K page boundary, or a branch destination is not known at assembly time (for whatever reason), and therefore cannot be easily included in the assembled code. These situations can all be handled by computing the destination address at run-time with standard arithmetic or table look-up instructions, then performing an indirect branch to that address. There are two simple ways to execute this last step, assuming the 16-bit destination address has already been computed. The first is to load the address into the DPH and DPL registers, clear the accumulator and branch using the JMP@A+DPTR instruction; the second is to push the destination address onto the stack, low-order byte first (so as to mimic a call instruction) then pop that address into the PC by performing a return instruction. This also adjusts the stack pointer to its previous value.

IN-LINE-CODE PARAMETER PASSING

Parameters can be passed by loading appropriate registers with values before calling the subroutine. This technique is inefficient if a lot of the parameters are constants, since each would require a separate register to carry it and a separate instruction to load the register each time the routine is called.

If the routine is called frequently, a more code-efficient way to transfer constants is "inline-code" parameter-passing. The constants are actually part of the program code, immediately following the call instruction. The subroutine determines where to find them from the return address on the stack, and then reads the parameters it needs from program memory.

For example, assume a utility named ADDBCD adds a 16-bit packed BCD constant with a two-byte BCD variable in internal RAM and stores the sum in a different two-byte buffer. The utility must be given the constant and both buffer addresses. Rather than using four working registers to carry this information, all four bytes could be inserted into program memory each time the utility is called. Specifically, the calling sequence below invokes the utility to add 12.34 (decimal) with the string at internal RAM address 56H, and store the sum in a buffer at location 78H.

The ADDITION subroutine determines at what point the call was made by popping the return address from the stack into the data pointer high- and low- order bytes. A MOVC instruction then reads the parameters from program memory as they are needed. When done, ADDITION resumes execution by jumping to the instruction following the last parameter.

This example illustrates several points:
  • The "subroutine" does not end with a normal return statement; instead, an indirect jump relative to the data pointer returns execution to the first instruction following the parameter list. The two initial POP instructions correct the stack pointer contents.
  • Either an ACALL or LCALL works with the subroutine, since each pushes the address of the next instruction or data byte onto the stack. The call may be made from anywhere in the full 8051 address space, since the MOVC instruction access all 64K bytes.
  • The parameters passed to the utility can be listed in whatever order is most. convenient, which may not be that in which they are used. The utility has essentially "random access" to the parameter list, by loading the appropriate constant into the accumulator before each MOVC instruction.
  • Other than the data pointer, the whole calling and processing sequence only affects the accumulator, PSW and pointer registers. The utility. could have pushed these registers on to the stack (after popping the parameter list starting address), and popped before returning.
  • Passing parameters through in-line-code can be used in conjunction with other variable passing techniques.
    The utility can also get input variables from working registers or from the stack, and return output variables to registers or to the stack.
BACK
SPONSORED LINKS