flat assembler
Message board for the users of flat assembler.

Index > DOS > Assembling legacy 16-bit sources with fasmg

Author
Thread Post new topic Reply to topic
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8363
Location: Kraków, Poland
Tomasz Grysztar 16 Apr 2019, 15:11
This is a continuation of efforts started in a thread about adapting/assembling PSR Invaders 1.1.

rugxulo wrote:
If you're super bored and truly want other files to convert, take a look at XGREP (MASMv4 syntax?? "struc", yuck!) or Kaboom (weirdo MASMv6). And there are many other quirky dialects. (One guy wrote a DOS RAM disk driver, SHSURDRV, using his own eccentric NASM macros, but it only works in old 0.98.39 from 2005.)


OK, I found a little time to try tackling your challenges, starting with XGREP103. This one demonstrates intermixing code and data segments and also some basic STRUC handling. Here's a minimal header that allowed me to assemble it correctly:
Code:
include 'cpu/80386.inc'

PAGE? = 256

macro XLAT?
        xlatb
end macro

macro format def
        ?format def
end macro

; use an added term to distinguish labels from absolute values:

element COMSEG
ORG COMSEG 

; register ASSUMEs, though emulation is currently limited to insertion of CS prefix under DS:NOTHING setting

macro ASSUME? statement&
        iterate each, statement
                match reg:seg, each
                        define ASSUMED?.reg? seg
                end match
        end iterate
end macro

; SEGMENTs grouped together into a single one for .COM file, initial 100h bytes ignored

struc GROUP? segments&
end struc

macro COMMIT_SEGMENTS
end macro
;postpone
macro END?.start
        COMMIT_SEGMENTS
        if $ - COMSEG = $%
                load COMCONTENT:$%%-100h from :100h
                restartout 100h
                db COMCONTENT
        end if
;end postpone
end macro

struc (name) SEGMENT? attr

        match alignment more, attr
                name.ALIGNMENT := alignment
        end match

        if defined name.SEG
                virtual name
        else
                name.SEG = 1
                name.SEG = 1 ; force variable
                virtual at name.BASE
                name::
                macro COMMIT_SEGMENTS
                        COMMIT_SEGMENTS
                        align name.ALIGNMENT
                        name.BASE:
                        virtual name
                                load name.CONTENT : $@ - $$ from $$
                                name.BSS = $ - $@
                        end virtual
                        if name.CONTENT
                                db name.CONTENT
                        end if
                        rb name.BSS
                end macro
        end if

        struc (ename) ENDS?
                match =name, ename
                        restruc ENDS?
                        end virtual
                else
                        err 'mismatched ENDS, expected one for ',`name
                end match
        end struc
end struc

; use ALIGN variant that ignores variable bases:

macro ALIGN? pow2*,value:?
        db  (-($ scale 0)) and (pow2-1)  dup value
end macro

; handle OFFSET in DW definitions:

macro DW? definitions&
        iterate unit, definitions
                match =OFFSET? addr, unit
                        match seg:offs, addr
                                dw offs - COMSEG
                        else
                                dw addr - COMSEG
                        end match
                else
                        dw unit
                end match
        end iterate
end macro

struc DW? definitions&
        label . : WORD
        DW definitions
end struc

; several constructions have a very straightforward emulation:

struc MACRO? definition&
        purge . ; disable forward-referencing
        esc macro . definition
end struc

macro ENDM?!
        esc end macro
end macro

macro IFDEF?! symbol
        if defined symbol
end macro

macro ENDIF?!
        end if
end macro

macro REPT? count
        macro ENDM?!
                end repeat
                purge ENDM?
        end macro
        repeat count
end macro

struc LABEL? size
        label . : size
end struc

struc (name) STRUC?
        struc (ename) ENDS?!
                end namespace
            esc end struc
                iterate reg, BX,SI,DI,BP
                        virtual at reg
                                reg? name
                                if % = 1
                                        label name : $ - $$ at 0
                                end if
                        end virtual
                end iterate
                match =name, ename
                        restruc ENDS?
                else
                        err 'mismatched ENDS, expected one for ',`name
                end match
        end struc
    esc struc name
        namespace .
end struc

define SIZE? 0 metadataof

