flat assembler
Message board for the users of flat assembler.

 Index > Macroinstructions > 'pk' macro (works this time :)
Author
Borsuc

Joined: 29 Dec 2005
Posts: 2465
Location: Bucharest, Romania
Borsuc 01 Feb 2006, 18:46
Hey,

I developed the 'pk' macro, which I had trouble with a few weeks ago because I was new to Fasm.. And it's based on Tomasz's 'classical' fasm method he posted.. thanks again

The reason I built this up was because I wanted to do it much more flexible and it proved to be a really good learning of Fasm preprocessor. I know, the implementation is a bit ugly because it uses a LOT of nesting stuff.. but it's pretty fast, because it has little to do at each pass (mainly because most of the job is done by preprocessor). Any suggestions on how it can be improved, or just some feedback is welcome.

Description:
Quote:
The 'pk' macro can be pretty useful. It can either make the code more readable in some cases, and add some optimizations for situations like:
Code:
```mov bh, 5
mov bl, 1
mov ax, 2    ```
Which, written using the 'pk' macro pk mov bh:5, bl:1, ax:2 becomes:
Code:
```mov bx, 0x0501  ; bh is 5 and bl is 1
mov ax, 2    ```
packs the constant values after the ':' into corresponding registers; everything is processed from left to right, so if you define ah:5 and then eax:2, the value in ah (5) will be completely replaced.

NOTE: this macro packs ONLY constants (immediate values).

Another example:
Code:
`pk mov ah:5, eax:2    `
will generate
Code:
`mov eax, 2    `
because eax:2 was encountered after ah:5, and it completely replaced it, since eax occupies the entire register, so it is, in fact, mov eax, 0x00000002, which completely sets ah to 0!

Please be careful when using these instructions (in fact, those that modify flags, e.g: add, sub, xor...). They DO NOT generate the same flags. Always try to imagine what will become after these instructions, and do the flag operations accordingly.

Another example:
Code:
`pk and cx:1125h, eax:2, ch:3    `
This produces the actual:
Code:
```and cx, 0x0325
and eax, 2    ```

ch:3 replaces the 0x11 in cx's upper byte, because it is on the right, which means it is processed after cx... eax should be obvious

and cx, 0x0325 is used here before and eax, 2 because the counter (cl,ch,cx,ecx) register was found before the accumulator (al,ah,ax,eax).

It's important to note this is only a macro, not something magical, and as such you should not overuse it, especially in some circumstances where it generates improper or poor code. And watch out for the flags!

Here's it:

PS: "@err" signals error to preprocessor (is defined as @err fix macro +) and "err" to assembler.. if anyone has a idea for other ways, please share it

