Fire Emblem: Shadow Dragon (FE11) documentation

I have recently been diving into researching Fire Emblem: Shadow Dragon (FE11). I wanted to document my findings in hopes that it can be (a) useful to folks exploring the DS games, and (b) built upon over time.

Since I have some real-life commitments coming up over the next little while, I wanted to get the information I’ve found down on paper before it goes stale in my brain.

If you have any information from your own DSFE hacking explorations, please feel free to add any findings of your own to this thread!

There are a number of terms that I’ll use below, which I’ll try to define ahead of time.

First, the term “ARM9 binary”. When you unpack the DS ROM using a tool like DSLazy, it will have a file called “arm9.bin”. This file contains the main code for the game. The DS has two processors, and “ARM9” refers to the one responsible for running the game code.

When I reference code or ROM data, I’ll use the notation FE11:0x<...>. This refers to a memory location of the game when the game is running. Since the DS loads the ROM to a base of 0x02000000, you can find the equivalent location in the “arm9.bin” file by subtracting 0x02000000 from the in-game address. All offsets refer to the US version of the game.

If/when I eventually start referencing the code or data inside of an overlay, I’ll use the notation FE11:Ovy##_0x<...>.

For completeness, the DS also has an ARM7 processor (and an ARM7 binary), which was mainly used for sound processing and other auxiliary tasks. Nintendo allegedly discouraged using ARM7 for game logic, so I likely won’t reference it much in this thread.

In general, I highly recommend the post Starcube Labs - “Reverse Engineering a DS Game”. It gives what I think is a clear introduction to loading DS ROMs into reverse-engineering tools like Ghidra, and offers helpful tips on identifying code and setting breakpoints in emulators.

Other relevant DSFE resources:

Overlays

The Nintendo DS employs a system called “overlays” to manage memory efficiently. Overlays allow the console to dynamically load code or data into memory only when needed and unload it when it’s no longer required. This helps optimize the limited memory resources of the DS.

Details

When you unpack a DSFE ROM using a tool like DSLazy, you will find a folder called “overlay”, which contains the binary files for each overlay. Additionally, the file “y9.bin” contains metadata about the overlays:

Overlay (Size: 0x20):
00 | word | Overlay ID
04 | word | Load address (where the overlay is placed in memory at runtime)
08 | word | Overlay size
0C | word | BSS (uninitialized data) size
10 | word | Static initializer start address
14 | word | Static initializer end address
18 | word | File ID
1C | word | 0 (delimiter between overlays)

For a detailed explanation of overlays, I recommend checking out the overlays section in the Starcube Laboratories post on Reverse Engineering a DS game.

FE11 features 12 overlays, which is relatively low compared to other DS games I’ve seen such as Pokemon HeartGold or Pokemon Mystery Dungeon: Explorers of Sky.

While the game is running, the table tracking the currently-loaded overlays is found at FE11:0x020E4DF4. It is represented by a series of bytes in memory. If an overlay is active, the byte value at that overlay’s index will be 1. If it’s inactive, the value will be 0.

The functions for managing overlays are as follows:

  • Loading an overlay: FE11:0x0200F20C
  • Unloading an overlay: FE11:0x0200F24C

There are additionally proc commands that involve loading/unloading overlays, which we will see later when I talk about the DSFE proc system.

From my initial research:

  • Overlay 0 and 2 are typically always active.
  • Overlay 4 seems related to core gameplay (e.g. battle maps).
  • Overlay 6 might handle the title and save screen.

There is still lots more to be uncovered about the overlays/how they are used.

Procs

FE11, like many other Fire Emblem games, features a “proc” system which is used to enable the game to do pseudo-parallel processing on an otherwise single-threaded CPU. This is something that has been very well detailed by others in the past:

For the sake of brevity, I won’t cover the entire scope of what procs are since I think Nat’s guide is already extremely thorough and comprehensive, and I would just be repeating a lot of that. Please read their research on it, and consider this to be the DSFE extension of that research.

Details

The DSFE proc system is, as far as I can tell, quite similar to the GBAFE proc system. It features the same general interfaces for starting/finding/ending procs, and the code itself appears to be relatively similar too.

There are a few differences. For example, each proc can have its own “function table”; a pointer to a list of callable functions that is stored at +0x00 of the proc. The purpose of this isn’t clear to me yet but I hope to understand how this complements the existing script-based format.