struc (name) PROC? type
        name:
        struc (name2) ENDP?
                match =name, name2
                        purge ENDP?
                else
                        err 'mismatched ENDP, expected one for ',`name
                end match
        end struc
end struc

; parse legacy operand syntax:

macro x86.parse_operand ns,op
        ns.size = 0
        ns.segment_prefix = 0
        ns.prefix = 0
        ns.opcode_prefix = 0
        match =OFFSET? value, op
                x86.parse_legacy_address ns,value
                if ns.type = 'mem'
                        ns.type = 'imm'
                        ns.imm = ns.address
                        ns.size = 0
                        ns.displacement_size = 0
                end if
        else match sz =PTR? value, op
                ns.size = sz
                x86.parse_legacy_address ns,value
        else
                x86.parse_legacy_address ns,op
        end match
        if ns.type = 'imm'
                ns.segment_prefix = 0
                ns.displacement_size = 0
                if ns.imm eq 1 elementof ns.imm
                        if 1 metadataof (1 metadataof ns.imm) relativeto x86.reg
                                ns.type = 'reg'
                                ns.mod = 11b
                                ns.rm = 1 metadataof ns.imm - 1 elementof (1 metadataof ns.imm)
                                if ns.size & ns.size <> 1 metadataof (1 metadataof ns.imm) - x86.reg
                                        err 'operand sizes do not match'
                                else
                                        ns.size = 1 metadataof (1 metadataof ns.imm) - x86.reg
                                end if
                        else if 1 metadataof ns.imm relativeto x86.sreg
                                ns.type = 'sreg'
                                ns.rm = 1 metadataof ns.imm - x86.sreg
                                if ns.size & ns.size <> 2
                                        err 'operand sizes do not match'
                                else
                                        ns.size = 2
                                end if
                        end if
                end if
        end if
end macro

macro x86.parse_legacy_address ns,op
        local buffer,prefix
        buffer equ op
        define prefix
        match seg:offs, buffer
                redefine buffer offs
        else match =Nothing?, ASSUMED?.DS
                redefine prefix CS:
        end match
        match seg:, prefix
                x86.parse_segment_prefix ns,seg
        end match
        match base[add][aadd]more, +buffer+0
                ns.type = 'mem'
                if elementsof(base+add+aadd+more) > elementsof(base+add+aadd+more-COMSEG)
                        x86.parse_address ns,base+add+aadd+more-COMSEG
                else
                        x86.parse_address ns,base+add+aadd+more
                end if
        else match base[add]more, +buffer+0
                ns.type = 'mem'
                if elementsof(base+add+more) > elementsof(base+add+more-COMSEG)
                        x86.parse_address ns,base+add+more-COMSEG
                else
                        x86.parse_address ns,base+add+more
                end if
        else
                if elementsof(buffer) > elementsof(buffer-COMSEG)
                        ns.type = 'mem'
                        x86.parse_address ns,buffer-COMSEG
                else
                        ns.imm = buffer
                        ns.type = 'imm'
                        ns.displacement_size = 0
                        if ns.imm eqtype ''
                                ns.imm = ns.imm bswap lengthof ns.imm
                        end if
                end if
        end match
end macro

macro x86.parse_jump_operand ns,op
        ns.size = 0
        match =far? dest, op
                x86.parse_operand_value ns,dest
                ns.jump_type = 'far'
        else match =near? dest, op
                x86.parse_operand_value ns,dest
                ns.jump_type = 'near'
        else match =short? dest, op
                x86.parse_operand_value ns,dest
                ns.jump_type = 'short'
        else
                x86.parse_operand_value ns,op
                ns.jump_type = ''
        end match
        if ns.type = 'imm'
                if ns.size = 0
                        ns.size = x86.mode shr 3
                end if
                if ns.imm relativeto 0 & (ns.imm < 0 | ns.imm >= 1 shl (ns.size*8))
                        err 'value out of range'
                end if
        end if
end macro    
The size of executable matches the original - again, there are just differences caused by assembler footprints.

I have also successfully merged these new tricks into a previous legacy header, there is now one file that allows to assemble INVADERS.ASM, SP2.ASM and XGREP.S. To make it easier to track this overall progress, I'm attaching a complete package with MAKE.BAT which assembles all four challenges solved so far. One could perhaps also make a nice script that would download all the pieces from various places in web and then do the assembly.


Description: Examples of legacy syntax assembled with fasmg
Download
Filename: legacy.zip
Filesize: 223.93 KB
Downloaded: 1207 Time(s)

Post 16 Apr 2019, 15:11
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8363
Location: Kraków, Poland
Tomasz Grysztar 24 Apr 2019, 07:21
I have beaten the second challenge and managed to assemble Kboom11 correctly.

This is the most complex set of headers yet. I borrowed some of the existing macros from Windows headers, but PROC/INVOKE needed a dedicated 16-bit implementation. ASSUME is this time actually working and adds segment prefixes where necessary to address a specific variable.

Most of the macros are implemented quite generally, so it might be possible to assemble other similar sources with them, except a few problematic places like FORC, which has a simplified implementation for this specific task (I did not really want to waste too much time on it).

Code:
; This header allows to assemble Kboom11 (https://www.sac.sk/download/pack/kboom11.zip) without any changes to the source text.

include 'format/mz.inc'
include 'macro/@@.inc'
include 'cpu/8086.inc'

macro COMMENT delimiter
        ; COMMENT and END provide natural boundaries of the source, we can use it to enclose the text
        ; with MATCH to replace some of fasmg's keywords with alternate names:
        outscope match format:match:repeat, _format:_match:rept
        macro ?! line&
                match =delimiter, line
                        purge ?
                end match
        end macro
end macro

macro END entrypoint:STARTUP
        end match
        if entrypoint relativeto entrypoint element 1 & entrypoint metadata 1 relativeto MZ.segment
                entry entrypoint metadata 1 : entrypoint scale 0
        else
                err 'invalid entry point'
        end if
end macro

NILPTR? = 0
PARA? = 16
PAGE? = 256

struc (name) BYTE?: def&
        match *, BYTE?.name
        else
                restore BYTE?.name
                define BYTE?.name *
                label . : .SIZE
        end match
        match any=, , def
                db any
                macro ? line&
                        purge ?
                        name BYTE line
                end macro
        else
                db def
                .SIZE = $ - .
        end match
end struc

struc WORD? def&
        label . : word
        DW def
end struc

struc DWORD? def&
        label . : dword
        DD def
end struc

struc MACRO?! definition&
        purge . ; disable forward-referencing
        esc macro . definition
end struc

macro ENDM?!
        esc end macro
        macrofooter
end macro

macro macrofooter
end macro

macro IFDEF?! symbol
        if defined symbol
end macro

macro ELSEIF?! cond
        else if cond
end macro

macro ENDIF?!
        end if
end macro

macro REPT?! count
        local content
        macro macrofooter
                purge macrofooter?
                repeat count
                        content
                end repeat
        end macro
        esc macro content
end macro

macro FORC?! name, text&
        local content,string,char
        macro macrofooter
                purge macrofooter?
                match <chars>, text
                        string = `chars
                else
                        string = `text
                end match
                repeat lengthof string, i:0
                        char = (string shr (8*i)) and 0FFh
                        match spec, &name       ; replace quoted string of form "&name", this is not a general solution
                                macro ? line&
                                        match head `spec tail, :line:
                                                match :inner:, head char tail
                                                        inner
                                                end match
                                        else
                                                line
                                        end match
                                end macro
                        end match
                        content char
                        purge ?
                end repeat
        end macro
        esc macro content! name
end macro

struc LABEL? size
        label . : size
end struc

struc (name) PROTO? decl,args&
        name.PROTO equ decl,args
end struc

element STACKFRAME

