示例#1
0
def main(args=None, emitter=None):
    """ Entry point when executed from command line.
    zxbc can be used as python module. If so, bear in mind this function
    won't be executed unless explicitly called.
    """
    # region [Initialization]
    config.init()
    zxbpp.init()
    zxbparser.init()
    arch.target.backend.init()
    arch.target.Translator.reset()
    asmparse.init()
    # endregion

    options = parse_options(args)
    arch.set_target_arch(OPTIONS.architecture)
    backend = arch.target.backend
    args = [
        options.PROGRAM
    ]  # Strip out other options, because they're already set in the OPTIONS container
    input_filename = options.PROGRAM

    zxbpp.setMode('basic')
    zxbpp.main(args)

    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    input_ = zxbpp.OUTPUT
    zxbparser.parser.parse(input_,
                           lexer=zxblex.lexer,
                           tracking=True,
                           debug=(OPTIONS.debug_level > 1))
    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    # Unreachable code removal
    unreachable_code_visitor = src.api.optimize.UnreachableCodeVisitor()
    unreachable_code_visitor.visit(zxbparser.ast)

    # Function calls graph
    func_call_visitor = src.api.optimize.FunctionGraphVisitor()
    func_call_visitor.visit(zxbparser.ast)

    # Optimizations
    optimizer = src.api.optimize.OptimizerVisitor()
    optimizer.visit(zxbparser.ast)

    # Emits intermediate code
    translator = arch.target.Translator()
    translator.visit(zxbparser.ast)

    if gl.DATA_IS_USED:
        gl.FUNCTIONS.extend(gl.DATA_FUNCTIONS)

    # This will fill MEMORY with pending functions
    func_visitor = arch.target.FunctionTranslator(gl.FUNCTIONS)
    func_visitor.start()

    # Emits data lines
    translator.emit_data_blocks()
    # Emits default constant strings
    translator.emit_strings()
    # Emits jump tables
    translator.emit_jump_tables()
    # Signals end of user code
    translator.ic_inline(';; --- end of user code ---')

    if OPTIONS.emit_backend:
        with open_file(OPTIONS.output_filename, 'wt', 'utf-8') as output_file:
            for quad in translator.dumpMemory(backend.MEMORY):
                output_file.write(str(quad) + '\n')

            backend.MEMORY[:] = []  # Empties memory
            # This will fill MEMORY with global declared variables
            translator = arch.target.VarTranslator()
            translator.visit(zxbparser.data_ast)

            for quad in translator.dumpMemory(backend.MEMORY):
                output_file.write(str(quad) + '\n')
        return 0  # Exit success

    # Join all lines into a single string and ensures an INTRO at end of file
    asm_output = backend.emit(backend.MEMORY,
                              optimize=OPTIONS.optimization_level > 0)
    asm_output = arch.target.optimizer.optimize(
        asm_output) + '\n'  # invoke the -O3

    asm_output = asm_output.split('\n')
    for i in range(len(asm_output)):
        tmp = backend.ASMS.get(asm_output[i], None)
        if tmp is not None:
            asm_output[i] = '\n'.join(tmp)

    asm_output = '\n'.join(asm_output)

    # Now filter them against the preprocessor again
    zxbpp.setMode('asm')
    zxbpp.OUTPUT = ''
    zxbpp.filter_(asm_output, filename=input_filename)

    # Now output the result
    asm_output = zxbpp.OUTPUT.split('\n')
    get_inits(asm_output)  # Find out remaining inits
    backend.MEMORY[:] = []

    # This will fill MEMORY with global declared variables
    var_checker = src.api.optimize.VariableVisitor()
    var_checker.visit(zxbparser.data_ast)
    translator = arch.target.VarTranslator()
    translator.visit(zxbparser.data_ast)
    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    tmp = [
        x for x in backend.emit(backend.MEMORY, optimize=False)
        if x.strip()[0] != '#'
    ]
    asm_output = backend.emit_start() + tmp \
                                      + ['%s:' % backend.DATA_END_LABEL, '%s:' % backend.MAIN_LABEL] \
                                      + asm_output + backend.emit_end()

    if OPTIONS.output_file_type == FileType.ASM:  # Only output assembler file
        with open_file(OPTIONS.output_filename, 'wt', 'utf-8') as output_file:
            output(asm_output, output_file)
    elif not options.parse_only:
        fout = StringIO()
        output(asm_output, fout)
        asmparse.assemble(fout.getvalue())
        fout.close()
        asmparse.generate_binary(
            OPTIONS.output_filename,
            OPTIONS.output_file_type,
            binary_files=options.append_binary,
            headless_binary_files=options.append_headless_binary,
            emitter=emitter)
        if gl.has_errors:
            return 5  # Error in assembly

    if OPTIONS.memory_map:
        if asmparse.MEMORY is not None:
            with open_file(OPTIONS.memory_map, 'wt', 'utf-8') as f:
                f.write(asmparse.MEMORY.memory_map)

    if not gl.has_errors and options.save_config:
        src.api.config.save_config_into_file(
            options.save_config, src.api.config.ConfigSections.ZXBC)

    return gl.has_errors  # Exit success