So far, I haven’t been able to identify things like what order the proc trees are executed in, or whether that is terribly important in DSFE.

The Proc Tree is located in RAM at FE11:0x02190F28.

The function at FE11:0x02018C58 seems to be equivalent to FE8’s Proc_Init. It iterates over 0x80 procs and initializes their fields to zero values. Based on that, it seems that the max proc count is 0x80 in FE11.

There appears to be 14 proc trees in FE11, based on the same Proc_Init function.

Many of the core functions for interacting with the procs are located in the “ITCM” (or “Instruction Tightly-Coupled Memory”) region. One of the features of the Nintendo DS is to give fast access to a limited amount of code.

What this means is that, at the time when the game is loaded, there is some code gets loaded the end of the ROM and put into a fast-access area. Most of the game’s code refers to the functions in that particular area. It makes it a touch more confusing for us as hackers to find references to that code (or at some point in the future, to modify it).

The Proc Function table is located at FE11:0x020E7898 in the ROM. It gets copied into the ITCM region, to FE11:0x01FFBBF8, when the game is running. In Nat’s doc, this aligns with the “Code Interpretation Table”.

Below, you can the locations of the proc functions along with some names that indicate their purposes (in most cases, I’ve tried to reuse the FE8 proc names where they seem to line up).

[00] 0x02019358 - ProcCmd_DELETE
[01] 0x02019358 - ProcCmd_DELETE
[02] 0x02019368 // returns 0
[03] 0x02019370 // returns 1
[04] 0x02019378 // advance and return 1
[05] 0x0201938c - ProcCmd_SET_DESTRUCTOR (?)
[06] 0x020193b4 - ? maybe set name?
[07] 0x020193dc - ProcCmd_CALL_ROUTINE
[08] 0x020193fc - ProcCmd_CALL_ARG_ADV
[09] 0x02019420 - ProcCmd_WHILE_ROUTINE
[0A] 0x0201945c - ProcCmd_CALL_ARG_ADVCOND (advance conditionally)
[0B] 0x0201951c - ?
[0C] 0x02019620 - ProcCmd_LOOP_ROUTINE
[0D] 0x02019640 - ProcCmd_WHILE_EXISTS
[0E] 0x02019674 - ProcCmd_NEW_CHILD
[0F] 0x020196a0 - ProcCmd_NEW_CHILD_BLOCKING
[10] 0x020196cc - ProcCmd_NEW_PROC_IN_TREE (?)
[11] 0x020196f8 - ProcCmd_END_ALL (sarg skips an additional pointer @ 027E1268)
[12] 0x02019734 - ProcCmd_BREAK_ALL (sarg skips an additional pointer @ 027E1268)
[13] 0x02019954 - ProcCmd_NOP_1D ? label?
[14] 0x02019770 - ProcCmd_GOTO
[15] 0x0201979c - ProcCmd_GOTO_IF_YES (if larg = 0 or larg func returns non-zero, goto)
[16] 0x020197e8 - ProcCmd_GOTO_IF_NO (same but if larg func returns zero)
[17] 0x02019834 - ProcCmd_JUMP
[18] 0x02019874 - ProcCmd_SLEEP
[19] 0x020198a4 - ProcCmd_SET_MARK (?)
[1A] 0x020198c4 - (?) end children?
[1B] 0x020198f8 - (?) end children?
[1C] 0x0201992c - (?) end children?
[1D] 0x02019954 - ProcCmd_NOP_1D
[1E] 0x02019968 - start a specific child (blocking if larg not &1)
[1F] 0x020199b8 - start a specific child (blocking if larg not &1)
[20] 0x02019a08 - start a specific child (blocking if larg not &1)
[21] 0x02019a58 - start a specific child (blocking if larg not &1)
[22] 0x02019aa8 - start a specific child (blocking if larg not &1)
[23] 0x02019af8 - start a specific child (blocking if larg not &1)
[24] 0x02019b48 - ProcCmd_OVERLAY - load or unload overlay; overlay ID = sarg, load/unload = larg
[25] 0x02019b84 - ProcCmd_ - maybe reset overlays? Unload all except sarg? Load only sarg?

Loading Units

Recently in the FEU Discord, there was some discussion about figuring out how units are loaded, particularly in the context of hard-mode scaling bonuses.

