Driver's disassembler

From S1MP3 Wiki

Jump to: navigation, search

Contents

Introduction

Utility written by Roland Neul, to help in disassembling drivers from the original firmware.

Instructions and how it works

The utility segments a driver into its main part and overlays. This helps getting the offsets in the disassembly right. It also generates a basic control file that contains a comment on how to call dz80 (including a working start offset).

dz80 is the z80 disassembler from the d52 package. It can be found on Development tools.

You will still have to find data sections and modify the control file accordingly, but it should be a good start. Be aware that the origin (start offset) is adjusted so the header can be included in the disassembly, this means the driver is really loaded 512 bytes higher (i.e. if the disassembly starts with org 0x400, the real driver starts at address 0x600.)

Roland tried to figure out how overlays are handled in applications, but up to now he had no success. The code for application detection I came up with so far is still included, so if anyone wants to try it, good luck. The code should compile with any decent C-compiler. Call the program with the name of the driver as argument. Depending on the driver you choose there might be quite a few files generated (the FAT driver comes in 35 segments in my firmware.) So best create a new directory which only contains the driver you want to examine.

Use the code on your own risk. If you don't understand what it does, don't use it!

Source code

The source code can be downloaded here : driver_disasm.c

Control Files

Control files tell the disassembler additional information about the object code. They are an important tool when trying to analyze unknown code.

The idea is not to edit the actual disassembly but only the control file and restart the disassembly after that. This way it is possible to, e.g. name subroutines and all references to them will be replaced by the chosen label. Even comments can be inserted into the disassembly.

As an example here is the control file produced by the above code for the main part of one driver:

; Control file for UI_L0724.DRV.main.bin
; automatically created by genctl
; disassemble with 'dz80 -d -x3e00 UI_L0724.DRV.main.bin'
;
# 3e00  This is the main part of UI_L0724.DRV
b 3e00-3e01
w 3e02-3e05
b 3e06-3ffd
w 3ffe-3fff
c 4000-47ff

After a few hours of looking over the code, the following was the resulting control file:

; Control file for UI_L0724.DRV.main.bin
; automatically created by genctl
; disassemble with 'dz80 -d -x3e00 UI_L0724.DRV.main.bin'
;
# 3e00  This is the main part of UI_L0724.DRV
b 3e00-3e01
w 3e02-3e05
b 3e06-3e11
i 3e12-3ffd
w 3ffe-3fff
b 4000-4003
w 4004-4005
b 4006-4319
a 431a-431b
b 431c-431c
a 431d-431e
b 431f-4321
a 4322-4323
b 4324-4324
a 4325-4326
b 4327-4329
a 432a-432b
b 432c-432c
a 432d-432e
b 432f-4331
a 4332-4333
b 4334-4334
a 4335-4336
b 4337-4339
a 433a-433b
b 433c-433c
a 433d-433e
c 433f-462f
i 4630-47ff
; label definitions
# 43ab Blit bitmap into  page buffer
# 43ab Parameters:
# 43ab   b    - column
# 43ab   h    - page
# 43ab   (de) - bitmap
# 43ab   c    - width
l 43ab pageBlit
l 43c0 pb_ispg2
l 43c5 pb_ispg1
l 43ca pb_ispg0
l 43cd pb_copy
l 43d6 pb_done
# 433f Blit bitmap to buffer
# 433f Parameters:
# 433f   (bc) - bitmap
# 433f   (de) - descriptor:
# 433f          BYTE x (destination)
# 433f          BYTE y (destination)
# 433f          BYTE width
# 433f          BYTE height
l 433f blitBmap
# 436c Blit bitmap to buffer (register parameters)
# 436c Parameters:
# 436c   b    - column
# 436c   c    - width
# 436c   h    - start page
# 436c   l    - number of pages
# 436c   (de) - bitmap
l 436c regBlitBmap
l 4381 rb_noclip
l 438b rb_firstpg
l 438e rb_pglp
l 43aa rb_done
# 4473 Blit (part of) a page buffer to the lcd
# 4473 Parameters:
# 4473   b    - width
# 4473   c    - page
# 4473   (hl) - page buffer
# 4473 Global variables:
# 4473   col_lo - command to set lower 4 bits of column address
# 4473   col_hi - command to set upper 4 bits of column address
l 4473 lcd_pageBlit
l 44be lpb_done
# 43da Blit (part of) page buffers to lcd
# 43da Parameters:
# 43da   (de) - Region:
# 43da          BYTE x
# 43da          BYTE y
# 43da          BYTE width
# 43da          BYTE height
# 43da Global variables:
# 43da   col_lo       - command to set lower 4 bits of column address
# 43da   col_hi       - command to set upper 4 bits of column address
# 43da   region_width - width of region to update
# 43da   region_pages - number of pages to update
l 43da update_region
l 43fa ur_gotparam
l 4416 ur_noclip
l 4446 ur_update
l 4459 ur_addlp
l 445c ur_pg0
l 4460 ur_pglp
l 446e ur_done
# 44c2 Send command to lcd
# 44c2 Parameters:
# 44c2   c - command to send
l 44c2 lcd_cmd
# 44d3 Send data to lcd
# 44d3 Parameters:
# 44d3   (hl) - data to send
# 44d3   b - size of data
l 44d3 lcd_data
l 44dd ld_cplp
# 44ea Set cursor position
# 44ea Parameters:
# 44ea   e - xpos
# 44ea   c - ypos
l 44ea setCursorPos
l 44f2 sc_xtoobig
l 44fa sc_ytoobig
# 454b Get the current cursor position
# 454b Returns:
# 454b   b - current column
# 454b   h - current page
l 454b getCursorPos
; variables
l 4001 ypos
l 4002 xpos
l 400c line_buf
l 4049 col_lo
l 4048 col_hi
l 4047 page
l 404a region_width
l 404b region_pages
l 404c width
l 404f p0_buf
l 40cf p1_buf
l 414f p2_buf
l 41cf p3_buf
; symbols
l 0080 LCD_WIDTH

