diff --git a/extension.properties b/extension.properties index 3a7d844..58f06c4 100644 --- a/extension.properties +++ b/extension.properties @@ -1,5 +1,5 @@ name=@extname@ description=Loader for Sega Mega Drive / Genesis ROMs author=Dr. MefistO -createdOn=07.04.2019 +createdOn= version=@extversion@ diff --git a/src/main/java/sega/NegVariableBaseReg.java b/src/main/java/sega/NegVariableBaseReg.java new file mode 100644 index 0000000..186ef2a --- /dev/null +++ b/src/main/java/sega/NegVariableBaseReg.java @@ -0,0 +1,7 @@ +package sega; + +public enum NegVariableBaseReg { + A4, + A5, + A6 +} diff --git a/src/main/java/sega/SegaAnalyzer.java b/src/main/java/sega/SegaAnalyzer.java new file mode 100644 index 0000000..524623c --- /dev/null +++ b/src/main/java/sega/SegaAnalyzer.java @@ -0,0 +1,114 @@ +package sega; + +import java.math.BigInteger; +import java.util.List; + +import ghidra.app.services.AbstractAnalyzer; +import ghidra.app.services.AnalyzerType; +import ghidra.app.util.importer.MessageLog; +import ghidra.framework.options.OptionType; +import ghidra.framework.options.Options; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.lang.Register; +import ghidra.program.model.lang.RegisterValue; +import ghidra.program.model.listing.ContextChangeException; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.FunctionIterator; +import ghidra.program.model.listing.FunctionManager; +import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.ProgramContext; +import ghidra.program.model.symbol.Symbol; +import ghidra.program.model.symbol.SymbolTable; +import ghidra.program.model.symbol.SymbolType; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +public class SegaAnalyzer extends AbstractAnalyzer { + + private static final long DEF_BASE_ADDR = 0xFF8000; + private static final String OPTION_BASE_REG = "Base Register"; + private static final String OPTION_BASE_VAL = "Base Address"; + private NegVariableBaseReg baseReg = NegVariableBaseReg.A6; + private String baseAddr = String.format("0x%06X", DEF_BASE_ADDR); + + private static boolean isSegaLoader(Program program) { + return program.getExecutableFormat().equalsIgnoreCase(SegaLoader.LOADER_NAME) || + program.getExecutableFormat().equalsIgnoreCase("Sega Genesis/MegaDrive ROM file v.2"); + } + + public SegaAnalyzer() { + super("Negative Offset Variables", "Finds variables like -$XXXX(Ax) and sets specified base address", AnalyzerType.FUNCTION_ANALYZER); + } + + @Override + public boolean getDefaultEnablement(Program program) { + return isSegaLoader(program); + } + + @Override + public boolean canAnalyze(Program program) { + return isSegaLoader(program); + } + + @Override + public void registerOptions(Options options, Program program) { + options.registerOption(OPTION_BASE_REG, OptionType.ENUM_TYPE, baseReg, null, "Register which is used in -$XXXX(Ax) expressions"); + + options.registerOption(OPTION_BASE_VAL, OptionType.STRING_TYPE, baseAddr, null, "Hexadecimal Base address for negative variables. 0xFF8000, for ex."); + } + + @Override + public void optionsChanged(Options options, Program program) { + super.optionsChanged(options, program); + + baseReg = options.getEnum(OPTION_BASE_REG, NegVariableBaseReg.A6); + baseAddr = options.getString(OPTION_BASE_VAL, baseAddr); + } + + @Override + public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) + throws CancelledException { + + Address resetAddr = null; + + SymbolTable symTable = program.getSymbolTable(); + List resets = symTable.getLabelOrFunctionSymbols(VectorsTable.VECTOR_NAMES[1], program.getGlobalNamespace()); + FunctionManager funcMgr = program.getFunctionManager(); + ProgramContext ctx = program.getProgramContext(); + Register reg = program.getRegister(baseReg.name()); + + for (Symbol sym: resets) { + if (sym.getSymbolType() == SymbolType.FUNCTION) { + resetAddr = sym.getAddress(); + break; + } + } + + if (resetAddr == null) { + return false; + } + + FunctionIterator it = funcMgr.getFunctions(set, true); + + while (it.hasNext()) { + Function func = it.next(); + + AddressSetView as = func.getBody(); + + if (as.getMinAddress().equals(resetAddr) || ctx.getRegisterValue(reg, as.getMinAddress()) != null) { + continue; + } + + RegisterValue regVal = new RegisterValue(reg, BigInteger.valueOf(Long.decode(baseAddr))); + try { + ctx.setRegisterValue(as.getMinAddress(), as.getMaxAddress(), regVal); + } catch (ContextChangeException e) { + return false; + } + } + + return true; + } + +} diff --git a/src/main/java/sega/SegaLoader.java b/src/main/java/sega/SegaLoader.java index 0e59dc9..b2341c5 100644 --- a/src/main/java/sega/SegaLoader.java +++ b/src/main/java/sega/SegaLoader.java @@ -53,12 +53,14 @@ */ public class SegaLoader extends AbstractLibrarySupportLoader { + public static final String LOADER_NAME = "Sega Mega Drive / Genesis Loader"; + private VectorsTable vectors; private GameHeader header; @Override public String getName() { - return "Sega Mega Drive / Genesis Loader"; + return LOADER_NAME; } @Override diff --git a/src/main/java/sega/VectorsTable.java b/src/main/java/sega/VectorsTable.java index 29413a3..8ad874c 100644 --- a/src/main/java/sega/VectorsTable.java +++ b/src/main/java/sega/VectorsTable.java @@ -13,7 +13,7 @@ class VectorsTable implements StructConverter { private static final int VECTORS_SIZE = 0x100; private static final int VECTORS_COUNT = VECTORS_SIZE / 4; - private static final String[] VECTOR_NAMES = { + public static final String[] VECTOR_NAMES = { "SSP", "Reset", "BusErr", "AdrErr", "InvOpCode", "DivBy0", "Check", "TrapV", "GPF", "Trace", "Reserv0", "Reserv1", "Reserv2", "Reserv3", "Reserv4", "BadInt", "Reserv10", "Reserv11", "Reserv12", "Reserv13", "Reserv14", "Reserv15", "Reserv16", "Reserv17", "BadIRQ", "IRQ1",