Details

The core function responsible for loading a unit is at FE11:0x02039FF8 in the main ARM9 binary. I’ve started decompiling the function here. Based on this, the function’s signature appears to be:

void func_02039FF8(struct Unit * unit, struct Dispo * dispo);

This function takes two parameters:

  • unit: A pointer to a Unit structure, which holds the data for the character being loaded.
  • dispo: A pointer to a Dispo structure, which provides information about how the unit should be loaded onto the map.

What is a “dispo”?

“Dispos” are, as far as I understand, the internal name for the data structure used to specify how to place units on the map. It’s likely an abbreviation for “disposition”. There are code references in the FE8 prototype that mention the term “dispos”, and they’re more commonly referenced by this name in hacking when we start seeing more strings in the code (such as FE9-10, and the 3DSFE games).

Each map has several “dispo groups” associated with it, which are loaded at different times (for example, for cutscenes or reinforcements). The layout of a single “dispo” entry is like this:

Dispo Entry (Size: 0x50):
00 | short | pid (character ID)
02 | short | jid (class ID)
04 | byte  | x position on load
05 | byte  | y position on load
06 | ?
09 | byte  | ?
0A | byte  | starting level
0B | byte  | ?
0C | word  | ?
10 | short[] | items; length 5?
1A | ?
1C | ?
24 | short | flags
26 | short | ?
28 | word[] | array of pointers to AI "types" (strings); length 4
38 | word[] | array of ?; length 4?
48 | byte | ?
49 | byte | ?
4A | short | ?
4C | byte | ?
4D | byte | ?
4E | ?

For more details on the dispo format, you can refer to the FE11 Unit Disposition format documentation by Blazer.

Hard-mode scaling bonuses

When one of the hard mode difficulties are selected, FE11 modifies enemy stats and equips units with different weapons based on the difficulty level.

In the function used for loading units, the game checks the unit’s dispo flags (+0x24) field. If the flags 0x110 are set, the game performs some stat modifications (an RNG roll based on the character/class growths), and weapon modifications based on the difficulty you’ve selected.

Weapon upgrade tables

When scaling the weapons for harder difficulties, the game uses a series of “upgrade ramps” (for lack of a better term). In the ROM, there is a lookup table at FE11:0x020D4BC0 which has pointers to each weapon type’s “upgrade ramp”.

FE11:0x020D4BC0 - Weapon Type upgrade lookup table
[0] 0x020D4B88 - pointer to Sword Table
[1] 0x020D4B48 - pointer to Lance Table
[2] 0x020D4B68 - pointer to Axe Table
[3] 0x020D4B78 - pointer to Bow Table
[4] 0x020D4B58 - pointer to Magic Table

Each of these tables contain pointers to string values for each item ID (IID), corresponding to which weapon the unit will receive for each difficulty. The weapon the unit receives depends on the difficulty selected (difficulty - 1).

Sword Table (FE11:0x020D4B88)
[0] 0x020D4AC8 - "IID_IRONSWORD"
[1] 0x020D4B08 - "IID_STEELSWORD"
[2] 0x020D4BE4 - "IID_SILVERSWORD"
[3] 0x020D4B18 - "IID_BRAVESWORD"

Lance Table (FE11:0x020D4B48)
[0] 0x020D4AD8 - "IID_IRONLANCE"
[1] 0x020D4B28 - "IID_STEELLANCE"
[2] 0x020D4B38 - "IID_SILVERLANCE"
[3] 0x020D4AF8 - "IID_BRAVELANCE"

Axe Table (FE11:0x020D4B68)
[0] 0x020D4A50 - "IID_IRONAXE"
[1] 0x020D4A68 - "IID_STEELAXE"
[2] 0x020D4AA8 - "IID_SILVERAXE"
[3] 0x020D4A78 - "IID_BRAVEAXE"

Bow Table (FE11:0x020D4B78)
[0] 0x020D4A5C - "IID_IRONBOW"
[1] 0x020D4A88 - "IID_STEELBOW"
[2] 0x020D4AB8 - "IID_SILVERBOW"
[3] 0x020D4A98 - "IID_BRAVEBOW"

Magic Table (FE11:0x020D4B58)
[0] 0x020D49B4 - "IID_FIRE"
[1] 0x020D4A44 - "IID_ELFIRE"
[2] 0x020D4AE8 - "IID_BOLGANONE"
[3] 0x020D4A38 - "IID_THORON"