struc (name) PROC? decl,args&
        name:
        namespace name
        PROTO equ decl
        match type =USES? regs, decl
                local buffer
                define buffer regs
                while 1
                        match car cdr, buffer
                                define SAVEREG car
                                redefine buffer cdr
                        else
                                SAVEREG equ buffer
                                break
                        end match
                end while
        end match
        virtual at STACKFRAME + 2 + 2
                iterate arg, args
                        match var:type, arg
                                name.var type ?
                        end match
                end iterate
                ARGV_SIZE = $ - $$
        end virtual
        macro LOCALV definitions&
                virtual LOCALV_BUFFER
                iterate def, definitions
                        match var:type, def
                                var type ?
                        end match
                end iterate
                end virtual
        end macro
        macro RET?
                irpv reg, SAVEREG
                        indx %%-%+1
                        pop reg
                end irpv
                if ARGV_SIZE | LOCALV_SIZE
                        if LOCALV_SIZE
                                mov sp,bp
                        end if
                        pop bp
                end if
                retn
        end macro
        struc (name2) ENDP?!
                end match
                virtual LOCALV_BUFFER
                        LOCALV_SIZE := $ - $$
                end virtual
                end namespace
                match =name, name2
                        purge ENDP?
                else
                        err 'mismatched ENDP, expected one for ',`name
                end match
        end struc
        virtual at STACKFRAME - LOCALV_SIZE
                LOCALV_BUFFER::
        end virtual
        if ARGV_SIZE | LOCALV_SIZE
                push bp
                mov bp,sp
                if LOCALV_SIZE
                        add sp,-LOCALV_SIZE-(LOCALV_SIZE and 1)
                end if
        end if
        irpv reg, SAVEREG
                push reg
        end irpv
        outscope match LOCAL?, LOCALV
end struc

macro INVOKE? proc,args&
        match =near? =C? arglist, proc.PROTO
                ; todo: validate argument list
                local size
                size = 0
                match any, args
                        iterate arg, args
                                indx %%-%+1
                                match =ADDR? val, arg
                                        lea ax,val
                                else
                                        mov ax,arg
                                end match
                                push ax
                                size = size + 2
                        end iterate
                end match
                call proc
                if size
                        add sp,size
                end if
        else
                err 'no function prototype or unknown calling convention'
        end match
end macro

define ASSUMED?

macro ASSUME? statement&
        iterate each, statement
                match reg:seg, each
                        match =NOTHING?, seg
                                ASSUMED?.reg? = 0
                        else match =@DATA?, seg
                                ASSUMED?.reg? = _DATA
                        else match =SEG? var, seg
                                if var relativeto var element 1 & var metadata 1 relativeto MZ.segment
                                        ASSUMED?.reg? = var metadata 1
                                else
                                        err 'invalid variable name'
                                end if
                        else
                                if seg relativeto MZ.segment
                                        ASSUMED?.reg? = seg
                                else
                                        err 'invalid segment name'
                                end if
                        end match
                end match
        end iterate
end macro

ASSUME CS:NOTHING, DS:NOTHING, ES:NOTHING, SS:NOTHING

CURSEG equ

macro COMMIT_SEGMENTS
end macro
postpone
        match name, CURSEG
                name ENDS
        end match
        COMMIT_SEGMENTS
end postpone

struc (name) SEGMENT? attr

        match alignment more, attr
                name.ALIGNMENT = alignment
        end match

        CURSEG equ name
        define $% (name.OFFSET+$-$$)

        if definite name.BUFFER
                virtual name.BUFFER
        else
                virtual at name.ORG + name.BASE
                name.BUFFER::
                macro COMMIT_SEGMENTS
                        COMMIT_SEGMENTS
                        if defined name.ALIGNMENT
                                rb  (-($ scale 0)) and (name.ALIGNMENT-1)
                        end if
                        if ~ definite name
                                name := MZ.segment + (($%-MZ.HEADER_LENGTH) and not 0Fh) shr 4
                                org ($%-MZ.HEADER_LENGTH) and 0Fh
                        end if
                        if ~ definite name.ORG
                                element name.ORG : name
                        end if
                        virtual name.BUFFER
                                load name.CONTENT : $@ - $$ from $$
                                name.BSS = $ - $@
                        end virtual
                        name.BASE:
                        name.OFFSET = $%
                        if name.CONTENT
                                db name.CONTENT
                        end if
                        rb name.BSS
                end macro
        end if

        struc (ename) ENDS?
                match =name, ename
                        restruc ENDS?
                        restore CURSEG
                        restore $%
                        end virtual
                else
                        err 'mismatched ENDS, expected one for ',`name
                end match
        end struc
end struc

struc (name) GROUP? list&
        iterate seg, list
                if % = 1
                        name := seg
                        element name.ORG : name
                else
                        seg := name
                        seg.ORG := name.ORG
                end if
        end iterate
end struc

; Parse legacy operand syntax:

macro x86.parse_operand ns,op
        ns.size = 0
        ns.segment_prefix = 0
        ns.prefix = 0
        ns.opcode_prefix = 0
        match =OFFSET? value, op
                x86.parse_legacy_address ns,value,1
                if ns.type = 'mem'
                        ns.type = 'imm'
                        ns.imm = ns.address
                        ns.size = 0
                        ns.displacement_size = 0
                end if
        else match =SEG? value, op
                x86.parse_legacy_address ns,value,1
                if ns.type = 'mem'
                        ns.type = 'imm'
                        if ns.var relativeto ns.var element 1 & ns.var metadata 1 relativeto MZ.segment
                                ns.imm = ns.var metadata 1
                        else
                                err 'invalid variable name'
                        end if
                        ns.size = 0
                        ns.displacement_size = 0
                end if
        else match sz =PTR? value, op
                ns.size = sz
                x86.parse_legacy_address ns,value
        else
                x86.parse_legacy_address ns,op
        end match
        if ns.type = 'imm'
                ns.segment_prefix = 0
                ns.displacement_size = 0
                if ns.imm eq 1 elementof ns.imm
                        if 1 metadataof (1 metadataof ns.imm) relativeto x86.reg
                                ns.type = 'reg'
                                ns.mod = 11b
                                ns.rm = 1 metadataof ns.imm - 1 elementof (1 metadataof ns.imm)
                                if ns.size & ns.size <> 1 metadataof (1 metadataof ns.imm) - x86.reg
                                        err 'operand sizes do not match'
                                else
                                        ns.size = 1 metadataof (1 metadataof ns.imm) - x86.reg
                                end if
                        else if 1 metadataof ns.imm relativeto x86.sreg
                                ns.type = 'sreg'
                                ns.rm = 1 metadataof ns.imm - x86.sreg
                                if ns.size & ns.size <> 2
                                        err 'operand sizes do not match'
                                else
                                        ns.size = 2
                                end if
                        end if
                end if
        end if
end macro

macro x86.parse_legacy_address ns,op,offsonly:0
        local buffer,prefix,address
        buffer equ op
        define prefix
        match seg:offs, buffer
                if defined seg & (seg eq DS | seg eq ES | seg eq CS | seg eq SS | seg eq FS | seg eq GS)
                        redefine prefix seg:
                else
                        err 'unknown segment prefix'
                end if
                redefine buffer offs
        end match
        match base[add][aadd]more, +buffer+0
                redefine buffer base+add+aadd+more
                ns.type = 'mem'
        else match base[add]more, +buffer+0
                redefine buffer base+add+more
                ns.type = 'mem'
        else
                ns.type = 'imm'
        end match
        if ns.size = 0
                ns.size = sizeof (buffer)
                match +name, buffer
                        match *any, BYTE?.name
                                ns.size = 1
                        end match
                end match
        end if
        address = buffer
        ns.var = +address
        repeat elementsof address
                if address metadata % relativeto MZ.segment & address scale % = 1
                        if ~ offsonly
                                match , prefix
                                        if ASSUMED?.DS eq address metadata %
                                                redefine prefix ds:
                                        else if ASSUMED?.ES eq address metadata %
                                                redefine prefix es:
                                        else if ASSUMED?.CS eq address metadata %
                                                redefine prefix cs:
                                        else if ASSUMED?.SS eq address metadata %
                                                redefine prefix ss:
                                        else
                                                err 'no segment register available to address this variable'
                                        end if
                                end match
                        end if
                        address = address - address element %
                        ns.type = 'mem'
                        break
                else if address element % eq STACKFRAME
                        address = address - address element % + BP
                        ns.type = 'mem'
                end if
        end repeat
        match seg:, prefix
                ns.segment = +seg
                if ns.segment eq 1 elementof ns.segment & 1 metadataof ns.segment relativeto x86.sreg
                        ns.segment = 1 metadataof ns.segment - x86.sreg
                        if ns.segment < 4
                                ns.segment_prefix = 26h + ns.segment shl 3
                        else
                                ns.segment_prefix = 64h + ns.segment-4
                        end if
                else
                        err 'invalid segment prefix'
                end if
        end match
        if ns.type = 'mem'
                x86.parse_address ns,address
        else
                ns.imm = address
                ns.displacement_size = 0
                if ns.imm eqtype ''
                        ns.imm = ns.imm bswap lengthof ns.imm
                end if
        end if