示例#2
0
def main(args=None):
    # Initializes asm parser state
    src.api.config.init()
    asmparse.init()
    zxbpp.init()

    # Create option parser
    o_parser = argparse.ArgumentParser()
    o_parser.add_argument('PROGRAM', type=str, help='ASM program file')
    o_parser.add_argument("-d",
                          "--debug",
                          action="count",
                          default=OPTIONS.Debug,
                          help="Enable verbosity/debugging output")

    o_parser.add_argument("-O",
                          "--optimize",
                          type=int,
                          dest="optimization_level",
                          help="Sets optimization level. 0 = None",
                          default=OPTIONS.optimization)

    o_parser.add_argument(
        "-o",
        "--output",
        type=str,
        dest="output_file",
        help="Sets output file. Default is input filename with .bin extension",
        default=None)

    o_parser.add_argument("-T",
                          "--tzx",
                          action="store_true",
                          dest="tzx",
                          default=False,
                          help="Sets output format to tzx (default is .bin)")

    o_parser.add_argument("-t",
                          "--tap",
                          action="store_true",
                          dest="tap",
                          default=False,
                          help="Sets output format to tzx (default is .bin)")

    o_parser.add_argument(
        "-B",
        "--BASIC",
        action="store_true",
        dest="basic",
        default=False,
        help=
        "Creates a BASIC loader which load the rest of the CODE. Requires -T ot -t"
    )

    o_parser.add_argument(
        "-a",
        "--autorun",
        action="store_true",
        default=False,
        help="Sets the program to auto run once loaded (implies --BASIC)")

    o_parser.add_argument(
        "-e",
        "--errmsg",
        type=str,
        dest="stderr",
        default=OPTIONS.StdErrFileName,
        help="Error messages file (standard error console by default")

    o_parser.add_argument("-M",
                          "--mmap",
                          type=str,
                          dest="memory_map",
                          default=None,
                          help="Generate label memory map")

    o_parser.add_argument(
        "-b",
        "--bracket",
        action="store_true",
        default=False,
        help="Allows brackets only for memory access and indirections")

    o_parser.add_argument('-N',
                          "--zxnext",
                          action="store_true",
                          default=False,
                          help="Enable ZX Next extra ASM opcodes!")

    o_parser.add_argument("--version",
                          action="version",
                          version="%(prog)s " + VERSION)

    options = o_parser.parse_args(args)

    if not os.path.exists(options.PROGRAM):
        o_parser.error("No such file or directory: '%s'" % options.PROGRAM)
        sys.exit(2)

    OPTIONS.Debug = int(options.debug)
    OPTIONS.inputFileName = options.PROGRAM
    OPTIONS.outputFileName = options.output_file
    OPTIONS.optimization = options.optimization_level
    OPTIONS.use_loader = options.autorun or options.basic
    OPTIONS.autorun = options.autorun
    OPTIONS.StdErrFileName = options.stderr
    OPTIONS.memory_map = options.memory_map
    OPTIONS.bracket = options.bracket
    OPTIONS.zxnext = options.zxnext

    if options.tzx:
        OPTIONS.output_file_type = 'tzx'
    elif options.tap:
        OPTIONS.output_file_type = 'tap'

    if not OPTIONS.outputFileName:
        OPTIONS.outputFileName = os.path.splitext(
            os.path.basename(OPTIONS.inputFileName)
        )[0] + os.path.extsep + OPTIONS.output_file_type

    if OPTIONS.StdErrFileName:
        OPTIONS.stderr = open(OPTIONS.StdErrFileName, 'wt')

    if int(options.tzx) + int(options.tap) > 1:
        o_parser.error("Options --tap, --tzx and --asm are mutually exclusive")
        return 3

    if OPTIONS.use_loader and not options.tzx and not options.tap:
        o_parser.error(
            'Option --BASIC and --autorun requires --tzx or tap format')
        return 4

    # Configure the preprocessor to use the asm-preprocessor-lexer
    zxbpp.setMode('asm')

    # Now filter them against the preprocessor
    zxbpp.main([OPTIONS.inputFileName])

    # Now output the result
    asm_output = zxbpp.OUTPUT
    asmparse.assemble(asm_output)
    if global_.has_errors:
        return 1

    if not asmparse.MEMORY.memory_bytes:  # empty seq.
        asmparse.warning(0, "Nothing to assemble. Exiting...")
        return 0

    current_org = max(asmparse.MEMORY.memory_bytes.keys() or [0]) + 1

    for label, line in asmparse.INITS:
        expr_label = asmparse.Expr.makenode(
            asmparse.Container(asmparse.MEMORY.get_label(label, line), line))
        asmparse.MEMORY.add_instruction(asmparse.Asm(0, 'CALL NN', expr_label))

    if len(asmparse.INITS) > 0:
        if asmparse.AUTORUN_ADDR is not None:
            asmparse.MEMORY.add_instruction(
                asmparse.Asm(0, 'JP NN', asmparse.AUTORUN_ADDR))
        else:
            asmparse.MEMORY.add_instruction(
                asmparse.Asm(0, 'JP NN', min(asmparse.MEMORY.orgs.keys()))
            )  # To the beginning of binary

        asmparse.AUTORUN_ADDR = current_org

    if OPTIONS.memory_map:
        with open(OPTIONS.memory_map, 'wt') as f:
            f.write(asmparse.MEMORY.memory_map)

    asmparse.generate_binary(OPTIONS.outputFileName, OPTIONS.output_file_type)
    return global_.has_errors