Forged items

There is additional logic that determines whether the unit gets an upgraded weapon or a forgeable weapon. This seems to involve the item’s data in the FE11 Database. If the item’s byte at +0x3A is equal to 0x7E, the game uses a forged weapon instead of following the upgrade ramp. Further investigation is still needed to understand this completely.

13 Likes

Battle Animations

Based on the existing research from Blazer (mirrored on Laqieer’s GitHub), along with some hints from HeartHero’s “Accessible FE12 Hacking” post about battle animations, I have been able to piece a bit more together about how Battle Animations are structured in DSFE.

It’s not perfect or totally solved yet (I haven’t been able to figure out the correct palette data, or if there is a script format embedded inside of the animations), but I’ll try to document as far as I’ve been able to get for now.

First, a GIF export of the ARCHER_F dodge animation using the data extracted from the /data/b/ARCHER_F binary file:

frames_1

Details

What a weird format!!

As HeartHero mentions in their “Accessible FE12 hacking” post, each binary file in the /data/b directory represents an “archive” for that particular class’s animations. I use the term “archive” because the binary file appears to consist multiple files.

Each class has one “archive” for its unarmed animation, and one “archive” per weapon that class can wield. Example: LORD for the unarmed Lord class, and LORD_SW for the Lord class wielding a sword.

The first part of the binary file is a list of pairs containing the offsets to an animation (e.g. dodging, attacking, etc.), as well as that animation’s size within the binary. For example, I’ll use ARCHER_F. I assume that the blanks here could be filled by other animations, since ARCHER_F_BO has more entries filled out up front.

0x00000100 0x00002CF0 -> anim starts at 0x100, of size 0x2CF0
0x00000000 0x00000000
0x00000000 0x00000000
0x00000000 0x00000000
0x00002DF0 0x00004A5C -> anim starts at 0x2DF0, of size 0x4A5C
0x00000000 0x00000000

If we go to the location of that first animation, we can see data that looks like how other DSFE files are stored – it has a file “header”:

00 | word | pointer to end of animation
04 | word | pointer to this animation's "pointer table"
08 | word | number of entries in this animation's "pointer table"
0C-20 | word | blank?

We’ll use ARCHER_F’s first animation as an example:

@ 0x100 + 0x00, we have the offset to the end of the current animation. To calculate, add the base of the animation to the value at this offset to get the final offset of the animation. For ARCHER_F, it’s 0x2CF0 + 0x100 = 0x2DF0.
@ 0x100 + 0x04, we have the offset to this animation’s pointer table. To calculate it, we need to add two values: the base of this animation (0x100), and the space taken up by the “animation header” (0x20). For our example, ARCHER_F, it’s 0x2C58 + 0x100 + 0x20 = 0x2D78.
@ 0x100 + 0x08, we have the number of entries in the pointer table. For ARCHER_F, there are 0x1E (30) entries in the first animation’s pointer table.

We can go to 0x2D78 to look at the pointer table. Each pointer should be resolved relative to the start of its animation, with an additional 0x20 added to account for the “file header”. So for ARCHER_F’s first animation, when we see 0x14 in the pointer table, it really means 0x14 + 0x100 (start of the animation) + 0x20 (size of file header) = 0x134 in the binary file.


Picture: ARCHER_F’s pointer table (highlighted)

Below, I’ll show the values of the pointer table in the following format:

[pointer as shown in the table] => [actual location in the binary file]: [value at that location]
@ ARCHER_F's pointer table at 0x2D78. There are 0x1E (30) pointers in the table.
0x00000014 => 0x134: 0x1C
0x00000018 => 0x138: 0x13C (pointer to ?)
0x00000020 => 0x140: 0x1BC (pointer to first anim frame, compressed)
0x00000028 => 0x148: 0xEC (pointer to ?)
0x00000030 => 0x150: 0x4DE (pointer to second anim frame, compressed)
0x00000038 => 0x158: 0xF2 (pointer to ?)
0x00000040 => 0x160: 0x81B (pointer to third anim frame, compressed)
0x00000048
0x00000050
0x00000058
// (more)

