diff options
author | H. Peter Anvin <hpa@zytor.com> | 2002-04-30 20:52:08 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2002-04-30 20:52:08 +0000 |
commit | d7ed89eac9580f280fe0017b22c8e38ca75ed8e3 (patch) | |
tree | 98c4fcdd286b44e14f79aa65271e5caa1c2c7be4 | |
parent | ea8382740dbe5e1607742d0a7c7c139dffcc5ae5 (diff) | |
download | nasm-d7ed89eac9580f280fe0017b22c8e38ca75ed8e3.tar.gz nasm-d7ed89eac9580f280fe0017b22c8e38ca75ed8e3.tar.bz2 nasm-d7ed89eac9580f280fe0017b22c8e38ca75ed8e3.zip |
NASM 0.94
-rw-r--r-- | Changes | 44 | ||||
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | Makefile.bc2 | 5 | ||||
-rw-r--r-- | Makefile.bor | 7 | ||||
-rw-r--r-- | Makefile.dos | 4 | ||||
-rw-r--r-- | Makefile.wc | 114 | ||||
-rw-r--r-- | Makefile.wcw | 114 | ||||
-rw-r--r-- | Readme | 45 | ||||
-rw-r--r-- | assemble.c | 101 | ||||
-rw-r--r-- | disasm.c | 2 | ||||
-rw-r--r-- | insns.dat | 64 | ||||
-rw-r--r-- | insns.h | 31 | ||||
-rw-r--r-- | insns.pl | 11 | ||||
-rw-r--r-- | internal.doc | 28 | ||||
-rw-r--r-- | labels.c | 2 | ||||
-rw-r--r-- | lcc/Readme | 5 | ||||
-rw-r--r-- | lcc/lin-aout.c | 2 | ||||
-rw-r--r-- | lcc/lin-elf.c | 2 | ||||
-rw-r--r-- | macros.c | 52 | ||||
-rw-r--r-- | misc/nasm.sl | 153 | ||||
-rw-r--r-- | misc/pmw.bat | 9 | ||||
-rw-r--r-- | names.c | 83 | ||||
-rw-r--r-- | nasm.c | 413 | ||||
-rw-r--r-- | nasm.doc | 535 | ||||
-rw-r--r-- | nasm.h | 147 | ||||
-rw-r--r-- | nasmlib.c | 83 | ||||
-rw-r--r-- | nasmlib.h | 24 | ||||
-rw-r--r-- | ndisasm.c | 3 | ||||
-rw-r--r-- | outaout.c | 8 | ||||
-rw-r--r-- | outas86.c | 24 | ||||
-rw-r--r-- | outbin.c | 4 | ||||
-rw-r--r-- | outcoff.c | 4 | ||||
-rw-r--r-- | outelf.c | 4 | ||||
-rw-r--r-- | outobj.c | 10 | ||||
-rw-r--r-- | outrdf.c | 32 | ||||
-rw-r--r-- | parser.c | 63 | ||||
-rw-r--r-- | parser.h | 4 | ||||
-rw-r--r-- | preproc.c | 2148 | ||||
-rw-r--r-- | preproc.h | 14 | ||||
-rw-r--r-- | rdoff/Makefile | 26 | ||||
-rw-r--r-- | rdoff/README | 85 | ||||
-rw-r--r-- | rdoff/collectn.h | 2 | ||||
-rw-r--r-- | rdoff/ldrdf.c | 362 | ||||
-rw-r--r-- | rdoff/rdf.doc | 99 | ||||
-rw-r--r-- | rdoff/rdf2bin.c | 125 | ||||
-rw-r--r-- | rdoff/rdfdump.c | 32 | ||||
-rw-r--r-- | rdoff/rdflib.c | 235 | ||||
-rw-r--r-- | rdoff/rdfload.c | 12 | ||||
-rw-r--r-- | rdoff/rdlib.c | 88 | ||||
-rw-r--r-- | rdoff/rdlib.h | 18 | ||||
-rw-r--r-- | rdoff/rdoff.c | 52 | ||||
-rw-r--r-- | rdoff/rdoff.h | 8 | ||||
-rw-r--r-- | rdoff/rdoff.txt | 114 | ||||
-rw-r--r-- | rdoff/symtab.c | 2 | ||||
-rw-r--r-- | sync.c | 7 | ||||
-rw-r--r-- | test/aouttest.asm | 26 | ||||
-rw-r--r-- | test/bintest.asm | 10 | ||||
-rw-r--r-- | test/cofftest.asm | 24 | ||||
-rw-r--r-- | test/elftest.asm | 24 | ||||
-rw-r--r-- | test/inc1.asm | 2 | ||||
-rw-r--r-- | test/inctest.asm | 8 | ||||
-rw-r--r-- | test/objtest.asm | 26 |
62 files changed, 4985 insertions, 815 deletions
@@ -81,3 +81,47 @@ seg-fault under Linux. Included a new Borland C makefile, Makefile.bc2, donated by Fox Cutter <lmb@comtch.iea.com>. + +0.94 not released yet +--------------------- + +Major item: added the macro processor. + +Added undocumented instructions SMI, IBTS, XBTS and LOADALL286. Also +reorganised CMPXCHG instruction into early-486 and Pentium forms. +Thanks to Thobias Jones for the information. + +Fixed two more stupid bugs in ELF, which were causing `ld' to +continue to seg-fault in a lot of non-trivial cases. + +Fixed a seg-fault in the label manager. + +Stopped FBLD and FBSTP from _requiring_ the TWORD keyword, which is +the only option for BCD loads/stores in any case. + +Ensured FLDCW, FSTCW and FSTSW can cope with the WORD keyword, if +anyone bothers to provide it. Previously they complained unless no +keyword at all was present. + +Some forms of FDIV/FDIVR and FSUB/FSUBR were still inverted: a +vestige of a bug that I thought had been fixed in 0.92. This was +fixed, hopefully for good this time... + +Another minor phase error (insofar as a phase error can _ever_ be +minor) fixed, this one occurring in code of the form + rol ax,forward_reference + forward_reference equ 1 + +The number supplied to TIMES is now sanity-checked for positivity, +and also may be greater than 64K (which previously didn't work on +16-bit systems). + +Added Watcom C makefiles, and misc/pmw.bat, donated by Dominik Behr. + +Added the INCBIN pseudo-opcode. + +Due to the advent of the preprocessor, the [INCLUDE] and [INC] +directives have become obsolete. They are still supported in this +version, with a warning, but won't be in the next. + +Updates to RDOFF subdirectory, and changes to outrdf.c. @@ -31,7 +31,8 @@ OBJ = o# NASMOBJS = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) \ assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) \ outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) \ - outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) + outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) \ + preproc.$(OBJ) NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \ insnsd.$(OBJ) @@ -57,10 +58,13 @@ outas86.$(OBJ): outas86.c nasm.h nasmlib.h outaout.$(OBJ): outaout.c nasm.h nasmlib.h outbin.$(OBJ): outbin.c nasm.h nasmlib.h outcoff.$(OBJ): outcoff.c nasm.h nasmlib.h +outdbg.$(OBJ): outdbg.c nasm.h nasmlib.h outelf.$(OBJ): outelf.c nasm.h nasmlib.h outobj.$(OBJ): outobj.c nasm.h nasmlib.h +outrdf.$(OBJ): outrdf.c nasm.h nasmlib.h outform.$(OBJ): outform.c outform.h nasm.h parser.$(OBJ): parser.c nasm.h nasmlib.h parser.h float.h names.c +preproc.$(OBJ): preproc.c macros.c preproc.h nasm.h nasmlib.h sync.$(OBJ): sync.c sync.h # These two source files are automagically generated from a single @@ -72,6 +76,15 @@ AUTOSRCS = insnsa.c insnsd.c $(AUTOSRCS): insns.dat insns.pl perl insns.pl +# This source file is generated from the standard macros file +# `standard.mac' by another Perl script. Again, it's part of the +# standard distribution. + +macros.c: standard.mac + perl macros.pl + +# Clean the whole thing up after compilation. + clean : rm -f $(NASMOBJS) $(NDISASMOBJS) nasm$(EXE) ndisasm$(EXE) make -C rdoff clean diff --git a/Makefile.bc2 b/Makefile.bc2 index f160fad..d2e9b52 100644 --- a/Makefile.bc2 +++ b/Makefile.bc2 @@ -71,7 +71,7 @@ DASM_ASM=$(CC) $(DCCFLAGS) $&.c #command line for NDISASM NASMOBJS = $(OBJD)nasm.$(OBJ) $(OBJD)nasmlib.$(OBJ) $(OBJD)float.$(OBJ) \ $(OBJD)insnsa.$(OBJ) $(OBJD)assemble.$(OBJ) $(OBJD)labels.$(OBJ) \ - $(OBJD)parser.$(OBJ) $(OBJD)outform.$(OBJ) + $(OBJD)parser.$(OBJ) $(OBJD)outform.$(OBJ) $(OBJD)preproc.$(OBJ) ################################################################ #The OBJ files that NDISASM is dependent on @@ -133,6 +133,9 @@ $(OBJD)nasmlib.$(OBJ): nasmlib.c nasm.h nasmlib.h $(OBJD)parser.$(OBJ): parser.c nasm.h nasmlib.h parser.h float.h names.c $(NASM_ASM) +$(OBJD)preproc.$(OBJ): preproc.c macros.c preproc.h nasm.h nasmlib.h + $(NASM_ASM) + $(OBJD)insnsa.$(OBJ): insnsa.c nasm.h insns.h $(NASM_ASM) diff --git a/Makefile.bor b/Makefile.bor index 75aed4f..7de3791 100644 --- a/Makefile.bor +++ b/Makefile.bor @@ -27,8 +27,9 @@ NASMOBJS1 = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) NASMOBJS2 = assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) NASMOBJS3 = outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) NASMOBJS4 = outobj.$(OBJ) outas86.$(OBJ) outdbg.$(OBJ) outrdf.$(OBJ) +NASMOBJS5 = preproc.$(OBJ) -NASMOBJS = $(NASMOBJS1) $(NASMOBJS2) $(NASMOBJS3) $(NASMOBJS4) +NASMOBJS = $(NASMOBJS1) $(NASMOBJS2) $(NASMOBJS3) $(NASMOBJS4) $(NASMOBJS5) NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \ insnsd.$(OBJ) @@ -42,7 +43,8 @@ nasm$(EXE): $(NASMOBJS) echo c0l.obj $(NASMOBJS1) +> nasmobjs.tmp echo $(NASMOBJS2) +>> nasmobjs.tmp echo $(NASMOBJS3) +>> nasmobjs.tmp - echo $(NASMOBJS4),nasm.exe,,cl.lib, >> nasmobjs.tmp + echo $(NASMOBJS4) +>> nasmobjs.tmp + echo $(NASMOBJS5),nasm.exe,,cl.lib, >> nasmobjs.tmp $(LINK) /Tde @nasmobjs.tmp ndisasm$(EXE): $(NDISASMOBJS) @@ -68,6 +70,7 @@ outobj.$(OBJ): outobj.c nasm.h nasmlib.h outrdf.$(OBJ): outrdf.c nasm.h nasmlib.h outform.$(OBJ): outform.c outform.h nasm.h parser.$(OBJ): parser.c nasm.h nasmlib.h parser.h float.h names.c +preproc.$(OBJ): preproc.c macros.c preproc.h nasm.h nasmlib.h sync.$(OBJ): sync.c sync.h clean : diff --git a/Makefile.dos b/Makefile.dos index ebf655d..99a7a72 100644 --- a/Makefile.dos +++ b/Makefile.dos @@ -24,7 +24,8 @@ OBJ = obj# NASMOBJS = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) \ assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) \ outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) \ - outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) + outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) \ + preproc.$(OBJ) NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \ insnsd.$(OBJ) @@ -57,6 +58,7 @@ outobj.$(OBJ): outobj.c nasm.h nasmlib.h outrdf.$(OBJ): outrdf.c nasm.h nasmlib.h outform.$(OBJ): outform.c outform.h nasm.h parser.$(OBJ): parser.c nasm.h nasmlib.h parser.h float.h names.c +preproc.$(OBJ): preproc.c macros.c preproc.h nasm.h nasmlib.h sync.$(OBJ): sync.c sync.h # Another grotty hack: QC is less likely to run out of memory than diff --git a/Makefile.wc b/Makefile.wc new file mode 100644 index 0000000..f2184a6 --- /dev/null +++ b/Makefile.wc @@ -0,0 +1,114 @@ +# host: watcom c (dos, windows, os/2) +# target: dos 32bit +# Makefile for the Netwide Assembler +# +# The Netwide Assembler is copyright (C) 1996 Simon Tatham and +# Julian Hall. All rights reserved. The software is +# redistributable under the licence given in the file "Licence" +# distributed in the NASM archive. +# +# this makefile is designed for use with dos version of Watcom C 32 bit +# compiler, it generates dos 32 bit executable +# it has been tested with +# borland make.exe 4.0 +# microsoft nmake.exe 1.3 +# watcom wmake.exe /u 3.2 (remember about that /u option :) +# +# all this should compile under watcom c 9.5, 10.0, 10.5 and 10.6 +# i dont know about 11.0 because i didnt yet see it + +CFLAGS = -fpi -mf -3r -s -bt=dos -oilrt +# -fpi inline math + emulation +# -mf flat model (isnt it by default :) +# -3r 386 register calling (does everyone have pentium nowadays ?) +# -s no stack checking +# -bt=dos target system - dos +# -oilrt mega cool optimization :) + +CC = wcc386.exe $(CFLAGS) +# compiler +LFLAGS = SYSTEM dos4g +# linker flags +# target system - dos4gw +LD = wlink.exe $(LFLAGS) +# linker +OBJ = obj +# whatever + +.c.$(OBJ): + $(CC) $*.c + +NASMOBJS = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) \ + assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) \ + outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) \ + outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) \ + preproc.$(OBJ) + +NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \ + insnsd.$(OBJ) + +all : nasm.exe ndisasm.exe + echo This is dummy command for dumb make + +nasm.exe: $(NASMOBJS) NASM.LNK + $(LD) @NASM.LNK + +ndisasm.exe: $(NDISASMOBJS) NDISASM.LNK + $(LD) @NDISASM.LNK + +# linker response files +# that may take long, too much spawning command.com :) +NASM.LNK: makefile.wc + echo N nasm.exe > NASM.LNK + echo F nasm.$(OBJ) >> NASM.LNK + echo F nasmlib.$(OBJ) >> NASM.LNK + echo F float.$(OBJ) >> NASM.LNK + echo F insnsa.$(OBJ) >> NASM.LNK + echo F assemble.$(OBJ) >> NASM.LNK + echo F labels.$(OBJ) >> NASM.LNK + echo F parser.$(OBJ) >> NASM.LNK + echo F preproc.$(OBJ) >> NASM.LNK + echo F outform.$(OBJ) >> NASM.LNK + echo F outbin.$(OBJ) >> NASM.LNK + echo F outaout.$(OBJ) >> NASM.LNK + echo F outcoff.$(OBJ) >> NASM.LNK + echo F outelf.$(OBJ) >> NASM.LNK + echo F outobj.$(OBJ) >> NASM.LNK + echo F outas86.$(OBJ) >> NASM.LNK + echo F outrdf.$(OBJ) >> NASM.LNK + echo F outdbg.$(OBJ) >> NASM.LNK + +NDISASM.LNK: makefile.wc + echo N ndisasm.exe > NDISASM.LNK + echo F ndisasm.$(OBJ) >> NDISASM.LNK + echo F disasm.$(OBJ) >> NDISASM.LNK + echo F sync.$(OBJ) >> NDISASM.LNK + echo F nasmlib.$(OBJ) >> NDISASM.LNK + echo F insnsd.$(OBJ) >> NDISASM.LNK + +assemble.$(OBJ): assemble.c nasm.h assemble.h insns.h +disasm.$(OBJ): disasm.c nasm.h disasm.h sync.h insns.h names.c +float.$(OBJ): float.c nasm.h +insnsa.$(OBJ): insnsa.c nasm.h insns.h +insnsd.$(OBJ): insnsd.c nasm.h insns.h +labels.$(OBJ): labels.c nasm.h nasmlib.h +nasm.$(OBJ): nasm.c nasm.h nasmlib.h parser.h assemble.h labels.h outform.h +nasmlib.$(OBJ): nasmlib.c nasm.h nasmlib.h +ndisasm.$(OBJ): ndisasm.c nasm.h sync.h disasm.h +outas86.$(OBJ): outas86.c nasm.h nasmlib.h +outaout.$(OBJ): outaout.c nasm.h nasmlib.h +outbin.$(OBJ): outbin.c nasm.h nasmlib.h +outcoff.$(OBJ): outcoff.c nasm.h nasmlib.h +outelf.$(OBJ): outelf.c nasm.h nasmlib.h +outobj.$(OBJ): outobj.c nasm.h nasmlib.h +outform.$(OBJ): outform.c outform.h nasm.h +parser.$(OBJ): parser.c nasm.h nasmlib.h parser.h float.h names.c +preproc.$(OBJ): preproc.c macros.c preproc.h nasm.h nasmlib.h +sync.$(OBJ): sync.c sync.h + +clean : + del *.obj + del *.lnk + del nasm.exe + del ndisasm.exe + diff --git a/Makefile.wcw b/Makefile.wcw new file mode 100644 index 0000000..4942df5 --- /dev/null +++ b/Makefile.wcw @@ -0,0 +1,114 @@ +# host: watcom c (dos, windows, os/2) +# target: windows nt character mode executable +# Makefile for the Netwide Assembler +# +# The Netwide Assembler is copyright (C) 1996 Simon Tatham and +# Julian Hall. All rights reserved. The software is +# redistributable under the licence given in the file "Licence" +# distributed in the NASM archive. +# +# this makefile is designed for use with of Watcom C 32 bit compiler +# it generates win32 console (character mode) executable +# it has been tested with +# borland make.exe 4.0 +# microsoft nmake.exe 1.3 +# watcom wmake.exe /u 3.2 (remember about that /u option :) +# +# all this should compile under watcom c 9.5, 10.0, 10.5 and 10.6 +# i dont know about 11.0 because i didnt yet see it + +CFLAGS = -fpi -mf -3r -s -bt=dos -oilrt +# -fpi inline math + emulation +# -mf flat model (isnt it by default :) +# -3r 386 register calling (does everyone have pentium nowadays ?) +# -s no stack checking +# -bt=nt target system - windows nt +# -oilrt mega cool optimization :) + +CC = wcc386.exe $(CFLAGS) +# compiler +LFLAGS = SYSTEM nt +# linker flags +# target system nt - character mode +LD = wlink.exe $(LFLAGS) +# linker +OBJ = obj +# whatever + +.c.$(OBJ): + $(CC) $*.c + +NASMOBJS = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) \ + assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) \ + outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) \ + outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) \ + preproc.$(OBJ) + +NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \ + insnsd.$(OBJ) + +all : nasm.exe ndisasm.exe + echo This is dummy command for dumb make + +nasm.exe: $(NASMOBJS) NASM.LNK + $(LD) @NASM.LNK + +ndisasm.exe: $(NDISASMOBJS) NDISASM.LNK + $(LD) @NDISASM.LNK + +# linker response files +# that may take long, too much spawning command.com :) +NASM.LNK: makefile.wcw + echo N nasm.exe > NASM.LNK + echo F nasm.$(OBJ) >> NASM.LNK + echo F nasmlib.$(OBJ) >> NASM.LNK + echo F float.$(OBJ) >> NASM.LNK + echo F insnsa.$(OBJ) >> NASM.LNK + echo F assemble.$(OBJ) >> NASM.LNK + echo F labels.$(OBJ) >> NASM.LNK + echo F parser.$(OBJ) >> NASM.LNK + echo F preproc.$(OBJ) >> NASM.LNK + echo F outform.$(OBJ) >> NASM.LNK + echo F outbin.$(OBJ) >> NASM.LNK + echo F outaout.$(OBJ) >> NASM.LNK + echo F outcoff.$(OBJ) >> NASM.LNK + echo F outelf.$(OBJ) >> NASM.LNK + echo F outobj.$(OBJ) >> NASM.LNK + echo F outas86.$(OBJ) >> NASM.LNK + echo F outrdf.$(OBJ) >> NASM.LNK + echo F outdbg.$(OBJ) >> NASM.LNK + +NDISASM.LNK: makefile.wcw + echo N ndisasm.exe > NDISASM.LNK + echo F ndisasm.$(OBJ) >> NDISASM.LNK + echo F disasm.$(OBJ) >> NDISASM.LNK + echo F sync.$(OBJ) >> NDISASM.LNK + echo F nasmlib.$(OBJ) >> NDISASM.LNK + echo F insnsd.$(OBJ) >> NDISASM.LNK + +assemble.$(OBJ): assemble.c nasm.h assemble.h insns.h +disasm.$(OBJ): disasm.c nasm.h disasm.h sync.h insns.h names.c +float.$(OBJ): float.c nasm.h +insnsa.$(OBJ): insnsa.c nasm.h insns.h +insnsd.$(OBJ): insnsd.c nasm.h insns.h +labels.$(OBJ): labels.c nasm.h nasmlib.h +nasm.$(OBJ): nasm.c nasm.h nasmlib.h parser.h assemble.h labels.h outform.h +nasmlib.$(OBJ): nasmlib.c nasm.h nasmlib.h +ndisasm.$(OBJ): ndisasm.c nasm.h sync.h disasm.h +outas86.$(OBJ): outas86.c nasm.h nasmlib.h +outaout.$(OBJ): outaout.c nasm.h nasmlib.h +outbin.$(OBJ): outbin.c nasm.h nasmlib.h +outcoff.$(OBJ): outcoff.c nasm.h nasmlib.h +outelf.$(OBJ): outelf.c nasm.h nasmlib.h +outobj.$(OBJ): outobj.c nasm.h nasmlib.h +outform.$(OBJ): outform.c outform.h nasm.h +parser.$(OBJ): parser.c nasm.h nasmlib.h parser.h float.h names.c +preproc.$(OBJ): preproc.c macros.c preproc.h nasm.h nasmlib.h +sync.$(OBJ): sync.c sync.h + +clean : + del *.obj + del *.lnk + del nasm.exe + del ndisasm.exe + @@ -13,12 +13,36 @@ search path (maybe /usr/local/bin, or ~/bin if you don't have root access). You may also want to copy the man page `nasm.1' (and maybe `ndisasm.1') to somewhere sensible. -To rebuild the DOS sources, three makefiles are provided: -Makefile.dos, the one the standard release is built from, designed -for a hybrid system using Microsoft C and Borland Make (don't ask -why :-), Makefile.bor (for Borland C) and Makefile.bc2 (also for -Borland C, contributed by Fox Cutter <lmb@comtch.iea.com>, may work -better than Makefile.bor in some cases). +To rebuild the DOS sources, various makefiles are provided: +- Makefile.dos, the one I build the standard releases from, designed + for a hybrid system using Microsoft C and Borland Make (don't ask + why :-) +- Makefile.bor (for Borland C) +- Makefile.bc2 (also for Borland C, contributed by Fox Cutter + <lmb@comtch.iea.com>, may work better than Makefile.bor in some + cases). +- Makefile.wc, for Watcom C, compiling to a 32-bit extended DOS + executable. Contributed by Dominik Behr. +- Makefile.wcw, also for Watcom C, compiling to a Win32 command- + line application. Also contributed by Dominik Behr. + +I don't guarantee that any of those, other than Makefile.dos, work, +since I don't have the compilers to test them myself. Also be +warned: I have had various conflicting reports regarding building +NASM using Borland C. Several people have informed me that it +doesn't work except under Huge model, and one or two have said that +it doesn't work under Huge model either. + +Dominik Behr has also contributed the file misc/pmw.bat, which is a +batch file to turn the output from Makefile.wc (NASM.EXE and +NDISASM.EXE) into standalone executables incorporating Tran's +PMODE/W DOS extender, rather than depending on an external extender +program. + +If you're trying to unpack the DOS (.ZIP format) archive under Unix +instead of using the .tar.gz version, you can save some time by +doing `unzip -aL', which will convert the DOS-format text files to +Unix and also convert all names to lower case. If you want to build a restricted version of NASM containing only some of the object file formats, you can achieve this by adding @@ -36,6 +60,10 @@ enable it to work with the 32-bit binary files NASM can output: the original extender and his port `WDOSX/N' are available from his web page, http://www.geocities.com/SiliconValley/Park/4493. +Matt Mastracci has written a document explaining how to write +assembly language modules in DJGPP programs using NASM: it's on his +web site at http://www.ucalgary.ca/~mmastrac/djgppasm.doc. + The `misc' directory contains `nasm.sl', a NASM editing mode for the JED programmers' editor (see http://space.mit.edu/~davis/jed.html for details about JED). The comment at the start of the file gives @@ -54,7 +82,8 @@ that in many ways it was too restrictive for developers. For information about how to use NASM, see `nasm.doc'. For information about how to use NDISASM, see `ndisasm.doc'. For information about the internal structure of NASM, see -`internals.doc'. +`internal.doc'. (In particular, _please_ read `internal.doc' before +writing any code for us...) Bug reports (and patches if you can) should be sent to -jules@dcs.warwick.ac.uk or anakin@pobox.com. +<jules@dcs.warwick.ac.uk> or <anakin@pobox.com>. @@ -77,8 +77,8 @@ static int chsize (operand *, int); long assemble (long segment, long offset, int bits, insn *instruction, struct ofmt *output, efunc error) { - int j, itimes, size_prob; - long insn_end; + int j, size_prob; + long insn_end, itimes; long start = offset; struct itemplate *temp; @@ -94,8 +94,8 @@ long assemble (long segment, long offset, int bits, instruction->opcode == I_DQ || instruction->opcode == I_DT) { extop *e; - long osize, wsize = 0; /* placate gcc */ - int t = instruction->times; + long wsize = 0; /* placate gcc */ + long t = instruction->times; switch (instruction->opcode) { case I_DB: wsize = 1; break; @@ -107,7 +107,6 @@ long assemble (long segment, long offset, int bits, while (t--) { for (e = instruction->eops; e; e = e->next) { - osize = 0; if (e->type == EOT_DB_NUMBER) { if (wsize == 1) { if (e->segment != NO_SEG) @@ -144,6 +143,63 @@ long assemble (long segment, long offset, int bits, return offset - start; } + if (instruction->opcode == I_INCBIN) { + static char fname[FILENAME_MAX]; + FILE *fp; + long len; + + len = FILENAME_MAX-1; + if (len > instruction->eops->stringlen) + len = instruction->eops->stringlen; + strncpy (fname, instruction->eops->stringval, len); + fname[len] = '\0'; + if (!(fp = fopen(fname, "rb"))) + error (ERR_NONFATAL, "`incbin': unable to open file `%s'", fname); + else if (fseek(fp, 0L, SEEK_END) < 0) + error (ERR_NONFATAL, "`incbin': unable to seek on file `%s'", + fname); + else { + static char buf[2048]; + long t = instruction->times; + long l; + + len = ftell (fp); + if (instruction->eops->next) { + len -= instruction->eops->next->offset; + if (instruction->eops->next->next && + len > instruction->eops->next->next->offset) + len = instruction->eops->next->next->offset; + } + while (t--) { + fseek (fp, + (instruction->eops->next ? + instruction->eops->next->offset : 0), + SEEK_SET); + l = len; + while (l > 0) { + long m = fread (buf, 1, (l>sizeof(buf)?sizeof(buf):l), + fp); + if (!m) { + /* + * This shouldn't happen unless the file + * actually changes while we are reading + * it. + */ + error (ERR_NONFATAL, "`incbin': unexpected EOF while" + " reading file `%s'", fname); + return 0; /* it doesn't much matter... */ + } + outfmt->output (segment, buf, OUT_RAWDATA+m, + NO_SEG, NO_SEG); + l -= m; + } + } + fclose (fp); + return instruction->times * len; + } + return 0; /* if we're here, there's an error */ + } + size_prob = FALSE; temp = nasm_instructions[instruction->opcode]; while (temp->opcode != -1) { @@ -269,6 +325,35 @@ long insn_size (long segment, long offset, int bits, return isize * instruction->times; } + if (instruction->opcode == I_INCBIN) { + char fname[FILENAME_MAX]; + FILE *fp; + long len; + + len = FILENAME_MAX-1; + if (len > instruction->eops->stringlen) + len = instruction->eops->stringlen; + strncpy (fname, instruction->eops->stringval, len); + fname[len] = '\0'; + if (!(fp = fopen(fname, "rb"))) + error (ERR_NONFATAL, "`incbin': unable to open file `%s'", fname); + else if (fseek(fp, 0L, SEEK_END) < 0) + error (ERR_NONFATAL, "`incbin': unable to seek on file `%s'", + fname); + else { + len = ftell (fp); + fclose (fp); + if (instruction->eops->next) { + len -= instruction->eops->next->offset; + if (instruction->eops->next->next && + len > instruction->eops->next->next->offset) + len = instruction->eops->next->next->offset; + } + return instruction->times * len; + } + return 0; /* if we're here, there's an error */ + } + temp = nasm_instructions[instruction->opcode]; while (temp->opcode != -1) { if (matches(temp, instruction) == 100) { @@ -448,8 +533,8 @@ static void gencode (long segment, long offset, int bits, case 030: case 031: case 032: if (ins->oprs[c-030].segment == NO_SEG && ins->oprs[c-030].wrt == NO_SEG && - (ins->oprs[c-030].offset < -32768 || - ins->oprs[c-030].offset > 65535)) + (ins->oprs[c-030].offset < -32768L || + ins->oprs[c-030].offset > 65535L)) errfunc (ERR_WARNING, "word value exceeds bounds"); data = ins->oprs[c-030].offset; outfmt->output (segment, &data, OUT_ADDRESS+2, @@ -460,7 +545,7 @@ static void gencode (long segment, long offset, int bits, data = ins->oprs[c-034].offset; size = ((ins->oprs[c-034].addr_size ? ins->oprs[c-034].addr_size : bits) == 16 ? 2 : 4); - if (size==16 && (data < -32768 || data > 65535)) + if (size==16 && (data < -32768L || data > 65535L)) errfunc (ERR_WARNING, "word value exceeds bounds"); outfmt->output (segment, &data, OUT_ADDRESS+size, ins->oprs[c-034].segment, ins->oprs[c-034].wrt); @@ -71,6 +71,8 @@ static int whichreg(long regflags, int regval) { return R_CR4; if (!(FPU0 & ~regflags)) return R_ST0; + if (!(REG_CS & ~regflags)) + return R_CS; if (!((REGMEM|BITS8) & ~regflags)) return reg8[regval]; if (!((REGMEM|BITS16) & ~regflags)) @@ -175,12 +175,18 @@ CMP mem,imm32 \321\300\1\x81\207\41 386,SM CMPSB void \1\xA6 8086 CMPSD void \321\1\xA7 386 CMPSW void \320\1\xA7 8086 -CMPXCHG mem,reg8 \300\2\x0F\xA6\101 486,SM -CMPXCHG reg8,reg8 \300\2\x0F\xA6\101 486 -CMPXCHG mem,reg16 \320\300\2\x0F\xA7\101 486,SM -CMPXCHG reg16,reg16 \320\300\2\x0F\xA7\101 486 -CMPXCHG mem,reg32 \321\300\2\x0F\xA7\101 486,SM -CMPXCHG reg32,reg32 \321\300\2\x0F\xA7\101 486 +CMPXCHG mem,reg8 \300\2\x0F\xB0\101 PENT,SM +CMPXCHG reg8,reg8 \300\2\x0F\xB0\101 PENT +CMPXCHG mem,reg16 \320\300\2\x0F\xB1\101 PENT,SM +CMPXCHG reg16,reg16 \320\300\2\x0F\xB1\101 PENT +CMPXCHG mem,reg32 \321\300\2\x0F\xB1\101 PENT,SM +CMPXCHG reg32,reg32 \321\300\2\x0F\xB1\101 PENT +CMPXCHG486 mem,reg8 \300\2\x0F\xA6\101 486,SM,UNDOC +CMPXCHG486 reg8,reg8 \300\2\x0F\xA6\101 486,UNDOC +CMPXCHG486 mem,reg16 \320\300\2\x0F\xA7\101 486,SM,UNDOC +CMPXCHG486 reg16,reg16 \320\300\2\x0F\xA7\101 486,UNDOC +CMPXCHG486 mem,reg32 \321\300\2\x0F\xA7\101 486,SM,UNDOC +CMPXCHG486 reg32,reg32 \321\300\2\x0F\xA7\101 486,UNDOC CMPXCHG8B mem \300\2\x0F\xC7\201 PENT CPUID void \2\x0F\xA2 PENT CWD void \320\1\x99 8086 @@ -215,7 +221,9 @@ FADD fpu0,fpureg \1\xD8\11\xC0 8086,FPU FADDP fpureg \1\xDE\10\xC0 8086,FPU FADDP fpureg,fpu0 \1\xDE\10\xC0 8086,FPU FBLD mem80 \300\1\xDF\204 8086,FPU +FBLD mem \300\1\xDF\204 8086,FPU FBSTP mem80 \300\1\xDF\206 8086,FPU +FBSTP mem \300\1\xDF\206 8086,FPU FCHS void \2\xD9\xE0 8086,FPU FCLEX void \2\xDB\xE2 8086,FPU FCMOVB fpureg \1\xDA\10\xC0 P6,FPU @@ -252,16 +260,16 @@ FDECSTP void \2\xD9\xF6 8086,FPU FDISI void \2\xDB\xE1 8086,FPU FDIV mem32 \300\1\xD8\206 8086,FPU FDIV mem64 \300\1\xDC\206 8086,FPU -FDIV fpureg|to \1\xDC\10\xF0 8086,FPU -FDIV fpureg,fpu0 \1\xDC\10\xF0 8086,FPU +FDIV fpureg|to \1\xDC\10\xF8 8086,FPU +FDIV fpureg,fpu0 \1\xDC\10\xF8 8086,FPU FDIV fpureg \1\xD8\10\xF0 8086,FPU FDIV fpu0,fpureg \1\xD8\11\xF0 8086,FPU FDIVP fpureg,fpu0 \1\xDE\10\xF8 8086,FPU FDIVP fpureg \1\xDE\10\xF8 8086,FPU FDIVR mem32 \300\1\xD8\207 8086,FPU FDIVR mem64 \300\1\xDC\207 8086,FPU -FDIVR fpureg|to \1\xDC\10\xF8 8086,FPU -FDIVR fpureg,fpu0 \1\xDC\10\xF8 8086,FPU +FDIVR fpureg|to \1\xDC\10\xF0 8086,FPU +FDIVR fpureg,fpu0 \1\xDC\10\xF0 8086,FPU FDIVR fpureg \1\xD8\10\xF8 8086,FPU FDIVR fpu0,fpureg \1\xD8\11\xF8 8086,FPU FDIVRP fpureg \1\xDE\10\xF0 8086,FPU @@ -299,7 +307,7 @@ FLD mem64 \300\1\xDD\200 8086,FPU FLD mem80 \300\1\xDB\205 8086,FPU FLD fpureg \1\xD9\10\xC0 8086,FPU FLD1 void \2\xD9\xE8 8086,FPU -FLDCW mem \300\1\xD9\205 8086,FPU +FLDCW mem \300\1\xD9\205 8086,FPU,SW FLDENV mem \300\1\xD9\204 8086,FPU FLDL2E void \2\xD9\xEA 8086,FPU FLDL2T void \2\xD9\xE9 8086,FPU @@ -331,26 +339,26 @@ FSQRT void \2\xD9\xFA 8086,FPU FST mem32 \300\1\xD9\202 8086,FPU FST mem64 \300\1\xDD\202 8086,FPU FST fpureg \1\xDD\10\xD0 8086,FPU -FSTCW mem \300\1\xD9\207 8086,FPU +FSTCW mem \300\1\xD9\207 8086,FPU,SW FSTENV mem \300\1\xD9\206 8086,FPU FSTP mem32 \300\1\xD9\203 8086,FPU FSTP mem64 \300\1\xDD\203 8086,FPU FSTP mem80 \300\1\xDB\207 8086,FPU FSTP fpureg \1\xDD\10\xD8 8086,FPU -FSTSW mem \300\1\xDD\207 8086,FPU +FSTSW mem \300\1\xDD\207 8086,FPU,SW FSTSW reg_ax \2\xDF\xE0 286,FPU FSUB mem32 \300\1\xD8\204 8086,FPU FSUB mem64 \300\1\xDC\204 8086,FPU -FSUB fpureg|to \1\xDC\10\xE0 8086,FPU -FSUB fpureg,fpu0 \1\xDC\10\xE0 8086,FPU +FSUB fpureg|to \1\xDC\10\xE8 8086,FPU +FSUB fpureg,fpu0 \1\xDC\10\xE8 8086,FPU FSUB fpureg \1\xD8\10\xE0 8086,FPU FSUB fpu0,fpureg \1\xD8\11\xE0 8086,FPU FSUBP fpureg \1\xDE\10\xE8 8086,FPU FSUBP fpureg,fpu0 \1\xDE\10\xE8 8086,FPU FSUBR mem32 \300\1\xD8\205 8086,FPU FSUBR mem64 \300\1\xDC\205 8086,FPU -FSUBR fpureg|to \1\xDC\10\xE8 8086,FPU -FSUBR fpureg,fpu0 \1\xDC\10\xE8 8086,FPU +FSUBR fpureg|to \1\xDC\10\xE0 8086,FPU +FSUBR fpureg,fpu0 \1\xDC\10\xE0 8086,FPU FSUBR fpureg \1\xD8\10\xE8 8086,FPU FSUBR fpu0,fpureg \1\xD8\11\xE8 8086,FPU FSUBRP fpureg \1\xDE\10\xE0 8086,FPU @@ -372,6 +380,10 @@ FXTRACT void \2\xD9\xF4 8086,FPU FYL2X void \2\xD9\xF1 8086,FPU FYL2XP1 void \2\xD9\xF9 8086,FPU HLT void \1\xF4 8086 +IBTS mem,reg16 \320\300\2\x0F\xA7\101 386,SW,UNDOC,ND +IBTS reg16,reg16 \320\300\2\x0F\xA7\101 386,UNDOC,ND +IBTS mem,reg32 \321\300\2\x0F\xA7\101 386,SD,UNDOC,ND +IBTS reg32,reg32 \321\300\2\x0F\xA7\101 386,UNDOC,ND ICEBP void \1\xF1 286,UNDOC IDIV rm8 \300\1\xF6\207 8086 IDIV rm16 \320\300\1\xF7\207 8086 @@ -406,6 +418,7 @@ INC reg32 \321\10\x40 386 INC rm8 \300\1\xFE\200 8086 INC rm16 \320\300\1\xFF\200 8086 INC rm32 \321\300\1\xFF\200 386 +INCBIN ignore ignore ignore INSB void \1\x6C 186 INSD void \321\1\x6D 386 INSW void \320\1\x6D 186 @@ -416,7 +429,7 @@ INT3 void \1\xCC 8086 INTO void \1\xCE 8086 INVD void \2\x0F\x08 486 INVLPG mem \300\2\x0F\x01\207 486 -IRET void \1\xCF 8086 +IRET void \322\1\xCF 8086 IRETD void \321\1\xCF 386 IRETW void \320\1\xCF 8086 JCXZ imm \320\1\xE3\50 8086 @@ -465,6 +478,7 @@ LMSW mem \300\2\x0F\x01\206 286,PRIV LMSW mem16 \300\2\x0F\x01\206 286,PRIV LMSW reg16 \300\2\x0F\x01\206 286,PRIV LOADALL void \2\x0F\x07 386,UNDOC +LOADALL286 void \2\x0F\x05 286,UNDOC LODSB void \1\xAC 8086 LODSD void \321\1\xAD 386 LODSW void \320\1\xAD 8086 @@ -642,14 +656,15 @@ PMULLW mmxreg,mem \301\2\x0F\xD5\110 PENT,MMX,SM PMULLW mmxreg,mmxreg \2\x0F\xD5\110 PENT,MMX POP mem16 \320\300\1\x8F\200 8086 POP mem32 \321\300\1\x8F\200 386 +POP reg_cs \1\x0F 8086,UNDOC,ND POP reg_dess \4 8086 POP reg_fsgs \1\x0F\5 386 POP reg16 \320\10\x58 8086 POP reg32 \321\10\x58 386 -POPA void \1\x61 186 +POPA void \322\1\x61 186 POPAD void \321\1\x61 386 POPAW void \320\1\x61 186 -POPF void \1\x9D 186 +POPF void \322\1\x9D 186 POPFD void \321\1\x9D 386 POPFW void \320\1\x9D 186 POR mmxreg,mem \301\2\x0F\xEB\110 PENT,MMX,SM @@ -713,10 +728,10 @@ PUSH reg32 \321\10\x50 386 PUSH imm8 \1\x6A\14 286 PUSH imm16 \320\1\x68\30 286 PUSH imm32 \321\1\x68\40 386 -PUSHA void \1\x60 186 +PUSHA void \322\1\x60 186 PUSHAD void \321\1\x60 386 PUSHAW void \320\1\x60 186 -PUSHF void \1\x9C 186 +PUSHF void \322\1\x9C 186 PUSHFD void \321\1\x9C 386 PUSHFW void \320\1\x9C 186 PXOR mmxreg,mem \301\2\x0F\xEF\110 PENT,MMX,SM @@ -857,6 +872,7 @@ SIDT mem \300\2\x0F\x01\201 286,PRIV SLDT mem \300\1\x0F\17\200 286,PRIV SLDT mem16 \300\1\x0F\17\200 286,PRIV SLDT reg16 \300\1\x0F\17\200 286,PRIV +SMI void \1\xF1 386,UNDOC SMSW mem \300\2\x0F\x01\204 286,PRIV SMSW reg16 \300\2\x0F\x01\204 286,PRIV STC void \1\xF9 8086 @@ -933,6 +949,10 @@ XADD mem,reg16 \320\300\2\x0F\xC1\101 486,SM XADD reg16,reg16 \320\300\2\x0F\xC1\101 486 XADD mem,reg32 \321\300\2\x0F\xC1\101 486,SM XADD reg32,reg32 \321\300\2\x0F\xC1\101 486 +XBTS reg16,mem \320\301\2\x0F\xA6\110 386,SW,UNDOC,ND +XBTS reg16,reg16 \320\301\2\x0F\xA6\110 386,UNDOC,ND +XBTS reg32,mem \321\301\2\x0F\xA6\110 386,SD,UNDOC,ND +XBTS reg32,reg32 \321\301\2\x0F\xA6\110 386,UNDOC,ND XCHG reg_ax,reg16 \320\11\x90 8086 XCHG reg_eax,reg32 \321\11\x90 386 XCHG reg16,reg_ax \320\10\x90 8086 @@ -36,9 +36,8 @@ struct itemplate { * * IF_SB invokes Size Byte: operands with unspecified size in the * template are really bytes, and so no non-byte specification in - * the input instruction will be tolerated. - * - * IF_SD similarly invokes Size Doubleword. + * the input instruction will be tolerated. IF_SW similarly invokes + * Size Word, and IF_SD invokes Size Doubleword. * * (The default state if neither IF_SM nor IF_SM2 is specified is * that any operand with unspecified size in the template is @@ -48,19 +47,19 @@ struct itemplate { #define IF_SM 0x0001 /* size match */ #define IF_SM2 0x0002 /* size match first two operands */ #define IF_SB 0x0004 /* unsized operands can't be non-byte */ -#define IF_SD 0x0008 /* unsized operands can't be nondword */ +#define IF_SW 0x0008 /* unsized operands can't be non-word */ +#define IF_SD 0x0010 /* unsized operands can't be nondword */ #define IF_8086 0x0000 /* 8086 instruction */ -#define IF_186 0x0010 /* 186+ instruction */ -#define IF_286 0x0020 /* 286+ instruction */ -#define IF_386 0x0030 /* 386+ instruction */ -#define IF_486 0x0040 /* 486+ instruction */ -#define IF_PENT 0x0050 /* Pentium instruction */ -#define IF_P6 0x0060 /* P6 instruction */ -#define IF_PMASK 0x00F0 /* the mask for processor types */ -#define IF_PRIV 0x0100 /* it's a privileged instruction */ -#define IF_UNDOC 0x0200 /* it's an undocumented instruction */ -#define IF_FPU 0x0400 /* it's an FPU instruction */ -#define IF_MMX 0x0800 /* it's an MMX instruction */ -#define IF_ND 0x1000 /* ignore this in the disassembler */ +#define IF_186 0x0100 /* 186+ instruction */ +#define IF_286 0x0200 /* 286+ instruction */ +#define IF_386 0x0300 /* 386+ instruction */ +#define IF_486 0x0400 /* 486+ instruction */ +#define IF_PENT 0x0500 /* Pentium instruction */ +#define IF_P6 0x0600 /* P6 instruction */ +#define IF_PMASK 0x0F00 /* the mask for processor types */ +#define IF_PRIV 0x1000 /* it's a privileged instruction */ +#define IF_UNDOC 0x2000 /* it's an undocumented instruction */ +#define IF_FPU 0x4000 /* it's an FPU instruction */ +#define IF_MMX 0x8000 /* it's an MMX instruction */ #endif @@ -21,14 +21,14 @@ while (<F>) { split; next if $#_ == -1; # blank lines (warn "line $line does not contain four fields\n"), next if $#_ != 3; - $formatted = &format(@_); + ($formatted, $nd) = &format(@_); if ($formatted) { $insns++; $aname = "aa_$_[0]"; push @$aname, $formatted; } $opcodes[$opcodes++] = $_[0], $done{$_[0]} = 1 if !$done{$_[0]}; - if ($formatted && $formatted !~ /IF_ND/) { + if ($formatted && !$nd) { push @big, $formatted; foreach $i (&startbyte($_[2])) { $aname = sprintf "dd_%02X",$i; @@ -105,9 +105,9 @@ printf STDERR "Done: %d instructions\n", $insns; sub format { local ($opcode, $operands, $codes, $flags) = @_; - local $num; + local $num, $nd = 0; - return undef if $operands eq "ignore"; + return (undef, undef) if $operands eq "ignore"; # format the operands $operands =~ s/:/|colon,/g; @@ -124,9 +124,10 @@ sub format { # format the flags $flags =~ s/,/|IF_/g; + $flags =~ s/(\|IF_ND|IF_ND\|)//, $nd = 1 if $flags =~ /IF_ND/; $flags = "IF_" . $flags; - "{I_$opcode, $num, {$operands}, \"$codes\", $flags},"; + ("{I_$opcode, $num, {$operands}, \"$codes\", $flags},", $nd); } # Here we determine the range of possible starting bytes for a given diff --git a/internal.doc b/internal.doc index f04152a..9f84bb5 100644 --- a/internal.doc +++ b/internal.doc @@ -8,13 +8,15 @@ the back end to a compiler. The assembler is composed of modules. The interfaces between them look like: + +--- preproc.c ----+ + | | +---- parser.c ----+ | | | | float.c | | | +--- assemble.c ---+ - | | | - nasm.c ---+ insnsa.c +--- nasmlib.c + nasm.c ---+ | +--- nasmlib.c + | insnsa.c | | | +---- labels.c ----+ | | @@ -22,10 +24,10 @@ look like: | | +----- *out.c -----+ -In other words, each of `parser.c', `assemble.c', `labels.c', -`outform.c' and each of the output format modules `*out.c' are -independent modules, which do not inter-communicate except through -the main program. +In other words, each of `preproc.c', `parser.c', `assemble.c', +`labels.c', `outform.c' and each of the output format modules +`*out.c' are independent modules, which do not inter-communicate +except through the main program. The Netwide *Disassembler* is not intended to be particularly portable or reusable or anything, however. So I won't bother @@ -39,6 +41,16 @@ may be referenced by all other modules. Among these are a set of wrappers around the standard `malloc' routines, which will report a fatal error if they run out of memory, rather than returning NULL. +preproc.c +--------- + +This contains a macro preprocessor, which takes a file name as input +and returns a sequence of preprocessed source lines. The only symbol +exported from the module is `nasmpp', which is a data structure of +type `Preproc', declared in nasm.h. This structure contains pointers +to all the functions designed to be callable from outside the +module. + parser.c -------- @@ -168,12 +180,12 @@ corresponding to a given name. The output modules ------------------ -Each of the output modules, `binout.o', `elfout.o' and so on, +Each of the output modules, `outbin.o', `outelf.o' and so on, exports only one symbol, which is an output driver data structure containing pointers to all the functions needed to produce output files of the appropriate type. -The exception to this is `coffout.o', which exports _two_ output +The exception to this is `outcoff.o', which exports _two_ output driver structures, since COFF and Win32 object file formats are very similar and most of the code is shared between them. @@ -87,6 +87,8 @@ static union label *find_label (char *label, int create) { while (lptr->admin.movingon != END_LIST) { if (lptr->admin.movingon == END_BLOCK) { lptr = lptr->admin.next; + if (!lptr) + break; } if (!strncmp(lptr->defn.label, prev, prevlen) && !strcmp(lptr->defn.label+prevlen, label)) @@ -50,8 +50,3 @@ To install: triple test, or the compile will fail. - Now it should pass the triple test, on either ELF or a.out. Voila! - -Known potential problems: - -- The machine description may occasionally generate `db' lines that - are longer than NASM's 1024-character maximum. diff --git a/lcc/lin-aout.c b/lcc/lin-aout.c index f1ac88a..e3f2a58 100644 --- a/lcc/lin-aout.c +++ b/lcc/lin-aout.c @@ -17,7 +17,7 @@ char *include[] = { "-I" LCCDIR "include", "-I/usr/local/include", "-I/usr/include", 0 }; char *com[] = { LCCDIR "rcc", "-target=x86/nasm", "$1", "$2", "$3", 0 }; -char *as[] = { NASMPATH, "-faout", "-o", "$3", "$1", "$2", 0 }; +char *as[] = { NASMPATH, "-a", "-faout", "-o", "$3", "$1", "$2", 0 }; char *ld[] = { "/usr/bin/ld", "-m", "i386linux", "-L/usr/i486-linuxaout/lib", "-o", "$3", "$1", diff --git a/lcc/lin-elf.c b/lcc/lin-elf.c index 15df9e5..3af9fe8 100644 --- a/lcc/lin-elf.c +++ b/lcc/lin-elf.c @@ -16,7 +16,7 @@ char *include[] = { "-I" LCCDIR "include", "-I/usr/local/include", "-I/usr/include", 0 }; char *com[] = { LCCDIR "rcc", "-target=x86/nasm", "$1", "$2", "$3", 0 }; -char *as[] = { NASMPATH, "-felf", "-o", "$3", "$1", "$2", 0 }; +char *as[] = { NASMPATH, "-a", "-felf", "-o", "$3", "$1", "$2", 0 }; char *ld[] = { "/usr/bin/ld", "-m", "elf_i386", "-dynamic-linker", "/lib/ld-linux.so.1", "-L/usr/i486-linux/lib", diff --git a/macros.c b/macros.c new file mode 100644 index 0000000..7509bca --- /dev/null +++ b/macros.c @@ -0,0 +1,52 @@ +/* This file auto-generated from standard.mac by macros.pl - don't edit it */ + +static char *stdmac[] = { + "%define __SECT__", + "%imacro section 1+", + "%define __SECT__ [section %1]", + "__SECT__", + "%endmacro", + "%imacro segment 1+", + "%define __SECT__ [segment %1]", + "__SECT__", + "%endmacro", + "%imacro absolute 1+", + "%define __SECT__ [absolute %1]", + "__SECT__", + "%endmacro", + "%imacro struc 1", + "%push struc", + "%define %$strucname %1", + "[absolute 0]", + "%endmacro", + "%imacro endstruc 0", + "%{$strucname}_size:", + "%pop", + "__SECT__", + "%endmacro", + "%imacro extern 1+", + "[extern %1]", + "%endmacro", + "%imacro bits 1+", + "[bits %1]", + "%endmacro", + "%imacro global 1+", + "[global %1]", + "%endmacro", + "%imacro common 1+", + "[common %1]", + "%endmacro", + "%imacro org 1+", + "[org %1]", + "%endmacro", + "%imacro group 1+", + "[group %1]", + "%endmacro", + "%imacro uppercase 1+", + "[uppercase %1]", + "%endmacro", + "%imacro library 1+", + "[library %1]", + "%endmacro", + NULL +}; diff --git a/misc/nasm.sl b/misc/nasm.sl index be4d30b..325e1b6 100644 --- a/misc/nasm.sl +++ b/misc/nasm.sl @@ -22,79 +22,52 @@ variable nasm_kw_3 = strncat("a16a32aaaaadaamaasadcaddandbsfbsrbtcbtrbtscbw", "jngjnljnojnpjnsjnzjpejpolarldslealeslfslgslsl", "lssltrmm0mm1mm2mm3mm4mm5mm6mm7movmulnegnopnot", "o16o32outpopporrclrcrrepretrolrorrsmsalsarsbb", - "segshlshrst0st1st2st3st4st5st6st7stcstdstistr", - "subtr3tr4tr5tr6tr7wrtxor", 9); + "segshlshrsmist0st1st2st3st4st5st6st7stcstdsti", + "strsubtr3tr4tr5tr6tr7wrtxor", 9); variable nasm_kw_4 = strncat("arplbytecallcltscwdeemmsfabsfaddfbldfchsfcom", "fcosfdivfenifildfistfld1fldzfmulfnopfsinfstp", - "fsubftstfxamfxchidivimulinsbinsdinswint3into", - "invdiretjcxzjnaejnbejngejnlelahflgdtlidtlldt", - "lmswlocklongloopmovdmovqnearpandpopapopfpush", - "pxorreperepzresbresdreswretfretnsahfsetasetb", - "setcsetesetgsetlsetosetpsetssetzsgdtshldshrd", - "sidtsldtsmswtestverrverwwaitwordxaddxchg", 8); -variable nasm_kw_5 = strncat("boundbswapcmpsbcmpsdcmpswcpuiddwordenterf2xm1", - "faddpfbstpfclexfcompfdisifdivpfdivrffreefiadd", - "ficomfidivfimulfinitfistpfisubfldcwfldpifmulp", - "fpremfptanfsavefsqrtfstcwfstswfsubpfsubrfucom", - "fyl2xiretdiretwjecxzleavelodsblodsdlodswloope", - "loopzmovsbmovsdmovswmovsxmovzxoutsboutsdoutsw", - "paddbpadddpaddwpandnpopadpopawpopfdpopfwpslld", - "psllqpsllwpsradpsrawpsrldpsrlqpsrlwpsubbpsubd", - "psubwpushapushfqwordrdmsrrdtscrepnerepnzscasb", - "scasdscaswsetaesetbesetgesetlesetnasetnbsetnc", - "setnesetngsetnlsetnosetnpsetnssetnzsetpesetpo", - "shortstosbstosdstoswtimestwordwrmsrxlatb", 12); -variable nasm_kw_6 = strncat("fcomppfdivrpficompfidivrfisubrfldenvfldl2e", - "fldl2tfldlg2fldln2fpatanfprem1frstorfscale", - "fsetpmfstenvfsubrpfucompinvlpgloopneloopnz", + "fsubftstfxamfxchibtsidivimulinsbinsdinswint1", + "int3intoinvdiretjcxzjnaejnbejngejnlelahflgdt", + "lidtlldtlmswlocklongloopmovdmovqnearpandpopa", + "popfpushpxorreperepzresbresdresqrestreswretf", + "retnsahfsalcsetasetbsetcsetesetgsetlsetosetp", + "setssetzsgdtshldshrdsidtsldtsmswtestumovverr", + "verwwaitwordxaddxbtsxchg", 9); +variable nasm_kw_5 = strncat("boundbswapcmovacmovbcmovccmovecmovgcmovlcmovo", + "cmovpcmovscmovzcmpsbcmpsdcmpswcpuiddwordenter", + "f2xm1faddpfbstpfclexfcomifcompfdisifdivpfdivr", + "ffreefiaddficomfidivfimulfinitfistpfisubfldcw", + "fldpifmulpfpremfptanfsavefsqrtfstcwfstswfsubp", + "fsubrfucomfyl2xicebpint01iretdiretwjecxzleave", + "lodsblodsdlodswloopeloopzmovsbmovsdmovswmovsx", + "movzxoutsboutsdoutswpaddbpadddpaddwpandnpopad", + "popawpopfdpopfwpslldpsllqpsllwpsradpsrawpsrld", + "psrlqpsrlwpsubbpsubdpsubwpushapushfqwordrdmsr", + "rdpmcrdtscrepnerepnzscasbscasdscaswsetaesetbe", + "setgesetlesetnasetnbsetncsetnesetngsetnlsetno", + "setnpsetnssetnzsetpesetposhortstosbstosdstosw", + "timestwordwrmsrxlatb", 14); +variable nasm_kw_6 = strncat("cmovaecmovbecmovgecmovlecmovnacmovnbcmovnc", + "cmovnecmovngcmovnlcmovnocmovnpcmovnscmovnz", + "cmovpecmovpofcmovbfcmovefcmovufcomipfcompp", + "fdivrpficompfidivrfisubrfldenvfldl2efldl2t", + "fldlg2fldln2fpatanfprem1frstorfscalefsetpm", + "fstenvfsubrpfucomifucompinvlpgloopneloopnz", "paddsbpaddswpmulhwpmullwpsubsbpsubswpushad", "pushawpushfdpushfwsetnaesetnbesetngesetnle", - "wbinvd", 6); -variable nasm_kw_7 = strncat("cmpxchgfdecstpfincstpfrndintfsincosfucompp", - "fxtractfyl2xp1paddusbpadduswpcmpeqbpcmpeqd", - "pcmpeqwpcmpgtbpcmpgtdpcmpgtwpmaddwdpsubusb", - "psubusw", 4); -variable nasm_kw_8 = "packssdwpacksswbpackuswb"; + "wbinvd", 9); +variable nasm_kw_7 = strncat("cmovnaecmovnbecmovngecmovnlecmpxchgfcmovbe", + "fcmovnbfcmovnefcmovnufdecstpfincstpfrndint", + "fsincosfucomipfucomppfxtractfyl2xp1loadall", + "paddusbpadduswpcmpeqbpcmpeqdpcmpeqwpcmpgtb", + "pcmpgtdpcmpgtwpmaddwdpsubusbpsubusw", 5); +variable nasm_kw_8 = "fcmovnbepackssdwpacksswbpackuswb"; variable nasm_kw_9 = strcat("cmpxchg8bpunpckhbwpunpckhdqpunpckhwdpunpcklbw", "punpckldqpunpcklwd"); - -define nasm_is_kw { - variable word; - variable len; - variable list, min, max, pos, cmp; - - word = strlow(()); - len = strlen(word); - - switch (len) - { case 0: return 1; } - { case 2: list = nasm_kw_2; } - { case 3: list = nasm_kw_3; } - { case 4: list = nasm_kw_4; } - { case 5: list = nasm_kw_5; } - { case 6: list = nasm_kw_6; } - { case 7: list = nasm_kw_7; } - { case 8: list = nasm_kw_8; } - { case 9: list = nasm_kw_9; } - { pop(); return 0; } - - min = -1; - max = strlen(list) / len; - while (max - min >= 2) { - pos = (max + min) / 2; - cmp = strcmp(word, substr(list, pos * len + 1, len)); - if (cmp == 0) - return 1; % it's a keyword - else if (cmp < 0) - max = pos; % bottom half - else if (cmp > 0) - min = pos; % top half - } - return 0; -} +variable nasm_kw_10 = "cmpxchg486loadall286"; define nasm_indent_line() { - variable word, len, e; + variable word, len, e, c; e = eolp(); @@ -106,6 +79,7 @@ define nasm_indent_line() { } bol_skip_white(); + c = what_column(); if (orelse {looking_at_char(';')} @@ -118,14 +92,40 @@ define nasm_indent_line() { return; } + if (looking_at_char('%')) { + go_right_1(); + !if (orelse + {looking_at_char('$')} + {looking_at_char('%')} + {looking_at_char('+')} + {looking_at_char('-')} + {looking_at_char('0')} + {looking_at_char('1')} + {looking_at_char('2')} + {looking_at_char('3')} + {looking_at_char('4')} + {looking_at_char('5')} + {looking_at_char('6')} + {looking_at_char('7')} + {looking_at_char('8')} + {looking_at_char('9')}) { + bol_trim(); + pop_spot(); + EXIT_BLOCK { + } + return; + } + go_left_1(); + } + push_mark(); + skip_chars("%$+-"); skip_chars("0-9a-zA-Z_."); word = bufsubstr(); - if (nasm_is_kw(word)) { - bol_trim(); - whitespace(Nasm_Instruction_Indent); - } else { + if (orelse + {c == 1} + {looking_at_char(':')}) { push_spot(); bol_trim(); pop_spot(); @@ -147,6 +147,9 @@ define nasm_indent_line() { push_spot(); } } + } else { + bol_trim(); + whitespace(Nasm_Instruction_Indent); } } @@ -247,7 +250,7 @@ define_syntax ('\'', '\'', $1); define_syntax ("0-9a-zA-Z_.@#", 'w', $1); define_syntax ("-+0-9a-fA-F.xXL", '0', $1); define_syntax (",:", ',', $1); -define_syntax ('#', '#', $1); +define_syntax ('%', '#', $1); define_syntax ("|^&<>+-*/%~", '+', $1); set_syntax_flags($1,1); @@ -270,13 +273,15 @@ define_highlight_rule("\"[^\"]*$", "string", $1); define_highlight_rule("'[^']*'", "string", $1); define_highlight_rule("'[^']*$", "string", $1); define_highlight_rule("[\\(\\)\\[\\],:]*", "delimiter", $1); -define_highlight_rule("[\\|\\^&<>\\+\\-\\*/%~]*", "operator", $1); define_highlight_rule("^[ \t]*#", "PQpreprocess", $1); -define_highlight_rule("@[0-9A-Za-z_\\.]*", "keyword1", $1); +define_highlight_rule("^[ \t]*\\%{?[^%\\$\\+\\-0-9]", "PQpreprocess", $1); +define_highlight_rule("^%$", "preprocess", $1); +define_highlight_rule("[\\|\\^&<>\\+\\-\\*/%~]*", "operator", $1); +define_highlight_rule("%([%\\$]?-?[0-9A-Za-z_\\.\\?\\$~@]+|{[^}]*}?)", + "preprocess", $1); define_highlight_rule("[ \t]*", "normal", $1); define_highlight_rule(".", "normal", $1); build_highlight_table($1); - #endif define_keywords_n($1, nasm_kw_2, 2, 0); @@ -287,10 +292,12 @@ define_keywords_n($1, nasm_kw_6, 6, 0); define_keywords_n($1, nasm_kw_7, 7, 0); define_keywords_n($1, nasm_kw_8, 8, 0); define_keywords_n($1, nasm_kw_9, 9, 0); +define_keywords_n($1, nasm_kw_10, 10, 0); !if (keymap_p ($1)) make_keymap ($1); definekey("nasm_bol_self_ins", ";", $1); definekey("nasm_bol_self_ins", "#", $1); +definekey("nasm_bol_self_ins", "%", $1); definekey("nasm_bol_self_ins", "[", $1); definekey("nasm_self_ins_ind", ":", $1); definekey("nasm_insert_comment", "^[;", $1); diff --git a/misc/pmw.bat b/misc/pmw.bat new file mode 100644 index 0000000..8ed2bef --- /dev/null +++ b/misc/pmw.bat @@ -0,0 +1,9 @@ +@echo off +rem some batch file to bind nasm and ndisasm with pmode/w +rem a mega cool dos extender for watcom done by tran +rem +rem max 8 megs, dpmi stack 256*16=4096, no banner +pmwlite.exe nasm.exe +pmwsetup.exe /X8388608 /P256 /B0 nasm.exe +pmwlite.exe ndisasm.exe +pmwsetup.exe /X8388608 /P256 /B0 ndisasm.exe @@ -21,47 +21,48 @@ static char *insn_names[] = { /* instruction names, as strings */ "aaa", "aad", "aam", "aas", "adc", "add", "and", "arpl", "bound", "bsf", "bsr", "bswap", "bt", "btc", "btr", "bts", "call", "cbw", "cdq", "clc", "cld", "cli", "clts", "cmc", "cmp", - "cmpsb", "cmpsd", "cmpsw", "cmpxchg", "cmpxchg8b", "cpuid", - "cwd", "cwde", "daa", "das", "db", "dd", "dec", "div", "dq", - "dt", "dw", "emms", "enter", "equ", "f2xm1", "fabs", "fadd", - "faddp", "fbld", "fbstp", "fchs", "fclex", "fcmovb", "fcmovbe", - "fcmove", "fcmovnb", "fcmovnbe", "fcmovne", "fcmovnu", "fcmovu", - "fcom", "fcomi", "fcomip", "fcomp", "fcompp", "fcos", "fdecstp", - "fdisi", "fdiv", "fdivp", "fdivr", "fdivrp", "feni", "ffree", - "fiadd", "ficom", "ficomp", "fidiv", "fidivr", "fild", "fimul", - "fincstp", "finit", "fist", "fistp", "fisub", "fisubr", "fld", - "fld1", "fldcw", "fldenv", "fldl2e", "fldl2t", "fldlg2", - "fldln2", "fldpi", "fldz", "fmul", "fmulp", "fnop", "fpatan", - "fprem", "fprem1", "fptan", "frndint", "frstor", "fsave", - "fscale", "fsetpm", "fsin", "fsincos", "fsqrt", "fst", "fstcw", - "fstenv", "fstp", "fstsw", "fsub", "fsubp", "fsubr", "fsubrp", - "ftst", "fucom", "fucomi", "fucomip", "fucomp", "fucompp", - "fxam", "fxch", "fxtract", "fyl2x", "fyl2xp1", "hlt", "icebp", - "idiv", "imul", "in", "inc", "insb", "insd", "insw", "int", - "int1", "int01", "int3", "into", "invd", "invlpg", "iret", - "iretd", "iretw", "jcxz", "jecxz", "jmp", "lahf", "lar", "lds", - "lea", "leave", "les", "lfs", "lgdt", "lgs", "lidt", "lldt", - "lmsw", "loadall", "lodsb", "lodsd", "lodsw", "loop", "loope", - "loopne", "loopnz", "loopz", "lsl", "lss", "ltr", "mov", "movd", - "movq", "movsb", "movsd", "movsw", "movsx", "movzx", "mul", - "neg", "nop", "not", "or", "out", "outsb", "outsd", "outsw", - "packssdw", "packsswb", "packuswb", "paddb", "paddd", "paddsb", - "paddsw", "paddusb", "paddusw", "paddw", "pand", "pandn", - "pcmpeqb", "pcmpeqd", "pcmpeqw", "pcmpgtb", "pcmpgtd", - "pcmpgtw", "pmaddwd", "pmulhw", "pmullw", "pop", "popa", - "popad", "popaw", "popf", "popfd", "popfw", "por", "pslld", - "psllq", "psllw", "psrad", "psraw", "psrld", "psrlq", "psrlw", - "psubb", "psubd", "psubsb", "psubsw", "psubusb", "psubusw", - "psubw", "punpckhbw", "punpckhdq", "punpckhwd", "punpcklbw", - "punpckldq", "punpcklwd", "push", "pusha", "pushad", "pushaw", - "pushf", "pushfd", "pushfw", "pxor", "rcl", "rcr", "rdmsr", - "rdpmc", "rdtsc", "resb", "resd", "resq", "rest", "resw", "ret", - "retf", "retn", "rol", "ror", "rsm", "sahf", "sal", "salc", - "sar", "sbb", "scasb", "scasd", "scasw", "sgdt", "shl", "shld", - "shr", "shrd", "sidt", "sldt", "smsw", "stc", "std", "sti", - "stosb", "stosd", "stosw", "str", "sub", "test", "umov", "verr", - "verw", "wait", "wbinvd", "wrmsr", "xadd", "xchg", "xlatb", - "xor" + "cmpsb", "cmpsd", "cmpsw", "cmpxchg", "cmpxchg486", "cmpxchg8b", + "cpuid", "cwd", "cwde", "daa", "das", "db", "dd", "dec", "div", + "dq", "dt", "dw", "emms", "enter", "equ", "f2xm1", "fabs", + "fadd", "faddp", "fbld", "fbstp", "fchs", "fclex", "fcmovb", + "fcmovbe", "fcmove", "fcmovnb", "fcmovnbe", "fcmovne", + "fcmovnu", "fcmovu", "fcom", "fcomi", "fcomip", "fcomp", + "fcompp", "fcos", "fdecstp", "fdisi", "fdiv", "fdivp", "fdivr", + "fdivrp", "feni", "ffree", "fiadd", "ficom", "ficomp", "fidiv", + "fidivr", "fild", "fimul", "fincstp", "finit", "fist", "fistp", + "fisub", "fisubr", "fld", "fld1", "fldcw", "fldenv", "fldl2e", + "fldl2t", "fldlg2", "fldln2", "fldpi", "fldz", "fmul", "fmulp", + "fnop", "fpatan", "fprem", "fprem1", "fptan", "frndint", + "frstor", "fsave", "fscale", "fsetpm", "fsin", "fsincos", + "fsqrt", "fst", "fstcw", "fstenv", "fstp", "fstsw", "fsub", + "fsubp", "fsubr", "fsubrp", "ftst", "fucom", "fucomi", + "fucomip", "fucomp", "fucompp", "fxam", "fxch", "fxtract", + "fyl2x", "fyl2xp1", "hlt", "ibts", "icebp", "idiv", "imul", + "in", "inc", "incbin", "insb", "insd", "insw", "int", "int1", + "int01", "int3", "into", "invd", "invlpg", "iret", "iretd", + "iretw", "jcxz", "jecxz", "jmp", "lahf", "lar", "lds", "lea", + "leave", "les", "lfs", "lgdt", "lgs", "lidt", "lldt", "lmsw", + "loadall", "loadall286", "lodsb", "lodsd", "lodsw", "loop", + "loope", "loopne", "loopnz", "loopz", "lsl", "lss", "ltr", + "mov", "movd", "movq", "movsb", "movsd", "movsw", "movsx", + "movzx", "mul", "neg", "nop", "not", "or", "out", "outsb", + "outsd", "outsw", "packssdw", "packsswb", "packuswb", "paddb", + "paddd", "paddsb", "paddsw", "paddusb", "paddusw", "paddw", + "pand", "pandn", "pcmpeqb", "pcmpeqd", "pcmpeqw", "pcmpgtb", + "pcmpgtd", "pcmpgtw", "pmaddwd", "pmulhw", "pmullw", "pop", + "popa", "popad", "popaw", "popf", "popfd", "popfw", "por", + "pslld", "psllq", "psllw", "psrad", "psraw", "psrld", "psrlq", + "psrlw", "psubb", "psubd", "psubsb", "psubsw", "psubusb", + "psubusw", "psubw", "punpckhbw", "punpckhdq", "punpckhwd", + "punpcklbw", "punpckldq", "punpcklwd", "push", "pusha", + "pushad", "pushaw", "pushf", "pushfd", "pushfw", "pxor", "rcl", + "rcr", "rdmsr", "rdpmc", "rdtsc", "resb", "resd", "resq", + "rest", "resw", "ret", "retf", "retn", "rol", "ror", "rsm", + "sahf", "sal", "salc", "sar", "sbb", "scasb", "scasd", "scasw", + "sgdt", "shl", "shld", "shr", "shrd", "sidt", "sldt", "smi", + "smsw", "stc", "std", "sti", "stosb", "stosd", "stosw", "str", + "sub", "test", "umov", "verr", "verw", "wait", "wbinvd", + "wrmsr", "xadd", "xbts", "xchg", "xlatb", "xor" }; static char *icn[] = { /* conditional instructions */ @@ -14,6 +14,7 @@ #include "nasm.h" #include "nasmlib.h" +#include "preproc.h" #include "parser.h" #include "assemble.h" #include "labels.h" @@ -31,6 +32,8 @@ static char inname[FILENAME_MAX]; static char outname[FILENAME_MAX]; static char realout[FILENAME_MAX]; static int lineno; /* for error reporting */ +static int lineinc; /* set by [LINE] or [ONELINE] */ +static int globallineno; /* for forward-reference tracking */ static int pass; static struct ofmt *ofmt = NULL; @@ -44,6 +47,27 @@ static long abs_offset; static struct SAA *forwrefs; /* keep track of forward references */ static int forwline; +static Preproc *preproc; +static int preprocess_only; + +/* used by error function to report location */ +static char currentfile[FILENAME_MAX]; + +/* + * This is a null preprocessor which just copies lines from input + * to output. It's used when someone explicitly requests that NASM + * not preprocess their source file. + */ + +static void no_pp_reset (char *, efunc); +static char *no_pp_getline (void); +static void no_pp_cleanup (void); +static Preproc no_pp = { + no_pp_reset, + no_pp_getline, + no_pp_cleanup +}; + /* * get/set current offset... */ @@ -62,6 +86,9 @@ int main(int argc, char **argv) { offsets = raa_init(); forwrefs = saa_init ((long)sizeof(int)); + preproc = &nasmpp; + preprocess_only = FALSE; + seg_init(); register_output_formats(); @@ -74,28 +101,60 @@ int main(int argc, char **argv) { return 1; } - if (!*outname) { - ofmt->filename (inname, realout, report_error); - strcpy(outname, realout); - } + if (preprocess_only) { + char *line; + + if (*outname) { + ofile = fopen(outname, "w"); + if (!ofile) + report_error (ERR_FATAL | ERR_NOFILE, + "unable to open output file `%s'", outname); + } else + ofile = NULL; + preproc->reset (inname, report_error); + strcpy(currentfile,inname); + lineno = 0; + lineinc = 1; + while ( (line = preproc->getline()) ) { + lineno += lineinc; + if (ofile) { + fputs(line, ofile); + fputc('\n', ofile); + } else + puts(line); + nasm_free (line); + } + preproc->cleanup(); + if (ofile) + fclose(ofile); + if (ofile && terminate_after_phase) + remove(outname); + } else { + if (!*outname) { + ofmt->filename (inname, realout, report_error); + strcpy(outname, realout); + } - ofile = fopen(outname, "wb"); - if (!ofile) { - report_error (ERR_FATAL | ERR_NOFILE, - "unable to open output file `%s'", outname); - } - ofmt->init (ofile, report_error, define_label); - assemble_file (inname); - if (!terminate_after_phase) { - ofmt->cleanup (); - cleanup_labels (); + ofile = fopen(outname, "wb"); + if (!ofile) { + report_error (ERR_FATAL | ERR_NOFILE, + "unable to open output file `%s'", outname); + } + ofmt->init (ofile, report_error, define_label); + assemble_file (inname); + if (!terminate_after_phase) { + ofmt->cleanup (); + cleanup_labels (); + } + fclose (ofile); + if (terminate_after_phase) + remove(outname); } - fclose (ofile); - if (terminate_after_phase) - remove(outname); if (want_usage) usage(); + raa_free (offsets); + saa_free (forwrefs); return 0; } @@ -132,10 +191,14 @@ static void parse_cmdline(int argc, char **argv) { break; case 'h': fprintf(stderr, - "usage: nasm [-o outfile] [-f format] filename\n"); + "usage: nasm [-o outfile] [-f format]" + " [-a] [-e] filename\n"); fprintf(stderr, " or nasm -r for version info\n\n"); fprintf(stderr, + " -e means preprocess only; " + "-a means don't preprocess\n\n"); + fprintf(stderr, "valid output formats for -f are" " (`*' denotes default):\n"); ofmt_list(ofmt); @@ -145,6 +208,12 @@ static void parse_cmdline(int argc, char **argv) { fprintf(stderr, "NASM version %s\n", NASM_VER); exit (0); /* never need usage message here */ break; + case 'e': /* preprocess only */ + preprocess_only = TRUE; + break; + case 'a': /* assemble only - don't preprocess */ + preproc = &no_pp; + break; default: report_error (ERR_NONFATAL | ERR_NOFILE | ERR_USAGE, "unrecognised option `-%c'", @@ -164,66 +233,54 @@ static void parse_cmdline(int argc, char **argv) { "no input file specified"); } -/* used by error function to report location */ -static char currentfile[FILENAME_MAX]; - static void assemble_file (char *fname) { - FILE *fp = fopen (fname, "r"); - FILE *oldfile = NULL; /* jrh - used when processing include files */ - int oldfileline = 0; - char *value, *p, buffer[1024+2]; /* maximum line length defined here */ + char *value, *p, *line; insn output_ins; - int i, seg, rn_error; - - if (!fp) { /* couldn't open file */ - report_error (ERR_FATAL | ERR_NOFILE, - "unable to open input file `%s'", fname); - return; - } + int i, rn_error; + long seg; init_labels (); - strcpy(currentfile,fname); /* pass one */ pass = 1; current_seg = ofmt->section(NULL, pass, &sb); + preproc->reset(fname, report_error); + strcpy(currentfile,fname); lineno = 0; - while (1) { - if (! fgets(buffer, sizeof(buffer), fp)) { /* EOF on current file */ - if (oldfile) { - fclose(fp); - fp = oldfile; - lineno = oldfileline; - strcpy(currentfile,fname); - oldfile = NULL; - continue; - } - else - break; - } - lineno++; - if (buffer[strlen(buffer)-1] == '\n') { - buffer[strlen(buffer)-1] = '\0'; - } else if (!feof(fp)) { + lineinc = 1; + globallineno = 0; + while ( (line = preproc->getline()) ) { + lineno += lineinc; + globallineno++; + + if (line[0] == '%') { + int ln, li; + char buf[FILENAME_MAX]; + /* - * We have a line that's too long. Throw an error, read - * to EOL, and ignore the line for assembly purposes. + * This will be a line number directive. They come + * straight from the preprocessor, so we'll subject + * them to only minimal error checking. */ - report_error (ERR_NONFATAL, "line is longer than %d characters", - sizeof(buffer)-2); - while (fgets(buffer, sizeof(buffer), fp) && - buffer[strlen(buffer)-1] != '\n'); - continue; /* read another line */ + if (strncmp(line, "%line", 5)) { + if (preproc == &no_pp) + report_error (ERR_WARNING, "unknown `%%' directive in " + " preprocessed source"); + } else if (sscanf(line, "%%line %d+%d %s", &ln, &li, buf) != 3) { + report_error (ERR_WARNING, "bogus line number directive in" + " preprocessed source"); + } else { + lineno = ln - li; + lineinc = li; + strncpy (currentfile, buf, FILENAME_MAX-1); + currentfile[FILENAME_MAX-1] = '\0'; + } + continue; } - /* - * Handle spurious ^Z, which may be inserted by some file - * transfer utilities. - */ - buffer[strcspn(buffer, "\032")] = '\0'; /* here we parse our directives; this is not handled by the 'real' * parser. */ - if ( (i = getkw (buffer, &value)) ) { + if ( (i = getkw (line, &value)) ) { switch (i) { case 1: /* [SEGMENT n] */ seg = ofmt->section (value, pass, &sb); @@ -254,27 +311,12 @@ static void assemble_file (char *fname) { break; } break; - case 4: /* [INC file] */ - oldfile = fp; - oldfileline = lineno; - lineno = 0; - strcpy(currentfile,value); - fp = fopen(value,"r"); - if (!fp) { - lineno = oldfileline; - fp = oldfile; - strcpy(currentfile,fname); - report_error (ERR_FATAL, - "unable to open include file `%s'\n", - value); - } - break; - case 5: /* [GLOBAL symbol] */ + case 4: /* [GLOBAL symbol] */ if (*value == '$') value++; /* skip initial $ if present */ declare_as_global (value, report_error); break; - case 6: /* [COMMON symbol size] */ + case 5: /* [COMMON symbol size] */ p = value; while (*p && !isspace(*p)) p++; @@ -294,7 +336,7 @@ static void assemble_file (char *fname) { report_error (ERR_NONFATAL, "no size specified in" " COMMON declaration"); break; - case 7: /* [ABSOLUTE address] */ + case 6: /* [ABSOLUTE address] */ current_seg = NO_SEG; abs_offset = readnum(value, &rn_error); if (rn_error) { @@ -304,17 +346,36 @@ static void assemble_file (char *fname) { } break; default: - if (!ofmt->directive (buffer+1, value, 1)) + if (!ofmt->directive (line+1, value, 1)) report_error (ERR_NONFATAL, "unrecognised directive [%s]", - buffer+1); + line+1); break; } } else { long offs = get_curr_ofs; parse_line (current_seg, offs, lookup_label, - 1, buffer, &output_ins, ofmt, report_error); + 1, line, &output_ins, ofmt, report_error); if (output_ins.forw_ref) - *(int *)saa_wstruct(forwrefs) = lineno; + *(int *)saa_wstruct(forwrefs) = globallineno; + + /* + * Hack to prevent phase error in the code + * rol ax,x + * x equ 1 + * + * We rule that the presence of a forward reference + * cancels out the UNITY property of the number 1. This + * isn't _strictly_ necessary in pass one, since the + * problem occurs in pass two, but for the sake of + * having the passes as near to identical as we can + * manage, we do it like this. + */ + if (output_ins.forw_ref) { + int i; + for (i=0; i<output_ins.operands; i++) + output_ins.oprs[i].type &= ~ONENESS; + } + if (output_ins.opcode == I_EQU) { /* * Special `..' EQUs get processed in pass two. @@ -354,7 +415,9 @@ static void assemble_file (char *fname) { } cleanup_insn (&output_ins); } + nasm_free (line); } + preproc->cleanup(); if (terminate_after_phase) { fclose(ofile); @@ -366,7 +429,6 @@ static void assemble_file (char *fname) { /* pass two */ pass = 2; - rewind (fp); saa_rewind (forwrefs); { int *p = saa_rstruct (forwrefs); @@ -378,35 +440,38 @@ static void assemble_file (char *fname) { current_seg = ofmt->section(NULL, pass, &sb); raa_free (offsets); offsets = raa_init(); + preproc->reset(fname, report_error); + strcpy(currentfile,fname); lineno = 0; - while (1) { - if (!fgets(buffer, sizeof(buffer), fp)) { - if (oldfile) { - fclose(fp); - fp = oldfile; - lineno = oldfileline; - strcpy(currentfile,fname); - oldfile = NULL; - continue; - } else - break; - } - lineno++; - if (buffer[strlen(buffer)-1] == '\n') - buffer[strlen(buffer)-1] = '\0'; - else if (!feof(fp)) - report_error (ERR_PANIC, - "too-long line got through from pass one"); - /* - * Handle spurious ^Z, which may be inserted by some file - * transfer utilities. - */ - buffer[strcspn(buffer, "\032")] = '\0'; + lineinc = 1; + globallineno = 0; + while ( (line = preproc->getline()) ) { + lineno += lineinc; + globallineno++; + + if (line[0] == '%') { + int ln, li; + char buf[FILENAME_MAX]; + + /* + * This will be a line number directive. They come + * straight from the preprocessor, so we'll subject + * them to only minimal error checking. + */ + if (!strncmp(line, "%line", 5) && + sscanf(line, "%%line %d+%d %s", &ln, &li, buf) == 3) { + lineno = ln - li; + lineinc = li; + strncpy (currentfile, buf, FILENAME_MAX-1); + currentfile[FILENAME_MAX-1] = '\0'; + } + continue; + } /* here we parse our directives; this is not handled by * the 'real' parser. */ - if ( (i = getkw (buffer, &value)) ) { + if ( (i = getkw (line, &value)) ) { switch (i) { case 1: /* [SEGMENT n] */ seg = ofmt->section (value, pass, &sb); @@ -431,34 +496,11 @@ static void assemble_file (char *fname) { break; } break; - case 4: - oldfile = fp; - oldfileline = lineno; - lineno = 0; - strcpy(currentfile,value); - fp = fopen(value,"r"); - if (!fp) { - lineno = oldfileline; - fp = oldfile; - strcpy(currentfile,fname); - /* - * We don't report this error in the PANIC - * class, even though we might expect to have - * already picked it up during pass one, - * because of the tiny chance that some other - * process may have removed the include file - * between the passes. - */ - report_error (ERR_FATAL, - "unable to open include file `%s'\n", - value); - } - break; - case 5: /* [GLOBAL symbol] */ + case 4: /* [GLOBAL symbol] */ break; - case 6: /* [COMMON symbol size] */ + case 5: /* [COMMON symbol size] */ break; - case 7: /* [ABSOLUTE addr] */ + case 6: /* [ABSOLUTE addr] */ current_seg = NO_SEG; abs_offset = readnum(value, &rn_error); if (rn_error) @@ -466,15 +508,15 @@ static void assemble_file (char *fname) { "in pass two"); break; default: - if (!ofmt->directive (buffer+1, value, 2)) + if (!ofmt->directive (line+1, value, 2)) report_error (ERR_PANIC, "invalid directive on pass two"); break; } } else { long offs = get_curr_ofs; parse_line (current_seg, offs, lookup_label, 2, - buffer, &output_ins, ofmt, report_error); - if (lineno == forwline) { + line, &output_ins, ofmt, report_error); + if (globallineno == forwline) { int *p = saa_rstruct (forwrefs); if (p) forwline = *p; @@ -483,7 +525,19 @@ static void assemble_file (char *fname) { output_ins.forw_ref = TRUE; } else output_ins.forw_ref = FALSE; - obuf = buffer; + + /* + * Hack to prevent phase error in the code + * rol ax,x + * x equ 1 + */ + if (output_ins.forw_ref) { + int i; + for (i=0; i<output_ins.operands; i++) + output_ins.oprs[i].type &= ~ONENESS; + } + + obuf = line; if (output_ins.label) define_label_stub (output_ins.label, report_error); if (output_ins.opcode == I_EQU) { @@ -517,7 +571,9 @@ static void assemble_file (char *fname) { cleanup_insn (&output_ins); set_curr_ofs (offs); } + nasm_free (line); } + preproc->cleanup(); } static int getkw (char *buf, char **value) { @@ -557,14 +613,12 @@ static int getkw (char *buf, char **value) { return 2; if (!strcmp(p, "bits")) return 3; - if (!strcmp(p, "inc") || !strcmp(p, "include")) - return 4; if (!strcmp(p, "global")) - return 5; + return 4; if (!strcmp(p, "common")) - return 6; + return 5; if (!strcmp(p, "absolute")) - return 7; + return 6; return -1; } @@ -574,7 +628,8 @@ static void report_error (int severity, char *fmt, ...) { if (severity & ERR_NOFILE) fputs ("nasm: ", stderr); else - fprintf (stderr, "%s:%d: ", currentfile, lineno); + fprintf (stderr, "%s:%d: ", currentfile, + lineno + (severity & ERR_OFFBY1 ? lineinc : 0)); if ( (severity & ERR_MASK) == ERR_WARNING) fputs ("warning: ", stderr); @@ -596,14 +651,16 @@ static void report_error (int severity, char *fmt, ...) { terminate_after_phase = TRUE; break; case ERR_FATAL: - fclose(ofile); - remove(outname); + if (ofile) { + fclose(ofile); + remove(outname); + } if (want_usage) usage(); exit(1); /* instantly die */ break; /* placate silly compilers */ case ERR_PANIC: - abort(); /* panic and dump core */ + abort(); /* halt, catch fire, and dump core */ break; } } @@ -677,3 +734,61 @@ static void register_output_formats(void) { */ ofmt = &OF_DEFAULT; } + +#define BUF_DELTA 512 + +static FILE *no_pp_fp; +static efunc no_pp_err; + +static void no_pp_reset (char *file, efunc error) { + no_pp_err = error; + no_pp_fp = fopen(file, "r"); + if (!no_pp_fp) + no_pp_err (ERR_FATAL | ERR_NOFILE, + "unable to open input file `%s'", file); +} + +static char *no_pp_getline (void) { + char *buffer, *p, *q; + int bufsize; + + bufsize = BUF_DELTA; + buffer = nasm_malloc(BUF_DELTA); + p = buffer; + while (1) { + q = fgets(p, bufsize-(p-buffer), no_pp_fp); + if (!q) + break; + p += strlen(p); + if (p > buffer && p[-1] == '\n') + break; + if (p-buffer > bufsize-10) { + bufsize += BUF_DELTA; + buffer = nasm_realloc(buffer, bufsize); + } + } + + if (!q && p == buffer) { + nasm_free (buffer); + return NULL; + } + + /* + * Play safe: remove CRs as well as LFs, if any of either are + * present at the end of the line. + */ + while (p > buffer && (p[-1] == '\n' || p[-1] == '\r')) + *--p = '\0'; + + /* + * Handle spurious ^Z, which may be inserted into source files + * by some file transfer utilities. + */ + buffer[strcspn(buffer, "\032")] = '\0'; + + return buffer; +} + +static void no_pp_cleanup (void) { + fclose(no_pp_fp); +} @@ -33,9 +33,11 @@ that maybe someone ought to write one. So here, for your coding pleasure, is NASM. At present it's still in prototype stage - we don't promise that it can outperform any of -these assemblers. But please, _please_ send us bug reports and fixes -and anything else you can get your hands on, and we'll improve it -out of all recognition. Again. +these assemblers. But please, _please_ send us bug reports, fixes, +helpful information, and anything else you can get your hands on +(and thanks to the many people who've done this already! You all +know who you are), and we'll improve it out of all recognition. +Again. Please see the file `Licence' for the legalese. @@ -198,27 +200,37 @@ Pseudo-Opcodes Pseudo-opcodes are not real x86 machine opcodes, but are used in the instruction field anyway because that's the most convenient place to -put them. The current pseudo-opcodes are DB, DW and DD, their -uninitialised counterparts RESB, RESW and RESD, the EQU command, and -the TIMES prefix. +put them. The current pseudo-opcodes are DB, DW, DD, DQ and DT, +their uninitialised counterparts RESB, RESW, RESD, RESQ and REST, +the INCBIN command, the EQU command, and the TIMES prefix. -DB, DW and DD work as you would expect: they can each take an -arbitrary number of operands, and when assembled, they generate +DB, DW, DD, DQ and DT work as you would expect: they can each take +an arbitrary number of operands, and when assembled, they generate nothing but those operands. All three of them can take string -constants as operands, which no other instruction can currently do. -See the `Constants' section for details about string constants. +constants as operands. See the `Constants' section for details about +string constants. -RESB, RESW and RESD are designed to be used in the BSS section of a -module: they declare _uninitialised_ storage space. Each takes a -single operand, which is the number of bytes, words or doublewords -to reserve. We do not support the MASM/TASM syntax of reserving -uninitialised space by writing `DW ?' or similar: this is what we do -instead. (But see `Critical Expressions' for a caveat on the nature -of the operand.) +RESB, RESW, RESD, RESQ and REST are designed to be used in the BSS +section of a module: they declare _uninitialised_ storage space. +Each takes a single operand, which is the number of bytes, words or +doublewords to reserve. We do not support the MASM/TASM syntax of +reserving uninitialised space by writing `DW ?' or similar: this is +what we do instead. (But see `Critical Expressions' for a caveat on +the nature of the operand.) (An aside: if you want to be able to write `DW ?' and have something vaguely useful happen, you can always code `? EQU 0'...) +INCBIN is borrowed from the old Amiga assembler Devpac: it includes +a binary file verbatim into the output file. This can be handy for +(for example) including graphics and sound data directly into a game +executable file. It can be called in one of these three ways: + + INCBIN "file.dat" ; include the whole file + INCBIN "file.dat",1024 ; skip the first 1024 bytes + INCBIN "file.dat",1024,512 ; skip the first 1024, and + ; actually include at most 512 + EQU defines a symbol to a specified value: when EQU is used, the LABEL field must be present. The action of EQU is to define the given label name to the value of its (only) operand. This definition @@ -676,15 +688,22 @@ Assembler Directives ==================== Assembler directives appear on a line by themselves (apart from a -comment), and must be enclosed in square brackets. No white space -may appear before the opening square bracket, although white space -and a comment may come after the closing bracket. +comment). They come in two forms: user-level directives and +primitive directives. Primitive directives are enclosed in square +brackets (no white space may appear before the opening square +bracket, although white space and a comment may come after the +closing bracket), and were the only form of directive supported by +earlier versions of NASM. User-level directives look the same, only +without the square brackets, and are the more modern form. (They are +implemented as macros expanding to primitive directives.) There is a +distinction in functionality, which is explained below in the +section on structures. Some directives are universal: they may be used in any situation, and do not change their syntax. The universal directives are listed below. -[BITS 16] or [BITS 32] switches NASM into 16-bit or 32-bit mode. +`BITS 16' or `BITS 32' switches NASM into 16-bit or 32-bit mode. (This is equivalent to USE16 and USE32 segments, in TASM or MASM.) In 32-bit mode, instructions are prefixed with 0x66 or 0x67 prefixes when they use 16-bit data or addresses; in 16-bit mode, the reverse @@ -692,20 +711,16 @@ happens. NASM's default depends on the object format; the defaults are documented with the formats. (See `obj', in particular, for some unusual behaviour.) -[INCLUDE filename] or [INC filename] includes another source file -into the current one. At present, only one level of inclusion is -supported. - -[SECTION name] or [SEGMENT name] changes which section the code you +`SECTION name' or `SEGMENT name' changes which section the code you write will be assembled into. Acceptable section names vary between output formats, but most formats (indeed, all formats at the moment) support the names `.text', `.data' and `.bss'. Note that `.bss' is an uninitialised data section, and so you will receive a warning from NASM if you try to assemble any code or data in it. The only -thing you can do in `.bss' without triggering a warning is use RESB, -RESW and RESD. That's what they're for. +thing you can do in `.bss' without triggering a warning is to use +RESB, RESW and RESD. That's what they're for. -[ABSOLUTE address] can be considered a different form of [SECTION], +`ABSOLUTE address' can be considered a different form of `SECTION', in that it must be overridden using a SECTION directive once you have finished using it. It is used to assemble notional code at an absolute offset address; of course, you can't actually assemble @@ -714,39 +729,432 @@ code in place, but you can use RESB, RESW and RESD, and you can define labels. Hence you could, for example, define a C-like data structure by means of - [ABSOLUTE 0] + absolute 0 stLong resd 1 stWord resw 1 stByte1 resb 1 stByte2 resb 1 st_size: - [SEGMENT .text] + segment .text and then carry on coding. This defines `stLong' to be zero, `stWord' to be 4, `stByte1' to be 6, `stByte2' to be 7 and `st_size' to be 8. -So this has defined a data structure. +So this has defined a data structure. The STRUC directive provides a +nicer way to do this: see below. -[EXTERN symbol] defines a symbol as being `external', in the C +`EXTERN symbol' defines a symbol as being `external', in the C sense: `EXTERN' states that the symbol is _not_ declared in this module, but is declared elsewhere, and that you wish to _reference_ it in this module. -[GLOBAL symbol] defines a symbol as being global, in the sense that +`GLOBAL symbol' defines a symbol as being global, in the sense that it is exported from this module and other modules may reference it. All symbols are local, unless declared as global. Note that the `GLOBAL' directive must appear before the definition of the symbol it refers to. -[COMMON symbol size] defines a symbol as being common: it is +`COMMON symbol size' defines a symbol as being common: it is declared to have the given size, and it is merged at link time with any declarations of the same symbol in other modules. This is not _fully_ supported in the `obj' file format: see the section on `obj' for details. +`STRUC structure' begins the definition of a data structure, and +`ENDSTRUC' ends it. The structure shown above may be defined, +exactly equivalently, using STRUC as follows: + + struc st + stLong resd 1 + stWord resw 1 + stByte1 resb 1 + stByte2 resb 1 + endstruc + +Notice that this code still defines the symbol `st_size' to be the +size of the structure. The `_size' suffix is automatically appended +to the structure name. Notice also that the assembler takes care of +remembering which section you were assembling in (whereas in the +version using `ABSOLUTE' it was up to the programmer to sort that +out). + +This is where user-level directives differ from primitives: the +`SECTION' (and `SEGMENT') user-level directives don't just call the +primitive versions, but they also `%define' the special preprocessor +symbol `__SECT__' to be the primitive directive that specifies the +current section. So the `ENDSTRUC' directive can remember what +section the assembly was directed to before the structure definition +began. For this reason, there is no primitive version of STRUC or +ENDSTRUC - they are implemented in terms of ABSOLUTE and SECTION. +This also means that if you use STRUC before explicitly announcing a +target section, you should explicitly announce one after ENDSTRUC. + +The primitive directive [INCLUDE filename] (or the equivalent form +[INC filename]) is supported as a synonym for the preprocessor- +oriented `%include' form, but only temporarily: this usage will be +phased out in the next version of NASM. + Directives may also be specific to the output file format. At present, the `bin' and `obj' formats define extra directives, which are specified below. +The Preprocessor +================ + +NASM contains a full-featured macro preprocessor, which supports +conditional assembly, multi-level file inclusion, two forms of macro +(single-line and multi-line), and a `context stack' mechanism for +extra macro power. Preprocessor directives all begin with a `%' +sign. + +Single-line macros +------------------ + +Single-line macros are defined in a similar way to C, using the +`%define' command. Hence you can do: + + %define ctrl 0x1F & + %define param(a,b) ((a)+(a)*(b)) + mov byte [param(2,ebx)], ctrl 'D' + +which will expand to + + mov byte [(2)+(2)*(ebx)], 0x1F & 'D' + +When the expansion of a single-line macro contains tokens which +invoke another macro, the expansion is performed at invocation time, +not at definition time. Thus the code + + %define a(x) 1+b(x) + %define b(x) 2*x + mov ax,a(8) + +will evaluate in the expected way to `mov ax,1+2*8', even though the +macro `b' wasn't defined at the time of definition of `a'. + +Macros defined with `%define' are case sensitive: after `%define foo +bar', only `foo' will expand to bar: `Foo' or `FOO' will not. By +using `%idefine' instead of `%define' (the `i' stands for +`insensitive') you can define all the case variants of a macro at +once, so that `%idefine foo bar' would cause `foo', `Foo' and `FOO' +all to expand to `bar'. + +There is a mechanism which detects when a macro call has occurred as +a result of a previous expansion of the same macro, to guard against +circular references and infinite loops. If this happens, the +preprocessor will report an error. + +Single-line macros with parameters can be overloaded: it is possible +to define two or more single-line macros with the same name, each +taking a different number of parameters, and the macro processor +will be able to distinguish between them. However, a parameterless +single-line macro excludes the possibility of any macro of the same +name _with_ parameters, and vice versa (though single-line macros +may be redefined, keeping the same number of parameters, without +error). + +Multiple-line macros +-------------------- + +These are defined using `%macro' and `%endmacro', so that simple things +like this can be done: + + %macro prologue 0 + push ebp + mov ebp,esp + %endmacro + +This defines `prologue' to be a multi-line macro, taking no +parameters, which expands to the two lines of code given. + +Similarly to single-line macros, multi-line macros are case- +sensitive, unless you define them using `%imacro' instead of +`%macro'. + +The `0' on the `%macro' line indicates that the macro `prologue' +expects no parameters. Macros can be overloaded: if two macros are +defined with the same name but different numbers of parameters, they +will be treated as separate. Multi-line macros may not be redefined. + +Macros taking parameters can be written using `%1', `%2' and so on +to reference the parameters. So this code + + %macro movs 2 + push %2 + pop %1 + %endmacro + movs ds,cs + +will define a macro `movs' to perform an effective MOV operation +from segment to segment register. The macro call given would of +course expand to `push cs' followed by `pop ds'. + +You can define a label inside a macro in such a way as to make it +unique to that macro call (so that repeated calls to the same macro +won't produce multiple labels with the same name), by prefixing it +with `%%'. So: + + %macro retz + jnz %%skip + ret + %%skip: + %endmacro + +This defines a different label in place of `%%skip' every time it's +called. (Of course the above code could have easily been coded using +`jnz $+3', but not in more complex cases...) The actual label +defined would be `macro.2345.skip', where 2345 is replaced by some +number that changes with each macro call. Users are warned to avoid +defining labels of this shape themselves. + +Sometimes you want a macro to be able to accept arbitrarily many +parameters and lump them into one. This can be done using the `+' +modifier on the `%macro' line: + + %macro fputs 2+ + [section .data] ; this is done as a primitive to avoid + ; disturbing the __SECT__ define + %%str db %2 + %%end: + __SECT__ ; this expands to a whole [section xxx] primitive + mov dx,%%str + mov cx,%%end-%%str + mov bx,%1 + call writefile + %endmacro + fputs [filehandle], "hi there", 13, 10 + +This declares `pstring' to be a macro that accepts _at least two_ +parameters, and all parameters after the first one are lumped +together as part of the last specified one (in this case %2). So in +the macro call, `%1' expands to `[filehandle]' while `%2' expands to +the whole remainder of the line: `"hi there", 13, 10'. Note also the +switching of sections in the middle of this macro expansion, to +ensure separation of data and code. + +There is an alternative mechanism for putting commas in macro +parameters: instead of specifying the large-parameter-ness at macro +definition time, you can specify it at macro call time, by the use +of braces to surround a parameter which you want to contain commas. +So: + + %macro table_entry 2 + %%start: + db %1 + times 32-($-%%start) db 0 + db %2 + times 64-($-%%start) db 0 + %endmacro + table_entry 'foo','bar' + table_entry 'megafoo', { 27,'[1mBAR!',27,'[m' } + +will expand to, effectively (actually, there will be labels present, +but these have been omitted for clarity), the following: + + db 'foo' + times 32-3 db 0 + db 'bar' + times 64-35 db 0 + db 'megafoo' + times 32-7 db 0 + db 27,'[1mBAR!',27,'[m' + times 64-46 db 0 + +Macro parameter expansions can be concatenated on to other tokens, +so that you can do this: + + %macro keytab_entry 2 + keypos%1 equ $-keytab + db %2 + %endmacro + keytab: + keytab_entry F1,128+1 + keytab_entry F2,128+2 + keytab_entry Return,13 + +which will define labels called `keyposF1', `keyposF2' and +`keyposReturn'. You can similarly do concatenations on the other +end, such as `%1foo'. If you need to concatenate a digit on to the +end of a macro parameter expansion, you can do this by enclosing the +parameter number in braces: `%{1}' is always a valid synonym for +`%1', and has the advantage that it can be legitimately prepended to +a digit, as in `%{1}2', and cause no confusion with `%{12}'. +Macro-specific labels and defines can be concatenated similarly: +`%{%foo}bar' will succeed where `%%foobar' would cause confusion. +(As it happens, `%%foobar' would work anyway, due to the format of +macro-specific labels, but for clarity, `%{%foo}bar' is recommended +if you _really_ want to do anything this perverse...) + +The parameter handling has a special case: it can treat a macro +parameter specially if it's thought to contain a condition code. The +reference `%+1' is identical to `%1' except that it will perform an +initial sanity check to see if the parameter in question is a +condition code; more usefully, the reference `%-1' will produce the +_opposite_ condition code to the one specified in the parameter. +This allows for things such as a conditional-MOV macro to be +defined: + + %macro movc 3 + j%-1 %%skip + mov %2,%3 + %%skip: + %endmacro + movc ae,ax,bx + +which will expand to something like + + jnae macro.1234.skip + mov ax,bx + macro.1234.skip: + +Note that `%+1' will allow CXZ or ECXZ to be passed as condition +codes, but `%-1' will of course be unable to invert them. + +Parameters can also be defaulted: you can define a macro which, for +example, said + + %macro strange 1-3 bx,3 + < some expansion text > + %endmacro + +This macro takes between 1 and 3 parameters (inclusive); if +parameter 2 is not specified it defaults to BX, and if parameter 3 +is not specified it defaults to 3. So the calls + + strange dx,si,di + strange dx,si + strange dx + +would be equivalent to + + strange dx,si,di + strange dx,si,3 + strange dx,bx,3 + +Defaults may be omitted, in which case they are taken to be blank. + +`%endm' is a valid synonym for `%endmacro'. + +Conditional Assembly +-------------------- + +Similarly to the C preprocessor, the commands `%ifdef' and `%endif' +may be used to bracket a section of code, which will then only be +assembled if at least one of the identifiers following `%ifdef' is +defined as a single-line macro. The command `%ifndef' has opposite +sense to `%ifdef', and `%else' can be placed between the `%if' and +the `%endif' to work as expected. Since there is no analogue to C's +`#if', there is no precise `elif' directive, but `%elifdef' and +`%elifndef' work as expected. + +There is another family of `%if' constructs: `%ifctx', `%ifnctx', +`%elifctx' and `%elifnctx', which operate on the context stack +(described below). + +File Inclusion +-------------- + +You can include a file using the `%include' directive. Included +files are only searched for in the current directory: there isn't +(yet - if there's demand for it it could be arranged) any default +search path for standard include files. + +This, again, works like C: `%include' is used to include a file. Of +course it's quite likely you'd want to do the normal sort of thing +inside the file: + + %ifndef MY_MACROS_FILE + %define MY_MACROS_FILE + < go and define some macros > + %endif + +and then elsewhere + + %include "my-macros-file" + < some code making use of the macros > + +so that it doesn't matter if the file accidentally gets included +more than once. + +The Context Stack +----------------- + +This is a feature which adds a whole extra level of power to NASM's +macro capability. The context stack is an internal object within the +preprocessor, which holds a stack of `contexts'. Each context has a +name - just an identifier-type token - and can also have labels and +`%define' macros associated with it. Other macros can manipulate the +context stack: this is where the power comes in. + +To start with: the preprocessor command `%push' will create a new +context with the given name, and push it on to the top of the stack. +`%pop', taking no arguments, pops the top context off the stack and +destroys it. `%repl' renames the top context without destroying any +associated labels or macros, so it's distinct from doing `%pop' +followed by `%push'. Finally, `%ifctx' and `%ifnctx' invoke +conditional assembly based on the name of the top context. (The +alternative forms `%elifctx' and `%elifnctx' are also available.) + +As well as the `%%foo' syntax to define labels specific to a macro +call, there is also the syntax `%$foo' to define a label specific to +the context currently on top of the stack. `%$$foo' can be used to +refer to the context below that, or `%$$$foo' below that, and so on. + +This lot allows the definition of macro combinations that enclose +other code, such as the following big example: + + %macro if 1 + %push if + j%-1 %$ifnot + %endmacro + %macro else 0 + %ifctx if + %repl else + jmp %$ifend + %$ifnot: + %else + %error "expected `if' before `else'" + %endif + %endmacro + %macro endif 0 + %ifctx if + %$ifnot: + %pop + %elifctx else + %$ifend: + %pop + %else + %error "expected `if' or `else' before `endif'" + %endif + %endmacro + +This will cope with a large `if/endif' construct _or_ an +`if/else/endif', without flinching. So you can code: + + cmp ax,bx + if ae + cmp bx,cx + if ae + mov ax,cx + else + mov ax,bx + endif + else + cmp ax,cx + if ae + mov ax,cx + endif + endif + +which will place the smallest out of AX, BX and CX into AX. Note the +use of `%repl' to change the current context from `if' to `else' +without disturbing the associated labels `%$ifend' and `%$ifnot'; +also note that the stack mechanism allows handling of nested IF +statements without a hitch, and that conditional assembly is used in +the `endif' macro in order to cope with the two possible forms with +and without an `else'. Note also the directive `%error', which +allows the user to report errors on improper invocation of a macro +and so can catch unmatched `endif's at preprocess time. + Output Formats ============== @@ -765,9 +1173,9 @@ before they become executable. the binary representation of the exact code you wrote. The `bin' format supports a format-specific directive, which is ORG. -[ORG addr] declares that your code should be assembled as if it were +`ORG addr' declares that your code should be assembled as if it were to be loaded into memory at the address `addr'. So a DOS .COM file -should state [ORG 0x100], and a DOS .SYS file should state [ORG 0]. +should state `ORG 0x100', and a DOS .SYS file should state `ORG 0'. There should be _one_ ORG directive, at most, in an assembly file: NASM does not support the use of ORG to jump around inside an object file, like MASM does (see the `Bugs' section for a demonstration of @@ -782,8 +1190,8 @@ past the end of the actual file. The `.data' and `.bss' sections are considered to be aligned on four-byte boundaries: this is achieved by inserting padding zero bytes between the end of the text section and the start of the data, if there is data present. Of course if no -[SECTION] directives are present, everything will go into `.text', -and you will get nothing in the output except the code you wrote. +SECTION directives are present, everything will go into `.text', and +you will get nothing in the output except the code you wrote. `bin' silently ignores GLOBAL directives, and will also not complain at EXTERN ones. You only get an error if you actually _reference_ an @@ -804,12 +1212,12 @@ format-specific directives, and their default output filename is `filename.o'. `aout' defines the three standard sections `.text', `.data' and -`.bss'. `elf' defines these three, but can also support user-defined -section names, which can be declared along with section attributes -like this: +`.bss'. `elf' also, defines these three, but in addition it can +support user-defined section names, which can be declared along with +section attributes like this: -[section foo align=32 exec] -[section bar write nobits] + section foo align=32 exec + section bar write nobits The available options are: @@ -835,9 +1243,9 @@ The available options are: The attributes of the default sections `.text', `.data' and `.bss' can also be redefined from their defaults. The NASM defaults are: -[section .text align=16 alloc exec nowrite progbits] -[section .data align=4 alloc write noexec progbits] -[section .bss align=4 alloc write noexec nobits] +section .text align=16 alloc exec nowrite progbits +section .data align=4 alloc write noexec progbits +section .bss align=4 alloc write noexec nobits ELF is a much more featureful object-file format than a.out: in particular it has enough features to support the writing of position @@ -905,12 +1313,12 @@ the type of section. Win32 also allows `info', which is an informational section type used by Microsoft C compilers to store linker directives. So you can do: -[section .mysect code] ; defines an extra code section + section .mysect code ; defines an extra code section or maybe, in Win32, -[section .drectve info] ; defines an MS-compatible directive section - db '-defaultlib:LIBC -defaultlib:OLDNAMES ' + section .drectve info ; defines an MS-compatible directive section + db '-defaultlib:LIBC -defaultlib:OLDNAMES ' to pass directives to the MS linker. @@ -928,7 +1336,7 @@ extensions are supported. you like. Unlike the other formats, too, segment names are actually defined as symbols, so you can write -[SEGMENT CODE] + segment CODE mov ax,CODE and get the _segment_ address of the segment, suitable for loading @@ -936,7 +1344,7 @@ into a segment register. Segments can be declared with attributes: -[SEGMENT CODE PRIVATE ALIGN=16 CLASS=CODE OVERLAY=OVL2 USE16] + SEGMENT CODE PRIVATE ALIGN=16 CLASS=CODE OVERLAY=OVL2 USE16 You can specify segments to be PRIVATE, PUBLIC, COMMON or STACK; their alignment may be any power of two from 1 to 256 (although only @@ -949,12 +1357,12 @@ alignment, USE16. You can also specify that a segment is _absolute_ at a certain segment address: -[SEGMENT SCREEN ABSOLUTE=0xB800] + SEGMENT SCREEN ABSOLUTE=0xB800 The ABSOLUTE and ALIGN keywords are mutually exclusive. -The format-specific directive GROUP allows segment grouping: [GROUP -DGROUP DATA BSS] defines the group DGROUP to contain segments DATA +The format-specific directive GROUP allows segment grouping: `GROUP +DGROUP DATA BSS' defines the group DGROUP to contain segments DATA and BSS. Segments are defined as part of their group by default: if variable @@ -971,13 +1379,13 @@ and unlike TASM), but will generate a warning (unlike A86!). References to the symbols in that segment will be resolved relative to the _first_ group it is defined in. -The directive [UPPERCASE] causes all symbol, segment and group names +The directive `UPPERCASE' causes all symbol, segment and group names output to the object file to be uppercased. The actual _assembly_ is still case sensitive. To avoid getting tangled up in NASM's local label mechanism, segment and group names have leading periods stripped when they are defined. -Thus, the directive [SEGMENT .text] will define a segment called +Thus, the directive `SEGMENT .text' will define a segment called `text', which will clash with any other symbol called `text', and you will _not_ be able to reference the segment base as `.text', but only as `text'. @@ -1001,13 +1409,12 @@ of course the normal caveats about EQU dependency still apply). `obj' has an unusual handling of assembly modes: instead of having a global default for the whole file, there is a separate default for -each segment. Thus, each [SEGMENT] directive carries an implicit -[BITS] directive with it, which switches to 16-bit or 32-bit mode -depending on whether the segment is a Use16 or Use32 segment. If you -want to place 32-bit code in a Use16 segment, you can use an -explicit [BITS 32] override, but if you switch temporarily away from -that segment, you will have to repeat the override after coming back -to it. +each segment. Thus, each SEGMENT directive carries an implicit BITS +directive with it, which switches to 16-bit or 32-bit mode depending +on whether the segment is a Use16 or Use32 segment. If you want to +place 32-bit code in a Use16 segment, you can use an explicit `BITS +32' override, but if you switch temporarily away from that segment, +you will have to repeat the override after coming back to it. `as86': Linux as86 (bin86-0.3) ------------------------------ @@ -8,12 +8,12 @@ * initial version: 27/iii/95 by Simon Tatham */ -#ifndef NASM_H -#define NASM_H +#ifndef NASM_NASM_H +#define NASM_NASM_H #define NASM_MAJOR_VER 0 -#define NASM_MINOR_VER 93 -#define NASM_VER "0.93" +#define NASM_MINOR_VER 94 +#define NASM_VER "0.94" #ifndef NULL #define NULL 0 @@ -63,6 +63,9 @@ typedef void (*efunc) (int severity, char *fmt, ...); #define ERR_MASK 0x0F /* mask off the above codes */ #define ERR_NOFILE 0x10 /* don't give source file name/line */ #define ERR_USAGE 0x20 /* print a usage message */ +#define ERR_OFFBY1 0x40 /* report error as being on the line + * we're just _about_ to read, not + * the one we've just read */ /* * ----------------------- @@ -82,6 +85,55 @@ typedef void (*ldfunc) (char *label, long segment, long offset, struct ofmt *ofmt, efunc error); /* + * Preprocessors ought to look like this: + */ +typedef struct { + /* + * Called at the start of a pass; given a file name and an + * error reporting function. + */ + void (*reset) (char *, efunc); + + /* + * Called to fetch a line of preprocessed source. The line + * returned has been malloc'ed, and so should be freed after + * use. + */ + char *(*getline) (void); + + /* + * Called at the end of a pass. + */ + void (*cleanup) (void); +} Preproc; + +/* + * ---------------------------------------------------------------- + * Some lexical properties of the NASM source language, included + * here because they are shared between the parser and preprocessor + * ---------------------------------------------------------------- + */ + +/* isidstart matches any character that may start an identifier, and isidchar + * matches any character that may appear at places other than the start of an + * identifier. E.g. a period may only appear at the start of an identifier + * (for local labels), whereas a number may appear anywhere *but* at the + * start. */ + +#define isidstart(c) ( isalpha(c) || (c)=='_' || (c)=='.' || (c)=='?' ) +#define isidchar(c) ( isidstart(c) || isdigit(c) || (c)=='$' || (c)=='#' \ + || (c)=='@' || (c)=='~' ) + +/* Ditto for numeric constants. */ + +#define isnumstart(c) ( isdigit(c) || (c)=='$' ) +#define isnumchar(c) ( isalnum(c) ) + +/* This returns the numeric value of a given 'digit'. */ + +#define numvalue(c) ((c)>='a' ? (c)-'a'+10 : (c)>='A' ? (c)-'A'+10 : (c)-'0') + +/* * ----------------------------------------------------------- * Format of the `insn' structure returned from `parser.c' and * passed into `assemble.c' @@ -152,6 +204,7 @@ typedef void (*ldfunc) (char *label, long segment, long offset, #define MEM_OFFS 0x00604000L /* simple [address] offset */ /* special type of immediate operand */ +#define ONENESS 0x00800000L /* so UNITY == IMMEDIATE | ONENESS */ #define UNITY 0x00802000L /* for shift/rotate instructions */ /* @@ -173,47 +226,49 @@ enum { /* instruction names */ I_AAA, I_AAD, I_AAM, I_AAS, I_ADC, I_ADD, I_AND, I_ARPL, I_BOUND, I_BSF, I_BSR, I_BSWAP, I_BT, I_BTC, I_BTR, I_BTS, I_CALL, I_CBW, I_CDQ, I_CLC, I_CLD, I_CLI, I_CLTS, I_CMC, I_CMP, - I_CMPSB, I_CMPSD, I_CMPSW, I_CMPXCHG, I_CMPXCHG8B, I_CPUID, - I_CWD, I_CWDE, I_DAA, I_DAS, I_DB, I_DD, I_DEC, I_DIV, I_DQ, - I_DT, I_DW, I_EMMS, I_ENTER, I_EQU, I_F2XM1, I_FABS, I_FADD, - I_FADDP, I_FBLD, I_FBSTP, I_FCHS, I_FCLEX, I_FCMOVB, I_FCMOVBE, - I_FCMOVE, I_FCMOVNB, I_FCMOVNBE, I_FCMOVNE, I_FCMOVNU, I_FCMOVU, - I_FCOM, I_FCOMI, I_FCOMIP, I_FCOMP, I_FCOMPP, I_FCOS, I_FDECSTP, - I_FDISI, I_FDIV, I_FDIVP, I_FDIVR, I_FDIVRP, I_FENI, I_FFREE, - I_FIADD, I_FICOM, I_FICOMP, I_FIDIV, I_FIDIVR, I_FILD, I_FIMUL, - I_FINCSTP, I_FINIT, I_FIST, I_FISTP, I_FISUB, I_FISUBR, I_FLD, - I_FLD1, I_FLDCW, I_FLDENV, I_FLDL2E, I_FLDL2T, I_FLDLG2, - I_FLDLN2, I_FLDPI, I_FLDZ, I_FMUL, I_FMULP, I_FNOP, I_FPATAN, - I_FPREM, I_FPREM1, I_FPTAN, I_FRNDINT, I_FRSTOR, I_FSAVE, - I_FSCALE, I_FSETPM, I_FSIN, I_FSINCOS, I_FSQRT, I_FST, I_FSTCW, - I_FSTENV, I_FSTP, I_FSTSW, I_FSUB, I_FSUBP, I_FSUBR, I_FSUBRP, - I_FTST, I_FUCOM, I_FUCOMI, I_FUCOMIP, I_FUCOMP, I_FUCOMPP, - I_FXAM, I_FXCH, I_FXTRACT, I_FYL2X, I_FYL2XP1, I_HLT, I_ICEBP, - I_IDIV, I_IMUL, I_IN, I_INC, I_INSB, I_INSD, I_INSW, I_INT, - I_INT1, I_INT01, I_INT3, I_INTO, I_INVD, I_INVLPG, I_IRET, - I_IRETD, I_IRETW, I_JCXZ, I_JECXZ, I_JMP, I_LAHF, I_LAR, I_LDS, - I_LEA, I_LEAVE, I_LES, I_LFS, I_LGDT, I_LGS, I_LIDT, I_LLDT, - I_LMSW, I_LOADALL, I_LODSB, I_LODSD, I_LODSW, I_LOOP, I_LOOPE, - I_LOOPNE, I_LOOPNZ, I_LOOPZ, I_LSL, I_LSS, I_LTR, I_MOV, I_MOVD, - I_MOVQ, I_MOVSB, I_MOVSD, I_MOVSW, I_MOVSX, I_MOVZX, I_MUL, - I_NEG, I_NOP, I_NOT, I_OR, I_OUT, I_OUTSB, I_OUTSD, I_OUTSW, - I_PACKSSDW, I_PACKSSWB, I_PACKUSWB, I_PADDB, I_PADDD, I_PADDSB, - I_PADDSW, I_PADDUSB, I_PADDUSW, I_PADDW, I_PAND, I_PANDN, - I_PCMPEQB, I_PCMPEQD, I_PCMPEQW, I_PCMPGTB, I_PCMPGTD, - I_PCMPGTW, I_PMADDWD, I_PMULHW, I_PMULLW, I_POP, I_POPA, - I_POPAD, I_POPAW, I_POPF, I_POPFD, I_POPFW, I_POR, I_PSLLD, - I_PSLLQ, I_PSLLW, I_PSRAD, I_PSRAW, I_PSRLD, I_PSRLQ, I_PSRLW, - I_PSUBB, I_PSUBD, I_PSUBSB, I_PSUBSW, I_PSUBUSB, I_PSUBUSW, - I_PSUBW, I_PUNPCKHBW, I_PUNPCKHDQ, I_PUNPCKHWD, I_PUNPCKLBW, - I_PUNPCKLDQ, I_PUNPCKLWD, I_PUSH, I_PUSHA, I_PUSHAD, I_PUSHAW, - I_PUSHF, I_PUSHFD, I_PUSHFW, I_PXOR, I_RCL, I_RCR, I_RDMSR, - I_RDPMC, I_RDTSC, I_RESB, I_RESD, I_RESQ, I_REST, I_RESW, I_RET, - I_RETF, I_RETN, I_ROL, I_ROR, I_RSM, I_SAHF, I_SAL, I_SALC, - I_SAR, I_SBB, I_SCASB, I_SCASD, I_SCASW, I_SGDT, I_SHL, I_SHLD, - I_SHR, I_SHRD, I_SIDT, I_SLDT, I_SMSW, I_STC, I_STD, I_STI, - I_STOSB, I_STOSD, I_STOSW, I_STR, I_SUB, I_TEST, I_UMOV, I_VERR, - I_VERW, I_WAIT, I_WBINVD, I_WRMSR, I_XADD, I_XCHG, I_XLATB, - I_XOR, I_CMOVcc, I_Jcc, I_SETcc + I_CMPSB, I_CMPSD, I_CMPSW, I_CMPXCHG, I_CMPXCHG486, I_CMPXCHG8B, + I_CPUID, I_CWD, I_CWDE, I_DAA, I_DAS, I_DB, I_DD, I_DEC, I_DIV, + I_DQ, I_DT, I_DW, I_EMMS, I_ENTER, I_EQU, I_F2XM1, I_FABS, + I_FADD, I_FADDP, I_FBLD, I_FBSTP, I_FCHS, I_FCLEX, I_FCMOVB, + I_FCMOVBE, I_FCMOVE, I_FCMOVNB, I_FCMOVNBE, I_FCMOVNE, + I_FCMOVNU, I_FCMOVU, I_FCOM, I_FCOMI, I_FCOMIP, I_FCOMP, + I_FCOMPP, I_FCOS, I_FDECSTP, I_FDISI, I_FDIV, I_FDIVP, I_FDIVR, + I_FDIVRP, I_FENI, I_FFREE, I_FIADD, I_FICOM, I_FICOMP, I_FIDIV, + I_FIDIVR, I_FILD, I_FIMUL, I_FINCSTP, I_FINIT, I_FIST, I_FISTP, + I_FISUB, I_FISUBR, I_FLD, I_FLD1, I_FLDCW, I_FLDENV, I_FLDL2E, + I_FLDL2T, I_FLDLG2, I_FLDLN2, I_FLDPI, I_FLDZ, I_FMUL, I_FMULP, + I_FNOP, I_FPATAN, I_FPREM, I_FPREM1, I_FPTAN, I_FRNDINT, + I_FRSTOR, I_FSAVE, I_FSCALE, I_FSETPM, I_FSIN, I_FSINCOS, + I_FSQRT, I_FST, I_FSTCW, I_FSTENV, I_FSTP, I_FSTSW, I_FSUB, + I_FSUBP, I_FSUBR, I_FSUBRP, I_FTST, I_FUCOM, I_FUCOMI, + I_FUCOMIP, I_FUCOMP, I_FUCOMPP, I_FXAM, I_FXCH, I_FXTRACT, + I_FYL2X, I_FYL2XP1, I_HLT, I_IBTS, I_ICEBP, I_IDIV, I_IMUL, + I_IN, I_INC, I_INCBIN, I_INSB, I_INSD, I_INSW, I_INT, I_INT1, + I_INT01, I_INT3, I_INTO, I_INVD, I_INVLPG, I_IRET, I_IRETD, + I_IRETW, I_JCXZ, I_JECXZ, I_JMP, I_LAHF, I_LAR, I_LDS, I_LEA, + I_LEAVE, I_LES, I_LFS, I_LGDT, I_LGS, I_LIDT, I_LLDT, I_LMSW, + I_LOADALL, I_LOADALL286, I_LODSB, I_LODSD, I_LODSW, I_LOOP, + I_LOOPE, I_LOOPNE, I_LOOPNZ, I_LOOPZ, I_LSL, I_LSS, I_LTR, + I_MOV, I_MOVD, I_MOVQ, I_MOVSB, I_MOVSD, I_MOVSW, I_MOVSX, + I_MOVZX, I_MUL, I_NEG, I_NOP, I_NOT, I_OR, I_OUT, I_OUTSB, + I_OUTSD, I_OUTSW, I_PACKSSDW, I_PACKSSWB, I_PACKUSWB, I_PADDB, + I_PADDD, I_PADDSB, I_PADDSW, I_PADDUSB, I_PADDUSW, I_PADDW, + I_PAND, I_PANDN, I_PCMPEQB, I_PCMPEQD, I_PCMPEQW, I_PCMPGTB, + I_PCMPGTD, I_PCMPGTW, I_PMADDWD, I_PMULHW, I_PMULLW, I_POP, + I_POPA, I_POPAD, I_POPAW, I_POPF, I_POPFD, I_POPFW, I_POR, + I_PSLLD, I_PSLLQ, I_PSLLW, I_PSRAD, I_PSRAW, I_PSRLD, I_PSRLQ, + I_PSRLW, I_PSUBB, I_PSUBD, I_PSUBSB, I_PSUBSW, I_PSUBUSB, + I_PSUBUSW, I_PSUBW, I_PUNPCKHBW, I_PUNPCKHDQ, I_PUNPCKHWD, + I_PUNPCKLBW, I_PUNPCKLDQ, I_PUNPCKLWD, I_PUSH, I_PUSHA, + I_PUSHAD, I_PUSHAW, I_PUSHF, I_PUSHFD, I_PUSHFW, I_PXOR, I_RCL, + I_RCR, I_RDMSR, I_RDPMC, I_RDTSC, I_RESB, I_RESD, I_RESQ, + I_REST, I_RESW, I_RET, I_RETF, I_RETN, I_ROL, I_ROR, I_RSM, + I_SAHF, I_SAL, I_SALC, I_SAR, I_SBB, I_SCASB, I_SCASD, I_SCASW, + I_SGDT, I_SHL, I_SHLD, I_SHR, I_SHRD, I_SIDT, I_SLDT, I_SMI, + I_SMSW, I_STC, I_STD, I_STI, I_STOSB, I_STOSD, I_STOSW, I_STR, + I_SUB, I_TEST, I_UMOV, I_VERR, I_VERW, I_WAIT, I_WBINVD, + I_WRMSR, I_XADD, I_XBTS, I_XCHG, I_XLATB, I_XOR, I_CMOVcc, + I_Jcc, I_SETcc }; enum { /* condition code names */ @@ -267,7 +322,7 @@ typedef struct { /* an instruction itself */ int operands; /* how many operands? 0-3 */ operand oprs[3]; /* the operands, defined as above */ extop *eops; /* extended operands */ - int times; /* repeat count (TIMES prefix) */ + long times; /* repeat count (TIMES prefix) */ int forw_ref; /* is there a forward reference? */ } insn; @@ -16,33 +16,88 @@ static efunc nasm_malloc_error; +#ifdef LOGALLOC +static FILE *logfp; +#endif + void nasm_set_malloc_error (efunc error) { nasm_malloc_error = error; +#ifdef LOGALLOC + logfp = fopen ("malloc.log", "w"); + setvbuf (logfp, NULL, _IOLBF, BUFSIZ); + fprintf (logfp, "null pointer is %p\n", NULL); +#endif } -void *nasm_malloc (size_t size) { +#ifdef LOGALLOC +void *nasm_malloc_log (char *file, int line, size_t size) +#else +void *nasm_malloc (size_t size) +#endif +{ void *p = malloc(size); if (!p) nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory"); +#ifdef LOGALLOC + else + fprintf(logfp, "%s %d malloc(%ld) returns %p\n", + file, line, (long)size, p); +#endif return p; } -void *nasm_realloc (void *q, size_t size) { +#ifdef LOGALLOC +void *nasm_realloc_log (char *file, int line, void *q, size_t size) +#else +void *nasm_realloc (void *q, size_t size) +#endif +{ void *p = q ? realloc(q, size) : malloc(size); if (!p) nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory"); +#ifdef LOGALLOC + else if (q) + fprintf(logfp, "%s %d realloc(%p,%ld) returns %p\n", + file, line, q, (long)size, p); + else + fprintf(logfp, "%s %d malloc(%ld) returns %p\n", + file, line, (long)size, p); +#endif return p; } -void nasm_free (void *q) { - if (q) +#ifdef LOGALLOC +void nasm_free_log (char *file, int line, void *q) +#else +void nasm_free (void *q) +#endif +{ + if (q) { free (q); +#ifdef LOGALLOC + fprintf(logfp, "%s %d free(%p)\n", + file, line, q); +#endif + } } -char *nasm_strdup (char *s) { +#ifdef LOGALLOC +char *nasm_strdup_log (char *file, int line, char *s) +#else +char *nasm_strdup (char *s) +#endif +{ char *p; + int size = strlen(s)+1; - p = nasm_malloc(strlen(s)+1); + p = malloc(size); + if (!p) + nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory"); +#ifdef LOGALLOC + else + fprintf(logfp, "%s %d strdup(%ld) returns %p\n", + file, line, (long)size, p); +#endif strcpy (p, s); return p; } @@ -69,7 +124,7 @@ int nasm_strnicmp (char *s1, char *s2, int n) { return 1; } -#define isnumchar(c) ( isalnum(c) || (c) == '$') +#define lib_isnumchar(c) ( isalnum(c) || (c) == '$') #define numvalue(c) ((c)>='a' ? (c)-'a'+10 : (c)>='A' ? (c)-'A'+10 : (c)-'0') long readnum (char *str, int *error) { @@ -82,7 +137,7 @@ long readnum (char *str, int *error) { while (isspace(*r)) r++; /* find start of number */ q = r; - while (isnumchar(*q)) q++; /* find end of number */ + while (lib_isnumchar(*q)) q++; /* find end of number */ /* * If it begins 0x, 0X or $, or ends in H, it's in hex. if it @@ -125,15 +180,15 @@ long seg_alloc(void) { } void fwriteshort (int data, FILE *fp) { - fputc (data & 255, fp); - fputc ((data >> 8) & 255, fp); + fputc ((int) (data & 255), fp); + fputc ((int) ((data >> 8) & 255), fp); } void fwritelong (long data, FILE *fp) { - fputc (data & 255, fp); - fputc ((data >> 8) & 255, fp); - fputc ((data >> 16) & 255, fp); - fputc ((data >> 24) & 255, fp); + fputc ((int) (data & 255), fp); + fputc ((int) ((data >> 8) & 255), fp); + fputc ((int) ((data >> 16) & 255), fp); + fputc ((int) ((data >> 24) & 255), fp); } void standard_extension (char *inname, char *outname, char *extension, @@ -10,17 +10,39 @@ #define NASM_NASMLIB_H /* + * If this is defined, the wrappers around malloc et al will + * transform into logging variants, which will cause NASM to create + * a file called `malloc.log' when run, and spew details of all its + * memory management into that. That can then be analysed to detect + * memory leaks and potentially other problems too. + */ +/* #define LOGALLOC */ + +/* * Wrappers around malloc, realloc and free. nasm_malloc will * fatal-error and die rather than return NULL; nasm_realloc will * do likewise, and will also guarantee to work right on being * passed a NULL pointer; nasm_free will do nothing if it is passed * a NULL pointer. */ +#ifdef NASM_NASM_H /* need efunc defined for this */ void nasm_set_malloc_error (efunc); +#ifndef LOGALLOC void *nasm_malloc (size_t); void *nasm_realloc (void *, size_t); void nasm_free (void *); char *nasm_strdup (char *); +#else +void *nasm_malloc_log (char *, int, size_t); +void *nasm_realloc_log (char *, int, void *, size_t); +void nasm_free_log (char *, int, void *); +char *nasm_strdup_log (char *, int, char *); +#define nasm_malloc(x) nasm_malloc_log(__FILE__,__LINE__,x) +#define nasm_realloc(x,y) nasm_realloc_log(__FILE__,__LINE__,x,y) +#define nasm_free(x) nasm_free_log(__FILE__,__LINE__,x) +#define nasm_strdup(x) nasm_strdup_log(__FILE__,__LINE__,x) +#endif +#endif /* * ANSI doesn't guarantee the presence of `stricmp' or @@ -46,8 +68,10 @@ long seg_alloc(void); * many output formats will be able to make use of this: a standard * function to add an extension to the name of the input file */ +#ifdef NASM_NASM_H void standard_extension (char *inname, char *outname, char *extension, efunc error); +#endif /* * some handy macros that will probably be of use in more than one @@ -7,6 +7,7 @@ */ #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <ctype.h> @@ -60,11 +61,9 @@ int main(int argc, char **argv) { case 'h': fprintf(stderr, help); return 0; - break; case 'r': fprintf(stderr, "NDISASM version " NASM_VER "\n"); return 0; - break; case 'u': /* USE32 */ bits = 32; p++; @@ -360,7 +360,7 @@ static void aout_write(void) { /* * Emit the a.out header. */ - fwritelong (0x640107, aoutfp); /* OMAGIC, M_386, no flags */ + fwritelong (0x640107L, aoutfp); /* OMAGIC, M_386, no flags */ fwritelong (stext.len, aoutfp); fwritelong (sdata.len, aoutfp); fwritelong (bsslen, aoutfp); @@ -400,12 +400,12 @@ static void aout_write_relocs (struct Reloc *r) { fwritelong (r->address, aoutfp); if (r->symbol >= 0) - word2 = r->symbol | 0x8000000; + word2 = r->symbol | 0x8000000L; else word2 = -r->symbol; if (r->relative) - word2 |= 0x1000000; - word2 |= (r->bytes == 2 ? 0x2000000 : 0x4000000); + word2 |= 0x1000000L; + word2 |= (r->bytes == 2 ? 0x2000000L : 0x4000000L); fwritelong (word2, aoutfp); r = r->next; @@ -354,7 +354,7 @@ static void as86_write(void) { sym->flags |= 0 << 14, symlen += 4; else if (sym->value >= 0 && sym->value <= 255) sym->flags |= 1 << 14, symlen += 5; - else if (sym->value >= 0 && sym->value <= 65535) + else if (sym->value >= 0 && sym->value <= 65535L) sym->flags |= 2 << 14, symlen += 6; else sym->flags |= 3 << 14, symlen += 8; @@ -365,31 +365,31 @@ static void as86_write(void) { * descriptor word at the same time. */ seglen = segsize = 0; - if ((unsigned long) stext.len > 65535) - segsize |= 0x03000000, seglen += 4; + if ((unsigned long) stext.len > 65535L) + segsize |= 0x03000000L, seglen += 4; else - segsize |= 0x02000000, seglen += 2; - if ((unsigned long) sdata.len > 65535) - segsize |= 0xC0000000, seglen += 4; + segsize |= 0x02000000L, seglen += 2; + if ((unsigned long) sdata.len > 65535L) + segsize |= 0xC0000000L, seglen += 4; else - segsize |= 0x80000000, seglen += 2; + segsize |= 0x80000000L, seglen += 2; /* * Emit the as86 header. */ - fwritelong (0x000186A3, as86fp); + fwritelong (0x000186A3L, as86fp); fputc (0x2A, as86fp); fwritelong (27+symlen+seglen+strslen, as86fp); /* header length */ fwritelong (stext.len+sdata.len, as86fp); fwriteshort (strslen, as86fp); fwriteshort (0, as86fp); /* class = revision = 0 */ - fwritelong (0x55555555, as86fp); /* segment max sizes: always this */ + fwritelong (0x55555555L, as86fp); /* segment max sizes: always this */ fwritelong (segsize, as86fp); /* segment size descriptors */ - if (segsize & 0x01000000) + if (segsize & 0x01000000L) fwritelong (stext.len, as86fp); else fwriteshort (stext.len, as86fp); - if (segsize & 0x40000000) + if (segsize & 0x40000000L) fwritelong (sdata.len, as86fp); else fwriteshort (sdata.len, as86fp); @@ -481,7 +481,7 @@ static void as86_write_section (struct Section *sect, int index) { */ as86_set_rsize (p->bytes); s = p->offset; - if (s > 65535) + if (s > 65535L) s = 3; else if (s > 255) s = 2; @@ -35,7 +35,7 @@ static struct Reloc { struct Section *target; } *relocs, **reloctail; -static int start_point; +static long start_point; static void add_reloc (struct Section *s, long bytes, long secref, long secrel) { @@ -57,7 +57,7 @@ static void bin_init (FILE *afp, efunc errfunc, ldfunc ldef) { error = errfunc; (void) ldef; /* placate optimisers */ - start_point = 0; /* default */ + start_point = 0L; /* default */ textsect.contents = saa_init(1L); datasect.contents = saa_init(1L); textsect.length = datasect.length = 0; @@ -486,14 +486,14 @@ static int coff_directives (char *directive, char *value, int pass) { } static void coff_write (void) { - long hdrs_end, pos, sympos, vsize; + long pos, sympos, vsize; int i; /* * Work out how big the file will get. Calculate the start of * the `real' symbols at the same time. */ - pos = hdrs_end = 0x14 + 0x28 * nsects; + pos = 0x14 + 0x28 * nsects; initsym = 3; /* two for the file, one absolute */ for (i=0; i<nsects; i++) { if (sects[i]->data) { @@ -544,7 +544,7 @@ static void elf_write(void) { for (i=0; i<nsects; i++) if (sects[i]->head) { p += strlen(p)+1; elf_section_header (p - shstrtab, 9, 0, sects[i]->rel, TRUE, - sects[i]->rellen, 6, i+1, 4, 8); + sects[i]->rellen, nsects+3, i+1, 4, 8); } fwrite (align_str, align, 1, elffp); @@ -659,7 +659,7 @@ static struct SAA *elf_build_reltab (long *len, struct Reloc *r) { long sym = r->symbol; if (sym >= GLOBAL_TEMP_BASE) - sym += -GLOBAL_TEMP_BASE + 6 + nlocals; + sym += -GLOBAL_TEMP_BASE + (nsects+3) + nlocals; p = entry; WRITELONG (p, r->address); @@ -248,7 +248,7 @@ static void obj_deflabel (char *name, long segment, if (segment < SEG_ABS && segment != NO_SEG && segment % 2) return; - if (segment >= SEG_ABS) { + if (segment >= SEG_ABS || segment == NO_SEG) { /* * SEG_ABS subcase of (ii). */ @@ -260,7 +260,7 @@ static void obj_deflabel (char *name, long segment, pub->next = NULL; pub->name = name; pub->offset = offset; - pub->segment = segment & ~SEG_ABS; + pub->segment = (segment == NO_SEG ? 0 : segment & ~SEG_ABS); } return; } @@ -887,7 +887,7 @@ static void obj_write_file (void) { int acbp; int sn, cn, on; /* seg, class, overlay LNAME idx */ - if (seg->use32 || seg->currentpos >= 0x10000) + if (seg->use32 || seg->currentpos >= 0x10000L) new_segdef = TRUE; else new_segdef = FALSE; @@ -909,7 +909,7 @@ static void obj_write_file (void) { acbp = (seg->combine << 2); /* C field */ - if (seg->currentpos >= 0x10000 && !new_segdef) + if (seg->currentpos >= 0x10000L && !new_segdef) acbp |= 0x02; /* B bit */ if (seg->use32) @@ -1188,7 +1188,7 @@ static unsigned char *obj_write_value(unsigned char *ptr, *ptr++ = 129; *ptr++ = data & 0xFF; *ptr++ = (data >> 8) & 0xFF; - } else if (data <= 0xFFFFFF) { + } else if (data <= 0xFFFFFFL) { *ptr++ = 132; *ptr++ = data & 0xFF; *ptr++ = (data >> 8) & 0xFF; @@ -22,6 +22,9 @@ #include "nasmlib.h" #include "outform.h" +/* VERBOSE_WARNINGS: define this to add some extra warnings... */ +#define VERBOSE_WARNINGS + #ifdef OF_RDF typedef short int16; /* not sure if this will be required to be altered @@ -252,13 +255,27 @@ static void write_bss_rec(struct BSSRec *r) membufwrite(header,&r->amount,-4); } +static void write_dll_rec(struct DLLRec *r) +{ + membufwrite(header,&r->type,1); + membufwrite(header,r->libname,strlen(r->libname) + 1); +} + static void rdf_deflabel(char *name, long segment, long offset, int is_global) { struct ExportRec r; struct ImportRec ri; +#ifdef VERBOSE_WARNINGS + static int warned_common = 0; +#endif if (is_global && segment > 4) { - error(ERR_WARNING,"common declarations not supported... using extern"); +#ifdef VERBOSE_WARNINGS + if (! warned_common) { + error(ERR_WARNING,"common declarations not supported... using extern"); + warned_common = 1; + } +#endif is_global = 0; } @@ -444,7 +461,18 @@ static long rdf_segbase (long segment) { } static int rdf_directive (char *directive, char *value, int pass) { - return 0; + struct DLLRec r; + + if (! strcmp(directive, "library")) { + if (pass == 1) { + r.type = 4; + strcpy(r.libname, value); + write_dll_rec(&r); + } + return 1; + } + + return 0; } static void rdf_filename (char *inname, char *outname, efunc error) { @@ -21,7 +21,6 @@ #include "names.c" - static long reg_flags[] = { /* sizes and special flags */ 0, REG8, REG_AL, REG_AX, REG8, REG8, REG16, REG16, REG8, REG_CL, REG_CREG, REG_CREG, REG_CREG, REG_CR4, REG_CS, REG_CX, REG8, @@ -164,7 +163,7 @@ insn *parse_line (long segment, long offset, lfunc lookup_label, int pass, } result->nprefix = 0; - result->times = 1; + result->times = 1L; while (i == TOKEN_PREFIX || (i==TOKEN_REG && !(REG_SREG & ~reg_flags[tokval.t_integer]))) { @@ -184,9 +183,13 @@ insn *parse_line (long segment, long offset, lfunc lookup_label, int pass, if (!is_simple (value)) { error (ERR_NONFATAL, "non-constant argument supplied to TIMES"); - result->times = 1; - } else + result->times = 1L; + } else { result->times = value->value; + if (value->value < 0) + error(ERR_WARNING, "TIMES value %d is negative", + value->value); + } } else { if (result->nprefix == MAXPREFIX) error (ERR_NONFATAL, @@ -231,7 +234,8 @@ insn *parse_line (long segment, long offset, lfunc lookup_label, int pass, result->opcode == I_DW || result->opcode == I_DD || result->opcode == I_DQ || - result->opcode == I_DT) { + result->opcode == I_DT || + result->opcode == I_INCBIN) { extop *eop, **tail = &result->eops; int oper_num = 0; @@ -315,6 +319,36 @@ insn *parse_line (long segment, long offset, lfunc lookup_label, int pass, } } } + + if (result->opcode == I_INCBIN) { + /* + * Correct syntax for INCBIN is that there should be + * one string operand, followed by one or two numeric + * operands. + */ + if (!result->eops || result->eops->type != EOT_DB_STRING) + error (ERR_NONFATAL, "`incbin' expects a file name"); + else if (result->eops->next && + result->eops->next->type != EOT_DB_NUMBER) + error (ERR_NONFATAL, "`incbin': second parameter is", + " non-numeric"); + else if (result->eops->next && result->eops->next->next && + result->eops->next->next->type != EOT_DB_NUMBER) + error (ERR_NONFATAL, "`incbin': third parameter is", + " non-numeric"); + else if (result->eops->next && result->eops->next->next && + result->eops->next->next->next) + error (ERR_NONFATAL, "`incbin': more than three parameters"); + else + return result; + /* + * If we reach here, one of the above errors happened. + * Throw the instruction away. + */ + result->opcode = -1; + return result; + } + return result; } @@ -595,25 +629,6 @@ static int is_comma_next (void) { return (*p == ',' || *p == ';' || !*p); } -/* isidstart matches any character that may start an identifier, and isidchar - * matches any character that may appear at places other than the start of an - * identifier. E.g. a period may only appear at the start of an identifier - * (for local labels), whereas a number may appear anywhere *but* at the - * start. */ - -#define isidstart(c) ( isalpha(c) || (c)=='_' || (c)=='.' || (c)=='?' ) -#define isidchar(c) ( isidstart(c) || isdigit(c) || (c)=='$' || (c)=='#' \ - || (c)=='@' || (c)=='~' ) - -/* Ditto for numeric constants. */ - -#define isnumstart(c) ( isdigit(c) || (c)=='$' ) -#define isnumchar(c) ( isalnum(c) ) - -/* This returns the numeric value of a given 'digit'. */ - -#define numvalue(c) ((c)>='a' ? (c)-'a'+10 : (c)>='A' ? (c)-'A'+10 : (c)-'0') - /* * This tokeniser routine has only one side effect, that of * updating `bufptr'. Hence by saving `bufptr', lookahead may be @@ -1,5 +1,5 @@ -/* parser.h header file for the parser module of version 0.1 of the - * Netwide Assembler +/* parser.h header file for the parser module of the Netwide + * Assembler * * The Netwide Assembler is copyright (C) 1996 Simon Tatham and * Julian Hall. All rights reserved. The software is diff --git a/preproc.c b/preproc.c new file mode 100644 index 0000000..cd8c617 --- /dev/null +++ b/preproc.c @@ -0,0 +1,2148 @@ +/* preproc.c macro preprocessor for the Netwide Assembler + * + * The Netwide Assembler is copyright (C) 1996 Simon Tatham and + * Julian Hall. All rights reserved. The software is + * redistributable under the licence given in the file "Licence" + * distributed in the NASM archive. + * + * initial version 18/iii/97 by Simon Tatham + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <ctype.h> + +#include "nasm.h" +#include "nasmlib.h" + +typedef struct SMacro SMacro; +typedef struct MMacro MMacro; +typedef struct Context Context; +typedef struct Token Token; +typedef struct Line Line; +typedef struct Include Include; +typedef struct Cond Cond; + +/* + * Store the definition of a single-line macro. + */ +struct SMacro { + SMacro *next; + char *name; + int casesense; + int nparam; + int in_progress; + Token *expansion; +}; + +/* + * Store the definition of a multi-line macro. + */ +struct MMacro { + MMacro *next; + char *name; + int casesense; + int nparam_min, nparam_max; + int plus; /* is the last parameter greedy? */ + int in_progress; + Token **defaults, *dlist; + Line *expansion; +}; + +/* + * The context stack is composed of a linked list of these. + */ +struct Context { + Context *next; + SMacro *localmac; + char *name; + unsigned long number; +}; + +/* + * This is the internal form which we break input lines up into. + * Typically stored in linked lists. + * + * TOK_PS_OTHER is a token type used internally within + * expand_smacro(), to denote a token which has already been + * checked for being a potential macro, but may still be a context- + * local label. + * + * Note that `type' serves a double meaning: TOK_SMAC_PARAM is not + * necessarily used as-is, but is intended to denote the number of + * the substituted parameter. So in the definition + * + * %define a(x,y) ( (x) & ~(y) ) + * + * the token representing `x' will have its type changed to + * TOK_SMAC_PARAM, but the one representing `y' will be + * TOK_SMAC_PARAM+1. + */ +struct Token { + Token *next; + char *text; + SMacro *mac; /* associated macro for TOK_MAC_END */ + int type; +}; +enum { + TOK_WHITESPACE = 1, TOK_COMMENT, TOK_ID, TOK_PREPROC_ID, TOK_STRING, + TOK_NUMBER, TOK_SMAC_END, TOK_OTHER, TOK_PS_OTHER, TOK_SMAC_PARAM +}; + +/* + * Multi-line macro definitions are stored as a linked list of + * these, which is essentially a container to allow several linked + * lists of Tokens. + * + * Note that in this module, linked lists are treated as stacks + * wherever possible. For this reason, Lines are _pushed_ on to the + * `expansion' field in MMacro structures, so that the linked list, + * if walked, would give the macro lines in reverse order; this + * means that we can walk the list when expanding a macro, and thus + * push the lines on to the `expansion' field in _istk_ in reverse + * order (so that when popped back off they are in the right + * order). It may seem cockeyed, and it relies on my design having + * an even number of steps in, but it works... + * + * Some of these structures, rather than being actual lines, are + * markers delimiting the end of the expansion of a given macro. + * This is for use in the cycle-tracking code. Such structures have + * `finishes' non-NULL, and `first' NULL. All others have + * `finishes' NULL, but `first' may still be non-NULL if the line + * is blank. + */ +struct Line { + Line *next; + MMacro *finishes; + Token *first; +}; + +/* + * To handle an arbitrary level of file inclusion, we maintain a + * stack (ie linked list) of these things. + */ +struct Include { + Include *next; + FILE *fp; + Cond *conds; + Line *expansion; + char *fname; + int lineno, lineinc; +}; + +/* + * Conditional assembly: we maintain a separate stack of these for + * each level of file inclusion. (The only reason we keep the + * stacks separate is to ensure that a stray `%endif' in a file + * included from within the true branch of a `%if' won't terminate + * it and cause confusion: instead, rightly, it'll cause an error.) + */ +struct Cond { + Cond *next; + int state; +}; +enum { + /* + * These states are for use just after %if or %elif: IF_TRUE + * means the condition has evaluated to truth so we are + * currently emitting, whereas IF_FALSE means we are not + * currently emitting but will start doing so if a %else comes + * up. In these states, all directives are admissible: %elif, + * %else and %endif. (And of course %if.) + */ + COND_IF_TRUE, COND_IF_FALSE, + /* + * These states come up after a %else: ELSE_TRUE means we're + * emitting, and ELSE_FALSE means we're not. In ELSE_* states, + * any %elif or %else will cause an error. + */ + COND_ELSE_TRUE, COND_ELSE_FALSE, + /* + * This state means that we're not emitting now, and also that + * nothing until %endif will be emitted at all. It's for use in + * two circumstances: (i) when we've had our moment of emission + * and have now started seeing %elifs, and (ii) when the + * condition construct in question is contained within a + * non-emitting branch of a larger condition construct. + */ + COND_NEVER +}; +#define emitting(x) ( (x) == COND_IF_TRUE || (x) == COND_ELSE_TRUE ) + +/* + * Condition codes. Note that we use c_ prefix not C_ because C_ is + * used in nasm.h for the "real" condition codes. At _this_ level, + * we treat CXZ and ECXZ as condition codes, albeit non-invertible + * ones, so we need a different enum... + */ +static char *conditions[] = { + "a", "ae", "b", "be", "c", "cxz", "e", "ecxz", "g", "ge", "l", "le", + "na", "nae", "nb", "nbe", "nc", "ne", "ng", "nge", "nl", "nle", "no", + "np", "ns", "nz", "o", "p", "pe", "po", "s", "z" +}; +enum { + c_A, c_AE, c_B, c_BE, c_C, c_CXZ, c_E, c_ECXZ, c_G, c_GE, c_L, c_LE, + c_NA, c_NAE, c_NB, c_NBE, c_NC, c_NE, c_NG, c_NGE, c_NL, c_NLE, c_NO, + c_NP, c_NS, c_NZ, c_O, c_P, c_PE, c_PO, c_S, c_Z +}; +static int inverse_ccs[] = { + c_NA, c_NAE, c_NB, c_NBE, c_NC, -1, c_NE, -1, c_NG, c_NGE, c_NL, c_NLE, + c_A, c_AE, c_B, c_BE, c_C, c_E, c_G, c_GE, c_L, c_LE, c_O, c_P, c_S, + c_Z, c_NO, c_NP, c_PO, c_PE, c_NS, c_NZ +}; + +static Context *cstk; +static Include *istk; + +static efunc error; + +static unsigned long unique; /* unique identifier numbers */ + +static char *linesync, *outline; + +/* + * The number of hash values we use for the macro lookup tables. + */ +#define NHASH 31 + +/* + * The current set of multi-line macros we have defined. + */ +static MMacro *mmacros[NHASH]; + +/* + * The current set of single-line macros we have defined. + */ +static SMacro *smacros[NHASH]; + +/* + * The multi-line macro we are currently defining, if any. + */ +static MMacro *defining; + +/* + * The number of macro parameters to allocate space for at a time. + */ +#define PARAM_DELTA 16 + +/* + * The standard macro set: defined as `static char *stdmac[]'. Also + * gives our position in the macro set, when we're processing it. + */ +#include "macros.c" +static char **stdmacpos; + +/* + * The pre-preprocessing stage... This function has two purposes: + * firstly, it translates line number indications as they emerge + * from GNU cpp (`# lineno "file" flags') into NASM preprocessor + * line number indications (`%line lineno file'), and secondly, it + * converts [INCLUDE] and [INC] old-style inclusion directives into + * the new-style `%include' form (though in the next version it + * won't do that any more). + */ +static char *prepreproc(char *line) { + int lineno, fnlen; + char *fname, *oldline; + + if (line[0] == '#' && line[1] == ' ') { + oldline = line; + fname = oldline+2; + lineno = atoi(fname); + fname += strspn(fname, "0123456789 "); + if (*fname == '"') + fname++; + fnlen = strcspn(fname, "\""); + line = nasm_malloc(20+fnlen); + sprintf(line, "%%line %d %.*s", lineno, fnlen, fname); + nasm_free (oldline); + return line; + } else if (!nasm_strnicmp(line, "[include", 8)) { + oldline = line; + fname = oldline+8; + fname += strspn(fname, " \t"); + fnlen = strcspn(fname, "]"); + line = nasm_malloc(20+fnlen); + sprintf(line, "%%include \"%.*s\"", fnlen, fname); + error (ERR_WARNING|ERR_OFFBY1, "use of [INCLUDE] is being phased out;" + " suggest `%%include'"); + nasm_free (oldline); + return line; + } else if (!nasm_strnicmp(line, "[inc", 4)) { + oldline = line; + fname = oldline+4; + fname += strspn(fname, " \t"); + fnlen = strcspn(fname, "]"); + line = nasm_malloc(20+fnlen); + sprintf(line, "%%include \"%.*s\"", fnlen, fname); + error (ERR_WARNING|ERR_OFFBY1, "use of [INC] is being phased out;" + " suggest `%%include'"); + nasm_free (oldline); + return line; + } else + return line; +} + +/* + * The hash function for macro lookups. Note that due to some + * macros having case-insensitive names, the hash function must be + * invariant under case changes. We implement this by applying a + * perfectly normal hash function to the uppercase of the string. + */ +static int hash(char *s) { + /* + * Powers of three, mod 31. + */ + static const int multipliers[] = { + 1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, + 30, 28, 22, 4, 12, 5, 15, 14, 11, 2, 6, 18, 23, 7, 21 + }; + int h = 0; + int i = 0; + + while (*s) { + h += multipliers[i] * (unsigned char) (toupper(*s)); + s++; + if (++i >= sizeof(multipliers)/sizeof(*multipliers)) + i = 0; + } + h %= NHASH; + return h; +} + +/* + * Free a linked list of tokens. + */ +static void free_tlist (Token *list) { + Token *t; + while (list) { + t = list; + list = list->next; + nasm_free (t->text); + nasm_free (t); + } +} + +/* + * Free a linked list of lines. + */ +static void free_llist (Line *list) { + Line *l; + while (list) { + l = list; + list = list->next; + free_tlist (l->first); + nasm_free (l); + } +} + +/* + * Pop the context stack. + */ +static void ctx_pop (void) { + Context *c = cstk; + SMacro *smac, *s; + + cstk = cstk->next; + smac = c->localmac; + while (smac) { + s = smac; + smac = smac->next; + nasm_free (s->name); + free_tlist (s->expansion); + nasm_free (s); + } + nasm_free (c->name); + nasm_free (c); +} + +/* + * Generate a line synchronisation comment, to ensure the assembler + * knows which source file the current output has really come from. + */ +static void line_sync (void) { + char text[80]; + sprintf(text, "%%line %d+%d %s", + (istk->expansion ? istk->lineno - istk->lineinc : istk->lineno), + (istk->expansion ? 0 : istk->lineinc), istk->fname); + if (linesync) + free (linesync); + linesync = nasm_strdup(text); +} + +#define BUF_DELTA 512 +/* + * Read a line from the top file in istk, handling multiple CR/LFs + * at the end of the line read, and handling spurious ^Zs. Will + * return lines from the standard macro set if this has not already + * been done. + */ +static char *read_line (void) { + char *buffer, *p, *q; + int bufsize; + + if (stdmacpos) { + if (*stdmacpos) + return nasm_strdup(*stdmacpos++); + else { + stdmacpos = NULL; + line_sync(); + } + } + + bufsize = BUF_DELTA; + buffer = nasm_malloc(BUF_DELTA); + p = buffer; + while (1) { + q = fgets(p, bufsize-(p-buffer), istk->fp); + if (!q) + break; + p += strlen(p); + if (p > buffer && p[-1] == '\n') { + istk->lineno += istk->lineinc; + break; + } + if (p-buffer > bufsize-10) { + bufsize += BUF_DELTA; + buffer = nasm_realloc(buffer, bufsize); + } + } + + if (!q && p == buffer) { + nasm_free (buffer); + return NULL; + } + + /* + * Play safe: remove CRs as well as LFs, if any of either are + * present at the end of the line. + */ + while (p > buffer && (p[-1] == '\n' || p[-1] == '\r')) + *--p = '\0'; + + /* + * Handle spurious ^Z, which may be inserted into source files + * by some file transfer utilities. + */ + buffer[strcspn(buffer, "\032")] = '\0'; + + return buffer; +} + +/* + * Tokenise a line of text. This is a very simple process since we + * don't need to parse the value out of e.g. numeric tokens: we + * simply split one string into many. + */ +static Token *tokenise (char *line) { + char *p = line; + int type; + Token *list = NULL; + Token *t, **tail = &list; + + while (*line) { + p = line; + if (*p == '%' && + (p[1] == '{' || p[1] == '!' || (p[1] == '%' && isidchar(p[2])) || + p[1] == '$' || p[1] == '+' || p[1] == '-' || isidchar(p[1]))) { + type = TOK_PREPROC_ID; + p++; + if (*p == '{') { + p++; + while (*p && *p != '}') { + p[-1] = *p; + p++; + } + p[-1] = '\0'; + if (*p) p++; + } else { + if (*p == '!' || *p == '%' || *p == '$' || + *p == '+' || *p == '-') p++; + while (*p && isidchar(*p)) + p++; + } + } else if (isidstart(*p)) { + type = TOK_ID; + p++; + while (*p && isidchar(*p)) + p++; + } else if (*p == '\'' || *p == '"') { + /* + * A string token. + */ + char c = *p; + p++; + type = TOK_STRING; + while (*p && *p != c) + p++; + if (*p) p++; + } else if (isnumstart(*p)) { + /* + * A number token. + */ + type = TOK_NUMBER; + p++; + while (*p && isnumchar(*p)) + p++; + } else if (isspace(*p)) { + type = TOK_WHITESPACE; + p++; + while (*p && isspace(*p)) + p++; + /* + * Whitespace just before end-of-line is discarded by + * pretending it's a comment; whitespace just before a + * comment gets lumped into the comment. + */ + if (!*p || *p == ';') { + type = TOK_COMMENT; + while (*p) p++; + } + } else if (*p == ';') { + type = TOK_COMMENT; + while (*p) p++; + } else { + /* + * Anything else is an operator of some kind; with the + * exceptions of >>, <<, // and %%, all operator tokens + * are single-character. + */ + char c = *p++; + type = TOK_OTHER; + if ( (c == '>' || c == '<' || c == '/' || c == '%') && *p == c) + p++; + } + if (type != TOK_COMMENT) { + *tail = t = nasm_malloc (sizeof(Token)); + tail = &t->next; + t->next = NULL; + t->type = type; + t->text = nasm_malloc(1+p-line); + strncpy(t->text, line, p-line); + t->text[p-line] = '\0'; + } + line = p; + } + + return list; +} + +/* + * Convert a line of tokens back into text. + */ +static char *detoken (Token *tlist) { + Token *t; + int len; + char *line, *p; + + len = 0; + for (t = tlist; t; t = t->next) { + if (t->type == TOK_PREPROC_ID && t->text[1] == '!') { + char *p = getenv(t->text+2); + nasm_free (t->text); + if (p) + t->text = nasm_strdup(p); + else + t->text = NULL; + } + if (t->text) + len += strlen(t->text); + } + p = line = nasm_malloc(len+1); + for (t = tlist; t; t = t->next) { + if (t->text) { + strcpy (p, t->text); + p += strlen(p); + } + } + *p = '\0'; + return line; +} + +/* + * Return the Context structure associated with a %$ token. Return + * NULL, having _already_ reported an error condition, if the + * context stack isn't deep enough for the supplied number of $ + * signs. + */ +static Context *get_ctx (char *name) { + Context *ctx; + int i; + + if (!cstk) { + error (ERR_NONFATAL|ERR_OFFBY1, "`%s': context stack is empty", name); + return NULL; + } + + i = 1; + ctx = cstk; + while (name[i+1] == '$') { + i++; + ctx = ctx->next; + if (!ctx) { + error (ERR_NONFATAL|ERR_OFFBY1, "`%s': context stack is only" + " %d level%s deep", name, i-1, (i==2 ? "" : "s")); + return NULL; + } + } + return ctx; +} + +/* + * Compare a string to the name of an existing macro; this is a + * simple wrapper which calls either strcmp or nasm_stricmp + * depending on the value of the `casesense' parameter. + */ +static int mstrcmp(char *p, char *q, int casesense) { + return casesense ? strcmp(p,q) : nasm_stricmp(p,q); +} + +/* + * Determine if we should warn on defining a single-line macro of + * name `name', with `nparam' parameters. If nparam is 0, will + * return TRUE if _any_ single-line macro of that name is defined. + * Otherwise, will return TRUE if a single-line macro with either + * `nparam' or no parameters is defined. + * + * If a macro with precisely the right number of parameters is + * defined, the address of the definition structure will be + * returned in `defn'; otherwise NULL will be returned. If `defn' + * is NULL, no action will be taken regarding its contents, and no + * error will occur. + * + * Note that this is also called with nparam zero to resolve + * `ifdef'. + */ +static int smacro_defined (char *name, int nparam, SMacro **defn) { + SMacro *m; + Context *ctx; + char *p; + + if (name[0] == '%' && name[1] == '$') { + ctx = get_ctx (name); + if (!ctx) + return FALSE; /* got to return _something_ */ + m = ctx->localmac; + p = name+1; + p += strspn(p, "$"); + } else { + m = smacros[hash(name)]; + p = name; + } + + while (m) { + if (!mstrcmp(m->name, p, m->casesense) && + (nparam == 0 || m->nparam == 0 || nparam == m->nparam)) { + if (defn) { + if (nparam == m->nparam) + *defn = m; + else + *defn = NULL; + } + return TRUE; + } + m = m->next; + } + return FALSE; +} + +/* + * Count and mark off the parameters in a multi-line macro call. + * This is called both from within the multi-line macro expansion + * code, and also to mark off the default parameters when provided + * in a %macro definition line. + */ +static void count_mmac_params (Token *t, int *nparam, Token ***params) { + int paramsize, brace; + + *nparam = paramsize = 0; + *params = NULL; + while (t) { + if (*nparam >= paramsize) { + paramsize += PARAM_DELTA; + *params = nasm_realloc(*params, sizeof(**params) * paramsize); + } + if (t && t->type == TOK_WHITESPACE) + t = t->next; + brace = FALSE; + if (t && t->type == TOK_OTHER && !strcmp(t->text, "{")) + brace = TRUE; + (*params)[(*nparam)++] = t; + while (t && (t->type != TOK_OTHER || + strcmp(t->text, brace ? "}" : ","))) + t = t->next; + if (t) { /* got a comma/brace */ + t = t->next; + if (brace) { + /* + * Now we've found the closing brace, look further + * for the comma. + */ + if (t && t->type == TOK_WHITESPACE) + t = t->next; + if (t && (t->type != TOK_OTHER || strcmp(t->text, ","))) { + error (ERR_NONFATAL|ERR_OFFBY1, + "braces do not enclose all of macro parameter"); + while (t && (t->type != TOK_OTHER || + strcmp(t->text, ","))) + t = t->next; + } + if (t) + t = t->next; /* eat the comma */ + } + } + else /* got EOL */ + break; + } +} + +/* + * Find out if a line contains a preprocessor directive, and deal + * with it if so. + * + * If a directive _is_ found, the line will never be de-tokenised + * as is, so we have carte blanche to fiddle with it and adjust + * token values. + * + * Return values go like this: + * + * bit 0 is set if a directive was found + * bit 1 is set if a blank line should be emitted + * bit 2 is set if a re-sync line number comment should be emitted + * + * (bits 1 and 2 are mutually exclusive in that the rest of the + * preprocessor doesn't guarantee to be able to handle the case in + * which both are set) + */ +static int do_directive (Token *tline) { + static char *directives[] = { + "%clear", "%define", "%elifctx", "%elifdef", "%elifnctx", + "%elifndef", "%else", "%endif", "%endm", "%endmacro", "%error", + "%idefine", "%ifctx", "%ifdef", "%ifnctx", "%ifndef", "%imacro", + "%include", "%line", "%macro", "%pop", "%push", "%repl" + }; + enum { + PP_CLEAR, PP_DEFINE, PP_ELIFCTX, PP_ELIFDEF, PP_ELIFNCTX, + PP_ELIFNDEF, PP_ELSE, PP_ENDIF, PP_ENDM, PP_ENDMACRO, PP_ERROR, + PP_IDEFINE, PP_IFCTX, PP_IFDEF, PP_IFNCTX, PP_IFNDEF, PP_IMACRO, + PP_INCLUDE, PP_LINE, PP_MACRO, PP_POP, PP_PUSH, PP_REPL + }; + int i, j, k, m, nparam; + char *p, *mname; + Include *inc; + Context *ctx; + Cond *cond; + SMacro *smac, **smhead; + MMacro *mmac; + Token *t, *tt, *param_start, *macro_start, *last; + + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || tline->type != TOK_PREPROC_ID || + (tline->text[1] == '%' || tline->text[1] == '$')) + return 0; + + i = -1; + j = sizeof(directives)/sizeof(*directives); + while (j-i > 1) { + k = (j+i) / 2; + m = nasm_stricmp(tline->text, directives[k]); + if (m == 0) { + i = k; + j = -2; + break; + } else if (m < 0) { + j = k; + } else + i = k; + } + + /* + * If we're in a non-emitting branch of a condition construct, + * we should ignore all directives except for condition + * directives. + */ + if (istk->conds && !emitting(istk->conds->state) && + i != PP_IFCTX && i != PP_IFDEF && i != PP_IFNCTX && i != PP_IFNDEF && + i!=PP_ELIFCTX && i!=PP_ELIFDEF && i!=PP_ELIFNCTX && i!=PP_ELIFNDEF && + i != PP_ELSE && i != PP_ENDIF) + return 0; + + /* + * If we're defining a macro, we should ignore all directives + * except for %macro/%imacro (which generate an error) and + * %endm/%endmacro. + */ + if (defining && i != PP_MACRO && i != PP_IMACRO && + i != PP_ENDMACRO && i != PP_ENDM) + return 0; + + if (j != -2) { + error(ERR_NONFATAL|ERR_OFFBY1, "unknown preprocessor directive `%s'", + tline->text); + return 0; /* didn't get it */ + } + + switch (i) { + + case PP_CLEAR: + if (tline->next) + error(ERR_WARNING|ERR_OFFBY1, + "trailing garbage after `%%pop' ignored"); + for (j=0; j<NHASH; j++) { + while (mmacros[j]) { + MMacro *m = mmacros[j]; + mmacros[j] = mmacros[j]->next; + nasm_free (m->name); + free_tlist (m->dlist); + free_llist (m->expansion); + nasm_free (m); + } + while (smacros[j]) { + SMacro *s = smacros[j]; + smacros[j] = smacros[j]->next; + nasm_free (s->name); + free_tlist (s->expansion); + nasm_free (s); + } + } + return 3; + + case PP_INCLUDE: + tline = tline->next; + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || tline->type != TOK_STRING) { + error(ERR_NONFATAL|ERR_OFFBY1, "`%%include' expects a file name"); + return 3; /* but we did _something_ */ + } + if (tline->next) + error(ERR_WARNING|ERR_OFFBY1, + "trailing garbage after `%%include' ignored"); + p = tline->text+1; /* point past the quote to the name */ + p[strlen(p)-1] = '\0'; /* remove the trailing quote */ + inc = nasm_malloc(sizeof(Include)); + inc->next = istk; + inc->conds = NULL; + inc->fp = fopen(p, "r"); + inc->fname = nasm_strdup(p); + inc->lineno = inc->lineinc = 1; + inc->expansion = NULL; + if (!inc->fp) + error (ERR_FATAL|ERR_OFFBY1, + "unable to open include file `%s'", p); + istk = inc; + return 5; + + case PP_PUSH: + tline = tline->next; + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || tline->type != TOK_ID) { + error(ERR_NONFATAL|ERR_OFFBY1, + "`%%push' expects a context identifier"); + return 3; /* but we did _something_ */ + } + if (tline->next) + error(ERR_WARNING|ERR_OFFBY1, + "trailing garbage after `%%push' ignored"); + ctx = nasm_malloc(sizeof(Context)); + ctx->next = cstk; + ctx->localmac = NULL; + ctx->name = nasm_strdup(tline->text); + ctx->number = unique++; + cstk = ctx; + break; + + case PP_REPL: + tline = tline->next; + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || tline->type != TOK_ID) { + error(ERR_NONFATAL|ERR_OFFBY1, + "`%%repl' expects a context identifier"); + return 3; /* but we did _something_ */ + } + if (tline->next) + error(ERR_WARNING|ERR_OFFBY1, + "trailing garbage after `%%repl' ignored"); + if (!cstk) + error(ERR_NONFATAL|ERR_OFFBY1, + "`%%repl': context stack is empty"); + else { + nasm_free (cstk->name); + cstk->name = nasm_strdup(tline->text); + } + break; + + case PP_POP: + if (tline->next) + error(ERR_WARNING|ERR_OFFBY1, + "trailing garbage after `%%pop' ignored"); + if (!cstk) + error(ERR_NONFATAL|ERR_OFFBY1, + "`%%pop': context stack is already empty"); + else + ctx_pop(); + break; + + case PP_ERROR: + tline = tline->next; + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || tline->type != TOK_STRING) { + error(ERR_NONFATAL|ERR_OFFBY1, + "`%%error' expects an error string"); + return 3; /* but we did _something_ */ + } + if (tline->next) + error(ERR_WARNING|ERR_OFFBY1, + "trailing garbage after `%%error' ignored"); + p = tline->text+1; /* point past the quote to the name */ + p[strlen(p)-1] = '\0'; /* remove the trailing quote */ + error(ERR_NONFATAL|ERR_OFFBY1, "user error: %s", p); + break; + + case PP_IFCTX: + case PP_IFNCTX: + tline = tline->next; + if (istk->conds && !emitting(istk->conds->state)) + j = COND_NEVER; + else { + j = FALSE; /* have we matched yet? */ + if (!cstk) + error(ERR_FATAL|ERR_OFFBY1, + "`%%if%sctx': context stack is empty", + (i==PP_IFNCTX ? "n" : "")); + else while (tline) { + if (tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || tline->type != TOK_ID) { + error(ERR_NONFATAL|ERR_OFFBY1, + "`%%ifctx' expects context identifiers"); + return 3; /* but we did _something_ */ + } + if (!nasm_stricmp(tline->text, cstk->name)) + j = TRUE; + tline = tline->next; + } + if (i == PP_IFNCTX) + j = !j; + j = (j ? COND_IF_TRUE : COND_IF_FALSE); + } + cond = nasm_malloc(sizeof(Cond)); + cond->next = istk->conds; + cond->state = j; + istk->conds = cond; + return 1; + + case PP_ELIFCTX: + case PP_ELIFNCTX: + tline = tline->next; + if (!istk->conds) + error(ERR_FATAL|ERR_OFFBY1, "`%%elif%sctx': no matching `%%if'", + (i==PP_ELIFNCTX ? "n" : "")); + if (emitting(istk->conds->state) || istk->conds->state == COND_NEVER) + istk->conds->state = COND_NEVER; + else { + j = FALSE; /* have we matched yet? */ + if (!cstk) + error(ERR_FATAL|ERR_OFFBY1, + "`%%elif%sctx': context stack is empty", + (i==PP_ELIFNCTX ? "n" : "")); + else while (tline) { + if (tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || tline->type != TOK_ID) { + error(ERR_NONFATAL|ERR_OFFBY1, + "`%%elif%sctx' expects context identifiers", + (i==PP_ELIFNCTX ? "n" : "")); + return 3; /* but we did _something_ */ + } + if (!nasm_stricmp(tline->text, cstk->name)) + j = TRUE; + tline = tline->next; + } + if (i == PP_ELIFNCTX) + j = !j; + istk->conds->state = (j ? COND_IF_TRUE : COND_IF_FALSE); + } + return 1; + + case PP_IFDEF: + case PP_IFNDEF: + tline = tline->next; + if (istk->conds && !emitting(istk->conds->state)) + j = COND_NEVER; + else { + j = FALSE; /* have we matched yet? */ + while (tline) { + if (tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || (tline->type != TOK_ID && + (tline->type != TOK_PREPROC_ID || + tline->text[1] != '$'))) { + error(ERR_NONFATAL|ERR_OFFBY1, + "`%%if%sdef' expects macro identifiers", + (i==PP_ELIFNDEF ? "n" : "")); + return 3; /* but we did _something_ */ + } + if (smacro_defined(tline->text, 0, NULL)) + j = TRUE; + tline = tline->next; + } + if (i == PP_IFNDEF) + j = !j; + j = (j ? COND_IF_TRUE : COND_IF_FALSE); + } + cond = nasm_malloc(sizeof(Cond)); + cond->next = istk->conds; + cond->state = j; + istk->conds = cond; + return 1; + + case PP_ELIFDEF: + case PP_ELIFNDEF: + tline = tline->next; + if (!istk->conds) + error(ERR_FATAL|ERR_OFFBY1, "`%%elif%sctx': no matching `%%if'", + (i==PP_ELIFNCTX ? "n" : "")); + if (emitting(istk->conds->state) || istk->conds->state == COND_NEVER) + istk->conds->state = COND_NEVER; + else { + j = FALSE; /* have we matched yet? */ + while (tline) { + if (tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || (tline->type != TOK_ID && + (tline->type != TOK_PREPROC_ID || + tline->text[1] != '$'))) { + error(ERR_NONFATAL|ERR_OFFBY1, + "`%%elif%sdef' expects macro identifiers", + (i==PP_ELIFNDEF ? "n" : "")); + return 3; /* but we did _something_ */ + } + if (smacro_defined(tline->text, 0, NULL)) + j = TRUE; + tline = tline->next; + } + if (i == PP_ELIFNDEF) + j = !j; + istk->conds->state = (j ? COND_IF_TRUE : COND_IF_FALSE); + } + return 1; + + case PP_ELSE: + if (tline->next) + error(ERR_WARNING|ERR_OFFBY1, + "trailing garbage after `%%else' ignored"); + if (!istk->conds) + error(ERR_FATAL|ERR_OFFBY1, + "`%%else': no matching `%%if'"); + if (emitting(istk->conds->state) || istk->conds->state == COND_NEVER) + istk->conds->state = COND_ELSE_FALSE; + else + istk->conds->state = COND_ELSE_TRUE; + return 1; + + case PP_ENDIF: + if (tline->next) + error(ERR_WARNING|ERR_OFFBY1, + "trailing garbage after `%%endif' ignored"); + if (!istk->conds) + error(ERR_FATAL|ERR_OFFBY1, + "`%%endif': no matching `%%if'"); + cond = istk->conds; + istk->conds = cond->next; + nasm_free (cond); + return 5; + + case PP_MACRO: + case PP_IMACRO: + if (defining) + error (ERR_FATAL|ERR_OFFBY1, + "`%%%smacro': already defining a macro", + (i == PP_IMACRO ? "i" : "")); + tline = tline->next; + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || tline->type != TOK_ID) { + error (ERR_NONFATAL|ERR_OFFBY1, + "`%%%smacro' expects a macro name", + (i == PP_IMACRO ? "i" : "")); + return 3; + } + defining = nasm_malloc(sizeof(MMacro)); + defining->name = nasm_strdup(tline->text); + defining->casesense = (i == PP_MACRO); + defining->plus = FALSE; + defining->in_progress = FALSE; + tline = tline->next; + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || tline->type != TOK_NUMBER) { + error (ERR_NONFATAL|ERR_OFFBY1, + "`%%%smacro' expects a parameter count", + (i == PP_IMACRO ? "i" : "")); + defining->nparam_min = defining->nparam_max = 0; + } else { + defining->nparam_min = defining->nparam_max = + readnum(tline->text, &j); + if (j) + error (ERR_NONFATAL|ERR_OFFBY1, + "unable to parse parameter count `%s'", tline->text); + } + if (tline && tline->next && tline->next->type == TOK_OTHER && + !strcmp(tline->next->text, "-")) { + tline = tline->next->next; + if (!tline || tline->type != TOK_NUMBER) + error (ERR_NONFATAL|ERR_OFFBY1, + "`%%%smacro' expects a parameter count after `-'", + (i == PP_IMACRO ? "i" : "")); + else { + defining->nparam_max = readnum(tline->text, &j); + if (j) + error (ERR_NONFATAL|ERR_OFFBY1, + "unable to parse parameter count `%s'", + tline->text); + if (defining->nparam_min > defining->nparam_max) + error (ERR_NONFATAL|ERR_OFFBY1, + "minimum parameter count exceeds maximum"); + } + } + if (tline && tline->next && tline->next->type == TOK_OTHER && + !strcmp(tline->next->text, "+")) { + tline = tline->next; + defining->plus = TRUE; + } + mmac = mmacros[hash(defining->name)]; + while (mmac) { + if (!strcmp(mmac->name, defining->name) && + (mmac->nparam_min<=defining->nparam_max || defining->plus) && + (defining->nparam_min<=mmac->nparam_max || mmac->plus)) { + error (ERR_WARNING|ERR_OFFBY1, + "redefining multi-line macro `%s'", defining->name); + break; + } + mmac = mmac->next; + } + /* + * Handle default parameters. + */ + if (tline && tline->next) { + int np, want_np; + + defining->dlist = tline->next; + tline->next = NULL; + count_mmac_params (defining->dlist, &np, &defining->defaults); + want_np = defining->nparam_max - defining->nparam_min; + defining->defaults = nasm_realloc (defining->defaults, + want_np*sizeof(Token *)); + while (np < want_np) + defining->defaults[np++] = NULL; + } else { + defining->dlist = NULL; + defining->defaults = NULL; + } + defining->expansion = NULL; + return 1; + + case PP_ENDM: + case PP_ENDMACRO: + if (!defining) { + error (ERR_NONFATAL|ERR_OFFBY1, "`%s': not defining a macro", + tline->text); + return 3; + } + k = hash(defining->name); + defining->next = mmacros[k]; + mmacros[k] = defining; + defining = NULL; + return 5; + + case PP_DEFINE: + case PP_IDEFINE: + tline = tline->next; + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || (tline->type != TOK_ID && + (tline->type != TOK_PREPROC_ID || + tline->text[1] != '$'))) { + error (ERR_NONFATAL|ERR_OFFBY1, + "`%%%sdefine' expects a macro identifier", + (i == PP_IDEFINE ? "i" : "")); + return 3; + } + mname = tline->text; + if (tline->type == TOK_ID) { + p = tline->text; + smhead = &smacros[hash(mname)]; + } else { + ctx = get_ctx (tline->text); + if (ctx == NULL) + return 3; + else { + p = tline->text+1; + p += strspn(p, "$"); + smhead = &ctx->localmac; + } + } + last = tline; + param_start = tline = tline->next; + nparam = 0; + if (tline && tline->type == TOK_OTHER && !strcmp(tline->text, "(")) { + /* + * This macro has parameters. + */ + + tline = tline->next; + while (1) { + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline) { + error (ERR_NONFATAL|ERR_OFFBY1, + "parameter identifier expected"); + return 3; + } + if (tline->type != TOK_ID) { + error (ERR_NONFATAL|ERR_OFFBY1, + "`%s': parameter identifier expected", + tline->text); + return 3; + } + tline->type = TOK_SMAC_PARAM + nparam++; + tline = tline->next; + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (tline && tline->type == TOK_OTHER && + !strcmp(tline->text, ",")) { + tline = tline->next; + continue; + } + if (!tline || tline->type != TOK_OTHER || + strcmp(tline->text, ")")) { + error (ERR_NONFATAL|ERR_OFFBY1, + "`)' expected to terminate macro template"); + return 3; + } + break; + } + last = tline; + tline = tline->next; + } + if (tline && tline->type == TOK_WHITESPACE) + last = tline, tline = tline->next; + macro_start = NULL; + last->next = NULL; + t = tline; + while (t) { + if (t->type == TOK_ID) { + for (tt = param_start; tt; tt = tt->next) + if (tt->type >= TOK_SMAC_PARAM && + !strcmp(tt->text, t->text)) + t->type = tt->type; + } + tt = t->next; + t->next = macro_start; + macro_start = t; + t = tt; + } + /* + * Good. We now have a macro name, a parameter count, and a + * token list (in reverse order) for an expansion. We ought + * to be OK just to create an SMacro, store it, and let + * tlist_free have the rest of the line (which we have + * carefully re-terminated after chopping off the expansion + * from the end). + */ + if (smacro_defined (mname, nparam, &smac)) { + if (!smac) + error (ERR_WARNING|ERR_OFFBY1, + "single-line macro `%s' defined both with and" + " without parameters", mname); + else { + /* + * We're redefining, so we have to take over an + * existing SMacro structure. This means freeing + * what was already in it. + */ + nasm_free (smac->name); + free_tlist (smac->expansion); + } + } else { + smac = nasm_malloc(sizeof(SMacro)); + smac->next = *smhead; + *smhead = smac; + } + smac->name = nasm_strdup(p); + smac->casesense = (i == PP_DEFINE); + smac->nparam = nparam; + smac->expansion = macro_start; + smac->in_progress = FALSE; + return 3; + + case PP_LINE: + /* + * Syntax is `%line nnn[+mmm] [filename]' + */ + tline = tline->next; + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || tline->type != TOK_NUMBER) { + error (ERR_NONFATAL|ERR_OFFBY1, "`%%line' expects line number"); + return 3; + } + k = readnum(tline->text, &j); + m = 1; + tline = tline->next; + if (tline && tline->type == TOK_OTHER && !strcmp(tline->text, "+")) { + tline = tline->next; + if (!tline || tline->type != TOK_NUMBER) { + error (ERR_NONFATAL|ERR_OFFBY1, + "`%%line' expects line increment"); + return 3; + } + m = readnum(tline->text, &j); + tline = tline->next; + } + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + istk->lineno = k; + istk->lineinc = m; + if (tline) { + char *s = detoken(tline); + nasm_free (istk->fname); + istk->fname = s; + } + return 5; + + default: + error(ERR_FATAL|ERR_OFFBY1, + "preprocessor directive `%s' not yet implemented", + directives[k]); + break; + } + return 3; +} + +/* + * Expand all single-line macro calls made in the given line. + * Return the expanded version of the line. The original is deemed + * to be destroyed in the process. (In reality we'll just move + * Tokens from input to output a lot of the time, rather than + * actually bothering to destroy and replicate.) + */ +static Token *expand_smacro (Token *tline) { + Token *t, *tt, *mstart, **tail, *thead; + SMacro *head, *m; + Token **params; + int *paramsize; + int nparam, sparam, brackets; + char *p; + + tail = &thead; + thead = NULL; + + while (tline) { + while (tline && tline->type != TOK_ID && + (tline->type != TOK_PREPROC_ID || tline->text[1] != '$')) { + if (tline->type == TOK_SMAC_END) { + tline->mac->in_progress = FALSE; + t = tline; + tline = tline->next; + nasm_free (t); + } else { + t = *tail = tline; + tline = tline->next; + t->mac = NULL; + t->next = NULL; + tail = &t->next; + if (t->type == TOK_PS_OTHER) { + /* + * If we see a PS_OTHER, we must at the very + * least restore its correct token type. We + * should also check for a %$ token, since this + * is the point at which we expand context- + * local labels. + */ + t->type = TOK_ID; + if (t->text[0] == '%' && t->text[1] == '$') { + Context *c = get_ctx (t->text); + char *p, *q, buffer[40]; + + if (c) { + q = t->text+1; + q += strspn(q, "$"); + sprintf(buffer, "macro.%lu.", c->number); + p = nasm_malloc (strlen(buffer)+strlen(q)+1); + strcpy (p, buffer); + strcat (p, q); + nasm_free (t->text); + t->text = p; + } + } + } + } + } + if (!tline) + break; + /* + * We've hit an identifier. As in is_mmacro below, we first + * check whether the identifier is a single-line macro at + * all, then think about checking for parameters if + * necessary. + */ + if (tline->type == TOK_ID) { + head = smacros[hash(tline->text)]; + p = tline->text; + } else { + Context *ctx = get_ctx (tline->text); + if (ctx) { + p = tline->text+1; + p += strspn(p, "$"); + head = ctx->localmac; + } else { + tline->type = TOK_OTHER; /* so it will get copied above */ + continue; + } + } + for (m = head; m; m = m->next) + if (!mstrcmp(m->name, p, m->casesense)) + break; + if (!m) { + /* + * Didn't find one: this can't be a macro call. Copy it + * through and ignore it. + */ + tline->type = TOK_PS_OTHER; /* so it will get copied above */ + continue; + } + mstart = tline; + if (m->nparam == 0) { + /* + * Simple case: the macro is parameterless. Discard the + * one token that the macro call took, and push the + * expansion back on the to-do stack. + */ + params = NULL; + paramsize = NULL; + } else { + /* + * Complicated case: at least one macro with this name + * exists and takes parameters. We must find the + * parameters in the call, count them, find the SMacro + * that corresponds to that form of the macro call, and + * substitute for the parameters when we expand. What a + * pain. + */ + nparam = sparam = 0; + params = NULL; + paramsize = NULL; + tline = tline->next; + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline || tline->type != TOK_OTHER || + strcmp(tline->text, "(")) { + /* + * This macro wasn't called with parameters: ignore + * the call. (Behaviour borrowed from gnu cpp.) + */ + tline = mstart; + tline->type = TOK_PS_OTHER; + continue; + } + tline = tline->next; + while (1) { + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (!tline) { + error(ERR_NONFATAL|ERR_OFFBY1, + "macro call expects terminating `)'"); + break; + } + if (nparam >= sparam) { + sparam += PARAM_DELTA; + params = nasm_realloc (params, sparam*sizeof(Token *)); + paramsize = nasm_realloc (paramsize, sparam*sizeof(int)); + } + params[nparam] = tline; + paramsize[nparam] = 0; + brackets = 0; + if (tline && tline->type == TOK_OTHER && + !strcmp(tline->text, "{")) { + params[nparam] = tline = tline->next; + while (tline && (brackets > 0 || + tline->type != TOK_OTHER || + strcmp(tline->text, "}"))) { + tline = tline->next; + paramsize[nparam]++; + } + tline = tline->next; + if (tline && tline->type == TOK_WHITESPACE) + tline = tline->next; + if (tline && (tline->type != TOK_OTHER || + (strcmp(tline->text, ")") && + strcmp(tline->text, ",")))) { + error (ERR_NONFATAL|ERR_OFFBY1, "braces do not " + "enclose all of macro parameter"); + } + if (tline && tline->type == TOK_OTHER && + !strcmp(tline->text, ",")) + tline = tline->next; + } else { + while (tline && (brackets > 0 || + tline->type != TOK_OTHER || + (strcmp(tline->text, ",") && + strcmp(tline->text, ")")))) { + if (tline->type == TOK_OTHER && !tline->text[1]) + brackets += (tline->text[0] == '(' ? 1 : + tline->text[0] == ')' ? -1 : 0); + tline = tline->next; + paramsize[nparam]++; + } + } + nparam++; + if (tline && !strcmp(tline->text, ")")) + break; + if (tline && !strcmp(tline->text, ",")) + tline = tline->next; + } + while (m && m->nparam != nparam) { + while ( (m = m->next) ) + if (!strcmp(m->name, mstart->text)) + break; + } + if (!m) { + error (ERR_WARNING|ERR_OFFBY1, + "macro `%s' exists, but not taking %d parameters", + mstart->text, nparam); + nasm_free (params); + nasm_free (paramsize); + tline = mstart; + tline->type = TOK_PS_OTHER; + continue; + } + if (m->in_progress) { + error (ERR_NONFATAL, "self-reference in single-line macro" + " `%s'", mstart->text); + nasm_free (params); + nasm_free (paramsize); + tline = mstart; + tline->type = TOK_PS_OTHER; + continue; + } + } + /* + * Expand the macro: we are placed on the last token of the + * call, so that we can easily split the call from the + * following tokens. We also start by pushing an SMAC_END + * token for the cycle removal. + */ + t = tline; + tline = tline->next; + t->next = NULL; + tt = nasm_malloc(sizeof(Token)); + tt->type = TOK_SMAC_END; + tt->text = NULL; + tt->mac = m; + m->in_progress = TRUE; + tt->next = tline; + tline = tt; + for (t = m->expansion; t; t = t->next) { + if (t->type >= TOK_SMAC_PARAM) { + Token *pcopy = tline, **ptail = &pcopy; + Token *ttt, *pt; + int i; + + ttt = params[t->type - TOK_SMAC_PARAM]; + for (i=0; i<paramsize[t->type-TOK_SMAC_PARAM]; i++) { + pt = *ptail = nasm_malloc(sizeof(Token)); + pt->next = tline; + ptail = &pt->next; + pt->text = nasm_strdup(ttt->text); + pt->type = ttt->type; + pt->mac = NULL; + ttt = ttt->next; + } + tline = pcopy; + } else { + tt = nasm_malloc(sizeof(Token)); + tt->type = t->type; + tt->text = nasm_strdup(t->text); + tt->mac = NULL; + tt->next = tline; + tline = tt; + } + } + + /* + * Having done that, get rid of the macro call, and clean + * up the parameters. + */ + nasm_free (params); + nasm_free (paramsize); + free_tlist (mstart); + } + + return thead; +} + +/* + * Ensure that a macro parameter contains a condition code and + * nothing else. Return the condition code index if so, or -1 + * otherwise. + */ +static int find_cc (Token *t) { + Token *tt; + int i, j, k, m; + + if (t && t->type == TOK_WHITESPACE) + t = t->next; + if (t->type != TOK_ID) + return -1; + tt = t->next; + if (tt && tt->type == TOK_WHITESPACE) + tt = tt->next; + if (tt && (tt->type != TOK_OTHER || strcmp(tt->text, ","))) + return -1; + + i = -1; + j = sizeof(conditions)/sizeof(*conditions); + while (j-i > 1) { + k = (j+i) / 2; + m = nasm_stricmp(t->text, conditions[k]); + if (m == 0) { + i = k; + j = -2; + break; + } else if (m < 0) { + j = k; + } else + i = k; + } + if (j != -2) + return -1; + return i; +} + +/* + * Determine whether the given line constitutes a multi-line macro + * call, and return the MMacro structure called if so. Doesn't have + * to check for an initial label - that's taken care of in + * expand_mmacro - but must check numbers of parameters. Guaranteed + * to be called with tline->type == TOK_ID, so the putative macro + * name is easy to find. + */ +static MMacro *is_mmacro (Token *tline, Token ***params_array) { + MMacro *head, *m; + Token **params; + int nparam; + + head = mmacros[hash(tline->text)]; + + /* + * Efficiency: first we see if any macro exists with the given + * name. If not, we can return NULL immediately. _Then_ we + * count the parameters, and then we look further along the + * list if necessary to find the proper MMacro. + */ + for (m = head; m; m = m->next) + if (!mstrcmp(m->name, tline->text, m->casesense)) + break; + if (!m) + return NULL; + + /* + * OK, we have a potential macro. Count and demarcate the + * parameters. + */ + count_mmac_params (tline->next, &nparam, ¶ms); + + /* + * So we know how many parameters we've got. Find the MMacro + * structure that handles this number. + */ + while (m) { + if (m->nparam_min <= nparam && (m->plus || nparam <= m->nparam_max)) { + /* + * This one is right. Just check if cycle removal + * prohibits us using it before we actually celebrate... + */ + if (m->in_progress) { + error (ERR_NONFATAL|ERR_OFFBY1, + "self-reference in multi-line macro `%s'", + m->name); + nasm_free (params); + return NULL; + } + /* + * It's right, and we can use it. Add its default + * parameters to the end of our list if necessary. + */ + params = nasm_realloc (params, (m->nparam_max+1)*sizeof(*params)); + if (m->defaults) { + while (nparam < m->nparam_max) { + params[nparam] = m->defaults[nparam - m->nparam_min]; + nparam++; + } + } else { + while (nparam < m->nparam_max) { + params[nparam] = NULL; + nparam++; + } + } + /* + * Then terminate the parameter list, and leave. + */ + params[m->nparam_max] = NULL; + *params_array = params; + return m; + } + /* + * This one wasn't right: look for the next one with the + * same name. + */ + for (m = m->next; m; m = m->next) + if (!mstrcmp(m->name, tline->text, m->casesense)) + break; + } + + /* + * After all that, we didn't find one with the right number of + * parameters. Issue a warning, and fail to expand the macro. + */ + error (ERR_WARNING|ERR_OFFBY1, + "macro `%s' exists, but not taking %d parameters", + tline->text, nparam); + nasm_free (params); + return NULL; +} + +/* + * Expand the multi-line macro call made by the given line, if + * there is one to be expanded. If there is, push the expansion on + * istk->expansion and return 1 or 2, as according to whether a + * line sync is needed (2 if it is). Otherwise return 0. + */ +static int expand_mmacro (Token *tline) { + Token *label = NULL, **params, *t, *tt, *ttt, *last = NULL; + MMacro *m = NULL; + Line *l, *ll; + int i, n, nparam, *paramlen; + int need_sync = FALSE; + + t = tline; + if (t && t->type == TOK_WHITESPACE) + t = t->next; + if (t && t->type == TOK_ID) { + m = is_mmacro (t, ¶ms); + if (!m) { + /* + * We have an id which isn't a macro call. We'll assume + * it might be a label; we'll also check to see if a + * colon follows it. Then, if there's another id after + * that lot, we'll check it again for macro-hood. + */ + last = t, t = t->next; + if (t && t->type == TOK_WHITESPACE) + last = t, t = t->next; + if (t && t->type == TOK_OTHER && !strcmp(t->text, ":")) + last = t, t = t->next; + if (t && t->type == TOK_WHITESPACE) + last = t, t = t->next; + if (t && t->type == TOK_ID) { + m = is_mmacro(t, ¶ms); + if (m) { + last->next = NULL; + label = tline; + tline = t; + } + } + } + } + if (!m) + return 0; + + /* + * If we're not already inside another macro expansion, we'd + * better push a line synchronisation to ensure we stay put on + * line numbering. + */ + if (!istk->expansion) + need_sync = TRUE; + + /* + * Fix up the parameters: this involves stripping leading and + * trailing whitespace, then stripping braces if they are + * present. + */ + for (nparam = 0; params[nparam]; nparam++); + paramlen = nparam ? nasm_malloc(nparam*sizeof(*paramlen)) : NULL; + + for (i = 0; params[i]; i++) { + int brace = FALSE; + int comma = !m->plus; + + t = params[i]; + if (t && t->type == TOK_WHITESPACE) + t = t->next; + if (t && t->type == TOK_OTHER && !strcmp(t->text, "{")) + t = t->next, brace = TRUE, comma = FALSE; + params[i] = t; + paramlen[i] = 0; + while (t) { + if (!t) /* end of param because EOL */ + break; + if (comma && t->type == TOK_OTHER && !strcmp(t->text, ",")) + break; /* ... because we have hit a comma */ + if (comma && t->type == TOK_WHITESPACE && + t->next->type == TOK_OTHER && !strcmp(t->next->text, ",")) + break; /* ... or a space then a comma */ + if (brace && t->type == TOK_OTHER && !strcmp(t->text, "}")) + break; /* ... or a brace */ + t = t->next; + paramlen[i]++; + } + } + + /* + * OK, we have a MMacro structure together with a set of + * parameters. We must now go through the expansion and push + * _copies_ of each Line on to istk->expansion, having first + * substituted for most % tokens (%1, %+1, %-1, %%foo). Note + * that %$bar, %$$baz, %$$$quux, and so on, do not get + * substituted here but rather have to wait until the + * single-line macro substitution process. This is because they + * don't just crop up in macro definitions, but can appear + * anywhere they like. + * + * First, push an end marker on to istk->expansion, and mark + * this macro as in progress. + */ + ll = nasm_malloc(sizeof(Line)); + ll->next = istk->expansion; + ll->finishes = m; + ll->first = NULL; + istk->expansion = ll; + m->in_progress = TRUE; + for (l = m->expansion; l; l = l->next) { + Token **tail; + + ll = nasm_malloc(sizeof(Line)); + ll->next = istk->expansion; + ll->finishes = NULL; + tail = &ll->first; + + for (t = l->first; t; t = t->next) { + char *text; + int type = 0, cc; /* type = 0 to placate optimisers */ + char tmpbuf[30]; + + if (t->type == TOK_PREPROC_ID && + (t->text[1] == '+' || t->text[1] == '-' || + t->text[1] == '%' || + (t->text[1] >= '0' && t->text[1] <= '9'))) { + /* + * We have to make a substitution of one of the + * forms %1, %-1, %+1, %%foo. + */ + switch (t->text[1]) { + case '%': + type = TOK_ID; + sprintf(tmpbuf, "macro.%lu.", unique); + text = nasm_malloc(strlen(tmpbuf)+strlen(t->text+2)+1); + strcpy(text, tmpbuf); + strcat(text, t->text+2); + break; + case '-': + n = atoi(t->text+2)-1; + tt = params[n]; + cc = find_cc (tt); + if (cc == -1) { + error (ERR_NONFATAL|ERR_OFFBY1, + "macro parameter %d is not a condition code", + n+1); + text = NULL; + } else { + type = TOK_ID; + if (inverse_ccs[cc] == -1) { + error (ERR_NONFATAL|ERR_OFFBY1, + "condition code `%s' is not invertible", + conditions[cc]); + text = NULL; + } else + text = nasm_strdup(conditions[inverse_ccs[cc]]); + } + break; + case '+': + n = atoi(t->text+2)-1; + tt = params[n]; + cc = find_cc (tt); + if (cc == -1) { + error (ERR_NONFATAL|ERR_OFFBY1, + "macro parameter %d is not a condition code", + n+1); + text = NULL; + } else { + type = TOK_ID; + text = nasm_strdup(conditions[cc]); + } + break; + default: + n = atoi(t->text+1)-1; + if (n < nparam) { + ttt = params[n]; + for (i=0; i<paramlen[n]; i++) { + tt = *tail = nasm_malloc(sizeof(Token)); + tt->next = NULL; + tail = &tt->next; + tt->type = ttt->type; + tt->text = nasm_strdup(ttt->text); + tt->mac = NULL; + ttt = ttt->next; + } + } + text = NULL; /* we've done it here */ + break; + } + } else { + type = t->type; + text = nasm_strdup(t->text); + } + + if (text) { + tt = *tail = nasm_malloc(sizeof(Token)); + tt->next = NULL; + tail = &tt->next; + tt->type = type; + tt->text = text; + tt->mac = NULL; + } + } + + istk->expansion = ll; + } + + /* + * If we had a label, push it on the front of the first line of + * the macro expansion. + */ + if (label) { + last->next = istk->expansion->first; + istk->expansion->first = label; + } + + /* + * Clean up. + */ + unique++; + nasm_free (paramlen); + nasm_free (params); + free_tlist (tline); + + return need_sync ? 2 : 1; +} + +static void pp_reset (char *file, efunc errfunc) { + int h; + + error = errfunc; + cstk = NULL; + linesync = outline = NULL; + istk = nasm_malloc(sizeof(Include)); + istk->next = NULL; + istk->conds = NULL; + istk->expansion = NULL; + istk->fp = fopen(file, "r"); + istk->fname = nasm_strdup(file); + istk->lineno = istk->lineinc = 1; + if (!istk->fp) + error (ERR_FATAL|ERR_NOFILE, "unable to open input file `%s'", file); + defining = NULL; + for (h=0; h<NHASH; h++) { + mmacros[h] = NULL; + smacros[h] = NULL; + } + unique = 0; + stdmacpos = stdmac; +} + +static char *pp_getline (void) { + char *line; + Token *tline; + int ret; + + if (outline) { + line = outline; + outline = NULL; + return line; + } + + while (1) { + /* + * Fetch a tokenised line, either from the macro-expansion + * buffer or from the input file. + */ + tline = NULL; + while (istk->expansion && istk->expansion->finishes) { + Line *l = istk->expansion; + tline = l->first; + l->finishes->in_progress = FALSE; + istk->expansion = l->next; + nasm_free (l); + if (!istk->expansion) + line_sync(); + } + if (istk->expansion) { + Line *l = istk->expansion; + tline = l->first; + istk->expansion = l->next; + nasm_free (l); + if (!istk->expansion) + line_sync(); + } else { + line = read_line(); + while (!line) { + /* + * The current file has ended; work down the istk + * until we find a file we can read from. + */ + Include *i; + fclose(istk->fp); + if (istk->conds) + error(ERR_FATAL, "expected `%%endif' before end of file"); + i = istk; + istk = istk->next; + nasm_free (i->fname); + nasm_free (i); + if (!istk) + return NULL; + else + line_sync(); + line = read_line(); + } + line = prepreproc(line); + tline = tokenise(line); + nasm_free (line); + } + + /* + * Check the line to see if it's a preprocessor directive. + */ + ret = do_directive(tline); + if (ret & 1) { + free_tlist (tline); + if (ret & 4) + line_sync(); + if ((ret & 2) && !stdmacpos) {/* give a blank line to the output */ + outline = nasm_strdup(""); + break; + } + else + continue; + } else if (defining) { + /* + * We're defining a multi-line macro. We emit nothing + * at all, not even a blank line (when we finish + * defining the macro, we'll emit a line-number + * directive so that we keep sync properly), and just + * shove the tokenised line on to the macro definition. + */ + Line *l = nasm_malloc(sizeof(Line)); + l->next = defining->expansion; + l->first = tline; + l->finishes = FALSE; + defining->expansion = l; + continue; + } else if (istk->conds && !emitting(istk->conds->state)) { + /* + * We're in a non-emitting branch of a condition block. + * Emit nothing at all, not even a blank line: when we + * emerge from the condition we'll give a line-number + * directive so we keep our place correctly. + */ + free_tlist(tline); + continue; + } else { + tline = expand_smacro(tline); + ret = expand_mmacro(tline); + if (!ret) { + /* + * De-tokenise the line again, and emit it. + */ + line = detoken(tline); + free_tlist (tline); + outline = line; + break; + } else { + if (ret == 2) + line_sync(); + continue; /* expand_mmacro calls free_tlist */ + } + } + } + + /* + * Once we're out of this loop, outline _must_ be non-NULL. The + * only question is whether linesync is NULL or not. + */ + if (linesync) { + line = linesync; + linesync = NULL; + } else { + line = outline; + outline = NULL; + } + return line; +} + +static void pp_cleanup (void) { + int h; + + if (defining) { + error (ERR_NONFATAL, "end of file while still defining macro `%s'", + defining->name); + nasm_free (defining->name); + free_tlist (defining->dlist); + free_llist (defining->expansion); + nasm_free (defining); + } + nasm_free (linesync); /* might just be necessary */ + nasm_free (outline); /* really shouldn't be necessary */ + while (cstk) + ctx_pop(); + for (h=0; h<NHASH; h++) { + while (mmacros[h]) { + MMacro *m = mmacros[h]; + mmacros[h] = mmacros[h]->next; + nasm_free (m->name); + free_tlist (m->dlist); + free_llist (m->expansion); + nasm_free (m); + } + while (smacros[h]) { + SMacro *s = smacros[h]; + smacros[h] = smacros[h]->next; + nasm_free (s->name); + free_tlist (s->expansion); + nasm_free (s); + } + } + while (istk) { + Include *i = istk; + istk = istk->next; + fclose(i->fp); + nasm_free (i->fname); + nasm_free (i); + } + while (cstk) + ctx_pop(); +} + +Preproc nasmpp = { + pp_reset, + pp_getline, + pp_cleanup +}; diff --git a/preproc.h b/preproc.h new file mode 100644 index 0000000..550a66e --- /dev/null +++ b/preproc.h @@ -0,0 +1,14 @@ +/* preproc.h header file for preproc.c + * + * The Netwide Assembler is copyright (C) 1996 Simon Tatham and + * Julian Hall. All rights reserved. The software is + * redistributable under the licence given in the file "Licence" + * distributed in the NASM archive. + */ + +#ifndef NASM_PREPROC_H +#define NASM_PREPROC_H + +extern Preproc nasmpp; + +#endif diff --git a/rdoff/Makefile b/rdoff/Makefile index 2e55dde..78eca4e 100644 --- a/rdoff/Makefile +++ b/rdoff/Makefile @@ -9,19 +9,19 @@ # portably). CC = gcc -CCFLAGS = -c -O -g -Wall -ansi -pedantic -I.. +CCFLAGS = -c -g -Wall -ansi -pedantic -I.. LINK = gcc -LINKFLAGS = -o +LINKFLAGS = -g -o DLINKFLAGS = -o -LIBRARIES = +LIBRARIES = STRIP = strip -LDRDFLIBS = rdoff.o ../nasmlib.o symtab.o collectn.o +LDRDFLIBS = rdoff.o ../nasmlib.o symtab.o collectn.o rdlib.o RDXLIBS = rdoff.o rdfload.o symtab.o collectn.o .c.o: $(CC) $(CCFLAGS) $*.c -all : rdfdump ldrdf rdx +all : rdfdump ldrdf rdx rdflib rdf2bin rdf2com rdfdump : rdfdump.o $(LINK) $(LINKFLAGS) rdfdump rdfdump.o @@ -29,15 +29,25 @@ ldrdf : ldrdf.o $(LDRDFLIBS) $(LINK) $(LINKFLAGS) ldrdf ldrdf.o $(LDRDFLIBS) rdx : rdx.o $(RDXLIBS) $(LINK) $(LINKFLAGS) rdx rdx.o $(RDXLIBS) +rdflib : rdflib.o + $(LINK) $(LINKFLAGS) rdflib rdflib.o +rdf2bin : rdf2bin.o $(RDXLIBS) ../nasmlib.o + $(LINK) $(LINKFLAGS) rdf2bin rdf2bin.o $(RDXLIBS) ../nasmlib.o +rdf2com : + ln -s rdf2bin rdf2com +rdf2bin.o : rdf2bin.c rdfdump.o : rdfdump.c rdoff.o : rdoff.c rdoff.h -ldrdf.o : ldrdf.c rdoff.h ../nasmlib.h symtab.h collectn.h +ldrdf.o : ldrdf.c rdoff.h ../nasmlib.h symtab.h collectn.h rdlib.h symtab.o : symtab.c symtab.h collectn.o : collectn.c collectn.h rdx.o : rdx.c rdoff.h rdfload.h symtab.h rdfload.o : rdfload.c rdfload.h rdoff.h collectn.h symtab.h +rdlib.o : rdlib.c rdlib.h +rdflib.o : rdflib.c clean : - rm -f *.o *~ rdfdump ldrdf rdx - make -C test clean + rm -f *.o *~ rdfdump ldrdf rdx rdflib rdf2bin + + diff --git a/rdoff/README b/rdoff/README new file mode 100644 index 0000000..54f3b2a --- /dev/null +++ b/rdoff/README @@ -0,0 +1,85 @@ +RDOFF Utils v0.2 +================ + +The files contained in this directory are the C source code of a set +of tools (and general purpose library files) for the manipulation of +RDOFF version 1 object files. Here is a brief summary of their usage: + +rdfdump +======= + +This tool prints a list of the header records in an RDOFF object in +human-readable form, and optionally prints a hex dump of the contents +of the code and data segments. + +Usage: + rdfdump [-v] filename + +The -v flag specifies that the hex dump (see above) should be printed. + +ldrdf +===== + +This tool is a version of unix 'ld' (or DOS 'link') for use with RDOFF +files. It is capable of linking RDOFF objects, and libraries produced +with the 'rdlib' utility discussed below. + +In normal usage, its command line takes the form: + + ldrdf [-o output-file] object files [-llibrary ...] + +Libraries must be specified with their path as no search is performed. +Modules in libraries are not linked to the program unless they are +referred to. + +Most of its options are not implemented, but those that are are listed here: + + -v increase verbosity level. Currently 4 verbosity levels are + available: default (which only prints error information), normal + (which prints information about the produced object, -v), medium + (which prints information about what the program is doing, -v -v) + and high (which prints all available information, -v -v -v). + + -p change alignment value to which multiple segments combigned into + a single segment should be aligned (must be either 1, 2, 4, 8, + 16, 32 or 256. Default is 16). + +The default output filename is 'aout.rdx'. + +rdx +=== + +This program simply loads and executes an RDOFF object, by calling +'_main', which it expects to be a C-style function, which will accept +two parameters, argc and argv in normal C style. + +rdflib +====== + +This program creates a library file for use with ldrdf. + +It is supplied with a shell script 'makelib' which should probably be used +to create libraries. + +Usage: + rdflib command library [optional arguments] + +Valid commands are: + + c Create the library + a Add a module (requires a filename and a name to give the + module, ie 'rdflib a libc.rdl strcpy.rdf strcpy' puts the + file 'strcpy.rdf' into 'libc.rdl', and calls it 'strcpy'. + x Extract (arguments are the opposite to the 'a' command, + ie you'd do 'rdflib x libc.rdl strcpy strcpy.rdf to get + a copy of strcpy.rdf back out again...) + +Remove and List commands will be added soon (they're already documented +as existing, but I haven't had time to implement them... if anyone +else wants to do this, they're welcome to. The file format should be +amply documented in the source code... look at 'rdflib.c' and 'rdlib.c', +and the relevant sections of 'ldrdf.c' to see how libraries can be +handled). + +Julian Hall (jules@dcs.warwick.ac.uk) + diff --git a/rdoff/collectn.h b/rdoff/collectn.h index b3f2d52..2dc786e 100644 --- a/rdoff/collectn.h +++ b/rdoff/collectn.h @@ -10,7 +10,7 @@ typedef struct tagCollection { void *p[32]; /* array of pointers to objects */ - + struct tagCollection *next; } Collection; diff --git a/rdoff/ldrdf.c b/rdoff/ldrdf.c index ce86b7e..e2541fa 100644 --- a/rdoff/ldrdf.c +++ b/rdoff/ldrdf.c @@ -11,21 +11,27 @@ * May require the system to make an extra pass of the modules to be * loaded eliminating those that aren't required. * + * Support all the existing documented options... + * * Support libaries (.a files - requires a 'ranlib' type utility) + * (I think I've got this working, so I've upped the version) * - * -s option to strip resolved symbols from exports. + * -s option to strip resolved symbols from exports. (Could make this an + * external utility) */ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include "nasm.h" #include "rdoff.h" #include "nasmlib.h" #include "symtab.h" #include "collectn.h" +#include "rdlib.h" -#define LDRDF_VERSION "0.11" +#define LDRDF_VERSION "0.30" /* global variables - those to set options: */ @@ -46,8 +52,17 @@ struct modulenode { struct modulenode *next; }; +#define newstr(str) strcpy(malloc(strlen(str) + 1),str) +#define newstrcat(s1,s2) strcat(strcpy(malloc(strlen(s1)+strlen(s2)+1),s1),s2) + + struct modulenode *modules = NULL,*lastmodule = NULL; +/* the linked list of libraries to be searched for missing imported + symbols */ + +struct librarynode * libraries = NULL, * lastlib = NULL; + void *symtab; /* The symbol table */ rdf_headerbuf * newheader ; /* New header to be written to output */ @@ -101,6 +116,218 @@ void loadmodule(char *filename) lastmodule->coderel,lastmodule->f.code_len, lastmodule->datarel,lastmodule->f.data_len); + lastmodule->header = malloc(lastmodule->f.header_len); + if (!lastmodule->header) { + fprintf(stderr,"ldrdf: out of memory\n"); + exit(1); + } + + if (rdfloadseg(&lastmodule->f,RDOFF_HEADER,lastmodule->header)) + { + rdfperror("ldrdf",filename); + exit(1); + } +} + +/* load_library add a library to list of libraries to search + * for undefined symbols + */ + +void load_library(char * name) +{ + if (verbose) + printf("adding library %s to search path\n",name); + + if (! lastlib) { + lastlib = libraries = malloc(sizeof(struct librarynode)); + } + else + { + lastlib->next = malloc(sizeof(struct librarynode)); + lastlib = lastlib->next; + } + + if (! lastlib) { + fprintf(stderr, "ldrdf: out of memory\n"); + exit(1); + } + strcpy (lastlib->name = malloc (1+strlen(name)), name); + lastlib->fp = NULL; + lastlib->referenced = 0; + lastlib->next = NULL; +} + + +/* build_symbols() step through each module's header, and locate + * exported symbols, placing them in a global table + */ + +long bsslength; + +void mod_addsymbols(struct modulenode * mod) +{ + rdfheaderrec *r; + symtabEnt e; + long cbBss; + + mod->bssrel = bsslength; + cbBss = 0; + rdfheaderrewind(&mod->f); + while ((r = rdfgetheaderrec(&mod->f))) + { + + if (r->type == 5) /* Allocate BSS */ + cbBss += r->b.amount; + + if (r->type != 3) continue; /* ignore all but export recs */ + + e.segment = r->e.segment; + e.offset = r->e.offset + + (e.segment == 0 ? mod->coderel : /* 0 -> code */ + e.segment == 1 ? mod->datarel : /* 1 -> data */ + mod->bssrel) ; /* 2 -> bss */ + + e.flags = 0; + e.name = malloc(strlen(r->e.label) + 1); + if (! e.name) + { + fprintf(stderr,"ldrdf: out of memory\n"); + exit(1); + } + strcpy(e.name,r->e.label); + symtabInsert(symtab,&e); + } + bsslength += cbBss; +} + +void build_symbols() +{ + struct modulenode *mod; + + if (verbose) printf("building global symbol table:\n"); + newheader = rdfnewheader(); + + symtab = symtabNew(); + bsslength = 0; /* keep track of location of BSS symbols */ + + for (mod = modules; mod; mod = mod->next) + { + mod_addsymbols( mod ); + } + if (verbose) + { + symtabDump(symtab,stdout); + printf("BSS length = %ld bytes\n\n",bsslength); + } +} + + +/* scan_libraries() search through headers of modules for undefined + * symbols, and scan libraries for those symbols, + * adding library modules found to list of modules + * to load. */ + +void scan_libraries(void) +{ + struct modulenode * mod, * nm; + struct librarynode * lib; + rdfheaderrec * r; + int found; + char * tmp; + + if (verbose) printf("Scanning libraries for unresolved symbols...\n"); + + mod = modules; + + while (mod) + { + rdfheaderrewind(&mod->f); + + while ((r = rdfgetheaderrec(&mod->f))) + { + if (r->type != 2) continue; /* not an import record */ + if ( symtabFind (symtab,r->i.label) ) + continue; /* symbol already defined */ + + /* okay, we have an undefined symbol... step through + the libraries now */ + if (verbose >= 2) { + printf("undefined symbol '%s'...",r->i.label); + fflush(stdout); + } + + lib = libraries; + found = 0; + + tmp = newstr(r->i.label); + while (! found && lib) + { + /* move this to an outer loop...! */ + nm = malloc(sizeof(struct modulenode)); + + if (rdl_searchlib(lib,tmp,&nm->f)) + { /* found a module in the library */ + + /* create a modulenode for it */ + + if (! nm) { + fprintf(stderr,"ldrdf: out of memory\n"); + exit(1); + } + + nm->name = newstrcat(lib->name,nm->f.name); + if (verbose >= 2) printf("found in '%s'\n",nm->name); + + nm->coderel = lastmodule->coderel + lastmodule->f.code_len; + if (nm->coderel % align != 0) + nm->coderel += align - (nm->coderel % align); + + nm->datarel = lastmodule->datarel + lastmodule->f.data_len; + if (nm->datarel % align != 0) + nm->datarel += align - (nm->datarel % align); + + nm->header = malloc(nm->f.header_len); + if (! nm->header) + { + fprintf(stderr,"ldrdf: out of memory\n"); + exit(1); + } + + if (rdfloadseg(&nm->f,RDOFF_HEADER,nm->header)) + { + rdfperror("ldrdf",nm->name); + exit(1); + } + + nm->next = NULL; + found = 1; + lastmodule->next = nm; + lastmodule = nm; + + if (verbose) + printf("%s code = %08lx (+%04lx), data = %08lx " + "(+%04lx)\n",lastmodule->name, + lastmodule->coderel,lastmodule->f.code_len, + lastmodule->datarel,lastmodule->f.data_len); + + /* add the module's info to the symbol table */ + mod_addsymbols(nm); + } + else + { + if (rdl_error) { + rdl_perror("ldrdf",lib->name); + exit(1); + } + free(nm); + } + lib = lib->next; + } + free(tmp); + if (!found && verbose >= 2) printf("not found\n"); + } + mod = mod->next; + } } /* load_segments() allocates memory for & loads the code & data segs @@ -108,7 +335,7 @@ void loadmodule(char *filename) */ char *text,*data; -long textlength,datalength,bsslength; +long textlength,datalength; void load_segments(void) { @@ -150,77 +377,17 @@ void load_segments(void) mod = modules; while (mod) { /* load the segments for each module */ - mod->header = malloc(mod->f.header_len); - if (!mod->header) { - fprintf(stderr,"ldrdf: out of memory\n"); - exit(1); - } - if (rdfloadseg(&mod->f,RDOFF_HEADER,mod->header) || - rdfloadseg(&mod->f,RDOFF_CODE,&text[mod->coderel]) || - rdfloadseg(&mod->f,RDOFF_DATA,&data[mod->datarel])) { - rdfperror("ldrdf",mod->name); - exit(1); - } - rdfclose(&mod->f); /* close file; segments remain */ - mod = mod->next; - } -} - -/* build_symbols() step through each module's header, and locate - * exported symbols, placing them in a global table - */ - -void build_symbols() -{ - struct modulenode *mod; - rdfheaderrec *r; - symtabEnt e; - long bssloc,cbBss; - - if (verbose) printf("building global symbol table:\n"); - newheader = rdfnewheader(); - - symtab = symtabNew(); - bssloc = 0; /* keep track of location of BSS symbols */ - - for (mod = modules; mod; mod = mod->next) - { - mod->bssrel = bssloc; - cbBss = 0; - rdfheaderrewind(&mod->f); - while ((r = rdfgetheaderrec(&mod->f))) - { - - if (r->type == 5) /* Allocate BSS */ - cbBss += r->b.amount; - - if (r->type != 3) continue; /* ignore all but export recs */ - - e.segment = r->e.segment; - e.offset = r->e.offset + - (e.segment == 0 ? mod->coderel : /* 0 -> code */ - e.segment == 1 ? mod->datarel : /* 1 -> data */ - mod->bssrel) ; /* 2 -> bss */ - e.flags = 0; - e.name = malloc(strlen(r->e.label) + 1); - if (! e.name) - { - fprintf(stderr,"ldrdf: out of memory\n"); - exit(1); - } - strcpy(e.name,r->e.label); - symtabInsert(symtab,&e); + if (verbose >= 2) printf(" loading %s\n",mod->name); + if (rdfloadseg(&mod->f,RDOFF_CODE,&text[mod->coderel]) || + rdfloadseg(&mod->f,RDOFF_DATA,&data[mod->datarel])) { + rdfperror("ldrdf",mod->name); + exit(1); } - bssloc += cbBss; + rdfclose(&mod->f); /* close file; segments remain */ + mod = mod->next; } - if (verbose) - { - symtabDump(symtab,stdout); - printf("BSS length = %ld bytes\n\n",bssloc); - } - bsslength = bssloc; } - + /* link_segments() step through relocation records in each module's * header, fixing up references. */ @@ -230,7 +397,7 @@ void link_segments(void) struct modulenode *mod; Collection imports; symtabEnt *s; - long rel,relto = 0; /* placate gcc */ + long rel,relto; char *seg; rdfheaderrec *r; int bRelative; @@ -243,6 +410,7 @@ void link_segments(void) if (verbose >= 2) printf("* processing %s\n",mod->name); rdfheaderrewind(&mod->f); while((r = rdfgetheaderrec(&mod->f))) { + if (verbose >= 3) printf("record type: %d\n",r->type); switch(r->type) { case 1: /* relocation record */ if (r->r.segment >= 64) { /* Relative relocation; */ @@ -275,19 +443,19 @@ void link_segments(void) break; } rel = s->offset; - - r->r.refseg = s->segment; /* change referred segment, + + r->r.refseg = s->segment; /* change referred segment, so that new header is correct */ } if (bRelative) /* Relative - subtract current segment start */ - rel -= relto; - else + rel -= relto; + else { /* Add new relocation header */ rdfaddheader(newheader,r); } - + /* Work out which segment we're making changes to ... */ if (r->r.segment == 0) seg = text; else if (r->r.segment == 1) seg = data; @@ -326,7 +494,7 @@ void link_segments(void) r->i.label,mod->name); errors = 1; } - else + else { *colln(&imports,r->i.segment - 2) = s; if (verbose >= 2) @@ -336,8 +504,11 @@ void link_segments(void) case 3: /* export; dump to output new version */ s = symtabFind(symtab, r->e.label); - if (! s) continue; /* eh? probably doesn't matter... */ - + if (! s) { + fprintf(stderr,"ldrdf: internal error - undefined symbol %s " + "exported in header of '%s'\n",r->e.label,mod->name); + continue; + } r->e.offset = s->offset; rdfaddheader(newheader,r); break; @@ -347,10 +518,14 @@ void link_segments(void) break; } } + if (rdf_errno != 0) { + rdfperror("ldrdf",mod->name); + exit(1); + } collection_reset(&imports); } } - + /* write_output() write linked program out to a file */ void write_output(char *filename) @@ -358,14 +533,16 @@ void write_output(char *filename) FILE * fp; rdfheaderrec r; + if (verbose) printf("writing output to '%s'\n",filename); + fp = fopen(filename,"wb"); if (! fp) { fprintf(stderr,"ldrdf: could not open '%s' for writing\n",filename); exit(1); } - - + + /* add BSS length count to header... */ if (bsslength) { @@ -412,7 +589,8 @@ void write_output(char *filename) */ const char *usagemsg = "usage:\n" -" ldrdf [-o outfile | -x] [-a x] [-v] [-p x] [--] infile [infile ...]\n\n" +" ldrdf [-o outfile | -x] [-a x] [-v] [-p x] [--] infile [infile ...]\n" +" [-l<libname> ...]\n\n" " ldrdf -h displays this message\n" " ldrdf -r displays version information\n\n" " -o selects output filename (default is aout.rdx)\n" @@ -420,7 +598,9 @@ const char *usagemsg = "usage:\n" " -a x causes object program to be statically relocated to address 'x'\n" " -v turns on verbose mode\n" " -p x causes segments to be aligned (padded) to x byte boundaries\n" -" (default is 16 bytes)\n"; +" (default is 16 bytes)\n" +" -l<name> causes 'name' to be linked in as a library. Note no search is\n" +" performed - the entire pathname MUST be specified.\n"; void usage(void) { @@ -499,6 +679,9 @@ int main(int argc,char **argv) } if (verbose > 1) printf("alignment %d selected\n",align); } + else if (procsw && !strncmp(*argv,"-l",2)) { + load_library(*argv + 2); + } else if (procsw && !strcmp(*argv,"--")) { procsw = 0; } @@ -512,12 +695,15 @@ int main(int argc,char **argv) unreferenced modules from the list of modules here, so that we know about the final size once libraries have been linked in */ + build_symbols(); /* build a global symbol table... */ + + scan_libraries(); /* check for imported symbols not in table, + and ensure the relevant library modules + are loaded */ + load_segments(); /* having calculated size of reqd segments, load each rdoff module's segments into memory */ - build_symbols(); /* build a global symbol table... - perhaps this should be done before load_segs? */ - link_segments(); /* step through each module's header, and resolve references to the global symbol table. This also does local address fixups. */ diff --git a/rdoff/rdf.doc b/rdoff/rdf.doc deleted file mode 100644 index 300c2bc..0000000 --- a/rdoff/rdf.doc +++ /dev/null @@ -1,99 +0,0 @@ -RDOFF: Relocatable Dynamically-linked Object File Format -======================================================== - -RDOFF was designed initially to test the object-file production -interface to NASM. It soon became apparent that it could be enhanced -for use in serious applications due to its simplicity; code to load -and execute an RDOFF object module is very simple. It also contains -enhancements to allow it to be linked with a dynamic link library at -either run- or load- time, depending on how complex you wish to make -your loader. - -The RDOFF format (version 1.1, as produced by NASM v0.91) is defined -as follows: - -The first six bytes of the file contain the string 'RDOFF1'. Other -versions of the format may contain other last characters other than -'1' - all little endian versions of the file will always contain an -ASCII character with value greater than 32. If RDOFF is used on a -big-endian machine at some point in the future, the version will be -encoded in decimal rather than ASCII, so will be below 32. - -All multi-byte fields follwing this are encoded in either little- or -big-endian format depending on the system described by this version -information. Object files should be encoded in the endianness of -their target machine; files of incorrect endianness will be rejected -by the loader - this means that loaders do not need to convert -endianness, as RDOFF has been designed with simplicity of loading at -the forefront of the design requirements. - -The next 4 byte field is the length of the header in bytes. The -header consists of a sequence of variable length records. Each -record's type is identified by the first byte of the record. Record -types 1-4 are currently supported. Record type 5 will be added in -the near future, when I implement BSS segments. Record type 6 may be -to do with debugging, when I get debugging implemented. - -Type 1: Relocation -================== - -Offset Length Description -0 1 Type (contains 1) -1 1 Segment that contains reference (0 = text, 1 = data) - Add 64 to this number to indicate a relative linkage - to an external symbol (see notes) -2 4 Offset of reference -6 1 Length of reference (1,2 or 4 bytes) -7 2 Segment to which reference is made (0 = text, 1 = - data, 2 = BSS [when implemented]) others are external - symbols. - -Total length = 9 bytes - -Type 2: Symbol Import -===================== - -0 1 Type (2) -1 2 Segment number that will be used in references to this - symbol. -3 ? Null terminated string containing label (up to 32 - chars) to match against exports in linkage. - -Type 3: Symbol Export -===================== - -0 1 Type (3) -1 1 Segment containing object to be exported (0/1/2) -2 4 Offset within segment -6 ? Null terminate string containing label to export (32 - char maximum length) - -Type 4: Dynamic Link Library -============================ - -0 1 Type (4) -1 ? Library name (up to 128 chars) - -Type 5: Reserve BSS -=================== - -0 1 Type (5) -1 4 Amount of BSS space to reserve in bytes - -Total length: 5 bytes - ------------------------------------------------------------------------------ - -Following the header is the text (code) segment. This is preceded by -a 4-byte integer, which is its length in bytes. This is followed by -the length of the data segment (also 4 bytes), and finally the data -segment. - -Notes -===== - -Relative linking: The number stored at the address is offset -required from the imported symbol, with the address of the end of -the instruction subtracted from it. This means that the linker can -simply add the address of the label relative to the beginning of the -current segment to it. diff --git a/rdoff/rdf2bin.c b/rdoff/rdf2bin.c new file mode 100644 index 0000000..97b45b4 --- /dev/null +++ b/rdoff/rdf2bin.c @@ -0,0 +1,125 @@ +/* rdf2bin: convert an RDOFF object file to flat binary */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "rdfload.h" +#include "rdoff.h" +#include "nasmlib.h" + +long origin = 0; +int align = 16; + +char *getfilename(char * pathname) +{ + char * lastslash = pathname - 1; + char * i = pathname; + + while ( *i ) { + if (*i == '/') lastslash = i; + i++; + } + return lastslash + 1; +} + +int main(int argc, char **argv) +{ + rdfmodule * m; + int tmp; + FILE *of; + char * padding; + int codepad, datapad; + + if (argc < 2) { + puts("Usage: rdf2bin [-o relocation-origin] [-p segment-alignment] " + "input-file output-file"); + puts(" rdf2com [-p segment-alignment] input-file output-file"); + return 1; + } + + if (! nasm_stricmp(getfilename(*argv),"rdf2com")) { + origin = 0x100; + } + argv++, argc--; + + while (argc > 2) { + if (! strcmp(*argv,"-o")) { + argv++, argc--; + origin = readnum(*argv, &tmp); + if (tmp) { + fprintf(stderr,"rdf2bin: invalid parameter: %s\n",*argv); + return 1; + } + } else if (! strcmp(*argv,"-p")) { + argv++, argc--; + align = readnum(*argv, &tmp); + if (tmp) { + fprintf(stderr,"rdf2bin: invalid parameter: %s\n",*argv); + return 1; + } + } else + break; + + argv++, argc--; + } + if (argc < 2) { + puts("rdf2bin: required parameter missing"); + return -1; + } + m = rdfload(*argv); + + if (! m) + { + rdfperror("rdf2bin",*argv); + return 1; + } + printf("relocating %s: origin=%lx, align=%d\n",*argv,origin,align); + + m->textrel = origin; + m->datarel = origin + m->f.code_len; + if (m->datarel % align != 0) { + codepad = align - (m->datarel % align); + m->datarel += codepad; + } + else + codepad = 0; + + m->bssrel = m->datarel + m->f.data_len; + if (m->bssrel % align != 0) { + datapad = align - (m->bssrel % align); + m->bssrel += datapad; + } + else + datapad = 0; + + printf("code: %08lx\ndata: %08lx\nbss: %08lx\n", + m->textrel, m->datarel, m->bssrel); + + rdf_relocate(m); + + argv++; + + of = fopen(*argv,"wb"); + if (!of) { + fprintf(stderr,"rdf2bin: could not open output file %s\n",*argv); + return 1; + } + + padding = malloc(align); + if (!padding) { + fprintf(stderr,"rdf2bin: out of memory\n"); + return 1; + } + + if (fwrite(m->t,1,m->f.code_len,of) != m->f.code_len || + fwrite(padding,1,codepad,of) != codepad || + fwrite(m->d,1,m->f.data_len,of) != m->f.data_len) + { + fprintf(stderr,"rdf2bin: error writing to %s\n", *argv); + return 1; + } + + fclose(of); + return 0; +} diff --git a/rdoff/rdfdump.c b/rdoff/rdfdump.c index 4d4f4df..bc55a97 100644 --- a/rdoff/rdfdump.c +++ b/rdoff/rdfdump.c @@ -17,7 +17,7 @@ long translatelong(long in) { /* translate from little endian to return r; } - + int translateshort(short in) { int r; unsigned char *i; @@ -28,8 +28,8 @@ int translateshort(short in) { return r; } void print_header(long length) { - unsigned char buf[129],t,s,l; - long o; + char buf[129],t,s,l; + long o,ll; short rs; while (length > 0) { @@ -39,7 +39,7 @@ void print_header(long length) { fread(&s,1,1,infile); fread(&o,4,1,infile); fread(&l,1,1,infile); - fread(&rs,2,1,infile); + fread(&rs,2,1,infile); printf(" relocation: location (%04x:%08lx), length %d, " "referred seg %04x\n",(int)s,translatelong(o),(int)l, translateshort(rs)); @@ -47,34 +47,34 @@ void print_header(long length) { break; case 2: /* import record */ fread(&rs,2,1,infile); - l = 0; + ll = 0; do { - fread(&buf[l],1,1,infile); - } while (buf[l++]); + fread(&buf[ll],1,1,infile); + } while (buf[ll++]); printf(" import: segment %04x = %s\n",translateshort(rs),buf); - length -= l + 3; + length -= ll + 3; break; case 3: /* export record */ fread(&s,1,1,infile); fread(&o,4,1,infile); l = 0; do { - fread(&buf[l],1,1,infile); - } while (buf[l++]); + fread(&buf[ll],1,1,infile); + } while (buf[ll++]); printf(" export: (%04x:%08lx) = %s\n",(int)s,translatelong(o),buf); - length -= l + 6; + length -= ll + 6; break; case 4: /* DLL record */ l = 0; do { - fread(&buf[l],1,1,infile); - } while (buf[l++]); + fread(&buf[ll],1,1,infile); + } while (buf[ll++]); printf(" dll: %s\n",buf); - length -= l + 1; + length -= ll + 1; break; case 5: /* BSS reservation */ - fread(&l,4,1,infile); - printf(" bss reservation: %08lx bytes\n",translatelong(l)); + fread(&ll,4,1,infile); + printf(" bss reservation: %08lx bytes\n",translatelong(ll)); length -= 5; break; default: diff --git a/rdoff/rdflib.c b/rdoff/rdflib.c new file mode 100644 index 0000000..5846562 --- /dev/null +++ b/rdoff/rdflib.c @@ -0,0 +1,235 @@ +/* rdflib - manipulate RDOFF library files (.rdl) */ + +/* an rdoff library is simply a sequence of RDOFF object files, each + preceded by the name of the module, an ASCII string of up to 255 + characters, terminated by a zero. There may be an optional + directory placed on the end of the file. The format of the + directory will be 'RDL' followed by a version number, followed by + the length of the directory, and then the directory, the format of + which has not yet been designed. */ + +#include <stdio.h> +#include <errno.h> +#include <string.h> + +/* functions supported: + create a library (no extra operands required) + add a module from a library (requires filename and name to give mod.) + remove a module from a library (requires given name) + extract a module from the library (requires given name and filename) + list modules */ + +const char *usage = + "usage:\n" + " rdflib x libname [extra operands]\n\n" + " where x is one of:\n" + " c - create library\n" + " a - add module (operands = filename module-name)\n" + " r - remove (module-name)\n" + " x - extract (module-name filename)\n" + " t - list\n"; + +char **_argv; + +#define _ENDIANNESS 0 /* 0 for little, 1 for big */ + +static void longtolocal(long * l) +{ +#if _ENDIANNESS + unsigned char t; + unsigned char * p = (unsigned char *) l; + + t = p[0]; + p[0] = p[3]; + p[3] = t; + t = p[1]; + p[1] = p[2]; + p[2] = p[1]; +#endif +} + +void copybytes(FILE *fp, FILE *fp2, int n) +{ + int i,t; + + for (i = 0 ; i < n; i++ ) + { + t = fgetc(fp); + if (t == EOF) + { + fprintf(stderr,"ldrdf: premature end of file in '%s'\n", + _argv[2]); + exit(1); + } + if (fp2) + if (fputc(t, fp2) == EOF) + { + fprintf(stderr,"ldrdf: write error\n"); + exit(1); + } + } +} + +long copylong(FILE *fp, FILE *fp2) +{ + long l; + int i,t; + unsigned char * p = (unsigned char *) &l; + + + for (i = 0 ; i < 4; i++ ) /* skip magic no */ + { + t = fgetc(fp); + if (t == EOF) + { + fprintf(stderr,"ldrdf: premature end of file in '%s'\n", + _argv[2]); + exit(1); + } + if (fp2) + if (fputc(t, fp2) == EOF) + { + fprintf(stderr,"ldrdf: write error\n"); + exit(1); + } + *p++ = t; + } + longtolocal (&l); + return l; +} + +int main(int argc, char **argv) +{ + FILE *fp, *fp2; + char *p, buf[256]; + int i; + + _argv = argv; + + if (argc < 3 || !strncmp(argv[1],"-h",2) || !strncmp(argv[1],"--h",3)) + { + printf(usage); + exit(1); + } + + switch(argv[1][0]) + { + case 'c': /* create library */ + fp = fopen(argv[2],"wb"); + if (! fp) { + fprintf(stderr,"ldrdf: could not open '%s'\n",argv[2]); + perror("ldrdf"); + exit(1); + } + fclose(fp); + break; + + case 'a': /* add module */ + if (argc < 5) { + fprintf(stderr,"ldrdf: required parameter missing\n"); + exit(1); + } + fp = fopen(argv[2],"ab"); + if (! fp) + { + fprintf(stderr,"ldrdf: could not open '%s'\n",argv[2]); + perror("ldrdf"); + exit(1); + } + + fp2 = fopen(argv[3],"rb"); + if (! fp) + { + fprintf(stderr,"ldrdf: could not open '%s'\n",argv[3]); + perror("ldrdf"); + exit(1); + } + + p = argv[4]; + do { + if ( fputc(*p,fp) == EOF ) { + fprintf(stderr,"ldrdf: write error\n"); + exit(1); + } + } while (*p++); + + while (! feof (fp2) ) { + i = fgetc (fp2); + if (i == EOF) { + break; + } + + if ( fputc(i, fp) == EOF ) { + fprintf(stderr,"ldrdf: write error\n"); + exit(1); + } + } + fclose(fp2); + fclose(fp); + break; + + case 'x': + if (argc < 5) { + fprintf(stderr,"ldrdf: required parameter missing\n"); + exit(1); + } + + fp = fopen(argv[2],"rb"); + if (! fp) + { + fprintf(stderr,"ldrdf: could not open '%s'\n",argv[2]); + perror("ldrdf"); + exit(1); + } + + fp2 = NULL; + while (! feof(fp) ) { + /* read name */ + p = buf; + while( ( *(p++) = (char) fgetc(fp) ) ) + if (feof(fp)) break; + + if (feof(fp)) break; + + /* check against desired name */ + if (! strcmp(buf,argv[3]) ) + { + fp2 = fopen(argv[4],"wb"); + if (! fp2) + { + fprintf(stderr,"ldrdf: could not open '%s'\n", argv[4]); + perror("ldrdf"); + exit(1); + } + } + else + fp2 = NULL; + + /* step over the RDOFF file, copying it if fp2 != NULL */ + copybytes(fp,fp2,6); /* magic number */ + copybytes(fp,fp2, copylong(fp,fp2)); /* header */ + copybytes(fp,fp2, copylong(fp,fp2)); /* text */ + copybytes(fp,fp2, copylong(fp,fp2)); /* data */ + + if (fp2) + break; + } + fclose(fp); + if (fp2) + fclose(fp2); + else + { + fprintf(stderr,"ldrdf: module '%s' not found in '%s'\n", + argv[3],argv[2]); + exit(1); + } + break; + + default: + fprintf(stderr,"ldrdf: command '%c' not recognised\n", + argv[1][0]); + exit(1); + } + return 0; +} + diff --git a/rdoff/rdfload.c b/rdoff/rdfload.c index ad340b3..b848344 100644 --- a/rdoff/rdfload.c +++ b/rdoff/rdfload.c @@ -27,7 +27,7 @@ rdfmodule * rdfload(const char *filename) char * hdr; rdfheaderrec *r; - if (f == NULL) + if (f == NULL) { rdf_errno = 6; /* out of memory */ return NULL; @@ -48,7 +48,7 @@ rdfmodule * rdfload(const char *filename) } /* read in text and data segments, and header */ - + f->t = malloc (f->f.code_len); f->d = malloc (f->f.data_len); /* BSS seg allocated later */ hdr = malloc (f->f.header_len); @@ -107,11 +107,11 @@ rdfmodule * rdfload(const char *filename) int rdf_relocate(rdfmodule * m) { rdfheaderrec * r; - Collection imports; + Collection imports; symtabEnt e; long rel; unsigned char * seg; - + rdfheaderrewind ( & m->f ); collection_init(&imports); @@ -155,7 +155,7 @@ int rdf_relocate(rdfmodule * m) case 3: /* export record - add to symtab */ e.segment = r->e.segment; - e.offset = r->e.offset + + e.offset = r->e.offset + (e.segment == 0 ? m->textrel : /* 0 -> code */ e.segment == 1 ? m->datarel : /* 1 -> data */ m->bssrel) ; /* 2 -> bss */ @@ -168,6 +168,6 @@ int rdf_relocate(rdfmodule * m) symtabInsert(m->symtab,&e); break; } - } + } return 0; } diff --git a/rdoff/rdlib.c b/rdoff/rdlib.c new file mode 100644 index 0000000..bc8d1e3 --- /dev/null +++ b/rdoff/rdlib.c @@ -0,0 +1,88 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "rdoff.h" +#include "rdlib.h" + +int rdl_error = 0; + +char *rdl_errors[3] = { + "no error","could not open file", "invalid file structure", +}; + +int rdl_searchlib (struct librarynode * lib, + const char * label, rdffile * f) +{ + char buf[257]; + int i; + void * hdr; + rdfheaderrec * r; + + rdl_error = 0; + lib->referenced ++; + + if (! lib->fp) + { + lib->fp = fopen(lib->name,"rb"); + + if (! lib->fp) { + rdl_error = 1; + return 0; + } + } + else + rewind(lib->fp); + + while (! feof(lib->fp) ) + { + i = 1; + while (fread(buf + i,1,1,lib->fp) == 1 && buf[i] && i < 257) + i++; + buf[0] = ':'; + + if (feof(lib->fp)) break; + + if ( rdfopenhere(f,lib->fp,&lib->referenced,buf) ) { + rdl_error = 2; + return 0; + } + + hdr = malloc(f->header_len); + rdfloadseg(f,RDOFF_HEADER,hdr); + + while ((r = rdfgetheaderrec(f))) + { + if (r->type != 3) /* not an export */ + continue; + + if (! strcmp(r->e.label, label) ) /* match! */ + { + free(hdr); /* reset to 'just open' */ + f->header_loc = NULL; /* state... */ + f->header_fp = 0; + return 1; + } + } + + /* find start of next module... */ + i = f->data_ofs + f->data_len; + rdfclose(f); + fseek(lib->fp,i,SEEK_SET); + } + + lib->referenced --; + if (! lib->referenced) + { + fclose(lib->fp); + lib->fp = NULL; + } + return 0; +} + +void rdl_perror(const char *apname, const char *filename) +{ + fprintf(stderr,"%s:%s:%s\n",apname,filename,rdl_errors[rdl_error]); +} + + + diff --git a/rdoff/rdlib.h b/rdoff/rdlib.h new file mode 100644 index 0000000..94592ce --- /dev/null +++ b/rdoff/rdlib.h @@ -0,0 +1,18 @@ +/* rdlib.h Functions for manipulating librarys of RDOFF object files */ + + +struct librarynode { + char * name; + FILE * fp; /* initialised to NULL - always check*/ + int referenced; /* & open if required. Close afterwards */ + struct librarynode * next; /* if ! referenced. */ +}; + + +extern int rdl_error; + +int rdl_searchlib (struct librarynode * lib, + const char * label, rdffile * f); +void rdl_perror(const char *apname, const char *filename); + + diff --git a/rdoff/rdoff.c b/rdoff/rdoff.c index 9a969ad..96620ec 100644 --- a/rdoff/rdoff.c +++ b/rdoff/rdoff.c @@ -13,9 +13,15 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <errno.h> #include "rdoff.h" +#define newstr(str) strcpy(malloc(strlen(str) + 1),str) +#define newstrcat(s1,s2) strcat(strcpy(malloc(strlen(s1) + strlen(s2) + 1), \ + s1),s2) + /* ======================================================================== * Code for memory buffers (for delayed writing of header until we know * how long it is). @@ -42,7 +48,7 @@ void membufwrite(memorybuffer *b, void *data, int bytes) { } if ((bytes < 0 && b->length - bytes > BUF_BLOCK_LEN) || (bytes > 0 && b->length + bytes > BUF_BLOCK_LEN)) { - + /* buffer full and no next allocated... allocate and initialise next * buffer */ @@ -84,7 +90,7 @@ void membufdump(memorybuffer *b,FILE *fp) if (!b) return; fwrite (b->buffer, 1, b->length, fp); - + membufdump(b->next,fp); } @@ -134,7 +140,18 @@ int rdf_errno = 0; int rdfopen(rdffile *f, const char *name) { + FILE * fp; + + fp = fopen(name,"rb"); + if (!fp) return rdf_errno = 1; /* error 1: file open error */ + + return rdfopenhere(f,fp,NULL,""); +} + +int rdfopenhere(rdffile *f, FILE *fp, int *refcount, char *name) +{ char buf[8]; + long initpos; if (translatelong(0x01020304) != 0x01020304) { /* fix this to be portable! */ @@ -143,9 +160,8 @@ int rdfopen(rdffile *f, const char *name) exit(3); } - - f->fp = fopen(name,"rb"); - if (!f->fp) return rdf_errno = 1; /* error 1: file open error */ + f->fp = fp; + initpos = ftell(fp); fread(buf,6,1,f->fp); /* read header */ buf[6] = 0; @@ -160,6 +176,8 @@ int rdfopen(rdffile *f, const char *name) return rdf_errno = 3; /* error 3: file read error */ } + f->header_ofs = ftell(f->fp); + if (fseek(f->fp,f->header_len,SEEK_CUR)) { fclose(f->fp); return rdf_errno = 2; /* seek past end of file...? */ @@ -182,20 +200,32 @@ int rdfopen(rdffile *f, const char *name) } f->data_ofs = ftell(f->fp); - rewind(f->fp); + fseek(f->fp,initpos,SEEK_SET); f->header_loc = NULL; + + f->name = newstr(name); + f->refcount = refcount; + if (refcount) (*refcount)++; return 0; } int rdfclose(rdffile *f) { - fclose(f->fp); + if (! f->refcount || ! *--f->refcount) + fclose(f->fp); + free(f->name); + return 0; } void rdfperror(const char *app,const char *name) { fprintf(stderr,"%s:%s: %s\n",app,name,rdf_errors[rdf_errno]); + if (rdf_errno == 1 || rdf_errno == 3) + { + perror(app); + } + } int rdfloadseg(rdffile *f,int segment,void *buffer) @@ -205,7 +235,7 @@ int rdfloadseg(rdffile *f,int segment,void *buffer) switch(segment) { case RDOFF_HEADER: - fpos = 10; + fpos = f->header_ofs; slen = f->header_len; f->header_loc = (char *)buffer; f->header_fp = 0; @@ -224,8 +254,8 @@ int rdfloadseg(rdffile *f,int segment,void *buffer) } if (fseek(f->fp,fpos,SEEK_SET)) - return rdf_errno = 4; - + return rdf_errno = 4; + if (fread(buffer,1,slen,f->fp) != slen) return rdf_errno = 3; @@ -294,7 +324,7 @@ rdfheaderrec *rdfgetheaderrec(rdffile *f) } return &r; } - + void rdfheaderrewind(rdffile *f) { f->header_fp = 0; diff --git a/rdoff/rdoff.h b/rdoff/rdoff.h index b022400..0f74b80 100644 --- a/rdoff/rdoff.h +++ b/rdoff/rdoff.h @@ -50,7 +50,7 @@ struct BSSRec { char type; /* must be 5 */ long amount; /* number of bytes BSS to reserve */ }; - + typedef union RDFHeaderRec { char type; /* invariant throughout all below */ struct RelocRec r; /* type == 1 */ @@ -66,10 +66,13 @@ typedef struct RDFFileInfo { long header_len; long code_len; long data_len; + long header_ofs; long code_ofs; long data_ofs; char *header_loc; /* keep location of header */ long header_fp; /* current location within header for reading */ + char *name; /* name of module in libraries */ + int *refcount; /* pointer to reference count on file, or NULL */ } rdffile; #define BUF_BLOCK_LEN 4088 /* selected to match page size (4096) @@ -91,8 +94,11 @@ typedef memorybuffer rdf_headerbuf; /* mask to find actual segment value in relocation records */ #define RDOFF_SEGMENTMASK 63 +extern int rdf_errno; + /* RDOFF file manipulation functions */ int rdfopen(rdffile *f,const char *name); +int rdfopenhere(rdffile *f, FILE *fp, int *refcount, char *name); int rdfclose(rdffile *f); int rdfloadseg(rdffile *f,int segment,void *buffer); rdfheaderrec *rdfgetheaderrec(rdffile *f); /* returns static storage */ diff --git a/rdoff/rdoff.txt b/rdoff/rdoff.txt new file mode 100644 index 0000000..7ee86d6 --- /dev/null +++ b/rdoff/rdoff.txt @@ -0,0 +1,114 @@ +The RDOFF version 1.1 Object File Format +======================================== + +I seem to keep writing this document... I don't know what keeps +happening to it. Anyway, this one will hopefully stay around for a +while. + +RDOFF is a relocatable object file format whose design goals were +mainly to keep it simple, so that an RDOFF object can be loaded and +executed by a very small piece of code (primarily so that it can be +used by the microkernel of an operating system to store system +modules, which can then go on to load and execute more complex object +files, eg ELF, if so desired), yet still be able to be cope with +everything required by the operating system; linkage of multiple +modules together (possibly with automatic loading of new libraries +that are referred to by the object) at load time, allowing static or +dynamic linking as required by the application. + +The overall format of the file is summarised in this table: + +Length (bytes) Description + 6 Contains the string 'RDOFF1' (little-endian targets), + or 'RDOFF' followed by the single byte 0x01 + (big-endian targets). + 4 Length of the header section + ? Header section (see above for length) + 4 Length of code section (.text) + ? Code section + 4 Length of data section (.data) + ? Data section + +Segments are referred to as numbers. Imported labels are implicitly +at offset zero from a segment; each is assigned a segment number when +it is imported. Segments in the object file itself are numbered: + 0 - text segemnt + 1 - data segment + 2 - bss segment + +The header consists of a sequence of records, each of which is +preceded by a byte to represent its type. + +These records are one of the following types: + +1: Relocation Record +-------------------- + + This record points to an address that will need either + relocation or linkage to an external segment when the object + is loaded or linked. + + Length Description + 1 Type identifier (must be 1) + 1 Segment number (0 or 1) plus 64 if the reference is + relative (and thus does not require relocation with + the base of the code, only by the difference between + the start of this segment, and the segment referred to + (see below) + 4 Offset from start of segment of item requiring reloc. + 1 Length of item (1, 2, or 4 bytes...) + 2 Segment number to which reference is made. + +2: Import Symbol Record +----------------------- + + This record defines a segment to start at the location of a + named symbol; this symbol may need to be fetched from an + external library. + + Length Description + 1 Type identifier (must be 2) + 2 Segment number to allocate + ? String containing label (null terminated, max length = + 32 chars) + +3: Export Symbol Record +----------------------- + + This record defines a symbol, to which external modules can + link using the above record type. + + Length Description + 1 Type identifier (must be 3) + 1 Segment containing symbol (0,1 or 2) + 4 Offset of symbol within segment + ? String containing label (null terminated, max length = + 32 chars) + +4: Import Library Record +------------------------ + + This record tells the loader that an extra library should be + loaded and linked to the module at either load- or run-time + (load time is easier, run-time is good, though...) + + Length Description + 1 Type identifier (must be 4) + ? Name of library (null terminated string, max len = 128) + +5: Reserve BSS Bytes +-------------------- + + This record tells the loader how much memory to reserve after + the executable code loaded from the object file for the BSS + segment (referred to as segment number 2). + A loader can safely assume that there will only be one of + these records per module, but the linker probably cannot... + NASM will only output one, but other utilities may be written + that do, and future versions of NASM may output more than one. + + Length Description + 1 Type identifier (must be 5) + 4 Number of bytes to reserve + + diff --git a/rdoff/symtab.c b/rdoff/symtab.c index c0ff3e5..3fc363e 100644 --- a/rdoff/symtab.c +++ b/rdoff/symtab.c @@ -62,7 +62,7 @@ symtabEnt *symtabFind(void *symtab,char *name) return &(l->ent); } l = l->next; - } + } return NULL; } @@ -13,10 +13,15 @@ #define SYNC_MAX 4096 /* max # of sync points */ +/* + * This lot manages the current set of sync points by means of a + * heap (priority queue) structure. + */ + static struct Sync { unsigned long pos; unsigned long length; -} synx[SYNC_MAX]; +} synx[SYNC_MAX+1]; /* synx[0] never used - who cares :) */ static int nsynx; void init_sync(void) { diff --git a/test/aouttest.asm b/test/aouttest.asm index c52f112..a11824f 100644 --- a/test/aouttest.asm +++ b/test/aouttest.asm @@ -23,17 +23,17 @@ ; [16] Reference a data-section symbol in the data section ; [17] Reference a BSS-section symbol in the data section -[BITS 32] -[GLOBAL _lrotate] ; [1] -[GLOBAL _greet] ; [1] -[GLOBAL _asmstr] ; [2] -[GLOBAL _textptr] ; [2] -[GLOBAL _selfptr] ; [2] -[GLOBAL _integer] ; [3] -[EXTERN _printf] ; [10] -[COMMON _commvar 4] ; [7] + BITS 32 + GLOBAL _lrotate ; [1] + GLOBAL _greet ; [1] + GLOBAL _asmstr ; [2] + GLOBAL _textptr ; [2] + GLOBAL _selfptr ; [2] + GLOBAL _integer ; [3] + EXTERN _printf ; [10] + COMMON _commvar 4 ; [7] -[SECTION .text] + SECTION .text ; prototype: long lrotate(long x, int num); _lrotate: ; [1] @@ -53,14 +53,14 @@ _greet mov eax,[_integer] ; [14] mov [localint],eax ; [14] push dword [_commvar] mov eax,[localptr] ; [13] - push dword [eax] ; + push dword [eax] push dword [_integer] ; [1] [14] push dword _printfstr ; [13] call _printf ; [11] add esp,16 ret -[SECTION .data] + SECTION .data ; a string _asmstr db 'hello, world', 0 ; [2] @@ -74,7 +74,7 @@ localptr dd localint ; [5] [17] _textptr dd _greet ; [15] _selfptr dd _selfptr ; [16] -[SECTION .bss] + SECTION .bss ; an integer _integer resd 1 ; [3] diff --git a/test/bintest.asm b/test/bintest.asm index 0a3c4ae..6799b38 100644 --- a/test/bintest.asm +++ b/test/bintest.asm @@ -19,10 +19,10 @@ ; [10] Reference a data-section symbol in the data section ; [11] Reference a BSS-section symbol in the data section -[BITS 16] -[ORG 0x100] + BITS 16 + ORG 0x100 -[SECTION .text] + SECTION .text jmp start ; [6] @@ -44,13 +44,13 @@ start mov byte [bss_sym],',' ; [1] [8] mov bx,[textptr] ; [7] jmp bx -[SECTION .data] + SECTION .data datasym db 'hello world', 13, 10, '$' ; [2] bssptr dw bss_sym ; [2] [11] dataptr dw datasym+5 ; [2] [10] textptr dw end ; [2] [9] -[SECTION .bss] + SECTION .bss bss_sym resb 1 ; [3] diff --git a/test/cofftest.asm b/test/cofftest.asm index bb843a1..88044c0 100644 --- a/test/cofftest.asm +++ b/test/cofftest.asm @@ -22,17 +22,17 @@ ; [16] Reference a data-section symbol in the data section ; [17] Reference a BSS-section symbol in the data section -[BITS 32] -[GLOBAL _lrotate] ; [1] -[GLOBAL _greet] ; [1] -[GLOBAL _asmstr] ; [2] -[GLOBAL _textptr] ; [2] -[GLOBAL _selfptr] ; [2] -[GLOBAL _integer] ; [3] -[EXTERN _printf] ; [10] -[COMMON _commvar 4] ; [7] + BITS 32 + GLOBAL _lrotate ; [1] + GLOBAL _greet ; [1] + GLOBAL _asmstr ; [2] + GLOBAL _textptr ; [2] + GLOBAL _selfptr ; [2] + GLOBAL _integer ; [3] + EXTERN _printf ; [10] + COMMON _commvar 4 ; [7] -[SECTION .text] + SECTION .text ; prototype: long lrotate(long x, int num); _lrotate: ; [1] @@ -59,7 +59,7 @@ _greet mov eax,[_integer] ; [14] add esp,16 ret -[SECTION .data] + SECTION .data ; a string _asmstr db 'hello, world', 0 ; [2] @@ -73,7 +73,7 @@ localptr dd localint ; [5] [17] _textptr dd _greet ; [15] _selfptr dd _selfptr ; [16] -[SECTION .bss] + SECTION .bss ; an integer _integer resd 1 ; [3] diff --git a/test/elftest.asm b/test/elftest.asm index a6034a6..c5a729f 100644 --- a/test/elftest.asm +++ b/test/elftest.asm @@ -23,17 +23,17 @@ ; [16] Reference a data-section symbol in the data section ; [17] Reference a BSS-section symbol in the data section -[BITS 32] -[GLOBAL lrotate] ; [1] -[GLOBAL greet] ; [1] -[GLOBAL asmstr] ; [2] -[GLOBAL textptr] ; [2] -[GLOBAL selfptr] ; [2] -[GLOBAL integer] ; [3] -[EXTERN printf] ; [10] -[COMMON commvar 4] ; [7] + BITS 32 + GLOBAL lrotate ; [1] + GLOBAL greet ; [1] + GLOBAL asmstr ; [2] + GLOBAL textptr ; [2] + GLOBAL selfptr ; [2] + GLOBAL integer ; [3] + EXTERN printf ; [10] + COMMON commvar 4 ; [7] -[SECTION .text] + SECTION .text ; prototype: long lrotate(long x, int num); lrotate: ; [1] @@ -60,7 +60,7 @@ greet mov eax,[integer] ; [14] add esp,16 ret -[SECTION .data] + SECTION .data ; a string asmstr db 'hello, world', 0 ; [2] @@ -74,7 +74,7 @@ localptr dd localint ; [5] [17] textptr dd greet ; [15] selfptr dd selfptr ; [16] -[SECTION .bss] + SECTION .bss ; an integer integer resd 1 ; [3] diff --git a/test/inc1.asm b/test/inc1.asm index e9e5819..0e1058b 100644 --- a/test/inc1.asm +++ b/test/inc1.asm @@ -2,3 +2,5 @@ ; See inctest.asm for build instructions. message: db 'hello, world',13,10,'$' + +%include "inc2.asm" diff --git a/test/inctest.asm b/test/inctest.asm index 95ab40f..d6d35cf 100644 --- a/test/inctest.asm +++ b/test/inctest.asm @@ -5,11 +5,9 @@ ; nasm -f bin inctest.asm -o inctest.com ; and when run, it should print `hello, world'. -[BITS 16] -[ORG 0x100] + BITS 16 + ORG 0x100 jmp _main -[INC inc1.asm] - -[INCLUDE inc2.asm] +%include "inc1.asm" diff --git a/test/objtest.asm b/test/objtest.asm index 8530bae..24e9fbd 100644 --- a/test/objtest.asm +++ b/test/objtest.asm @@ -23,19 +23,19 @@ ; [15] Use SEG on a non-external ; [16] Use SEG on an external -[bits 16] + bits 16 -[global _bsssym] ; [1] -[global _function] ; [1] -[global _selfptr] ; [1] -[global _selfptr2] ; [1] -[common _commvar 2] ; [3] -[extern _printf] ; [6] + global _bsssym ; [1] + global _function ; [1] + global _selfptr ; [1] + global _selfptr2 ; [1] + common _commvar 2 ; [3] + extern _printf ; [6] -[group mygroup mybss mydata] ; [10] -[group mygroup2 mycode mycode2] ; [10] + group mygroup mybss mydata ; [10] + group mygroup2 mycode mycode2 ; [10] -[segment mycode private] + segment mycode private _function push bp mov bp,sp @@ -65,18 +65,18 @@ _function push bp .printf dw _printf, seg _printf ; [2] [4] [16] -[segment mycode2 private] + segment mycode2 private trampoline: pop ax push cs push ax jmp far _printf -[segment mybss private] + segment mybss private _bsssym resw 64 ; [12] -[segment mydata private] + segment mydata private _selfptr dw _selfptr, seg _selfptr ; [8] [15] _selfptr2 dw _selfptr2 wrt mydata, mydata ; [11] [13] |