This example, while probably being of no general use (most firmwares will use different offsets), shows a few useful control file directives. Here is a short list of the directives used:

  • # <address> <text>: Insert <text> as a comment at address <address>.
  • b <address1>-<address2>: Assume the section from <address1> to <address2> contains byte data.
  • w <address1>-<address2>: Assume the section from <address1> to <address2> contains word data.
  • a <address1>-<address2>: Assume the section from <address1> to <address2> contains address data. This means each word is assumed to be a code address, where a label will be generated.
  • c <address1>-<address2>: Assume the section from <address1> to <address2> contains code.
  • i <address1>-<address2>: Ignore the section from <address1> to <address2>. No output is generated for an ignored section. Instead an 'org' is inserted that adjusts the offset to the next section.
  • l <address> <name>: Defines a label named <name> at address <address>. All references to this address (in calls, branches, etc.) will be replaced by <name>.

Disassembling the original 'UI_L0724.DRV.main.bin' using this control file results in a disassembly with nice comments for each subroutine, telling what it does and which parameters it expects.

Overlays

Overlays are a means to cope with limited address space. This is definately the case in a z80 based system, the address space is limited to 65536 Bytes. This is somewhat tiny for complex applications. Overlays are parts of program code/data that are only loaded on demand. There generally is a main part of the application/driver which contains everything that is frequently needed. The parts needed less often are arranged in multiple overlays.

These overlays are all loaded to the same address. This means the application/driver only needs space for its main part and some additional space that can take the largest overlay. The space for the overlay can be shared between several applications and/or drivers.

The problem when disassembling a file that contains multiple overlays, is that each overlay expects to be loaded to the same base address, while the disassembler works on a flat memory model (i.e. addresses are increasing and unique.) To get around this limitation the code above splits the object file into multiple parts.

It tries to get the split points and the addresses where the different parts expect to be loaded to by analyzing the data in the object file. Since I don't have a complete understanding neither of the file header nor of the overlay headers, this is not guaranteed to be right.

The advantage of splitting the file is that the addresses used in absolute references (e.g. calls) are correct, as long as the start offset was determined correctly. You will see how good it works if you check the resulting disassembly. Generally it can be expected that most calls and absolute branches in an overlay will target parts inside the overlay (or the main part.) So if everything went all right most calls should lead to some code you can find.

Some clues whether your disassembly uses the right base addresses might be found in the Memory map. It is probably a good idea to discuss any inconsistencies you might find. I guess a good place for this could be the talk-page of the memory map or the mailing list.

Personal tools
about this site
Advertisement