Module: Zemu

Defined in:
lib/zemu.rb,
lib/zemu/debug.rb,
lib/zemu/config.rb,
lib/zemu/instance.rb,
lib/zemu/interactive.rb

Overview

Zemu is a module providing an interface to build and interact with configurable Z80 emulators.

Configurations can be defined in a declarative syntax, from which an emulator executable can be compiled. This executable can then be run from within Zemu and controlled programmatically.

Examples:


require 'zemu'

# A simple configuration with a ROM block
# and a RAM block.
conf = Zemu::Config.new do
    name "zemu_emulator"

    add_memory Zemu::Config::ROM.new do
        name "rom"
        address 0x0000
        size 0x4000

        contents from_binary("app.bin")
    end

    add_memory Zemu::Config::RAM.new do
        name "ram"
        address 0x8000
        size 0x1000
    end
end

# Start a new instance with this configuration.
instance = Zemu.start(conf)

# Program breakpoint.
# Will trigger if the emulator is about to execute an
# instruction at 0x102.
instance.break 0x102, :program

# Continue. Emulator will run until HALT or until
# the breakpoint (set above) is hit.
instance.continue

# Display the value of the A register (accumulator)
# at the breakpoint.
puts instance.register["A"]

# Close the instance.
instance.quit

Defined Under Namespace

Modules: Debug Classes: Config, ConfigError, ConfigObject, Instance, InstanceError, InteractiveInstance

Constant Summary collapse

SRC =

Location of C source for the emulator.

File.join(__dir__, "..", "src")

Class Method Summary collapse

Class Method Details

.build(configuration, user_defines = {}) ⇒ Object

Builds a library according to the given configuration. Returns true if the build is a success, false (build failed) or nil (compiler not found) otherwise.

Parameters:

  • configuration (Zemu::Config)

    The configuration for which an emulator will be generated.

  • user_defines (defaults to: {})

    Any user-defined preprocessor macros.


90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/zemu.rb', line 90

def Zemu::build(configuration, user_defines={})
    # Create the output directory unless it already exists.
    unless Dir.exist? configuration.output_directory
        Dir.mkdir configuration.output_directory
    end

    # Generate the autogenerated source files.
    generate(configuration)

    output = File.join(configuration.output_directory, "#{configuration.name}.so")

    autogen = File.join(configuration.output_directory, "autogen_#{configuration.name}")

    compiler = configuration.compiler

    inputs = [
        "main.c",                       # main library functionality
        "debug.c",                      # debug functionality
        "interrupt.c",                  # interrupt functionality
        "external/z80/sources/Z80.c"    # z80 core library
    ]

    inputs_str = inputs.map { |i| File.join(SRC, i) }.join(" ")

    inputs_str += " " + File.join(autogen, "bus.c")

    defines = {
        "CPU_Z80_STATIC" => 1,
        "CPU_Z80_USE_LOCAL_HEADER" => 1
    }

    defines.merge! user_defines

    defines_str = defines.map { |d, v| "-D#{d}=#{v}" }.join(" ")

    includes = [
        "external/Z/API",
        "external/z80/API",
        "external/z80/API/emulation/CPU",
        "."
    ]

    includes_str = includes.map { |i| "-I#{File.join(SRC, i)}" }.join(" ")

    includes_str += " -I" + autogen

    command = "#{compiler} -O2 -Werror -Wno-unknown-warning-option -fPIC -shared -Wl,-undefined -Wl,dynamic_lookup #{includes_str} #{defines_str} -o #{output} #{inputs_str}"
    
    # Run the compiler and generate a library.
    return system(command)
end

.generate(configuration) ⇒ Object

Generates the prerequisite source and header files for a given configuration.

Parameters:

  • configuration (Zemu::Config)

    The configuration for which an emulator will be generated.


145
146
147
# File 'lib/zemu.rb', line 145

def Zemu::generate(configuration)
    generate_bus(configuration)
end

.generate_bus(configuration) ⇒ Object

Generates the bus.c and bus.h files for a given configuration.


150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/zemu.rb', line 150

def Zemu::generate_bus(configuration)
    header_template = ERB.new File.read(File.join(SRC, "bus.h.erb"))
    source_template = ERB.new File.read(File.join(SRC, "bus.c.erb"))

    autogen = File.join(configuration.output_directory, "autogen_#{configuration.name}")

    unless Dir.exist? autogen
        Dir.mkdir autogen
    end

    File.write(File.join(autogen, "bus.h"),
               header_template.result(configuration.get_binding))

    File.write(File.join(autogen, "bus.c"),
               source_template.result(configuration.get_binding))
end

.start(configuration, user_defines = {}) ⇒ Object

Build and start an emulator according to the given configuration. Returns the emulator instance.

Parameters:

  • configuration (Zemu::Config)

    The configuration for which an emulator will be generated.

  • user_defines (defaults to: {})

    Any user-defined preprocessor macros.


69
70
71
72
73
# File 'lib/zemu.rb', line 69

def Zemu::start(configuration, user_defines={})
    build(configuration, user_defines)

    return Instance.new(configuration)
end

.start_interactive(configuration, options = {}) ⇒ Object

Starts an interactive instance of an emulator, according to the given configuration.

Parameters:

  • configuration (Zemu::Config)

    The configuration for which an emulator will be generated.


78
79
80
81
82
83
# File 'lib/zemu.rb', line 78

def Zemu::start_interactive(configuration, options = {})
    instance = start(configuration)

    interactive = InteractiveInstance.new(instance, options)
    interactive.run
end