The compression scheme of the frame data is the same as the “FEDS portrait compression”. Nintenlord’s decompression utilities can be used to decompress the individual frames. As I understand, it’s some sort of run-length encoding (RLE) used for the DSFE games.

There are still a lot of unknowns but hopefully this can be used as a building block for further research.

6 Likes

This is very cool, and very helpful. Thanks for this!

1 Like

Sweet! How did you break down the files the /data/b folder into the multiple sub-files (the sprite frames and the ‘command’ binary files)?

Could the various “pointer to ?” be ‘camera’ commands (like for panning over to the enemy for projectile hits, or following ‘charging’ cav).

Here’s an update on the FE11 battle animation files. I’ve made some progress on understanding how they’re structured, so it’s time to update the documentation.

I’m hoping that soon I’ll have a reliable and presentable way of extracting the animations. For now, here’s some more progress GIFs:

Animations

LORD_SW - dodge:
dodge

SOLDIER_LA - ranged attack:
range_attack

BISHOP_F_MA - magic attack:
magic_attack

The first 0x100 bytes are the archive’s file table. This is an array of the following 8-byte structure:

00 | word | start of file
04 | word | file size

In practice, there are really two tables. The first half of the 0x100 bytes are the animation files, and the second half are the palette files. Effectively, each animation can have a maximum of 16 animations and 16 palettes.

0x000-0x080 - animation files
0x080-0x100 - palette files

The animation files are indexed by the type of animation. So far, these are the ones I have come across:

0: Dodge
1: Ranged Attack
2: Ranged Critical
3: Retreat (i.e. after attacking)
4: Idle
5: Magic Attack
6: Magic Critical
7: Melee Attack
8: Melee Double
9: Melee Critical
10: Melee Double Critical

Animation files:

File header:

Each animation file has a “header” (size: 0x20 bytes) that describes a few attributes about the file, such as its size and the location of the “pointer table”.

The pointer table’s purpose appears to be to give the game the exact location of a pointer in the file, so that it can loop over the pointers and update them to their real value after being loaded into memory at runtime.

00    | word | size (excludes header)
04    | word | offset to pointer table (excludes header)
08    | word | number of pointers in the file
0C-20 | ?    | blank

General animation metadata:

00-10 | ?     | blank? maybe populated at runtime
10    | short | frame count
12    | short | unknown
14    | word  | pointer to frame image / OAM data
18    | word  | pointer to sequence data

Frame data (image / OAM):

Each frame of animation has information about the sprite that will be displayed. Some frames of animation have a “primary” and a “secondary” image, depending on their size. For example, the Cavalier class (SOCIALKNIGHT) has two images that need to be loaded.

I’m unsure if this is to divide the palette of the hair from the palette of the base sprite, but it seems to be that way at a glance.

00 | short | unknown
02 | short | OAM entry count
04 | word  | pointer to main image data ("primary image"); compressed
08 | word  | pointer to additional image data ("secondary image"); compressed
0C | word  | pointer to OAM data

OAM Data:

Object Attribute Memory (OAM for short) relates to how the Nintendo DS draws sprites to the screen. Each sprite has 3 “attributes” that describe how the sprite gets constructed and drawn from the image data loaded into VRAM.

References for more information:

00 | short | attribute 0 (y coord, mode, shape, etc.)
02 | short | attribute 1 (x coord, rotation/scaling, size)
04 | short | attribute 2 (char name, priority, palette)

Sequence data:

I’m calling this the “sequence” data since it represents the order and duration of how each frame of animation is drawn. It seems somewhat similar to the standard DS “NANR” format, but dissimilar enough that it seems like Intelligent Systems may have their own custom adaptation.

00 | short | unknown
02 | short | unknown
04 | short | sequence entry count
06 | short | unknown
08 | word  | pointer to sequence entry list

Sequence Entry:

Each “entry” in the sequence refers to a frame (i.e. the sprite/image details).

Most of the vanilla animations seem to have every frame of animation have a duration of 3 frames. In theory, this could be modified so that the frames have more “punch” to them.

The shorts 04 and 06 seem to adjust the position of the drawn frame.

00 | byte  | frame
01 | byte  | unknown
02 | short | duration (in frames)
04 | short | maybe x offset?
06 | short | maybe y offset?
3 Likes