end macro

macro x86.parse_jump_operand ns,op
        ns.size = 0
        match =far? dest, op
                x86.parse_operand_value ns,dest
                ns.jump_type = 'far'
        else match =near? dest, op
                x86.parse_operand_value ns,dest
                ns.jump_type = 'near'
        else match =short? dest, op
                x86.parse_operand_value ns,dest
                ns.jump_type = 'short'
        else
                x86.parse_operand_value ns,op
                ns.jump_type = ''
        end match
        if ns.type = 'imm'
                if ns.size = 0 & defined x86.mode
                        ns.size = x86.mode shr 3
                end if
                if ns.imm relativeto 0 & (ns.imm < 0 | ns.imm >= 1 shl (ns.size*8))
                        err 'value out of range'
                end if
        end if
end macro

; Optimize LEA to MOV:

macro lea? dest*,src*
        x86.parse_operand @src,src
        if @src.type = 'mem' & @src.address_registers eq 0
                mov     dest,@src.address
        else
                lea     dest,src
        end if
end macro

; Make trampolines for conditional jumps when they get out of range:

iterate J, jo,jno, jc,jnc, jb,jnb, jae,jnae, je,jne ,jz,jnz, ja,jbe, jna,jnbe, js,jns, jp,jnp, jpe,jpo, jl,jnl, jge,jnge, jg,jng, jle,jnl

        if % and 1
                indx %+1
        else
                indx %-1
        end if
        match JN, J
                indx %

                macro J? target*
                        if target-($+1) < 80h & target-($+1) >= -80h
                                J       target
                        else
                                local   skip
                                JN      skip
                                jmp     target
                            skip:
                        end if
                end macro
        end match

end iterate

; Forward directives starting with dot to macros in "Directives" namespace:

struc (instr) ? args&
        match .name, instr
                macro ? line
                        purge ?
                        Directives.name args
                end macro
        end match
        outscope instr args
end struc

define Directives
namespace Directives

        macro MODEL? decl
                match =SMALL?, decl
                        _TEXT segment byte public 'CODE'
                        _TEXT ends
                        _DATA segment word public 'DATA'
                        _DATA ends
                        _BSS segment page public 'BSS'
                        _BSS ends
                        _STACK segment para public 'STACK'
                                db 400h dup ?
                                stack _STACK : $ scale 0
                        _STACK ends
                        DGROUP group _DATA, _BSS
                else
                        err 'unknown model'
                end match
        end macro

        macro CODE
                match name, CURSEG
                        name ENDS
                end match
                _TEXT segment
        end macro

        macro DATA
                match name, CURSEG
                        name ENDS
                end match
                _DATA segment
        end macro

        macro DATA?
                match name, CURSEG
                        name ENDS
                end match
                _BSS segment
        end macro

        macro STACK
        end macro

        macro STARTUP
                STARTUP:
                        mov dx,DGROUP
                        mov ds,dx
                        mov bx,ss
                        sub bx,dx
                        shl bx,1
                        shl bx,1
                        shl bx,1
                        shl bx,1
                        cli
                        mov ss,dx
                        add sp,bx
                        sti
                assume CS:_TEXT, DS: DGROUP, SS: DGROUP
        end macro

        macro EXIT code
                match , code
                        mov ah,4Ch
                else
                        mov ax,4C00h + (code) and 0FFh
                end match
                int 21h
        end macro

        macro IF? cond
                __IF equ :
                local endif
                __ENDIF equ endif
                local else
                __ELSE equ else
                jcondexpr __ELSE,1,cond
        end macro

        macro ELSE?
                jmp __ENDIF
                match _else, __ELSE
                        _else:
                end match
                restore __IF
                __IF equ
        end macro

        macro ELSEIF? cond
                jmp __ENDIF
                match _else, __ELSE
                        _else:
                end match
                restore __ELSE
                local else
                __ELSE equ else
                jcondexpr __ELSE,1,cond
        end macro

        macro ENDIF?
                match :_else, __IF __ELSE
                        _else:
                end match
                match endif, __ENDIF
                        endif:
                end match
                restore __ELSE
                restore __ENDIF
                restore __IF
        end macro

        macro WHILE cond
                local endw
                __ENDW equ endw
                match =1, cond
                else
                        jmp __ENDW
                end match
                local while
                __WHILE equ jcondexpr while,0,cond
                while:
        end macro

        macro ENDW
                match endw, __ENDW
                        endw:
                end match
                match while, __WHILE
                        while
                end match
                restore __ENDW
                restore __WHILE
        end macro

end namespace

macro newlocal? var
        local new
        redefine var new
end macro

