User Tools

Site Tools


simulating_20assembler_20macros

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

simulating_20assembler_20macros [2018/03/31 13:19]
127.0.0.1 external edit
simulating_20assembler_20macros [2018/04/13 11:40] (current)
richardrussell
Line 1: Line 1:
 =====Simulating assembler macros===== =====Simulating assembler macros=====
  
-//by Richard Russell, December 2008//\\ \\ //This article supplements the information in the main Help documentation under [[http://​www.bbcbasic.co.uk/​bbcwin/​manual/​bbcwina.html#​macros|Conditional assembly and macros]]//​.\\ \\  The assembler built into //BBC BASIC for Windows// doesn'​t have a conventional [[http://​en.wikipedia.org/​wiki/​Macro_(computer_science)|macro]] facility, but something similar can be simulated utilising the [[http://​www.bbcbasic.co.uk/​bbcwin/​manual/​bbcwin6.html#​opt|OPT]] pseudo-instruction. **OPT** is normally used to control the assembler options (for example whether a listing is generated) but in this application it has two useful features: firstly it generates __no code__ and secondly it takes a numeric parameter. This provides a means of calling a user-defined function from within the assembler, and if that function itself outputs code (or data) it can behave like a macro.\\ \\  One common use of a **macro** is to insert a block of code that you use frequently. Suppose your program often wants to find the [[http://​en.wikipedia.org/​wiki/​Absolute_value|absolute value]] of the signed 32-bit number in the **eax** register, but you don't want the overhead of making a subroutine call to do so. The following code simulates a macro for the purpose:\\ \\ +//by Richard Russell, December 2008//\\ \\ //This article supplements the information in the main Help documentation under [[http://​www.bbcbasic.co.uk/​bbcwin/​manual/​bbcwina.html#​macros|Conditional assembly and macros]]//​.\\ \\  The assembler built into //BBC BASIC for Windows// doesn'​t have a conventional [[http://​en.wikipedia.org/​wiki/​Macro_(computer_science)|macro]] facility, but something similar can be simulated utilising the [[http://​www.bbcbasic.co.uk/​bbcwin/​manual/​bbcwin6.html#​opt|OPT]] pseudo-instruction. **OPT** is normally used to control the assembler options (for example whether a listing is generated) but in this application it has two useful features: firstly it generates __no code__ and secondly it takes a numeric parameter. This provides a means of calling a user-defined function from within the assembler, and if that function itself outputs code (or data) it can behave like a macro.\\ \\  One common use of a **macro** is to insert a block of code that you use frequently. Suppose your program often wants to find the [[http://​en.wikipedia.org/​wiki/​Absolute_value|absolute value]] of the signed 32-bit number in the **eax** register, but you don't want the overhead of making a subroutine call to do so. The following code simulates a macro for the purpose: 
 + 
 +<code bb4w> ​
         DEF FNabs         DEF FNabs
         [OPT 0         [OPT 0
Line 9: Line 11:
         ]         ]
         = pass%         = pass%
-Now, whenever you want to calculate the absolute value of eax you can do so in your main assembler code as follows:\\ \\ +</​code>​ 
 + 
 +Now, whenever you want to calculate the absolute value of eax you can do so in your main assembler code as follows: 
 + 
 +<code bb4w> ​
         DIM code% 100, L% -1         DIM code% 100, L% -1
         FOR pass% = 8 TO 11 STEP 3         FOR pass% = 8 TO 11 STEP 3
Line 22: Line 28:
         NEXT pass%         NEXT pass%
         END         END
 +</​code>​
 +
 Here **FNabs** returns the current value of **pass%**, so in the main assembler loop the **OPT FNabs** pseudo-instructions simply set the assembler options (to what they already were). However the called function itself emits the opcodes corresponding to the absolute value calculation. You can see the effect by looking at the listing generated (note that the **OPT 0** in FNabs prevents any listing being generated there):\\ \\  Here **FNabs** returns the current value of **pass%**, so in the main assembler loop the **OPT FNabs** pseudo-instructions simply set the assembler options (to what they already were). However the called function itself emits the opcodes corresponding to the absolute value calculation. You can see the effect by looking at the listing generated (note that the **OPT 0** in FNabs prevents any listing being generated there):\\ \\ 
   1002181A ​                               OPT pass%   1002181A ​                               OPT pass%
Line 29: Line 37:
   1002181F 99 33 C2 2B C2                 OPT FNabs   1002181F 99 33 C2 2B C2                 OPT FNabs
   10021824 ​                               ; etc.   10021824 ​                               ; etc.
-Another way in which this facility can be used is to extend the assembler to handle instructions which are not supported natively. For example the instruction **SYSENTER** is available only on the Pentium Pro and later range of processors, and is therefore not supported in the //BBC BASIC for Windows// assembler. The following user-defined function can be used to '​add'​ that instruction:​\\ \\ +Another way in which this facility can be used is to extend the assembler to handle instructions which are not supported natively. For example the instruction **SYSENTER** is available only on the Pentium Pro and later range of processors, and is therefore not supported in the //BBC BASIC for Windows// assembler. The following user-defined function can be used to '​add'​ that instruction:​ 
 + 
 +<code bb4w> ​
         DEF FNsysenter         DEF FNsysenter
         [OPT 0         [OPT 0
Line 36: Line 46:
         ]         ]
         = pass%         = pass%
