diff --git a/crossassemblers/macro11/CHANGES b/crossassemblers/macro11/CHANGES index 0cc8d0b..94ea61c 100644 --- a/crossassemblers/macro11/CHANGES +++ b/crossassemblers/macro11/CHANGES @@ -1,3 +1,1658 @@ + +xx-Dec-2023 Rhialto + - some very small adjustments to tests and docs. + - made a release version 0.9. + +----------- Mike Hill's entries ------------------ + +18-Oct-2023: Mike Hill + - README.MAC + Finally completed the 'macro11 Reference Manual' in source form + Generate the manual with ... macro11 README.MAC -l README.LST -ysl 8 -e BMK + +22-Aug-2023: Mike Hill + - assemble.c + Ignore .PAGE if .NLIST (list_level < 0) + Do not list .PAGE with comment if '.NLIST COM' + Strip trailing spaces/tabs from .TITLE string (page heading) + Strip trailing spaces/tabs from .SBTTL string (TOC) + - stream2.c + Do not translate to '.PAGE' if .NLIST (list_level < 0) + - macro11.h + Updated to version 0.9 and set date to "(22 Aug 2023)" + Re-ran all tests (all OKAY) - just in case. + +29-Jun-2023: Mike Hill + Add -apb and -ff command-line qualifiers + - listing.h + Declare page_break_ff (and list_page_ff) and auto_page_break (and line_on_page) and #define PAGE_LENGTH + Add force_throw parameter to list_throw_page() + - listing.c + Define page_break_ff (and list_page_ff) and auto_page_break (and line_on_page) + Update line_on_page and throw new page if > PAGE_LENGTH (only for -apb) + If -ff, use \f for page breaks + Add force_throw = FALSE on all calls to list_throw_page() + - macro11.c + Parse -abp and -ff and update 'usage' + Initialize line_on_page on each pass + - mlb2.c + #include "listing.h" + If -apb, put a page-break [ list_throw_page(TRUE) ] between -xl macros + - macro11.h + Updated date of version 0.8.9 to "(29 Jun 2023)" + Re-ran all tests (all OKAY) - just in case. + +01-Jun-2023: Mike Hill + Change version to 0.8.9 + - macro11.h + Only open .LIBRARY files once + - assemble.c + Make P_LIBRARY code look the same as P_INCLUDE + Scan the mlbs[] for each new hitfile and only open it if it is different to existing ones + Scan .MCALLs from the LAST mlb to the FIRST mlb (as documented for V05.00) + - macros.c + Generate an error if too many MLBs are opened + - macro11.c + - assemble.c + +25-May-2023: Mike Hill + - macro11.c - Fix usage message for '-fe' + - assemble_aux.c - Change "Invalid expression ... in .WORD" to ".BYTE" where appropriate + - assemble.c - Fix .CROSS .NOCROSS .GLOBL .WEAK bug if arg was invalid + Check for closing quote in .ASCII .ASCIZ .IDENT .RAD50 if -strict + - assemble.c + assemble_rad50() + also enforce MACRO-11 quirk of x...X being valid but x...x not + P_ASCII & P_ASCIZ + ditto + Fix error message truncation (e.g. "Junk at end of line") when inside a macro call ... use strcspn() now + - assemble.c + - parse.c + Fit 'more' symbols to symbol-table listing + - symbols.c - now fits -ysl 8 better (in exactly 80 columns for .LIST TTM) + Re-run tests - no differences! + Updated date of version 0.8.8 to "(25 May 2023)" + - macro11.h + +19-May-2023: Mike Hill + Add #define PASS1 and PASS2 to improve understanding + - assemble_globals.h + - assemble_globals.c + - assemble_aux.c + - macro11.c + - listing.c + - assemble.c + +18-May-2023: Mike Hill + Rename -x command-line option to -xtract (to avoid unwanted creation of lots of files if -m supplied) + - macro11.c + Also change '.END was not supplied' to WARNING instead of ERROR + Make .LIST show '+' for >=0 (similar to '-' for <0) and not 3-digits + - listing.h + Change (was commented-out) list_short_value() to list_signed_value() + - listing.c + Change (was commented-out) list_short_value() to list_signed_value() and put a '+' for >= 0 + - assemble.c + For P_LIST call list_signed_value() instead of list_3digit_value() + Add -fe and -fe2 command-line options to exit on fatal errors (pass 1 or [undocumented] pass 2) + - listing.h + Declare exit_if_pass and exit_requested (currently only used in listing.c -- future-use globally?) + - listing.c + Define exit_if_pass and exit_requested + 'report_fatal()' sets exit_requested if -fe/-fe2 + list_flush() now exits if exit_requested is TRUE + - macro11.c + Parse -fe and -fe2 and update 'usage' + Re-run tests - All differences were expected (either .LIST/.NLIST or WARNING for no .END) + +16-May-2023: Mike Hill + Added -xl command-line qualifier to store macro library contents to a single file (or list contents) + - mlb.h + Declare mlb_list() + - mlb2.c + Define mlb_list() to do the heavy-lifting + - mlb-rt11.c + Catch NULL return from mlb_entry() + - macro11.c + Update 'usage' with '-xl' + Remove (comment-out) command-line support for '-x -m ' to extract object modules + Only exit from '-x' if we have at least one '-m' file + Process the '-xl' option + Make the '.IF DF/NDF' code in extree optional (and disable it) + - extree.h + #define DISABLE_EXTREE_IF_DF_NDF 1 [ or 0 to ENABLE ] + - assemble.c + eval_ifdf_ifndf() should not WARN with -relaxed or (-ym11 [ -strict ]) if no parameter + P_IF now forces use of eval_ifdf_ifndf() if DISABLE_EXTREE_IF_DF_NDF + - extree.c + Put #if ... #endif around the EVALUATE_DEFINEDNESS stuff + Re-run tests - no differences! + +15-May-2023: Mike Hill + Added -nodev and -nodir command-line options to improve .INCLUDE & .LIBRARY portability + - macro11.h + Updated BASE_VERSION to "0.8.8" + - assemble_globals.h + Declared ignore_fn_dev and ignore_fn_dir + - assemble_globals.c + Defined ignore_fn_dev and ignore_fn_dir + - macro11.c + Added -nodev and -nodir and updated 'usage' + - assemble.c + Moved P_LIBRARY to follow P_INCLUDE (because they are closely related) + Made "Bad .INCLUDE/.LIBRARY file name" fatal (like MACRO-11 V05.05) + - parse.c + getstring_fn(): use ignore_fn_d* to cut the device and/or directory from the filename + Also change "empty-file" check to be (!RELAXED) instead of (STRICT) + Re-ran tests (-ym11 -strict and -stringent): test-include.mac - expected (error occurs in pass 1 too) + +09-May-2023: Mike Hill + Change "FATAL INTERNAL ERROR" if 'A=1 // A=B // B=2 // .END' to "warning" + - symbols.c + This [incorrect] warning message will now only be visible on pass 1 if '-yl1 -yl1' is provided + If .SBTTL occurs within a macro call, don't show the sequence number in the TOC + - assemble.c + P_SBTTL now shows a blank sequence (line) number in this case + Fix .GLOBAL UNDEF // .IF DF UNDEF ... incorrectly returning TRUE if -strict + - assemble.c + eval_ifdf_ifndf() now checks if SYM_IS_IMPORTED() + Also suppress the warning message if there is no parameter to .I[I]F [N]DF with -ym11 + Re-ran tests (-ym11 -strict and -stringent): exec.m11 (listing changes expected - but no changes in .OBJ) + Added DODO analog to TODO and NODO (because it's easy to grep/FIND it) + - util.h (#define DODO) + - assemble.c (use DODO in the comment from 04-May-2023) + Add a "NODO" compile-time option to make .NLIST COM suppress comment-only lines instead of only LHS-comments + - listing.c + Updated version to 0.8.7 on (09 May 2023) + - macro11.h + +04-May-2023: Mike Hill + Correct symbol-table comment: for R symbols: program segment number (unless ". ABS." or ". BLK.") + - symbols.c + Add some comments about suppressing .LIST and .NLIST listing lines + - assemble.c (and add a comment about always suppressing .PAGE if .NLIST COM) + Updated version to 0.8.6 on (04 May 2023) + - macro11.h + +30-Apr-2023: Rhialto + Fixes to make the code GCC compliant + - stream2.c + inject_source() now uses memmove() instead of strcpy() for overlapping move + - listing.c + Renamed trunc() to trunc_line() to avoid potential confusion over trunc() in math.h + Added { ... } in list_process() to fix GCC warning + Added (int) cast for offsetof(LSTFORMAT, ...) + - assemble.c + Added { ... } at P_PRINT to fix a missing pair (oops) and fix a GCC warning + - macro11.c + Added { ... } around 'if (enabl_debug)' to fix GCC warning + - symbols.c + Added a comment for '=' signifying absolute symbol values + +28-Apr-2023: Mike Hill + Add '-s' command line option to prepend source lines + - stream2.h + Declare inject_source() and stack_injected_source() + - stream2.c + Add a global buffer pointer 'inject_buf' + Define inject_source() and stack_injected_source() + - macro11.c + #include "stream2.h" + Add 2 parameters to prepare_pass() + Use inject_source() and stack_injected_source() to stack the '-s' statements(s) + Ditto for the '-yd' lines - instead of fprintf() + Update 'usage' + Parse the '-s' option(s) + Catch too many input files (MAX_FILES) + Allow no if '-s' provided + Check .IRP and .IRPC syntax better if -strict or -stringent + - rept_irpc.c + We now warn if .IRP[C] X (-strict) or .IRP[C] X, if '<...>' are not used (-stringent) + Check length of PROCESSED lines (e.g. expanded from macros) if -stringent + - assemble.c + Warn if the processed line is too long (> 132) + Added "[ignored]" to "Directive 'xxx' does not accept arguments" + Re-ran tests (-ym11 -strict and -stringent): test-blkb.mac (expected) + Updated date of version 0.8.5 to "(28 Apr 2023)" + - macro11.h + +27-Apr-2023: Mike Hill + Create function to pass arbitrary filename/linenumber to report_xxx() + - stream2.h + Declare stream_here() + - stream2.c + Define stream_here() + - assemble.c + Change P_REM to use stream_here() + Also, while we're at it, only show ".ENDR " warning if -strict + - macros.c + Change read_body() to use stream_here() + - macro11.c + Use stream_here() to report "Unterminated conditional" & append "(here)" + Re-ran tests (-ym11 -strict and -stringent): exec.m11 (expected) + Fix string overflow if -ysl > 6 + - macros.c + new_macro_stream() needs to allow space for SYMMAX_MAX (use a constant for efficiency) in the macro name + +25-Apr-2023: Mike Hill + Use report_fatal() instead of fprintf(stderr, ...) + - stream2.c + Enable dump_all_macros() with DEBUG_MACROS + - macros.h + #define DEBUG_MACROS 0 + Declare dump_all_macros() + - macros.c + Put dumpmacro() and dump_all_macros() behind #if DEBUG_MACROS + - macro11.c + #include "macros.h" + #if DEBUG_MACROS ... #endif + +24-Apr-2023: Mike Hill + Implement .NLIST LOC with .LIST SEQ,BIN + - listing.c + Put 'nlist_loc_only' into list_process() and segregate the list line on output + At last, we clean up the { ... } blocks we inserted and marked with /**/ on 04-Jan-2023 + - assemble.c + - macro11.c + - mlb-rsx.c + - mlb-rt11.c + - parse.c + - util.c + Do some of the [easier] TODOs + - assemble_aux.c + Use report_fatal() instead of fprintf() for duplicate global symbols (-ysl > 6) + - symbols.c + Use report_fatal() instead of report_err() so we see the messages on both passes + - macros.c + Changed message to "Body of '?' (here) has missing .ENDM" and made it fatal + Removed some of the '#if TODO' unused/old code + - assemble.c + - extree.c + - listing.c + - macro11.c + - parse.c + +22-Apr-2023: Mike Hill + For .LIST MEB return to the way MACRO-11 does it (for now) + - listing.c + Add #define HAVE_SEQ, HAVE_LOC, and HAVE_WORD1 to make the code easier to read + We now only check the location field has a valid digit + For .LIST LD keep the PC as an octal value (symbol table will all be decimal if .LIST LD in effect) + - listing.c + Only translate the 'words' to decimal when calling list_oct2dec() + For .IF and .IFT/.IFF/.IFTF and .IFF (false) put '=' in the flags field + - listing.h + Add list_value_if() and rename list_value_short() to list_value_short_if() + - listing.c + Add list_value_if() and rename list_value_short() to list_value_short_if() + - assemble.c + Call list_short_value_if() instead of list_short_value() and list_value_if() at P_IF: + Re-ran tests (-ym11 -strict and -stringent) -- differences (expected) in: + test-enabl-lcm.mac, endm, if, prec, psect, + at.sml, code.m11, exec, expr, fltg, getl, lout, mac, macro, misc, often, pst, srch, xlat + +20-Apr-2023: Mike Hill + Implement report_warn() etc. + - listing.h + Use new report_xxx() definitions and declare report_generic() + - listing.c + report_generic() is similar to report() but prints "WARNING", "ERROR", and { "FATAL" = also on pass 1 } + - assemble.c + Changed two OC_MARK errors to ERROR instead of WARNING + - assemble_aux.c + Appended "[.EVEN implied]" to ".WORD on odd boundary" message + Re-ran tests (-ym11 -strict and -stringent) -- differences (expected) in: + test-asciz.mac, blkb, if, include, jmp, opcodes, packed, prec, ua-pl, word-comma, exec.m11 + Rename ARGS_ALL_TYPES to ARGS_VISIBLE (to make its function clearer) + - symbols.h, symbols.c, macro11.c + +19-Apr-2023: Mike Hill + Implement command-line options -p1 and -p2 (pass1 or pass2 only) + - macro11.c + Add execute_p1 and execute_p2 (default TRUE) + Update 'usage' + Init errcount and report_errcnt_p1 to 0 + Move dump_dirargs() outside prepare_pass() + In prepare_pass() - lookup_sym(DEBUG_SYM_ERRCNT) instead of if (!pass) + Only execute the relevant passes and show the relevant messages + Fix .REM /TEXT/ [closing quote on same line] when .NLIST COM is in effect + - assemble.c + We now return early within P_REM if needed + +18-Apr-2023: Mike Hill + Also support -a0 for .NARG .NCHR .NTYPE .PACKED + - assemble.c + New routine abs_section_addr() + Call this wherever we want a 'variable' absolute_section or add a /* TODO */ if unsure + - assemble_aux.c and parse.c + Add a /* TODO */ wherever absolute_section is referenced (no code changes) + Tweaked the listing level output + - assemble.c + Changed the limits for P_NLIST/P_LIST + Re-ran tests -- differences (expected) in: test-listing.mac, at.sml, code.m11, ... + Show the count of characters in the .IRPC assigned to the symbol + - rept_irpc.c + list_value() in expand_irpc() + Re-ran tests -- differences (expected) in: exec.m11, expr, fltg, getl, lout, mac, misc, srch, xlat + Make -e and -d suppress the "unsupported" error message (e.g. for PIC if -stringent) + - symbols.h + #define ARGS_MUST_SUPPORT + - symbols.c + Add '!' to ARG_FLAGS() + Change ARGS_IGNORE_THIS to ARGS_MUST_SUPPORT for the STRINGENT stuff + Remove the STRINGENT tests from list_symbol_table() and list_section() + - listing.c + Remove the STRINGENT test from list_process() for LIST(LD) + - macro11.c + Set ARGS_MUST_SUPPORT if -e or -d + Make sure that line-suppression is cleared after listing a line (because we missed it somewhere) + - listing.c + list_flush() now clears the LIST_SUPPRESS_LINE + LIST_FORCE_LISTING flag after listing line + +17-Apr-2023: Mike Hill + .PSECT ABS,CON will override '-a0' for that section (you never know if you'll want it somewhen) + - assemble.c + Correctly handle ": :" and "= [=] [:]" e.g. "A : : NOP" or "A = = : 0" + - parse.c + skipwhite() in get_op() and also loop for multiple labels on a line (even if -stringent) + - assemble.c + Near 'reassemble:' skipwhite() after ':' and '=' (unless -stringent) + +16-Apr-2023: Mike Hill + Fix (and enhance) V05.03 change to put ABS symbol in ABS section referencing '.' into the current section + - assemble_globals.h + Declare abs_0_based + - assemble_globals.c + Define abs_0_based = 0 + - macro11.c + Add '-a0' command-line option + - assemble.c + Note that with '-a0' and 'ABS=.' the object file may now change (use: dumpobj -nosort) + Near 'reassemble:' we check for '.' and create a new ex tree with current_pc + We also now assign absolute symbols to the current PSECT if it is ABS and '-a0' + Re-ran tests (with -a0 and without) -- differences (expected) in: test-reg.mac + Change -ylls to list "[$nnnnn]" instead of "[L=nnnn]" + - listing.c + Change -ylls to also show the blank section number in the symbol table (normally suppressed as per V05.05) + - symbols.c + If -sp then .PRINT lines should always appear in the listing + - assemble.c + .LIST and .NLIST should never be listed if within a macro expansion (ignore limits we set below) + - assemble.c + Re-ran tests -- differences (expected) in: exec.m11, lout + If no TOC and we've seen a .TITLE on pass 1, print a title string at the top of the listing (unless -e BMK) + - listing.h + Declare list_title_line() + - listing.c + #include and #include "macro11.h" + Define list_title_line() and move code from P_SBTTL in assemble.c + - assemble.c + Remove #include and "macro11.h" + Move title output from assemble.c to listing.c + - macro11.c + Call list_title_line() on pass 2 (if TOC not shown) + Report a warning if .IRPC has > 124 characters (-stringent) + - rept_irpc.c + Move MAX_FILE_LINE_LENGTH + - From assemble.h to assemble_globals.h + +14-Apr-2023: Mike Hill + For -yfl [etc.] put a '-' in listing column 1 if line would have been suppressed but isn't + - listing.c + Add this to list_flush() + Only list .LIST and .NLIST outside macro calls unless not 'usual' + - In P_LIST: check for !within_macro_expansion() + +13-Apr-2023: Mike Hill + Increase field widths for psect-numbers (just in case [unlikely] we have > 0777 of them) + - symbols.c + Make list_short_value() & list_3digit_value() signed (because we want it for .NLIST later on) + - listing.c + We now put '-' in the flag field if < 0 (e.g. 177777 -> ' 001-') + Add listing action of LIST_FORCED_LISTING to force the listing of the current line (overrides LIST_SUPPRESS_LINE) + - listing.h + Declare LIST_FORCED_LISTING and listing_forced + #define MUST_LIST_THIS_LINE() similar to DONT_LIST_THIS_LINE() + - listing.c + Define listing_forced and use LIST_FORCE_LISTING in list_flush() + Set list_line_act '&= ~LIST_FORCE_LISTING' and '|= listing_forced' in list_source() + - macro11.c + Add -yfl (force listing) command-line option and update 'usage' + - assemble.c + P_LIST & P_NLIST without parameters always lists line for '-yd' and not for 'usual' .LIST or .NLIST levels + Make the list_level match the docu (6.1.1) + - symbols.c + Set [E_LIS] to 0 instead of 1 + - assemble.c + Change P_SBTTL to use new 'list-range' of >= 0 + Change P_NLIST & P_LIST accordingly + - listing.c + Change dolist() to use new 'list-range' of >= 0 and check for LIST_FORCE_LISTING + Change handling of LIST(ME) in list_process() -> treat '(list_level >= 0) as .LIST ME + - macros.c + Change read_body() to use new 'list-range' of >= 0 + Re-ran tests -- differences (expected) in: test-listing.mac, at.sml, code.m11, ... exec, ... lout + Suppress extra listing line at EOF if .END not seen + - stream2.c + LIST_SUPPRESS_LINE if EOF and len==0 in file_getline() + Also, if EOF: increment sequence number + Re-ran tests -- differences (expected) in almost ALL files + +12-Apr-2023: Mike Hill + Put in the stubs for .CROSS & .NOCROSS without parameters + - assemble.c + Move cases for P_CROSS & P_NOCROSS from (1) to (4) + Update E_CRF if we should + Add listing 3-digit values to the 'value' column [like list_short_value() etc.] + - listing.h + Declare list_3digit_value() + - listing.c + Define list_3digit_value() similar to list_short_value() + - assemble.c + Change listing of L_LIS value (.LIST/.NLIST) to 3-digits + Re-ran tests -- differences (expected) in: test-listing.mac, exec.m11 + [at.sml, code.m11, debug, errs, expr, fltg, getl, lout, mac, macro, mist, often, pst, srch, syscalls, xlat, xpcor] + List the 3-digit psect number for .ASECT, .CSECT, .PSECT, .SAVE, and .RESTORE + Re-ran tests -- differences (expected) in: test-backpatch.mac, enabl-ama, jmp, psect, reg, reloc, ua-pl + code.m11, debug, errs, exec, ... + +11-Apr-2023: Mike Hill + Put the version creation date into the -v string when -yd is specified first + - macro11.h + #define THIS_VERSION in addition to VERSIONSTR + - macro11.c + If -yd is followed by -v output THIS_VERSION before outputting VERSIONSTR + Truncate listing lines which are longer than a certain length + - listing.h + #define MAX_LISTING_LINE_LEN 148 (120 would match V05.05) + - listing.c + Use MAX_LISTING_LINE_LEN in trunc() + Handle file-input lines which are too long (-strict) and implement .ENABL CDR (-e CDR) + - symbols.c + Set the correct flag for E_CDR + - stream2.h + Declare from_file_stream() + - stream2.c + Define from_file_stream(str) to return TRUE if 'str' is a file_stream + - assemble.h + #define MAX_FILE_LINE_LENGTH 132 + - assemble.c + Use from_file_stream() and check line length against MAX_FILE_LINE_LENGTH (first, like RSX) + If ENABL(CDR) truncate the input line after 72 characters + - listing.c + Truncate show_error_line() at 132 characters (to avoid VERY long errors with -se or -sp) + +09-Apr-2023: Mike Hill + Fix bug in .REPT where a count of > 077777 would really [try to] repeat this many times + - rept_irpc.c + expand_rept() now checks bit 15 of the repeat count + If .IIF is FALSE, show the flag '0' in the value column (a-la .IFT etc.) + - assemble.c + P_IIF: Add a call to list_short_value() + Re-run all the tests -- differences (expected) in: + .mac: test-enabl-lcm, test-if, test-psect + .m11: exec, lout, macro, pst, xlat + Add ARGS_HIDDEN as a DIRARGS flag + - symbols.h + #define it + - symbols.c + dump_dirargs() shows it as ';' + Set BMK and LD to HIDDEN + Add the LSB number to the LHS of the listing for -ylls + - symbols.h + Declare int looked_up_local_sym + - symbols.c + Define looked_up_local_sym + Set looked_up_local_sym to TRUE for local symbols in lookup_sym() + - listing.c + Set looked_up_local_sym to FALSE in list_source() + If -ylls and looked_up_local_sym then list the 'lsb' as "[L=xxxx]" at the LHS + - assemble.c + P_ENABL LSB & P_DSABL LSB set looked_up_local_sym to TRUE + P_DSABL now does not start a new LSB unless '-relaxed' (see docu section 3.5 for the reason) + Disable (#if DEBUG_LSB) "LSB:" debugging in get_next_lsb() + - parse.c + Also disable (#if DEBUG_LSB) "LSB:" debugging in get_symbol() + - macros.c + expandmacro() no longer resets the local symbol range for '?' arguments (30000$ ...) when the LSB changes + - macro11.c + last_locsym = START_LOCSYM (without the '- 1' at the end) + Re-run all the tests -- differences (expected) in: + .m11: exec, expr, fltg, lout, mac, xlat + Support relative flag (') when listing symbolic values + - listing.h + Declare list_symbol() + - listing.c + #include "object.h" + Add the list_symbol() function + - assemble.c + Call list_symbol() where necessary (near reassemble: and P_PRINT & P_END) + Re-run all the tests -- differences (expected) in: + .mac: test-backpatch + .m11: exec, lout, macro + Change places where '#if 0' really means '#if TODO' to ... 'TODO' (and ... 'NODO' = we know we don't want to do it) + - util.h + #define NODO 0 + #define TODO 0 + - assemble.c, assemble_aux.c, extree.c, listing.c, macro11.c, macros.c, parse.c + Replace '#if 0' with either '#if TODO' or '#if NODO' + +08-Apr-2023: Mike Hill + Clean up symbol-table printf formats etc. + - symbols.c + For .PRINT use '-sp' instead of '-se' + - listing.h + Declare show_print_lines and #define show_print_line() show_error_line() + - listing.c + Define show_print_lines + - macro11.c + Add '-sp' and update 'usage' + - assemble.c + Changed the P_PRINT code to use show_print_line() + Also changed "Argument to .RADIX" error message from "(%d)" to "(%d.)" + Enable both list_location() and list_value() to be used on the same listing-line + - listing.c + Add a new routine list_format_value() ready for handling flags + Call list_value_flags() from list_value() and list_short_value() + Re-run all the tests -- differences (expected) in: + .m11: getl, lout, mac, xlat + - assemble.c + Call list_location() in P_PRINT + Join P_BYTE and P_WORD together + Enable .BLKB & .BLKW to show the byte/word count in the value field + Re-run all the tests -- differences (expected) in: + .mac: test-backpatch, test-blkb, test-bsl-mac-arg + .m11: code, debug, expr, fltg, getl, lout, mac, macro, misc, srch, xlat, xpcor + +07-Apr-2023: Mike Hill + 'Misuse' .[N]LIST LD to 'list decimal' - similar to '.[N]LIST HEX' + Ignored if -stringent or -ym11 else takes precidence over .LIST HEX + - symbols.c + Add a 3rd format string to list_symbol_table() and list_section() + - listing.c + Add a new function list_oct2dec() a-la list_oct2hex() and call it from list_process() + Add command line option '-se' to show error lines after error messages + - listing.h + Declare show_error_lines and show_error_line() + - listing.c + Define show_error_lines + Add function build_list() similar to can_list() and dolist() to support show_error_lines + Replace some calls to can_list() or dolist() with build_list() + Add function show_error_line() -> format is always .LIST SEQ,LOC,BIN,SRC,COM,TTM and .NLIST BEX,LD,HE + Change how list_process() detects 'BEX' lines (in case we one-day show '*' for errors) + list_flush() now also shows the line in error on stderr if '-se' + - macro11.c + Add '-se' and update 'usage' (and edited the usage text a little) + - assemble.c + P_PRINT now shows the .PRINT on stderr if '-se' + Fixed symflags() bug and add missing flags + - symbols.c + 'return temp' instead of fp + Add 'Weak', 'Undefined', 'Local', 'Implicit_global' + Replace some calls to list_value() with list_short_value() + - listing.h + Declare list_short_value() similarly to list_value() + - listing.c + Define list_short_value() - exactly like list_value() except the value is NOT zero-filled (test: not even displayed) + - assemble.c + Enable all related TODO code -- .IFT .IFF .IFTF and .IF DF/NDF/IDN/DIF/B/NB + Where we replace list_value() with list_short_value() + Replace where necessary -- .IFT .IFF .IFTF and .IF DF/NDF/IDN/DIF/B/NB (currently still TODO) + Suppress generation of list_value() if an "Invalid .[I]IF condition" error is generated + Re-run tests with list_short_value() display suppressed + The only difference is in 'test-if' due to the above suppressed list_value() + Enable output of short value in list_short_value() and re-run tests -- differences (expected) in: + test-if & test-prec + 2.11BSD-m11: code, exec, expr, fltg, getl, mac, macro, misc, often, pst, xlat + All of .IFT .IFF .IFTF and .IF DF/NDF/IDN/DIF/B/NB now show whether code will be generated (0=No, 1=Yes) + Re-run all the tests with: + - symbols.c + list_symbol_table() + Use octal section numbers + Suppress the section number for the blank section + Suppress some trailing spaces + list_section() + Use octal section numbers + If -stringent and -rsx, do not show the "NOSAV" flag + - assemble.c + .LIST and .NLIST now show the list-level in the 'value' column (six-digit octal) + Check for .NTYPE outside macro-call if -stringent + - macros.h + Added within_macro_expansion() + - macros.c + Added #include "rept_irpc.h" + Added within_macro_expansion() + - listing.c + Removed #include "rept_irpc.h" + Use within_macro_expansion() instead of doing it directly + - assemble.c + Use within_macro_expansion() and generate warning if .NTYPE is invalid (-stringent) + +06-Apr-2023: Mike Hill + Initialize title_string on pass 2 (for future use) + - macro11.c + Make 'list_line_act |= LIST_SUPPRESS_LINE' easier to read + - listing.h + #define DONT_LIST_THIS_LINE() (list_line_act |= LIST_SUPPRESS_LINE) + - assemble.c + Replace 'list_line_act |= LIST_SUPPRESS_LINE' with 'DONT_LIST_THIS_LINE()' + Support m11 extensions to MACRO-11 + - assemble_globals.h and assemble_globals.c + Add extern int support_m11 (default = 0) + - macro11.c + Add the '-ym11' command line option (sets support_m11 = 1) + - symbols.c + Add CNZ (=CLN|CLZ) and .MACR (=.MACRO) and .ENABL/.DSABL PIC (but PIC not implemented) + - assemble.c + Add all the m11 .PSECT flags (except we do not [yet] zero BSS sections) + Fixed some missing casts to (unsigned char) for isdigit() + - assemble_aux.c + - listing.c + Re-run all the tests with: + RSX: -stringent -e BMK -e ME + BSD: -strict -ym11 -e BMK -e ME -dc BEX,ME + Only some of the BSD listing files differ: + There is now a TOC for some of the files + No error messages for the .PSECTs with BSD-specific flags + The section list after the symbol table has some PSECTs with different [BSD-compatible] flags + Which also means the object files are different (but only vary in the PSECT flags) + +05-Apr-2023: Mike Hill + Changed version to 0.8.5 + - macro11.h + Fixed .NLIST CND bugs (because I didn't test properly when I wrote it) + - assemble.c + Suppress line BEFORE we parse it when 'suppressed' + Suppress .[I]IF DF,NDF,IDN,DIF,B,NB statements too + Add code to put the TRUE/FALSE flag for DF,NDF,IDN,DIF,B,NB into the value field (currently disabled) + Add missing symbols (LDUB, LDSC, STQ0) and add .ENABL/.DSABL BMK,DBG & .LIST/.NLIST LD + - symbols.h & symbols.c + Implement .ENABL BMK + - assemble.c + If .ENABL BMK (or -e BMK) we no longer write the version or date & time on the TOC heading + Slightly changed the TOC format to " %6d - %s" (now using SIZEOF_MEMBER() etc.) + +04-Apr-2023: Mike Hill + Detect badly formed .MACROs + - macros.c + If -strict we now enable a previously disabled check in defmacro() + Join the two DIRARGs enabl_arg* & list_arg* into enabl_list_arg* + - symbols.h + Removed E_res and replaced E__LAST & L__LAST with ARGS__LAST + Changed ENABL() and LIST() + Replaced lookup_enabl_arg() and lookup_list_arg() with lookup_arg() + - symbols.c + As symbols.h + load_dirargs() only needs to update enabl_list_arg[] + dump_dirargs() now uses flags to determine the arg type + usage_dirarg*() only needs to scan enabl_list_arg[] + lookup_arg() replaces lookup_enabl_arg() and lookup_list_arg() + - listing.h + Changed #define list_level -to- enabl_list_arg[L_LIS].curval + - assemble.c + Use lookup_arg() and enabl_list_arg[] etc. + Join P_NLIST + P_LIST and P_DSABL + P_ENABL together + - macro11.c + Use lookup_arg() and enabl_list_arg[] etc. + Add the -dc command line option (like -d) to disable changes to directive arguments + - symbols.h + Add #define ARGS_IGNORE_THIS + Add #define ARGS_IGNORE() + - symbols.c + Updated dump_dirargs() + - assemble.c + Use ARGS_IGNORE() + - macro11.c + Added 'flags' to enable_tf() + Added "-dc" option and updated 'Usage' + Implement .NLIST CND + - symbols.c + Change the flags for L_CND + - assemble.c + Suppress the line if !LIST(CND) in P_IF etc. + For .IIF suppress the line (if FALSE) or reload the listing line (if TRUE) + Fix .NLIST MD + - assemble.c + Suppress the '.MACRO' line at P_MACRO + Implement .NLIST MC,ME,MEB + - symbols.c + Change the flags for MC,ME,MEB + - assemble.c + Suppress the listing line for macro calls if .NLIST MC + - listing.h + Declare list_within_exp + - listing.c + Add #include "macros.h" and #include "rept_irpc.h" + Define list_within_exp + list_source() sets list_within_exp if the source line came from a macro/rept/irp/irpc + list_process() now handles suppressing .NLIST ME,MEB + Ran test suite with command line options -e ME -d TOC -dc BEX,ME,MEB + And got the same results as 0.8.3 (from 28-Mar-2023) + Except ... test-packed ... because of .LIST HEX + Changed version date of 0.8.4 to 04-Apr-2023 + - macro11.h + +03-Apr-2023: Mike Hill + Change debug level of LSB debug messages to 2 + - assemble.c + - parse.c + Implement .SBTTL + - listing.h + Declare title_string[32] and toc_shown + - listing.c + Initialize title_string[32] and toc_shown + - macro11.h + Add #define PROGRAM_NAME "macro11" + - macro11.c + Use PROGRAM_NAME instead of "macro11" + Arrange for a page-throw if the TOC is shown + - assemble.c + Add #include and #include "macro11.h" + Move P_TITLE to above P_SBTTL + P_TITLE: Acquire the title string + P_SBTTL: Output the TOC heading and .SBTTL + Further .LIST/.NLIST & .ENABL/.DSABL enhancements + - symbols.c + list_symbol_table() + Make sure that -ysl 64 works with .LIST TTM + list_section() + Removed ",NOSAV" if -stringent and -rsx [ TODO: enable it in list_symbol_table() ] + Add flags field to dirargs and changed show_dirargs() to dump_dirargs() + - symbols.h + Add the #define ARGS_* from symbols.c here + - symbols.c + Remove the #define ARGS_* here (they are in symbols.h now) + Changed how the flags are used + - macro11.c + Updated dirargs + +01-Apr-2023: Mike Hill + Further .LIST/.NLIST & .ENABL/.DSABL enhancements + - symbols.c + Changed flags for SEQ,LOC,BIN,HEX in add_dirargs() + - listing.h + Changed LSTFORMAT (again) + - listing.c + Added #include and + Changed uses of LSTFORMAT + Completed .LIST TTM + Added .NLIST SEQ,LOC,BIN + Added .LIST HEX + - listing.c + New routine - list_oct2hex() + - symbols.c + list_symbol_table() + Added printf formats for octal & hex + Miss off the trailing space on output lines [ TODO: enable it in list_symbol_table() ] + list_section() + Added printf formats for octal & hex + Removed ",NOSAV" if -stringent and -rsx (currently commented out) + +31-Mar-2023: Mike Hill + Further .LIST/.NLIST & .ENABL/.DSABL enhancements + - assemble.c + For -yd: show_dirargs() at the end of pass 2 + - symbols.c + Changed flags for BEX in add_dirargs() + - listing.h + Changed LSTFORMAT + - listing.c + Added .[N]LIST BEX + +30-Mar-2023: Mike Hill + Symbol table with < 2 PSECTs which have relocatable symbols no longer shows section numbers + - symbols.c + Do not show the section number for the blank section (line MACRO-11) + Currently disabled to keep the test output consistent [ TODO: enable it in list_symbol_table() ] + Updated 'usage' for -e and -d + - symbols.h + Changed L_lev to L_LIS + Add usage_dirargs() + - symbols.c + Changed L_lev to L_LIS + load_dirargs() now copies the defval to curval for L_LIS + Add usage_dirarg() and usage_dirargs() + Updated ADD_LIST_ARG(BEX & ME) + Made add_dirarg() static etc. + show_dirargs() now ignores flags for L_LIS + - macro11.c + Move add_dirargs() to before call to print_help() + Removed old -e and -d options list + Call usage_dirargs() to show the new options list + enable_tf(): -e LIS increases list level, -d LIS decreases it all others now keep the flags intact + - assemble.c + P_LIST / P_NLIST now need to disable use of 'LIS' + Remove list_bex and list_me (initialized but never used) + - listing.h + - listing.c + - macro11.c + - assemble.c + Change list_level from an int to a reference to list_arg[L_LIS].curval = LIST(LIS) + - listing.h + #include "symbols.h" + Replace extern int list_level with #define + - listing.c + Remove int list_level + Change list_md to LIST(MD) + - listing.h (remove list_md) + - listing.c (ditto) + - macro11.c (ditto) + - assemble.c (ditto) + - macros.c (replace) + - rept_irpc.c (replace) + Change enabl_xxx (xxx=ama,gbl,lc,lcm,lsb,mcl) to ENABL(xxx) and remove opt_enabl_ama + - assemble_globals.h (remove) + - assemble_globals.c (remove) + - assemble.c (remove & replace) + - macro11.c (remove) + - parse.c (replace enabl_ama) + - assemble_aux.c (replace enabl_gbl) + Further .LIST/.NLIST & .ENABL/.DSABL enhancements + - symbols.c + Add comments and change flags for various add_dirargs() + - parse.c + Only round floating-point if .DSABL FPT (default) + - assemble.c + P_SBTTL: only show TOC if .LIST TOC + P_REM: only list .REM contents if .LIST COM (default) + - listing.c + Add trunc() to truncate lines + Add list_process() to process lines before listing them + .NLIST SEQ,LOC,BIN,SRC suppresses all lines + .NLIST COM does not list lines starting with ';' or empty lines (unlike MACRO-11) + .NLIST SRC does not list lines which would only have a sequence number (also unlike MACRO-11) + The listing line will be truncated -- and suppressed if empty (TODO: .NLIST SEQ LOC BIN) + Note: all parts of lines with errors will always be listed + +29-Mar-2023: Mike Hill + Changed version to 0.8.4 + - macro11.h + Added flags to DIR_ARGS defval (currently not exported in symbols.h) + - symbols.c + add_dirarg() #define some flags + add_dirargs() use those flags + load_dirargs() only copy bit 0 from defval to curval + show_dirargs() show the flags + - macro11.c + Removed commented-out copies of lookup_enabl_arg() and lookup_list_arg() + Never suppress listing lines on pass 1 for -yl1 + - listing.c + +28-Mar-2023: Mike Hill + Addition of .LIST/.NLIST & .ENABL/.DSABL & -e/-d command line options + - symbols.c + Do not display symbol table/psects if .NLIST SYM (-d SYM) + Implement .LIST TTM for symbol table (only) + - macro11.c + #include "parse.h" [we now use skipwhite() etc.] + Parse the options to -e and -d in enable_tf() + We now allow -e "AMA TTM" or -d AMA,TTM etc. + - assemble.c + Fixed bug when .ENABL/.DSABL parameter was not a valid label (e.g. .ENABL ?). + Added lookup_enabl_arg() to P_ENABL & P_DSABL + Moved P_LIST and P_NLIST to before P_ENABL and joined them together + Added lookup_list_arg() to P_LIST (and P_NLIST) + Implemented .[N]LIST BEX, MD, and ME (using previous variables) + List the list_level in the value field for empty .LIST or .NLIST (currently disabled) + List the suppressedness in the value field for .IFT .IFF .IFTF (currently disabled) + Changed version to 0.8.3 and date to 28-Mar-2023 + - macro11.h + +27-Mar-2023: Mike Hill + Fixed "dangling else" and case "FALL THROUGH" GCC informationals found by Rhialto + - assemble.c + Added {} to 'if' and '/* FALL THROUGH */' comments + Fixed __VA_ARGS__ with no '...' args in GCC found by Rhialto + - listing.h + Changed #define report_* to ##__VA_ARGS__ + Fixed the bug Rhialto found that I introduced with pagination (\f .MACRO X ; followed by a blank line) + - macros.c + Now flush the listing buffer BEFORE calling stack_getline() + - assemble.c + Suppress the LIST_SUPPRESS_LINE for .END (in case of '\f' before .END) + Added memcheck() to malloc() + - assemble_aux.c + - symbols.c + Added -ylls command line option (local symbols are no longer listed in the symbol table by default) + - macro11.c + Populate symbol_list_locals and update 'usage' + - symbols.h + Declare symbol_list_locals + - symbols.c + Define symbol_list_locals + Use symbol_list_locals instead of skip_locals in symbol_table() + Do not show the DOT symbol unless -ylls + Only sort and print the "Symbol Table" heading if there are symbols to list + Changed symbol_table() to check_sym_invariants() for all symbols during first scan of the symbol table + List the sections outside the symbol-sort block + Enable use of rad50cmp() to sort the symbol table etc. + Prepare for .ENABL/.DSABL and .LIST/.NLIST additions + - symbols.h + Add struct DIRARG and enums enabl_args & list_args and extern DIRARG for enabl & list + Add #define ENABL() and LIST() to get a current directive argument value + Declare add_dirargs() & load_dirargs() & show_dirargs() + - symbols.c + Define DIRARG for enabl & list + Add routines for add_dirargs() & load_dirargs() & show_dirargs() + - macro11.c + add_dirargs() before parsing the command line + load_dirargs() at the start of each pass + If -yd and/or -yl1 show_dirargs() + +21-Mar-2023: Mike Hill + Fixed error message within macro call showing following lines + - assemble.c + Use (..., "...%*s\n", strcspn(cp, "\n"), cp); + .IF XX now treats undefined symbols as zero (still with an error) + - assemble.c + Removed the "Pick something" and replaced it with a new_ex_lit(0) + We now check for -ysl > 6 that there are no duplicate globals + - assemble_aux.c + Added write_psect_globals() to check the globals and output the psects & globals + - symbols.h + Declare symbol_compar() + - symbols.c + Remove 'static' from symbol_compar() + +20-Mar-2023: Mike Hill + Fixes after trying all tests with -stringent (.mac) and -strict (.m11) + - parse.c + case '_': report_warn() only (but return correct result) + case 'r': changed report_warn() message + case 'p': report_warn() only (but return correct result) + - assemble.c + eval_ifdf_ifndf(): report_warn() if missing symbol only if -strict*2 (m11 treats '.IF DF' w/o arg as FALSE) + P_END: now removes LIST_PAGE_BEFORE flag from list_line_act + P_IF: now always lists the result of the .IF (extension to MACRO-11, but I like it) + P_IIF: now never lists the result (because the TRUE part might want to put a value there) + OC_1GEN: and OC_JSR: now always show the message for "JMP/JSR/TSTSET/WRTLCK Rn is impossible" + - symbols.c + dump_sym(): Replaced report_err() with report_warn() -- just to be ready + +17-Mar-2023: Mike Hill + Changed .INCLUDE to NOT try to include the file if there is a syntax error on the line (like V05.05) + - assemble.c + CHECK_EOL before trying to open the include file + - util.c + Added a memcheck() to defext() + Allow whitespace after '@' and '-' etc. -- E.g. 'TST @ - ( R0 )' + - parse.c + Avoid phase errors & improve handling of complex register refs. [ e.g. A = %B // TST A // B=5. // L1: // .END ] + The down-side is that illegal register assignments will list the wrong value (e.g. A=%8. will list 177700) + The same applies to instructions referencing illegal registers (e.g. MOV %8.,-(SP) will generate 170046) + - symbols.h + Add an extra reg_sym to handle errors (REG_ERR) + - symbols.c + add_sym() REG_ERR: "%E" = 0177700 (chosen so binary generated is visibly wrong) + - assemble_aux.c + get_register() now returns NO_REG if SECTION_REGISTER value is invalid (only applies to REG_ERR = reg_sym[8]) + - extree.c + evaluate() returns REG_ERR if the EVALUATE_OUT_IS_REGISTER is out-of-range + - parse.c + get_mode() does not return FALSE for NO_REG errors but DOES return error message + - assemble.c + New routine get_mode_check() as a wrapper to get_mode() + Replaced all calls to get_mode() with get_mode_check() + Put a local wrapper around call to get_fp_src_mode() + +16-Mar-2023: Mike Hill + Further updates for page throwing + - assemble.c + .PAGE XXX now throws the error after throwing the page + - listing.h + Add list_page_fmt + Add list_throw_page() + - listing.c + Add list_page_fmt = "\n\n" + Add list_throw_page() + - stream2.c + Throw a page between files (command-line or .INCLUDE) + +15-Mar-2023: Mike Hill + Updated report_xxx() for future expansion (see 06-Mar) + - listing.h + Added #define REPORT_xxx + Changed #define report_xxx to use __VA_ARGS__ + Make sure that EVALUATE_OUT_IS_REGISTER for a bad register returns a non-EX_LIT EX_ERR + We do this to get the same result as tests/test-reg.mac (but any result is "wrong") + - extree.c + Sort symbol table in RADIX50 order (make ready) + - symbols.c + Added two routines: rad50order() and rad50cmp() + Added a #define rad50cmp to [currently] disable this + Fixed initialization of 'longest_symbol' in list_symbol_table() + Removed the ":" from "Program sections" to match "Symbol table" + Implement page throws (2 x \n) before .PAGE [not suppressed if ';'] (also for '\f') and after .INCLUDE + Note: Blank lines (truly blank: 'whitespace != blank') after a [suppressed] page-throw are suppressed + - listing.h + Added int list_page_top, list_line_act with #define LIST_* + - listing.c + Added int list_page_top, list_line_act + list_flush() now uses LIST_* to throw pages + report() now throws a page before the error message if the line asked for a page-throw 'BEFORE' + - macro11.c + Initialize list_page_top & list_line_act in prepare_pass() + - assemble.c + P_PAGE: throw page and suppress '.PAGE' if no comment on line (unlike MACRO-11, we don't auppress if ';') + P_INCLUDE: throw page after showing .INCLUDE + - stream2.c + Handle '\f' with both BEFORE and AFTER (and suppress lines which ONLY have '\f' -- like .PAGE) + +14-Mar-2023: Mike Hill + Further changes + - assemble.c + Silently [unless -stringent] truncate .CSECT & .PSECT names to max 6 characters (-ysl > 6) + - parse.c + Allow whitespace between '^' and 'p' etc. (if not -stringent) + Finish cleaning up the ^P code + +13-Mar-2023: Mike Hill + More changes to parsing and error handling + - assemble.c + If not -strict*2 treat .END as only .ENDing the current stack level + This allows the .m11 .INCLUDE files (which contain .END) to assemble + Handle .ENDR as a synonym to .ENDM but with a -stringent warning if it has a parameter + Changed "unknown flag ... given to .PSECT directive" message (P_CSECT/P_PSECT) + ... and treat it as a warning (continue parsing) + Disallow '.' as a .CSECT or .PSECT name [ but commented out - it IS allowed on RSX ] + Changed error message '.END within .IF' to be clearer + Undid the change from 02-Mar to set pseudo symbols flags = SYMBOLFLAG_GLOBAL (there is ALWAYS a better way) + - symbols.c + Restored ADD_SYM_PSEUDO to (SYMBOLFLAG_PERMANENT | SYMBOLFLAG_DEFINITION) + - assemble.c + For -strict we now check for this in eval_ifdf_ifndf() else we allow '.IF DF .ASCII' etc. + +11-Mar-2023: Mike Hill + Changed version to 0.8.2 + - macro11.h + Fixes to stuff found by Rhialto:- + - macro11.c + Fixed order of printf() parameters + - assemble.c + Fixed .ASCII and .ASCIZ error message for ';' as a quote + Also for .IDENT .INCLUDE .LIBRARY .RAD50 (unless -stringent) and .REM + Added a TODO for P_REM: to make 'top_here' into a routine in stream2 + - parse.c + Fixed ^PX looping + Also fixed ^P etc. (started rewriting the case 'p': part) - but need to clean it up + +09-Mar-2023: Mike Hill + Added syntax checking for .CROSS & .NOCROSS and create GX symbols if undefined (as V05.05) + - symbols.h + Added #define SYMBOLFLAG_NONE 0 (for readability) + Added dump_sym() because I needed to test the changes + - symbols.c + Made dump_sym() non-static + Added the missing "%s" to dump_sym() for " IMPLICIT_GLOBAL" + - assemble.c + Joined P_CROSS & P_NOCROSS to P_GLOBAL + Added a 'switch' to set up the symtab and flags for each directive + add_sym() to the relevant table with the relevant flags + Added and/or fixed a few comments (TODOs) + - rad50.c + - rad50.h + - macro11.c + - macro11.h (put today's date in VERSIONSTR) + +07-Mar-2023: Mike Hill + Allow EMT and TRAP to have -ve argument + - assemble.c + Move SPL from OC_1REG to OC_MARK (to improve error handling & messages) + - symbols.c + - assemble.c + Fix when x is external and < 0 also a is < 0 + - extree.c + Make EX_LSH use three operations (EX_AND & EX_DIV & EX_AND) if right < 0 + +06-Mar-2023: Mike Hill + Prepare for the addition of report_err(), report_warn(), and report_fatal() + - listing.h + Add #define for them - TODO: we create instances! + Put in report_warn() where the result would be the same if no warning were issued & also report_err() where not + - assemble.c (and fixed wrong return value in P_PACKED:) + - assemble_aux.c + - extree.c + - macro11.c + - macros.c + - parse.c + - rept_irpc.c + - symbols.c + If not -strict, allow alpha-numerics as quotes to .INCLUDE & .LIBRARY + - parse.c + Improve EX_DIV and EX_MUL optimization and fix (best-effort) EX_LSH (see above) + - extree.c + Fix .FLT2 .FLT4 .LIMIT on odd boundary + - assemble.c + Added a new section which forces all directives 'which require it' to an even boundary + Clean up related tests which have become unnecessary + +05-Mar-2023: Mike Hill + Allow '_' as a valid rad50 character (only if -yus) + Note that the resulting object file may not be compatible with RSX/RT-11 if global symbols or psects contain '_' + - rad50.h + Add a routine rad50_enable_underscore() to switch this feature on + - rad50.c + Renamed radtbl[] to rad50tbl[] and added rad50charset[] & rad50charset_ul[] + unrad50() always translates using '_' and returns "???" if invalid + Recompile dumpobj to make use of this and display GLOBAL symbols & PSECTs with '_' correctly + - macro11.c + Call rad50_enable_underscore() if -yus + .BYTE should report values outside the range -400:377 + Also applies to .ASCII & .ASCIZ <...> format + - assemble_aux.c + Added a check to store_word() + Added the symbol value to the listing for .NCHR [always] and .NTYPE [only visible if .LIST ME] + - assemble.c + Changed P_NCHR: & P_NTYPE: + +04-Mar-2023: Mike Hill + Disallow '.' used in the wrong context + - macros.c + defmacro() now rejects '.' as a macro name + - assemble.c + Reject '.' as LHS of ':' (at reassemble:) + Do not allow '.' as a .GLOBL or .WEAK argument + Parse .INCLUDE and .LIBRARY correctly + - assemble.c + P_LIBRARY now catches getstr_fn() == NULL + - parse.c + getstr_fn() returns NULL if final quote does not match initial quote (-strict) + ditto for empty filename (e.g. .INCLUDE //) + +03-Mar-2023: Mike Hill + assert() some (just 'pass' at the moment) of the variables we use are really correct (and won't cause trouble later) + - macro11.c + Added assert() to prepare_pass() + +02-Mar-2023: Mike Hill + Fix .IF DF .directive is TRUE (e.g. .IIF DF .WORD .ERROR ; We know '.WORD') + - symbols.c + Set pseudo symbols flags = SYMBOLFLAG_GLOBAL + Clean up the symbol table creation (making the code easier to read) + - symbols.h + Sorted and aligned the pseudo_ops and instruction_ops + Put the comments on the same line as the operand_codes + - symbols.c + Replace ADD_SYM() with 3 new #defines for ADD_SYM_REGISTER, ADD_SYM_PSEUDO, and ADD_SYM_INST + Sort and align them all within their categories (pseudo sorted by value-text!) + +28-Feb-2023: Mike Hill + Added STRINGENT to check the code against the documentation not the V05.05 assembler + - assemble_globals.h + Added #define STRINGENT and STRINGENTNESS + - macro11.c + Added -stringent and changed usage etc. + Added pseudo-symbols for directives with > 6 characters if -ysl > 6 and not -stringent + - symbols.c + Added the same error as JMP Rn to TSTSET and WRTLCK + - assemble.c + +27-Feb-2023: Mike Hill + Fix A / B where A[15] or B[15] = 0x8000 + - extree.c + EX_DIV: Made '/' signed + EX_LSH: Optimized A _ B if A == 0 and comment that '_' always does a signed shift (see above) + Pre-process each directive before parsing the arguments + This catches stuff like ;;;;; // .ASCII writing "random" bytes to the object file without error + - assemble.c + Added a large switch block at the beginning of 'case SECTION_PSEUDO:' + Removed individual checks from many of the other cases + +25-Feb-2023: Mike Hill + Handle EX_DIV by 0 (e.g. A = 1/0) + - extree.c + Unless RELAXED we throw an error else result = 0 + Allow certain complex expressions of EX_ERR type to be stored and assigned to symbols + - assemble_aux.c + Added additional code to do_word() + - assemble.c + Added additional code to symbol assignment ('=') at reassemble: + - parse.c + Remove ^PL / ^PH code to generate a new_ex_lit(0) on pass 1 ... + ... because we now handle this correctly + Do not store values > 16 bits (e.g. A=200000 // B=A/2 sets B->100000 not 0) + - extree.c + new_ex_lit() now stores only 16-bits + - symbols.c + Added a check in check_sym_invariants() + +24-Feb-2023: Mike Hill + Allow .END to continue parsing for -relaxed*2 + - assemble_globals.h + Added #define VERY_RELAXED + - assemble.c + if (VERY_RELAXED) just continue parsing the source file(s) + +23-Feb-2023: Mike Hill + Check for correct format of .ENDR and .ENDM (if -strict) + - macros.c + Fixed (or commented) all the 'FIXME's + - assemble.c + Added .IFZ, etc. (PAL-11R defines .IFEQ .IFZ etc. as backwards compat.) + - symbols.h + Renamed P_IFDF to P_IFXX + - assemble.c + Renamed P_IFDF to P_IFXX + - symbols.c + Renamed P_IFDF to P_IFXX + add_sym() for .IFZ .IFEQ .IFNZ .IFNE .IFL .IFLT .IFG .IFGT .IFLE .IFGE + Disallow ^PLpsect and ^PHpsect if -strict*3 + - parse.c + Generate errors for constants out-of-range + - parse.c + Catch ' at EOL (MACRO-11 'A' error) + Catch " at EOL or 1 character before EOL (MACRO-11 'A' error) + Catch constant value > 0177777 (if -strict*3) (MACRO-11 'T' error) + +22-Feb-2023: Mike Hill + Disallow '_' (Logical SHift) if -strict*3 + - parse.c + Report .END within .IF (unless -relaxed) + - assemble.c + Report .END within .MACRO & .REPT / .IRP / .IRPC (if -strict) + - macros.c + Better handling of unclosed .MACRO & .REPT / .IRP / .IRPC + - macros.c + read_body() remembers stack->top of the directive and ... + ... writes out an error on pass 1 if no .ENDM / .ENDR found + - rept_irpc.c + Pass ".REPT" / ".IRP" / ".IRPC" to read_body() + +21-Feb-2023: Mike Hill + Improve line number handling if \f is in the stream + - stream2.c + file_getline() now skips leading FF characters + We still mess up the line numbers of lines containing \f (as before) + Similarly, process vertical-tabs (VT or \v) as EOL characters + +20-Feb-2023: Mike Hill + For -yd and not -yl1 ... show the .SBTTL contents during pass 1 + This is an aid to debugging until we get the whole TOC stuff working + - assemble.c + Added code to P_SBTTL: to do this + report() if .END is missing for '-strict -strict -strict' [-strict*3] + - assemble_globals.h + Added #define STRICTEST + - assemble_aux.c + Handle xfer_address == NULL + - macro11.c + Only if !STRICTEST initialize the xfer_address to ex_lit(1) (else NULL) + If STRICTEST and no .END -> ".END was not supplied" + Put in a stub to catch a recursive stack_push() ... E.g. for a macro which calls itself + - stream2.h + Add stack_depth & MAX_STACK_DEPTH + - stream2.c + Init stream_depth to 0 and update it in stack_push() and stack_pop() + If we hit MAX_STACK_DEPTH write an error message to stderr + TODO: Handle this better (perhaps with a fatal() routine) + +19-Feb-2023: Mike Hill + Fixed .RADIX <> 8 at the end of pass 1 causing pass 2 to start with wrong radix + - macro11.c + Set radix = 8; in prepare_pass() + - assemble_globals.c + Initialize radix to zero instead of 8 + Catch undefined local symbol references (must not be turned into global references) + - assemble_aux.c + EX_UNDEFINED_SYM: now also detects local symbols (starting with a digit) + migrate_implicit() should not migrate local symbols + - symbols.c + Force add_sym() to always set SYMBOLFLAG_LOCAL for local symbols (even if not requested) + And throw a report() message (but you'll only see it on pass 1 if you -yl1 -yd) + Only dump_sym() in check_sym_invariants() if (enabl_debug) + - assemble.c + free(label) for local symbols in P_GLOBL: and also parse the rest of the line + Also catch more .GLOBL / .WEAK syntax errors + Catch local symbols out of range (0$ and much-too-large$) + - assemble_globals.h + Added BAD_LOCSYM = 999999 + - parse.c + get_symbol() -> report() error for local symbol out-of-range (without stack->top) + Retro-fit STRICT to changes made earlier which ought to be behind -strict + - assemble.c + report() 'Local symbol may not assigned a value' is now only for -strict + Replaced all 'STRICT_IF_DF_NDF' with if (STRICT) { ... } = -strict + Test this with the regression tests - if okay, remove the non-strict code + Enhanced STRICT_IF_DF_NDF_RECURSES -> if '-strict -strict' we DO NOT allow recursion < ... > + Test this with the regression tests - if okay, set the #define to 0 + JMP Rn etc. is now not shown if -strict -strict (-strict might be better) + 'Z' type errors are not shown if -relaxed + -strict: report() if .PSECT flags change (but change them anyway) + -strict: disallow 'MARK #' (same for EMT / TRAP) + -relaxed: allow 'MARK' without parameters else report() + +16-Feb-2023: Mike Hill + - assemble.c + Improve .END handling + Fix MARK to require a parameter (also after '#' - not strictly supported) + +15-Feb-2023: Mike Hill + Add a check for closing quote to .REM + - assemble.c + Also write a message for the original .REM if the closing quote is missing + And the quote characters must be case-insensitive (regardless of .ENABL LCM) + +14-Feb-2023: Mike Hill + Fix .REM to list "discarded" lines and check for EOL after closing quote + - assemble.c + +13-Feb-2023: Mike Hill + Retro-fitted changes by Rhialto made on 12-Feb-2023 + - parse.c + Added (unsigned char) to isdigit() and toupper() + - rept_irpc.c + Changed message "Invalid .IRPC syntax (label" -> "... (symbol" + - symbols.c + Added comments to MED6X and MED74C (and new ones to STA0, STB0, XFC) + +12-Feb-2023: Mike Hill + Additions to report() on pass 1 + - macro11.c + Print errcount after the symbol table has been listed (because '-l -' wants it there) + Added a counter to main() to count the report() errors during pass 1 + This is only printed for -yl1 and/or -yd + - listing.c + Added a "*" at the left of the listing line for each report() on that line [pass1] (-yl1 only) + Maybe do this for pass2 (?) -or- [in future] write out the error-letter(s) like MACRO-11 + +11-Feb-2023: Mike Hill + -macro11.h + Do not #include "git-info.h" #if defined(SKIP_GIT_INFO) + - dumpobj.c + Only compile the auto-detect code #if DEFAULT_OBJECTFORMAT_RT11 < 0 + #include "git-info.h" #if !defined(SKIP_GIT_INFO) + Show the GIT_VERSION and GIT_AUTHOR_DATE if available + #define VERSION to 11-Feb-2023 + +10-Feb-2023: Mike Hill + Add some -yd symbols to the symbol table for use by .MAC regression tests + - listing.h + Add global variable 'report_errcnt' + - symbols.h + Add ADD_DEBUG_SYM() etc. + - macro11.c + Set 'report_errcnt=0' in prepare_pass() + Show the 'report_errcnt' after showing the 'errcount' + Add "$M11.D =: " symbol (constant: count of -yd options) + Add "$M11.E =: " symbol (increases with each error) + Add "$M11.S =: " symbol (constant: -strict = 1, -relaxed = -1, else 0) + Add them as permanent symbols to stop them being unintentionally overwritten + Send them to the listing + - listing.c + Update 'report_errcnt' in report() and update "$M11.E" + Stop report() printing the error message twice if option "-l -" is used + Keep the symbol table clean in add_symbols() + - symbols.c + Show the section name in check_sym_invariants() for "undefinedness" + The final ADD_SYM() used flags=0 which is "naughty" (but I don't see the need) + +09-Feb-2023: Mike Hill + Add '-strict' and '-relaxed' command-line options + - assemble_globals.h + Add strictness variable (<0 = relaxed, 0 = normal, >0 = strict, >1 = stricter) + - assemble_globals.c + Initialize strictness variable to zero + - macro11.c + Add help text for -strict & -relaxed (and update some other help text) + Parse -strict & -relaxed to generate value for 'strictness' + Added a message to show and list the -yd debug level (if > 0) + If debugging, print the version information to the lstfile (unless '-l -') + Only write the 'version' to the list file if -yd is followed by -v on the command line + Added a 'pass #' -yd debug message at the start of each pass + +08-Feb-2023: Mike Hill + - dumpobj.c + Fixed gcc warnings (embedded comments; int and will report an error when -strict[est] is used (later) + +04-Feb-2023: Mike Hill + - dumpobj.c + Added -h option to show the help text and examples + +02-Feb-2023: Mike Hill + Enhancements (fixes) to dumpobj + - dumpobj.c + LDA output will not write data > MAX_LDA_ADDR + Added command options: + -align : aligns GSD fields (easier to read) + -noalign : GSD format remains the same as previous versions + -nosort : does not sort the GSD entries (preserves OBJ order) + -of : same as '-o' but does not relocate 'internal' RLDs + -w <...> : add patch words to the LDA file + -x : provide a new LDA transfer address + Summary '-s' now shows a list of PSECTs (currently disabled) + Error message if an LDA file is written with more than one PSECT with data + +01-Feb-2023: Mike Hill + Enhancements (fixes) to dumpobj + - dumpobj.c + Now writes the relocated 'Internal' RLD to the LDA output + Added -o for LDA output + Made CSECT + VSECT + 'COMPRTN' + LIBHDR + LIBEND a 'badbin' + +31-Jan-2023: Mike Hill + Changed the blank PSECT name for '-rsx' to ". BLK." (commented out!) + - macro11.c + Enhancements to dumpobj + - dumpobj.c + Implement auto-detection of object type when -rt11 and -rsx not specified + #define DEFAULT_OBJECTFORMAT_RT11 -1 /* Enables this option */ + Added '-q' [quiet] and '-qt' [quiet-text] and '-s' [summary] options + Changed the usage text accordingly and added information about dumpobj + Count bad format (badfmt) and bad binary errors (badbin) + +24-Jan-2023: Mike Hill + Implement 'Z' errors [JMP/CALLR/JSR/CALL to Rn is not an error with V05.05] + - assemble.c (OC_1GEN & OC_2GEN and OC_JSR) + CALLR Rn / CALLR (Rn)+ / JMP (Rn)+ + CALL Rn / JSR Rn,(Rm)+ + MOV[B]/CMP/BIT/BIC/BIS/SUB/XOR Rn,[@]-(Rn) and [@](Rn)+ + +24-Jan-2023: Mike Hill + Retrofit all changes made in 0.9wip from GitLab on 17-Aug-2022 + - assemble.c + Changed report() from 'Illegal' to 'Invalid' etc. + Parse multiple labels (A: B: ...) using 'while' instead of 'goto' + New parameter to add_sym(... , *stream); + Added in P_PACKED: (after moving 'int' and 'char *' to the top of the block) + - assemble_aux.c + New parameter to add_sym(... , *stream); + Changed report() from 'Illegal' to 'Invalid' + - macros.c + Changed report() from 'Illegal' to 'Invalid' + - parse.c + ^R allows > 3 characters + - rept_irpc.c + Changed report() from 'Illegal' to 'Invalid' ... 'expected' + - symbols.c + New parameter to add_sym(... , *stream); + New parameter to check_sym_invariants(... , *stream); + New checks: permanent symbol redefinition/phase error + New report: 'Bad symbol redefinition' + - symbols.h + New parameter to add_sym(... , *stream); + +17-Jan-2023: Mike Hill + Bug fixes + - extree.c + evaluate_rec(,EVALUATE_DEFINEDNESS) guarantees new_ex_lit(non-zero) + This now solves the real issue of '.IF DF A ! B' not firing when A=0 + - assemble.c + Correctly parse multiple labels on one line (e.g. 'A: B: C: NOP') + +16-Jan-2023: Mike Hill + Enhancements + - assemble.c + Added an option to have STRICT_IF_DF_NDF (without EVALUATE_DEFINEDNESS) + This parses the line directly without creating a dummy tree etc. + There is a further option to define STRICT_IF_DF_NDF_RECURSES ... + ... which enables the use of < ... > within a .IF/.IIF DF/NDF + This is to solve the issue of '.IF DF A ! B' not firing even if A is defined + See 19-Feb-2023 above! + +09-Jan-2023: Mike Hill + Enhancements + - assemble.c: + '.EOT' is now ignored (same as MACRO-11 V05.05) + '.MDELETE' now throws an error if no arguments + Added a place-holder for 'P_PACKED:' (see 0.9wip above) + Added some 'TODO:' comments for future reference + Local symbols may not be set global (using '::') (at reassemble:) + Setting is ignored - symbol remains local + Local symbols may not be the LHS of an '=' assignment (at reassemble:) + Local symbol is assigned anyway - error message only + Changed generated local symbol range from 32768$ to 30000$ + - assemble_globals.h + Added #define START_LOCSYM to 30000 and MAX_LOCSYM to 65535 + - assemble_globals.c + Changed initializer for last_locsym from 32768 to zero + - macro11.c + Use START_LOCSYM-1 instead of 32767 + - macros.c + Use START_LOCSYM instead of 32768 + - parse.c + Reject local symbols > MAX_LOCSYM + Note that V05.05 simply wrapped the value (65537$ == 1$) + +07-Jan-2023: Mike Hill + Updates + - macro11.h: + #define BASE_VERSION "0.8.1" [ change to "0.9" when we're done ] + #define VERSIONSTR changes + - macro11.c: + Added 'modified' string to print_version() + +06-Jan-2023: Mike Hill + Enhancements + - assemble.c: + '.PAGE' now checks there are no arguments + +05-Jan-2023: Mike Hill + Enhancements + - assemble.c: + '.BLKW' now throws an error on a byte boundary and forces .EVEN + '.PRINT' and '.ERROR' both now list the value (if provided) + '.END' now shows the transfer address (if any) in the listing + .PRINT/.ERROR/.END need more work to list the same value as SYM=x should + Bug fixes + - macro11.c: + Changed error messages for '-ysl' + - symbols.c: + Changed '.MDELE' to '.MDELETE' (else mismatch if -ysl > 6) + Changed '.NOCRO' to '.NOCROSS' (else mismatch if -ysl > 6) + - assemble.c: + '.END' now terminates processing of all input streams + - parse.c + Local-symbol length increased to SYMMAX_MAX (e.g. for 0000$10) + +05-Jan-2023: Mike Hill + Created a 'WIN32 BASELINE' dumpobj.exe & macro11.exe from yesterday's sources + This is to locally test against Rhialto's original 0.8 from 07-Jul-2022 + Compile with /W4 (warning level 4) with _CRT_SECURE_NO_WARNINGS + In order to get the maximum error checking possible for future edits + - util.c: + Use the 'comma' operator to avoid 'assignment within conditional expression' + Typecast to avoid 'conversion ... possible loss of data' warnings + - assemble.c: + Typecast to avoid 'conversion ... possible loss of data' warnings + Remove unreachable 'return 1' after 'for(;;)' + - macros.c: + Move assignment to avoid 'assignment within conditional expression' + Unpack complex 'if' to avoid 'assignment within conditional expression' + Avoid 'non-constant aggregate initializer' for 'macstack' + - mlb2.c: + Use the 'comma' operator to avoid 'assignment within conditional expression' + - mlb-rsx.c: + Assign NULL to 'ent2' to avoid 'potentially uninitialized local variable' + Typecast to avoid 'conversion ... possible loss of data' warnings + - mlb-rt11.c: + Assign NULL to 'ent2' to avoid 'potentially uninitialized local variable' + Changed 'char' to 'int' for 'c = fgetc()' + Typecast to avoid 'conversion ... possible loss of data' warning + - object.c: + Typecast to avoid 'conversion ... possible loss of data' warnings + - parse.c: + Move assignments to avoid 'assignment within conditional expression' + Comment out 'if (1)' to avoid 'conditional expression is constant' + Typecast to avoid 'conversion ... possible loss of data' warning + - stream2.c: + Typecast to avoid 'conversion ... possible loss of data' warning + +04-Jan-2023: Mike Hill + Restored portability with C/C++ from Microsoft Visual Studio 9.0 + Microsoft (R) 32-bit C/C++ Version 15.00.30729.01 for 80x86 + Microsoft (R) Incremental Linker Version 9.00.30729.01 + Compile with /W3 (warning level 3) with _CRT_SECURE_NO_WARNINGS + - dumpobj.c: + For WIN32, use stricmp() instead of strcasecmp() + Comment out memcheck() because it is duplicated from util.h + Put a {} block around embedded type statement + These are marked with /**/ in all source modules + Indentation has NOT been changed + - util.c: + For WIN32, use a local copy of [my_]strtok_r() + Put a {} block around embedded type statements (see above) + - git-info.h: + Created dummy file because macro11.h #includes it + - macro11.c: + For WIN32, use stricmp() instead of strcasecmp() + Move type statement outside for() statement + Put a {} block around embedded type statement + - assemble.c: + Move type statement outside for() statements + Put a {} block around embedded type statements + - macros.c: + If WIN32, use a local copy of [my_]stpncpy() + - parse.c + Put a {} block around embedded type statements + Change statement order to avoid embedded type statements + Move type statement outside for() statement + - mlb-rt11.c: + Commented out mlb_rt11_vtbl structure names + Put a {} block around embedded type statement + - mlb-rsx.c: + Commented out mlb_rsx_vtbl structure names + Put a {} block around embedded type statements + - symbols.c: + Moved type definition to avoid embedded type statement + Put a {} block around embedded type statements + +----------- Rhialto's entries ------------------ + +??.??.202?: Rhialto [GitLab version from 17.08.2022] + version 0.9wip: + - Fix ^R to ignore RAD50 characters after the first three.(Paul Koning) + - Add .packed directive. + - Better check for bad symbol redefinitions. + 07.07.2022: Rhialto version 0.8: - Improve parsing of symbols, e.g. `4..` is not a symbol. @@ -76,7 +1731,7 @@ - Very simple .LIST and .NLIST implementation. - Add ^pl and ^ph expressions from 2.11BSD's m11. - Object-ified macro libraries with an eye to support the .sml - files from 2.11BSD's m11. But since the given file does not + files from 2.11BSD's m11. But since the given file does not just contain .MACROs (it even contains conditionals) I'm not sure how it is supposed to work. - Added 2.11BSD/m11 as test files. To make this reasonable, a @@ -119,9 +1774,7 @@ Begin rework by Joerg Hoppe (j_hoppe@t-online.de) All my changes are marked with "/*JH: .. */" comments - ------------ Richard Krebiehls entries ------------------ - +----------- Richard Krebiehl's entries ------------------ 15-July-2001 version 0.2 diff --git a/crossassemblers/macro11/CHANGES_dumpobj.txt b/crossassemblers/macro11/CHANGES_dumpobj.txt new file mode 100644 index 0000000..16f26be --- /dev/null +++ b/crossassemblers/macro11/CHANGES_dumpobj.txt @@ -0,0 +1,68 @@ + +----------- Mike Hill's entries ------------------ + +13-Feb-2023: Mike Hill + - dumpobj.c + RLD 'global reloc' records now count as bad records for LDA output + Minor 'LDA output' error message changes etc. + #define XFERAD_WHEN_ZERO 2 instead of using a constant (2) + #define VERSION to 13-Feb-2023 + +11-Feb-2023: Mike Hill + - dumpobj.c + Only compile the auto-detect code #if DEFAULT_OBJECTFORMAT_RT11 < 0 + #include "git-info.h" #if !defined(SKIP_GIT_INFO) + Show the GIT_VERSION and GIT_AUTHOR_DATE if available + #define VERSION to 11-Feb-2023 + +08-Feb-2023: Mike Hill + - dumpobj.c + Fixed gcc warnings (embedded comments; int MAX_LDA_ADDR + Added command options: + -align : aligns GSD fields (easier to read) + -noalign : GSD format remains the same as previous versions + -nosort : does not sort the GSD entries (preserves OBJ order) + -of : same as '-o' but does not relocate 'internal' RLDs + -w <...> : add patch words to the LDA file + -x : provide a new LDA transfer address + Summary '-s' now shows a list of PSECTs (currently disabled) + Error message if an LDA file is written with more than one PSECT with data + +01-Feb-2023: Mike Hill + Enhancements (fixes) to dumpobj + - dumpobj.c + Now writes the relocated 'Internal' RLD to the LDA output + Added -o for LDA output + Made CSECT + VSECT + 'COMPRTN' + LIBHDR + LIBEND a 'badbin' + +31-Jan-2023: Mike Hill + Enhancements to dumpobj + - dumpobj.c + Implement auto-detection of object type when -rt11 and -rsx not specified + #define DEFAULT_OBJECTFORMAT_RT11 -1 /* Enables this option */ + Added '-q' [quiet] and '-qt' [quiet-text] and '-s' [summary] options + Changed the usage text accordingly and added information about dumpobj + Count bad format (badfmt) and bad binary errors (badbin) + +05-Jan-2023: Mike Hill + Compile with /W4 (warning level 4) with _CRT_SECURE_NO_WARNINGS + In order to get the maximum error checking possible for future edits + +04-Jan-2023: Mike Hill + Restored portability with C/C++ from Microsoft Visual Studio 9.0 + Microsoft (R) 32-bit C/C++ Version 15.00.30729.01 for 80x86 + Microsoft (R) Incremental Linker Version 9.00.30729.01 + Compile with /W3 (warning level 3) with _CRT_SECURE_NO_WARNINGS + - dumpobj.c: + For WIN32, use stricmp() instead of strcasecmp() + Comment out memcheck() because it is duplicated from util.h diff --git a/crossassemblers/macro11/Makefile b/crossassemblers/macro11/Makefile index 5f7df01..1ed64a6 100644 --- a/crossassemblers/macro11/Makefile +++ b/crossassemblers/macro11/Makefile @@ -8,7 +8,8 @@ OBJFORMAT ?= -DDEFAULT_OBJECTFORMAT_RT11=0 #SANITIZE ?= -fsanitize=address -fsanitize=undefined -fsanitize-recover=all -fno-omit-frame-pointer DEBUG ?= -ggdb $(SANITIZE) OPT ?= -O3 -CFLAGS ?= -std=gnu99 $(WARNS) $(DEBUG) $(OPT) $(OBJFORMAT) +CFLAGS ?= -std=gnu99 $(WARNS) $(DEBUG) $(OPT) +LDFLAGS ?= MACRO11_SRCS = macro11.c \ assemble.c assemble_globals.c assemble_aux.c \ @@ -17,21 +18,24 @@ MACRO11_SRCS = macro11.c \ MACRO11_OBJS = $(MACRO11_SRCS:.c=.o) -DUMPOBJ_SRCS = dumpobj.c rad50.c +DUMPOBJ_SRCS = dumpobj.c rad50.c util.c DUMPOBJ_OBJS = $(DUMPOBJ_SRCS:.c=.o) +# object.c has some special extra flags. +CFLAGS.object = ${OBJFORMAT} + ALL_SRCS = $(MACRO11_SRCS) $(DUMPOBJ_SRCS) -all: macro11 dumpobj +all: macro11 dumpobj readme.lst tags: macro11 dumpobj ctags *.c *.h macro11: git-info.h $(MACRO11_OBJS) Makefile - $(CC) $(CFLAGS) -o macro11 $(MACRO11_OBJS) -lm + $(CC) $(CFLAGS) $(LDFLAGS) -o macro11 $(MACRO11_OBJS) -lm -dumpobj: $(DUMPOBJ_OBJS) Makefile +dumpobj: git-info.h $(DUMPOBJ_OBJS) Makefile $(CC) $(CFLAGS) -o dumpobj $(DUMPOBJ_OBJS) $(MACRO11_OBJS): Makefile @@ -41,9 +45,12 @@ git-info.h: ./make-git-info # Bootstrap dependency on the git header file, which otherwise -# gets generated too late. +# (sometime) gets generated too late. macro11.o: git-info.h -macro11.c: git-info.h +dumpobj.o: git-info.h + +readme.lst: macro11 README.MAC + ./macro11 README.MAC -l readme.lst -ysl 8 -e BMK clean: -rm -f $(MACRO11_OBJS) $(DUMPOBJ_OBJS) macro11 dumpobj @@ -63,9 +70,9 @@ argtests: macro11 if [ $$? = 1 ]; then echo PASS; else echo FAIL; fi; \ echo " $$OPT fol. by option"; \ done - @ ./macro11 foo.mac $$OPT -x -v 2> /dev/null; \ + @ ./macro11 foo.mac $$OPT -xtract -v 2> /dev/null; \ if [ $$? = 1 ]; then echo PASS; else echo FAIL; fi; \ - echo " -x must be the last option" + echo " -xtract must be the last option" LSAN_OPTIONS=suppressions=../macro11.supp @@ -80,7 +87,7 @@ endif # Make .d files as side effect of compiling .c to .o %.d %.o: %.c - $(CC) $(CFLAGS) -c -o $*.o $< + $(CC) $(CFLAGS) $(CFLAGS.$*) -c -o $*.o $< @set -e; rm -f $*.d; \ $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o \1.d : ,g' < $@.$$$$ > $*.d; \ diff --git a/crossassemblers/macro11/README b/crossassemblers/macro11/README index f4be345..4ec8e02 100644 --- a/crossassemblers/macro11/README +++ b/crossassemblers/macro11/README @@ -100,6 +100,15 @@ macro11 [options...] files... Options: -v Prints program version. + -a0 Use sections with ABS,OVR for their own local symbol + definitions. + + -nodev Ignore 'ddn:' style device names in .INCLUDE and + .LIBRARY filenames. + + -nodir Ignore '[directory]' style names in .INCLUDE and + .LIBRARY filenames. + -e opt .ENABL option. Implemented options are AMA, GBL, and also .LIST options ME, BEX, and MD, though the status of listing control is @@ -136,6 +145,21 @@ Options: (DEV:[DIR]FILE.EXT) then the search is also tried without DEV: and without DEV:[DIR]. + -s Prepend a to the source + (e.g. -s "DEBUG\t=\t1\t\t; Enable"). + + -se Shows source line in error after the error message. + + -sp Shows .PRINT source lines on stderr (similar to -se). + + -fe Fatal errors will abort assembly. + + -apb Automatic page-break after 57 listing lines + (also with -xl). + + -ff Use a form-feed between listing pages + (also with -apb & -xl). + -o objname Gives the name of the object file. No extension is assumed; if you want .OBJ you have to say it. With no -o option, no object file is generated. @@ -146,7 +170,7 @@ Options: say it. With no -l option, no listing file is written. - -x Tells macro11 not to assemble anything, but rather + -xtract Tells macro11 not to assemble anything, but rather to simply extract all the macros in all the -m macro libraries into individual .MAC files in the current directory. This should be the last option @@ -154,13 +178,19 @@ Options: This also works for extracting an object library (.OLB) file. - -rsx Tells macro11 to generate rsx style object files. + -xl as -xtract but stores macros to '-l' file. If none, only lists names. + + -rsx Tells macro11 to generate rsx style object files + and expect libraries in rsx format. This is the default. - -rt11 Tells macro11 to generate rt11 style object files. + -rt11 Tells macro11 to generate rt11 style object files + and expect libraries in rt11 format. Various options starting with -y enable extensions: + -yd Extension: enable debugging + -ysl Allow longer symbols up to the given length. The maximal allowed value is 64. @@ -170,6 +200,12 @@ Options: listing file. This may be useful for finding phase errors. + -yfl Force listing of suppressed lines. + + -ylls List local symbols when used and in the symbol table. + + -ym11 Support BSD m11 language extensions. + files... Any number of input files. They will be assembled as if they were concatenated together. diff --git a/crossassemblers/macro11/README.MAC b/crossassemblers/macro11/README.MAC new file mode 100644 index 0000000..f1d2e6a --- /dev/null +++ b/crossassemblers/macro11/README.MAC @@ -0,0 +1,1389 @@ + .NLIST ; Don't list the preamble (unless '-e LIS') + .NLIST SEQ,LOC,BIN ; Disable listing SEQuence, LOCation, & BINary + + +; *********************************************************************** +; ** m a c r o 1 1 R E F E R E N C E M A N U A L ** +; *********************************************************************** +; ** The COPYRIGHT NOTICE appears at the end of this source file ** +; *********************************************************************** + +; +---------------------------------------------------------------------+ +; | H O W T O C R E A T E T H E m a c r o 1 1 M A N U A L | +; +---------------------------------------------------------------------+ +; +; macro11 should be used to create the Reference Manual as follows:- +; +; macro11 README.MAC -l README.LST -ysl 8 -e BMK +; +; Some additional optional qualifiers:- +; +; -e LIS Lists the preamble (not recommended) +; -dc LIS,CND Force listing of the whole source file (not recommended) +; -ysl 6 Truncates symbols (like .INCLUDE) to max. 6 characters +; -ym11 Enables m11 (2.11BSD Unix) extensions (CNZ and .MACR) +; -dc TTM Makes 'Sample Listing' and symbol table 132 columns wide +; -s $BRIEF=1 Cuts out some of the "unneeded" documentation +; -s $GLSYM=1 Defines all opcode and directive symbols to be GLOBAL +; -s $TEST=1 Test the directives by appending '\\\' to them (nasty) + +; See the end of THIS file (not the listing file) for where to find the +; original MACRO-11 Manual PDF file and other reference material. + +;------------------------------------------------------------------------------ + + .NLIST CND ; Disable listing unsatisfied conditionals + .IIF DF $BRIEF .NLIST SYM ; Do not list the symbol table if '$BRIEF' + +;- .ENABL BMK ; Select benchmark mode (no date/time etc.) +;- .DSABL GBL ; Do not allow external symbols +;- .DSABL LC ; Force all source to upper-case + +;------------------------------------------------------------------------------ + +; +; Older versions of macro11 did not support .IF P1 or .IF P2 (but we use these) +; +; Code taken from SOS-11 (with '$' prefixed):- +; +; $P1 = 0 on the first pass and -1 on the second pass +; $P2 = -1 on the first pass and 0 on the second pass +; +; .IIF EQ $P1 ; We are on the first pass [not needed here] +; .IIF EQ $P2 ; We are on the second pass +; + + .IIF NDF $P2 $P2 = 0 ; If $P2 is not defined [i.e. on pass 1] ... +$P2 = ^C<$P2> ; ... we want $P2 to be non-zero [on pass 1] ... +;- $P1 = ^C<$P2> ; ... and we want $P1 to be the opposite + +;------------------------------------------------------------------------------ + +; +; Older versions of macro11 did not support .PAGE (but did assemble .REPT 0) +; + + .REPT 0 + .MACRO .PAGE + .ENDM .PAGE + .ENDR + +;------------------------------------------------------------------------------ + +; +; h - Heading (will appear in the TOC) +; + + .MACRO h A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z + .SBTTL A B C D E F G H I J K L M N O P Q R S T U V W X Y Z + .ENDM h + +; +; c - Comment (ignored) +; + + .MACRO c A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z + .ENDM c + +; +; o - Opcode (declare opcodes) +; + + .MACRO o A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z + .IIF EQ $P2 .MEXIT + .IRP OP, +OP =: OP + .IIF DF OP & $GLSYM .GLOBL OP + .ENDR + .ENDM o + +; +; d - Directive (declare directives [without the preceding '.']) +; + + .IF NDF $TEST + + .MACRO d A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z + .IIF EQ $P2 .MEXIT + .IRP DIR, +.'DIR =: .'DIR + .IIF DF .'DIR & $GLSYM .GLOBL .'DIR + .ENDR + .ENDM d + + .IFF ; NDF $TEST + + .MACRO d A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z +;- .IIF EQ $P2 .MEXIT + .IRP DIR, +.'DIR =: .'DIR + .IIF DF .'DIR & $GLSYM .GLOBL .'DIR + .PRINT ; *** .'DIR \\\ + .IIF DIF "DIR","END" .IIF DIF "DIR","ENDR" .'DIR \\\ +;+ .IIF EQ 0 .ENDC ; End the .IFxxx in case we started one + .ENDR + .ENDM d + + .ENDC ; NDF $TEST + +;------------------------------------------------------------------------------ +; The macro11 Reference Manual actually starts ... HERE ... +;------------------------------------------------------------------------------ + + .LIST + .TITLE macro11 - Manual and Features + .IDENT /MSHILL/ + .REM /18-Oct-2023/ + +h Preface +c ------- +c +; The objective of macro11 is to provided a portable assembler for +; PDP-11 MACRO-11 source code. Every attempt has been made to make +; macro11 produce equivalent object code to MACRO-11. The resulting +; executable program should be the same as on the original platform. + +; Source code which would produce errors on the original platform may +; produce different results using macro11. However, some effort has +; been made to detect errors and handle them similarly to the original. + +; macro11 includes most of the features of MACRO-11 up to V05.05. +; Enhancements, differences, and omissions are explained later. + +; This manual is intended to accompany the MACRO-11 Manual (see below). +; We show the differences between macro11 and MACRO-11 and illustrate +; additional features of macro11 which may prove useful when creating +; (or modifying) code which needs to be compatible with MACRO-11. + +; This manual pertains to macro11 0.9 (from August 22, 2023). +; macro11 - portable MACRO-11 assembler for DEC PDP-11. +; - and - +; dumpobj - Version from February 13, 2023. + +; The table of contents (TOC) is structured the same as the MACRO-11 +; Manual. Equivalent sections like (C.1) are shown in parenthesis. +; Use the MACRO-11 Manual to find the original text for comparison. + +; Reference documentaton for MACRO-11 V05.05:- +; +; PDP-11 MACRO-11 Language Reference Manual. +; Order Number AA-KX10A-TC May 1988. +; Including Update Notice 1, AD-KX10A-T1. +; For MACRO-11 Version 5.5 on IAS, MICRO/RSX, MICRO/RSTS, +; RSTS/E, RSX-llM, RSX-llM-PLUS, RT-11, P/OS, and VAX/VMS. + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; The following sections are very similar to the MACRO-11 Manual:- +; Character Set (3.1) +; .LIST and .NLIST Directive Symbolic Arguments (6.1.1) +; .ENABL and .DSABL Directive Symbolic Arguments (6.2.1) +; .PSECT Directive Symbolic Arguments (6.7.1) +; .ASECT and .CSECT Directives Default Values (6.7.2) +; Special Characters (B.1) +; Summary of Address Mode Syntax (B.2 also see Chapter 5) +; Embedded Assembler Directives (B.3 also see Chapters 6 & 7) + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; The permanent symbol table VALUES (Appendix C in MACRO-11) are listed +; in the symbol table at the end of this document. +; +; Opcodes (C.1), (C.2), and (C.3) are the symbols starting +; with a letter. +; +; Directives (C.4) are the symbols starting with a dot ('.'). +; Note that these are NOT defined as symbols in MACRO-11 but +; ARE defined (and have values) in macro11. +; +; Symbols starting with a dollar-sign ('$') are used internally +; to create this manual. + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.page +h Differences between PORTABLE macro11 and PDP-11 MACRO-11 +c -------------------------------------------------------- +c + .REM ~ + + E N H A N C E M E N T S + ----------------------- + +The macro11 listing file contains enhancements which MACRO-11 does not have. +.IF directives (also .IFT, .IFF, and .IFTF) put '=' in the flags field to +show if they are true (1=) or not (0=). .IF with an expression shows the +value of the expression in 6-digits (e.g. '012345=') instead of '1=' or '0='. +Some directives (e.g. .NARG, .NCHR, and .NTYPE) list the returned value. +macro11 lists the PSECT number in 3-digits (e.g. 001) with .PSECT, .SAVE, +and .RESTORE directives. If a symbol table is shown [.LIST SYM] a list +of all PSECTs with their numbers is appended. + +.IF ... .ENDC pairs may be nested more than the 16 levels which MACRO-11 allows. + +The following instructions do not generate errors on MACRO-11 but DO generate +warnings on macro11 (register mode, where Rn is one of R0-R5, SP, PC):- + JMP Rn + CALLR Rn + JSR Rm,Rn + CALL Rn + TSTSET Rn + WRTLCK Rn + +macro11 documents the following .ENABL / .DSABL "undocumented" arguments:- + BMK Benchmark: remove references to date/time etc. from the listing + DBG Has no function in macro11 (MACRO-11 writes ISD object records) + +macro11 documents the following .LIST / .NLIST "undocumented" arguments:- + LD Lists values in decimal (similar to .LIST HEX) insted of octal + +PAL-11R defines .IFEQ .IFZ etc. as backwards compatible. (DOS-11 also ignored +the .EOT directive). 'macro11 -stringent' flags these with a warning. It does +not flag .IF Z etc. See the 'Obsolete (but allowed) Directives' section for a +complete list. + +With macro11 it is possible to put absolute symbols in their respective PSECTS +within the object file. This is not possible with MACRO-11. See '-a0'. + +The object file always stores global symbol names sorted in Radix-50 order +within PSECTs whereas MACRO-11 may not. Some complex records [RLD type 17] may +be different to MACRO-11 because macro11 can perform more optimizations. + + + D I F F E R E N C E S + --------------------- + +On MACRO-11 the errors are listed as a single letter (A, ...) on the same line +as the source line. On macro11 the errors are listed preceding the source line. +With command-line option '-se' you will also see an asterisk on the same line. +One asterisk per reported message for that line. + +macro11 generates three levels of 'verbose error' message:- + WARNING - Will probably (but may not) assemble as expected + ERROR - Will probably NOT assemble correctly (similar to MACRO-11) + FATAL - Would probably cause MACRO-11 to abort the assembly (see -fe) + +Unlike MACRO-11, in macro11 .NLIST SEQ does not allocate space for the sequence +number (line number) in the listing. The listing line will be 8 characters +shorter (the 'error' field and the sequence field will be missing). This is +because the 'error' field is not normally needed by macro11 (see above). + +Bugs in MACRO-11 source-code may not have the same effect with macro11. +For example, MACRO-11 on RSX-11M/M-PLUS treats a form-feed which is not at the +end of a line as an invalid character. macro11 treats this as a page-throw +request (the same as MACRO-11 on RT-11). + +In MACRO-11, .PRINT statements will be printed on the console. By default, +macro11 does not do this. Use the '-sp' qualifier to achieve this effect. + +MACRO-11 treats .NOCROXX the same as .NOCROSS (also .INCLU, .LIBRA, etc.) +macro11 does the same unless '-ysl' is set to greater than 6. + +For .PSECT (etc.) arguments, MACRO-11 ignores characters beyond the third. +For example .PSECT BLOCK,ABSOLUTE,NOS is the same as .PSECT BLOCK,ABS,NOSAVE. +macro11 expects these arguments to be exactly as documented. + +By default, .PAGE (and a or ^L) will not perform a true page-throw. Two +blank lines will be left in the listing. You can force a to be written +instead by using the '-ff' qualifier. Unlike MACRO-11, macro11 does not list +(or track) page numbers. + +By default, a missing '.END' does not produce a warning in macro11. A warning +WILL be generated if either '-strict -strict -strict' or '-stringent' is +provided on the command line. + + + O M I S S I O N S + ----------------- + +macro11 does not report an error when too many formal parameters are passed +to a macro call. E.g. .MACRO TEST PARAM followed by TEST 1,2. Extra +formal parameters will simply be ignored (apart from syntax checking). + +.ENABL ABS is ignored by macro11. Use dumpobj to generate the LDA if possible. +.DSABL PNC is ignored by macro11. Object code generation is always enabled. +.DSABL REG is ignored by macro11. R0-R5, SP, and PC are always defined. + +.MDELETE does nothing in macro11. MACRO-11 since V05.00 will generate an +error if a '.MDELETEd' macro is referenced after deletion. + +macro11 does not support cross-referencing. This means that .CROSS and .NOCROSS +(introduced in MACRO-11 V05.00) do not generate cross-reference information. +They do, however, expect their parameters (if any) to be defined symbols. +If they are not defined, they will be treated as external global symbols. + +Invalid control characters (e.g. ^X) are not flagged by macro11 whereas +MACRO-11 since V05.04 [on RT-11, earlier on RSX-11M/M-PLUS] does detect them. + +macro11 only supports up to 254 unique named PSECTs whereas MACRO-11 V05.05 +has no limit to the number of PSECTs allowed (but only lists the first 256). + +Global register assignments are not written to the object file by macro11 but +are by MACRO-11. E.g. R7 == %7 will NOT be written to the object file by +macro11 whereas MACRO-11 would write the equivalent of 'R7 == 7' to the object +file. + +2.11BSD m11 .PSECT name,BSS will not zero-fill the 'name'd PSECT. + + .ENDREM ~ + +.page +h m11 (from 2.11BSD Unix) Additions / Compatibility +c ------------------------------------------------- +c +; m11 (documented separately) is the assembler from 2.11BSD Unix. +; macro11 provides backwards compatibility with source-code designed +; for m11. Generated object code will only be in either RSX or RT-11 +; format. No attempt has been made to follow the default PSECT +; attributes of m11. + +; Some m11 features are enabled using '-ym11' and some are disabled +; using '-strict -strict -strict' or '-stringent'. + +; m11 provides the following additions to MACRO-11:- +; A _ B = 'A' shifted by 'B' bits (disabled by '-yus') +; ^PLname = 0 (but 'name' must be a known .PSECT name) +; ^PHname = Highest address in .PSECT 'name' +; CNZ = +; .MACR = .MACRO + +; In m11, .IF DF and .IF NDF allow the right-hand-side to be empty which +; equates to FALSE. This will be disabled with '-strict -strict' or +; higher (e.g. -stringent). + +; .ENABL PIC and .DSABL PIC have no function in (and are ignored by) +; macro11. + +; m11 provides the following additional PSECT attributes:- +; .PSECT name,PRV = RW +; .PSECT name,SHR = RO +; .PSECT name,INS = I +; .PSECT name,DAT = D +; .PSECT name,BSS = RW,DAT (but will not be set to zero) +; .PSECT name,B = RW,DAT (but will not be set to zero) +; .PSECT name,HGH = Attribute ignored +; .PSECT name,LOW = Atrribute ignored + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.page +h Character Set (3.1) +c ------------------- +c +; Char Designation Function +; ---- ----------- -------- +; : Colon Label terminator. +; :: Double colon Label terminator; defines the label + ; as a global label. +; = Equal sign Direct assignment operator and macro + ; keyword indicator. +; == Double equal sign Direct assignment operator; defines + ; the symbol as a global symbol. +; =: Equal sign colon Direct assignment operator; macro + ; keyword indicator; causes error + ; in listing if an attempt is made to + ; change the value of the symbol. +; ==: Double equal sign colon Direct assignment operator; defines + ; the symbol as a global symbol; + ; causes error in listing if an attempt + ; is made to change the value of the + ; symbol. +; % Percent sign Register term indicator. +; Horizontal tab Item terminator or field terminator. +; Space Item terminator or field terminator. +; # Number sign Immediate expression indicator. +; @ At sign Deferred addressing indicator. +; ( Left parenthesis Initial register indicator. +; ) Right parenthesis Terminal register indicator. +; . Period Current location counter. +; , Comma Operand field separator. +; ; Semicolon Comment field indicator. +; < Left angle bracket Initial argument or expression indic. +; > Right angle bracket Terminal argument or expression indic. +; + Plus sign Unary plus, arithmetic addition. + ; operator, or autoincrement indicator. +; - Minus sign Unary minus, arithmetic subtraction. + ; operator, or autodecrement indicator. +; * Asterisk Arithmetic multiplication operator. +; / Slash Arithmetic division operator. +; & Ampersand Logical AND operator. +; ! Exclamation point Logical inclusive OR operator. +; " Double quote Double ASCII character indicator. +; ' Single quote Single ASCII character indicator or + ; concatenation indicator. +; ^ Circumflex Universal unary operator or argument + ; indicator. +; \ Backslash Macro call numeric argument indicator. +; _ Underscore [m11 only] Arithmetic shift operator. + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.page +h Relocation and Linking (Chapter 4) +c ---------------------------------- +c +; macro11 produces equivalent object code to MACRO-11. The object files +; may be transferred to a PDP-11 (or emulator) to be linked (TKB or LINK). + +; macro11 can create either RSX-11M/M-PLUS or RT-11 (including RSTS/E) +; formatted object modules. + +; Although m11 (from 2.11BSD Unix) source format is supported (see the +; -ym11 qualifier) it is not possible to create an object file in the +; same format as m11. + +; dumpobj may be used to dump the contents of a macro11 object file +; and also MACRO-11 object files copied from a PDP-11. dumpobj may +; also be used to create an absolute binary (paper-tape) image from +; an object file. + +; Unlike MACRO-11 on RT-11, macro11 cannot directly create a paper-tape +; formatted (LDA) file. On RT-11 this may be done using the .ENABL ABS +; directive or either of the qualifiers /E:ABS or /ENABLE:ABS. + +; You may choose to use obj2bin (documented separately) to link multiple +; object modules or, for a single object module with only one PSECT, you +; may create a paper-tape (LDA) image using dumpobj (documented here). + +; Recommended way to create a paper-tape image from an object file: +; +; dumpobj -q OBJECT.obj IMAGE.lda +; +; The source file may only have one PSECT (either ABS or REL) with data in +; it and must not reference any undefined symbols. dumpobj will show the +; reasons when the created paper-tape image may not be 100% correct. + +; The 'Sample Assembly Listing' at the end of this manual is also an +; example of using dumpobj to create absolute binary (paper-tape) output. +; The example creates three PSECTs (of which only CORE contains code) and +; creates two global symbols which could just as easily have been local. + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.page +h .LIST and .NLIST Directive Symbolic Arguments (6.1.1) +c ----------------------------------------------------- +c +; Arg. Default Function +; --- ------- -------------------------------------------------------- +; BEX List Controls the listing of binary extensions (the locations + ; and binary contents beyond those that will fit on the + ; source statement line). This is a subset of BIN. +; BIN List Controls the listing of generated binary code. If this + ; field is suppressed through a .NLIST BIN directive, + ; left-justification of the source code field occurs in + ; the same manner described above for the LOC field. +; CND List Controls the listing of unsatisfied conditional coding + ; and associated .IF and .ENDC directives in the source + ; program. A .NLIST CND directive lists only satisfied + ; conditional coding. +; COM List Controls the listing of comments. This is a subset of + ; the SRC argument. .NLIST COM causes comments starting + ; in column 1 to be suppressed. In macro11, end-of-line + ; comments will not be suppressed (except for .NLIST SRC). +; LD No list Controls radix used for assembly listing. If you + ; specify .LIST LD, contents are given in decimal, + ; rather than octal. +; HEX No list Controls radix used for assembly listing. If you + ; specify .LIST HEX, addresses and contents are given + ; in hexadecimal, rather than octal. +; LOC List Controls the listing of the current location counter + ; field. Normally, this field is not suppressed. + ; However, if it is suppressed through the .NLIST LOC + ; directive, the suppression of the current location + ; counter (LOC) field effectively left-justifies all + ; subsequent fields (while preserving positional + ; relationships) to the position normally occupied by + ; the counter's field. +; MC List Controls the listing of macro calls and repeat range + ; expansions. +; MD List Controls the listing of macro definitions and repeat + ; range expansions. +; ME No list Controls the listing of macro expansions. +; MEB No list Controls the listing of macro expansion binary code. + ; A .LIST MEB directive lists only those macro expansion + ; statements that generate binary code. This is a subset + ; of the ME argument. +; SEQ List Controls the listing of the sequential numbers assigned + ; to the source lines. If this number field is suppressed + ; through a .NLIST SEQ directive, macro11 left-justifies + ; all subsequent fields. +; SRC List Controls the listing of source lines. +; SYM List Controls the listing of the symbol table (and the PSECT + ; list) resulting from the assembly of the source program. +; TOC List Controls the listing of the table of contents during + ; assembly pass 1 (by the .SBTTL directive). +; TTM No list Controls the listing output format. TTM is for terminal + ; (teletype) mode and reduces the listing width by only + ; allocating 8 characters for the BEX listing. This is + ; useful if you want your listing to fit in 80 columns. + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.page +h .ENABL and .DSABL Directive Symbolic Arguments (6.2.1) +c ------------------------------------------------------ +c +; Arg. Default Function +; --- ------- -------------------------------------------------------- +; ABS Disable No function. In MACRO-11 enabling this function + ; produces output in absolute binary (.LDA) format. +; AMA Disable Enabling this function causes all relative addresses + ; (address mode 67) to be assembled as absolute addresses + ; (address mode 37). This function is useful during the + ; debugging phase of program development. +; BMK Disable Does not list dates and times etc. to make listing + ; comparison between versions of macro11 easier. +; CDR Disable Enabling this function causes source columns from 73 + ; to the end of the line to be treated as a comment. +; CRF Enable No function. In MACRO-11 disabling this function + ; inhibits the generation of cross-reference output. +; DBG Disable No function. In MACRO-11 enabling this function + ; writes internal symbol director (ISD) records + ; to the object file. +; FPT Disable Enabling this function causes floating-point truncation; + ; disabling this function causes floating-point rounding. +; GBL Enable Disabling this function causes macro11 to signal an + ; error for all undefined references in assembly pass 2. +; LC Enable Disabling this function causes macro11 to convert all + ; ASCII input to uppercase before processing it. +; LCM Disable Enabling this function causes the macro11 conditional + ; assembly directives .IF IDN and .IF DIF to be + ; alphabetically case sensitive. By default, these + ; directives are not case sensitive. +; LSB Disable This argument permits the enabling or disabling of a + ; local symbol block. Although a local symbol block is + ; normally established by encountering a new symbolic + ; label, a .PSECT directive, or a .RESTORE directive in + ; the source program, a .ENABL LSB directive establishes + ; a new local symbol block which is not terminated until + ; another .ENABL LSB is encountered, or another symbolic + ; label, .PSECT directive, or .RESTORE directive is + ; encountered following a paired .DSABL LSB directive. +; MCL Disable Enabling this function causes macro11 to search all + ; known macro libraries for a macro definition that + ; matches any undefined symbols appearing in the op code + ; field of a MACRO-11 statement. +; PNC Enable No function. In MACRO-11 disabling this function + ; inhibits binary output until a .ENABL PNC statement + ; is encountered within the same module. +; REG Enable No function. In MACRO-11 disabling this function + ; inhibits the normal MACRO-11 default register + ; definitions (R0=%0, ..., R5=%5, SP=%6, PC=%7). + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.page +h .PSECT Directive Symbolic Arguments (6.7.1) +c ------------------------------------------- +c +; Format: +; .PSECT name, argl, arg2, ... argn + +; Arg. Default Meaning +; ---- ------- ------- +; name Blank ; Establishes the program section name, which is + ; specified as one to six Radix-50 characters. + ; If this argument is omitted, a comma must appear + ; in place of the name parameter. +; RO/RW RW ; Defines which type of access is permitted to the + ; program section. RT-11 and RSX-llM use only RW. +; I/D I ; Defines the contents of the program section. +; GBL/LCL LCL ; Defines the scope of the program section, as it + ; will be interpreted at link time. The GBL/LCL + ; arguments apply only in the case of overlays. +; ABS/REL REL ; Defines the relocatability attribute of the program + ; section. +; CON/OVR CON ; Defines the allocation requirements of the program + ; section. +; SAV/NOSAV SAV ; [RT-11 only] Determines where the Linker allocates + ; storage for the program section + +; See the 'm11 (from 2.11BSD Unix) Additions / Compatibility' section for +; a list of m11 PSECT symbolic arguments. + + +h .ASECT and .CSECT Directives Default Values (6.7.2) +c --------------------------------------------------- +c +; Directive Name Default Values +; .ASECT .ABS . ,RW,I,GBL,ABS,OVR +; .CSECT ,RW,I,LCL,REL,CON +; .CSECT name name ,RW,I,GBL,REL,OVR +; .PSECT [name] name ,RW,I,LCL,REL,CON + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.page +h <<<<<<<<<< Part IV - Operating Procedures (Chapters 8 and 9) >>>>>>>>>> +c +h Operating Procedures - Comparison of macro11 to MACRO-11 Qualifiers +c ------------------------------------------------------------------- +c +; macro11 RSX-MCR RSX-DCL RSTS/RT-11 RT-11 DCL +; ------- ------- ------- ---------- --------- +; -d arg /DS:arg /DISABLE:type /D:arg /DISABLE:arg +; -d arg /NL:arg /NOSHOW:type /N:arg /NOSHOW:arg +; -d TTM /NL:TTM /WIDE /N:TTM /NOSHOW:TTM +; -e arg /EN:arg /ENABLE:type /E:arg /ENABLE:arg +; -e arg /LI:arg /SHOW:type /L:arg /SHOW:arg +; -e TTM /LI:TTM /NOWIDE /L:TTM /SHOW:TTM +; -l file /LIST:file - /LIST:file +; -l - /LIST:TI: - /LIST:TT: +; -m file file/ML file/LIBRARY file/M file/LIBRARY +; -o file /OBJECT:file - /OBJECT:file +; -p1 /PA:1 /PASS:1 /P:1 /PASS:1 (obs.) +; -p2 /PA:2 /PASS:2 /P:2 /PASS:2 (obs.) +; +; Note: macro11 allows far more 'arg' values to -d and -e than MACRO-11. +; See Part IV (Chapters 8-9) in the MACRO-11 Manual for more information. + +.page +h Operating Procedures - Using macro11 to assemble and list source code +c --------------------------------------------------------------------- +c +; macro11 provides a plethora of command-line qualifiers. These by far +; exceed the number provided by MACRO-11, which can be both confusing and +; (potentially) rewarding. + +; macro11 can generate object code in either RSX-11M/M-PLUS (-rsx) or +; RT-11 (-rt11) format. Either is also suitable for RSTS/E. + +; Whereas MACRO-11 provides two different qualifiers to set up +; .LIST / .NLIST and .ENABL / .DSABL, macro11 only provires one: -e / -d. +; Also, macro11 allows multiple arguments to be provided to one qualifier. +; For examnple: -e AMA,MEB (equivalent to '.ENABL AMA' and '.LIST MEB'). +; +; Another feature of macro11 is to disable the changing of these with +; the '-dc' qualifier. For example: -dc MEB would cause .LIST MEB in +; the source-code to be ignored. Thus with '-e MEB -dc MEB' it is +; possible to force macro11 to always have '.LIST MEB' in force. +; +; Unlike MACRO-11, the '-e' and '-d' qualfiers accept all possible +; arguments to .LIST / .NLIST and .ENABL / .DSABL. Additionally, +; you may provide '-e LIS' or '-d LIS' which is equivalent to +; .LIST / .NLIST without any arguments (enable / disable listing). + +; A number of features are designed to make life easier in a portable +; environment. For example, it may be desirable to extract all modules +; from a macro library into a directory (-xtract). Equally, you may +; want to read (understand) the source code of a macro library (-xl). + +; Useful features when developing MACRO-11 source-code are the ablility +; to list the first assembler pass (-yl1) or list local symbols (-ylls). +; You may also force the listing of lines which would normally be +; suppressed (-yfl). + +; A nice addition for code which does not need to be back-ported to +; a real PDP-11 is the ability to have longer symbols (-ysl ) and +; allow underscores in symbol names (-yus). Care must be taken if +; either of these appear in global symbols. Global symbols are +; truncated to 6 characters. Underscores ('_') are stored with the +; value of the undefined Radix-50 character (035 octal). + +; macro11 provides the '-s' qualifier to prepend statements to the +; source-code being assembled. This is useful where certain symbol +; definitions need to be made. For example '-s DEBUG=1' would act +; as if 'DEBUG=1' were at the top of the source file. If spaces +; are required, the qualifier needs to be surrounded by double-quotes. +; For example: -s "TEXT:\t.ASCII\t/Hello, world!/\t; Message text". +; +; It is actually possible to assemble a program without using a source +; file at all. For example: '-s NOP -s HALT -s .END'. + +; If macro11 does not seem to do what you think it should, you may +; enable the internal debugging code (-yd) or enhanced debuggung +; (-yd -yd). + +; macro11 will exit 'with status' to indicate whether the assemble +; was successful or not. This may be used in a script when +; assembling. + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; See the 'Summary of macro11 Command Line Qualifiers' section for a +; complete list of macro11 command-line qualifiers. + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Further information can be found in the 'Operating Procedures - Hints +; and Tricks' section. + +.page +h Operating Procedures - Running dumpobj +c -------------------------------------- +c +; dumpobj is designed for two purposes:- +; 1) To analyze the macro11 object files or compare to originals +; 2) To create an absolute binary (paper-tape) file + +; When analyzing the object file contents by hand (eye) use -align -nosort +; and, optionally, -qt and/or -s. + +; When using dumpobj to compare generated object files with originals +; from a PDP-11 MACRO-11 assembly, do not use -nosort (or -align). + +; When creating a paper-tape file, use -q to suppress the analysis. +; +; You may modify the origin (base address) and, by default, change +; the 'internal relocations' accordingly:- +; -o will add to all generated absolute addresses. +; -of does the same EXCEPT 'internal relocations' will not +; be modified (their absolute addresses will remain unchanged). +; +; You may want to use -w = to write individual words to the +; paper-tape file. For example, -w 0=240,0 would write NOP & HALT +; to words 0 and 2 in the paper-tape file. You may also change non- +; contiguous locations, for example '-w 4=6,0,24=26,0'. + +; You may also generate (modify) the transfer address the object file +; provided using -x . For example, '-x 1000'. + +; Example (relocate code to address 1000 and 000=NOP, HALT, CLR PC):- +; +; dumpobj -q OBJECT.obj IMAGE.lda -o 1000 -w 0=240,0,5007 -x 2 +; +; If OBJECT.obj is an empty file (or the null device) then only +; the words from -w will be written. Thus it is possible for +; dumpobj to generate papar-tape files without any source at all. + +.page +h Operating Procedures - Hints and Tricks +c --------------------------------------- +c +; macro11 has a number of qualifiers to ensure that the source code +; will also assemble correctly under MACRO-11. Because macro11 is less +; strict than MACRO-11 there are four qualifiers to catch or ignore +; potential errors which MACRO-11 would find (or, indeed, miss):- +; +; -ysl 8 Symbols like .INCLUDE may not be truncated to 6 chars. +; -stringent The code is expected to conform to the MACRO-11 Manual +; -strict The code should match MACRO-11 as much as possible +; -relaxed The code may contain things MACRO-11 would not accept +; +; Note that the command line may have up to 3 x -strict or 2 x -relaxed. +; The more qualifiers, the more strict (or relaxed) the parsing will be. +; [ 4 x -strict is equivalent to -stringent. ] + +; Because macro11 is designed for modern computers, the listing is not +; the same as the one from MACRO-11. There are no page numbers or page +; headings and page-breaks are shown by leaving two blank lines. The +; sequence (line) numbers match the line in the source file as seen by +; an editor. Also, the lines in the symbol table will be padded with +; trailing spaces to make it easier to process if you want to feed it +; into another program. +; +; There are two qualifiers to get closer to the MACRO-11 listing:- +; +; -apb Automatic page-break after 57 listing lines +; -ff Use a form-feed between listing pages + +; Because non-PDP-11 systems do not have the concept of PDP-11 device +; names and PDP-11 directory names, these may be removed from .INCLUDE +; and .LIBRARY names using the following two qualifiers:- +; +; -nodev Ignore 'ddn:' style device names +; -nodir Ignore '[directory]' style names + +; When assembling 2.11BSD m11 source files, you may like to supply +; the -ym11 qualifier. This will suppress some messages which may +; not apply. You may also need to supply the -relaxed (x 2) qualifier. + +; There are a number of qualifiers which are useful when debugging +; source code:- +; +; -d disable +; -dc disable changing of +; -e enable +; -fe fatal errors will abort assembly +; -s prepend a to the source +; -se shows source line in error after the error message +; -sp shows .PRINT source lines on stderr (similar to -se) +; -yfl Extension: force listing of suppressed lines +; -yl1 Extension: list the first pass too, not only the second +; -ylls Extension: list local symbols when used and in the symbol table + +; Here's how to convert a file with embedded into an RSX supported +; version (with or without ). This also removes trailing spaces and +; tabs from the source and suppresses [extra] blank lines at the top of +; pages. Note .PRINT within macro calls will be listed - these would +; need to be deleted by hand (if they exist). +; +; macro11 -d TOC,SEQ,LOC,BIN,SYM +; -dc TOC,SEQ,LOC,BIN,BEX,COM,SYM,LIS,CND,LC,MD,ME,TTM +; -ff OLD.MAC -l NEW.MAC + +; ======================================================================= +; +; The following qualifiers are recommended for MACRO-11 compatibility:- +; +; macro11 -se -sp -fe -stringent -ysl 8 -nodev -nodir ... +; +; ======================================================================= + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.page +h Summary of macro11 Command Line Qualifiers +c ------------------------------------------ +c + .REM ~ + +> macro11 -yd -h + +macro11 - portable MACRO-11 assembler for DEC PDP-11 + Version 0.9 (22 Aug 2023) + Version 0.9 ([ Unknown GIT version ] + [ Aug 22 2023 11:09:39 ]) + Copyright 2001 Richard Krehbiel, + modified 2009 by Joerg Hoppe, + modified 2015-2017,2020-2023 by Olaf 'Rhialto' Seibert, + modified 2023 by Mike Hill. + +Usage: + macro11 [-o ] [-l {- | }] [-p1 | -p2] [-se] [-sp] [-fe[2]] + [-h] [-v] [-apb] [-ff] [-e ] [-d ] [-dc ] + [-a0] [-rsx | -rt11] [-stringent | -strict | -relaxed] + [-ym11] [-ysl ] [-yus] [-yfl] [-ylls] [-yl1] [-yd] + [-s ["]["]] [-s ...] + [-nodev] [-nodir] + [-I ] + [-m ] [-p ] [-xl] [-xtract] + [ ...] + +Arguments: + MACRO-11 source file(s) to assemble + +Options: +-apb automatic page-break after 57 listing lines (also with -xl). +-d disable (see below). +-dc disable changing of (see below). +-e enable (see below). +-fe fatal errors will abort assembly. +-ff use a form-feed between listing pages (also with -apb & -xl). +-h print this help. +-I gives the name of a directory in which .INCLUDEd files may be found. + Sets environment variable "INCLUDE". +-l gives the listing file name (.LST). + -l - enables listing to stdout. +-o gives the object file name (.OBJ). +-p gives the name of a directory in which .MCALLed macros may be found. + Sets environment variable "MCALL". +-p1 only assemble pass 1 (not supported or recommended). +-p2 only assemble pass 2 (not supported or recommended). +-s prepend a to the source (e.g. -s "DEBUG\t=\t1\t\t; Enable"). +-se shows source line in error after the error message. +-sp shows .PRINT source lines on stderr (similar to -se). +-v print version. + +-a0 Use sections with ABS,OVR for their own local symbol definitions. +-nodev Ignore 'ddn:' style device names in .INCLUDE and .LIBRARY filenames. +-nodir Ignore '[directory]' style names in .INCLUDE and .LIBRARY filenames. +-rsx Generate RSX style object files (default). +-rt11 Generate RT-11 style object files. + +-m load RSX-11 or RT-11 compatible macro library from which + .MCALLed macros can be found. Similar to .LIBRARY directive. + Multiple allowed. Affected by any -rsx or -rt11 which come before. +-xl as -xtract but stores macros to '-l' file. If none, only lists names. +-xtract invokes macro11 to expand the contents of the registered + macro libraries (see -m) into individual .MAC files in the + current directory. No assembly of input is done. + This must be the last command line option! + +-relaxed Relax some of the usual rules for source code. +-strict Expect source code to closely match MACRO-11 V05.05. +-stringent Expect source code to closely match MACRO-11 documentation. + +-yd Extension: enable debugging. +-yfl Extension: force listing of suppressed lines. +-yl1 Extension: list the first pass too, not only the second. +-ylls Extension: list local symbols when used and in the symbol table. +-ym11 Syntax extension: support BSD m11 language extensions. +-ysl Syntax extension: change length of symbols (from 6 up to 64). +-yus Syntax extension: allow underscore "_" in symbols. + +Options for -e, -d, and -dc are: + [.ENABL] -e AMA,CDR,FPT,LCM,LSB,MCL + [.DSABL] -d GBL,LC + [.LIST ] -e LIS,HEX,ME,MEB,TTM + [.NLIST] -d BEX,BIN,CND,COM,LOC,MC,MD,SEQ,SRC,SYM,TOC + +No source files provided + + .ENDREM ~ + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.page +h Summary of dumpobj Command Line Qualifiers +c ------------------------------------------ +c + .REM ~ + +> dumpobj.exe -h + +dumpobj - portable OBJECT file dumper for DEC PDP-11 + Version from February 13, 2023 compiled on Mar 9 2023 at 09:48:24 + Copyright 2001 by Richard Krehbiel, + modified 2013,2015,2020,2021 by Olaf 'Rhialto' Seibert, + modified 2023 by Mike Hill. + +Usage: + dumpobj [-h] [-q|-qt] [-[no]align] [-nosort] [-s] [-rt11|-rsx] + [ [-o[f] ] [-w [=]] [-x ] ] + +Arguments: + -h show this help text + -q quiet output - only errors will be shown + -qt quiet-text - omit the contents of TXT records + -align align fields when dumping GSD entries + -nosort do not sort the GSD entries (dump them in input order) + -s show a summary of all record types seen in the object file + -rt11 expects object file to be in RT-11 format + -rsx expects object file to be in RSX-11M/PLUS format + required .OBJ input file (format can be auto-detected) + + -o sets the octal origin for relocatable LDA code (default is 0) + -of as -o but keep 'internal relocations' fixed not origin based + -w =,.. write words to the - [=][,...]] + -x writes the octal transfer address to the + optional .LDA output file (in PDP-11 punched-tape format) + +Examples: + dumpobj OBJECT.OBJ Dump contents of OBJECT.OBJ + dumpobj -qt -align -nosort -s OBJECT.OBJ Now dump in object file order + dumpobj -q -s OBJECT.OBJ Only show a summary of records + dumpobj -q -s OBJECT.OBJ -w 0 Show errors ready for LDA o/p + dumpobj -q OBJECT.OBJ PTAPE.LDA Only create an LDA file + dumpobj -q OBJECT.OBJ -o 1000 PTAPE.LDA Relocate code to address 1000 + dumpobj EMPTY.OBJ -w 0,40=0,5237,0,774 -x 40 0:0,40:HALT,42:INC @#0,46:BR 40 + dumpobj EMPTY.OBJ -w ... -x ... PTAPE.LDA Create an LDA file with data + + .ENDREM ~ + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.page +h <<<<<<<<<<<<<<<<<<<<<<<<<<<<< Appendices >>>>>>>>>>>>>>>>>>>>>>>>>>>>> +c + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +h Special Characters (B.1) +c ------------------------ +c +; Char Function +; ---- -------- +; : Label terminator +; = Direct assignment operator +; % Register term indicator +; Item terminator or field terminator +; Item terminator or field terminator +; # Immediate expression indicator +; @ Deferred addressing indicator +; ( Initial register indicator +; ) Terminal register indicator +; , (comma) Operand field separator +; ; Comment field indicator +; + Arithmetic addition operator or autoincrement indicator +; - Arithmetic subtraction operator or autodecrement indic. +; * Arithmetic multiplication operator +; / Arithmetic division operator +; & Logical AND operator +; ! Logical OR operator +; " Double ASCII character indicator +; ' single quote Single ASCII character or concatenation indicator +; . Assembly location counter +; < Initial argument indicator +; > Terminal argument indicator +; ^ Universal unary operator or argument indicator +; \ Macro call numeric argument indicator +; _ underscore [m11 only] Arithmetic shift operator +; vertical tab Source line terminator + +.page + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +h Summary of Address Mode Syntax (B.2 also see Chapter 5) +c ------------------------------------------------------- +c +; Format Name Number Meaning +; ---------- -------------- ------ ----------------------------------- +; R Register 0n Register R contains the operand. +; +; @R or (ER) Register Deferred 1n Register R contains the address +; of the operand. +; +; (ER)+ Autoincrement 2n The contents of the register +; specified as (ER) are incremented +; after being used as the address of +; the operand. +; +; @(ER)+ Autoincr. Deferred 3n The register specified as (ER) +; contains the pointer to the address +; of the operand; the register (ER) +; is incremented after use. +; +; -(ER) Autodecrement 4n The contents of the register +; specified as (ER) are decremented +; before being used as the address +; of the operand. +; +; @-(ER) Autodecr. Deferred 5n The contents of the register +; specified as (ER) are decremented +; before being used as the pointer to +; the address of the operand. +; +; E(ER) Index 6n The expression E, plus the contents +; of the register specified as (ER), +; yield the address of the operand. +; +; @E(ER) Index Deferred 7n The expression E, plus the contents +; of the register specified as (ER), +; yield a pointer to the address of +; the operand. +; +; #E Immediate 27 The expression E is the operand +; itself. +; +; @#E Absolute 37 The expression E is the address +; of the operand. +; +; E Relative 67 The address of the operand E, +; relative to the instruction, +; follows the instruction. +; +; @E Relative Deferred 77 The address of the operand is +; pointed to by E, whose address, +; relative to the instruction, +; follows the instruction. +; +; Symbols used in the table (above):- +; n is an integer, 0 to 7, representing a register number. +; R is a register expression. +; E is an expression. +; ER is R (above) or an expression whose value is in the range 0 to 7. + +.page + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +h Embedded Assembler Directives (B.3 also see Chapters 6 and 7) +c ------------------------------------------------------------- +c +; Form Operation +; ---- --------------------------------------------------------------- +; ' Followed by one ASCII character, a single quote (apostrophe) +; generates a word which contains the 7-bit ASCII representation +; of the character in the low-order byte and zero in the high- +; order byte. Single quote is also used as a concatenation +; indicator in the expansion of macro arguments. + +; " Followed by two ASCII characters, a double quote generates a +; word which contains the 7-bit ASCII representation of the two +; characters. The first character is stored in the low-order +; byte; the second character is stored in the high-order byte. + +; ^Bn A temporary radix control, causes the value n to be treated +; as a binary number. + +; ^Cexpr A temporary numeric control, causes the expression's value +; to be one's complemented. + +; ^Dn A temporary radix control, causes the value n to be treated +; as a decimal number. + +; ^Fn A temporary numeric control, causes the value n to be treated +; as a 16-bit floating-point number. + +; ^On A temporary radix control, causes the value n to be treated +; as an octal number. + +; ^PHp [m11 only] Returns the highest address in PSECT p. + +; ^PLp [m11 only] Returns the lowest address in PSECT p (always 0). + +; ^Rccc Converts ccc to Radix-50 form. + +; ^Xn A temporary radix control, causes the value n to be treated +; as a hexadecimal number. The value n must begin with a digit, +; which may be 0. + +; See the MACRO-11 Manual for the directive (e.g. .ASCII) operations. +; A list of available directives is in section C.4 below. + +.page + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + .IF NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + .NLIST +h [ The symbol values for sections (C.1) - (C.3) are appended ] ; TOC + .LIST + .ENDC ; NDF $BRIEF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +h PDP-11 Basic (all models) Instruction Set Op Codes (C.1) +c -------------------------------------------------------- +c +o ADC ADCB ADD ASL ASLB ASR ASRB +o BCC BCS BEQ BGE BGT BHI BHIS +o BIC BICB BIS BISB BIT BITB +o BLE BLO BLOS BLT BMI BNE BPL +o BPT +o BR BVC BVS +o CALL CALLR CCC CLC CLN +o CLR CLRB +o CLV CLZ ; CNZ = BSD m11 only +o CMP CMPB COM COMB DEC DECB EMT HALT +o INC INCB IOT JMP JSR MOV MOVB NEG NEGB +o NOP RESET RETURN ROL ROLB ROR RORB RTI RTS +o SBC SBCB SCC SEC SEN SEV SEZ +o SUB SWAB TRAP TST TSTB WAIT + +.page +h Additional PDP-11/45 and PDP-11/70 Instruction Set Op Codes (C.1) +c ----------------------------------------------------------------- +c +o ASH ASHC DIV MARK MFPD MFPI MTPD MTPI +o MUL RTT SOB SPL SXT XOR + +.page +h Additional PDP-11 (various models) Instruction Set Op Codes (C.1) +c ----------------------------------------------------------------- +c +o CSM +o FADD FDIV FMUL FSUB +o MED6X MED74C +o MFPS MFPT MTPS TSTSET WRTLCK +o ; XFC = PDP-11/60 only + +.page +h Commercial Instruction Set (CIS) Op Codes (C.2) +c ----------------------------------------------- +c +o ADDN ADDNI ADDP ADDPI ASHN ASHNI ASHP ASHPI +o CMPC CMPCI CMPN CMPNI CMPP CMPPI CVTLN CVTLNI +o CVTLP CVTLPI CVTNL CVTNLI CVTNP CVTNPI CVTPL CVTPLI +o CVTPN CVTPNI DIVP DIVPI +o L2D0 L2D1 L2D2 L2D3 L2D4 L2D5 L2D6 L2D7 +o L3D0 L3D1 L3D2 L3D3 L3D4 L3D5 L3D6 L3D7 +o LOCC LOCCI MATC MATCI MOVC MOVCI MOVRC MOVRCI +o MOVTC MOVTCI MULP MULPI SCANC SCANCI SKPC SKPCI +o SPANC SPANCI SUBN SUBNI SUBP SUBPI + +.page +h Floating-Point Processor Op Codes (C.3) +c --------------------------------------- +c +o ABSD ABSF ADDD ADDF CFCC CLRD CLRF CMPD CMPF +o DIVD DIVF LDCDF LDCFD LDCID LDCIF LDCLD LDCLF LDD +o LDEXP LDF LDFPS MODD MODF MULD MULF +o NEGD NEGF SETD SETF SETI SETL +o STCDF STCDI STCDL STCFD STCFI STCFL STD STEXP STF +o STFPS STST SUBD SUBF TSTD TSTF + +.page +h MACRO-11 Directives [start with '.'] (C.4 also see B.3 and Chapter 6) +c --------------------------------------------------------------------- +c +d ASCII ASCIZ ASECT BLKB BLKW BYTE CROSS CSECT DSABL +d ENABL END ENDC ENDM ENDR ERROR EVEN FLT2 FLT4 +d GLOBL IDENT IF IFF IFT IFTF IIF INCLUDE IRP +d IRPC LIBRARY LIMIT LIST MACRO MCALL MDELETE MEXIT +d NARG NCHR NLIST NOCROSS NTYPE ODD PACKED PAGE PRINT +d PSECT RAD50 RADIX REM REPT RESTORE SAVE SBTTL TITLE +d WEAK WORD ; .MACR = BSD m11 only + +.page +h Obsolete (but allowed) Directives [start with '.'] (I.1) +c -------------------------------------------------------- +c +d EOT +d IFZ IFEQ IFNZ IFNE IFL IFLT +d IFG IFGT IFLE IFGE IFDF IFNDF + +.page +h Sample Assembly Listing (also see Appendix H) +c --------------------------------------------- +c +c Seq Loc Binary Label Opcode Arguments Comment +c ----- ------ ------ ------- ------ --------------- ------------------------ + .LIST SEQ,LOC,BIN,TTM ; CND,MC,MD,SRC,SYM,TOC +;- .NLIST BEX ;-COM,HEX,ME,MEB + +;+ .TITLE TEST Program + .IDENT /MSHILL/ + +; Instructions:- +; +; macro11 README.MAC -o README.obj +; dumpobj -align -nosort README.obj README.lda +; pdp11 +; sim> load README.lda +; sim> examine -m 000-003 +; sim> examine 004-013 +; sim> examine -m 014-067 +; sim> examine -a 070-111 +; sim> examine -b 112-112 +; sim> go + + .GLOBL $GLOBL ; Undefined global sym. + + .PSECT CORE,RW,REL ; Program section + +$TTO ==: 177560 + 4 ; Console status reg. + +$BASE:: NOP ; 000: Bootstrap ident. + BR 30$ ; 002: Start program + + .WORD 20$ - $BASE ; 004: Timeout vector + .WORD 000340 + 004 ; 006: PR7 + 4 + + .WORD 20$ - $BASE ; 010: Illegal instr. + .WORD 000340 + 010 ; 012: PR7 + 10 + +10$: BIC R0,R0 ; 014: Clear R0 and ... + SEC ; 016: ... set carry + + ; 020: .HRESET when ... + ; 020: ... RUN under ... + ; 020: ... RT-11 and ... + ; 020: ... restart @ 000 + +20$: HALT ; 020: HALT the CPU + +30$: RESET ; 022: RESET the CPU + MOV #001000,SP ; 024: Setup stack + MOV #$TTO,R0 ; 030: ^Printer status + + MOV PC,R1 ; 034: Point to ... + ADD #<$HELLO-.>,R1 ; 036: ... $HELLO buf. + + CALL $OUTS ; 042: Output string ... + BR 10$ ; 046: ... then HALT + +; $OUTS - Output a string. R0 = ^TTO, R1 = ^String. + + .ENABL LSB + +10$: TSTB (R0) ; 050: Printer ready? + BPL 10$ ; 052: If MI, no + + MOVB -1(R1),2(R0) ; 054: Print char. + +$OUTS:: TSTB (R1)+ ; 062: Reached end? + BPL 10$ ; 064: If PL, no + + RETURN ; 066: Return from subr. + + .DSABL LSB + +;+ .PAGE ;+Text message + .NLIST BEX +$HELLO: .ASCII "Hello, world!" ; 070: Message text + .ASCIZ <015><012> ; 105: + .BYTE 000,000 ; 110: 2 x + .BYTE -1 ; 112: End marker + + .END $BASE + 2 ; Start at the start! + +;------------------------------------------------------------------------------ + + .ERROR . ; .END is not the end! (E.g. -relaxed -relaxed) + .NLIST SEQ,LOC,BIN ; Disable listing SEQuence, LOCation, & BINary + + .PAGE + .REM ~ + + ***************************************** + ** C O P Y R I G H T N O T I C E ** + ***************************************** + + Copyright (c) 2023, Mike Hill + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + +------------------------------------------------------------------------------- + + OUTPUT FROM 'dumpobj -align -nosort README.obj' + ----------------------------------------------- + +GSD: + MODNAME MACRO1 = 0 flags=0 + IDENT MSHILL = 0 flags=0 + PSECT . ABS. = 0 OVR RW ABS GBL I flags=104 + GLOBAL $GLOBL = 0 REF ABS flags=100 + GLOBAL $TTO = 177564 DEF ABS flags=110 + PSECT = 0 CON RW REL LCL I flags=40 + PSECT CORE = 113 CON RW REL LCL I flags=40 + GLOBAL $BASE = 0 DEF REL flags=150 + XFER CORE = 2 flags=10 +ENDGSD +RLD + Location counter definition CORE+0 +TEXT ADDR=0 LEN=113 + 000000: 000240 000407 000020 000344 ........ + 000010: 000020 000350 040000 000261 .....@.. + 000020: 000000 000005 012706 001000 ........ + 000030: 012700 177564 010701 062701 ..t....e + 000040: 000032 004767 000014 000762 ........ + 000050: 105710 100376 116160 177777 ....p... + 000060: 000002 105721 100371 000207 ........ + 000070: 062510 066154 026157 073440 Hello, w + 000100: 071157 062154 006441 000012 orld!... + 000110: 000000 377 ... +ENDMOD + +------------------------------------------------------------------------------- + + ========================================================= + ======== R E F E R E N C E M A T E R I A L ======== + ========================================================= + +MACRO-11 Manual (see the Preface):- + https://usermanual.wiki/Document/AAKX10ATCRSTSEV95PDP11MACRO11LanguageReferenceOct87.185150910.pdf + +Source code of macro11 and dumpobj:- + https://gitlab.com/Rhialto/macro11 + +Source code of obj2bin:- + https://github.com/AK6DN/obj2bin + +Source code of the SIMH emulator:- + https://github.com/simh/simh + https://github.com/simh/Win32-Development-Binaries (how to get binaries) + +Source code of the 2.11 BSD m11 assembler:- + https://minnie.tuhs.org/cgi-bin/utree.pl?file=2.11BSD/src/new/m11 + /Document - m11 documentation (New UCB Edition of m11) + /m11.1 - Usage of m11 (manual page) + +------------------------------------------------------------------------------- + +> REM ======================================================================= +> REM ======== B U I L D m a c r o 1 1 W I T H M S V C + + ======== +> REM ======================================================================= +> "%ProgramFiles(x86)%\Microsoft Visual Studio 9.0\common7\tools\vsvars32.bat" +> REM The following command is all on a single line ... +> cl /D "WIN32" /D "_CRT_SECURE_NO_WARNINGS" /TC /W4 + macro11.c + assemble.c assemble_aux.c assemble_globals.c extree.c listing.c + macros.c mlb2.c mlb-rsx.c mlb-rt11.c object.c parse.c + rad50.c rept_irpc.c stream2.c symbols.c util.c + +------------------------------------------------------------------------------- + + .ENDREM ~ + .END ; OF FILE diff --git a/crossassemblers/macro11/assemble.c b/crossassemblers/macro11/assemble.c index 6d5bf59..0374afc 100644 --- a/crossassemblers/macro11/assemble.c +++ b/crossassemblers/macro11/assemble.c @@ -1,34 +1,153 @@ #define ASSEMBLE__C +/* + * Main assembly code. + */ +#include #include #include -#include "assemble.h" /* my own definitions */ +#include "assemble.h" /* My own definitions */ -#include "assemble_globals.h" #include "assemble_aux.h" - -#include "util.h" - +#include "assemble_globals.h" +#include "extree.h" +#include "listing.h" +#include "macros.h" #include "mlb.h" #include "object.h" -#include "listing.h" #include "parse.h" -#include "symbols.h" -#include "extree.h" -#include "macros.h" +#include "rad50.h" #include "rept_irpc.h" +#include "symbols.h" +#include "util.h" -#include "rad50.h" +#define DEBUG_LSB 0 /* See also parse.c */ + +#define CHECK_EOL check_eol(stack, cp) +#define STRICT_IF_DF_NDF_RECURSES 0 /* 0 = '< ... >' forbidden, else allowed if !STRICTER */ +/* eval_ifdf_ifndf assembles a '.IF DF' or '.IF NDF' statement (or .IIF) */ + +// .if df x & y -> both must be defined +// .if df x ! y -> either may be defined +// .if ndf x & y -> both must be undefined +// .if ndf x ! y -> either may be undefined + +static int eval_ifdf_ifndf( + char **cpp, + int if_df, + STACK *stack) +{ + char *cp = *cpp; + int ok = TRUE; /* 0 = false, 1 = true */ + int op = 0; /* 0 = '&', 1 = '!' */ + + for (;;) { + char *ncp; + int islocal; + int found; + + cp = skipwhite(cp); + +#if STRICT_IF_DF_NDF_RECURSES + if (!STRICTER && *cp == '<') { + ncp = skipwhite(++cp); + found = eval_ifdf_ifndf(&ncp, if_df, stack); + if (*ncp != '>') { + report_err(stack->top, "Missing '>'\n"); + return 0; + } + ncp++; + } else /* { ... Continues below */ +#endif + { /* ... This could be the 'else' part from above */ + char *label; + SYMBOL *sym; + + if ((label = get_symbol(cp, &ncp, &islocal)) == NULL) { +#if DISABLE_EXTREE_IF_DF_NDF + if (RELAXED) + return 0; /* We allow -relaxed to have no parameter */ +#endif + if (support_m11) + if (!STRICTER) + return 0; /* We allow -ym11 without -strict -strict to have no parameter */ + + report_err(stack->top, "Missing symbol name\n"); + return 0; /* Assume a missing symbol returns FALSE */ + } + if (islocal) { + report_err(stack->top, "Local symbols are not allowed: %s\n", label); + free(label); + return 0; /* Assume a local symbol returns FALSE */ + } + sym = lookup_sym(label, &symbol_st); + found = (sym != NULL); + if (found) { + if (SYM_IS_IMPORTED(sym)) { /* Unassigned global symbols are undefined */ + found = 0; + } else if (sym->flags & SYMBOLFLAG_UNDEFINED) { /* Symbols marked undefined are undefined */ + found = 0; + } else if (sym->section->type == SECTION_INSTRUCTION) { /* Opcodes are undefined */ + found = 0; + } else if (sym->section->type == SECTION_PSEUDO) { /* Directives are undefined */ + found = 0; + } else { + ; /* It must be truly "found" */ + } + } + free(label); + } + + if (!if_df) found = !found; + + if (op) { /* '!' = OR */ + ok |= found; /* C has no '||=' */ + } else { /* '&' = AND */ + ok &= found; /* C has no '&&=' */ + } + + cp = skipwhite(ncp); + op = -1; + if (*cp == '&') + op = 0; + if (*cp == '!') + op = 1; + if (op < 0) + break; + cp++; + } + *cpp = cp; + return ok; +} + + +/* abs_section_addr - returns the address of the relevant absolute section */ +/* If the section is REL then the absolute_section address will be returned */ +/* If -a0 is in effect and the section is OVR the parameter will be returned */ +/* Else the absolute_section address will be returned */ + + +static SECTION *abs_section_addr( + SECTION *sect) +{ + if (sect->flags & PSECT_REL) + return &absolute_section; + + if (abs_0_based) + if (sect->flags & PSECT_COM) + return sect; + + return &absolute_section; +} -#define CHECK_EOL check_eol(stack, cp) /* assemble a rad50 value (some number of words). */ -static unsigned * assemble_rad50 ( +static unsigned *assemble_rad50( char *cp, int max, int *count, @@ -38,47 +157,48 @@ static unsigned * assemble_rad50 ( unsigned *ret; int i, len, wcnt; - /* - * Allocate storage sufficient for the rest of - * the line. - */ + /* Allocate storage sufficient for the rest of the line. */ radstr = memcheck(malloc(strlen(cp))); len = 0; + cp = skipwhite(cp); do { - cp = skipwhite(cp); if (*cp == '<') { EX_TREE *value; /* A byte value */ value = parse_unary_expr(cp, 0); cp = value->cp; if (value->type != EX_LIT) { - report(stack->top, "expression must be constant\n"); - radstr[len++] = 0; + report_err(stack->top, "Expression must be constant\n"); + radstr[len++] = '\0'; } else if (value->data.lit >= 050) { - report(stack->top, "invalid character value %o\n", value->data.lit); - radstr[len++] = 0; + report_err(stack->top, "Invalid character value %o\n", + value->data.lit & 0xFFFF); + radstr[len++] = '\0'; } else { - radstr[len++] = value->data.lit; + radstr[len++] = (char) value->data.lit; } free_tree(value); } else { - char quote = *cp++; + char quote = (char) toupper((unsigned char) *cp++); /* MACRO-11 quirk */ - while (*cp && *cp != '\n' && *cp != quote) { + while (*cp != '\0' && *cp != '\n' && *cp != quote) { int ch = ascii2rad50(*cp++); - if (ch == -1) { - report(stack->top, "invalid character '%c'\n", cp[-1]); - radstr[len++] = 0; + if (ch < 0) { + report_err(stack->top, "Invalid character '%c'\n", cp[-1]); + radstr[len++] = '\0'; } else { - radstr[len++] = ch; + radstr[len++] = (char) ch; } } - cp++; /* Skip closing quote */ + if (*cp++ != quote) { + if (STRICT) { + report_warn(stack->top, "Mismatched quote (%c)\n", quote); + } + } } - cp = skipwhite(cp); } while (!EOL(*cp)); @@ -93,8 +213,10 @@ static unsigned * assemble_rad50 ( ret = memcheck (malloc (((wcnt < max) ? max : wcnt) * sizeof (int))); for (i = 0; i < wcnt; i++) { int word = packrad50word(radstr + i * 3, len - (i * 3)); + ret[i] = word; } + /* If max is specified, zero fill */ for (; i < max; i++) ret[i] = 0; @@ -103,6 +225,29 @@ static unsigned * assemble_rad50 ( return ret; } + +/* Get the mode and check the result */ + +int get_mode_check( + char *cp, + char **endp, + ADDR_MODE *mode, + char **error, + STREAM *str, + char *fmt) +{ + int retval; + + *error = NULL; + retval = get_mode(cp, endp, mode, error); + + if (*error) + report_err(str, fmt, *error); + + return retval; +} + + /* assemble - read a line from the input stack, assemble it. */ /* This function is way way too large, because I just coded most of @@ -118,36 +263,61 @@ static int assemble( char *label; /* A label */ char *line; /* The whole line */ SYMBOL *op; /* The operation SYMBOL */ - int local; /* Whether a label is a local label or - not */ + int local; /* Whether a label is a local label or not */ line = stack_getline(stack); if (line == NULL) return -1; /* Return code for EOF. */ - if (!enabl_lc) { /* If lower case disabled, */ + if (!ENABL(LC)) { /* If lower case disabled, */ upcase(line); /* turn it into upper case. */ } cp = line; + stmtno++; /* Increment statement number */ + + list_source(stack->top, line); /* List source */ + + if (STRICT || ENABL(CDR)) { + if (from_file_stream(stack->top)) { + int len = strlen(line) - 1; /* Line will be terminated by "\n") */ + + if (STRICT) + if (len > MAX_FILE_LINE_LENGTH) + report_warn(stack->top, "Line length of %d is longer than the allowed %d\n", + len, MAX_FILE_LINE_LENGTH); + + if (ENABL(CDR)) + if (len > 72) + line[72] = '\0'; + } else { + if (STRINGENT) { + int len = strcspn(line, "\n"); + + if (len >= MAX_FILE_LINE_LENGTH) + report_warn(stack->top, "Processed line length of %d is longer than the allowed %d\n", + len + 1, MAX_FILE_LINE_LENGTH); + } + } + } + /* Frankly, I don't need to keep "line." But I found it quite handy during debugging, to see what the whole operation was, when I'm down to parsing the second operand and things aren't going right. */ - stmtno++; /* Increment statement number */ - - list_source(stack->top, line); /* List source */ - if (suppressed) { /* Assembly is suppressed by unsatisfied conditional. Look for ending and enabling statements. */ + if (!LIST(CND)) + DONT_LIST_THIS_LINE(); + op = get_op(cp, &cp); /* Look at operation code */ - /* FIXME: this code will blindly look into .REM commentary and - find operation codes. Incidentally, so will read_body(). + /* No need to FIX ME: this code will blindly look into .REM commentary + and find operation codes. Incidentally, so will read_body(). That doesn't really matter, though, since the original also did that (line 72 ends the suppressed conditional block): @@ -167,25 +337,29 @@ O 75 .endc if (op->section->type != SECTION_PSEUDO) return 1; /* Not a pseudo-op. */ switch (op->value) { + /* MACRO-11 does not list .IIF (only the second part) or .IF or .IFT or .IFF or .IFTF or .ENDC */ case P_IF: - case P_IFDF: + case P_IFXX: /* .IFxx where xx=DF NDF Z EQ NZ NE L LT G GT LE GE */ suppressed++; /* Nested. Suppressed. */ break; case P_IFTF: if (suppressed == 1) /* Reduce suppression from 1 to 0. */ suppressed = 0; + list_short_value_if(stack->top, suppressed == 0); /* Extension to MACRO-11 */ break; case P_IFF: if (suppressed == 1) { /* Can reduce suppression from 1 to 0. */ if (!conds[last_cond].ok) suppressed = 0; } + list_short_value_if(stack->top, suppressed == 0); /* Extension to MACRO-11 */ break; case P_IFT: if (suppressed == 1) { /* Can reduce suppression from 1 to 0. */ if (conds[last_cond].ok) suppressed = 0; } + list_short_value_if(stack->top, suppressed == 0); /* Extension to MACRO-11 */ break; case P_ENDC: suppressed--; /* Un-nested. */ @@ -196,34 +370,45 @@ O 75 .endc return 1; } - /* The line may begin with "label:[:]" */ + /* The line may begin with "label:[:]", possibly repeated. */ /* PSEUDO P_IIF jumps here. */ reassemble: opcp = cp; - if ((label = get_symbol(cp, &ncp, &local)) != NULL) { + while ((label = get_symbol(cp, &ncp, &local)) != NULL) { int flag = SYMBOLFLAG_PERMANENT | SYMBOLFLAG_DEFINITION | local; SYMBOL *sym; ncp = skipwhite(ncp); if (*ncp == ':') { /* Colon, for symbol definition? */ ncp++; + if (!STRINGENT) + ncp = skipwhite(ncp); /* maybe it's a global definition */ if (*ncp == ':') { - flag |= SYMBOLFLAG_GLOBAL; /* Yes, include global flag */ + if (local) { + report_warn(stack->top, "Local symbol may not be set global '%s'\n", label); + } else { + flag |= SYMBOLFLAG_GLOBAL; /* Yes, include global flag */ + } ncp++; } - - sym = add_sym(label, DOT, flag, current_pc->section, &symbol_st); + if (label[0] == '.' && label[1] == '\0') + sym = NULL; + else + sym = add_sym(label, DOT, flag, current_pc->section, &symbol_st, + stack->top); cp = ncp; if (sym == NULL) - report(stack->top, "Illegal symbol definition %s\n", label); + report_err(stack->top, "Invalid label definition '%s'\n", label); free(label); + list_location(stack->top, DOT); + /* See if local symbol block should be incremented */ - if (!enabl_lsb && !local) { + if (!ENABL(LSB) && !local) { lsb = get_next_lsb(); } @@ -231,6 +416,10 @@ O 75 .endc opcp = cp; label = get_symbol(cp, &ncp, NULL); /* Now, get what follows */ } + else { + /* No more labels */ + break; + } } cp = skipwhite(cp); @@ -253,12 +442,25 @@ O 75 .endc /* Symbol assignment. */ + if (STRICT && local) { + report_warn(stack->top, "Local symbol may not assigned a value '%s'\n", label); + /* return 0; // But do it anyway */ + } + flags = SYMBOLFLAG_DEFINITION | local; cp++; + if (!STRINGENT) + cp = skipwhite(cp); if (*cp == '=') { - flags |= SYMBOLFLAG_GLOBAL; /* Global definition */ + if (local) { + report_warn(stack->top, "Local symbol may not be set global '%s'\n", label); + } else { + flags |= SYMBOLFLAG_GLOBAL; /* Global definition */ + } cp++; } + if (!STRINGENT) + cp = skipwhite(cp); if (*cp == ':') { flags |= SYMBOLFLAG_PERMANENT; cp++; @@ -266,11 +468,29 @@ O 75 .endc cp = skipwhite(cp); - value = parse_expr(cp, 0); + /* V05.03 (DOC: J.1.3) fixed a bug where '.' is on the RHS of an equation: + * .PSECT TESTPS,ABS + * TESTPC == . ; Should be in section 'TESTPS' not '. ABS.' + * .END + * This block fixes the specific case of assigning '.' -- not '.+n' etc. + * See also below for processing with the '-a0' command line option. */ + + if (/* !abs_0_based && */ *cp == '.') { + ncp = skipwhite(cp + 1); + if (EOL(*ncp)) { + value = new_ex_tree(EX_SYM); + value->cp = ncp; + value->data.symbol = current_pc; + } else { + value = parse_expr(cp, 0); + } + } else { + value = parse_expr(cp, 0); + } cp = value->cp; - /* Special code: if the symbol is the program counter, - this is harder. */ + /* Special code: if the symbol is the '.' program counter, + * this is harder. */ if (strcmp(label, ".") == 0) { if (current_pc->section->flags & PSECT_REL) { @@ -282,13 +502,13 @@ O 75 .endc section must = current. */ if (!express_sym_offset(value, &symb, &offset)) { - report(stack->top, "Illegal ORG (for relocatable section)\n"); + report_err(stack->top, "Invalid ORG (for relocatable section)\n"); } else if (SYM_IS_IMPORTED(symb)) { - report(stack->top, "Can't ORG to external location\n"); + report_err(stack->top, "Can't ORG to external location\n"); } else if (symb->flags & SYMBOLFLAG_UNDEFINED) { - report(stack->top, "Can't ORG to undefined sym\n"); + report_err(stack->top, "Can't ORG to undefined sym\n"); } else if (symb->section != current_pc->section) { - report(stack->top, "Can't ORG to alternate section (use PSECT)\n"); + report_err(stack->top, "Can't ORG to alternate section (use .PSECT)\n"); } else { DOT = symb->value + offset; list_value(stack->top, DOT); @@ -298,7 +518,7 @@ O 75 .endc /* If the current section is absolute, the value must be a literal */ if (value->type != EX_LIT) { - report(stack->top, "Can't ORG to non-absolute location\n"); + report_err(stack->top, "Can't ORG to non-absolute location\n"); free_tree(value); free(label); return 0; @@ -314,23 +534,59 @@ O 75 .endc /* regular symbols */ if (value->type == EX_LIT) { - sym = add_sym(label, value->data.lit, flags, &absolute_section, &symbol_st); + + /* V05.03 (DOC: J.1.3) fixed a bug where '.' is on the RHS of an equation: + * .PSECT TESTPS,ABS + * TESTPC == . ; Should be in section 'TESTPS' not '. ABS.' + * .END + * + * I think MACRO-11 should have gone one further: + * If a symbol with an absolute value is assigned in + * a PSECT with the ABS attribute then it should belong + * to that PSECT and not '. ABS.'. Of course, this is if we + * KNOW that this section will not be relocated somewhere else. + * + * This is what we do here for '-a0'. If an absolute symbol is + * assigned within a REL PSECT, it is assigned to '. ABS.' else it + * will be assigned to the current ABS PSECT (which may be '. ABS.'). + * + * Although you can set the attributes ABS,CON it is always + * treated as ABS,OVR. There is no concatination of ABS sections. + * We use ABS,CON to override the effect of '-a0' for a given section. + * + * TODO: Provide a fool-proof solution (?) */ + + sym = add_sym(label, value->data.lit, flags, + abs_section_addr(current_pc->section), &symbol_st, stack->top); + } else if (value->type == EX_SYM || value->type == EX_TEMP_SYM) { - sym = add_sym(label, value->data.symbol->value, flags, value->data.symbol->section, &symbol_st); + sym = add_sym(label, value->data.symbol->value, flags, + value->data.symbol->section, &symbol_st, stack->top); } else { - report(stack->top, "Complex expression cannot be assigned to a symbol\n"); - - if (!pass) { - /* This may work better in pass 2 - something in - RT-11 monitor needs the symbol to apear to be - defined even if I can't resolve its value. */ - sym = add_sym(label, 0, SYMBOLFLAG_UNDEFINED, &absolute_section, &symbol_st); - } else - sym = NULL; + if (value->type == EX_ERR && + value->cp != NULL && + value->data.child.right == NULL && + value->data.child.left != NULL && + value->data.child.left->type == EX_LIT) { + report_warn(stack->top, "Complex expression has been assigned to a symbol\n"); + sym = add_sym(label, value->data.child.left->data.lit, flags, + abs_section_addr(current_pc->section), &symbol_st, stack->top); + } else { + report_err(stack->top, "Complex expression cannot be assigned to a symbol\n"); + if (pass == PASS1) { + /* This may work better in pass 2 - something in + RT-11 monitor needs the symbol to apear to be + defined even if I can't resolve its value. */ + sym = add_sym(label, 0, SYMBOLFLAG_UNDEFINED, + &absolute_section, &symbol_st, stack->top); /* TODO: Use abs_section_addr() ? */ + } else { + sym = NULL; + } + } } if (sym != NULL) - list_value(stack->top, sym->value); + list_symbol(stack->top, sym); free_tree(value); free(label); @@ -358,6 +614,9 @@ O 75 .endc stack_push(stack, macstr); /* Push macro expansion onto input stream */ + if (!LIST(MC)) + DONT_LIST_THIS_LINE(); + return 1; } @@ -370,36 +629,281 @@ O 75 .endc switch (op->section->type) { case SECTION_PSEUDO: + + /* Handle all directives which require DOT to be even */ + + switch (op->value) { + case P_BLKW: + case P_FLT2: + case P_FLT4: + case P_LIMIT: + case P_RAD50: + case P_WORD: + if (DOT & 1) { + report_warn(stack->top, "%s on odd boundary [.EVEN implied]\n", op->label); + DOT++; /* And fix it */ + } + } + + /* Parse the basic syntax of the directive (and its stringentness):- + * + * 0) Skip any white-space + * 1) Ignore directives which do NOTHING if they have no arguments + * 2) Report an error for directives which do NOT accept arguments + * 3) Report an error for directives which REQUIRE arguments but have none + * 4) Pass any directives which allow optional arguments unchanged + * 5) Report an error if we forgot any directives (all must appear here!) + */ + + cp = skipwhite(cp); switch (op->value) { + + /* 1) Ignore directives which do NOTHING if they have no arguments */ + + case P_DSABL: /* -stringent */ + case P_ENABL: /* -stringent */ + case P_FLT2: /* -stringent */ + case P_FLT4: /* -stringent */ + case P_GLOBL: /* -stringent */ + case P_MCALL: /* -stringent */ + case P_MDELETE: /* -stringent */ + case P_WEAK: /* -stringent */ + + if (!EOL(*cp)) + break; + if (!STRINGENT) + return 1; + report_warn(stack->top, "Directive '%s' should have arguments\n", op->label); + return 1; /* Not strictly an error */ + + /* 2) Report an error for directives which do NOT accept arguments */ + case P_PAGE: - case P_PRINT: - case P_SBTTL: + + if (list_level >= 0) { /* Only do .PAGE if .LIST is in force */ + list_line_act |= LIST_PAGE_BEFORE; /* Special case for .PAGE in case of error */ + } + + /* FALL THROUGH */ + + case P_ASECT: + case P_ENDC: + case P_ENDR: /* Undocumented: in MACRO-11 .ENDR is a synonym for .ENDM */ + case P_EOT: /* -stringent (MACRO-11 ignores this depricated directive) */ + case P_EVEN: + case P_IFF: + case P_IFT: + case P_IFTF: + case P_LIMIT: + case P_MEXIT: + case P_ODD: + case P_RESTORE: + case P_SAVE: + + if (!EOL(*cp)) { + if (STRICT || (op-> value != P_ENDR)) + report_warn(stack->top, "Directive '%s' does not accept arguments [ignored]\n", op->label); + if (op-> value != P_ENDR) + while (!EOL(*cp)) + cp++; /* Remove all the arguments (just in case we CHECK_EOL etc.) */ + } + if (!STRINGENT) + break; /* Regardless of the message (if any) */ + if (op->value != P_EOT) + break; + report_warn(stack->top, "Directive '%s' is depricated\n", op->label); + return 1; /* Not strictly an error and there's nothing to do */ + + /* 3) Report an error for directives which REQUIRE arguments but have none */ + + case P_ASCII: /* Note that MACRO-11 uppercases the first character (bug) */ + case P_ASCIZ: /* Note that MACRO-11 uppercases the first character (bug) */ + case P_REM: /* Note that MACRO-11 uppercases the first character (bug) */ + + if (*cp == ';') /* Documented behaviour */ + break; + + /* FALL THROUGH */ + + case P_IDENT: /* Note that MACRO-11 uppercases the first character (bug) */ + case P_INCLUDE: + case P_LIBRARY: + case P_RAD50: /* Note that MACRO-11 uppercases the first character (bug) */ + + if (!STRINGENT && *cp == ';') + break; /* We will treat ';' as an error (below) if -stringent */ + + /* FALL THROUGH */ + + case P_IRP: + case P_IRPC: + case P_IF: /* MACRO-11 treats this as TRUE and expects a .ENDC etc. */ + case P_IFXX: /* -stringent (but the same action as P_IF) */ + case P_IIF: + case P_MACRO: + case P_NARG: + case P_NCHR: + case P_NTYPE: + case P_REPT: + case P_TITLE: /* Note that MACRO-11 uppercases the first character (bug) */ + + if (STRINGENT && op->value == P_IFXX) + report_warn(stack->top, "Directive '%s' is depricated\n", op->label); + if (!EOL(*cp)) + break; + report_err(stack->top, "Directive '%s' requires arguments\n", op->label); + if (op->value == P_IF || op->value == P_IFXX) + push_cond(TRUE, stack->top); /* Despite the error, we act on the .IF */ + return 0; + + /* 4) Pass any directives which allow optional arguments unchanged */ + + case P_BLKB: /* -stringent */ + case P_BLKW: /* -stringent */ + case P_BYTE: /* -stringent */ case P_CROSS: - case P_NOCROSS: - return 1; /* Accepted, ignored. (An obvious - need: get assembly listing - controls working fully. ) */ + case P_CSECT: + case P_END: + case P_ENDM: + case P_ERROR: case P_LIST: - if (pass > 0) { - cp = skipwhite(cp); - if (EOL(*cp)) - list_level++; + case P_NLIST: + case P_NOCROSS: + case P_PACKED: /* -stringent */ + case P_PRINT: + case P_PSECT: + case P_RADIX: + case P_SBTTL: + case P_WORD: /* -stringent */ + + if (!EOL(*cp)) + break; + if (!STRINGENT) + break; + if (op->value != P_BLKB && op->value != P_BLKW && + op->value != P_BYTE && op->value != P_PACKED && + op->value != P_WORD) + break; + report_warn(stack->top, "Directive '%s' should have arguments\n", op->label); + break; /* Not strictly an error */ + + /* 5) Report an error if we forgot any directives (all MUST appear here!) */ + + default: + report_err(stack->top, "Directive '%s' is not yet supported\n", op->label); + return 0; + } + + /* At this point, we only have directives which need to do something + * cp will either point to a character (skipwhite done) or eol. + * If the directive should be on an even boundary, it will be. */ + + switch (op->value) { + + case P_EOT: + /* MACRO-11 ignores this depricated directive */ + return 1; + + case P_PAGE: + /* Note that V05.05 even suppresses lines with labels on them (!) */ + + if (list_level < 0) + return 1; /* Ignore .PAGE if .NLIST is in force */ + + if (*cp == ';' && LIST(COM)) /* Choose if .NLIST COM should always suppress .PAGE */ + list_line_act |= LIST_PAGE_BEFORE; /* Extension: list the .PAGE if it has a comment */ + else + list_line_act |= LIST_PAGE_BEFORE | LIST_SUPPRESS_LINE; /* TODO: Check for labels (?) */ + return 1; + + case P_TITLE: + /* accquire module name and title string */ + { + int len = 0; + + while (ascii2rad50(cp[len]) > 0 && len < symbol_len) + len++; + if (!len) { + report_err(stack->top, "Invalid .TITLE module-name\n"); + return 0; + } + + if (module_name != NULL) { + free(module_name); + } + module_name = memcheck(malloc(len + 1)); + memcpy(module_name, cp, len); + module_name[len] = '\0'; + upcase(module_name); + + strncpy(title_string, cp, 31); + title_string[strcspn(title_string, "\n")] = '\0'; + + /* Remove trailing spaces and/or tabs */ + len = strlen(title_string); + while (--len >= 0) { + if (title_string[len] == ' ' || title_string[len] == '\t') + title_string[len] = '\0'; + else + break; + } + +#if NODO + /* MACRO-11 upper-cases the first character - but it's probably a bug */ + if (islower((unsigned char) title_string[0])) + title_string[0] = (char) toupper((unsigned char) title_string[0]); +#endif } return 1; - case P_NLIST: - if (pass > 0) { - cp = skipwhite(cp); - if (EOL(*cp)) - list_level--; + + case P_SBTTL: + if (lstfile == NULL) + return 1; /* Ignore if we are not listing */ + + if (pass == PASS2 || (list_pass_0 && LIST(LIS) >= 0)) + return 1; /* Ignore if on pass 2 or listing pass 1 itself */ + + if (!LIST(TOC)) + return 1; /* Ignore for .NLIST TOC */ + + if (!toc_shown) { + list_title_line("Table of contents"); + toc_shown = 1; } + + cp[strcspn(cp, "\n")] = '\0'; + + /* Remove trailing spaces and/or tabs */ + { + int len = strlen(cp); + + while (--len >= 0) { + if (cp[len] == ' ' || cp[len] == '\t') + cp[len] = '\0'; + else + break; + } + } + + /* TODO: Instead of suppressing the sequence number within macro calls ... + * ... we could go back up the chain until we find a 'useful' one */ + + fprintf(lstfile, "%*s%*.0d%*s- %.119s\n", + (int) SIZEOF_MEMBER(LSTFORMAT, flag), "", + (int) SIZEOF_MEMBER(LSTFORMAT, line_number), + (int) (within_macro_expansion(stack->top)) ? 0 : stack->top->line, + (int) SIZEOF_MEMBER(LSTFORMAT, gap_after_seq), "", + cp); return 1; case P_IDENT: - if (ident) /* An existing ident? */ - free(ident); /* Discard it. */ + { + if (ident) /* An existing ident? */ + free(ident); /* Discard it. */ - ident = assemble_rad50 (cp, 2, NULL, stack); - return 1; + ident = assemble_rad50(cp, 2, NULL, stack); + return 1; + } case P_RADIX: { @@ -407,19 +911,20 @@ O 75 .endc EX_TREE *value; int ok = 1; - cp = skipwhite(cp); if (EOL(*cp)) { /* If no argument, assume 8 */ radix = 8; + list_value(stack->top, radix); return 1; } + /* Parse the argument in decimal radix */ radix = 10; value = parse_expr(cp, 0); cp = value->cp; if (value->type != EX_LIT) { - report(stack->top, "Argument to .RADIX must be constant\n"); + report_err(stack->top, "Argument to .RADIX must be constant\n"); radix = old_radix; ok = 0; } else { @@ -427,8 +932,8 @@ O 75 .endc list_value(stack->top, radix); if (radix != 8 && radix != 10 && radix != 2 && radix != 16) { + report_err(stack->top, "Argument to .RADIX (%d.) must be 2, 8, 10, or 16\n", radix); radix = old_radix; - report(stack->top, "Argument to .RADIX must be 2, 8, 10, or 16\n"); ok = 0; } } @@ -447,7 +952,7 @@ O 75 .endc if (parse_float(cp, &cp, (op->value == P_FLT4 ? 4 : 2), flt)) { /* All is well */ } else { - report(stack->top, "Bad floating point format\n"); + report_err(stack->top, "Bad floating point format\n"); ok = 0; /* Don't try to parse the rest of the line */ flt[0] = flt[1] /* Store zeroes */ = flt[2] @@ -466,33 +971,81 @@ O 75 .endc } case P_ERROR: - report(stack->top, "%.*s\n", strcspn(cp, "\n"), cp); - return 0; + report_err(stack->top, "%.*s\n", strcspn(cp, "\n"), cp); + + /* FALL THROUGH */ + + case P_PRINT: + { + int ok = (op->value == P_PRINT); + + list_location(stack->top, DOT); + + if (!EOL(*cp)) { + EX_TREE *value = parse_expr(cp, 0); + + cp = value->cp; + ok = ok && CHECK_EOL; + + if (value->type == EX_LIT) { + list_value(stack->top, value->data.lit); + } else if (value->type == EX_SYM || value->type == EX_TEMP_SYM) { + list_symbol(stack->top, value->data.symbol); + } else { + report_warn(stack->top, "Complex expression to %s is not supported\n", op->label); + ok = FALSE; + } + free_tree(value); + } + if (ok) { + if (op->value == P_PRINT) { + if (within_macro_expansion(stack->top)) + MUST_LIST_THIS_LINE(); /* Always list the .PRINT line if in macro expansion */ + if (show_print_lines) { + MUST_LIST_THIS_LINE(); /* Always list the .PRINT line for -sp */ + show_print_line(); /* If not listing, show it instead */ + } + } + } + return ok; + } case P_SAVE: if (sect_sp >= SECT_STACK_SIZE - 1) { - report(stack->top, "Too many saved sections for .SAVE\n"); + report_err(stack->top, "Too many saved sections for .SAVE\n"); return 0; } + + if (STRINGENT) + if (sect_sp >= 16 - 1) + report_warn(stack->top, + "The program section context stack can strictly only handle 16 .SAVEs\n"); + + /* Contrary to the documentation, V05.05 does not appear to save + * and restore the maximum section size */ + sect_sp++; sect_stack[sect_sp] = current_pc->section; dot_stack[sect_sp] = DOT; - return CHECK_EOL; + /* list_location(stack->top, DOT); // V05.05 does not list the location */ + list_3digit_value(stack->top, current_pc->section->sector); + return 1; case P_RESTORE: if (sect_sp < 0) { - report(stack->top, "No saved section for .RESTORE\n"); + report_err(stack->top, "No saved section for .RESTORE\n"); return 0; } else { go_section(tr, sect_stack[sect_sp]); DOT = dot_stack[sect_sp]; list_location(stack->top, DOT); - if (!enabl_lsb) { + list_3digit_value(stack->top, current_pc->section->sector); + if (!ENABL(LSB)) { lsb = get_next_lsb(); } sect_sp--; } - return CHECK_EOL; + return 1; case P_NARG: { @@ -503,7 +1056,7 @@ O 75 .endc label = get_symbol(cp, &cp, &islocal); if (label == NULL) { - report(stack->top, "Bad .NARG syntax\n"); + report_err(stack->top, "Bad .NARG syntax (symbol expected)\n"); return 0; } @@ -513,15 +1066,15 @@ O 75 .endc str = str->next) ; if (!str) { - report(str, ".NARG not within macro expansion\n"); + report_err(str, ".NARG not within macro expansion\n"); free(label); return 0; } mstr = (MACRO_STREAM *) str; - add_sym(label, mstr->nargs, SYMBOLFLAG_DEFINITION | islocal, &absolute_section, - &symbol_st); + add_sym(label, mstr->nargs, SYMBOLFLAG_DEFINITION | islocal, + abs_section_addr(current_pc->section), &symbol_st, stack->top); free(label); list_value(stack->top, mstr->nargs); return CHECK_EOL; @@ -531,22 +1084,26 @@ O 75 .endc { char *string; int islocal; + int nchars; label = get_symbol(cp, &cp, &islocal); if (label == NULL) { - report(stack->top, "Bad .NCHR syntax\n"); + report_err(stack->top, "Bad .NCHR syntax (symbol expected)\n"); return 0; } cp = skipdelim(cp); string = getstring(cp, &cp); + nchars = strlen(string); + free(string); - add_sym(label, strlen(string), SYMBOLFLAG_DEFINITION | islocal, &absolute_section, - &symbol_st); + add_sym(label, nchars, SYMBOLFLAG_DEFINITION | islocal, + abs_section_addr(current_pc->section), &symbol_st, stack->top); free(label); - free(string); + + list_value(stack->top, nchars); return CHECK_EOL; } @@ -558,20 +1115,28 @@ O 75 .endc label = get_symbol(cp, &cp, &islocal); if (label == NULL) { - report(stack->top, "Bad .NTYPE syntax\n"); + report_err(stack->top, "Bad .NTYPE syntax (symbol expected)\n"); return 0; } - cp = skipdelim(cp); + /* MACRO-11 does NOT create the symbol if .NTYPE is at the top-level (OQ error) */ + /* However, it DOES create the symbol within .REPT, .IRP, .IRPC */ + /* If you want .NTYPE outside a macro, you could use .REPT 1; .NTYPE ...; .ENDR */ + + if (STRICTEST) + if (!within_macro_expansion(stack->top)) + report_warn(stack->top, ".NTYPE should strictly be within a macro expansion\n"); - if (!get_mode(cp, &cp, &mode, &error)) { - report(stack->top, - "Bad .NTYPE addressing mode (%s)\n", error); + cp = skipdelim(cp); + if (!get_mode_check(cp, &cp, &mode, &error, stack->top, + "Bad .NTYPE addressing mode (%s)\n")) { free(label); return 0; } - add_sym(label, mode.type, SYMBOLFLAG_DEFINITION | islocal, &absolute_section, &symbol_st); + add_sym(label, mode.type, SYMBOLFLAG_DEFINITION | islocal, + abs_section_addr(current_pc->section), &symbol_st, stack->top); + list_value(stack->top, mode.type); free_addr_mode(&mode); free(label); @@ -581,58 +1146,153 @@ O 75 .endc case P_INCLUDE: { char *name = getstring_fn(cp, &cp); - STREAM *incl; char hitfile[FILENAME_MAX]; + STREAM *incl; if (name == NULL) { - report(stack->top, "Bad .INCLUDE file name\n"); + report_fatal(stack->top, "Bad .INCLUDE file name\n"); return 0; } - name = defext (name, "MAC"); + if (!CHECK_EOL) { + free(name); + return 0; /* V05.05 does not attempt to .INCLUDE for syntax errors */ + } + + name = defext(name, "MAC"); my_searchenv(name, "INCLUDE", hitfile, sizeof(hitfile)); + /* If we can't .INCLUDE the file ... + * ... MACRO-11 throws ".INCLUDE directive file error" and exits */ + if (hitfile[0] == '\0') { - report(stack->top, "Unable to find .INCLUDE file \"%s\"\n", name); + report_fatal(stack->top, "Unable to find .INCLUDE file \"%s\"\n", name); free(name); return 0; } - free(name); incl = new_file_stream(hitfile); if (incl == NULL) { - report(stack->top, "Unable to open .INCLUDE file \"%s\"\n", hitfile); + report_fatal(stack->top, "Unable to open .INCLUDE file \"%s\"\n", hitfile); return 0; } stack_push(stack, incl); + list_line_act |= LIST_PAGE_AFTER; /* Throw a page after listing the '.INCLUDE' */ + return 1; + } - return CHECK_EOL; + case P_LIBRARY: + { + char *name = getstring_fn(cp, &cp); + char hitfile[FILENAME_MAX]; + + if (name == NULL) { + report_fatal(stack->top, "Bad .LIBRARY file name\n"); + return 0; + } + + if (!CHECK_EOL) { + free(name); + return 0; /* V05.05 does not attempt to add the .LIBRARY for syntax errors */ + } + + name = defext(name, "MLB"); + my_searchenv(name, "MCALL", hitfile, sizeof(hitfile)); + + /* If we can't open the .LIBRARY file ... + * ... MACRO-11 throws ".LIBRARY directive file error" and exits */ + + if (hitfile[0] == '\0') { + report_fatal(stack->top, "Unable to find .LIBRARY file \"%s\"\n", name); + free(name); + return 0; + } + free(name); + + /* Only open a specific library ONCE */ + { + int i; + + for (i = 0; i < nr_mlbs; i++) { + if (strcmp(hitfile, mlbs[i]->name) == 0) { + return 1; + } + } + } + + if (nr_mlbs < MAX_MLBS) { + mlbs[nr_mlbs] = mlb_open(hitfile, 0); + } + if (nr_mlbs >= MAX_MLBS || mlbs[nr_mlbs] == NULL) { + report_fatal(stack->top, "Unable to register macro library \"%s\"\n", hitfile); + } else { + nr_mlbs++; + } } + return 1; case P_REM: + /* Read and list [or discard] lines until one with a closing quote */ { char quote[4]; + char quote_ch = *cp++; + char *rem_name; + int rem_line; + + if (quote_ch == '\0' || quote_ch == '\n') { /* ';' is allowed */ + report_err(stack->top, "Unexpected end-of-line (quote character expected)\n"); + return 0; + } - /* Read and discard lines until one with a - closing quote */ + /* Remember where the .REM started in case we need to report_err() it */ + rem_name = memcheck(strdup(stack->top->name)); + rem_line = stack->top->line; - cp = skipwhite(cp); - quote[0] = *cp++; - quote[1] = '\n'; - quote[2] = 0; + /* V05.05: .REM x ... X works (it is case insensitive) */ + quote[0] = (char) toupper((unsigned char) quote_ch); + quote[1] = (char) tolower((unsigned char) quote_ch); + quote[2] = '\n'; + quote[3] = 0; + + if (!LIST(COM)) { + /* Just in case the closing quote is on the same line as .REM */ + cp += strcspn(cp, quote); + if (*cp == quote[0] || *cp == quote[1]) { + cp++; + return CHECK_EOL; /* MACRO-11 does not allow further non-comment stuff on line */ + } + list_flush(); + } for (;;) { cp += strcspn(cp, quote); - if (*cp == quote[0]) - break; /* Found closing quote */ - cp = stack_getline(stack); /* Read next input line */ - if (cp == NULL) - break; /* EOF */ + if (*cp == quote[0] || *cp == quote[1]) + break; + + if (LIST(COM)) + list_flush(); + + line = stack_getline(stack); /* Read next input line */ + if (line == NULL) { + /* list_line_act &= ~LIST_PAGE_BEFORE; // Suppress the EOF page throw */ + report_err(stream_here(rem_name, rem_line), + "Closing quote to .REM %c (here) is missing\n", quote_ch); + free(rem_name); + /* exit(EXIT_FAILURE); */ + return 0; /* EOF */ + } + if (!ENABL(LC)) { /* If lower case disabled, */ + upcase(line); /* turn it into upper case. */ + } + cp = line; + list_source(stack->top, line); /* List source */ } + free(rem_name); } - return 1; + cp++; /* Skip the closing quote */ + return CHECK_EOL; /* MACRO-11 does not allow further non-comment stuff on line */ case P_IRP: { @@ -652,28 +1312,6 @@ O 75 .endc return str != NULL; } - case P_LIBRARY: - { - char hitfile[FILENAME_MAX]; - char *name = getstring_fn(cp, &cp); - - name = defext (name, "MLB"); - my_searchenv(name, "MCALL", hitfile, sizeof(hitfile)); - - if (hitfile[0]) { - mlbs[nr_mlbs] = mlb_open(hitfile, 0); - if (mlbs[nr_mlbs] == NULL) { - report(stack->top, "Unable to register macro library \"%s\"\n", hitfile); - } else { - nr_mlbs++; - } - } else { - report(stack->top, "Unable to locate macro library \"%s\"\n", name); - } - free(name); - } - return CHECK_EOL; - case P_MCALL: { for (;;) { @@ -696,7 +1334,7 @@ O 75 .endc label = get_symbol(cp, &cp, NULL); if (!label) { - report(stack->top, "Illegal .MCALL format\n"); + report_err(stack->top, "Invalid .MCALL syntax (symbol expected)\n"); return 0; } @@ -711,24 +1349,31 @@ O 75 .endc /* Do the actual macro library search */ if (!do_mcall (label, stack)) - report(stack->top, "MACRO %s not found\n", label); + report_err(stack->top, "MACRO '%s' not found\n", label); free(label); } /* NOTREACHED */ } - return 1; + /* NOTREACHED */ case P_MACRO: { - MACRO *mac = defmacro(cp, stack, CALLED_NORMAL); + MACRO *mac; + + if (!LIST(MD)) + DONT_LIST_THIS_LINE(); + mac = defmacro(cp, stack, CALLED_NORMAL); return mac != NULL; } case P_MDELETE: - return 1; /* TODO: or should it just be a NOP? */ - + /* MACRO-11 only really uses this to save assembler memory */ + /* TODO: After a macro has been .MDELETEd its use should cause an error (OQ) + * For the moment, we simply ignore the directive (without syntax checking). */ + return 1; + case P_MEXIT: { STREAM *macstr; @@ -739,120 +1384,254 @@ O 75 .endc macstr = stack->top; if (macstr->vtbl != ¯o_stream_vtbl && macstr->vtbl != &rept_stream_vtbl && macstr->vtbl != &irp_stream_vtbl && macstr->vtbl != &irpc_stream_vtbl) { - report(stack->top, ".MEXIT not within a macro\n"); + report_err(stack->top, ".MEXIT not within a macro\n"); return 0; } - /* and finally, pop the macro */ - stack_pop(stack); + /* and finally, pop the macro */ + stack_pop(stack); + + return 1; + } + + case P_REPT: + { + STREAM *reptstr = expand_rept(stack, cp); + + if (reptstr) + stack_push(stack, reptstr); + return reptstr != NULL; + } + + case P_NLIST: + case P_LIST: + if (pass == PASS1) + return 1; /* Ignore .NLIST [*] and .LIST [*] on pass 1 */ + + /* TODO: Documentation (6.1.1) says list level < 0 suppresses all lines + * except errors and = 0 uses the .LIST/.NLIST settings else, + * if > 0 always lists the line. */ + + if (EOL(*cp)) { + if (!ARGS_IGNORE(L_LIS)) { + /* Don't change the list-level if we disabled the 'LIS' dirarg */ + /* TODO: We _could_ limit the list_level to a 'reasonable' value (?) */ + list_level += (op->value == P_LIST ? 1 : -1); + } + + /* Note that V05.05 does not list '.LIST' and '.NLIST' lines ... + * ... even if they start with a label e.g. 'LABEL: .LIST' */ + +#if NODO /* Set to DODO if you want to always suppress .[N]LIST lines like MACRO-11 V05.05 */ + DONT_LIST_THIS_LINE(); /* Don't list .[N]LIST if within a macro expansion */ +#else + if (within_macro_expansion(stack->top)) { + DONT_LIST_THIS_LINE(); /* Don't list .[N]LIST if within a macro expansion */ + } else { + if (list_level < -1) + MUST_LIST_THIS_LINE(); /* Extension: if transitioning outside the 'expected' levels */ + else if (list_level <= 0) + DONT_LIST_THIS_LINE(); /* Extension: if transitioning outside the 'expected' levels */ + } +#endif + + if (enabl_debug) { + MUST_LIST_THIS_LINE(); /* Always list the line for -yd */ + } + + /* Could use list_value() or [ list_short_value() ] instead -- Extension to MACRO-11 */ + list_signed_value(stack->top, (list_level >= 0) ? + (unsigned) list_level : + (unsigned) 0x8000 | (list_level & 0x7fff)); + return 1; + } + + /* FALLS THROUGH */ + + case P_DSABL: + case P_ENABL: + { + int arg_type; + int arg_value; + + if (op->value == P_NLIST || op->value == P_LIST) { + arg_type = ARGS_NLIST_LIST; + arg_value = (op->value == P_NLIST) ? ARGS_NLIST : ARGS_LIST; + } else { + arg_type = ARGS_DSABL_ENABL; + arg_value = (op->value == P_DSABL) ? ARGS_DSABL : ARGS_ENABL; + } + + while (!EOL(*cp)) { + int argnum; + + label = get_symbol(cp, &cp, NULL); + if (!label) { + report_err(stack->top, "Missing %s option\n", op->label); + return 0; + } + + if (strlen(label) > 3) + argnum = -1; + else + argnum = lookup_arg(label, arg_type); - return CHECK_EOL; - } + if (argnum < 0) { + report_warn(stack->top, "Invalid %s option '%s' [ignored]\n", op->label, label); + free(label); + cp = skipdelim(cp); + continue; + } - case P_REPT: - { - STREAM *reptstr = expand_rept(stack, cp); + if (!ARGS_IGNORE(argnum)) { + if (enabl_list_arg[argnum].flags & ARGS_NOT_SUPPORTED) + report_warn(stack->top, "Unsupported %s option '%s'\n", op->label, label); - if (reptstr) - stack_push(stack, reptstr); - return reptstr != NULL; - } + enabl_list_arg[argnum].curval = arg_value; - case P_ENABL: - /* FIXME - add all the rest of the options. */ - while (!EOL(*cp)) { - label = get_symbol(cp, &cp, NULL); - if (strcmp(label, "AMA") == 0) - enabl_ama = 1; - else if (strcmp(label, "LSB") == 0) { - enabl_lsb = 1; - lsb = get_next_lsb(); - } else if (strcmp(label, "GBL") == 0) { - enabl_gbl = 1; - } else if (strcmp(label, "LC") == 0) { - enabl_lc = 1; - } else if (strcmp(label, "LCM") == 0) { - enabl_lcm = 1; - } else if (strcmp(label, "MCL") == 0) { - enabl_mcl = 1; - } - free(label); - cp = skipdelim(cp); - } - return 1; + if (argnum == E_LSB) { + looked_up_local_sym = TRUE; /* So we see it in the listing if -ylls */ + if (op->value == P_ENABL || RELAXED) + lsb = get_next_lsb(); + } + } - case P_DSABL: - /* FIXME Ditto as for .ENABL */ - while (!EOL(*cp)) { - label = get_symbol(cp, &cp, NULL); - if (strcmp(label, "AMA") == 0) - enabl_ama = 0; - else if (strcmp(label, "LSB") == 0) { - lsb = get_next_lsb(); - enabl_lsb = 0; - } else if (strcmp(label, "GBL") == 0) { - enabl_gbl = 0; - } else if (strcmp(label, "LC") == 0) { - enabl_lc = 0; - } else if (strcmp(label, "LCM") == 0) { - enabl_lcm = 0; - } else if (strcmp(label, "MCL") == 0) { - enabl_mcl = 0; + free(label); + cp = skipdelim(cp); } - free(label); - cp = skipdelim(cp); + return 1; } - return 1; case P_LIMIT: store_limits(stack->top, tr); - return CHECK_EOL; - - case P_TITLE: - /* accquire module name */ - if (module_name != NULL) { - free(module_name); - } - module_name = get_symbol(cp, &cp, NULL); return 1; case P_END: /* Accquire transfer address */ - cp = skipwhite(cp); - if (!EOL(*cp)) { + { + int retval = 1; + if (xfer_address) free_tree(xfer_address); - xfer_address = parse_expr(cp, 0); - cp = xfer_address->cp; + + if (EOL(*cp)) { + xfer_address = new_ex_lit(1); + } else { + xfer_address = parse_expr(cp, 0); + cp = xfer_address->cp; + if (xfer_address->type != EX_LIT && + xfer_address->type != EX_SYM && + xfer_address->type != EX_TEMP_SYM) { + report_err(stack->top, "Complex expression to .END is not supported\n"); + free_tree(xfer_address); + xfer_address = new_ex_lit(1); + retval = 0; + } + } + + if (xfer_address->type == EX_LIT) + list_value(stack->top, xfer_address->data.lit); + else { /* Can only be either EX_SYM or EX_TEMP_SYM */ + list_symbol(stack->top, xfer_address->data.symbol); + } + + retval = (retval && CHECK_EOL); + + if (VERY_RELAXED) /* Continue processing without error for -relaxed*2 */ + return retval; + + /* TODO: Check if we are within a MACRO call or .REPT / .IRP / .IRPC block */ + /* At the moment, we report references to .END in read_body() - which is not ideal */ + /* We could [mis-]use list_within_exp to do this */ + + /* Check if we are within a .IF block (unless -relaxed) */ + if (!RELAXED && last_cond >= 0) { + if (STRICT) { /* If -strict simply report the error */ + report_warn(stack->top, ".END within .IF will not end assembly\n"); + return 0; + } + report_err(stack->top, ".END within .IF has been executed\n"); + retval = 0; /* Else honour it */ + } + + /* Before flushing the whole stack ... flush the topmost (current) entry */ + /* TODO: Put this in stream2.c: stack_flush(stack); */ + + do + cp = stack->top->vtbl->getline(stack->top); + while (cp != NULL); + list_line_act &= ~(LIST_PAGE_BEFORE | LIST_SUPPRESS_LINE); /* Suppress the EOF page throw */ + + /* If not -strict we only flush the current .INCLUDE etc. */ + + if (!STRICTER) + return retval; /* TODO: Provide better m11 support (using -ym11 ?) */ + + /* We stop all further assembly here. + * This means ignoring any further files + * on the command line. */ + + /* Read and discard any lines following the .END */ + /* TODO: Count them and if -yd write the count out (?) */ + /* TODO: Put this in stream2.c: stack_flush_all(stack); */ + do + cp = stack_getline(stack); + while (cp != NULL); + list_line_act &= ~(LIST_PAGE_BEFORE | LIST_SUPPRESS_LINE); /* Suppress the EOF page throw */ + return retval; } - return CHECK_EOL; - case P_IFDF: + case P_IFXX: /* .IFxx where xx=DF NDF Z EQ NZ NE L LT G GT LE GE */ opcp = skipwhite(opcp); - cp = opcp + 3; /* Point cp at the "DF" or - "NDF" part */ + cp = opcp + 3; /* Point cp at the "DF" or "NDF" part etc. */ /* FALLS THROUGH */ + case P_IIF: case P_IF: { - EX_TREE *value; - int ok = FALSE; + int ok = -1; /* Will be set to FALSE or TRUE if 'ok' has been changed */ + unsigned value_word = 0; /* Value to be shown in the value field */ + int show_value = 0; /* < 0 = ok flag, 0 = no value, > 0 = comparison value */ label = get_symbol(cp, &cp, NULL); /* Get condition */ cp = skipdelim(cp); if (!label) { - report(stack->top, "Missing .(I)IF condition\n"); + report_err(stack->top, "Missing %s condition\n", op->label); } else if (strcmp(label, "DF") == 0) { - value = parse_expr(cp, EVALUATE_DEFINEDNESS); - cp = value->cp; - ok = eval_defined(value); - free_tree(value); + +#if DISABLE_EXTREE_IF_DF_NDF + ok = eval_ifdf_ifndf(&cp, 1, stack); +#else /* Optionally use extree to evaluate definedness */ + if (STRICT) { + ok = eval_ifdf_ifndf(&cp, 1, stack); + } else { + EX_TREE *value; + + value = parse_expr(cp, EVALUATE_DEFINEDNESS); + cp = value->cp; + ok = eval_defined(value); + free_tree(value); + } +#endif + } else if (strcmp(label, "NDF") == 0) { - value = parse_expr(cp, EVALUATE_DEFINEDNESS); - cp = value->cp; - ok = eval_undefined(value); - free_tree(value); + +#if DISABLE_EXTREE_IF_DF_NDF + ok = eval_ifdf_ifndf(&cp, 0, stack); +#else /* Optionally use extree to evaluate definedness */ + if (STRICT) { + ok = eval_ifdf_ifndf(&cp, 0, stack); + } else { + EX_TREE *value; + + value = parse_expr(cp, EVALUATE_DEFINEDNESS); + cp = value->cp; + ok = eval_undefined(value); + free_tree(value); + } +#endif + } else if (strcmp(label, "B") == 0 || strcmp(label, "NB") == 0) { /* @@ -892,7 +1671,7 @@ O 75 .endc else thing2 = memcheck(strdup("")); - if (!enabl_lcm) { + if (!ENABL(LCM)) { upcase(thing1); upcase(thing2); } @@ -904,10 +1683,14 @@ O 75 .endc free(thing1); free(thing2); } else if (strcmp(label, "P1") == 0) { - ok = (pass == 0); + ok = (pass == PASS1); } else if (strcmp(label, "P2") == 0) { - ok = (pass == 1); - } else { + ok = (pass == PASS2); + } + + if (ok >= 0) { + show_value = -1; /* We have successfully parsed a TRUE/FALSE .IF [or .IIF] */ + } else if (label) { int sword; unsigned uword; EX_TREE *tvalue = parse_expr(cp, 0); @@ -915,46 +1698,79 @@ O 75 .endc cp = tvalue->cp; if (tvalue->type != EX_LIT) { - report(stack->top, "Bad .IF expression\n"); - list_value(stack->top, 0); + report_err(stack->top, "Bad %s expression\n", op->label); free_tree(tvalue); - ok = TRUE; /* Pick something. */ - } else { - unsigned word; - - /* Convert to signed and unsigned words */ - sword = tvalue->data.lit & 0x7fff; - - /* FIXME I don't know if the following - is portable enough. */ - if (tvalue->data.lit & 0x8000) - sword |= ~0x7FFF; /* Render negative */ - - /* Reduce unsigned value to 16 bits */ - uword = tvalue->data.lit & 0xffff; - - if (strcmp(label, "EQ") == 0 || strcmp(label, "Z") == 0) - ok = (uword == 0), word = uword; - else if (strcmp(label, "NE") == 0 || strcmp(label, "NZ") == 0) - ok = (uword != 0), word = uword; - else if (strcmp(label, "GT") == 0 || strcmp(label, "G") == 0) - ok = (sword > 0), word = sword; - else if (strcmp(label, "GE") == 0) - ok = (sword >= 0), word = sword; - else if (strcmp(label, "LT") == 0 || strcmp(label, "L") == 0) - ok = (sword < 0), word = sword; - else if (strcmp(label, "LE") == 0) - ok = (sword <= 0), word = sword; - else - ok = 0, word = 0; - - list_value(stack->top, word); - free_tree(tvalue); + /* Undefined symbols are treated as zero for '.IF XX' + * V05.05 does strange things when XX is undefined for ... + * ... '.IIF XX' (I'm not quite sure what) */ + + /* If the condition is also invalid, we'll see two errors */ + tvalue = new_ex_lit(0); + } + + /* Convert to signed and unsigned words */ + sword = tvalue->data.lit & 0x7fff; + + /* No need to FIX ME: + * I don't know if the following + * is portable enough. It should be but + * the replacement below surely is. + * if (tvalue->data.lit & 0x8000) + * sword |= ~0x7FFF; // Render negative */ + + if (tvalue->data.lit & 0x8000) + sword = sword - 0x8000; /* Render negative */ + + /* Reduce unsigned value to 16 bits */ + uword = tvalue->data.lit & 0xffff; + + if (strcmp(label, "EQ") == 0 || strcmp(label, "Z") == 0) + ok = (uword == 0), value_word = uword; + else if (strcmp(label, "NE") == 0 || strcmp(label, "NZ") == 0) + ok = (uword != 0), value_word = uword; + else if (strcmp(label, "GT") == 0 || strcmp(label, "G") == 0) + ok = (sword > 0), value_word = sword; + else if (strcmp(label, "GE") == 0) + ok = (sword >= 0), value_word = sword; + else if (strcmp(label, "LT") == 0 || strcmp(label, "L") == 0) + ok = (sword < 0), value_word = sword; + else if (strcmp(label, "LE") == 0) + ok = (sword <= 0), value_word = sword; + else { + report_err(stack->top, "Invalid %s condition '%s'\n", op->label, label); } + + free_tree(tvalue); + if (ok >= 0) + show_value = 1; /* We want to show the actual value we compared against */ } - free(label); + if (label) + free(label); + + if (ok < 0) { + ok = FALSE; /* Pick TRUE or FALSE if we failed to parse the line */ + } else { + if (op->value == P_IIF) { + if (LIST(CND)) { + if (!ok) + list_short_value_if(stack->top, ok); /* Extension to MACRO-11 */ + } else { + if (ok) + list_source(stack->top, skipdelim(cp)); /* Skip to AFTER the .IIF part */ + else + DONT_LIST_THIS_LINE(); + } + } else { + if (show_value < 0) + list_short_value_if(stack->top, ok); /* Extension to MACRO-11 */ + else if (show_value > 0) + list_value_if(stack->top, value_word); /* Extension to MACRO-11 */ + if (!LIST(CND)) + DONT_LIST_THIS_LINE(); + } + } if (op->value == P_IIF) { stmtno++; /* the second half is a @@ -979,54 +1795,62 @@ O 75 .endc return CHECK_EOL; case P_IFF: + if (!LIST(CND)) + DONT_LIST_THIS_LINE(); + if (last_cond < 0) { - report(stack->top, "No conditional block active\n"); + report_err(stack->top, "No conditional block active\n"); return 0; } - if (conds[last_cond].ok) /* Suppress if last cond - is true */ + if (conds[last_cond].ok) /* Suppress if last cond is true */ suppressed++; - return CHECK_EOL; + list_short_value_if(stack->top, suppressed == 0); /* Extension to MACRO-11 */ + return 1; case P_IFT: + if (!LIST(CND)) + DONT_LIST_THIS_LINE(); + if (last_cond < 0) { - report(stack->top, "No conditional block active\n"); + report_err(stack->top, "No conditional block active\n"); return 0; } - if (!conds[last_cond].ok) /* Suppress if last cond - is false */ + if (!conds[last_cond].ok) /* Suppress if last cond is false */ suppressed++; - return CHECK_EOL; + list_short_value_if(stack->top, suppressed == 0); /* Extension to MACRO-11 */ + return 1; case P_IFTF: + if (!LIST(CND)) + DONT_LIST_THIS_LINE(); + if (last_cond < 0) { - report(stack->top, "No conditional block active\n"); + report_err(stack->top, "No conditional block active\n"); return 0; } - return CHECK_EOL; /* Don't suppress. */ + list_short_value_if(stack->top, suppressed == 0); /* Extension to MACRO-11 */ + return 1; /* Don't suppress. */ case P_ENDC: + if (!LIST(CND)) + DONT_LIST_THIS_LINE(); + if (last_cond < 0) { - report(stack->top, "No conditional block active\n"); + report_err(stack->top, "No conditional block active\n"); return 0; } - pop_cond(last_cond - 1); - return CHECK_EOL; + return 1; case P_ENDM: - report(stack->top, "No macro definition block active\n"); + report_err(stack->top, "No .MACRO definition block active\n"); return 0; case P_ENDR: - report(stack->top, "No repeat block active\n"); + report_err(stack->top, "No repeat block active\n"); return 0; case P_EVEN: - cp = skipwhite(cp); - if (!EOL(*cp)) { - report(stack->top, ".EVEN must not have an argument\n"); - } if (DOT & 1) { list_word(stack->top, DOT, 0, 1, ""); DOT++; @@ -1035,9 +1859,6 @@ O 75 .endc return 1; case P_ODD: - if (!EOL(*cp)) { - report(stack->top, ".ODD must not have an argument\n"); - } if (!(DOT & 1)) { list_word(stack->top, DOT, 0, 1, ""); DOT++; @@ -1046,30 +1867,49 @@ O 75 .endc return 1; case P_ASECT: - if (!enabl_lsb) { + if (!ENABL(LSB)) { lsb = get_next_lsb(); } go_section(tr, &absolute_section); list_location(stack->top, DOT); - return CHECK_EOL; + list_3digit_value(stack->top, current_pc->section->sector); + return 1; case P_CSECT: case P_PSECT: { SYMBOL *sectsym; SECTION *sect; - unsigned int old_flags = ~0u; + unsigned int old_flags; label = get_symbol(cp, &cp, NULL); if (label == NULL) { sect = &blank_section; - } - else { + cp = skipwhite(cp); + if (*cp != ',' && !EOL(*cp)) { + report_err(stack->top, "Invalid %s name: %.*s\n", + op->label, strcspn(cp, "\n"), cp); + return 0; + } + } else { +#if NODO + if (label[0] == '.' && label[1] == '\0') { + report_err(stack->top, "%s '.' is not permitted\n", op->label); + /* Actually, RSX DOES permit this */ + free(label); + return 0; + } +#endif + if (strlen(label) > 6) { /* Only possible if -ysl > 6 */ + if (STRINGENT) + report_warn(stack->top, "%s name '%s' truncated to '%.6s'\n", + op->label, label, label); + label[6] = '\0'; + } sectsym = lookup_sym(label, §ion_st); if (sectsym) { sect = sectsym->section; free(label); - old_flags = sect->flags; } else { sect = new_section(); sect->label = label; @@ -1078,7 +1918,9 @@ O 75 .endc sect->size = 0; sect->type = SECTION_USER; sections[sector++] = sect; - sectsym = add_sym(label, 0, SYMBOLFLAG_DEFINITION, sect, §ion_st); + sectsym = add_sym(label, 0, + SYMBOLFLAG_DEFINITION, sect, + §ion_st, stack->top); /* page 6-41 table 6-5 */ if (op->value == P_PSECT) { @@ -1089,11 +1931,32 @@ O 75 .endc } } + old_flags = sect->flags; + + if (op->value == P_CSECT) { + cp = skipwhite(cp); + if (*cp == ',') + report_warn(stack->top, "Unexpected flag(s) to .CSECT directive: %.*s\n", + strcspn(cp, "\n"), cp); + /* Process them anyway and use them as best we can */ + } + cp = skipdelim(cp); if (!EOL(*cp)) { while (cp = skipdelim(cp), !EOL(*cp)) { /* Parse section options */ label = get_symbol(cp, &cp, NULL); + if (!label) { + report_err(stack->top, "Unknown flag(s) to %s directive: %s", + op->label, cp); + return 0; + } + + /* MACRO-11 V05.05 is a lot more flexible with arguments. + * Appended characters to the name are ignored. + * E.g. NOSAVE is valid as is ABSOLUTE. + * TODO: If not STRINGENT we could do this here. */ + if (strcmp(label, "ABS") == 0) { sect->flags &= ~PSECT_REL; /* Not relative */ sect->flags |= PSECT_COM; /* implies common */ @@ -1107,24 +1970,41 @@ O 75 .endc sect->flags |= PSECT_COM; /* Is common */ } else if (strcmp(label, "CON") == 0) { sect->flags &= ~PSECT_COM; /* Concatenated */ - } else if (strcmp(label, "RW") == 0) { + } else if (strcmp(label, "RW") == 0 || (support_m11 && + strcmp(label, "PRV") == 0)) { sect->flags &= ~PSECT_RO; /* Not read-only */ - } else if (strcmp(label, "RO") == 0) { + } else if (strcmp(label, "RO") == 0 || (support_m11 && + strcmp(label, "SHR") == 0)) { sect->flags |= PSECT_RO; /* Is read-only */ - } else if (strcmp(label, "I") == 0) { + } else if (strcmp(label, "I") == 0 || (support_m11 && + strcmp(label, "INS") == 0)) { sect->flags &= ~PSECT_DATA; /* Not data */ - } else if (strcmp(label, "D") == 0) { - sect->flags |= PSECT_DATA; /* data */ + } else if (strcmp(label, "D") == 0 || (support_m11 && + strcmp(label, "DAT") == 0)) { + sect->flags |= PSECT_DATA; /* Is data */ } else if (strcmp(label, "GBL") == 0) { sect->flags |= PSECT_GBL; /* Global */ } else if (strcmp(label, "LCL") == 0) { sect->flags &= ~PSECT_GBL; /* Local */ + } else if (support_m11 && (strcmp(label, "BSS") == 0 || + strcmp(label, "B") == 0)) { + sect->flags &= ~PSECT_RO; /* Not read-only */ + sect->flags |= PSECT_DATA; /* Is data */ + /* TODO: We could initialize all BSS sections with zeros + * We simply need to keep a 'ZER' PSECT flag and ... + * ... for each 'ZER' PSECT, write an RLD followed by ... + * ... a TEXT of zeros (size of ZER PSECT) to the object file ... + * ... at the end of pass 1 just after ENDGSD */ + } else if (support_m11 && strcmp(label, "HGH") == 0) { + /* For compatability with DEC assemblers (?) */ + } else if (support_m11 && strcmp(label, "LOW") == 0) { + /* For compatability with DEC assemblers (?) */ } else { - report(stack->top, "Unknown flag %s given to .PSECT directive\n", label); - free(label); - return 0; + report_warn(stack->top, "Unknown %s attribute '%s' [ignored]\n", + op->label, label); + /* free(label); + * return 0; // Continue parsing anyway */ } - free(label); } /* If a section is declared a second time, and flags @@ -1132,10 +2012,12 @@ O 75 .endc * first time. * See page 6-38 of AA-KX10A-TC_PDP-11_MACRO-11_Reference_Manual_May88.pdf . */ - if (old_flags != ~0u && sect->flags != old_flags) { + if (sect->flags != old_flags) { /* The manual also says that any different * flags are ignored, and an error issued. * Apparently, that isn't true. + * MACRO-11 silently replaces the old flags + * with the new. This seems to be a bug. * Kermit seems to do this in k11cmd.mac: * .psect $pdata ; line 16 * .psect $pdata ,ro,d,lcl,rel,con @@ -1145,91 +2027,131 @@ O 75 .endc * $PDATA 001074 003 (RO,D,LCL,REL,CON) */ - /* - sect->flags = old_flags; - report(stack->top, "Program section flags not identical\n"); - */ + if (STRICTEST) { + report_warn(stack->top, "Program section flags not identical [ignored]\n"); + /* sect->flags = old_flags; // Do not restore the flags */ + } } } - if (!enabl_lsb) { + if (!ENABL(LSB)) { lsb = get_next_lsb(); } go_section(tr, sect); list_location(stack->top, DOT); - + list_3digit_value(stack->top, current_pc->section->sector); return CHECK_EOL; } /* end PSECT code */ break; - case P_WEAK: + case P_CROSS: + case P_NOCROSS: + /* The .CROSS directive with no symbol list is equivalent to .ENABL CRF and + * the .NOCROSS directive with no symbol list is equivalent to .DSABL CRF */ + + if (EOL(*cp)) { + if (!ARGS_IGNORE(E_CRF)) + enabl_list_arg[E_CRF].curval = (op->value == P_CROSS); + return 1; + } + + /* V05.05 .[NO]CROSS X where X is undefined creates a GX reference */ + /* Fall through to P_GLOBL (.[NO]CROSS can both create global undefined symbols) */ + /* FALL THROUGH */ + case P_GLOBL: + case P_WEAK: + /* V05.05 .GLOBL / .WEAK X where X is undefined creates a G reference */ { SYMBOL *sym; int islocal = 0; + int retval = 1; + + SYMBOL_TABLE *add_table = NULL; + int add_flags = SYMBOLFLAG_NONE, + upd_flags = SYMBOLFLAG_NONE; + + switch (op->value) { + case P_CROSS: + case P_NOCROSS: + add_table = &implicit_st; + add_flags = SYMBOLFLAG_GLOBAL; + upd_flags = SYMBOLFLAG_NONE; + break; + + case P_GLOBL: + add_table = &symbol_st; + add_flags = SYMBOLFLAG_GLOBAL; + upd_flags = add_flags; + break; + + case P_WEAK: + add_table = &symbol_st; + add_flags = SYMBOLFLAG_GLOBAL | SYMBOLFLAG_WEAK; /* TODO: Is this BAD for RSX? */ + upd_flags = add_flags; + break; + } - while (!EOL(*cp)) { - /* Loop and make definitions for - comma-separated symbols */ + do { + /* Loop and make definitions for comma-separated symbols */ label = get_symbol(cp, &ncp, &islocal); + if (label != NULL && label[0] == '.' && label[1] == '\0') { + free(label); + label = NULL; + } + if (label == NULL) { - report(stack->top, "Illegal .GLOBL/.WEAK syntax\n"); + int len = strcspn(cp, "\n"); + + report_err(stack->top, "Invalid syntax [symbol expected] ('%.*s')\n", + (len > 20) ? 20 : len, cp); return 0; } if (islocal) { - report(stack->top, "Local label used in .GLOBL/.WEAK\n"); - return 0; + report_err(stack->top, "Local label '%s' invalid in %s\n", label, op->label); + retval = 0; + } else { + sym = lookup_sym(label, &symbol_st); + if (sym) { + sym->flags |= upd_flags; + } else { + sym = add_sym(label, 0, add_flags, + &absolute_section, /* TODO: Use abs_section_addr() ? */ + add_table, stack->top); + } } - - sym = lookup_sym(label, &symbol_st); - if (sym) { - sym->flags |= SYMBOLFLAG_GLOBAL | (op->value == P_WEAK ? SYMBOLFLAG_WEAK : 0); - } else - sym = add_sym(label, 0, - SYMBOLFLAG_GLOBAL | (op->value == P_WEAK ? SYMBOLFLAG_WEAK : 0), - &absolute_section, &symbol_st); - free(label); - cp = skipdelim(ncp); - } + cp = skipwhite(ncp); + if (*cp == ',' && !EOL(cp[1])) + cp = skipdelim(cp); + } while (!EOL(*cp)); + return retval; } - return CHECK_EOL; + case P_BYTE: case P_WORD: { - /* .WORD might be followed by nothing, which - is an implicit .WORD 0 */ + int size = (op->value == P_WORD ? 2 : 1); + + /* .BYTE or .WORD might be followed by nothing, which + * is an implicit '.BYTE 0' or '.WORD 0' */ if (EOL(*cp)) { - if (DOT & 1) { - report(stack->top, ".WORD on odd boundary\n"); - DOT++; /* Fix it, too */ - } - store_word(stack->top, tr, 2, 0); + store_word(stack->top, tr, size, 0); return 1; } else - return do_word(stack, tr, cp, 2); + return do_word(stack, tr, cp, size); } - case P_BYTE: - if (EOL(*cp)) { - /* Blank .BYTE. Same as .BYTE 0 */ - store_word(stack->top, tr, 1, 0); - return 1; - } else - return do_word(stack, tr, cp, 1); - case P_BLKW: case P_BLKB: { EX_TREE *value; int ok = 1; - cp = skipwhite(cp); if (EOL(*cp)) { /* If no argument, assume 1. Documented but * discouraged. Par 6.5.3, page 6-32. */ - /* warning(stack->top, "Argument to .BLKB/.BLKW should be present; 1 assumed\n"); */ value = new_ex_lit(1); } else { value = parse_expr(cp, 0); @@ -1237,10 +2159,13 @@ O 75 .endc } if (value->type != EX_LIT) { - report(stack->top, "Argument to .BLKB/.BLKW must be constant\n"); + report_err(stack->top, "Argument to .BLKB/.BLKW must be constant\n"); ok = 0; } else { - list_value(stack->top, DOT); + if ((op->value == P_BLKW) && (DOT & 1)) + DOT++; /* Force .BLKW to word boundary */ + list_location(stack->top, DOT); + list_value(stack->top, value->data.lit); /* Extension to MACRO-11 */ DOT += value->data.lit * (op->value == P_BLKW ? 2 : 1); change_dot(tr, 0); } @@ -1248,11 +2173,11 @@ O 75 .endc return ok && CHECK_EOL; } - case P_ASCIZ: case P_ASCII: + case P_ASCIZ: { + cp = skipwhite(cp); do { - cp = skipwhite(cp); if (*cp == '<') { EX_TREE *value; /* A byte value */ @@ -1261,13 +2186,17 @@ O 75 .endc store_value(stack, tr, 1, value); free_tree(value); } else { - char quote = *cp++; + char quote = (char) toupper((unsigned char) *cp++); /* MACRO-11 quirk */ - while (*cp && *cp != '\n' && *cp != quote) + while (*cp != '\0' && *cp != '\n' && *cp != quote) store_word(stack->top, tr, 1, *cp++); - cp++; /* Skip closing quote */ - } + if (*cp++ != quote) { + if (STRICT) { + report_warn(stack->top, "Mismatched quote (%c)\n", quote); + } + } + } cp = skipwhite(cp); } while (!EOL(*cp)); @@ -1279,25 +2208,98 @@ O 75 .endc } case P_RAD50: - if (DOT & 1) { - report(stack->top, ".RAD50 on odd boundary\n"); - DOT++; /* Fix it */ - } { - int i, count; - unsigned *rad50; - + int i, + count; + unsigned *rad50; + /* Now assemble the argument */ - rad50 = assemble_rad50 (cp, 0, &count, stack); + rad50 = assemble_rad50(cp, 0, &count, stack); for (i = 0; i < count; i++) { - store_word (stack->top, tr, 2, rad50[i]); + store_word(stack->top, tr, 2, rad50[i]); } - free (rad50); + free(rad50); } return 1; + case P_PACKED: +#define PACKED_POSITIVE 0x0C /* 1100(2) */ +#define PACKED_NEGATIVE 0x0D /* 1101(2) */ +#define PACKED_UNSIGNED 0x0F /* 1111(2) */ + { + int sign; /* The sign comes at the end */ + int ndigits, + nybbles, + byte; + char *tmp; + + cp = skipwhite(cp); + + if (*cp == '+') { + sign = PACKED_POSITIVE; + cp++; + } else if (*cp == '-') { + sign = PACKED_NEGATIVE; + cp++; + } else { + sign = PACKED_UNSIGNED; + } + + /* Count number of digits */ + ndigits = 0; + for (tmp = cp; + isdigit((unsigned char )*tmp); + tmp++) { + ndigits++; + } + + if (ndigits > 31) { + report_err(stack->top, "Too many packed decimal digits\n"); + return 0; + } + + /* If the number of digits is even, + * prefix an imaginary zero. */ + nybbles = !(ndigits % 2); + byte = 0; + + while (isdigit((unsigned char)*cp)) { + int value = *cp - '0'; + byte = (byte << 4) + value; + nybbles++; + if ((nybbles % 2) == 0) { + store_word(stack->top, tr, 1, byte); + byte = 0; + } + cp++; + } + + /* Append the sign, making an even number of nybbles. */ + byte = (byte << 4) + sign; + store_word(stack->top, tr, 1, byte); + + /* Maybe store the number of digits into a symbol. */ + cp = skipdelim(cp); + if (!EOL(*cp)) { + int islocal; + + label = get_symbol(cp, &cp, &islocal); + + if (label == NULL) { + report_err(stack->top, "Bad .PACKED syntax (symbol expected)\n"); + return 0; + } + + add_sym(label, ndigits, SYMBOLFLAG_DEFINITION | islocal, + abs_section_addr(current_pc->section), &symbol_st, stack->top); + free(label); + } + + } + return CHECK_EOL; + default: - report(stack->top, "Unimplemented directive %s\n", op->label); + report_err(stack->top, "Unimplemented directive %s\n", op->label); return 0; } /* end switch (PSEUDO operation) */ @@ -1305,8 +2307,8 @@ O 75 .endc { /* The PC must always be even. */ if (DOT & 1) { - report(stack->top, "Instruction on odd address\n"); - DOT++; /* ...and fix it... */ + report_warn(stack->top, "Instruction on odd address [.EVEN implied]\n"); + DOT++; /* And fix it */ } switch (op->flags & OC_MASK) { @@ -1316,39 +2318,52 @@ O 75 .endc return CHECK_EOL; case OC_MARK: - /* MARK, EMT, TRAP */ { + /* MARK, EMT, TRAP, SPL */ /* TODO: XFC (?) */ + { EX_TREE *value; cp = skipwhite(cp); + if (*cp == '#') { + if (STRICT) + report_warn(stack->top, + "Use of immediate mode (%s #n) is not strictly allowed\n", + op->label); + cp = skipwhite(++cp); /* Allow the hash, but don't require it */ + } + if (EOL (*cp)) { + if (STRICT && (op->value == I_MARK || op->value == I_SPL)) + report_warn(stack->top, "%s requires an operand [assumed 0]\n", op->label); /* Default argument is 0 */ - store_word (stack->top, tr, 2, op->value); - } - else { - if (*cp == '#') - cp++; /* Allow the hash, but - don't require it */ + store_word(stack->top, tr, 2, op->value); + } else { value = parse_expr(cp, 0); cp = value->cp; if (value->type != EX_LIT) { - if (op->value == I_MARK) { - report(stack->top, "Instruction requires " "simple literal operand\n"); + if (op->value == I_MARK || op->value == I_SPL) { + report_err(stack->top, + "%s requires a simple literal operand\n", op->label); store_word(stack->top, tr, 2, op->value); - } - else { + } else { /* EMT, TRAP: handle as two bytes */ - store_value (stack, tr, 1, value); - store_word (stack->top, tr, 1, op->value >> 8); + store_value(stack, tr, 1, value); + store_word(stack->top, tr, 1, op->value >> 8); } } else { - unsigned v = value->data.lit; - unsigned int max = (op->value == I_MARK)? 077 : 0377; - - if (v > max) { - report(stack->top, "Literal operand too large (%d. > %d.)\n", value->data.lit, max); - v = max; + if (op->value == I_MARK || op->value == I_SPL) { + unsigned mask = (op->value == I_MARK) ? 077 /* MARK */ : 07 /* SPL */; + + if (value->data.lit & ~mask) + report_err(stack->top, "%s %d. is out of range [0:%d.]\n", + op->label, value->data.lit, mask); + store_word(stack->top, tr, 2, op->value | (value->data.lit & mask)); + } else { /* I_EMT || I_TRAP */ + if((value->data.lit & 0x8000) ? + (value->data.lit < 0xff00) : (value->data.lit > 0xff)) + report_err(stack->top, "%s ^O%o is out of range [-400:377]\n", + op->label, value->data.lit); + store_word(stack->top, tr, 2, op->value | (value->data.lit & 0377)); } - store_word(stack->top, tr, 2, op->value | v); } free_tree(value); } @@ -1361,14 +2376,23 @@ O 75 .endc unsigned word; char *error; - if (!get_mode(cp, &cp, &mode, &error)) { - report(stack->top, - "Invalid addressing mode (%s)\n", error); + if (!get_mode_check(cp, &cp, &mode, &error, stack->top, + "Invalid addressing mode (%s)\n")) { return 0; } - if (op->value == I_JMP && (mode.type & 070) == 0) { - report(stack->top, "JMP Rn is illegal\n"); + if ((mode.type & 070) == MODE_REG && + (op->value == I_JMP || op->value == I_CALL || + op->value == I_TSTSET || op->value == I_WRTLCK)) { + /* if (!STRICTER) */ + report_warn(stack->top, "%s Rn is impossible\n", op->label); + /* But encode it anyway... */ + } + + if ((mode.type & 070) == MODE_AUTO_INCR && + (op->value == I_JMP || op->value == I_CALL)) { + if (!RELAXED) + report_warn(stack->top, "%s (Rn)+ is architecture dependent\n", op->label); /* But encode it anyway... */ } @@ -1386,28 +2410,38 @@ O 75 .endc unsigned word; char *error; - if (!get_mode(cp, &cp, &left, &error)) { - report(stack->top, - "Invalid addressing mode (1st operand: %s)\n", - error); + if (!get_mode_check(cp, &cp, &left, &error, stack->top, + "Invalid addressing mode (1st operand: %s)\n")) { return 0; } cp = skipwhite(cp); if (*cp++ != ',') { - report(stack->top, "Invalid syntax (comma expected)\n"); + report_err(stack->top, "Invalid syntax (comma expected)\n"); free_addr_mode(&left); return 0; } - if (!get_mode(cp, &cp, &right, &error)) { - report(stack->top, - "Invalid addressing mode (2nd operand: %s)\n", - error); + if (!get_mode_check(cp, &cp, &right, &error, stack->top, + "Invalid addressing mode (2nd operand: %s)\n")) { free_addr_mode(&left); return 0; } + if ((left.type & 070) == MODE_REG && + (right.type & 070) >= MODE_AUTO_INCR && + (right.type & 070) < MODE_OFFSET && + (right.type & 007) == (left.type & 007)) { + if (!RELAXED) + report_warn(stack->top, + "%s Rn,%s%s(Rn)%s is architecture dependent\n", + op->label, + ((right.type & 010) == 010) ? "@" : "", + ((right.type & 060) == 040) ? "-" : "", + ((right.type & 060) == 020) ? "+" : ""); + /* But encode it anyway... */ + } + /* Build instruction word */ word = op->value | left.type << 6 | right.type; store_word(stack->top, tr, 2, word); @@ -1439,9 +2473,9 @@ O 75 .endc if (!express_sym_offset(value, &sym, &offset) || sym->section != current_pc->section) { - report(stack->top, "Bad branch target (%s)\n", - sym ? "not same section" - : "can't express offset"); + report_err(stack->top, "Bad branch target (%s)\n", + sym ? "not same section" + : "can't express offset"); store_word(stack->top, tr, 2, op->value); free_tree(value); return 0; @@ -1453,7 +2487,7 @@ O 75 .endc offset -= DOT + 2; } else { if (value->type != EX_LIT) { - report(stack->top, "Bad branch target (not literal; ABS section)\n"); + report_err(stack->top, "Bad branch target (not literal; ABS section)\n"); store_word(stack->top, tr, 2, op->value); free_tree(value); return 0; @@ -1488,13 +2522,13 @@ O 75 .endc reg = get_register(value); free_tree(value); if (reg == NO_REG) { - report(stack->top, "Invalid addressing mode (register expected)\n"); + report_err(stack->top, "Invalid addressing mode (register expected)\n"); return 0; } cp = skipwhite(cp); if (*cp++ != ',') { - report(stack->top, "Invalid syntax (comma expected)\n"); + report_err(stack->top, "Invalid syntax (comma expected)\n"); return 0; } @@ -1506,13 +2540,13 @@ O 75 .endc SYMBOL *sym; if (!express_sym_offset(value, &sym, &offset)) { - report(stack->top, "Bad branch target (can't express offset)\n"); + report_err(stack->top, "Bad branch target (can't express offset)\n"); free_tree(value); return 0; } /* Must be same section */ if (sym->section != current_pc->section) { - report(stack->top, "Bad branch target (different section)\n"); + report_err(stack->top, "Bad branch target (different section)\n"); free_tree(value); offset = 0; } else { @@ -1522,7 +2556,7 @@ O 75 .endc } } else { if (value->type != EX_LIT) { - report(stack->top, "Bad branch target (not a literal)\n"); + report_err(stack->top, "Bad branch target (not a literal)\n"); offset = 0; } else { offset = DOT + 2 - value->data.lit; @@ -1548,16 +2582,14 @@ O 75 .endc unsigned word; char *error; - if (!get_mode(cp, &cp, &mode, &error)) { - report(stack->top, - "Invalid addressing mode (1st operand: %s)\n", - error); + if (!get_mode_check(cp, &cp, &mode, &error, stack->top, + "Invalid addressing mode (1st operand: %s)\n")) { return 0; } cp = skipwhite(cp); if (*cp++ != ',') { - report(stack->top, "Invalid syntax (comma expected)\n"); + report_err(stack->top, "Invalid syntax (comma expected)\n"); free_addr_mode(&mode); return 0; } @@ -1566,7 +2598,7 @@ O 75 .endc reg = get_register(value); if (reg == NO_REG) { - report(stack->top, "Invalid addressing mode (2nd operand: register expected)\n"); + report_err(stack->top, "Invalid addressing mode (2nd operand: register expected)\n"); free_tree(value); free_addr_mode(&mode); return 0; @@ -1593,27 +2625,45 @@ O 75 .endc reg = get_register(value); if (reg == NO_REG) { - report(stack->top, "Invalid addressing mode (1st operand: register exected)\n"); + report_err(stack->top, "Invalid addressing mode (1st operand: register exected)\n"); free_tree(value); return 0; } cp = skipwhite(cp); if (*cp++ != ',') { - report(stack->top, "Invalid syntax (comma expected)\n"); + report_err(stack->top, "Invalid syntax (comma expected)\n"); return 0; } - if (!get_mode(cp, &cp, &mode, &error)) { - report(stack->top, - "Invalid addressing mode (2nd operand: %s)\n", - error); + if (!get_mode_check(cp, &cp, &mode, &error, stack->top, + "Invalid addressing mode (2nd operand: %s)\n")) { free_tree(value); return 0; } if (op->value == I_JSR && (mode.type & 070) == 0) { - report(stack->top, "JSR Rn,Rm is illegal\n"); + /* if (!STRICTER) */ + report_warn(stack->top, "JSR Rn,Rm is impossible\n"); + /* But encode it anyway... */ + } + + if (op->value == I_JSR && (mode.type & 070) == MODE_AUTO_INCR) { + if (!RELAXED) + report_warn(stack->top, "JSR Rn,(Rm)+ is architecture dependent\n"); + /* But encode it anyway... */ + } + + if (op->value == I_XOR && (mode.type & 070) >= MODE_AUTO_INCR && + (mode.type & 070) < MODE_OFFSET && + (mode.type & 007) == reg) { + if (!RELAXED) + report_warn(stack->top, + "%s Rn,%s%s(Rn)%s is architecture dependent\n", + op->label, + ((mode.type & 010) == 010) ? "@" : "", + ((mode.type & 060) == 040) ? "-" : "", + ((mode.type & 060) == 020) ? "+" : ""); /* But encode it anyway... */ } @@ -1625,7 +2675,7 @@ O 75 .endc return CHECK_EOL; case OC_1REG: - /* One register (RTS,FADD,FSUB,FMUL,FDIV,SPL) */ { + /* One register (RTS, FADD, FSUB, FMUL, FDIV) */ { EX_TREE *value; unsigned reg; @@ -1633,7 +2683,7 @@ O 75 .endc cp = value->cp; reg = get_register(value); if (reg == NO_REG) { - report(stack->top, "Invalid addressing mode (register expected)\n"); + report_err(stack->top, "Invalid addressing mode (register expected)\n"); reg = 0; } @@ -1642,7 +2692,7 @@ O 75 .endc } return CHECK_EOL; -#if 0 +#if NODO /* * Although it is arguable that the FPP TSTF/TSTD instruction has 1 * operand which is a floating point source, the PDP11 Architecture @@ -1654,8 +2704,12 @@ O 75 .endc ADDR_MODE mode; unsigned word; - if (!get_fp_src_mode(cp, &cp, &mode)) { - report(stack->top, "Illegal addressing mode\n"); + error = NULL; + get_fp_src_mode(cp, &cp, &mode, &error); + if (error) { + report_err(stack->top, + "Invalid addressing mode (1st operand, fsrc: %s)\n", + error); return 0; } @@ -1677,24 +2731,24 @@ O 75 .endc char *error; if ((op->flags & OC_MASK) == OC_FPP_FSRCAC) { - if (!get_fp_src_mode(cp, &cp, &mode, &error)) { - report(stack->top, - "Invalid addressing mode (1st operand, fsrc: %s)\n", - error); + error = NULL; + get_fp_src_mode(cp, &cp, &mode, &error); + if (error) { + report_err(stack->top, + "Invalid addressing mode (1st operand, fsrc: %s)\n", + error); return 0; } } else { - if (!get_mode(cp, &cp, &mode, &error)) { - report(stack->top, - "Invalid addressing mode (1st operand: %s)\n", - error); + if (!get_mode_check(cp, &cp, &mode, &error, stack->top, + "Invalid addressing mode (1st operand: %s)\n")) { return 0; } } cp = skipwhite(cp); if (*cp++ != ',') { - report(stack->top, "Invalid syntax (comma expected)\n"); + report_err(stack->top, "Invalid syntax (comma expected)\n"); free_addr_mode(&mode); return 0; } @@ -1704,7 +2758,7 @@ O 75 .endc reg = get_register(value); if (reg == NO_REG || reg > 3) { - report(stack->top, "Invalid destination fp register\n"); + report_err(stack->top, "Invalid destination fp register\n"); reg = 0; } @@ -1733,21 +2787,19 @@ O 75 .endc reg = get_register(value); if (reg == NO_REG || reg > 3) { - report(stack->top, "Invalid source fp register\n"); + report_err(stack->top, "Invalid source fp register\n"); reg = 0; } cp = skipwhite(cp); if (*cp++ != ',') { - report(stack->top, "Invalid syntax (comma expected)\n"); + report_err(stack->top, "Invalid syntax (comma expected)\n"); free_tree(value); return 0; } - if (!get_mode(cp, &cp, &mode, &error)) { - report(stack->top, - "Invalid addressing mode (2nd operand: %s)\n", - error); + if (!get_mode_check(cp, &cp, &mode, &error, stack->top, + "Invalid addressing mode (2nd operand: %s)\n")) { free_tree(value); return 0; } @@ -1785,20 +2837,26 @@ O 75 .endc nwords = 4; cis_common: if (!EOL(*cp)) { - for (int i = 0; i < nwords; i++) { + int i; + + for (i = 0; i < nwords; i++) { if (i > 0) { cp = skipwhite(cp); if (*cp++ != ',') { - report(stack->top, "Invalid syntax (operand %d: comma expected)\n", i+1); + report_err(stack->top, + "Invalid syntax (operand %d: comma expected)\n", i+1); cp--; } } - EX_TREE *ex = parse_expr(cp, 0); - if (!expr_ok(ex)) { - report(stack->top, "Invalid expression (operand %d)\n", i+1); + { + EX_TREE *ex = parse_expr(cp, 0); + + if (!expr_ok(ex)) { + report_err(stack->top, "Invalid expression (operand %d)\n", i+1); + } + cp = ex->cp; + expr[i] = ex; } - cp = ex->cp; - expr[i] = ex; } } else { expr[0] = NULL; @@ -1807,7 +2865,9 @@ O 75 .endc store_word(stack->top, tr, 2, op->value); if (expr[0]) { - for (int i = 0; i < nwords; i++) { + int i; + + for (i = 0; i < nwords; i++) { store_value(stack, tr, 2, expr[i]); } } @@ -1815,7 +2875,7 @@ O 75 .endc return CHECK_EOL; default: - report(stack->top, "Unimplemented instruction format\n"); + report_err(stack->top, "Unimplemented instruction format\n"); return 0; } /* end(handle an instruction) */ } @@ -1826,14 +2886,14 @@ O 75 .endc /* If .ENABL MCL is in effect, try to define the symbol as a * library macro if it is not a defined symbol. */ - if (enabl_mcl) { + if (ENABL(MCL)) { if (lookup_sym(label, &symbol_st) == NULL) { if (do_mcall (label, stack)) { goto do_mcalled_macro; } } } - + /* Only thing left is an implied .WORD directive */ /*JH: fall through in case of illegal opcode, illegal label! */ free(label); @@ -1846,20 +2906,23 @@ int get_next_lsb( { if (lsb_used) { lsb_used = 0; - if (enabl_debug && lstfile) { +#if DEBUG_LSB + if (enabl_debug > 1 && lstfile) { fprintf(lstfile, "get_next_lsb: lsb: %d becomes %d (= next_lsb)\n", lsb, next_lsb); } +#endif return next_lsb++; } else { - if (enabl_debug && lstfile) { +#if DEBUG_LSB + if (enabl_debug > 1 && lstfile) { fprintf(lstfile, "get_next_lsb: lsb: stays %d\n", lsb); } +#endif return lsb; } } -/* assemble_stack assembles the input stack. It returns the error - count. */ +/* assemble_stack assembles the input stack. It returns the error count. */ int assemble_stack( STACK *stack, diff --git a/crossassemblers/macro11/assemble.h b/crossassemblers/macro11/assemble.h index 369a007..08638ce 100644 --- a/crossassemblers/macro11/assemble.h +++ b/crossassemblers/macro11/assemble.h @@ -1,19 +1,20 @@ - #ifndef ASSEMBLE__H #define ASSEMBLE__H -#include "stream2.h" #include "object.h" - +#include "stream2.h" #define DOT (current_pc->value) /* Handy reference to the current location */ + int assemble_stack( STACK *stack, TEXT_RLD *tr); + int get_next_lsb( void); -#endif + +#endif /* ASSEMBLE__H */ diff --git a/crossassemblers/macro11/assemble_aux.c b/crossassemblers/macro11/assemble_aux.c index b00fb3a..00d2b4c 100644 --- a/crossassemblers/macro11/assemble_aux.c +++ b/crossassemblers/macro11/assemble_aux.c @@ -1,22 +1,21 @@ - /* - Smaller operators for assemble -*/ + * Smaller operators for assemble + */ +#include +#include #include #include -#include - -#include "util.h" -#include "assemble_aux.h" /* my own definitions */ +#include "assemble_aux.h" /* My own definitions */ -#include "assemble_globals.h" -#include "macros.h" #include "assemble.h" +#include "assemble_globals.h" #include "listing.h" -#include "symbols.h" +#include "macros.h" #include "parse.h" +#include "symbols.h" +#include "util.h" /* Allocate a new section */ @@ -36,7 +35,6 @@ SECTION *new_section( } - /* This is called by places that are about to store some code, or which want to manually update DOT. */ @@ -63,6 +61,7 @@ void change_dot( current_pc->section->size = DOT + size; } + /* store_word stores a word to the object file and lists it to the listing file */ @@ -72,12 +71,16 @@ int store_word( int size, unsigned word) { + if (size == 1) + if((word & 0x8000) ? word < 0xff00 : word > 0xff) + report_warn(str, "Truncated BYTE %o to %o\n", word, word & 0xff); change_dot(tr, size); list_word(str, DOT, word, size, ""); return text_word(tr, &DOT, size, word); } -/* store_word stores a word to the object file and lists it to the + +/* store_displaced_word stores a word to the object file and lists it to the listing file */ static int store_displaced_word( @@ -171,22 +174,21 @@ void free_addr_mode( mode->offset = NULL; } + /* Get the register indicated by the expression */ unsigned get_register( EX_TREE *expr) { - unsigned reg; + unsigned reg = 0xE; /* Invalid register number */ - if (expr->type == EX_LIT && expr->data.lit <= 7) { + if (expr->type == EX_LIT) reg = expr->data.lit; - return reg; - } - - if (expr->type == EX_SYM && expr->data.symbol->section->type == SECTION_REGISTER) { + else if (expr->type == EX_SYM && expr->data.symbol->section->type == SECTION_REGISTER) reg = expr->data.symbol->value; + + if (reg <= 7) return reg; - } return NO_REG; } @@ -201,7 +203,7 @@ unsigned get_register( void implicit_gbl( EX_TREE *value) { - if (pass || !value) + if (pass > PASS1 || !value) return; /* Only do this in first pass */ switch (num_subtrees(value)) { @@ -209,13 +211,14 @@ void implicit_gbl( switch (value->type) { case EX_UNDEFINED_SYM: { - if (!(value->data.symbol->flags & SYMBOLFLAG_LOCAL)) { + if (!(value->data.symbol->flags & SYMBOLFLAG_LOCAL) && + !isdigit((unsigned char) value->data.symbol->label[0])) { /* Unless it's a local symbol, */ - if (enabl_gbl) { + if (ENABL(GBL)) { /* either make the undefined symbol into an implicit global */ add_sym(value->data.symbol->label, 0, SYMBOLFLAG_GLOBAL, - &absolute_section, &implicit_st); + &absolute_section, &implicit_st, NULL); /* TODO: Use abs_section_addr() ? */ } else { /* or add it to the undefined symbol table, purely for listing purposes. @@ -224,10 +227,10 @@ void implicit_gbl( #define ADD_UNDEFINED_SYMBOLS_TO_MAIN_SYMBOL_TABLE 0 #if ADD_UNDEFINED_SYMBOLS_TO_MAIN_SYMBOL_TABLE add_sym(value->data.symbol->label, 0, SYMBOLFLAG_UNDEFINED, - &absolute_section, &symbol_st); + &absolute_section, &symbol_st, stack); /* TODO: Use abs_section_addr() ? */ #else add_sym(value->data.symbol->label, 0, SYMBOLFLAG_UNDEFINED, - &absolute_section, &undefined_st); + &absolute_section, &undefined_st, NULL); /* TODO: Use abs_section_addr() ? */ #endif } } @@ -250,6 +253,7 @@ void implicit_gbl( } } + /* Done between the first and second passes */ /* Migrates the symbols from the "implicit" table into the main table. */ @@ -265,13 +269,21 @@ void migrate_implicit( if (sym) { continue; // It's already in there. Great. } + + if (isym->flags & SYMBOLFLAG_LOCAL) + continue; /* Do not attempt to migrate local symbols */ + /* These are noticed on pass 1 but they will ... + * ... be reported as invalid expressions later */ + isym->flags |= SYMBOLFLAG_IMPLICIT_GLOBAL; - sym = add_sym(isym->label, isym->value, isym->flags, isym->section, &symbol_st); + sym = add_sym(isym->label, isym->value, isym->flags, isym->section, + &symbol_st, NULL); // Just one other thing - migrate the stmtno sym->stmtno = isym->stmtno; } } + /* Done between second pass and listing */ /* Migrates the symbols from the "undefined" table into the main table. */ @@ -288,7 +300,8 @@ void migrate_undefined( continue; /* It's already in there. Great. */ } isym->flags |= SYMBOLFLAG_UNDEFINED; /* Just in case */ - sym = add_sym(isym->label, isym->value, isym->flags, isym->section, &symbol_st); + sym = add_sym(isym->label, isym->value, isym->flags, isym->section, + &symbol_st, NULL); /* Just one other thing - migrate the stmtno */ sym->stmtno = isym->stmtno; } @@ -338,6 +351,7 @@ int express_sym_offset( return 0; } + /* Translate an EX_TREE into a TEXT_COMPLEX suitable for encoding into the object file. */ @@ -434,6 +448,7 @@ int complex_tree( } } + /* store a word which is represented by a complex expression. */ static void store_complex( @@ -451,7 +466,7 @@ static void store_complex( text_complex_begin(&tx); /* Open complex expression */ if (!complex_tree(&tx, value)) { /* Translate */ - report(refstr, "Invalid expression (complex relocation)\n"); + report_err(refstr, "Invalid expression (complex relocation)\n"); store_word(refstr, tr, size, 0); } else { list_word(refstr, DOT, 0, size, "C"); @@ -459,6 +474,7 @@ static void store_complex( } } + /* store_complex_displaced is the same as store_complex but uses the "displaced" RLD code */ @@ -477,7 +493,7 @@ static void store_complex_displaced( text_complex_begin(&tx); if (!complex_tree(&tx, value)) { - report(refstr, "Invalid expression (complex displaced relocation)\n"); + report_err(refstr, "Invalid expression (complex displaced relocation)\n"); store_word(refstr, tr, size, 0); } else { list_word(refstr, DOT, 0, size, "C"); @@ -485,6 +501,7 @@ static void store_complex_displaced( } } + /* mode_extension - writes the extension word required by an addressing mode */ @@ -559,6 +576,7 @@ void mode_extension( free_addr_mode(mode); } + /* eval_defined - take an EX_TREE and returns TRUE if the tree represents "defined" symbols. */ @@ -581,6 +599,7 @@ int eval_defined( } } + /* eval_undefined - take an EX_TREE and returns TRUE if it represents "undefined" symbols. */ @@ -601,6 +620,7 @@ int eval_undefined( } } + /* push_cond - a new conditional (.IF) block has been activated. Push its context. */ @@ -615,6 +635,7 @@ void push_cond( conds[last_cond].line = str->line; } + /* pop_cond - pop stacked conditionals. */ @@ -647,6 +668,7 @@ void go_section( DOT = sect->pc; } + /* store_value - used to store a value represented by an expression tree into the object file. Used by do_word and .ASCII/.ASCIZ. @@ -682,6 +704,7 @@ void store_value( } } + /* do_word - used by .WORD, .BYTE, and implied .WORD. */ int do_word( @@ -693,7 +716,7 @@ int do_word( int comma; if (size == 2 && (DOT & 1)) { - report(stack->top, ".WORD on odd boundary\n"); + report_warn(stack->top, ".WORD on odd boundary [.EVEN implied]\n"); store_word(stack->top, tr, 1, 0); /* Align it */ } @@ -708,11 +731,23 @@ int do_word( if (value->type != EX_ERR && value->cp > cp) { store_value(stack, tr, size, value); - cp = value->cp; } else { - report(stack->top, "Invalid expression in .WORD\n"); - cp = ""; /* force loop to end */ + char *byteword[] = { "BYTE", "WORD" }; + + if (value->type == EX_ERR && + value->cp != NULL && + value->data.child.right == NULL && + value->data.child.left != NULL && + value->data.child.left->type == EX_LIT) { + report_warn(stack->top, "Invalid expression stored in .%s\n", byteword[size - 1]); + + store_value(stack, tr, size, value->data.child.left); + cp = value->cp; + } else { + report_err(stack->top, "Invalid expression in .%s\n", byteword[size - 1]); + cp = ""; /* force loop to end */ + } } free_tree(value); @@ -727,6 +762,7 @@ int do_word( return 1; } + /* check_branch - check branch distance. */ @@ -740,7 +776,7 @@ int check_branch( int s_offset; if (offset & 1) { - report(stack->top, "Bad branch target (odd address)\n"); + report_err(stack->top, "Bad branch target (odd address)\n"); } /* Sign-extend */ @@ -753,81 +789,138 @@ int check_branch( /* printf can't do signed octal. */ my_ltoa(s_offset, temp, 8); - report(stack->top, "Branch target out of range (distance=%s)\n", temp); + report_err(stack->top, "Branch target out of range (distance=%s)\n", temp); return 0; } return 1; } -/* write_globals writes out the GSD prior to the second assembly pass */ +/* write_psect_globals writes out the psect header and globals for each psect */ +/* if gsd == NULL we only test the globals are unique and update the psect info */ -void write_globals( - FILE *obj) +void write_psect_globals( + GSD *gsd) { - GSD gsd; SYMBOL *sym; SECTION *psect; SYMBOL_ITER sym_iter; int isect; - - if (obj == NULL) { - for (isect = 0; isect < sector; isect++) { - psect = sections[isect]; - - psect->sector = isect; /* Assign it a sector */ - psect->pc = 0; /* Reset its PC for second pass */ + unsigned nsyms = 0; + SYMBOL **symbols = NULL; + + /* TODO: Warnings if global symbols or PSECTs have '_' in them (?) */ + + /* Count the global symbols in the table */ + for (sym = first_sym(&symbol_st, &sym_iter); sym != NULL; sym = next_sym(&symbol_st, &sym_iter)) + if (sym->flags & SYMBOLFLAG_GLOBAL) + nsyms++; + + /* Sort them by name */ + if (nsyms) { + SYMBOL **symbolp = symbols = memcheck(malloc(nsyms * sizeof (SYMBOL *))); + + for (sym = first_sym(&symbol_st, &sym_iter); sym != NULL; sym = next_sym(&symbol_st, &sym_iter)) + if (sym->flags & SYMBOLFLAG_GLOBAL) + *symbolp++ = sym; + + qsort(symbols, nsyms, sizeof(SYMBOL *), symbol_compar); + + if (nsyms > 1) { + unsigned i = 0, + j = 0; + + while (++j < nsyms) { + if (symbols[i] && strncmp(symbols[i]->label, symbols[j]->label, 6) == 0) { + if (strncmp(symbols[i]->label, symbols[j]->label, 6) == 0) { + report_fatal(NULL, "Global symbol %s (in %s) causes %s (in %s) to be ignored\n", + symbols[i]->label, + (symbols[i]->section->label[0] == '\0') ? ". BLK." : symbols[i]->section->label, + symbols[j]->label, + (symbols[j]->section->label[0] == '\0') ? ". BLK." : symbols[j]->section->label); + + symbols[j] = NULL; + } + } else { + i = j; + } + } } - return; /* Nothing more to do if no OBJ file. */ } - gsd_init(&gsd, obj); - - gsd_mod(&gsd, module_name); - - if (ident) - gsd_ident(&gsd, ident); - /* write out each PSECT with its global stuff */ /* Sections must be written out in the order that they - appear in the assembly file. */ + * appear in the assembly file. */ for (isect = 0; isect < sector; isect++) { psect = sections[isect]; - gsd_psect(&gsd, psect->label, psect->flags, psect->size); + if (gsd) + gsd_psect(gsd, psect->label, psect->flags, psect->size); psect->sector = isect; /* Assign it a sector */ psect->pc = 0; /* Reset its PC for second pass */ - sym = first_sym(&symbol_st, &sym_iter); - while (sym) { - if ((sym->flags & SYMBOLFLAG_GLOBAL) && sym->section == psect) { - gsd_global(&gsd, sym->label, - ((sym->flags & SYMBOLFLAG_DEFINITION) ? GLOBAL_DEF : 0) | - ((sym->flags & SYMBOLFLAG_WEAK) ? GLOBAL_WEAK : 0) | - ((sym->section->flags & PSECT_REL) ? GLOBAL_REL : 0) | - 0100, - /* Looks undefined, but add it in anyway */ - sym->value); + if (gsd && nsyms) { + unsigned i; + + for (i = 0; i < nsyms; i++) { + if (symbols[i] && symbols[i]->section == psect) { + gsd_global(gsd, symbols[i]->label, + ((symbols[i]->flags & SYMBOLFLAG_DEFINITION) ? GLOBAL_DEF : 0) | + ((symbols[i]->flags & SYMBOLFLAG_WEAK) ? GLOBAL_WEAK : 0) | + ((symbols[i]->section->flags & PSECT_REL) ? GLOBAL_REL : 0) | + 0100, + /* Looks undefined, but add it in anyway */ + symbols[i]->value); + } } - sym = next_sym(&symbol_st, &sym_iter); } } - /* Now write out the transfer address */ - if (xfer_address->type == EX_LIT) { - gsd_xfer(&gsd, ". ABS.", xfer_address->data.lit); - } else { - SYMBOL *lsym; - unsigned offset; + if (nsyms) + free(symbols); +} - if (!express_sym_offset(xfer_address, &lsym, &offset)) { - report(NULL, "Illegal program transfer address\n"); + +/* write_globals writes out the GSD prior to the second assembly pass */ + +void write_globals( + FILE *obj) +{ + GSD gsd; + + if (obj == NULL) { + write_psect_globals(NULL); /* Test the globals and fix up the psect if we don't have an object file */ + return; + } + + gsd_init(&gsd, obj); + + gsd_mod(&gsd, module_name); + + if (ident) + gsd_ident(&gsd, ident); + + write_psect_globals(&gsd); + + /* Finally write out the transfer address */ + if (xfer_address == NULL) { + gsd_xfer(&gsd, ". ABS.", 1); + } else { + if (xfer_address->type == EX_LIT) { + gsd_xfer(&gsd, ". ABS.", xfer_address->data.lit); } else { - gsd_xfer(&gsd, lsym->section->label, lsym->value + offset); + SYMBOL *lsym; + unsigned offset; + + if (!express_sym_offset(xfer_address, &lsym, &offset)) { + report_err(NULL, "Invalid program transfer address" /* " [set to 1]" */ "\n"); + /* gsd_xfer(&gsd, ". ABS.", 1); */ + } else { + gsd_xfer(&gsd, lsym->section->label, lsym->value + offset); + } } } gsd_flush(&gsd); - gsd_end(&gsd); } diff --git a/crossassemblers/macro11/assemble_globals.c b/crossassemblers/macro11/assemble_globals.c index 83a8bb1..417b444 100644 --- a/crossassemblers/macro11/assemble_globals.c +++ b/crossassemblers/macro11/assemble_globals.c @@ -1,57 +1,42 @@ - #define ASSEMBLE_GLOBALS__C - -#include "assemble_globals.h" /* own definitions */ +#include "assemble_globals.h" /* My own definitions */ #include "object.h" /* GLOBAL VARIABLES */ -int pass = 0; /* The current assembly pass. 0 = first pass */ -int stmtno = 0; /* The current source line number */ -int radix = 8; /* The current input conversion radix */ - +int pass; /* The current assembly pass. 0 = first pass [PASS1] */ +int stmtno; /* The current source line number */ +int radix; /* The current input conversion radix */ -int lsb = 0; /* The current local symbol section identifier */ -int lsb_used = 0; /* Whether there was a local symbol using this lsb */ -int next_lsb = 0; /* The number of the next local symbol block */ -int last_macro_lsb = 0; /* The last block in which a macro - automatic label was created */ +int lsb; /* The current local symbol section identifier */ +int lsb_used; /* Whether there was a local symbol using this lsb */ +int next_lsb; /* The number of the next local symbol block */ +int last_macro_lsb; /* The last block in which a macro + * automatic label was created */ -int last_locsym = 32768; /* The last local symbol number generated */ +int last_locsym; /* The last local symbol number generated */ +int suppressed; /* Assembly suppressed by failed conditional */ int enabl_debug = 0; /* Whether assembler debugging is enabled */ -int opt_enabl_ama = 0; /* May be changed by command line */ -int enabl_ama; /* When set, chooses absolute (037) versus - PC-relative */ - /* (067) addressing mode */ -int enabl_lsb = 0; /* When set, stops non-local symbol - definitions from delimiting local - symbol sections. */ - -int enabl_gbl = 1; /* Implicit definition of global symbols */ - -int enabl_lc = 1; /* If lowercase disabled, convert assembler - source to upper case. */ - -int enabl_lcm = 0; /* If lowercase disabled, .IF IDN/DIF are - case-sensitive. */ +int support_m11 = 0; /* Do we want to support m11 extensions? */ -int enabl_mcl = 0; /* When set, unknown symbols are looked up - as if .MCALL had been done. */ +int abs_0_based = 0; /* TRUE if all ABSolute sections are zero based (else only '. ABS.') */ -int suppressed = 0; /* Assembly suppressed by failed conditional */ +int strictness = 0; /* Neither -strict nor -relaxed are in effect */ +int ignore_fn_dev = 0; /* Ignore device names in .INCLUDE and .LIBRARY? */ +int ignore_fn_dir = 0; /* Ignore directory names in .INCLUDE and .LIBRARY? */ MLB *mlbs[MAX_MLBS]; /* macro libraries specified on the - command line */ + * command line */ int nr_mlbs = 0; /* Number of macro libraries */ -COND conds[MAX_CONDS]; /* Stack of recent conditions */ -int last_cond; /* 0 means no stacked cond. */ +COND conds[MAX_CONDS]; /* Stack of recent conditions */ +int last_cond; /* 0 means no stacked cond. */ SECTION *sect_stack[SECT_STACK_SIZE]; /* 32 saved sections */ int dot_stack[SECT_STACK_SIZE]; /* 32 saved sections */ @@ -73,16 +58,16 @@ the assembler: */ SECTION register_section = { "*REGISTERS*", SECTION_REGISTER, 0, 0, 0, 0 -}; /* the section containing the registers */ +}; /* The section containing the registers */ SECTION pseudo_section = { "*PSEUDO*", SECTION_PSEUDO, 0, 0, 0, 0 -}; /* the section containing the - pseudo-operations */ +}; /* The section containing the + * pseudo-operations */ SECTION instruction_section = { "*INSTR*", SECTION_INSTRUCTION, 0, 0, 0, 0 -}; /* the section containing instructions */ +}; /* The section containing instructions */ SECTION macro_section = { "*MACRO*", SECTION_SYSTEM, 0, 0, 0, 0 @@ -93,16 +78,15 @@ SECTION macro_section = { SECTION absolute_section = { ". ABS.", SECTION_SYSTEM, PSECT_GBL | PSECT_COM, 0, 0, 0 }; /* The default - absolute section */ + * absolute section */ SECTION blank_section = { "", SECTION_SYSTEM, PSECT_REL, 0, 0, 1 }; /* The default relocatable section */ SECTION *sections[256] = { - /* Array of sections in the order they were - defined */ + /* Array of sections in the order they were defined */ &absolute_section, &blank_section, }; -int sector = 2; /* number of such sections */ +int sector = 2; /* Number of such sections */ diff --git a/crossassemblers/macro11/assemble_globals.h b/crossassemblers/macro11/assemble_globals.h index c229852..d32a4af 100644 --- a/crossassemblers/macro11/assemble_globals.h +++ b/crossassemblers/macro11/assemble_globals.h @@ -1,15 +1,20 @@ - #ifndef ASSEMBLE_GLOBALS__H #define ASSEMBLE_GLOBALS__H +#include "extree.h" #include "mlb.h" #include "symbols.h" -#include "extree.h" +#define MAX_FILE_LINE_LENGTH 132 /* DOC: 2.2 - Max length of an input source line from a file */ +#define START_LOCSYM 30000 /* DOC: J.1.6 - Start locally generated symbols at 30000$ */ +#define MAX_LOCSYM 65535 /* DOC: 3.5 - Strictly, local symbols are 1$ to 65535$ */ +#define BAD_LOCSYM 999999 /* No local symbol may ever be higher than this */ +#define MAX_MLBS 32 /* DOC: 6.10.1 - Number of macro libraries (RT-11 max <= 12) */ + /* TODO: DOC: 6.10.2 - max nesting of .INCLUDE directives is five */ + /* TODO: Add other limits which we have coded directly (!) */ -#define MAX_MLBS 32 /* number of macro libraries */ #define MAX_CONDS 256 typedef struct cond { @@ -20,9 +25,12 @@ typedef struct cond { #define SECT_STACK_SIZE 32 +#define PASS1 0 /* Value of 'pass' for pass 1 */ +#define PASS2 1 /* Value of 'pass' for pass 2 */ + #ifndef ASSEMBLE_GLOBALS__C /* GLOBAL VARIABLES */ -extern int pass; /* The current assembly pass. 0 = first pass */ +extern int pass; /* The current assembly pass. 0 = first pass [PASS1] */ extern int stmtno; /* The current source line number */ extern int radix; /* The current input conversion radix */ extern int lsb; /* The current local symbol section identifier */ @@ -35,36 +43,55 @@ extern int last_locsym; /* The last local symbol number generated */ extern int enabl_debug; /* Whether assembler debugging is enabled */ -extern int opt_enabl_ama; /* May be changed by command line */ +extern int support_m11; /* Do we want to support m11 extensions? */ -extern int enabl_ama; /* When set, chooses absolute (037) versus - PC-relative */ - /* (067) addressing mode */ -extern int enabl_lsb; /* When set, stops non-local symbol - definitions from delimiting local - symbol sections. */ +extern int abs_0_based; /* TRUE if all ABSolute sections are zero based (else only '. ABS.') */ -extern int enabl_gbl; /* Implicit definition of global symbols */ +extern int strictness; /* How strict (relaxed) do we want to be? */ + /* <0 = relaxed, 0 = normal, >0 = strict */ -extern int enabl_lc; /* If lowercase disabled, convert assembler - source to upper case. */ -extern int enabl_lcm; /* If lowercase disabled, .IF IDN/DIF are - case-sensitive. */ -extern int suppressed; /* Assembly suppressed by failed conditional */ +#ifndef STRINGENTNESS +#define STRINGENTNESS 4 /* Strictness level we consider to be STRINGENT (0-4) */ +#endif +#define STRINGENT (strictness >= STRINGENTNESS) /* As STRICTEST but also follow the MACRO-11 documentation */ + +#if (STRINGENTNESS > 3) +#define STRICT (strictness > 0) /* Close to MACRO-11 V05.05 syntax */ +#define STRICTER (strictness > 1) /* As close as we like or even more */ +#define STRICTEST (strictness > 2) /* Really mega-strict (e.g. .END required) */ +#elif (STRINGENTNESS > 2) +#define STRICT (strictness >= 0) /* Close to MACRO-11 V05.05 syntax */ +#define STRICTER (strictness > 0) /* As close as we like or even more */ +#define STRICTEST (strictness > 1) /* Really mega-strict (e.g. .END required) */ +#elif (STRINGENTNESS > 1) +#define STRICT (strictness >= 0) /* Close to MACRO-11 V05.05 syntax */ +#define STRICTER (strictness >= 0) /* As close as we like or even more */ +#define STRICTEST (strictness > 0) /* Really mega-strict (e.g. .END required) */ +#else +#define STRICT (strictness >= 0) /* Close to MACRO-11 V05.05 syntax */ +#define STRICTER (strictness >= 0) /* As close as we like or even more */ +#define STRICTEST (strictness >= 0) /* Really mega-strict (e.g. .END required) */ +#endif -extern MLB *mlbs[MAX_MLBS]; /* macro libraries specified on the command line */ -extern int nr_mlbs; /* Number of macro libraries */ +#define RELAXED (strictness < 0) /* Relax the rules as much as we like */ +#define VERY_RELAXED (strictness < -1) /* Relax the rules so much that even .END isn't the end */ -extern int enabl_mcl; /* If MCALL of unknown symbols is enabled. */ +extern int suppressed; /* Assembly suppressed by failed conditional */ -extern COND conds[MAX_CONDS]; /* Stack of recent conditions */ -extern int last_cond; /* 0 means no stacked cond. */ +extern int ignore_fn_dev; /* Ignore device names in .INCLUDE and .LIBRARY? */ +extern int ignore_fn_dir; /* Ignore directory names in .INCLUDE and .LIBRARY? */ -extern SECTION *sect_stack[SECT_STACK_SIZE]; /* 32 saved sections */ -extern int dot_stack[SECT_STACK_SIZE]; /* 32 saved sections */ +extern MLB *mlbs[MAX_MLBS]; /* macro libraries specified on the command line */ +extern int nr_mlbs; /* Number of macro libraries */ + +extern COND conds[MAX_CONDS]; /* Stack of recent conditions */ +extern int last_cond; /* 0 means no stacked cond. */ + +extern SECTION *sect_stack[SECT_STACK_SIZE]; /* Saved sections: ^psect */ +extern int dot_stack[SECT_STACK_SIZE]; /* Saved sections: '.' */ extern int sect_sp; /* Stack pointer */ -extern char *module_name; /* The module name (taken from the 'TITLE'); */ +extern char *module_name; /* The module name (taken from the '.TITLE') */ extern unsigned *ident; /* .IDENT name (encoded RAD50 value) */ @@ -88,7 +115,6 @@ extern SECTION blank_section; extern SECTION *sections[256]; /* Array of sections in the order they were defined */ extern int sector; /* number of such sections */ -#endif +#endif /* ASSEMBLE_GLOBALS__C */ - -#endif +#endif /* ASSEMBLE_GLOBALS__H */ diff --git a/crossassemblers/macro11/dumpobj.c b/crossassemblers/macro11/dumpobj.c index c81aeaa..af83b02 100644 --- a/crossassemblers/macro11/dumpobj.c +++ b/crossassemblers/macro11/dumpobj.c @@ -1,4 +1,5 @@ /* Dump and interpret an object file. */ +/* Optionally create a paper-tape (LDA) file. */ /* Copyright (c) 2001, Richard Krehbiel @@ -41,26 +42,120 @@ DAMAGE. #include #include -#include "rad50.h" +#ifdef WIN32 +#define strcasecmp stricmp +#if !__STDC__ +#define stricmp _stricmp +#endif +#endif +#include "rad50.h" #include "util.h" -#define WORD(cp) ((*(cp) & 0xff) + ((*((cp)+1) & 0xff) << 8)) +#ifndef SKIP_GIT_INFO +#include "git-info.h" +#endif + +#define VERSION "February 13, 2023" /* Also check the text above the "Usage:" section below */ #define NPSECTS 256 +#define MAX_LDA_ADDR 0175777 /* Higher addresses will not be written to the LDA */ + /* TODO: If we 'variablize' MAX_LDA_ADDR force it to odd */ #ifndef DEFAULT_OBJECTFORMAT_RT11 -#define DEFAULT_OBJECTFORMAT_RT11 0 +#define DEFAULT_OBJECTFORMAT_RT11 -1 /* -1 = Auto-detect, 0 = RSX-11M/PLUS, 1 = RT-11 */ +#endif + +#ifndef DEFAULT_ALIGNMENT +#define DEFAULT_ALIGNMENT ' ' /* Default output text alignment: choose ' ' or '\t' */ +#endif + +#ifndef BADBIN_XFERAD +#define BADBIN_XFERAD 0 /* xferad to use if badbin > 0: choose 0 or 1 */ #endif +#ifndef XFERAD_WHEN_ZERO +#define XFERAD_WHEN_ZERO 2 /* xferad to use if the provides XFER=0 */ +#endif /* Set to zero to disable this functionality (keep 0=0) */ + +#define WORD(cp) ((*(cp) & 0xff) + ((*((cp)+1) & 0xff) << 8)) + int psectid = 0; char *psects[NPSECTS]; FILE *bin = NULL; +int badfmt = 0; int badbin = 0; int xferad = 1; +unsigned highest_addr = 0; +int psects_with_data = 0; + +int loud = 1; /* Set to 0 by '-q' */ +int loudtxt = 1; /* Set to 0 by '-qt' */ +char alignment = DEFAULT_ALIGNMENT; /* Set with the '-[no]align' option */ +int sortgsd = 1; /* Set to 0 by '-nosort' */ +unsigned origin = 0; /* Set with the '-o[f]' option */ +int reloc_internal = 1; /* Set to 0 by '-of' & 1 by '-o' */ +char *word_patch = NULL; /* Set with the '-w' option */ +unsigned new_xferad = MAX_LDA_ADDR+1; /* Set with the '-x' option */ + +/**** Summary [ '-s' option ] name strings and counters ****/ +/* Strings starting with "*" may cause binary errors */ + +char *blkname[] = { + "*" "Unused ", /* 00 */ + " " "GSD ", /* 01 */ /* Must be first in OBJ */ + " " "ENDGSD ", /* 02 */ /* Only one - must be before ENDMOD and no more GSDs follow */ + " " "TXT ", /* 03 */ /* At least one RLD must precede the first TXT (i.e. psect)*/ + " " "RLD ", /* 04 */ + " " "ISD ", /* 05 */ /* The RT-11 linker ignores this */ + " " "ENDMOD ", /* 06 */ /* Only one - must be last in OBJ */ + "*" "LIBHDR ", /* 07 */ + "*" "LIBEND " /* 10 */ + }; + +char *gsdname[] = { + " " "MODNAME", /* 00 */ + "*" "CSECT ", /* 01 */ + " " "ISD ", /* 02 */ /* The RT-11 linker ignores this */ + " " "XFER ", /* 03 */ + " " "GLOBAL ", /* 04 */ /* Each name ought to be unique */ + " " "PSECT ", /* 05 */ /* This seems to work if not unique */ + " " "IDENT ", /* 06 */ + "*" "VSECT ", /* 07 */ + "*" "COMPRTN" /* 10 */ + }; + +char *rldname[] = { + "*" "Not used (0) ", /* 00 */ + " " "Internal reloc", /* 01 */ + "*" "Global reloc", /* 02 */ + "*" "Internal Disp ", /* 03 */ + "*" "Global Disp ", /* 04 */ + "*" "Global + Off ", /* 05 */ + "*" "Gbl + Off Disp", /* 06 */ + " " "Loc (.) Def ", /* 07 */ + " " "Loc (.) Mod ", /* 10 */ + "*" ".LIMIT ", /* 11 */ + "*" "Psect ", /* 12 */ + "*" "Not used (13) ", /* 13 */ + "*" "Psect Disp ", /* 14 */ + "*" "Psect + Off ", /* 15 */ + "*" "Psect+Off Disp", /* 16 */ + "*" "Complex " /* 17 */ + }; + +#define BLKSIZ (sizeof(blkname) / sizeof(blkname[0])) +#define GSDSIZ (sizeof(gsdname) / sizeof(gsdname[0])) +#define RLDSIZ (sizeof(rldname) / sizeof(rldname[0])) + +int blkcnt[BLKSIZ] = { 0 }; +int gsdcnt[GSDSIZ] = { 0 }; +int rldcnt[RLDSIZ] = { 0 }; /* memcheck - crash out if a pointer (returned from malloc) is NULL. */ +/* - this routine is already [unchanged] in util.c */ +#if 0 void *memcheck( void *ptr) { @@ -71,6 +166,7 @@ void *memcheck( return ptr; } +#endif char *readrec( FILE *fp, @@ -92,6 +188,7 @@ char *readrec( if (c != 1) { fprintf(stderr, "Improperly formatted OBJ file (1)\n"); + badfmt++; return NULL; /* Not a properly formatted file. */ } @@ -100,6 +197,7 @@ char *readrec( c = fgetc(fp); if (c != 0) { fprintf(stderr, "Improperly formatted OBJ file (2)\n"); + badfmt++; return NULL; /* Not properly formatted */ } @@ -110,6 +208,7 @@ char *readrec( if (c == EOF) { if (rt11) { fprintf(stderr, "Improperly formatted OBJ file (3)\n"); + badfmt++; } return NULL; } @@ -120,6 +219,7 @@ char *readrec( c = fgetc(fp); if (c == EOF) { fprintf(stderr, "Improperly formatted OBJ file (4)\n"); + badfmt++; return NULL; } @@ -133,12 +233,14 @@ char *readrec( if (*len < 0) { fprintf(stderr, "Improperly formatted OBJ file (5)\n"); + badfmt++; return NULL; } buf = malloc(*len); if (buf == NULL) { fprintf(stderr, "Out of memory allocating %d bytes\n", *len); + badfmt++; return NULL; /* Bad alloc */ } @@ -146,6 +248,7 @@ char *readrec( if (i < *len) { free(buf); fprintf(stderr, "Improperly formatted OBJ file (6)\n"); + badfmt++; return NULL; } @@ -159,7 +262,8 @@ char *readrec( if (c != chksum) { free(buf); - fprintf(stderr, "Bad record checksum, " "calculated=$%04x, recorded=$%04x\n", chksum, c); + fprintf(stderr, "Bad record checksum, calculated=$%04x, recorded=$%04x\n", chksum, c); + badfmt++; return NULL; } } else { @@ -169,9 +273,9 @@ char *readrec( if (c == EOF) { free(buf); fprintf(stderr, "EOF where padding byte should be\n"); + badfmt++; return NULL; } - } } @@ -185,6 +289,8 @@ void dump_bytes( int i, j; + if (!loud) return; /* Nothing to do for -q[uiet] */ + for (i = 0; i < len; i += 8) { printf("\t%3.3o: ", i); for (j = i; j < len && j < i + 8; j++) @@ -212,6 +318,8 @@ void dump_words( int i, j; + if (!loud) return; /* Nothing to do for -q[uiet] */ + for (i = 0; i < len; i += 8) { printf("\t%6.6o: ", addr); @@ -246,9 +354,27 @@ void dump_bin( int chksum; /* Checksum is negative sum of all bytes including header and length */ int FBR_LEAD1 = 1, - FBR_LEAD2 = 0; + FBR_LEAD2 = 0; int i; - unsigned hdrlen = len + 6; + unsigned hdrlen; + + if (addr + len - 1 > highest_addr) + highest_addr = addr + len - 1; + + if (addr > MAX_LDA_ADDR) { + /* We can't write anything at all */ + /* badbin++; // We do this ONCE later */ + return; + } + + if (addr + len - 1 > MAX_LDA_ADDR) { + len = MAX_LDA_ADDR - addr + 1; /* Write as much as we can */ + /* badbin++; // We do this ONCE later */ + } + + if (!bin) return; /* Nothing more to do if no */ + + hdrlen = len + 6; for (i = 0; i < 8; i++) fputc(0, bin); @@ -336,6 +462,7 @@ void got_gsd( char name[8]; unsigned value; unsigned flags; + char *equals = (alignment == '\t') ? " = " : "="; gsdline = memcheck(malloc(256)); @@ -346,22 +473,25 @@ void got_gsd( value = WORD(cp + i + 6); flags = cp[i + 4] & 0xff; + if ((cp[i + 5] & 0xff) < GSDSIZ) gsdcnt[cp[i + 5] & 0xff]++; switch (cp[i + 5] & 0xff) { case 0: - sprintf(gsdline, "\tMODNAME %s=%o flags=%o\n", name, value, flags); + sprintf(gsdline, "\tMODNAME%c%s%s%o%cflags=%o\n", alignment, name, equals, value, alignment, flags); break; case 1: - sprintf(gsdline, "\tCSECT %s=%o flags=%o\n", name, value, flags); + sprintf(gsdline, "\tCSECT%c%s%s%o%cflags=%o\n", alignment, name, equals, value, alignment, flags); + badbin++; break; case 2: - sprintf(gsdline, "\tISD %s=%o flags=%o\n", name, value, flags); + sprintf(gsdline, "\tISD%c%s%s%o%cflags=%o\n", alignment, name, equals, value, alignment, flags); break; case 3: - sprintf(gsdline, "\tXFER %s=%o flags=%o\n", name, value, flags); - xferad = value; + sprintf(gsdline, "\tXFER%c%s%s%o%cflags=%o\n", alignment, name, equals, value, alignment, flags); + xferad = origin + value; break; case 4: - sprintf(gsdline, "\tGLOBAL %s=%o %s%s%s %s flags=%o\n", name, value, + sprintf(gsdline, "\tGLOBAL%c%s%s" + "%o%c%s%s%s %s flags=%o\n", alignment, name, equals, value, alignment, flags & 01 ? "WEAK " : "", flags & 04 ? "LIB " : "", flags & 010 ? "DEF" : "REF", @@ -369,7 +499,10 @@ void got_gsd( flags); break; case 5: - sprintf(gsdline, "\tPSECT %s=%o %s%s %s %s %s %s %s flags=%o\n", name, value, + if (value) psects_with_data++; + sprintf(gsdline, "\tPSECT%c%s%s" + "%o%c%s%s%s%s %s %s %s %s flags=%o\n", alignment, name, equals, value, alignment, + (alignment == '\t') ? "" : " ", /* For backwards compatibility with original dumpobj */ flags & 01 ? "SAV " : "", flags & 02 ? "LIB " : "", flags & 04 ? "OVR" : "CON", @@ -383,16 +516,21 @@ void got_gsd( psectid %= NPSECTS; break; case 6: - sprintf(gsdline, "\tIDENT %s=%o flags=%o\n", name, value, flags); + sprintf(gsdline, "\tIDENT%c%s%s%o%cflags=%o\n", alignment, name, equals, value, alignment, flags); break; case 7: - sprintf(gsdline, "\tVSECT %s=%o flags=%o\n", name, value, flags); + sprintf(gsdline, "\tVSECT%c%s%s%o%cflags=%o\n", alignment, name, equals, value, alignment, flags); + badbin++; break; case 010: - sprintf(gsdline, "\tCompletion Routine Name %s=%o flags=%o\n", name, value, flags); + sprintf(gsdline, "\t" "COMPRTN" /* "Completion Routine Name" */ + "%c%s%s%o%cflags=%o\n", alignment, name, equals, value, alignment, flags); + badbin++; break; default: sprintf(gsdline, "\t***Unknown GSD entry type %d flags=%o\n", cp[i + 5] & 0xff, flags); + if (!loud) fprintf(stderr, "%s", gsdline+1); + badfmt++; break; } @@ -424,16 +562,17 @@ void got_endgsd( return; } - qsort(all_gsds, nr_gsds, sizeof(char *), compare_gsdlines); + if (loud && sortgsd) + qsort(all_gsds, nr_gsds, sizeof(char *), compare_gsdlines); - printf("GSD:\n"); + if (loud) printf("GSD:\n"); for (i = 0; i < nr_gsds; i++) { - fputs(all_gsds[i], stdout); + if (loud) fputs(all_gsds[i], stdout); free(all_gsds[i]); } - printf("ENDGSD\n"); + if (loud) printf("ENDGSD\n"); free(all_gsds); all_gsds = NULL; @@ -451,12 +590,14 @@ void got_text( last_text_addr = addr; - printf("TEXT ADDR=%o LEN=%o\n", last_text_addr, len - 4); - - dump_words(last_text_addr, cp + 4, len - 4); + if (loud) { + printf("TEXT ADDR=%o LEN=%o\n", last_text_addr, len - 4); + if (loudtxt) + dump_words(last_text_addr, cp + 4, len - 4); + } - if (bin) - dump_bin(last_text_addr, cp + 4, len - 4); + if (bin || word_patch) + dump_bin(origin + last_text_addr, cp + 4, len - 4); } void rad50name( @@ -475,7 +616,7 @@ void got_rld( { int i; - printf("RLD\n"); + if (loud) printf("RLD\n"); for (i = 2; i < len;) { unsigned addr; @@ -490,92 +631,106 @@ void got_rld( if (cp[i] & 0200) byte = " byte"; + if ((cp[i] & 0x7f) < RLDSIZ) rldcnt[cp[i] & 0x7f]++; switch (cp[i] & 0x7f) { case 01: - printf("\tInternal%s %o=%o\n", byte, addr, WORD(cp + i + 2)); + word = WORD(cp + i + 2); + if (loud) printf("\tInternal%s %o=%o\n", byte, addr, word); + if (bin || word_patch) { + char bytes[2]; + + if (reloc_internal) + word += origin; + + bytes[0] = word & 0xff; + bytes[1] = (word >> 8) & 0xff; + dump_bin(origin + addr, bytes, 2); + } i += 4; break; case 02: rad50name(cp + i + 2, name); - printf("\tGlobal%s %o=%s\n", byte, addr, name); + if (loud) printf("\tGlobal%s %o=%s\n", byte, addr, name); i += 6; + badbin++; break; case 03: - printf("\tInternal displaced%s %o=%o\n", byte, addr, WORD(cp + i + 2)); + if (loud) printf("\tInternal displaced%s %o=%o\n", byte, addr, WORD(cp + i + 2)); i += 4; - badbin = 1; + badbin++; break; case 04: rad50name(cp + i + 2, name); - printf("\tGlobal displaced%s %o=%s\n", byte, addr, name); + if (loud) printf("\tGlobal displaced%s %o=%s\n", byte, addr, name); i += 6; - badbin = 1; + badbin++; break; case 05: rad50name(cp + i + 2, name); word = WORD(cp + i + 6); - printf("\tGlobal plus offset%s %o=%s+%o\n", byte, addr, name, word); + if (loud) printf("\tGlobal plus offset%s %o=%s+%o\n", byte, addr, name, word); i += 8; - badbin = 1; + badbin++; break; case 06: rad50name(cp + i + 2, name); word = WORD(cp + i + 6); - printf("\tGlobal plus offset displaced%s %o=%s+%o\n", byte, addr, name, word); + if (loud) printf("\tGlobal plus offset displaced%s %o=%s+%o\n", byte, addr, name, word); i += 8; - badbin = 1; + badbin++; break; case 07: rad50name(cp + i + 2, name); word = WORD(cp + i + 6); - printf("\tLocation counter definition %s+%o\n", name, word); + if (loud) printf("\tLocation counter definition %s+%o\n", name, word); i += 8; last_text_addr = word; break; case 010: word = WORD(cp + i + 2); - printf("\tLocation counter modification %o\n", word); + if (loud) printf("\tLocation counter modification %o\n", word); i += 4; last_text_addr = word; break; case 011: - printf("\t.LIMIT %o\n", addr); + if (loud) printf("\t.LIMIT %o\n", addr); i += 2; + badbin++; /* .LIMIT will not be filled in by us */ break; case 012: rad50name(cp + i + 2, name); - printf("\tPSECT%s %o=%s\n", byte, addr, name); + if (loud) printf("\tPSECT%s %o=%s\n", byte, addr, name); i += 6; - badbin = 1; + badbin++; break; case 014: rad50name(cp + i + 2, name); - printf("\tPSECT displaced%s %o=%s\n", byte, addr, name); + if (loud) printf("\tPSECT displaced%s %o=%s\n", byte, addr, name); i += 6; - badbin = 1; + badbin++; break; case 015: rad50name(cp + i + 2, name); word = WORD(cp + i + 6); - printf("\tPSECT plus offset%s %o=%s+%o\n", byte, addr, name, word); + if (loud) printf("\tPSECT plus offset%s %o=%s+%o\n", byte, addr, name, word); i += 8; - badbin = 1; + badbin++; break; case 016: rad50name(cp + i + 2, name); word = WORD(cp + i + 6); - printf("\tPSECT plus offset displaced%s %o=%s+%o\n", byte, addr, name, word); + if (loud) printf("\tPSECT plus offset displaced%s %o=%s+%o\n", byte, addr, name, word); i += 8; - badbin = 1; + badbin++; break; case 017: - badbin = 1; - printf("\tComplex%s %o=", byte, addr); + badbin++; + if (loud) printf("\tComplex%s %o=", byte, addr); i += 2; { char *xp = cp + i; int size; @@ -584,57 +739,59 @@ void got_rld( size = 1; switch (*xp) { case 000: - fputs("nop ", stdout); + if (loud) fputs("nop ", stdout); break; case 001: - fputs("+ ", stdout); + if (loud) fputs("+ ", stdout); break; case 002: - fputs("- ", stdout); + if (loud) fputs("- ", stdout); break; case 003: - fputs("* ", stdout); + if (loud) fputs("* ", stdout); break; case 004: - fputs("/ ", stdout); + if (loud) fputs("/ ", stdout); break; case 005: - fputs("& ", stdout); + if (loud) fputs("& ", stdout); break; case 006: - fputs("! ", stdout); + if (loud) fputs("! ", stdout); break; case 010: - fputs("neg ", stdout); + if (loud) fputs("neg ", stdout); break; case 011: - fputs("^C ", stdout); + if (loud) fputs("^C ", stdout); break; case 012: - fputs("store ", stdout); + if (loud) fputs("store ", stdout); break; case 013: - fputs("store{disp} ", stdout); + if (loud) fputs("store{disp} ", stdout); break; case 016: rad50name(xp + 1, name); - printf("%s ", name); + if (loud) printf("%s ", name); size = 5; break; case 017: assert((xp[1] & 0377) < psectid); - printf("%s:%o ", psects[xp[1] & 0377], WORD(xp + 2)); + if (loud) printf("%s:%o ", psects[xp[1] & 0377], WORD(xp + 2)); size = 4; break; case 020: - printf("%o ", WORD(xp + 1)); + if (loud) printf("%o ", WORD(xp + 1)); size = 3; break; + default: - printf("**UNKNOWN COMPLEX CODE** %o\n", *xp & 0377); + fprintf(stderr, "***UNKNOWN COMPLEX CODE** %o\n", *xp & 0377); + badfmt++; return; } i += size; @@ -642,12 +799,13 @@ void got_rld( break; xp += size; } - fputc('\n', stdout); + if (loud) fputc('\n', stdout); break; } default: - printf("\t***Unknown RLD code %o\n", cp[i] & 0xff); + fprintf(stderr, "%s***Unknown RLD code %o\n", (loud) ? "\t" : "", cp[i] & 0xff); + badfmt++; return; } } @@ -658,7 +816,7 @@ void got_isd( int len) { (void)cp; - printf("ISD len=%o\n", len); + if (loud) printf("ISD len=%o\n", len); } void got_endmod( @@ -667,7 +825,7 @@ void got_endmod( { (void)cp; (void)len; - printf("ENDMOD\n"); + if (loud) printf("ENDMOD\n"); } void got_libhdr( @@ -676,7 +834,7 @@ void got_libhdr( { (void)cp; (void)len; - printf("LIBHDR\n"); + if (loud) printf("LIBHDR\n"); } void got_libend( @@ -685,7 +843,7 @@ void got_libend( { (void)cp; (void)len; - printf("LIBEND\n"); + if (loud) printf("LIBEND\n"); } int main( @@ -695,47 +853,187 @@ int main( int len; FILE *fp; int arg; + int summary = 0; /* Set to 1 by '-s' option */ int rt11 = DEFAULT_OBJECTFORMAT_RT11; - char *infile = 0; - char *outfile = 0; + char *infile = NULL; + char *outfile = NULL; + char *cp; for (arg = 1; arg < argc; arg++) { if (*argv[arg] == '-') { - char *cp; - cp = argv[arg] + 1; - if (!strcasecmp(cp, "rt11")) { + if (!strcasecmp(cp, "h")) { + infile = NULL; + break; + } else if (!strcasecmp(cp, "q")) { + loud = 0; + } else if (!strcasecmp(cp, "qt")) { + loudtxt = 0; + } else if (!strcasecmp(cp, "align")) { + alignment = '\t'; + } else if (!strcasecmp(cp, "noalign")) { + alignment = ' '; + } else if (!strcasecmp(cp, "nosort")) { + sortgsd = 0; + } else if (!strcasecmp(cp, "s")) { + summary = 1; + } else if (!strcasecmp(cp, "rt11")) { rt11 = 1; } else if (!strcasecmp(cp, "rsx")) { rt11 = 0; + } else if (!strcasecmp(cp, "o") || !strcasecmp(cp, "of")) { + char *endptr; + + if (++arg >= argc) { + fprintf(stderr, "Missing to '-%s'\n", cp); + return /* exit */ (EXIT_FAILURE); + } + + if (cp[1] == 'f') { + reloc_internal = 0; /* Don't relocate Internal RLD addresses */ + } else { + reloc_internal = 1; /* Relocate Internal RLD addresses */ + } + + origin = strtoul(argv[arg], &endptr, 8); + /* strtoul() accepts "-" but, unless "-0", this creates a very big unsigned number */ + if (*endptr != '\0' || origin > MAX_LDA_ADDR || (origin & 1)) { + fprintf(stderr, "Invalid '%s'\n", argv[arg]); + return /* exit */ (EXIT_FAILURE); + } + } else if (!strcasecmp(cp, "w")) { + if (++arg >= argc) { + fprintf(stderr, "Missing parameter to '-w'\n"); + return /* exit */ (EXIT_FAILURE); + } + if (word_patch) /* Note: we replace invalid '-w' entries without parsing them */ + fprintf(stderr, "'-w %s' replaced by '-w %s'\n", word_patch, argv[arg]); + word_patch = argv[arg]; + } else if (!strcasecmp(cp, "x")) { + char *endptr; + + if (++arg >= argc) { + fprintf(stderr, "Missing address to '-x'\n"); + return /* exit */ (EXIT_FAILURE); + } + + new_xferad = strtoul(argv[arg], &endptr, 8); + if (*endptr != '\0' || new_xferad > MAX_LDA_ADDR) { + fprintf(stderr, "Invalid address '%s'\n", argv[arg]); + return /* exit */ (EXIT_FAILURE); + } } else { - fprintf(stderr, "Unknown option %s\n", argv[arg]); - exit(EXIT_FAILURE); + fprintf(stderr, "Unknown option '%s'\n", argv[arg]); + return /* exit */ (EXIT_FAILURE); } } - else if (infile == 0) { + else if (infile == NULL) { infile = argv[arg]; } - else if (outfile == 0) { + else if (outfile == NULL) { outfile = argv[arg]; } else { - fprintf(stderr, "Extra parameter %s\n", argv[arg]); - exit(EXIT_FAILURE); + fprintf(stderr, "Extra parameter '%s'\n", argv[arg]); + return /* exit */ (EXIT_FAILURE); } } - if (infile == 0) { - fprintf(stderr, "Usage: dumpobj [ -rt11 ] [ -rsx ] input.obj [ output.obj ]\n"); - exit(1); + /**** Display the usage ****/ + + if (infile == NULL) { + fprintf(stderr, "\n" + "dumpobj - portable OBJECT file dumper for DEC PDP-11\n"); + fprintf(stderr, " Version from " VERSION " compiled on " __DATE__ " at " __TIME__ "\n"); +#ifdef GIT_VERSION + fprintf(stderr, " (git version: " GIT_VERSION ",\n" + " git author date: " GIT_AUTHOR_DATE ")\n"); +#endif + fprintf(stderr, " Copyright 2001 by Richard Krehbiel,\n"); + fprintf(stderr, " modified 2013,2015,2020,2021 by Olaf 'Rhialto' Seibert,\n"); + fprintf(stderr, " modified 2023 by Mike Hill.\n"); + fprintf(stderr, "\n" + "Usage:\n"); + fprintf(stderr, " dumpobj [-h] [-q|-qt] [-[no]align] [-nosort] [-s] [-rt11|-rsx] \n"); + fprintf(stderr, " [ [-o[f] ] [" /*-b|*/ "-w [=]] [-x ] ]\n"); + fprintf(stderr, "\n" + "Arguments:\n"); + fprintf(stderr, " -h show this help text%s\n", (argc <= 1) ? " with added examples" : ""); + fprintf(stderr, " -q quiet output - only errors will be shown\n"); + fprintf(stderr, " -qt quiet-text - omit the contents of TXT records\n"); +#if (DEFAULT_ALIGNMENT != '\t') + fprintf(stderr, " -align align fields when dumping GSD entries\n"); +#else + fprintf(stderr, " -noalign do not align fields when dumping GSD entries\n"); +#endif + fprintf(stderr, " -nosort do not sort the GSD entries (dump them in input order)\n"); + fprintf(stderr, " -s show a summary of all record types seen in the object file\n"); + fprintf(stderr, " -rt11 expects object file to be in RT-11 format\n"); + fprintf(stderr, " -rsx expects object file to be in RSX-11M/PLUS format\n"); +#if DEFAULT_OBJECTFORMAT_RT11 < 0 + fprintf(stderr, " required .OBJ input file (format can be auto-detected)\n"); +#elif DEFAULT_OBJECTFORMAT_RT11 > 0 + fprintf(stderr, " required .OBJ input file (default is RT-11)\n"); +#else + fprintf(stderr, " required .OBJ input file (default is RSX-11M/PLUS)\n"); +#endif + fprintf(stderr, "\n" + " -o sets the octal origin for relocatable LDA code (default is 0)\n"); + fprintf(stderr, " -of as -o but keep 'internal relocations' fixed not origin based\n"); +/* fprintf(stderr, " -b =,.. write bytes to the - [=][,...]]\n"); */ + fprintf(stderr, " -w =,.. write words to the - [=][,...]]\n"); + fprintf(stderr, " -x writes the octal transfer address to the \n"); + fprintf(stderr, " optional .LDA output file (in PDP-11 punched-tape format)\n"); + + /* Only display the examples if at least one argument was on the command line */ + + if (argc <= 1) + return /* exit */ (EXIT_FAILURE); + + fprintf(stderr, "\n" + "Examples:\n"); + fprintf(stderr, " dumpobj OBJECT.OBJ Dump contents of OBJECT.OBJ\n"); + fprintf(stderr, " dumpobj -qt -align -nosort -s OBJECT.OBJ Now dump in object file order\n"); + fprintf(stderr, " dumpobj -q -s OBJECT.OBJ Only show a summary of records\n"); + fprintf(stderr, " dumpobj -q -s OBJECT.OBJ -w 0 Show errors ready for LDA o/p\n"); + fprintf(stderr, " dumpobj -q OBJECT.OBJ PTAPE.LDA Only create an LDA file\n"); + fprintf(stderr, " dumpobj -q OBJECT.OBJ -o 1000 PTAPE.LDA Relocate code to address 1000\n"); + fprintf(stderr, " dumpobj EMPTY.OBJ -w 0,40=0,5237,0,774 -x 40 0:0,40:HALT,42:INC @#0,46:BR 40\n"); + fprintf(stderr, " dumpobj EMPTY.OBJ -w ... -x ... PTAPE.LDA Create an LDA file with data\n"); + + return /* exit */ (EXIT_FAILURE); } + /**** Start dumping the object file ****/ + fp = fopen(infile, "rb"); if (fp == NULL) { fprintf(stderr, "Unable to open %s\n", infile); return EXIT_FAILURE; } - if (outfile != 0) { + + /* Auto-detect the format of the object file (if enabled) + * If the first non-zero byte in > 1 assume RSX-11M/PLUS */ + +#if DEFAULT_OBJECTFORMAT_RT11 < 0 + if (rt11 < 0) { + int c; + + rt11 = 1; /* Assume RT-11 (including empty or zero-filled file) */ + + while (c = fgetc(fp), /* c != EOF && */ c == 0) /* continue */; + if (c > 1) rt11 = 0; /* Force to RSX-11M/PLUS */ + + if (/* rewind */ fseek(fp, 0L, SEEK_SET)) { + fprintf(stderr, "Unable to rewind %s\n", infile); + return EXIT_FAILURE; + } +#endif + + /* if (loud) printf("Detected %s formatted object file\n", (rt11) ? "RT-11" : "RSX-11M/PLUS"); */ + } + + if (outfile != NULL) { bin = fopen(outfile, "wb"); if (bin == NULL) { fprintf(stderr, "Unable to open %s\n", outfile); @@ -743,9 +1041,8 @@ int main( } } - char *cp; - while ((cp = readrec(fp, &len, rt11)) != NULL) { + if ((cp[0] & 0xff) < BLKSIZ) blkcnt[cp[0] & 0xff]++; switch (cp[0] & 0xff) { case 1: got_gsd(cp, len); @@ -767,25 +1064,219 @@ int main( break; case 7: got_libhdr(cp, len); + badbin++; break; - case 8: + case 010: got_libend(cp, len); + badbin++; break; default: - printf("Unknown record type %o\n", cp[0] & 0xff); + fprintf(stderr, "***Unknown record type %o\n", cp[0] & 0xff); + badfmt++; break; } free(cp); } + fclose(fp); /* We don't need the any more */ + + /**** Display the summary ****/ + + if (summary) { + unsigned i; + int stars = 0; + int showbin = (bin || word_patch); /* Show LDA errors */ + + for (i=0; i 0) { + printf("\nPSECTs:\n"); + for (i=0; i '-o%s %o' is ignored\n", (reloc_internal) ? "" : "f", origin); + + if (new_xferad <= MAX_LDA_ADDR) + fprintf(stderr, "With no '-x %o' is ignored\n", new_xferad); + + if (word_patch) + fprintf(stderr, "With no '-w %s' is ignored\n", word_patch); + } + + /**** Process the '-w' parameter ****/ + + if (word_patch) { + char *endptr; + unsigned address = 0; + unsigned data; + int wantdata = 0; + + cp = word_patch; + do { + while (*cp == ' ') cp++; /* Skip leading spaces if -w "addr=data" is used */ + + data = strtoul(cp, &endptr, 8); /* This accepts leading "+" or "-" but we catch "-n" below */ + if (endptr == cp) { + if (*endptr) + fprintf(stderr, "Unexpected character in -w at '%s'\n", endptr); + else + fprintf(stderr, "Missing -w at end of '%s'\n", word_patch); + badbin++; + break; + } + if (*endptr == '=') { + if (wantdata) { + fprintf(stderr, "Missing -w or unexpected '=' at '%s'\n", --cp); + badbin++; + break; + } + wantdata++; + address = data; + cp = endptr + 1; + if (*cp != '\0') endptr++; + continue; + } + if (address >= MAX_LDA_ADDR || address & 1) { + fprintf(stderr, "Invalid -w '%o' at '%s'\n", address, cp); + badbin++; + break; + } + if (data > 0xffff) { + fprintf(stderr, "Invalid -w '%o' at '%s'\n", data, cp); + badbin++; + break; + } + if (*endptr != ',' && *endptr != '\0') { + fprintf(stderr, "Unexpected character in -w at '%s'\n", endptr); + badbin++; + break; + } + /* if (showbin) */ { + char bytes[2]; + + bytes[0] = data & 0xff; + bytes[1] = (data >> 8) & 0xff; + dump_bin(address, bytes, 2); + } + + if (!bin) + printf("-w would have patched %6.6o to %6.6o\n", address, data); + + wantdata = 0; + address += 2; + cp = endptr + 1; + } while (*endptr != '\0'); + } + + /**** Process and report actual and potential errors ****/ + + if (!bin) + outfile = ""; + + if (bin || word_patch) { + if (psects_with_data > 1) { + fprintf(stderr, "More than one (%d) PSECT contains data\n", psects_with_data); + badbin++; + } + + if (new_xferad <= MAX_LDA_ADDR) { + xferad = new_xferad; + } else { +#if XFERAD_WHEN_ZERO + if (xferad == 0) { + fprintf(stderr, "Transfer address 0 has been modified to %d\n", XFERAD_WHEN_ZERO); + xferad = XFERAD_WHEN_ZERO; + } +#endif + } + + if (highest_addr == 0) /* If -w is specified, there will always be data */ + fprintf(stderr, "No data bytes written to binary file '%s'\n", outfile); + + if (origin < 0160000 && highest_addr >= 0160000) + fprintf(stderr, "Binary file '%s' overlaps I/O space (160000-%o)\n", + outfile, (highest_addr > MAX_LDA_ADDR) ? MAX_LDA_ADDR : highest_addr); + + if (highest_addr > MAX_LDA_ADDR) { + fprintf(stderr, "Binary file '%s' skipped data from %o-%o\n", + outfile, MAX_LDA_ADDR + 1, highest_addr); + badbin++; + } + } + + if (badfmt) { + fprintf(stderr, "Detected %d errors in the object file '%s'\n", badfmt, infile); + xferad = BADBIN_XFERAD; + } + + if (badbin && (bin || word_patch)) { + fprintf(stderr, "Possibly %d mistake%s in the binary file '%s'\n", + badbin, (badbin == 1) ? "" : "s", outfile); + if (!summary) + fprintf(stderr, "Try 'dumpobj -q -s ...' to see a summary of records with mistakes\n"); + xferad = BADBIN_XFERAD; + } + + /* Write the transfer address and close the LDA file */ + if (bin) { dump_bin(xferad, NULL, 0); fclose(bin); + if (badbin) - fprintf(stderr, "Probable errors in binary file\n"); + return EXIT_FAILURE; } - fclose(fp); + if (badfmt) + return EXIT_FAILURE; + return EXIT_SUCCESS; } diff --git a/crossassemblers/macro11/extree.c b/crossassemblers/macro11/extree.c index 2ee90fe..586628a 100644 --- a/crossassemblers/macro11/extree.c +++ b/crossassemblers/macro11/extree.c @@ -325,11 +325,12 @@ EX_TREE *evaluate_rec( /* Change some symbols to "undefined" */ +#if !DISABLE_EXTREE_IF_DF_NDF if (flags & EVALUATE_DEFINEDNESS) { int is_undefined = 0; /* I'd prefer this behavior, but MACRO.SAV is a bit too primitive. */ -#if 0 +#if NODO /* A temporary symbol defined later is "undefined." */ if (!(sym->flags & PERMANENT) && sym->stmtno > stmtno) is_undefined = 1; @@ -351,11 +352,20 @@ EX_TREE *evaluate_rec( break; } } +#endif /* Turn defined absolute symbol to a literal */ if (!(sym->section->flags & PSECT_REL) && !SYM_IS_IMPORTED(sym)) { + +#if DISABLE_EXTREE_IF_DF_NDF res = new_ex_lit(sym->value); +#else + if (flags & EVALUATE_DEFINEDNESS && !sym->value) + res = new_ex_lit(1); + else + res = new_ex_lit(sym->value); +#endif if (sym->section->type == SECTION_REGISTER) { *outflags |= EVALUATE_OUT_IS_REGISTER; @@ -624,6 +634,14 @@ EX_TREE *evaluate_rec( break; } + if (right->type == EX_LIT && /* Symbol times -1 == -symbol */ + right->data.lit == 0xffff) { + res = evaluate(new_ex_una(EX_NEG, left), 0); + free_tree(left); + free_tree(right); + break; + } + /* Associative: *y == A* */ /* If x*y is constant, I can do this math. */ /* Is this safe? I will potentially be doing it */ @@ -652,20 +670,58 @@ EX_TREE *evaluate_rec( EX_TREE *left, *right; - left = evaluate_rec(tp->data.child.left, flags, outflags); + left = evaluate_rec(tp->data.child.left, flags, outflags); right = evaluate_rec(tp->data.child.right, flags, outflags); + /* Optimize if right == -1 || 0 || +1 */ + if (right->type == EX_LIT) { + if (right->data.lit == 0) { + /* Can't divide by zero - V05.05 returns 0 without error */ + + /* Note that the RSX-11M/PLUS task builder does not allow + * a divide-by-zero with the following error message ... + * TKB -- *DIAG*-Complex relocation error-divide by zero module x */ + + /* Handle divide by zero (= 0) */ + if (!RELAXED) + res = ex_err(new_ex_lit(0), right->cp); + else + res = new_ex_lit(0); + free_tree(left); + free_tree(right); + break; + } + + if (right->data.lit == 1) { + /* Handle divide by one (= left) */ + res = left; + free_tree(right); + break; + } + + if (right->type == EX_LIT && + right->data.lit == 0xffff) { + /* Handle divide by minus-one (= -left) */ + res = evaluate(new_ex_una(EX_NEG, left), 0); + free_tree(left); + free_tree(right); + break; + } + } + /* Can only divide if both are literals */ if (left->type == EX_LIT && right->type == EX_LIT) { - res = new_ex_lit(left->data.lit / right->data.lit); - free_tree(left); - free_tree(right); - break; - } + int dividend = (int) left->data.lit & 0xffff; + int divisor = (int) right->data.lit & 0xffff; - if (right->type == EX_LIT && /* Symbol divided by 1 == symbol */ - right->data.lit == 1) { - res = left; + if (dividend & 0x8000) + dividend = dividend - 0x10000; + + if (divisor & 0x8000) + divisor = divisor - 0x10000; + + res = new_ex_lit(dividend / divisor); + free_tree(left); free_tree(right); break; } @@ -770,43 +826,57 @@ EX_TREE *evaluate_rec( EX_TREE *left, *right; - left = evaluate_rec(tp->data.child.left, flags, outflags); + left = evaluate_rec(tp->data.child.left, flags, outflags); right = evaluate_rec(tp->data.child.right, flags, outflags); - /* Operate if both are literals */ - if (left->type == EX_LIT && right->type == EX_LIT) { - int shift = right->data.lit; - if (shift < 0) - res = new_ex_lit(left->data.lit >> -shift); - else - res = new_ex_lit(left->data.lit << shift); - free_tree(left); - free_tree(right); - break; - } - - if (right->type == EX_LIT && /* Symbol shifted 0 == symbol */ - right->data.lit == 0) { + /* Zero shifted by anything == 0 */ + if (left->type == EX_LIT && + left->data.lit == 0) { res = left; free_tree(right); break; } - if (right->type == EX_LIT && /* Anything shifted 16 == 0 */ - ((int)right->data.lit > 15 || - (int)right->data.lit < -15)) { - res = new_ex_lit(0); - free_tree(left); - free_tree(right); - break; - } + if (right->type == EX_LIT) { + int shift = right->data.lit; - if (right->type == EX_LIT) { /* Other shifts become * or / */ - int shift = right->data.lit; + /* Symbol shifted 0 == symbol */ + if (shift == 0) { + res = left; + free_tree(right); + break; + } + + if (shift & 0x8000) + shift = shift - 0x10000; + + /* Anything shifted 16 == 0 */ + if (shift > 15 || shift < -15) { + res = new_ex_lit(0); + free_tree(left); + free_tree(right); + break; + } + + /* Operate if both are literals */ + if (left->type == EX_LIT) { + if (shift < 0) + res = new_ex_lit(left->data.lit >> -shift); + else + res = new_ex_lit(left->data.lit << shift); + free_tree(left); + free_tree(right); + break; + } + + /* Other shifts become '*' or '/' */ if (shift > 0) res = new_ex_bin(EX_MUL, left, new_ex_lit(1 << shift)); - else - res = new_ex_bin(EX_DIV, left, new_ex_lit(1 << -shift)); + else { /* (shift < 0) */ + res = new_ex_bin(EX_AND, left, new_ex_lit(~(0x7fff >> (-shift-1)))); + res = new_ex_bin(EX_DIV, res, new_ex_lit(1 << -shift)); + res = new_ex_bin(EX_AND, res, new_ex_lit(0x7fff >> (-shift-1))); + } free_tree(right); break; } @@ -854,18 +924,25 @@ EX_TREE *evaluate( res = evaluate_rec(tp, flags, &outflags); if (outflags & EVALUATE_OUT_IS_REGISTER) { - int regno = get_register(res); + int regno = get_register(res); if (regno == NO_REG) { - report(NULL, "Register expression out of range.\n"); + report_err(NULL, "Register expression out of range\n"); #if DEBUG_REGEXPR print_tree(stderr, tp, 0); print_tree(stderr, res, 0); #endif /* DEBUG_REGEXPR */ /* TODO: maybe make this a EX_TEMP_SYM? */ - res = ex_err(res, res->cp); + { + EX_TREE *newresult = new_ex_tree(EX_SYM); + + newresult->cp = res->cp; + newresult->data.symbol = REG_ERR; + free_tree(res); + res = newresult; + } } else { - EX_TREE *newresult = new_ex_tree(EX_SYM); + EX_TREE *newresult = new_ex_tree(EX_SYM); newresult->cp = res->cp; newresult->data.symbol = reg_sym[regno]; @@ -915,7 +992,7 @@ EX_TREE *new_ex_lit( EX_TREE *tp; tp = new_ex_tree(EX_LIT); - tp->data.lit = value; + tp->data.lit = value & 0xffff; /* Force to 16-bits */ return tp; } diff --git a/crossassemblers/macro11/extree.h b/crossassemblers/macro11/extree.h index 6dce952..88630cf 100644 --- a/crossassemblers/macro11/extree.h +++ b/crossassemblers/macro11/extree.h @@ -1,4 +1,3 @@ - #ifndef EXTREE__H #define EXTREE__H @@ -56,29 +55,41 @@ typedef struct ex_tree { EX_TREE *new_ex_tree( int type); + void free_tree( EX_TREE *tp); EX_TREE *new_ex_lit( unsigned value); + EX_TREE *ex_err( EX_TREE *tp, char *cp); + EX_TREE *new_ex_bin( int type, EX_TREE *left, EX_TREE *right); + EX_TREE *new_ex_una( int type, EX_TREE *left); + int num_subtrees( EX_TREE *tp); + EX_TREE *evaluate( EX_TREE *tp, int flags); -#define EVALUATE_DEFINEDNESS 1 - -#define EVALUATE_OUT_IS_REGISTER 1 +#define DISABLE_EXTREE_IF_DF_NDF 1 /* 0=extree may be used to evaluate definedness, 1=extree NEVER used */ + /* TODO: If we want to permanently disable this, we could remove the + * 'flags' from parse_expr() and evaluate() etc. */ +#if !DISABLE_EXTREE_IF_DF_NDF +#define EVALUATE_DEFINEDNESS 1 /* Flag used by parse_expr() to evaluate definedness */ #endif + +#define EVALUATE_OUT_IS_REGISTER 1 + +#endif /* EXTREE__H */ diff --git a/crossassemblers/macro11/listing.c b/crossassemblers/macro11/listing.c index cd34d16..ddff1d4 100644 --- a/crossassemblers/macro11/listing.c +++ b/crossassemblers/macro11/listing.c @@ -1,100 +1,561 @@ #define LISTING__C +/* Listing and error-message related. */ + +#include +#include +#include +#include #include #include -#include -#include +#include -#include "listing.h" /* my own definitions */ +#include "listing.h" /* My own definitions */ -#include "util.h" #include "assemble_globals.h" +#include "macro11.h" +#include "macros.h" +#include "object.h" +#include "util.h" /* GLOBAL VARIABLES */ -int list_md = 1; /* option to list macro/rept definition = yes */ +char title_string[32] = ""; /* .TITLE string (up to 31 characters) */ +int toc_shown = 0; /* Flags that at least one .SBTTL was listed in the TOC */ + +int page_break_ff = 0; /* TRUE if -ff (use form-feed for listing page-breaks */ +int auto_page_break = 0; /* TRUE if -apb (automatic-page-break) selected */ +int line_on_page; /* Current line number on the listing page */ + +static char *list_page_fmt = "\n\n"; /* Format to use for the page throw */ +static char *list_page_ff = "\f\n"; /* Format to use if page_break_ff is TRUE */ + +int list_page_top; /* Are we at the top of a page? */ + +int list_line_act; /* Action to perform when listing the current line */ +int listing_forced = 0; /* If set to LIST_FORCE_LISTING most lines will be listed */ + +static int list_within_exp; /* Flags the listing line is DIRECTLY from a macro/rept/irp/irpc expansion */ + +static char binline[sizeof(LSTFORMAT) + 16] = ""; /* For octal expansion */ -int list_me = 1; /* option to list macro/rept expansion = yes */ +static char *listline; /* Source lines */ -int list_bex = 1; /* option to show binary */ +FILE *lstfile = NULL; /* Listing file */ -int list_level = 1; /* Listing control level. .LIST - increments; .NLIST decrements */ +int list_pass_0 = 0; /* Also list what happens during the first pass */ -static char *listline; /* Source lines */ +int report_errcnt = 0; /* Count the number of times report() has been called */ -static char *binline; /* for octal expansion */ +int show_error_lines = 0; /* Show the line with the error when reporting errors */ +int show_print_lines = 0; /* Show .PRINT lines (similar to show_error_lines) */ -FILE *lstfile = NULL; +int exit_if_pass = PASS2+1; /* Exit if a fatal error occurs on this pass [or higher] */ +int exit_requested = 0; /* Set to TRUE if a 'fatal' exit is requested (-fe only) */ -int list_pass_0 = 0;/* Also list what happens during the first pass */ +static int errline = 0; /* Set if current line has an error */ -static int errline = 0; /* Set if current line has an error */ -/* maybe_list returns TRUE if listing may happen for this line. */ +/* can_list returns TRUE if listing may happen for this line. */ static int can_list( void) { int ok = lstfile != NULL && - (pass > 0 || list_pass_0); + (pass > PASS1 || list_pass_0); return ok; } -/* do_list returns TRUE if listing is enabled. */ + +/* build_list returns TRUE if the listing line might be used later. */ + +static int build_list( + void) +{ + int ok = can_list() || + (show_error_lines && pass > PASS1) /* || exit_if_pass >= pass */; + + return ok; +} + + +/* dolist returns TRUE if listing is enabled. */ static int dolist( void) { - int ok = can_list () && - (list_level > 0 || errline); + int ok = can_list() && + (list_level >= 0 || errline || (list_line_act & LIST_FORCE_LISTING)); return ok; } + /* list_source saves a text line for later listing by list_flush */ void list_source( STREAM *str, char *cp) { - if (can_list()) { + if (build_list()) { int len = strcspn(cp, "\n"); /* Not an error yet */ errline = 0; - /* Save the line text away for later... */ + + /* We might see a local symbol later on */ + looked_up_local_sym = FALSE; + + /* Unless -yfl, we don't want to force the line to be listed */ + list_line_act &= ~LIST_FORCE_LISTING; + list_line_act |= listing_forced; + + /* Flag whether this listing line is within a macro/rept/irp/irpc expansion */ + list_within_exp = within_macro_expansion(str); + + /* Construct the LHS of the listing line */ + +#if NODO + if (!binline) + binline = memcheck(malloc(sizeof(LSTFORMAT) + 16)); +#endif + + sprintf(binline, "%*s%*d ", (int)SIZEOF_MEMBER(LSTFORMAT, flag), "", + (int)SIZEOF_MEMBER(LSTFORMAT, line_number), str->line); + + /* Save the line-text away for later ... */ if (listline) free(listline); listline = memcheck(malloc(len + 1)); memcpy(listline, cp, len); - listline[len] = 0; + listline[len] = '\0'; + } +} - if (!binline) - binline = memcheck(malloc(sizeof(LSTFORMAT) + 16)); - sprintf(binline, "%*s%*d", (int)SIZEOF_MEMBER(LSTFORMAT, flag), "", (int)SIZEOF_MEMBER(LSTFORMAT, line_number), - str->line); +/* list_title_line shows the title heading line(s) */ +/* Heading will be printed prior to printing the first .SBTTL on pass 1 */ +/* Or if a .TITLE was seen heading will be printed prior to starting pass 2 */ +/* -e BMK will suppress version and date & time information for pass 1 */ +/* -e BMK will suppress the whole heading on pass 2 (i.e. if no .SBTTL seen) */ + +void list_title_line( + const char *line2) +{ + if (line2 == NULL) { /* Will not be NULL if called from P_SBTTL */ + if (!can_list()) + return; + + if (ENABL(BMK) || title_string[0] == '\0') + return; + } + + fprintf(lstfile, "%.31s " PROGRAM_NAME, (title_string[0] == '\0') ? ".MAIN." : title_string); + + if (!ENABL(BMK)) { + time_t current_time = time(NULL); + struct tm *local_time = localtime(¤t_time); + const char *day_name[7] = { + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" + }; + const char *month_name[12] = { + "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" + }; + + fprintf(lstfile, " " BASE_VERSION); + if (local_time != NULL && local_time->tm_year > 69) + fprintf(lstfile, " %s %02d-%3s-%4d %02d:%02d", + day_name[local_time->tm_wday], + local_time->tm_mday, + month_name[local_time->tm_mon], + local_time->tm_year + 1900, + local_time->tm_hour, + local_time->tm_min); + } else { +#if TODO /* (?) */ + fprintf(lstfile, " " "Version#"); + fprintf(lstfile, " Weekday dd-Mmm-yyyy hh:mm"); +#endif + } +/* fprintf(lstfile, "\n"); // This is done below */ + + if (line2 == NULL) + fprintf(lstfile, "\n\n"); + else + fprintf(lstfile, "\n%.*s\n\n", (STRINGENT) ? 80 /* DOC: J1.1.1 */ : 132, line2); +} + + +/* list_throw_page starts a new page on the listing */ +/* Note: Unlike MACRO-11 blank lines at the top of the page will be suppressed. + * For example, all EMPTY lines following a .INCLUDE will not be listed. + * Or, indeed, all of the first EMPTY lines in a file. */ + +void list_throw_page( + int force_throw) +{ + if (force_throw || (dolist() && !list_page_top)) { + if (page_break_ff) + fputs(list_page_ff, lstfile); + else + fputs(list_page_fmt, lstfile); + } + list_page_top = 1; + line_on_page = 0; +} + + +/* trunc_line truncates a string by replacing all ' ' and '\t' at the end with '\0' */ +/* If the string is longer than the maximum allowed, it will truncate from there instead */ + +static void trunc_line( + char *string) +{ + int len = strlen(string); + + if (len > MAX_LISTING_LINE_LEN) { + len = MAX_LISTING_LINE_LEN; + string[len] = '\0'; + } + + while (--len >= 0) + if (string[len] == ' ' || string[len] == '\t') + string[len] = '\0'; + else + break; + + return; +} + + +/* list_oct2hex converts all octal numbers in a string to hex */ +/* We can handle octal numbers with 1-6 digits. */ + +static void list_oct2hex( + char *cp) +{ + char *cpe; + int len; + int octval; + char oldche; + const char format[6][6] = { /* Digits Octal Hexadec */ + "%1.1X", /* 1 7 7 */ + "%2.2X", /* 2 77 3F */ + "%3.2X", /* 3 777 1FF */ /* But usually one byte (0x00-0xFF) */ + "%4.3X", /* 4 7777 FFF */ + "%5.4X", /* 5 77777 7FFF */ + "%6.4X" /* 6 177777 FFFF */ + }; + + for (;;) { + while (!isdigit((unsigned char) *cp)) + if (*cp++ == '\0') + return; + + octval = strtol(cp, &cpe, 8); + len = cpe - cp; + assert(len > 0 && len <= 6); + + oldche = *cpe; + sprintf(cp, format[len-1], octval); + *cpe = oldche; + + cp = cpe; + } +} + + +/* list_oct2dec converts all octal numbers in a string to decimal */ +/* We can handle octal numbers with 1-6 digits. */ + +static void list_oct2dec( + char *cp) +{ + char *cpe; + int len; + int octval; + char oldche; + const char format[6][4] = { /* Digits Octal Decimal */ + "%1d", /* 1 7 7 */ + "%2d", /* 2 77 63 */ + "%3d", /* 3 777 511 */ + "%4d", /* 4 7777 4095 */ + "%5d", /* 5 77777 32767 */ + "%6d" /* 6 177777 65535 */ + }; + + for (;;) { + while (!isdigit((unsigned char) *cp)) + if (*cp++ == '\0') + return; + + octval = strtol(cp, &cpe, 8); + len = cpe - cp; + assert(len > 0 && len <= 6); + + oldche = *cpe; + sprintf(cp, format[len-1], octval); + *cpe = oldche; + + cp = cpe; + } +} + + +/* list_process processes the listing line prior to outputting it */ +/* This handles .[N]LIST SEQ,LOC,BIN,BEX,SRC,COM,HEX (except LOC without either SEQ or BIN). */ +/* .NLIST COM is handled differently to MACRO-11 -- Lines starting with ';' or blank lines are suppressed */ + +static void list_process( + void) +{ + int binstart = 0; + int nlist_loc_only = 0; + +#define HAVE_SEQ (isdigit((unsigned char) binline[offsetof(LSTFORMAT, gap_after_seq) - 1])) +#define HAVE_LOC (isdigit((unsigned char) binline[offsetof(LSTFORMAT, pc) + 5])) +#define HAVE_WORD1 (isdigit((unsigned char) binline[offsetof(LSTFORMAT, words) + 0]) && \ + /* Ignore .IF lines */ (binline[offsetof(LSTFORMAT, words) + 6] != '=')) + + if (LIST(TTM)) + padto(binline, offsetof(LSTFORMAT, words[1])); + else + padto(binline, offsetof(LSTFORMAT, source)); + + trunc_line(listline); + + if (!errline) { /* Never pre-process error lines */ + + /* Handle .NLIST SEQ,LOC,BIN,SRC */ + + if (!(LIST(SEQ) || LIST(LOC) || LIST(BIN) || LIST(SRC))) + return; /* Completely suppress all [non-error] lines */ + + /* Handle NLIST BEX [BIN] and .NLIST COM */ + + if (!HAVE_SEQ) { + if (!LIST(BEX) || !LIST(BIN)) + return; /* If no sequence number, suppress the BEX line */ + } else if (!LIST(COM)) { +#if NODO /* Don't forget to #include "parse.h" */ + if (listline[0] == '\0' || *skipwhite(listline) == ';') + /* if (binline[0] == ' ') */ + return; /* Completely suppress comment-only lines and blank lines */ +#else + if (listline[0] == ';' || listline[0] == '\0') + /* if (binline[0] == ' ') */ + return; /* Completely suppress LHS-comment lines and blank lines */ +#endif + } + + /* Handle .NLIST ME,MEB */ + + if (list_within_exp && !LIST(ME) && LIST(LIS) < 1) { /* V05.05 treats list_level >= 1 as .LIST ME */ + if (!LIST(MEB) && !(list_line_act & LIST_FORCE_LISTING)) /* (Optional) -yfl forces .LIST MEB */ + return; /* Suppress all source lines within a macro/rept/irp/irpc expansion */ + +#if TODO + /* TODO: Future extension: for .LIST MEBX we do not suppress ME lines which have + * a 'valid' value in WORD1 (e.g. symbol definitions) */ + + /* if (LIST(MEBX)) */ { + if (!(HAVE_LOC || HAVE_WORD1)) + return; /* If no location and no 'valid; word1, suppress the ME line */ + } +#else + if (!HAVE_LOC && binline[0] != '-') + return; /* If no location, suppress the ME line (unless forced) */ +#endif + } + + /* Handle .NLIST LOC (but .LIST SEQ,BIN) */ + + nlist_loc_only = (!LIST(LOC) && LIST(SEQ) && LIST(BIN)); + + /* Handle .NLIST BIN[,LOC] */ + + if (!LIST(BIN)) { + binline[offsetof(LSTFORMAT, words)] = '\0'; + if (!LIST(LOC)) { + binline[offsetof(LSTFORMAT, pc)] = '\0'; + } + } + + /* Handle .NLIST SRC */ + + if (!LIST(SRC)) { + listline[0] = '\0'; + trunc_line(binline); + if ( /* (binline[0] == ' ' || binline[0] == '\0') && */ + strlen(binline) < offsetof(LSTFORMAT, pc)) + return; /* Completely suppress source lines with only a sequence number */ + } + + /* Handle .NLIST SEQ[,LOC] */ + + if (!LIST(SEQ)) { + /* Note that we lose the 'flag' field this way */ + binstart = offsetof(LSTFORMAT, pc); + if (!LIST(LOC)) + binstart = offsetof(LSTFORMAT, words); + } + } + + if (listline[0] == '\0') + trunc_line(binline); + + /* Handle .LIST LD (extension to MACRO-11) and .LIST HEX */ + /* Remember: if binline has been truncated it will be filled with '\0' */ + + if (LIST(LD)) { + list_oct2dec(&binline[offsetof(LSTFORMAT, words)]); /* Keep the LOC (PC) octal */ + } else { + if (LIST(HEX)) + list_oct2hex(&binline[offsetof(LSTFORMAT, pc)]); + } + + /* Handle automatic-page-breaks (-apb) */ + + if (auto_page_break && line_on_page >= PAGE_LENGTH) + list_throw_page(FALSE); + + /* Output the line */ + + line_on_page++; + if (binline[binstart] != '\0') { + if (symbol_list_locals) { + if (looked_up_local_sym) + fprintf(lstfile, "[$%5d]", lsb); + else + fputs(" ", lstfile); + } + if (nlist_loc_only) { + binline[offsetof(LSTFORMAT, gap_after_seq) + 1] = '\0'; + fputs(binline, lstfile); + binstart = offsetof(LSTFORMAT, words); + } + fputs(&binline[binstart], lstfile); + } + + if (listline[0] != '\0') { + fputs(listline, lstfile); } + + /* If both binline & listline are empty, we will still need an empty line */ + fputc('\n', lstfile); + list_page_top = 0; + +#undef HAVE_SEQ +#undef HAVE_LOC +#undef HAVE_WORD1 + +} + + +/* show_error_line shows a source line on stderr with format: + * .LIST SEQ,LOC,BIN,SRC,COM,TTM and .NLIST BEX,LD,HEX */ + +void show_error_line( /* Synonym for show_print_line() */ + void) +{ + if (pass == PASS1) + return; + + if (!build_list()) + return; + + if (lstfile == stdout) + return; + + if (binline[0] == '\0') + return; + + if (!isdigit((unsigned char) binline[offsetof(LSTFORMAT, gap_after_seq) - 1])) + return; + + fprintf(stderr, "%-*s%.132s\n", (int)offsetof(LSTFORMAT, words), binline, listline); } + /* list_flush produces a buffered list line. */ void list_flush( void) { + if (build_list()) { + /* Remember: binline might be empty (but never NULL) */ + if (errline) { + list_line_act &= ~LIST_SUPPRESS_LINE; /* Error lines are never suppressed */ + if (show_error_lines || pass == PASS1) { /* TODO: Do this on both passes (?) */ + int i; + + /* TODO: Implement error-letters like MACRO-11 (not trivial) */ + for (i = 0; i < errline; i++) { + if (binline[i] != ' ') + break; + binline[i] = '*'; + } + + if (show_error_lines) + show_error_line(); + } + } + + if (list_line_act & LIST_SUPPRESS_LINE) { + if (list_line_act & LIST_FORCE_LISTING) { + list_line_act &= ~LIST_SUPPRESS_LINE; /* Forced-listing lines are never suppressed */ + if (binline[0] == ' ') + binline[0] = '-'; /* Flag the line as 'suppressed' but 'forced' */ + } + } + } + if (dolist()) { - padto(binline, offsetof(LSTFORMAT, source)); - fputs(binline, lstfile); - fputs(listline, lstfile); - fputc('\n', lstfile); - listline[0] = 0; - binline[0] = 0; + if (pass == PASS1) + list_line_act &= ~LIST_SUPPRESS_LINE; /* Never suppress lines on pass 1 (for -yl1) */ + + if (list_line_act & LIST_SUPPRESS_LINE) { + list_line_act &= ~LIST_SUPPRESS_LINE; /* Defer a LIST_PAGE_BEFORE */ + } else { + if (list_line_act & LIST_PAGE_BEFORE) { + list_line_act &= ~LIST_PAGE_BEFORE; + list_throw_page(FALSE); + } + if (binline[0]) + list_process(); + } + + if (list_line_act & LIST_PAGE_AFTER) { /* If we want a page throw 'after' we've listed the line ... */ + list_line_act &= ~LIST_PAGE_AFTER; /* ... turn it into ... */ + list_line_act |= LIST_PAGE_BEFORE; /* ... a LIST_PAGE_BEFORE */ + } + } + + /* Show the line in error (if any) and exit if a fatal error occurred with -fe or -fe2 */ + if (exit_requested) { + if (listline != NULL && *listline != '\0') + fprintf(stderr, "%.132s\n", listline); + exit(EXIT_FAILURE); + } + + if (build_list()) { + list_line_act &= ~(LIST_SUPPRESS_LINE | LIST_FORCE_LISTING); /* TODO: We shouldn't need this - but we do (!) */ + binline[0] = '\0'; + listline[0] = '\0'; } } + /* list_fit checks to see if a word will fit in the current listing line. If not, it flushes and prepares another line. */ @@ -102,14 +563,18 @@ static void list_fit( STREAM *str, unsigned addr) { - size_t col1 = offsetof(LSTFORMAT, source); + size_t col1; size_t col2 = offsetof(LSTFORMAT, pc); + if (LIST(TTM)) + col1 = offsetof(LSTFORMAT, words[1]); + else + col1 = offsetof(LSTFORMAT, source); + if (strlen(binline) >= col1) { list_flush(); - listline[0] = 0; - binline[0] = 0; - sprintf(binline, "%*s %6.6o", (int)offsetof(LSTFORMAT, pc), "", addr); + listline[0] = '\0'; + sprintf(binline, "%*s%6.6o", (int)offsetof(LSTFORMAT, pc), "", addr); padto(binline, offsetof(LSTFORMAT, words)); } else if (strlen(binline) <= col2) { sprintf(binline, "%*s%*d %6.6o", (int)SIZEOF_MEMBER(LSTFORMAT, flag), "", @@ -118,21 +583,130 @@ static void list_fit( } } + +/* list_format_value is used to format and show a computed value with associated flag */ + +static void list_format_value( + STREAM *str, + const char *format, + unsigned word, + char flag) +{ + (STREAM *) str; /* This parameter is currently unused */ + +// assert(build_list()); /* TODO: Remove sanity check */ + +/* if (build_list()) */ { /* TODO: Don't need this 'if' around this block - OR remove the checks from the callers */ + assert(strlen(binline) >= (int)offsetof(LSTFORMAT, gap_after_seq)); /* binline long enough? */ + assert(isdigit((unsigned char) binline[offsetof(LSTFORMAT, gap_after_seq) - 1])); /* Has a sequence number? */ + padto(binline, offsetof(LSTFORMAT, words)); + sprintf(&binline[offsetof(LSTFORMAT, words)], format, word & 0177777); + sprintf(binline + strlen(binline), "%c", flag); + } +} + + +/* list_symbol is used to show a symbol value and the 'relative-flag' */ + +void list_symbol( + STREAM *str, + SYMBOL *sym) +{ + (STREAM *) str; /* This parameter is currently unused */ + + if (build_list()) { + /* Print the symbol value and relative-flag */ + if (sym->section->flags & PSECT_REL) + list_format_value(str, "%6.6o", sym->value, '\''); + else + list_format_value(str, "%6.6o", sym->value, '\0'); + } +} + + /* list_value is used to show a computed value */ void list_value( STREAM *str, unsigned word) { - if (dolist()) { + (STREAM *) str; /* This parameter is currently unused */ + + if (build_list()) { + /* Print the value and go */ + list_format_value(str, "%6.6o", word, '\0'); + } +} + + +/* list_value_if is used to show a computed value for a .IF or .IFxx */ + +void list_value_if( + STREAM *str, + unsigned word) +{ + (STREAM *) str; /* This parameter is currently unused */ + + if (build_list()) { /* Print the value and go */ - binline[0] = 0; - sprintf(binline, "%*s%*d %6.6o", (int)SIZEOF_MEMBER(LSTFORMAT, flag), "", - (int)SIZEOF_MEMBER(LSTFORMAT, line_number), str->line, word & 0177777); + list_format_value(str, "%6.6o", word, '='); + } +} + + +/* list_3digit_value is used to show a '3-digit' computed value */ +/* This is similar to list_value() except only the first 3 digits of value will be zero-filled. */ +/* Also, if the value is < 0 it will be shown as a +ve value with a '-' flag. */ + +void list_3digit_value( + STREAM *str, + unsigned word) +{ + (STREAM *) str; /* This parameter is currently unused */ + + if (build_list()) { + list_format_value(str, "%6.3o", word, '\0'); + } +} + + +/* list_signed_value is used to show a signed 'short' computed value */ +/* This is similar to list_value() except the value will not be zero-filled. */ +/* If the value is >=0 it will be shown with a '+' flag. */ +/* Also, if the value is < 0 it will be shown as a +ve value with a '-' flag. */ + +void list_signed_value( + STREAM *str, + unsigned word) +{ + (STREAM *) str; /* This parameter is currently unused */ + + if (build_list()) { + if (word & 0x8000) + list_format_value(str, "%6o", 0x8000 - (word & 0x7fff), '-'); + else + list_format_value(str, "%6o", word, '+'); } } -/* Print a word to the listing file */ + +/* list_short_value_if is used to show a 'short' computed value for .IFT/.IFF/.IFTF and .IIF */ +/* This is similar to list_value() except the value will not be zero-filled */ +/* Also, the value flag will be set to '=' (as .IF) */ + +void list_short_value_if( + STREAM *str, + unsigned word) +{ + (STREAM *) str; /* This parameter is currently unused */ + + if (build_list()) { + list_format_value(str, "%6o", word, '='); + } +} + + +/* list_word - print a word to the listing file */ void list_word( STREAM *str, @@ -141,7 +715,7 @@ void list_word( int size, char *flags) { - if (dolist()) { + if (build_list()) { list_fit(str, addr); if (size == 1) sprintf(binline + strlen(binline), " %3.3o%1.1s ", value & 0377, flags); @@ -151,19 +725,89 @@ void list_word( } -/* Print just a line with the address to the listing file */ +/* list_location - print just a line with the address to the listing file */ void list_location( STREAM *str, unsigned addr) { - if (dolist()) { + if (build_list()) { list_fit(str, addr); } } +/* report_generic - reports warning, error, and fatal messages */ +/* Fatal messages are listed on both passes (if we didn't exit) */ +void report_generic( + unsigned type, + STREAM *str, + char *fmt, + ...) +{ + va_list ap; + char *name = "**"; + int line = 0; + const char *mess; + + report_errcnt++; + if (enabl_debug) + UPD_DEBUG_SYM(DEBUG_SYM_ERRCNT, report_errcnt); + + errline++; + switch (type) { + + case REPORT_WARNING: + if (pass == PASS1 && list_pass_0 < 2) + return; /* Don't report now */ + mess = "WARNING"; + break; + + case REPORT_ERROR: + if (pass == PASS1 && list_pass_0 < 2) + return; /* Don't report now */ + mess = "ERROR"; + break; + + case REPORT_FATAL: + mess = "FATAL"; + if (pass >= exit_if_pass) { + exit_requested = TRUE; + show_error_lines = FALSE; /* We don't want the message twice */ + if (str == NULL) { + if (listline != NULL) + listline[0] = '\0'; + } + } + break; + + default: + mess = "BADERR"; /* Should never happen */ + } + + if (str) { + name = str->name; + line = str->line; + } + + if (list_line_act & LIST_PAGE_BEFORE) + list_throw_page(FALSE); + fprintf(stderr, "%s:%d: ***%s ", name, line, mess); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (lstfile && lstfile != stdout) { + fprintf(lstfile, "%s:%d: ***%s ", name, line, mess); + va_start(ap, fmt); + vfprintf(lstfile, fmt, ap); + va_end(ap); + } +} + + +#if NODO /* See listing.h */ /* reports errors */ void report( STREAM *str, @@ -174,25 +818,32 @@ void report( char *name = "**"; int line = 0; - if (!pass && list_pass_0 < 2) + report_errcnt++; + if (enabl_debug) + UPD_DEBUG_SYM(DEBUG_SYM_ERRCNT, report_errcnt); + + errline++; + if (pass > PASS1 && list_pass_0 < 2) return; /* Don't report now. */ - errline = 1; - if (str) { name = str->name; line = str->line; } + if (list_line_act & LIST_PAGE_BEFORE) + list_throw_page(FALSE); + fprintf(stderr, "%s:%d: ***ERROR ", name, line); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); - if (lstfile) { + if (lstfile && lstfile != stdout) { fprintf(lstfile, "%s:%d: ***ERROR ", name, line); va_start(ap, fmt); vfprintf(lstfile, fmt, ap); va_end(ap); } } +#endif /* NODO */ diff --git a/crossassemblers/macro11/listing.h b/crossassemblers/macro11/listing.h index 184edd5..f225398 100644 --- a/crossassemblers/macro11/listing.h +++ b/crossassemblers/macro11/listing.h @@ -1,42 +1,116 @@ - #ifndef LISTING__H #define LISTING__H #include "stream2.h" +#include "symbols.h" /* - format of a listing line - Interestingly, no instances of this struct are ever created. - It lives to be a way to layout the format of a list line. - I wonder if I should have bothered. -*/ + * Format of a listing line. + * Interestingly, no instances of this struct are ever created. + * It lives to be a way to layout the format of a list line. + * I wonder if I should have bothered. + * Of course you should ... you use offsetof() etc. + */ + +/* 0123456789012345678901234567890123456789 + * fssssss llllll aaaaaaaabbbbbbbbcccccccc... + * ? 123 001000 012737 052100 000776 ... + */ typedef struct lstformat { - char flag[2]; /* Error flags */ - char line_number[6]; /* Line number */ - char pc[8]; /* Location */ - char words[8][3]; /* three instruction words */ - char source[1]; /* source line */ + char flag[1]; /* Error flags (ought to be [3]) */ + char line_number[6]; /* Line [sequence] number (ought to be [4]) */ + char gap_after_seq[1]; /* Space */ + char pc[6]; /* Location */ + char gap_after_pc[2]; /* Spaces */ + char words[3][8]; /* Up to three instruction words or bytes */ + char source[1]; /* Source line */ } LSTFORMAT; /* GLOBAL VARIABLES */ -#ifndef LISTING__C -extern int list_md; /* option to list macro/rept definition = yes */ +#ifndef LISTING__C + +extern char title_string[32]; /* .TITLE string (up to 31 characters) */ +extern int toc_shown; /* Flags that at least one .SBTTL was listed in the TOC */ + +extern int page_break_ff; /* TRUE if -ff (use form-feed for listing page-breaks */ +extern int auto_page_break; /* TRUE if -apb (automatic-page-break) selected */ +extern int line_on_page; /* Current line number on the listing page */ + +extern char *list_page_fmt; /* Format to use for the page throw */ + +extern int list_page_top; /* Are we at the top of a page? */ + +extern int list_line_act; /* Action to perform when listing the current line */ +extern int listing_forced; /* If set to LIST_FORCE_LISTING all lines will be listed */ + +extern int list_within_exp; /* Flag if the listing line is DIRECTLY within a macro/rept/irp/irpc expansion */ -extern int list_me; /* option to list macro/rept expansion = yes */ +extern FILE *lstfile; /* Listing file descriptor */ -extern int list_bex; /* option to show binary */ +extern int list_pass_0; /* Also list what happens during the first pass */ -extern int list_level; /* Listing control level. .LIST - increments; .NLIST decrements */ +extern int report_errcnt; /* Count the number of times report() has been called */ -extern FILE *lstfile; +extern int show_error_lines; /* Show the line with the error when reporting errors */ +extern int show_print_lines; /* Show .PRINT lines (similar to show_error_lines) */ -extern int list_pass_0; /* Also list what happens during the first pass */ +extern int exit_if_pass; /* Exit if a fatal error occurs on this pass [or higher] */ +extern int exit_requested; /* Set to TRUE if a 'fatal' exit is requested (-fe only) */ #endif +#define PAGE_LENGTH 57 /* Number of listing lines on the page for -apb */ + +#define MAX_LISTING_LINE_LEN 148 /* V05.05 truncates after 120 characters */ + /* The 'binline' part (including LSB) can be up to 48 characters. This means + * that if 'listline' is 148 long, we can get a listing line 196 bytes long. */ + +#define list_level enabl_list_arg[L_LIS].curval /* = LIST(LIS) // Directly access the .[N]LIST 'LIS' level (= value) */ + +#define LIST_SUPPRESS_LINE 1 /* Suppress the line itself (e.g. '.PAGE') */ +#define LIST_FORCE_LISTING 2 /* Force the listing of the line (regardelss of LIST_SUPPRESS_LINE) */ +#define LIST_PAGE_BEFORE 4 /* New page BEFORE listing the line */ +#define LIST_PAGE_AFTER 8 /* New page AFTER listing the line */ + +#define DONT_LIST_THIS_LINE() (list_line_act |= LIST_SUPPRESS_LINE) +#define MUST_LIST_THIS_LINE() (list_line_act |= LIST_FORCE_LISTING) + + +void list_source( + STREAM *str, + char *cp); + +void list_title_line( + const char *line2); + +void list_throw_page( + int force_throw); + +void list_symbol( + STREAM *str, + SYMBOL *sym); + +void list_value( + STREAM *str, + unsigned word); + +void list_value_if( + STREAM *str, + unsigned word); + +void list_3digit_value( + STREAM *str, + unsigned word); + +void list_signed_value( + STREAM *str, + unsigned word); + +void list_short_value_if( + STREAM *str, + unsigned word); void list_word( STREAM *str, @@ -45,25 +119,43 @@ void list_word( int size, char *flags); -void list_value( - STREAM *str, - unsigned word); - void list_location( STREAM *str, unsigned word); -void list_source( - STREAM *str, - char *cp); +#define show_print_line() show_error_line() + +void show_error_line( + void); void list_flush( void); +#if 0 // TODO /* TODO: Just in case ##__VA_ARGS__ doesn't work everywhere - see listing.c */ + +#define report_warn report +#define report_err report +#define report_fatal report + void report( STREAM *str, char *fmt, ...); - #endif + +#define REPORT_WARNING 1 /* Will [optionally] be displayed on pass 2 (and pass 1 if -yl1*2) */ +#define REPORT_ERROR 2 /* Will be displayed on pass 2 (and pass 1 if -yl1*2) */ +#define REPORT_FATAL 3 /* Will be displayed on pass 1 and pass 2 */ + +#define report_warn(str, fmt, ...) report_generic(REPORT_WARNING, str, fmt, ##__VA_ARGS__) +#define report_err(str, fmt, ...) report_generic(REPORT_ERROR, str, fmt, ##__VA_ARGS__) +#define report_fatal(str, fmt, ...) report_generic(REPORT_FATAL, str, fmt, ##__VA_ARGS__) + +void report_generic( + unsigned type, + STREAM *str, + char *fmt, + ...); + +#endif /* LISTING__H */ diff --git a/crossassemblers/macro11/macro11.c b/crossassemblers/macro11/macro11.c index 5444bb9..eaa6b76 100644 --- a/crossassemblers/macro11/macro11.c +++ b/crossassemblers/macro11/macro11.c @@ -34,52 +34,123 @@ DAMAGE. */ +/* + * The goal of this project is to provide a portable MACRO-11 assembler + * which, as far as possible, will assemble source code which MACRO-11 on + * RSX-11M/PLUS would assemble. The resulting executable program should be + * the same as on the original platform. + * + * Source code which would produce errors on the original platform may produce + * different results using this program. However, some effort has been made + * to detect errors and handle them similarly to the original. + * + * Reference version is MACRO-11 V05.05 for RSX-11M/PLUS and RT-11. + * Documentation is the PDP-11 MACRO-11 Language Reference Manual (May 88) ... + * ... Order Number AA-KX10A-TC including Update Notice 1, AD-KXIOA-Tl. + * + */ + +#include +#include #include #include -#include -#include #include "macro11.h" -#include "util.h" - -#include "assemble_globals.h" #include "assemble.h" #include "assemble_aux.h" +#include "assemble_globals.h" #include "listing.h" +#include "macros.h" #include "object.h" +#include "parse.h" +#include "rad50.h" +#include "stream2.h" #include "symbols.h" +#include "util.h" + + +#ifdef WIN32 +#define strcasecmp stricmp +#if !__STDC__ +#define stricmp _stricmp +#endif +#endif + /* enable_tf is called by command argument parsing to enable and - disable named options. */ + disable named options. If flags != 0 then flags are |'ed instead. */ static void enable_tf( - char *opt, - int tf) + char *opt, + int tf, + unsigned flags) { - if (strcmp(opt, "AMA") == 0) - opt_enabl_ama = tf; - else if (strcmp(opt, "GBL") == 0) - enabl_gbl = tf; /* Unused in pass 2 */ - else if (strcmp(opt, "ME") == 0) - list_me = tf; - else if (strcmp(opt, "BEX") == 0) - list_bex = tf; - else if (strcmp(opt, "MD") == 0) - list_md = tf; + char *cp = opt; + char argnam[4]; + int argnum; + int len; + + do { + if (cp == opt) + cp = skipwhite(cp); + else + cp = skipdelim(cp); + + for (len = 0; len < 4; len++) + if (isupper((unsigned char) *cp)) + argnam[len] = *cp++; + else + break; + + if (len < 2 || len > 3) { + fprintf(stderr, "Invalid -%s option: %s [ignored]\n", (flags) ? "-dc" : (tf) ? "e" : "d", (cp - len)); + break; + } + + argnam[len] = '\0'; + + argnum = lookup_arg(argnam, ARGS_VISIBLE); + if (argnum < 0) { + fprintf(stderr, "Unknown -%s option: %s [ignored]\n", (flags) ? "dc" : (tf) ? "e" : "d", argnam); + /* break; */ + } else { + if (flags == 0) { + if (argnum == L_LIS) { + if (tf) + enabl_list_arg[L_LIS].defval++; /* Increase list level */ + else + enabl_list_arg[L_LIS].defval--; /* Decrease list level */ + } else { + enabl_list_arg[argnum].defval = tf; + } + enabl_list_arg[argnum].flags &= ~ARGS_NOT_SUPPORTED; /* -e and -d ... */ + enabl_list_arg[argnum].flags |= ARGS_MUST_SUPPORT; /* ... force support */ + } else { + /* enabl_list_arg[argnum].flags &= ~ARGS_NOT_SUPPORTED; // If -dc xxx, pretend we support 'xxx' */ + enabl_list_arg[argnum].flags |= flags; + } + } + } while (*cp != '\0'); } + /*JH:*/ static void print_version( FILE *strm) { - fprintf(strm, "macro11 - portable MACRO11 assembler for DEC PDP-11\n"); - fprintf(strm, " Version %s\n", VERSIONSTR); + fprintf(strm, PROGRAM_NAME " - portable MACRO-11 assembler for DEC PDP-11\n"); + if (enabl_debug) + fprintf(strm, " Version " THIS_VERSION "\n"); + fprintf(strm, " Version " VERSIONSTR "\n"); fprintf(strm, " Copyright 2001 Richard Krehbiel,\n"); - fprintf(strm, " modified 2009 by Joerg Hoppe,\n"); - fprintf(strm, " modified 2015-2017,2020-2021 by Olaf 'Rhialto' Seibert.\n"); + fprintf(strm, " modified 2009 by Joerg Hoppe,\n"); + fprintf(strm, " modified 2015-2017,2020-2023 by Olaf 'Rhialto' Seibert,\n"); + fprintf(strm, " modified 2023 by Mike Hill.\n"); + fprintf(strm, "\n"); } + static void append_env( char *envname, char *value) @@ -104,74 +175,109 @@ static void append_env( putenv(temp); } + /*JH:*/ static void print_help( void) { printf("\n"); print_version(stdout); - printf("\n"); printf("Usage:\n"); - printf(" macro11 [-o ] [-l []] \n"); - printf(" [-h] [-v][-e