macro JCONDEXPR?: target,mode,cond
        local buffer,current,counter
        local neg,conj
        local f,t
        buffer equ cond
        newlocal f
        newlocal t
        while 1
                match !x, buffer
                        buffer equ x
                        neg = (mode) xor 1
                else
                        neg = mode
                end match

                match (x, buffer
                        counter = 1
                        current equ (
                        buffer equ x
                        while counter > 0
                                match p)s, buffer
                                        match (ps, p
                                                counter = counter + 1
                                                current equ current (
                                                buffer equ ps)s
                                        else match pp(ps, p
                                                counter = counter + 1
                                                current equ current pp(
                                                buffer equ ps)s
                                        else
                                                counter = counter - 1
                                                current equ current p
                                                buffer equ )s
                                        end match
                                else
                                        current equ current buffer
                                        buffer equ
                                        break
                                end match
                        end while
                else
                        current equ
                end match

                match a||b, buffer
                        match c&d, a
                                current equ current c
                                buffer equ &&d||b
                        else
                                current equ current a
                                buffer equ ||b
                        end match
                else match c&&d, buffer
                        match a||b, c
                                current equ current a
                                buffer equ ||b&&d
                        else
                                current equ current c
                                buffer equ &&d
                        end match
                else
                        current equ current buffer
                        buffer equ
                end match

                match , buffer
                        match (c), current
                                jcondexpr t,neg,c
                        else match c, current
                                jcond t,neg,c
                        end match
                        break
                else
                        match ||b, buffer
                                buffer equ b
                                conj = 0
                        else match &&d, buffer
                                buffer equ d
                                conj = 1
                        else
                                err 'invalid expression'
                        end match
                        if (mode) xor conj
                                match (c), current
                                        jcondexpr f,neg xor 1,c
                                else match c, current
                                        jcond f,neg xor 1,c
                                end match
                                match t,t
                                        t:
                                end match
                                newlocal t

                        else
                                match (c), current
                                        jcondexpr t,neg,c
                                else match c, current
                                        jcond t,neg,c
                                end match
                                match f,f
                                        f:
                                end match
                                newlocal f
                        end if
                end match
        end while
        match t,t
                label t at target
        end match
        match f,f
                f:
        end match
end macro

macro JCOND? target,neg,cond
        match v1>==v2, cond
                cmp v1,v2
                jae target
        else match v1<==v2, cond
                cmp v1,v2
                jbe target
        else match v1====v2, cond
                cmp v1,v2
                if neg
                        jne target
                else
                        je target
                end if
        else match v1!==v2, cond
                cmp v1,v2
                if neg
                        je target
                else
                        jne target
                end if
        else match v1>v2, cond
                cmp v1,v2
                if neg
                        jbe target
                else
                        ja target
                end if
        else match v1<v2, cond
                cmp v1,v2
                if neg
                        jae target
                else
                        jb target
                end if
        else match =ZERO=?, cond
                if neg
                        jnz target
                else
                        jz target
                end if
        else match =CARRY=?, cond
                if neg
                        jnc target
                else
                        jc target
                end if
        else match =OVERFLOW=?, cond
                if neg
                        jno target
                else
                        jo target
                end if
        else match =SIGN=?, cond
                if neg
                        jns target
                else
                        js target
                end if
        else match =PARITY?, cond
                if neg
                        jnp target
                else
                        jp target
                end if
        else match v, cond
                x86.parse_operand @aux,v
                if @aux.type = 'imm'
                        if neg
                                if v = 0
                                        jmp target
                                end if
                        else
                                if v
                                        jmp target
                                end if
                        end if
                else if @aux.type = 'reg'
                        test v,v
                        if neg
                                jz target
                        else
                                jnz target
                        end if
                else
                        cmp v,0
                        if neg
                                je target
                        else
                                jne target
                        end if
                end if
        end match
end macro    
Most of the instructions are identical, but use addresses that differ from original. I found that I could get rid of most of the differences by adding some made-to-measure padding to the MZ header and data segments, although there still remain a few instructions that fasmg assembles as smaller, changing some of the offsets in code.

There is one more challenge left, which is going to require writing a more or less complete emulation of NASM macro syntax. This should be doable, again it's just a matter of finding enough time to play with it.
Post 24 Apr 2019, 07:21
View user's profile Send private message Visit poster's website Reply with quote
rugxulo



Joined: 09 Aug 2005
Posts: 2341
Location: Usono (aka, USA)
rugxulo 28 Apr 2019, 04:03
Nice work. Cool (Okay, I haven't tried reassembling it yet, but I trust you, heh.) It's not exactly the most "obvious" utility, but I did find the dialect to be fascinatingly obtuse. Quite a heroic effort on your part!

(Not sure if the SHSURDRV example is truly worthy of your time, but again, I found his overreliance on quirky macros fascinating.)
Post 28 Apr 2019, 04:03
View user's profile Send private message Visit poster's website Reply with quote
rugxulo



Joined: 09 Aug 2005
Posts: 2341
Location: Usono (aka, USA)
rugxulo 28 Apr 2019, 05:32
rugxulo wrote:
(Okay, I haven't tried reassembling it yet, but I trust you, heh.)


Hmmm, the standard download "fasmg.zip" doesn't have @@.inc. Then it finally assembles (and seems to work okay, in limited testing).

Actually, I'll inline it here (if that's okay) since it's small enough.

Code:
macro @@ tail
        match label, @f?
                label tail
                @b?. equ @f?
        end match
        local anonymous
        @f?. equ anonymous
end macro

define @f?
@@
    
Post 28 Apr 2019, 05:32
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8363
Location: Kraków, Poland
Tomasz Grysztar 19 May 2019, 16:14
rugxulo wrote:
(Not sure if the SHSURDRV example is truly worthy of your time, but again, I found his overreliance on quirky macros fascinating.)
It was not easy to find enough spare time to deal with this challenge, but I finally have succeeded to faithfully assemble one of the driver files from SHSUFDRV package.

A practical (and sane) method would be to write some fasmg macros that would take place of the macros used originally (kept in NASM.MAC file).

Instead I have written macros that emulate a large chunk of NASM's preprocessor and then let them process the NASM.MAC file and parse through all these quirky macros in their original form.

I am surprised myself that this actually worked. But it succeeded to assemble SHSUFDRV.NSM with result being identical to original. Here comes my NASMPRE.INC header that does all the syntax processing:
Code:
if defined DEBUG
        macro debug! command&
                command
        end macro
else
        macro debug! command&
        end macro
end if

Header:
        .Signature                      dw "MZ"
        .BytesInLastSector              dw %_FILE_SIZE mod 512
        .NumberOfSectors                dw (%_FILE_SIZE-1)/512 + 1
        .NumberOfRelocations            dw 0
        .NumberOfHeaderParagraphs       dw 40h/16
        .MinHeap                        dw (%_RESERVED_SIZE-1)/16 + 1
        .MaxHeap                        dw 0FFFFh
        .InitialSS                      dw %_SS
        .InitialSP                      dw %_SP
        .Checksum                       dw 0
        .InitialIP                      dw %_IP
        .InitialCS                      dw 0
        .RelocationsOffset              dw 40h
                                        db 40h - $ dup 0
org 0

macro nasm_section? decl
        match name= settings, decl :
                if defined %_Section
                        end virtual
                end if
                if definite %_Section#name
                        virtual %_Section#name
                else
                        define %_Section %_Section#name
                        virtual at %_Section#name.BASE
                        %_Section#name::
                end if
                match =align?==boundary tail, settings
                        if boundary > 1
                                align boundary
                        end if
                end match
        end match
end macro

nasm_section .text
postpone
        end virtual
        irpv name, %_Section
                if defined name.STACKSEG
                        db (-$%) and 15 dup ?
                        %_SS := ($% - 40h)/16
                end if
                if ~ definite name.BASE
                        name.BASE := $
                end if
                virtual name
                        if defined name.STACKSEG
                                %_SP := $
                        end if
                        load %_CONTENT : $@ - $$ from $$
                        %_RESERVE = $ - $@
                end virtual
                if lengthof %_CONTENT
                        db %_CONTENT
                end if
                rb %_RESERVE
        end irpv
        %_FILE_SIZE = $%%
        %_RESERVED_SIZE = $% - $%%
end postpone

macro segment? decl
        match name= settings, decl :
                match =stack? tail, settings
                        %_Section#name.BASE := 0
                        %_Section#name.STACKSEG := 1
                end match
                nasm_section name
        end match
end macro

macro group? decl
        ; ignore
end macro

macro sqbr_list? flag
        ; ignore
end macro

macro cpu? selection
        ; ignore
end macro

include 'cpu/80386.inc'

macro x86.parse_operand ns,op
        x86.parse_operand ns,op
        if ns.type = 'imm' & ns.size = 1
                ns.size = 0
                if ns.imm < -80h | ns.imm > 0FFh
                        err 'value out of range'
                end if
        end if
end macro

macro times? statement
        match count= instruction, statement
                repeat count
                        instruction
                end repeat
        end match
end macro

macro nasm_struc? name
        virtual at 0
        name
        macro endstruc?
                purge endstruc?
                name#_size := $
                end virtual
        end macro
end macro

struc (name) ? def&
        match :any, def:
                name def
        else match , def
                name:
                match ns.nm, name
                else
                        redefine %_LABEL name
                end match
        else match ==any, def:
                name def
        else match =equ? any, def:
                name def
        else match =reequ? any, def:
                name def
        else
                name: def
        end match
end struc

%_COUNTER = 0

macro %_uid
        %_COUNTER = %_COUNTER + 1
        repeat 1, id:%_COUNTER
                %_UID = `id
        end repeat
end macro

%_STATE = 0

macro %_line
end macro

macro %_preprocess: line&
        local Line
        debug display 'Before preprocessing: ',`line,13,10
        match [directive], line
                define Line { sqbr_#directive }
        else match =section? decl, line
                define Line { nasm_section decl }
        else match ..=start?, line
                define Line { %_IP: }
        else
                define Line { line }
                while 1
                        match head{tail, Line
                                redefine Line head <tail
                        else match head}tail, Line
                                redefine Line head> tail
                        else match head!==tail, Line
                                redefine Line head<>tail
                        else match head====tail, Line
                                redefine Line head=tail
                        else match head|tail, Line
                                redefine Line head or tail
                        else match head&tail, Line
                                redefine Line head and tail
                        else match head~tail, Line
                                redefine Line head not tail
                        else match head<<tail, Line
                                redefine Line head shl tail
                        else match head>>tail, Line
                                redefine Line head shr tail
                        else match head= =if?= tail, Line
                                redefine Line head nasm_if tail
                        else match head= =else?= tail, Line
                                redefine Line head nasm_else tail
                        else match head= =repeat?= tail, Line
                                redefine Line head nasm_repeat tail
                        else match head= =while?= tail, Line
                                redefine Line head nasm_while tail
                        else match head= =break?= tail, Line
                                redefine Line head nasm_break tail
                        else match head= =restore?= tail, Line
                                redefine Line head nasm_restore tail
                        else match head= =struc?= tail, Line
                                redefine Line head nasm_struc tail
                        else match head= =format?= tail, Line
                                redefine Line head nasm_format tail
                        else match head= =equ?= tail, Line
                                redefine Line head = tail
                        else match head= .tail, Line
                                match %_label, %_LABEL
                                        redefine Line head %_label.tail
                                end match
                        else match head.= tail, Line
                                redefine Line head._ tail
                        else match head.=,tail, Line
                                redefine Line head._,tail
                        else
                                break
                        end match
                end while
                if `line and 0FFh <> '%'
                        %_expand Line
                        match head= =%+= tail, Line
                                redefine Line head#tail
                        end match
                end if
        end match
        match {preprocessed}, Line
                debug display ' After preprocessing: ',`preprocessed,13,10
                macro %_line
                        purge %_line
                        preprocessed
                        debug if %_STATE > 0
                        debug         display '                      (skipped)',13,10
                        debug end if
                end macro
        end match
end macro

macro %_expand Line
        while 1
                match ln, Line
                        Line reequ ln
                        while 1
                                match head :<inline>: (tail, Line
                                        local Buffer,Value
                                        define Buffer tail
                                        match args)more, Buffer
                                                ; todo: bracket balance
                                                iterate arg, args
                                                        inline#%% Value, args
                                                        break
                                                end iterate
                                                redefine Buffer more
                                        else
                                                err 'inline macro syntax error'
                                        end match
                                        match valtail, Value Buffer
                                                redefine Line head valtail
                                        end match
                                else
                                        break
                                end match
                        end while
                        match expanded, Line
                                if `expanded = `ln
                                        break
                                end if
                        end match
                end match
        end while
end macro

macro %_macroargs %_context,proto,argv&
        local Proto,Defaults,Min,Max,Last,Greedy
        define Proto proto
        match argc= defaults, Proto
                define Proto argc
                define Defaults defaults
        else
                define Defaults
        end match
        match argc.=nolist?, Proto
                define Proto argc
        end match
        match min-max+, Proto
                Min = min
                match *, max
                else
                        Max = max
                end match
                Greedy = 1
        else match min-max, Proto
                Min = min
                match *, max
                else
                        Max = max
                end match
                Greedy = 0
        else match minmax+, Proto
                match *, minmax
                        Min = 0
                else
                        Min = minmax
                        Max = minmax
                end match
                Greedy = 1
        else
                match *, Proto
                        Min = 0
                else
                        Min = Proto
                        Max = Proto
                end match
                Greedy = 0
        end match
        match defaults, Defaults
                iterate default, defaults
                        repeat 1, i:Min+%
                                define %_context.i default
                        end repeat
                end iterate
        end match
        define Last 0
        iterate arg, argv
                match any, arg
                        redefine Last %
                        if ~ defined Max | % <= Max
                                define %_context.% arg
                        else if Greedy
                                repeat 1, i:Max
                                        match prev, %_context.i,
                                                match some=,some, :arg:
                                                        redefine %_context.i prev <arg>
                                                else
                                                        redefine %_context.i prev arg
                                                end match
                                        end match
                                end repeat
                        else
                                define Last -1
                                break
                        end if
                else
                        define %_context.%
                end match
        end iterate
        if Last >= Min \
                       | Last = 0  ; work around argument being replaced with empty value before we have a chance to look at it
                %_context.0 equ Last
        end if
end macro

macro %_macroline: %_context, line&
        if %_STATE = 0 | ( `line and 0FFh = '%' & `line and 0FFFFh <> '%%' & `line and 0FFFFh <> '%$' )
                local Text,Cmd
                debug display '            In macro: ',`line,13,10
                match %_top, %_TOP
                        virtual at 0
                                virtual at 0
                                        Text:: db `line
                                        Text.length = $
                                end virtual
                                local Pos,Char,Mod,NamePos,NameStr,Name,LeaveFirst
                                db '%_preprocess '
                                Pos = 0
                                LeaveFirst = 0
                                match + any, %_.line
                                        if ( `any and 0FFFFh = '%%' | `any and 0FFFFh = '%$' )
                                                LeaveFirst = 1
                                        end if
                                end match
                                while Pos < Text.length
                                        load Char:byte from Text:Pos
                                        Pos = Pos + 1
                                        if Char = '%' & Pos < Text.length
                                                NamePos = Pos
                                                load Char:byte from Text:Pos
                                                Pos = Pos + 1
                                                if ( Char = '%' | Char = '$' | Char = '+' | Char = '-' ) & Pos < Text.length
                                                        Mod = Char
                                                        NamePos = Pos
                                                        load Char:byte from Text:Pos
                                                        Pos = Pos + 1
                                                else
                                                        Mod = ''
                                                end if
                                                if ( Char >= '0' & Char <= '9' ) | ( ( Mod = '%' | Mod = '$' ) & ( ( Char >= 'a' & Char <= 'z' ) | ( Char >= 'A' & Char <= 'Z' ) ) )
                                                        while ( Char >= '0' & Char <= '9' ) | ( ( Mod = '%' | Mod = '$' ) & ( ( Char >= 'a' & Char <= 'z' ) | ( Char >= 'A' & Char <= 'Z' ) ) )
                                                                if Pos < Text.length
                                                                        load Char:byte from Text:Pos
                                                                else
                                                                        Char = ''
                                                                end if
                                                                Pos = Pos + 1
                                                        end while
                                                        load NameStr : Pos-1-NamePos from Text:NamePos
                                                        eval string 'define Name ' + NameStr shl (12*8)
                                                        match name, Name
                                                                if Mod = '%'
                                                                        if LeaveFirst
                                                                             LeaveFirst = 0
                                                                             db '%_context.',NameStr
                                                                        else match =%_context.=name, %_context.name
                                                                                db '%_context.',NameStr
                                                                        else match value, %_context.name
                                                                                db `value
                                                                        end match
                                                                else if Mod = '$'
                                                                        if LeaveFirst
                                                                             LeaveFirst = 0
                                                                             db '%_top.',NameStr
                                                                        else match =%_top.=name, %_top.name
                                                                                db '%_top.',NameStr
                                                                        else match value, %_top.name
                                                                                db `value
                                                                        end match
                                                                else
                                                                        match =%_context.=name, %_context.name
                                                                        else match value, %_context.name
                                                                                if Mod = '-'
                                                                                        if `value and 0FFh = 'n' | `value and 0FFh = 'N'
                                                                                                db string `value shr 8
                                                                                        else
                                                                                                db 'n',`value
                                                                                        end if
                                                                                else
                                                                                        db `value
                                                                                end if
                                                                        end match
                                                                end if
                                                        end match
                                                else
                                                        db '%',Mod
                                                end if
                                        end if
                                        db Char
                                end while
                                load Cmd:$ from 0
                        end virtual
                        eval Cmd
                end match
        end if
        %_line
end macro

namespace %_
        define %define? +
        define %idefine? +
        define %xdefine? +
        define %ixdefine? +
        define %assign? +
        define %strlen? +
        define %substr? +
        define %ifdef +
        define %ifndef +
        define %elifdef +
        define %elifndef +
end namespace

macro %_defines %i,i

        macro %i#define? decl&
                if %_STATE = 0
                        local Decl
                        define Decl decl
                        match name(argdecl)= value, Decl
                                iterate argname, argdecl
                                        macro name#_%_#%% target,argdecl&
                                                define target value
                                        end macro
                                        break
                                end iterate
                                restore name#i
                                redefine name#i :<name#_%_>:
                        else match name= value, Decl
                                restore name#i
                                redefine name#i value
                        else match name, Decl
                                restore name#i
                                redefine name#i
                        end match
                end if
        end macro

        macro %i#xdefine? decl&
                if %_STATE = 0
                        local Decl,Value
                        define Decl decl
                        match name= value, Decl
                                define Value { value }
                                %_expand Value
                                restore name#i
                                match {expanded}, Value
                                        redefine name#i expanded
                                end match
                        else match name, Decl
                                restore name#i
                                redefine name#i
                        end match
                end if
        end macro

        macro %i#macro? decl&
                local Builder
                match name= proto, decl
                        macro Builder
                                esc macro name#i argv&
                                        %_uid
                                        eval 'match %_context, loc',%_UID
                                        define %_CONTEXT %_context
                                        %_macroargs %_context,proto,argv
                                        if ~ definite %_context.0
                                                name argv
                                        else
                        end macro
                else
                        err 'invalid macro declaration'
                end match
                %_STATE =: -1
                macro %_macrodefinition line&
                        match =%endmacro?, line
                                restore %_STATE
                                macro Builder
                                        Builder
                                        end if
                                        restore %_CONTEXT
                                        end match
                                        esc end macro
                                end macro
                                if %_STATE = 0
                                        Builder
                                end if
                        else
                                macro Builder
                                        Builder
                                        %_macroline %_context,line
                                end macro
                        end match
                end macro
        end macro

end macro

%_defines %
%_defines %i,?

macro %undef names&
        iterate name, names
                restore name
        end iterate
end macro

macro %assign? decl&
        if %_STATE = 0
                local Decl,Value
                define Decl decl
                match name= value, Decl
                        define Decl { value }
                        %_expand Decl
                        match {expanded}, Decl
                                if expanded < 0
                                        repeat 1, v: -(expanded)
                                                redefine name -v
                                        end repeat
                                else
                                        repeat 1, v: expanded
                                                redefine name v
                                        end repeat
                                end if
                        end match
                end match
        end if
end macro

macro %strlen? decl&
        if %_STATE = 0
                local Decl
                define Decl decl
                match name= string, Decl
                        repeat 1, v: lengthof(string)
                                redefine name v
                        end repeat
                end match
        end if
end macro

macro %substr? decl&
        if %_STATE = 0
                local Decl,Pos,Mask
                define Decl decl
                match name= str= range, Decl
                        match pos=,len, range
                                Pos = (pos-1)*8
                                Mask = 1 shl (len*8) - 1
                        else
                                Pos = (range-1)*8
                                Mask = 0FFh
                        end match
                        name = string (str shr Pos) and Mask
                end match
        end if
end macro

macro %rotate? n
        if %_STATE = 0
                match %_context, %_CONTEXT
                        local Tmp
                        repeat %_context.0, di:1
                                        repeat 1, si:1+(%%+di-1+n) mod %%
                                                Tmp equ %_context.di
                                                %_context.di reequ %_context.si
                                                %_context.di equ Tmp
                                        end repeat
                        end repeat
                        repeat %_context.0, di:1
                                restore %_context.di
                        end repeat
                end match
        end if
end macro

macro %push? ctxname
        if %_STATE = 0
                %_uid
                eval 'match %_top, ctx',%_UID
                        define %_TOP %_top
                eval 'end match'
                define %_TOP.name ctxname
        end if
end macro

macro %pop? ctxname
        if %_STATE = 0
                match, ctxname
                else match =ctxname, %_TOP.name
                else
                        err 'name does not match the top context'
                end match
                restore %_TOP
                restore %_TOP.name
        end if
end macro

macro %repl? ctxname
        if %_STATE = 0
                redefine %_TOP.name ctxname
        end if
end macro

macro %if? cond&
        if %_STATE = 0
                if cond
                        %_STATE =: 0
                else
                        %_STATE =: 1
                end if
        else
                %_STATE =: 2
        end if
end macro

macro %else?
        if %_STATE = 1
                %_STATE = 0
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %elif? cond&
        if %_STATE = 1 & cond
                %_STATE = 0
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %endif?
        if %_STATE = 3
                restore %_STATE
                %_STATE = 3
        else
                restore %_STATE
        end if
end macro

macro %_ifidn? neg,args&
        if %_STATE > 0
                %_STATE =: 2
        else
                local A,B
                match {a}=,{b}, args
                        A = `a
                        B = `b
                else match a=,{b}, args
                        A = `a
                        B = `b
                else match {a}=,b, args
                        A = `a
                        B = `b
                else match a=,b, args
                        A = `a
                        B = `b
                else match =,, args
                        A = ''
                        B = ''
                else
                        A = `args
                        B = ''
                end match
                if A = B
                        %_STATE =: 0 xor neg
                else
                        %_STATE =: 1 xor neg
                end if
        end if
end macro

macro %ifidn? args&
        %_ifidn 0,args
end macro

macro %ifnidn? args&
        %_ifidn 1,args
end macro

macro %elifidn? args&
        if %_STATE = 1
                %endif
                %_ifidn 0,args
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %elifnidn? args&
        if %_STATE = 1
                %endif
                %_ifidn 1,args
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %_ifidni? neg,args&
        if %_STATE > 0
                %_STATE =: 2
        else
                local A,B
                match {a}=,{b}, args
                        define A a
                        define B { b ,
                else match a=,{b}, args
                        define A a
                        define B { b ,
                else match {a}=,b, args
                        define A a
                        define B { b ,
                else match a=,b, args
                        define A a
                        define B { b ,
                else match =,, args
                        define A
                        define B ,
                else
                        define A args
                        define B ,
                end match
                while 1
                        match { token tail, B
                                define B =token { tail
                        else match head { token tail, B
                                define B head =token { tail
                        else match head { =, , B
                                define B head ,
                                break
                        else
                                break
                        end match
                end while
                match BA, B A
                        match BA
                                %_STATE =: 0 xor neg
                        else
                                %_STATE =: 1 xor neg
                        end match
                end match
        end if
end macro

macro %ifidni? args&
        %_ifidni 0,args
end macro

macro %ifnidni? args&
        %_ifidni 1,args
end macro

macro %elifidni? args&
        if %_STATE = 1
                %endif
                %_ifidni 0,args
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %elifnidni? args&
        if %_STATE = 1
                %endif
                %_ifidni 1,args
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %ifid? tokens
        if %_STATE > 0
                %_STATE =: 2
        else
                %_STATE =: 1
                match token tail, tokens :
                        match token, ::
                                %_STATE = 0
                        end match
                end match
        end if
end macro

macro %elifid? tokens
        if %_STATE = 1
                %endif
                %ifid tokens
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %ifnum? token
        if %_STATE = 0
                if `token and 0FFh >= '0' & `token and 0FFh < '9'
                        %_STATE =: 0
                else
                        %_STATE =: 1
                end if
        else
                %_STATE =: 2
        end if
end macro

macro %elifnum? token
        if %_STATE = 1 & `token and 0FFh >= '0' & `token and 0FFh < '9'
                %_STATE = 0
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %ifstr? token
        if %_STATE = 0
                if defined token & token eqtype ''
                        %_STATE =: 0
                else
                        %_STATE =: 1
                end if
        else
                %_STATE =: 2
        end if
end macro

macro %elifstr? token
        if %_STATE = 1 & defined token & token eqtype ''
                %_STATE = 0
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %ifnstr? token
        if %_STATE = 0
                if ~ ( defined token & token eqtype '' )
                        %_STATE =: 0
                else
                        %_STATE =: 1
                end if
        else
                %_STATE =: 2
        end if
end macro

macro %elifnstr? token
        if %_STATE = 1 & ~ ( defined token & token eqtype '' )
                %_STATE = 0
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %_ifdef? neg,name
        if %_STATE = 0
                %_STATE =: 0 xor neg
                match val, name
                        if `val = `name
                                if ~ defined name
                                        %_STATE = 1 xor neg
                                end if
                        end if
                end match
        else
                %_STATE =: 2
        end if
end macro

macro %ifdef? names
        %_ifdef 0,names
end macro

macro %ifndef? names
        %_ifdef 1,names
end macro

macro %elifdef? names
        if %_STATE = 1
                %endif
                %_ifdef 0,names
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %elifndef? names
        if %_STATE = 1
                %endif
                %_ifdef 1,names
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %_ifctx? neg,names
        if %_STATE > 0
                %_STATE =: 2
        else
                %_STATE =: 1 xor neg
                local Buffer
                define Buffer names :
                while 1
                        match ctxname tail, Buffer
                                match =ctxname, %_TOP.name
                                        %_STATE = 0 xor neg
                                        break
                                end match
                                redefine Buffer tail
                        else
                                break
                        end match
                end while
        end if
end macro

macro %ifctx? names
        %_ifctx 0,names
end macro

macro %ifnctx? names
        %_ifctx 1,names
end macro

macro %elifctx? names
        if %_STATE = 1
                %endif
                %_ifctx 0,names
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %elifnctx? names
        if %_STATE = 1
                %endif
                %_ifctx 1,names
        else if %_STATE = 0
                %_STATE = 2
        end if
end macro

macro %rep? count
        local Count
        Count = count
        if %_STATE = 0 & Count
                %_STATE =: 0
        else
                if %_STATE = 0
                        %_STATE =: 2
                else
                        %_STATE =: %_STATE
                end if
                Count = 1
        end if
        repeat Count
end macro

macro %endrep?
        end repeat
        restore %_STATE
end macro

macro %exitrep?
        if %_STATE = 0
                %_STATE = 3
        end if
end macro

macro %error? message
        if %_STATE = 0
                err message
        end if
end macro

macro %include? path
        local Src
        virtual at 0
                db '%_preprocessor',10
                file path
                db '%_preprocessor_off'
                load Src:$ from 0
        end virtual
        eval Src
end macro

macro %_preprocessor
        macro ?! line&
                if %_STATE = -1
                        %_macrodefinition line
                else if %_STATE = 0 | `line and 0FFh = '%'
                        %_preprocess line
                end if
                %_line
        end macro
end macro

macro %_preprocessor_off
        purge ?
end macro

postpone
        %_preprocessor_off
end postpone

%_preprocessor

%idefine resb rb
%idefine resw rw
%idefine resd rd    
The emulation could probably be organized better and be closer to original NASM's preprocessor, but my aim was to get this specific source assembled with as little work as possible.

In the beginning of NASMPRE.INC there are simplified MZ header definitions that I made instead of using my standard MZ formatting macros in order to reproduce the original headers exactly.

As for SHSURDRV.NSM, it is not as easy to generate the file directly, since it links to functions from ZLIBCDRD.LIB. We would need to either generate an OMF object file and use external linker, or link the .LIB file at the assembly time. I don't know which one would be easier.
Post 19 May 2019, 16:14
View user's profile Send private message Visit poster's website Reply with quote
rugxulo



Joined: 09 Aug 2005
Posts: 2341
Location: Usono (aka, USA)
rugxulo 21 May 2019, 17:57
Tomasz Grysztar wrote:
It was not easy to find enough spare time to deal with this challenge, but I finally have succeeded to faithfully assemble one of the driver files from SHSUFDRV package.


Again, nice work. Cool

Haven't tried it yet, but I will take a look later.

Tomasz Grysztar wrote:

As for SHSURDRV.NSM, it is not as easy to generate the file directly, since it links to functions from ZLIBCDRD.LIB. We would need to either generate an OMF object file and use external linker, or link the .LIB file at the assembly time. I don't know which one would be easier.


The ZLIB stuff is only for compressed disk images. Actually, for my own use, I reassembled without that and just use it as a simple RAM driver. So it's much smaller (~8 kb). Sorry for the confusion, that's what I meant, without the ZLIB stuff.

To me, that is a relatively small and simple (but useful) utility, an unloadable RAM disk driver (386+, XMSv3 version).
Post 21 May 2019, 17:57
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.