Code:
```macro pk [def]
{
common
; list holds the ordered register bases, which are in fact
; symbolic constants representing the size mask
;
; ex: FFFF means 16-bit register, FF00 means high-part 8-bit reg (ah,bh,ch,dh), etc...
;
define list +  ; dummy value to start

; base is register type (accumulator, counter, etc...)
; the SIZE MASK is the second symbol
; third symbol is shift left value (i.e ch has shift=8 )
;
; these are stored temporary in 't' symbolic constant
; 't' also serves for other purposes (like error checking)
define t

;
; each base name (eg: ax for accumulator, si for source index) has 2 type of prefixes
; if prefix is '_' then the specified symbolic constant contains the SIZE MASK
; if prefix is 'c' it means it's the 'immediate' or 'constant' value for the register
;
; eg: _ax is the SIZE MASK for accumulator
;     cax is the immediate value for accumulator
;
;     if _ax = \$FFFFFFFF
;       mov eax, cax
;     else if _ax = \$FFFF
;       mov ax, cax
;     ...
;
; or something like that
;
irp r, ax,bx,cx,dx,si,di,bp,sp \{ define _\#r \}  ; null-out all the SIZE MASKS

match instr p, def \{
irp parm, p \\{ match reg:v, parm \\\{

irp r, ax,bx,cx,dx,si,di,bp,sp \\\\{
match =e\\\\#r, reg \\\\\{ define t r FFFFFFFF 0 \\\\\}
match =r, reg \\\\\{ define t r FFFF 0 \\\\\} \\\\}

irp r, a,b,c,d \\\\{
match =r\\\\#h, reg \\\\\{ define t r\\\\\#x FF 8 \\\\\}
match =r\\\\#l, reg \\\\\{ define t r\\\\\#x FF 0 \\\\\} \\\\}

match , t \\\\{
@err ; invalid register
\\\\}

match base mask shift, t \\\\{
restore t  ; revert back to empty definition

if (~ v eqtype 0) | (v > \$\\\\#mask)
err ; invalid immediate operand
end if

match , _\\\\#base \\\\\{
match old, list \\\\\\{
restore list
define list old,base  ; append it to the list
\\\\\\}

restore _\\\\\#base
define _\\\\\#base 0  ; starting number (size mask)
define c\\\\\#base 0
\\\\\}

match size, _\\\\#base \\\\\{
restore _\\\\\#base
define _\\\\\#base (size) or (\$\\\\\#mask shl shift)  ; combine it with the mask
\\\\\}

match val, c\\\\#base \\\\\{
restore c\\\\\#base

; now calculate the immediate
; first clear the bits in val, within our current mask
; then 'or' it with the new value to yield the result
define c\\\\\#base (val) and not (\$\\\\\#mask shl shift)  or  ((v) shl shift)
\\\\\}

\\\\} define t + \\\}    ; just a signal

match , t \\\{
@err ; invalid arguments
\\\}
restore t                ; remove the signal
\\}

; iterate through the list and encode the specified instructions
; each element in the list is the name of a symbolic constant (base name)
; well, in fact it's the name of the symbolic constant without '_' prefix
; anyway, we also need it for constant value (with a 'c' prefix)
; '_' prefix means the SIZE MASK for the specified base register

restore t  ; remove the empty definition
define t + ; just a signal to indicate our  match instr p, def  did work

match +=,regs, list \\{   ; list began with a dummy '+' value, so ignore it and the comma
restore list

irp r, regs \\\{

if _\\\#r = \$FFFFFFFF   ; ex: eax
instr e\\\#r, c\\\#r
else if _\\\#r = \$FFFF  ; ex: ax
instr r, c\\\#r
else if _\\\#r = \$FF00  ; ex: ah
irp i, a,b,c,d \\\\{ match =i\\\\#x, r \\\\\{
instr i\\\\\#h, c\\\\\#r shr 8
\\\\\} \\\\}
else
irp i, a,b,c,d \\\\{ match =i\\\\#x, r \\\\\{
instr i\\\\\#l, c\\\\\#r
\\\\\} \\\\}
end if

restore _\\\#r,c\\\#r ; clean up
\\\}
\\}
\}

; see if pk has invalid arguments (if first match was false)
; use t as an arbitrary variable to signal if match was processed
match , t \{
@err ; invalid use of instruction argument
\}

; cleanup
restore t
}    ```

Any suggestions or improvements are welcome.
Regards

Last edited by Borsuc on 08 Feb 2006, 11:38; edited 1 time in total
01 Feb 2006, 18:46
vid
Verbosity in development

Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 04 Feb 2006, 16:15
it would be nice to say what your macro should do first. "pk" isn't very descriptive
04 Feb 2006, 16:15
RedGhost

Joined: 18 May 2005
Posts: 443
RedGhost 05 Feb 2006, 09:42
The_Grey_Beast wrote:
mov bx, 0x0501 ; bh is 5 and bl is 1

that never even crossed my mind when you need to set the low and high byte to just set the 16bit register (one less mov instruction) i think im gonna optimize some of my realmode/dos apps, thanks

_________________
redghost.ca
05 Feb 2006, 09:42
Borsuc