示例#3
0
def main(args=None, emitter=None):
    """ Entry point when executed from command line.
    You can use zxbc.py as a module with import, and this
    function won't be executed.
    """
    # region [Initialization]
    config.init()
    zxbpp.init()
    zxbparser.init()
    arch.target.backend.init()
    arch.target.Translator.reset()
    asmparse.init()
    # endregion

    parser = args_parser.parser()
    options = parser.parse_args(args=args)

    # ------------------------------------------------------------
    # Setting of internal parameters according to command line
    # ------------------------------------------------------------
    OPTIONS.Debug = options.debug
    OPTIONS.optimization = options.optimize
    OPTIONS.outputFileName = options.output_file
    OPTIONS.StdErrFileName = options.stderr
    OPTIONS.array_base = options.array_base
    OPTIONS.string_base = options.string_base
    OPTIONS.Sinclair = options.sinclair
    OPTIONS.heap_size = options.heap_size
    OPTIONS.memoryCheck = options.debug_memory
    OPTIONS.strictBool = options.strict_bool or OPTIONS.Sinclair
    OPTIONS.arrayCheck = options.debug_array
    OPTIONS.emitBackend = options.emit_backend
    OPTIONS.enableBreak = options.enable_break
    OPTIONS.explicit = options.explicit
    OPTIONS.memory_map = options.memory_map
    OPTIONS.strict = options.strict
    OPTIONS.headerless = options.headerless
    OPTIONS.zxnext = options.zxnext
    OPTIONS.expect_warnings = gl.EXPECTED_WARNINGS = options.expect_warnings
    OPTIONS.hide_warning_codes = options.hide_warning_codes

    if options.arch not in arch.AVAILABLE_ARCHITECTURES:
        parser.error(f"Invalid architecture '{options.arch}'")
        return 2

    OPTIONS.architecture = options.arch
    arch.set_target_arch(options.arch)
    backend = arch.target.backend

    # region [Enable/Disable Warnings]
    enabled_warnings = set(options.enable_warning or [])
    disabled_warnings = set(options.disable_warning or [])
    duplicated_options = [f"W{x}" for x in enabled_warnings.intersection(disabled_warnings)]

    if duplicated_options:
        parser.error(f"Warning(s) {', '.join(duplicated_options)} cannot be enabled "
                     f"and disabled simultaneously")
        return 2

    for warn_code in enabled_warnings:
        errmsg.enable_warning(warn_code)

    for warn_code in disabled_warnings:
        errmsg.disable_warning(warn_code)

    # endregion

    OPTIONS.org = src.api.utils.parse_int(options.org)
    if OPTIONS.org is None:
        parser.error("Invalid --org option '{}'".format(options.org))

    if options.defines:
        for i in options.defines:
            macro = list(i.split('=', 1))
            name = macro[0]
            val = ''.join(macro[1:])
            OPTIONS.__DEFINES[name] = val
            zxbpp.ID_TABLE.define(name, value=val, lineno=0)

    if OPTIONS.Sinclair:
        OPTIONS.array_base = 1
        OPTIONS.string_base = 1
        OPTIONS.strictBool = True
        OPTIONS.case_insensitive = True

    if options.ignore_case:
        OPTIONS.case_insensitive = True

    debug.ENABLED = OPTIONS.Debug

    if int(options.tzx) + int(options.tap) + int(options.asm) + int(options.emit_backend) + \
            int(options.parse_only) > 1:
        parser.error("Options --tap, --tzx, --emit-backend, --parse-only and --asm are mutually exclusive")
        return 3

    if options.basic and not options.tzx and not options.tap:
        parser.error('Option --BASIC and --autorun requires --tzx or tap format')
        return 4

    if options.append_binary and not options.tzx and not options.tap:
        parser.error('Option --append-binary needs either --tap or --tzx')
        return 5

    if options.asm and options.memory_map:
        parser.error('Option --asm and --mmap cannot be used together')
        return 6

    OPTIONS.use_loader = options.basic
    OPTIONS.autorun = options.autorun

    if options.tzx:
        OPTIONS.output_file_type = 'tzx'
    elif options.tap:
        OPTIONS.output_file_type = 'tap'
    elif options.asm:
        OPTIONS.output_file_type = 'asm'
    elif options.emit_backend:
        OPTIONS.output_file_type = 'ic'

    args = [options.PROGRAM]
    if not os.path.exists(options.PROGRAM):
        parser.error("No such file or directory: '%s'" % args[0])
        return 2

    if OPTIONS.memoryCheck:
        OPTIONS.__DEFINES['__MEMORY_CHECK__'] = ''
        zxbpp.ID_TABLE.define('__MEMORY_CHECK__', lineno=0)

    if OPTIONS.arrayCheck:
        OPTIONS.__DEFINES['__CHECK_ARRAY_BOUNDARY__'] = ''
        zxbpp.ID_TABLE.define('__CHECK_ARRAY_BOUNDARY__', lineno=0)

    if OPTIONS.enableBreak:
        OPTIONS.__DEFINES['__ENABLE_BREAK__'] = ''
        zxbpp.ID_TABLE.define('__ENABLE_BREAK__', lineno=0)

    OPTIONS.include_path = options.include_path
    OPTIONS.inputFileName = zxbparser.FILENAME = \
        os.path.basename(args[0])

    if not OPTIONS.outputFileName:
        OPTIONS.outputFileName = \
            os.path.splitext(os.path.basename(OPTIONS.inputFileName))[0] + os.path.extsep + \
            OPTIONS.output_file_type

    if OPTIONS.StdErrFileName:
        OPTIONS.stderr = open_file(OPTIONS.StdErrFileName, 'wt', 'utf-8')

    zxbpp.setMode('basic')
    zxbpp.main(args)

    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    input_ = zxbpp.OUTPUT
    zxbparser.parser.parse(input_, lexer=zxblex.lexer, tracking=True,
                           debug=(OPTIONS.Debug > 1))
    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    # Unreachable code removal
    unreachable_code_visitor = src.api.optimize.UnreachableCodeVisitor()
    unreachable_code_visitor.visit(zxbparser.ast)

    # Function calls graph
    func_call_visitor = src.api.optimize.FunctionGraphVisitor()
    func_call_visitor.visit(zxbparser.ast)

    # Optimizations
    optimizer = src.api.optimize.OptimizerVisitor()
    optimizer.visit(zxbparser.ast)

    # Emits intermediate code
    translator = arch.target.Translator()
    translator.visit(zxbparser.ast)

    if gl.DATA_IS_USED:
        gl.FUNCTIONS.extend(gl.DATA_FUNCTIONS)

    # This will fill MEMORY with pending functions
    func_visitor = arch.target.FunctionTranslator(gl.FUNCTIONS)
    func_visitor.start()

    # Emits data lines
    translator.emit_data_blocks()
    # Emits default constant strings
    translator.emit_strings()
    # Emits jump tables
    translator.emit_jump_tables()
    # Signals end of user code
    translator.ic_inline(';; --- end of user code ---')

    if OPTIONS.emitBackend:
        with open_file(OPTIONS.outputFileName, 'wt', 'utf-8') as output_file:
            for quad in translator.dumpMemory(backend.MEMORY):
                output_file.write(str(quad) + '\n')

            backend.MEMORY[:] = []  # Empties memory
            # This will fill MEMORY with global declared variables
            translator = arch.target.VarTranslator()
            translator.visit(zxbparser.data_ast)

            for quad in translator.dumpMemory(backend.MEMORY):
                output_file.write(str(quad) + '\n')
        return 0  # Exit success

    # Join all lines into a single string and ensures an INTRO at end of file
    asm_output = backend.emit(backend.MEMORY, optimize=OPTIONS.optimization > 0)
    asm_output = arch.target.optimizer.optimize(asm_output) + '\n'  # invoke the -O3

    asm_output = asm_output.split('\n')
    for i in range(len(asm_output)):
        tmp = backend.ASMS.get(asm_output[i], None)
        if tmp is not None:
            asm_output[i] = '\n'.join(tmp)

    asm_output = '\n'.join(asm_output)

    # Now filter them against the preprocessor again
    zxbpp.setMode('asm')
    zxbpp.OUTPUT = ''
    zxbpp.filter_(asm_output, args[0])

    # Now output the result
    asm_output = zxbpp.OUTPUT.split('\n')
    get_inits(asm_output)  # Find out remaining inits
    backend.MEMORY[:] = []

    # This will fill MEMORY with global declared variables
    var_checker = src.api.optimize.VariableVisitor()
    var_checker.visit(zxbparser.data_ast)
    translator = arch.target.VarTranslator()
    translator.visit(zxbparser.data_ast)
    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    tmp = [x for x in backend.emit(backend.MEMORY, optimize=False) if x.strip()[0] != '#']
    asm_output = backend.emit_start() + tmp \
                                      + ['%s:' % backend.DATA_END_LABEL, '%s:' % backend.MAIN_LABEL] \
                                      + asm_output + backend.emit_end()

    if options.asm:  # Only output assembler file
        with open_file(OPTIONS.outputFileName, 'wt', 'utf-8') as output_file:
            output(asm_output, output_file)
    elif not options.parse_only:
        fout = StringIO()
        output(asm_output, fout)
        asmparse.assemble(fout.getvalue())
        fout.close()
        asmparse.generate_binary(OPTIONS.outputFileName, OPTIONS.output_file_type,
                                 binary_files=options.append_binary,
                                 headless_binary_files=options.append_headless_binary,
                                 emitter=emitter)
        if gl.has_errors:
            return 5  # Error in assembly

    if OPTIONS.memory_map:
        if asmparse.MEMORY is not None:
            with open_file(OPTIONS.memory_map, 'wt', 'utf-8') as f:
                f.write(asmparse.MEMORY.memory_map)

    return gl.has_errors  # Exit success