flat assembler
Message board for the users of flat assembler.
![]() |
Author |
|
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 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.
|
|||||||||||
![]() |
|
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 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. |
|||
![]() |
|
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?
@@
|
|||
![]() |
|
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.) 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 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. |
|||
![]() |
|
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. ![]() Haven't tried it yet, but I will take a look later. Tomasz Grysztar wrote:
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). |
|||
![]() |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.