Joined: 29 Dec 2005
Posts: 2465
Location: Bucharest, Romania
Borsuc 08 Feb 2006, 10:51
You're welcome.
I tried to explain it in the Description quote, if you think you can add something there to make it more 'descriptive' feel free to suggest it
08 Feb 2006, 10:51
vid
Verbosity in development

Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 08 Feb 2006, 11:10
oh, sorry. but description should come first because then you don't know what are you reading about until you reach description
08 Feb 2006, 11:10
Borsuc

Joined: 29 Dec 2005
Posts: 2465
Location: Bucharest, Romania
Borsuc 08 Feb 2006, 11:38
thanks
fixed
08 Feb 2006, 11:38
vid
Verbosity in development

Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 08 Feb 2006, 11:55
by the way: are you sure "err" signals error to assembler? How did you define it? Because if it's undefined symbol then it causes error during parsing, meaning that
Code:
```if 0
err
end if    ```
causes error too
08 Feb 2006, 11:55
Borsuc

Joined: 29 Dec 2005
Posts: 2465
Location: Bucharest, Romania
Borsuc 08 Feb 2006, 12:13
it's not defined, since it's not a valid instruction anyway.
not to worry, i tested, but I'm also a little confused between parser and assembler.
08 Feb 2006, 12:13
vid
Verbosity in development

Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 08 Feb 2006, 12:17
after preprocessing, you have TEXTUAL source. During parsing, everything is translated into FASM internal binary form. All expressions, instruction arguments, strings etc. have some binary representation. But representation for directives/instructions (treated same by FASM) are pointers to handler of that directive/instruction. And of course, for undefined ones there is no handler, so error occurs during preprocessing, as there is no binary representation for undefined symbol and textual source cannot be translated to binary form.
08 Feb 2006, 12:17
Borsuc

Joined: 29 Dec 2005
Posts: 2465
Location: Bucharest, Romania
Borsuc 08 Feb 2006, 12:19
I thought parser stops when it sees if 0 because it only recognizes eqtype in eq..
it worked when I tested, maybe there's some trick I coded and I am not even aware of
08 Feb 2006, 12:19
vid
Verbosity in development

Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 08 Feb 2006, 13:06
oh, yes, i forgot, parser can actually also remove if-ed blocks which are already predictable without assembling (if 0, if abcd eq abcd, if a in <a,b,c> etc.) i shouldn't forget that, especially because it was my idea ...

but in this example it will (hopefully) cause error:
Code:
```a=1
if a=0
err
end if    ```
08 Feb 2006, 13:06
Borsuc

Joined: 29 Dec 2005
Posts: 2465
Location: Bucharest, Romania
Borsuc 08 Feb 2006, 13:09
it doesn't give error when i compile.
maybe because that's the assembly stage, not parse stage, and maybe it's different? (parser doesn't have numeric constants, that's why it's assembly stage)
08 Feb 2006, 13:09
Tomasz Grysztar

Joined: 16 Jun 2003
Posts: 8346
Location: Kraków, Poland
Tomasz Grysztar 08 Feb 2006, 13:19
There are almost no errors that may happen during parsing stage - the illegal instruction error like here happens at the assembly stage, not parsing.
08 Feb 2006, 13:19
vid
Verbosity in development

Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 08 Feb 2006, 13:22
you store something that stores such "parser" error and signals them only if they occur in final assembling pass? Or how does it work... i know you wrote you want to do something like that but then you didn't do because not all errors can be romeved from parser
08 Feb 2006, 13:22
 Display posts from previous: All Posts1 Day7 Days2 Weeks1 Month3 Months6 Months1 Year Oldest FirstNewest First

 Jump to: Select a forum Official----------------AssemblyPeripheria General----------------MainTutorials and ExamplesDOSWindowsLinuxUnixMenuetOS Specific----------------MacroinstructionsOS ConstructionIDE DevelopmentProjects and IdeasNon-x86 architecturesHigh Level LanguagesProgramming Language DesignCompiler Internals Other----------------FeedbackHeapTest Area

Forum Rules:
 You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot vote in polls in this forumYou cannot attach files in this forumYou can download files in this forum