Web 51 P-Code Interpreter
Portions of Web 51 utilize the P-Code Interpreter to achieve two goals:
- Allow a program code to be executed from other than the internal program memory - for example, to execute a program from external serial I2C memory.
- Shrink code size.
P-Code structure responds to several requirements:
- Commands should be addressed directly, no index table is to be used. In other words, a P-Code instruction should contain the address of the procedure that performs it (the P-Procedure).
- Command parameters may be specified in either MSB-LSB or LSB-MSB order.
- Parameters may be specified indirectly, as pointers into RAM.
- A command has at most 3 parameters, each at most 16 bits wide.
Taking into account the above requirements, the following structure can be used:AAAAAAAA procedure address MSB AAAAAAAA -"- LSB __SSSIII options: (S)wap LSB/MSB, (I)ndirect address 11111111 parameter 1 MSB (MSB1) 11111111 -"- LSB (LSB1) 22222222 parameter 2 MSB (MSB2) 22222222 -"- LSB (LSB2) 33333333 parameter 3 MSB (MSB3) 33333333 -"- LSB (LSB3)
Such a code would use 9 bytes per instruction. That is quite a lot, considering that many commands won't use all three parameters. More, parameters are often only 8 bits wide. Solution is simple. The P-Code structure will contain 6 additional bits, specifying whether any of the corresponding parameter bytes (MSB1 to LSB3) are equal to zero. Additional bits allow variable P-Code length - now it is possible to omit entire parameters or their MSB/LSB parts if equal to zero. Placing a little limitation on MSB/LSB swapping of the second parameter and shortening procedure address to 13 bits, the following structure is obtained:IIIAAAAA procedure address MSB, (I)ndirect parameter address AAAAAAAA -"- LSB abcdefSS option: (S)wap LSB/MSB, parameter MSB1..LSB3 non-zero 11111111 parametr 1 MSB (MSB1 - a) 11111111 -"- LSB (LSB1 - b) 22222222 parametr 2 MSB (MSB2 - c) 22222222 -"- LSB (LSB2 - d) 33333333 parametr 3 MSB (MSB3 - e) 33333333 -"- LSB (LSB3 - f)
Resulting P-Code instruction is 3 to 9 bytes long. The following macro (from an old versions of our development system) shows its construction:; ; pcode EQU ; blank equ 0 none EQU 0000h IND1 EQU 8000h ;par1 INDirect IND2 EQU 4000h ;par2 INDirect IND3 EQU 2000h ;par3 INDirect SWAP1 EQU 8002h ;always Indirect & Swap SWAP3 EQU 2001h ; DP MACRO fce, option, par1, par2, par3 DW fce + (option and 0E000h) btk1 SET 0 btk2 SET 0 btk3 SET 0 btk4 SET 0 btk5 SET 0 btk6 SET 0 if (par1 SHR 8) <> 0 btk1 SET 10000000b endif if (par1 AND 0FFh) <> 0 btk2 SET 01000000b endif if (par2 SHR 8) <> 0 btk3 SET 00100000b endif if (par2 AND 0FFh) <> 0 btk4 SET 00010000b endif if (par3 SHR 8) <> 0 btk5 SET 00001000b endif if (par3 AND 0FFh) <> 0 btk6 SET 00000100b endif DB btk1 or btk2 or btk3 or btk4 or btk5 or btk6 or (option and 0003h) if (par1 SHR 8) <> 0 DB high(par1) endif if (par1 AND 0FFh) <> 0 DB low(par1) endif if (par2 SHR 8) <> 0 DB high(par2) endif if (par2 AND 0FFh) <> 0 DB low(par2) endif if (par3 SHR 8) <> 0 DB high(par3) endif if (par3 AND 0FFh) <> 0 DB low(par3) endif ENDM
Parameter definitions at the beginning indicate that SWAP flags are only effective together with corresponding INDirect flags (it is possible to swap LSB/MSB of a literal value at compile-time). Procedure address definition says that 13 bits are available. This is not entirely true. A termination character, binary zero, is used to indicate that a P-Code section ends and machine code continues (similarly to ASCIIZ text strings). A P-Code sequence is terminated by, for example, .byte 0 (or DB 0 for Intel/Keil assemblers). However, this zero byte must not be taken as the address field of another P-Code instruction without INDirect parameters. Therefore, MSB of the address may not be zero. This further restricts the address range of P-Code execution procedures to 100h...1FFFh, and must be taken into account when linking. No P-Procedure may begin within 0000...00FFh.
Parameters are passed by the P-Code interpreter to the P-Procedures in registers:
- R6(MSB)R7(LSB) - 1st parameter
- R4(MSB)R5(LSB) - 2nd parameter
- R2(MSB)R3(LSB) - 3rd parameter
If the P-Code instruction has less than 6 bytes of parameters, omitted values are zeroed out. First, parameters are read into the registers. Then, any INDirect parameters are processed. If the flag is set, LSB part of the corresponding register pair (that is, register R7, R5 or R3) is taken as an address into the internal RAM and a 16-bit value is fetched in MSB-LSB order, as shown in the following code fragment:NoOpt2: JNB token.7,noI1 MOV A,R7 MOV R0,A MOV A,@R0 MOV R6,A INC R0 MOV A,@R0 MOV R7,A noI1: JNB token.6,noI2
Then, Swap flags are evaluated, swapping LSB and MSB parts of corresponding register pairs if set. Again, a code fragment is the best explanation:noI3: jnb pcodeOption.1,NoOpt1 MOV A,R6 ;swap1 XCH A,R7 MOV R6,A NoOpt1: jnb pcodeOption.0,NoOpt0
The DP macro (above) is universal. It automatically detects sizes (and presence) of parameters. However, since a program can be split into modules, it is necessary to have the option of specifying sizes (BYTE/WORD) of parameters explicitly. Macros DPB__, ..., DP__B, ..., DPW__, where B, W or _ at the appropriate position indicates byte-, word- or auto-sized parameter, serve for this purpose.
Current version of the development system uses native support of mcs51-as (assembler).
Syntax of the .pcode command is similar to that of standard MCS51 instruction set. Three main addressing modes are used:
- direct constant - specified by # prefix, e.g.
.pcode xmit_frame #SIZE_OF_ETH_PKT_HDR+SIZE_OF_ARP
- variable - no prefix at all, e.g.
.pcode paddwi IPlen, #(IP_HEADER_LEN+TCP_HEADER_LEN)
- indirect variable - specified by @ prefix, e.g.
.pcode paddwi BYTE data_addr, @(buf+(TCP_SEQ-IP_LEN)+2)
What are the differences?
Direct constant and variable are the same - both pass the given value into the P-Procedure. Different syntax for a constant and a variable only serves to make source code more legible. Whether the parameter is a constant or an address of a variable depends only on the P-Procedure being called.
Indirect constant sets the P-Code Indirect flag. P-Code Interpreter fetches the value from the specified address before calling the P-Procedure, as shown above.
Several other modifiers effect parameters passing:
P-Code Interpreter, part 2
- #BYTE, BYTE - when the actual size of a parameter is unknown at compile-time, compiler selects 2 bytes by default. If it is certain that the parameter will be at most 8 bits wide (e.g. an address into the internal RAM), it is possible to override the default with the BYTE modifier. Actual size is checked by compiler/linker and an error is reported if a mismatch is detected.
- #WORD, WORD - turns off autodetection of 8-bit values, 16-bit value will be passed.
- #SWAP, SWAP - sets Swap flag; the specified value will have LSB and MSB swapped before the P-Procedure is called. #SWAP and SWAP expect 16-bit parameter.
- @BYTE, @WORD - First, the 8-bit parameter is fetched into a temporary variable, and then used to fetch a 16-bit value from the internal RAM, that gets passed to the P-Procedure. Modifiers BYTE/WORD respect that indirect addressing fetches MSB first. If a 8-bit value is used in the P-Procedure, the indirect address must be decremented by 1 (here the unused MSB is stored). If a @BYTE modifier is specified, the compiler inserts this correction automatically.
- @SWAP - Sets the Swap flag; that is, the given 8-bit value is read into a temp variable and taken as an address into the internal RAM. There, a 16-bit value is fetched, its MSB and LSB are swapped, and the result is passed to the P-Procedure.
- #SHL8, SHL8 - Expects a 8-bit parameter that is passed as the MSB of a 16-bit value to the P-Procedure (LSB of this value is zero, just like any other unspecified parameter).
|Web51 description||News||FAQ||ORDER FORM||DOWNLOAD||Links|
|(c)Copyright 2000 - 2002, HW server & Radek Benedikt