-Now, whenever you require to call the SYSENTER instruction,​ you can do that in your assembler code as follows:\\ \\ +</​code>​ 
 + 
 +Now, whenever you require to call the SYSENTER instruction,​ you can do that in your assembler code as follows: 
         OPT FNsysenter         OPT FNsysenter
-A similar technique can be used when the instruction takes a parameter, for example a register name:\\ \\ + 
 +A similar technique can be used when the instruction takes a parameter, for example a register name: 
 + 
 +<code bb4w> ​
         DEF FNfcmovb(reg$)         DEF FNfcmovb(reg$)
         [OPT 0         [OPT 0
Line 45: Line 61:
         ]         ]
         = pass%         = pass%
-This implements the **FCMOVB** instruction which takes as a parameter an FPU register name (**st0** to **st7**). The routine automatically adjusts the emitted opcode according to the register specified (for simplicity the operand is not checked for validity, but it could be):\\ \\ +</​code>​ 
 + 
 +This implements the **FCMOVB** instruction which takes as a parameter an FPU register name (**st0** to **st7**). The routine automatically adjusts the emitted opcode according to the register specified (for simplicity the operand is not checked for validity, but it could be): 
         OPT FNfcmovb("​st5"​)         OPT FNfcmovb("​st5"​)
 +
 With care the technique can be extended to instructions which take more complex operands.\\ \\  With care the technique can be extended to instructions which take more complex operands.\\ \\ 
 ==== Using labels in macros ==== ==== Using labels in macros ====
-\\  If you need to use labels in the assembler code generated by a macro, care is needed to prevent a **Multiple label** error occurring when the macro is called more than once. If the labels are genuinely '​local'​ to the macro (this implies only '​backwards'​ references) then you can simply declare them as **LOCAL**:\\ \\ +\\  If you need to use labels in the assembler code generated by a macro, care is needed to prevent a **Multiple label** error occurring when the macro is called more than once. If the labels are genuinely '​local'​ to the macro (this implies only '​backwards'​ references) then you can simply declare them as **LOCAL**: 
 + 
 +<code bb4w> ​
         DEF FNincarray         DEF FNincarray
         LOCAL label         LOCAL label
Line 59: Line 81:
         ]         ]
         = pass%         = pass%
-Often you can rearrange code to ensure all references to labels are '​backwards'​ (i.e. the label is defined before the reference to it) but sometimes '​forward references'​ are unavoidable. In such cases the macro must be adapted so that a //​different//​ label is created each time it is used. The easiest way of achieving that is to use an array element as the label:\\ \\ +</​code>​ 
 + 
 +Often you can rearrange code to ensure all references to labels are '​backwards'​ (i.e. the label is defined before the reference to it) but sometimes '​forward references'​ are unavoidable. In such cases the macro must be adapted so that a //​different//​ label is created each time it is used. The easiest way of achieving that is to use an array element as the label: 
 + 
 +<code bb4w> ​
         DEF FNsimplisticabs(i%)         DEF FNsimplisticabs(i%)
         PRIVATE label()         PRIVATE label()
Line 74: Line 100:
         label(i%) = label         label(i%) = label
         = pass%         = pass%
-Here the macro takes as a parameter an '​instance'​ value which is different for each use of the macro in your program:\\ \\ +</​code>​ 
 + 
 +Here the macro takes as a parameter an '​instance'​ value which is different for each use of the macro in your program: 
 + 
 +<code bb4w> ​
         DIM code% 100, L% -1         DIM code% 100, L% -1
         FOR pass% = 8 TO 11 STEP 3         FOR pass% = 8 TO 11 STEP 3
Line 87: Line 117:
         NEXT pass%         NEXT pass%
         END         END
-As shown, the macro can be called up to ten times from the program (0 to 9); if this is insufficient the size of the **label()** array may be increased.\\ \\  If it's inconvenient to pass an '​instance'​ value each time the macro is called (and to ensure they are all different) this may be automated by checking whether the value of **pass%** has changed, and if so initialising the array subscript:\\ \\ +</​code>​ 
 + 
 +As shown, the macro can be called up to ten times from the program (0 to 9); if this is insufficient the size of the **label()** array may be increased.\\ \\  If it's inconvenient to pass an '​instance'​ value each time the macro is called (and to ensure they are all different) this may be automated by checking whether the value of **pass%** has changed, and if so initialising the array subscript: 
 + 
 +<code bb4w> ​
         DEF FNsimplisticabs         DEF FNsimplisticabs
         PRIVATE label(), p%, i%         PRIVATE label(), p%, i%
Line 104: Line 138:
         i% += 1         i% += 1
         = pass%         = pass%
 +</​code>​
 +
 **Note:** Although the interpreter accepts the direct use of array elements as assembler labels, which would allow the above code to be slightly simplified, this is not strictly valid syntax and is not permitted by the compiler. **Note:** Although the interpreter accepts the direct use of array elements as assembler labels, which would allow the above code to be slightly simplified, this is not strictly valid syntax and is not permitted by the compiler.
simulating_20assembler_20macros.txt · Last modified: 2018/04/13 11:40 by richardrussell