Hello Eebit, I’m trying the first attempt on the project and encountered several issues:

  1. (sovled) how should I set the base address of each overlay when import the unpacked binary to Ghidra?

  2. It seems not to be valid asm code at the start of armv9.bin, Where is the entry point for the entire program? How is the entire game started?

  3. When decomping functions, how should I select gcc and cflags?

  4. How does it make it to compile *.c to *.o?

  5. Just for discussion: The current repository linking strategy seems to be very different from what we are used to. Do you think it is a feasible strategy to try to use the traditional gnu-make strategy to compile armv9.bin and overlay separately?

Hey Mokha!

The DS is definitely a bit different from loading in a GBA ROM and it has a few points where I stumbled as well. Let me do my best to answer your questions!

  1. The NDS loads the ARM9 binary into RAM at memory address 0x2000000. According to GBATek, the pointer to the ARM9 binary entrypoint is at the NDS ROM header offset 0x24. For FE11, the address of the _start function is 0x2000800.

  2. I have so far found success in decompiling functions for FE11 by using the MetroWerks C Compiler for Embedded ARM with version 2.0/sp2p2. On decomp.me, that is “3.0 build 137 (MWCC 2.0sp2p2)”. The compiler flags I have used are below:

Compiler flags
-O4,p -enum int -proc arm946e -gccext,on -fp soft -lang c99 -inline on,noauto -Cpp_exceptions off -gccinc -interworking -gccdep -g -sym on
    "-O4,p",                # Optimize maximally for performance
    "-enum int",            # Use int-sized enums
    "-proc arm946e",        # Target processor
    "-gccext,on",           # Enable GCC extensions
    "-fp soft",             # Compute float operations in software
    "-lang c99",
    "-char signed",         # Char type is signed
    "-inline on,noauto",    # Inline only functions marked with 'inline'
    "-Cpp_exceptions off",  # Disable C++ exceptions
    "-gccinc",              # Interpret #include "..." and #include <...> equally
    "-interworking",        # Enable ARM/Thumb interworking
    "-gccdep",
    "-sym on",              # Debug info, including line numbers
    "-nolink",              # Do not link
    "-msgstyle gcc",        # Use GCC-like messages (some IDEs will make file names clickable)

I will also note that I have found a few functions in what I’m calling unit.c that may require the use of the flag -ipa file, but I have not consistently been able to get this matching… For example, func_0203c378

  1. I have copied much of the setup from the Zelda: Phantom Hourglass decomp project, which uses a script called tools/configure.py. The script generates a ninja build file for all of the C file targets. This happens inside of the function add_mwcc_builds

  2. It could be feasible to set up a similar Make-based project to the existing GBA decomp projects – the pret pokeheartgold and pmd-sky projects are some examples of existing DS decompilations that use make.
    However, I found that using the ds-decomp toolkit developed by Aetias is a very robust solution that requires a lot less manual effort of setup. The wiki for pmd-sky “How was this project originally set up?” describes a lot of manual effort spent in identifying functions, whereas ds-decomp has very good heuristics for identifying the functions. As well, a lot of the work of splitting files is taken care of by the ds-decomp framework. It also handles the linker script generation for us, among (many) other things.
    In addition, the ds-decomp workflow is set up for integration with the tool objdiff. This tool is very handy for local decompilation and faster development cycles, and also offers the possibility to export a scratch to decomp.me.

2 Likes

I have found several interesting thing in booting routine:

The main program boots at function:0x02000800 and then call the function:0x020009FC after setup stack pointer, which may copy several data on reference the config table in 0x02000B68 and 0x20E8A60. Further analysis shows that:

  • data from src=0x020E3CA0 is copied to dst=0x01FF8000 (ITCM) with size=0x4CE0
  • data from src=(0x020E3CA0 + 0x4CE0) = 0x020E8980 is copied to dst=0x027E0000 with size=0xE0

Then, these area at 0x020E3CA0 - 0x021A23E0 is directly released and becomes the BSS segment.

So we can get conclusions:

  1. The area starting from 0xE3CA0 in arm9.bin stores data before loading and then is used as BSS in runtime, which is an overlay, rather than just rodata.
  2. Just like the arm function of GBAFE, the data of ITCM is copied directly from arm9.bin. These ITCM part, offset=0xE3CA0, size=0x4CE0 in arm9.bin is better to be treated as a sepearte subsystem, seperately compiled and linked to base=0x01FF8000.
1 Like