Blame | Last modification | View Log | Download
(README 2-Apr-1998 by John Hartman. jhartman@compuserve.com)I have made several modifications to the CUG292 assemblers andlinker, beginning with version 1.7 (the most recent I know of).The original assembler was written by* Alan R. Baldwin* 721 Berkeley St.* Kent, Ohio 44240To conserve space on my web site, this ZIP file does not include allof the files in the original CUG292 release. In particular, theassembler test files and ASSIST monitors have been removed. All sourcefiles and documents are included. The original is widely availableon the net.Your comments and bug reports are solicited.The changes are of three types1) bug fixes and small changes2) an 8051 version of the assembler3) generation of line and symbol information for NoICE========================================================================MISCELLANEOUS CHANGES* There is a bug in LKMAIN: it tests S_DEF flag in "s_flag".No one else uses s_flag in the linker - S_DEF is defined in s_typeinstead. Presumably LKMAIN should use s_type as well? Changed.* There is a portability problem in aslex: the testwhile (ctype[c=get()] & ~(SPACE | ILL))causes an infinite loop with my (old Zortech) compiler:ILL = 0x80, SPACE=0. When I read a null at the end of a line,ctype[] returns "ILL". My compiler sign extends this 0x80 to int 0xFF80.Sign extend on ~ILL makes 0x7F into 0xFF7F. The result of the AND istrue and we spin. I changed this towhile (ctype[c=get()] & (0xFF - (SPACE|ILL)))* I made changes to mlookup() so that mnemonics and pseudo-ops are alwayscase insensitive, regardless of the CASE_SENSITIVE flag. This simplifiesusing the assembler on existing code.* The scheme described below for debug information can make for very longsymbol names. Thus, I have modified the assembler and linker to allownames up to 80 characters, moving the name strings out of the sym struct.This will save significant heap space over simply increasing NCPS to 80.* I have added one module, ASNOICE.C, to each assembler; and one module,LKNOICE.C, to the linker. My make files are named XSnnnn.MAK for theasseblers, and XSLINK.MAK for the linker. I have not modified anyof the original make or project files, since I have no means to testthem.========================================================================8051 ASSEMBLERI was somewhat surprised that there was no AS8051 - so I wrote one.It is comprised of the modules:i8051.hi51pst.ci51mch.ci51adr.ci51ext.cappendk.txt "Appendix K" about the 8051 for the documentationI added four attributes to the .area directive to supportthe 8051's multiple address spaces:CODE for codespaceDATA for internal dataBIT for internal bit-addressableXDATA for external data.These will typically be used as follows (names are examples):.area IDATA (DATA).area IBIT (BIT).area MYXDATA (REL,CON,XDATA).area MYCODE (REL,CON,CODE)The default segment, _CODE, should not be used for the 8051. Forcompatibility reasons, _CODE is allocated in "DATA" space. Youshould always define an area in the CODE space.DETAILS:i51mch.c is not especially pretty - it includes some brute-force switchstatements which could, I suspect, be trimmed down by application ofa few appropriate functions.The 8051 includes two instructions, AJMP and ACALL, which have elevenbit destination addresses. The upper three address bits are encodedinto the top three bits of the op-code. In order to achieve this, Iwas forced to make changes to several ASxxx and LKxxx modules:asm.h line 179 equate for R_J11, 583 outr11 prototypeasout.c lines 1087-1132 function outr11: output 11 bit destaslink.h line 131 equate for R_J11lkrloc.c lines 354-377 link/locate 11 bit destinationThe definition of R_J11 is as (R_WORD | R_BYT2)A comment in lkrloc says* R_WORD with the R_BYT2 mode is flagged* as an 'r' error by the assembler,* but it is processed here anyway.This is no longer true, so the code in question is #defined outin the linker only. I suspect that this would cause problemsif a module with R_WORD | R_BYT1 by other cause were to be processed.I am not entirely happy with outr11 in the case where the destinationis an absolute value. The ideal would be to pass the value thru to thelinker, and resolve at link time whether or not the address is within2K of the instruction location. Unfortunately, I couldn't figure outhow to pass an absolute value to the linker, as it has no area. Thus,I interpreted absolute values as being relative to the beginning of thecurrent area, as is done in the other assemblers for relative branchinstructions. I am less happy with this solution here, as a 2K rangeis far larger than the +-128 for a branch instruction. I can envisioncode such asreset = 123...ajmp resetIf the ajmp is in a relocatable area, the effect will be not at all whatis desired. If you can offer any other solution, I would appreciate it.========================================================================SOURCE-LEVEL DEBUG OF ASSEMBLY CODE WITH NoICE1) The switch "-j" has been added to the assembler. This causesassembly lines to generate line number information in the objectfile. You may also wish to use the "-a" switch to make all symbolsglobal. Non-global symbols are not passed to the object file.2) The assemblers will pass any line beginning with the characters";!" (semi-colon, exclamation point) intact to the object file.You can use such comments in your assembly modules to embed NoICEcommands in your source code.3) The switch "-j" has been added to the linker. This causes aNoICE debug file, with extension ".NOI" to be created. All symboland line number information in the object files, as well as any";!" comments will be included. Specifying the -j switch will forcea map file to be produced as well.4) The linker will process any line beginning with the characters";!" (semi-colon, exclamation point) by removing the ";!" andpassing the remainder of the line to the .NOI file (if any).This allows NoICE commands to be placed as ";!" comments inthe assembly file, and passed through the assember and linkerto the .NOI file.5) If the linker is requested to produce a hex output file (-i or -sswitches), a LOAD command for the hex file will be placed in the.NOI file (if any).6) The linker will output the ";!" lines after all symbols have beenoutput. Thus, such lines can contain NoICE commands which refer tosymbols by name.========================================================================SOURCE-LEVEL DEBUG OF C CODE FOR NoICEThis section is primarily of interest to compiler writers.Compilers which produce assembly code can pass debug informationthrough the assembler and linker to the NoICE command file. Inaddition, the linker will provide special processing of symbolswith certain formats described below.1) The switch "-j" should NOT be used on assembly files whichrepresent compiler output. Instead, the compiler should generateline number symbols for each code-producing source line asdescribed below. if your project contains a mixture of C and assemblysource files, you may wish to use "-j" on the assembly modules.2) The assemblers will pass any line beginning with the characters";!" (semi-colon, exclamation point) intact to the .REL file.The compiler can make use of this fact to pass datatype informationand stack offsets for automatic symbols through the assembler andlinker to NoICE. This is described in detail below.3) The switch "-j" has been added to the linker. This causes aNoICE debug file, with extension ".NOI" to be created. Contentswill be as described below. Specifying the -j switch will forcea map file to be produced as well.4) The linker will process any line beginning with the characters";!" (semi-colon, exclamation point) by removing the ";!" andpassing the remainder of the line to the .NOI file (if any).5) If the linker is requested to produce a hex output file (-i or -sswitches), a LOAD command for the hex file will be placed in the.NOI file (if any).6) The linker will process symbols with names of the formtextinto NoICE DEFINE (global symbol) commands in the .NOI output fileDEF name symbolvalue7) The linker will process symbols with names of the formtext.integerinto NoICE FILE and LINE (line number) commands in the .NOI output file.It will assume that "text" is the file name without path or extension,that "integer" is the decimal line number within the file, and thatthe value of the symbol is equal to the address of the first instructionproduced by the line.8) The linker will process symbols with names of the formtext.nameinto NoICE FILE and DEFINESCOPED commands in the .NOI file(if any), to define file-scope variables:FILE textDEFS name symbolvalue9) The linker will process symbols with names of the formtext.name.name2into NoICE FILE, FUNCTION, and DEFINESCOPED commands in the.NOI file (if any), to define function-scope variables:FILE textFUNC nameDEFS name2 symbolvalue10) The linker will process symbols with names of the formtext.name.name2.integerinto NoICE FILE, FUNCTION, and DEFINESCOPED commands in the.NOI file (if any), to define function-scope variables, to allowmultiple scopes within a single C function. "Integer" is a scopenumber, and should be zero for the first scope, and incrementfor each new scope within the function. Since NoICE cannot currentlycope with scope finer than function, it will produce symbols ofthe form:FILE textFUNC nameDEFS name2_integer symbolvalueThe trailing "_integer" will be omitted for integer == 0 (function).11) The linker will process symbols with names of the formtext.name..FNinto NoICE FILE, DEFINE, and FUNCTION commands in the .NOIfile (if any), to define the start of a global function:FILE textDEF name symbolvalue %codeFUNC name symbolvalue12) The linker will process symbols with names of the formtext.name..SFNinto NoICE FILE, DEFINESCOPED, and SFUNCTION commands in the .NOIfile (if any), to define the start of a file-scope (static)function:FILE textDEFS name symbolvalue %codeSFUNC name symbolvalue13) The linker will process symbols with names of the formtext.name..EFNinto NoICE ENDFUNCTION commands in the .NOI file (if any) todefine the end of a global or file-scope function:ENDF name symbolvalue14) The linker will output the symbols in each "area" or memorysection in order of increasing address.15) The linker will output the ";!" lines after all symbolshave been output.The features listed above may be used to add full source-leveldebug information to assembly files produced by a compiler. Theexample file ctest1.c, and the hypothetical ctest1.s produced bycompiling it illustrate this. The comments in the file describethe information, but would not be present in an actual implementation.1) Begin each file with a ";!FILE" specifying the file name and itsoriginal extension (usually ".c"), and with the path if the file isnot in the current directory.;!FILE ctest1.c2) Define any basic data types: char defaults to S08. Redefine as U08 orASCII if you desire. "int" defaults to S16. Redefine if necessary.;!DEFT 0 char %ASCII3) Define any data structures, typedefs, enums, etc. (C generallydoes this per source file. Types will remain in scope unlessredefined). For example, the C structuretypedef struct {char c;int i;int ai[ 10 ];int *pi;} STR;would generate the commands:;!STRUCT 0. STR;!DEFT 0. c %char;!DEFT 1. i %int;!DEFT 3. ai %int[10.];!DEFT 23. pi %*int;!ENDS 25.Since the user can change input radix at will, it is generallyrecommended to specify radix explicitly in the ;! commands: bya trailing "." for decimal, or leading "0x" for hex.4) Use ;!FUNC, (or ;!SFUNC), ;!DEFS, and ;!ENDF to define anyfunction arguments and local variables. The functionvoid main( void ){/* declare some local variables */char lc, *plc;int *pli;int *lnpi;int *lfpi;...would generate stack-based symbol definitions with their datatypes.(Note that the stack offsets are not passed to the assembler byname, as they need not be relocated. Thus, it is the compiler'sduty to generate these. Note that the 68HC11 TSX instructionincrements the value of SP by one. Thus, "SP+nn" should use"nn" values one greater than for use as offsets from X.;!FUNC main;!DEFS lfpi SP+6. %*int;!DEFS lnpi SP+8. %*int;!DEFS pli SP+10. %*int;!DEFS plc SP+12. %*char;!DEFS lc SP+14. %charWhen all local variables and parameters have been defined, thefunction scope must be closed:;!ENDF5) In general, it is desirable to generate two symbols for eachfunction: one with an underbar, at the first byte of thefunction, so that the disassembler will show it as the destinationof the JSR; and a second without an underbar at the address ofthe first source line after stack frame is set up. The latterwill be a common breakpoint location.CUG292 can generate global symbols by using a "::"_main::tsxxgdxsubd #44xgdxtxs6) Once the stack frame is set up, declare the beginning of thefunction body. The value of this symbol is the lowest addresswhich NoICE will consider to be within the function for scopingpurposes.ctest1.main..FN::7) Each C source line which produces code should emit a symbolconsisting of the file name without path or extension, followedby the line number (in decimal) in the C source file.ctest1.56::ldd #6std _gestr8) Declare the end of the function body. The value of this symbolis the highest address which NoICE will consider to be within thefunction for scoping purposes. The address must be on or beforethe RTS, so that it does not overlap the following function.Normally, the address will be the last C source line in thefunction before stack frame is destroyed.ctest1.main..EFN::xgdxaddd #44xgdxtxsrts9) Global variables defined in the file, and their datatypes, may bedefined at any time. Debugging is most convenient if thetraditional C leading underbar is omitted. The global declarationsint gi;STR *pgstr;would generate:;!DEF gi %*intgi::.blkb 2;!DEF pgstr %*STRpgstr::.blkb 2Here, the ";!" command defines the datatype, which is unknown tothe assembler, while the "::" defintion defines the value, whichis unknown until link time.10) File-scope static variables, and their datatypes, must be definedbetween the ;!FILE and the ;!ENDFILE in order to set proper scope.Debugging is most convenient if the traditional C leading underbaris omitted. The static declarationsstatic int si;static STR sstr;would generate:;!DEFS si %*intctest1.si::.blkb 2;!DEFS sstr %STRctest1.sstr::.blkb 25We note that while the ;!DEFS must be between ;!FILE and ;!ENDFILE,the "::" definitions may be elsewhere in the file if it isconvenient, as the symbol name carries the scoping information.11) Function-scope static variables, and their datatypes, must bedefined between the ;!FUNC (or ;!SFUNC) and the corresponding;!ENDF in order to set proper scope. Debugging is most convenientif the traditional C leading underbar is omitted. The staticdeclarationsvoid main( void ){static int si;static STR sstr;would generate:;!FUNC mainat some point, and then;!DEFS si %*intctest1.main.si::.blkb 2;!DEFS sstr %STRctest1.main.sstr::.blkb 25We note that while the ;!DEFS must be between ;!FUNC and ;!ENDF,the "::" definitions may be elsewhere in the file if it isconvenient, as the symbol name carries the scoping information.12) After all code, data, and ;! defintions, declare end of file.This is necessary to prevent mangled scope when several modulesare linked together.;!ENDFILECTEST1.C - sample C source codeCTEST1.S - output from ImageCraft compiler, hand-doctoredto add additional debug informationCTEST2.C - second C moduleCTEST2.S - output from ImageCraft compiler, undoctoredCTEST.BAT - assemble and link CTEST1+CTEST2Run CTEST.BAT to produce CTEST1.NOI, a NoICE command file.end README