Magic Lantern Forum

Developing Magic Lantern => Reverse Engineering => Topic started by: a1ex on September 25, 2014, 04:11:05 PM

Title: EekoAddRawPath
Post by: a1ex on September 25, 2014, 04:11:05 PM
Starting from the latest discovery from g3gg0 (http://www.magiclantern.fm/forum/index.php?topic=13163), I've looked into some routines that appeared to do raw additions and subtractions (EekoAddRawPath). They seem to work, you can also do some scaling, compute min/max, and they are fairly fast (about 60 ms for a full-size raw buffer, enough for stacking some photos in the camera).

Problem: these functions are only available on DIGIC 5 cameras. However, g3gg0 found out they use the TWOADD module (present on all cameras), so there might be some hope.

5D3 stubs:

/* 1.2.3 */
void (*EekoAddRawPath)(void *a, void *b, void *out, int op, int off_a, int gain_a, int black_a, int gain_b, int black_b, int div8, int out_off, void (*cbr)(void*), void* cbr_arg) = 0xFF32C538;
void (*EekoAddRawPath_cleanup_engio)() = 0xFF5127F0;
void (*EekoAddRawPath_cleanup_reslock)() = 0xFF512698;

/* 1.1.3 */
FF327A54 (called after "%s Addsub Count:%d")
FF507CE8 (called after "stsCompleteMultiExp", after BEQ)
FF507B90 (called next)


Basic call to add two images:

    EekoAddRawPath(image_a, image_b, image_out, 0, 0, 4096, 2048, 4096, 2048, 0, 0, (void(*)(void*))give_semaphore, eeko_sem);
    take_semaphore(eeko_sem, 5000);
    EekoAddRawPath_cleanup_engio();
    EekoAddRawPath_cleanup_reslock();


Operations:

To figure them out, I've created two gradients (raw image buffers with fake data), horizontal and vertical, with values from 0 to 16383, and used them as operands for the Eeko routine. The third image is the result.

0: a + b
(http://a1ex.magiclantern.fm/bleeding-edge/eeko/sum.png)

1: a - b
(http://a1ex.magiclantern.fm/bleeding-edge/eeko/dif.png)

2: max(a, b)
(http://a1ex.magiclantern.fm/bleeding-edge/eeko/max.png)

3: min(a, b)
(http://a1ex.magiclantern.fm/bleeding-edge/eeko/min.png)

Octave code of what it does (incomplete, doesn't model all overflows):

function out = eeko(a, b, op, off_a, gain_a, black_a, gain_b, black_b, div8, out_off)

    % valid range for the parameters
    op = bitand(op, 3);
    gain_a = bitand(gain_a, 8191);
    gain_b = bitand(gain_b, 8191);
   
    % offset image A
    a = a + off_a;
    a = max(a, -2048);

    if gain_a ~= 4096,
        % scale image A, relative to black level
        a = round(max((a - black_a) * gain_a / 4096, -2048) + black_a);

        % note: without the "if", clamping to 16383 here will fail some tests
        a = min(a, 16383);
    end
   
    if black_a > 4096
        % some strange overflow
        a = min(a + 8192, 16383);
    end
   
    if gain_b ~= 4096,
        % scale image B, relative to black level
        b = round(max((b - black_b) * gain_b / 4096, -2048) + black_b);

        % note: without the "if", clamping to 16383 here will fail some tests
        b = min(b, 16383);
    end
   
    % perform some operation between A and B
    switch op
        case 0
            out = a + b;
        case 1
            out = a - b;
        case 2
            out = max(a, b);
        case 3
            out = min(a, b);
    end

    out = coerce(out, 0, 16383);

    if div8
        % darken image by 3 stops (not adjustable)
        % optionally offset by image; for some reason, the offset is multiplied by 7/8 (why?)
        out = round((out + out_off*7) / 8);
    end
   
    out = coerce(out, 0, 16383);
end

function y = coerce(x, lo, hi)
    y = min(max(x, lo), hi);
end


Notes:
- image_out buffer can be reused (you may use either image_a or image_b)
- to change resolution, one has to call some lower-level routines, with uglier syntax

To figure out the meaning of the parameters, I've started from the basic call and changed the values (one or two at a time). Then, by trial and error, I've adjusted the octave code (eeko.m) until it matched the images saved from camera.

Test data, camera code and octave scripts can be found here: https://www.dropbox.com/sh/k037ulitl3kkf0t/AABXZYcTQYDqvqMQ-bNrHRtpa?dl=0
Title: Re: EekoAddRawPath
Post by: 1% on September 25, 2014, 07:42:47 PM
The twoadd might convert 14 to 12?  TWOADD_SETUP_14_12
Title: Re: EekoAddRawPath
Post by: a1ex on September 25, 2014, 07:59:10 PM
Yeah, that's why I said we may hope for 12-bit conversion. But so far I have no idea how to use it.
Title: Re: EekoAddRawPath
Post by: Levas on September 25, 2014, 08:13:09 PM
Ah, maybe 14 to 12 conversion

Was already wondering what was so interesting about these set of routines.
Raw image blending can be perfectly done in post, outside the camera...
And why put functions in ML which can be easily done in post.

14 to 12 conversion can be done in post too  :P
Title: Re: EekoAddRawPath
Post by: a1ex on September 25, 2014, 08:32:25 PM
Yes, but I may want to take a long exposure (say 1 minute) made up of silent pictures (say 5fps, because it's daylight and can't use longer exposures), and that would require 1.2 GB. For a 5-minute exposure, I would get a 6 GB MLV file. In this case I'd prefer to have all these frames merged in the camera, and only save a 40MB DNG. Or maybe two, if the 14 bits will not be enough.
Title: Re: EekoAddRawPath
Post by: Levas on September 25, 2014, 08:36:29 PM
Ah, that's useful indeed.
Sort of neutral density filter replacement for photos.
I can see myself using it for photo's of moving water or crowded places in the city(and get a photo with no people on it)
Title: Re: EekoAddRawPath
Post by: a1ex on September 25, 2014, 08:43:18 PM
Speaking of crowded places: since we have add, sub, min, max and scaling, can you figure out how to compute the median, just from these operations?

(the median is good at removing the people from stacked images)
Title: Re: EekoAddRawPath
Post by: nikfreak on September 25, 2014, 08:58:28 PM
Quote from: Levas on September 25, 2014, 08:13:09 PM
...
14 to 12 conversion can be done in post too  :P

Dunno if you guys are talking bout raw but corect me if i am wrong (we had a long thread which is closed now):
12 is less data so needs less time to write to ram/sdcard.
Title: Re: EekoAddRawPath
Post by: 1% on September 25, 2014, 09:07:51 PM
It could be fast enough for real time if a smaller frame takes less than 41.66ms to convert.
Title: Re: EekoAddRawPath
Post by: Levas on September 25, 2014, 09:14:18 PM
"12 is less data so needs less time to write to ram/sdcard"
                      :D
That's why I put the  :P behind my words...


Add, sub, min, max, and scaling...median :-\
Edit: only way to do median if you have the data of all the pictures, the whole set...
Only two pictures can be processed at a time I guess ?

Did one of you already succeed in doing this functions on smaller frames ?
No doubt this function is used by canon to do in camera double exposures and noise reduction on long exposures.
But can it also handle smaller frames ?
Title: Re: EekoAddRawPath
Post by: chris_overseas on September 27, 2014, 12:52:43 AM
I think this is about the best that can be done for finding (an approximation of) the median without having to store all the frames:

http://web.ipac.caltech.edu/staff/fmasci/home/statistics_refs/Remedian.pdf

Multiple frames of storage are still required though. With three frames the median is easy to compute, max(A, min(B, C)). Three frames may not be enough but it might be worth a try?
Title: Re: EekoAddRawPath
Post by: a1ex on September 27, 2014, 12:29:46 PM
Great find.

BTW, max(5,min(4,3)) is not median.
Title: Re: EekoAddRawPath
Post by: chris_overseas on September 27, 2014, 02:20:33 PM
Argh good point - that'll teach me for trying to use my brain too late at night. How about this one:

median = max(min(A,B), min(max(A,B),C)) Though that will require another temporary buffer (unless there's a way to set A=min(A,B), B=max(A,B) without each computation affecting the other? Presumably that would require trickery like row-by-row access and a row sized buffer).

Alternatively:

median = (A + B + C) - min(A, min(B,C)) - max(A, max(B,C)) But that has the same problem as above, plus overflow issues.
Title: Re: EekoAddRawPath
Post by: a1ex on September 27, 2014, 02:26:46 PM
On 5D3 I think you can allocate 5 buffers at once: http://www.magiclantern.fm/forum/index.php?topic=12528.msg120670#msg120670

so the first formula looks doable.

This may also provide some hints: http://ndevilla.free.fr/median/median/src/optmed.c

(I guess PIX_SORT(a,b) can be implemented as [min,max] = MINMAX(a, b) with a temporary buffer)
Title: Re: EekoAddRawPath
Post by: chris_overseas on September 27, 2014, 03:10:26 PM
Quote from: a1ex on September 27, 2014, 02:26:46 PM
(I guess PIX_SORT(a,b) can be implemented as [min,max] = MINMAX(a, b) with a temporary buffer)

Indeed, the opt_med3() formula is exactly equivalent to max(min(A,B), min(max(A,B),C))
Title: Re: EekoAddRawPath
Post by: g3gg0 on October 01, 2014, 11:29:29 PM
i did some research.

from what i understand, there are three pixel modes:

0 - 10 bit per pixel
1 - 12 bit per pixel
2 - 14 bit per pixel
3 - 16 bit per pixel


the function you found
  void EekoAddRawPath(void *img_a, void *img_b, void *img_out, int op, int minus_off_a, int gain_a, int black_a, int gain_b, int black_b, int div8, int out_off, void (__cdecl *cbr)(void *), void *cbr_arg)

it fills these structs with important stuff

struct struc_eeko_opts_shad // for image [a]
{
    uint16_t SHAD_PRESETUP_14_12; // set to -black_a
    uint16_t SHAD_POSTSETUP_14_12; // set to black_a
    uint32_t SHAD_MODE; // set to 0
    uint32_t SHAD_GAIN; // amplify by (gain / 1024)
    uint32_t SHAD_RMODE; // set to 1
} // probably sets up: out = (in - black) * gain + black

struct struc_eeko_opts // one for image [b, out]
{
    uint16_t presetup;
    uint16_t gain; // amplify by (gain / 128)
    uint16_t zero; // what does it do?
    uint16_t postsetup;
    uint16_t mask; // probably saturated maximum value
    uint16_t unused;
}

struct struc_eeko_img_ptr // one per image [a, b, out]
{
    void *ptr;
    uint32_t pixel_mode; // hardcoded to 2 - 14 bit per pixel
}



the opts_out.gain is being hardcoded to 0x80, which results in a gain by 0.5 - this means the addition result is divided by two. (averaging)


then it calls:

int EekoAddRawPath_Start(int a1, struct struc_eeko_img_ptr *img_a, struct struc_eeko_img_ptr *img_b, struct struc_eeko_img_ptr *img_out, int xsize, int ysize, int out_mode, struc_eeko_opts_shad *opts_shad, struc_eeko_opts *opts_img_b, int minus_off_a, struc_eeko_opts *opts_img_out, void (__cdecl *cbr)(void *), int cbr_ctx)

with out_mode==2, so probably hardcoded to 14 bit per pixel output here *too*.
xsize and ysize are read from pDsDefine and are the MEM1 resolution.

EekoAddRawPath_Start now copies all onto stack again, sets the callback and calls the real processing function.
it checks which bit mode per in/out image is set and sets up the engines properly.

i would say: can you cache hack/manually reimplement the calling function and replace 2 by 1 for 12 bit? :)



edit: updated as suggested by a1ex
Title: Re: EekoAddRawPath
Post by: nikfreak on October 01, 2014, 11:54:14 PM
simply wow!  8)
Title: Re: EekoAddRawPath
Post by: ItsMeLenny on October 02, 2014, 03:37:05 AM
Just adding a comment so I can follow this post.

And I'm available to test on 550D if it makes it to it. (550D is not Digic 5).
Title: Re: EekoAddRawPath
Post by: a1ex on October 02, 2014, 11:43:06 AM
Quote from: nikfreak on October 01, 2014, 11:54:14 PM
simply wow!  8)

Can't wait to try :D
Title: Re: EekoAddRawPath
Post by: g3gg0 on October 02, 2014, 05:30:02 PM
updated some details about the struc_eeko_opts struct.
gain is relative to 0x80 (128) for the image_b and image_out "engine part", whereas the shad parameter which is used for image_a is relative to 0x400 (1024)
Title: Re: EekoAddRawPath
Post by: 1% on October 02, 2014, 07:36:31 PM
Which path constructs LV raw? maybe cache hacking there would be the simplest way to achieve 12 or 10 bit for video without re inventing the wheel.
Title: Re: EekoAddRawPath
Post by: g3gg0 on October 02, 2014, 07:44:43 PM
as soon we understand the things, we can do that ;)
yet this is finding which of the 480 small wheels on the car do what.
Title: Re: EekoAddRawPath
Post by: g3gg0 on October 02, 2014, 07:49:38 PM
by the way, the LV_RAW_type_C0F37014 register is also involved when converting.
its set to 0x500 or 0x1C00. on multiple exposures its only 0x1C00 for the last exposure.
Title: Re: EekoAddRawPath
Post by: g3gg0 on October 02, 2014, 08:00:44 PM
here are the functions how i reversed them in IDA, just for reference.

edit: http://pastebin.com/sCxBeF87

these enums make the code a bit more readable:

; enum enum_pixelmode
enum_pixelmode_10bpp  EQU 0
enum_pixelmode_12bpp  EQU 1
enum_pixelmode_14bpp  EQU 2
enum_pixelmode_16bpp  EQU 3
enum_pixelmode_4  EQU 4
enum_pixelmode_5  EQU 5
enum_pixelmode_6  EQU 6
enum_pixelmode_7  EQU 7

; enum enum_eeko_operation
enum_eeko_op_ADD  EQU 0
enum_eeko_op_SUB  EQU 1
enum_eeko_op_MAX  EQU 2
enum_eeko_op_MIN  EQU 3

; enum enum_eeko_chain
enum_eeko_chain_DSUNPACK  EQU 0
enum_eeko_chain_UNPACK32  EQU 1
enum_eeko_chain_ADUNPACK  EQU 2
enum_eeko_chain_UNK2  EQU 3
enum_eeko_chain_UNK3  EQU 4

; enum enum_eeko_outchain
enum_eeko_outchain_PACK32  EQU 0
enum_eeko_outchain_PACK16  EQU 1
enum_eeko_outchain_UNK  EQU 2
Title: Re: EekoAddRawPath
Post by: a1ex on October 03, 2014, 02:19:17 PM
Minor correction:


struct struc_eeko_opts
{
  __int16 minus_black_b;
  __int16 gain;
  __int16 zero;
  __int16 black;
  __int16 mask;
  __int16 field_A;
};
Title: Re: EekoAddRawPath
Post by: g3gg0 on October 03, 2014, 03:38:27 PM
yeah thanks!
this should fix the 32 bit assignment aligned to the 16 bit element "mask" in _PrepareAsync then.
edit: i meant EekoAddRawPath directly, but it didnt help :D

the "minus_black_b" and "black", i would tend to call them PRESETUP and POSTSETUP just like the SHAD pendant.
looks like the value to add *before* processing data and the one to add *after* processing.

just like we do with the black level when scaling: out = (in + (-black)) * gain + black

what do you think?
Title: Re: EekoAddRawPath
Post by: a1ex on October 03, 2014, 09:22:59 PM
Makes sense.
Title: Re: EekoAddRawPath
Post by: g3gg0 on October 04, 2014, 12:20:31 AM
updated the decompiled code, placed on pastebin.
added some enums to make code better understandable
Title: Re: EekoAddRawPath
Post by: a1ex on October 05, 2014, 04:46:58 PM
Some experiments:

- pixel_mode is indeed 10/12/14/16 bits per pixel
- all 3 images seem to require the same bit depth (semaphore timeout otherwise)
- problem: in 10/12-bit modes, the image data is still interpreted as 14-bit, but it only processes 12/14 or 10/14 of a full raw size
- 16-bit mode is interpreted correctly (little endian, can be saved directly as DNG without swapping byte order)
- I wasn't able to change the processing size (seems hardcoded to 5936x3952; using 5936x3950 resulted in timeout at the semaphore
=> obviously, the 16-bit processing overflows and locks up the camera (with chances of bricking it)
SOLVED!
- out_mode seems to have no effect; valid values are 1 and 2, otherwise it throws an error at at ./PathDrv/EekoAddRawPathCore.c:893
- .zero fields seem to be some sort of gain (valid values 0...7, didn't check the exact meaning yet, probably it's in stops)
- .rmode doesn't add if set to 0 (it appears to copy one of the source images, didn't check the exact meaning)

Some very rough test code: eeko_test2.c (to be ran on top of fullres-silent-pics branch)
https://www.dropbox.com/sh/k037ulitl3kkf0t/AABXZYcTQYDqvqMQ-bNrHRtpa?dl=0

More details coming soon.
Title: Re: EekoAddRawPath
Post by: nikfreak on October 05, 2014, 09:30:16 PM
Besides of knowing this is all experimental: would it be possible to use 10/12bit also for normal shooting (not liveview)? Or do the discoveries (eeko) only apply to liveview which I actually think.

Title: Re: EekoAddRawPath
Post by: 1% on October 05, 2014, 09:46:36 PM
can the pixel mode be set for create/capture test image? I see 16bit mode is only used for faCalculateVProjection
Title: Re: EekoAddRawPath
Post by: a1ex on October 06, 2014, 09:47:54 AM
@nikfreak: I'm running the test on the full-res silent picture framework, but I expect it to apply to both stills and LiveView.

I was able to change the size - just had to make sure the height is mod16. Maybe mod8 and mod4 might work as well, didn't try.

16-bit processing on half of the image:
(http://a1ex.magiclantern.fm/bleeding-edge/eeko/01300088.jpg)
DNG (http://a1ex.magiclantern.fm/bleeding-edge/eeko/01300088.DNG)

(the last strip is beyond a 14-bit full raw size, which is the maximum size we can allocate without fragmentation)
Title: Re: EekoAddRawPath
Post by: g3gg0 on October 06, 2014, 10:49:38 AM
depending on the bit width of the images, every edmac is set up with transfer sizes related to it.
so there are transfer sizes for 12, 14 and "anything else" widths.
some constant for other edmac registers lead to the conclusion that the "block" size is 8 bytes. (it sets up Xn etc).


((x*y*bpp_a / 8) % 8 == 0) && ((x*y*bpp_b / 8) % 8 == 0)


maybe thats the limitation and then processing in various bit widths is possible?
Title: Re: EekoAddRawPath
Post by: a1ex on October 06, 2014, 11:10:18 AM
Correct.

The mystery now is how to interpret the data as 12- or 10-bit, rather than just limiting the transfer size.

Will try to get a detailed log of what these routines do.
Title: Re: EekoAddRawPath
Post by: g3gg0 on October 06, 2014, 11:52:01 AM
i cannot folow you.
dont the engines write 12 bit streams when configuring them with pixelmode for 12bpp?
Title: Re: EekoAddRawPath
Post by: a1ex on October 06, 2014, 03:12:22 PM
Nope, they still interpret the data as 14-bit. Only the processed size gets smaller (12/14 of a full raw size, or 10/14).
Title: Re: EekoAddRawPath
Post by: DavidSh on October 06, 2014, 03:46:15 PM
10 bit raw is absolutely WOW!
Cant wait.
Title: Re: EekoAddRawPath
Post by: a1ex on October 08, 2014, 02:54:13 PM
Here's a matrix showing the problem at lower bit depths:

(http://a1ex.magiclantern.fm/bleeding-edge/eeko/matrix.jpg)

Left to right: operand A, operand B, pixel mode (from eeko_img_ptr, all 3 buffers) = 0, 1, 2 and 3
Top to bottom: input data and DNG set to 10, 12, 14 and 16 bits (see raw_get_pixel_x / raw_set_pixel_x for the exact encoding)
Processing size set to 3/4 of a full image size (to avoid overflow in 16-bit mode).

The first two columns can be regarded as a sanity test for raw_get_pixel_x / raw_set_pixel_x (for all 4 bit depths).

Expected result: clean image on the diagonal (where the bit depts of input data and DNG matches the bit depth parameter of the eeko routine), gibberish on all other positions.
Actual result:
- clean image on last row, last column => 16-bit OK
- clean image on the 14-bit row and the 14-bit column => 14-bit OK
- clean image on the 14-bit row and the 10-bit and 12-bit columns => low bit depths misinterpreted as 14-bit data by the eeko routine
- same type of distortion on the 12-bit row, 10/12-bit columns
- same type of distortion on the 10-bit row, 10/12-bit columns
- different type of distortion in the last column, 10/12/14-bit rows (expected)

=> the 12-bit and 10-bit data are both misinterpreted as 14-bit data, but the processing size (line pitch) is correct.

Here you can find:
- test code (requires branches fullres-silent-pics, dm-spy-experiments and 5D3-123)
- DNG outputs, conversion to JPEG and matrix view
- detailed log (eeko-dm.log) which covers all the 16 test cases
https://www.dropbox.com/sh/vomj5nuvkng8d9b/AABdmwtj-Br9U10kZCKRlE8ea?dl=0
Title: Re: EekoAddRawPath
Post by: senzazn12 on October 08, 2014, 05:55:58 PM
So 16 bit RAW might be more possible in the future than 10 and 12 bit?
Title: Re: EekoAddRawPath
Post by: kyrobb on October 11, 2014, 12:25:16 AM
Hmmm. 10 bit raw would be just awesome.
Title: Re: EekoAddRawPath
Post by: tin2tin on October 11, 2014, 06:20:14 PM
@a1ex Trying to get my head around this exciting stuff. Black magic it seems.  :P Are the 10 and 12 bit images read correctly from a computer? In other words is the distortion caused by the misinterpretation by the eeko DNG reading or writing routine, or do you think something else is causing the distortion?
Title: Re: EekoAddRawPath
Post by: bjacklee on October 12, 2014, 02:21:35 PM
Hi, If 10 bit raw can be implemented successfully, would it be possible for the 6d to have continuos 1080p 10bit raw? or 6d write speed still not enough to have it continuos?
Title: Re: EekoAddRawPath
Post by: Levas on October 12, 2014, 02:55:42 PM
Not in 16:9 aspect ratio. Almost continuous in 2.39 ratio. That is with 25 frames per second.

16:9 -> 1792 x 1008 (max resolution 6d) x 10bit x 25 frames per second / 8(bits to bytes conversion) / 1000000(bytes to Megabytes conversion = 56MB/s writing speed needed (6d can do about 40MB/s)

2:39 -> 1792 x 750 x 10bit x 25 frames per second / 8(bits to bytes conversion) / 1000000(bytes to Megabytes conversion = 42MB/s writing speed needed, with the memory buffer the 6d has, it can do this for about 2 minutes before the buffer is full and frameskipping starts.

2.39 -> 1728 x 720 x 10bit x 25 frames per second / 8(bits to bytes conversion) / 1000000(bytes to Megabytes conversion = 39MB/s = continuous
Title: Re: EekoAddRawPath
Post by: kitor on October 12, 2014, 03:07:25 PM
But bytes to megabytes should be 1048576 (1024x1024, not 1000x1000), that gives 40.05MB/s in 1792x750. So maybe even more than 2 minutes.
Title: Re: EekoAddRawPath
Post by: g3gg0 on October 12, 2014, 06:20:08 PM
please lets keep the discussion at a deep technical level.
i.e. which engines get configured for setting up bit depth conversion - and how it can be done.
Title: Re: EekoAddRawPath
Post by: ItsMeLenny on December 23, 2014, 01:17:29 AM
Did this get any further in the technical sense, or did it dead end?
Title: Re: EekoAddRawPath
Post by: pursehouse on January 28, 2015, 04:27:21 AM
Yes the ( ( oldAvg * count - 1 ) + newValue ) / count )
is the lowest-overhead way I know of to keep track of processing an average over time.
That way you only need the resulting image buffer (which gets altered in-place), and the incoming image buffer.
Assuming it is possible to alter an image buffer directly rather than having to write to a brand new one?

It looks like you guys delved into this quite a bit a few months ago, is it still in action?
Title: Re: EekoAddRawPath
Post by: Greg on September 13, 2016, 12:34:19 AM
Subtracting frames low-level code (works on DIGIC IV) :
Process time similar to raw bit depth reduction (few milliseconds more).


static void sub_dark(struct raw_info * raw_info, void* buf1, void* buf2, void* buf3)
{
    uint32_t edmac_read_chan = 0xa;
    uint32_t edmac_read_chan2 = 0xb;
    uint32_t edmac_write_chan = 3;
    uint32_t edmac_read_connection = 0;
    uint32_t edmac_read_connection2 = 1;
    uint32_t edmac_write_connection = 0x10;

    /* it locks up without reslock */
    if (!resLock)
    {
        int edmac_read_ch_index = edmac_channel_to_index(edmac_read_chan);
        int edmac_read_ch_index2 = edmac_channel_to_index(edmac_read_chan2);
        int edmac_write_ch_index = edmac_channel_to_index(edmac_write_chan);
        uint32_t resIds[] = {
            0x00000003 | edmac_write_ch_index,       /* write edmac channel */
            0x00010002 | edmac_read_ch_index,        /* read edmac channel */
            0x00010003 | edmac_read_ch_index2,       /* read edmac channel */
            0x00020010 | edmac_write_connection,     /* write connection */
            0x00030000 | edmac_read_connection,      /* read connection */
            0x00030001 | edmac_read_connection2,     /* read connection */
            0x00050002,                              /* DSUNPACK? */
            0x00050003,                              /* UNPACK24? */
            0x00050006,                              /* DARK? */
            0x0005001d,                              /* PACK16/WDMAC16 */
            0x0005001f,                              /* PACK16/WDMAC16 */
        };
        resLock = CreateResLockEntry(resIds, COUNT(resIds));
    }
   
    LockEngineResources(resLock);

    /* configure image processing modules */
    /* register addresses from strings */
    const uint32_t DS_SEL           = 0xC0F08104;
    const uint32_t PACK16_ISEL      = 0xC0F082D0;
    const uint32_t WDMAC16_ISEL     = 0xC0F082D8;
   
    const uint32_t DSUNPACK_ENB     = 0xC0F08060;
    const uint32_t DSUNPACK_MODE    = 0xC0F08064;
    const uint32_t DSUNPACK_DM_EN   = 0xC0F08274;
   
    const uint32_t UNPACK24_ENB     = 0xC0F08070;
    const uint32_t UNPACK24_MODE    = 0xC0F08074;
    const uint32_t UNPACK24_DM_EN   = 0xC0F08260;
   
    const uint32_t DARK_ENB         = 0xC0F08000;
    const uint32_t DARK_MODE        = 0xC0F08004;
    const uint32_t DARK_SETUP       = 0xC0F08008;
    const uint32_t DARK_LIMIT       = 0xC0F0800C;
    const uint32_t DARK_SETUP_14_12 = 0xC0F08010;
    const uint32_t DARK_LIMIT_14_12 = 0xC0F08014;
    const uint32_t DARK_SAT_LIMIT   = 0xC0F08018;
    const uint32_t DARK_KZMK_SAV_A  = 0xC0F082A0;
    const uint32_t DARK_KZMK_SAV_B  = 0xC0F082A4;
   
    const uint32_t PACK16_ENB       = 0xC0F08120;
    const uint32_t PACK16_MODE      = 0xC0F08124;
    const uint32_t PACK16_DEFM_ON   = 0xC0F082B8;
    const uint32_t PACK16_ILIM      = 0xC0F085B4;

    /* for PACK16_MODE, DSUNPACK_MODE, ADUNPACK_MODE (mask 0x131) */
    const uint32_t MODE_14BIT       = 0x030;

    engio_write((uint32_t[]) {
        /* input selection for the processing modules? */
        DS_SEL,         0,
        PACK16_ISEL,    0,
        WDMAC16_ISEL,   0,
       
        /* DSUNPACK module (input image data) */
        DSUNPACK_ENB,   0x80000000,
        DSUNPACK_MODE,  MODE_14BIT,
        DSUNPACK_DM_EN, 0,
       
        /* UNPACK24 module (input image data?) */
        UNPACK24_ENB,   0x80000000,
        UNPACK24_MODE,  MODE_14BIT,
        UNPACK24_DM_EN, 0,
       
        /* DARK module */
        DARK_ENB,       0x80000000,
        DARK_MODE,      0x11014,
        DARK_SETUP,     0x110,
        DARK_LIMIT,     0x7c,
        DARK_SETUP_14_12, 0x400,
        DARK_LIMIT_14_12, 0x3fff,
        DARK_SAT_LIMIT, 0x3fff,
        DARK_KZMK_SAV_A, 0x1,
        DARK_KZMK_SAV_B, 0x0,
       
        /* PACK16 module (output image data) */
        PACK16_ENB,     0x80000000,
        PACK16_MODE,    MODE_14BIT,
        PACK16_DEFM_ON, 1,
        PACK16_ILIM,    0x3FFF, /* white level? */
       
        /* whew! */
        0xFFFFFFFF,     0xFFFFFFFF
    });

    /* EDMAC setup */
    RegisterEDmacCompleteCBR(edmac_read_chan, edmac_read_complete_cbr, 0);
    RegisterEDmacCompleteCBR(edmac_read_chan2, edmac_read_complete_cbr, 0);
    RegisterEDmacCompleteCBR(edmac_write_chan, edmac_write_complete_cbr, 0);

    ConnectWriteEDmac(edmac_write_chan, edmac_write_connection);
    ConnectReadEDmac(edmac_read_chan, edmac_read_connection);
    ConnectReadEDmac(edmac_read_chan2, edmac_read_connection2);

    struct edmac_info src_edmac_info = {
        .xb = raw_info->pitch,
        .yb = raw_info->height - 1,
    };
   
    SetEDmac(edmac_read_chan, buf1, &src_edmac_info, 0x20000);
    SetEDmac(edmac_read_chan2, buf2, &src_edmac_info, 0x20000);
    SetEDmac(edmac_write_chan, buf3, &src_edmac_info, 1);

    /* time the operation */
    info_led_on();
    int64_t t0 = get_us_clock_value();
   
    /* start processing */
    StartEDmac(edmac_write_chan, 0);

    /* start operation */
    engio_write((uint32_t[]) {
        PACK16_ENB,     1,
        DARK_ENB,       1,
        DSUNPACK_ENB,   1,
        UNPACK24_ENB,   1,
        0xFFFFFFFF,     0xFFFFFFFF
    });
   
    StartEDmac(edmac_read_chan, 2);
    StartEDmac(edmac_read_chan2, 4);
   
    /* wait for everything to finish */
    take_semaphore(edmac_write_done_sem, 0);

    int64_t t1 = get_us_clock_value();
    info_led_off();
   
    /* finished */
   
    /* reset processing modules */
    engio_write((uint32_t[]) {
        PACK16_ENB,     0x80000000,
        DARK_ENB,       0x80000000,
        DSUNPACK_ENB,   0x80000000,
        UNPACK24_ENB,   0x80000000,
        0xFFFFFFFF,     0xFFFFFFFF
    });

    UnregisterEDmacCompleteCBR(edmac_read_chan);
    UnregisterEDmacCompleteCBR(edmac_read_chan2);
    UnregisterEDmacCompleteCBR(edmac_write_chan);
    UnLockEngineResources(resLock);

    printf("Process time: %d us\n", (int)(t1 - t0));

    /* update raw_info */
    raw_info->buffer = buf3;
}


Question is how add frames on DIGIC IV.
Digic V uses the unknown module that is not available on DIGIC IV.
Title: Re: EekoAddRawPath
Post by: a1ex on November 22, 2016, 12:37:53 AM
Just tinkering.


call("FA_SetCRawBitNum", 14);
call("FA_SubtractTestImage", src1, src2, dst);


60D-FA_SubtractTestImage.log (http://a1ex.magiclantern.fm/bleeding-edge/capture/60D-FA_SubtractTestImage.LOG)


call("FA_MaxSelectTestImage", src1, src2, dst, 1);


60D-FA_MaxSelectTestImage.log (http://a1ex.magiclantern.fm/bleeding-edge/capture/60D-FA_MaxSelectTestImage.LOG)
Title: Re: EekoAddRawPath
Post by: a1ex on November 29, 2016, 12:29:21 AM
Two days ago, g3gg0 found out what this Eeko is: a small DryOS core running Thumb-2 code (somewhat similar to Digic 6). We started analyzing this core, and today I've committed our initial findings (https://bitbucket.org/hudson/magic-lantern/commits/all?search=branch(qemu)+%26+keyword(eeko)), in executable format (QEMU).

My initial reaction was something like this (http://www.youtube.com/watch?v=f38SupsC93E). I mean, we want to hack those multi-core Digic 6 cameras, but we didn't even know about the existence of a Thumb-2 core on 5D3?! It was hiding in plain sight!

(just run "strings" on your ROM dump from any DIGIC 5 camera, and look for DryOS version)

Some notes:
- it's started on request, see Eeko WakeUp (e.g. when taking a picture, or when developing a raw image in play mode)
- it's shut down when it's no longer needed (StopEeko)
- its memory is populated from the main CPU, using EekoBltDmac (similar to dma_memcpy), as follows (5D3 1.1.3):

# on startup (Eeko Initialized:Async)
ShootCaptu:000afe98:00:00: *** EekoBltDmac_copy(0x0, 0x1e00000, 0xff8bf888, 0x4ef14, 0xff217de8, 0x0), from ff217e34
# on each use (Eeko WakeUp):
systemtask:000afe98:00:00: *** EekoBltDmac_copy(0x0, 0xd0288000, 0xff99541c, 0x6b8c, 0xff508e78, 0x0), from ff508f30
systemtask:000afe98:00:00: *** EekoBltDmac_copy(0x0, 0xd0280000, 0xff99bfa8, 0x1bc, 0xff508e78, 0x0), from ff508fd0
systemtask:000afe98:00:00: *** EekoBltDmac_copy(0x0, 0x1e80000, 0xff99c164, 0x10e8, 0xff508e78, 0x0), from ff508fd0


The contents written at 0xd0288000 are mapped at address 0 on the Eeko CPU. The contents at 0xd0280000 are mapped to some unknown location (my guess was 0x40000000, not confirmed).

Memory at 0x1e00000 is mapped at the same address on both CPUs, until 0x1F40000.

Memory regions:

00000050: MCR p15,0,Rd,cr6,cr2,0:       RGNR <- 0x0       
0000005C: MCR p15,0,Rd,cr6,cr1,0:      DRBAR <- 0x0       
00000064: MCR p15,0,Rd,cr6,cr1,2:       DRSR <- 0x3F       (0x100000000)
0000006C: MCR p15,0,Rd,cr6,cr1,4:      DRACR <- 0x308      (P:RW U:RW; Outer and Inner Non-cacheable; Non-shared)
00000074: MCR p15,0,Rd,cr6,cr2,0:       RGNR <- 0x1       
0000007C: MCR p15,0,Rd,cr6,cr1,0:      DRBAR <- 0xC0000000
00000084: MCR p15,0,Rd,cr6,cr1,2:       DRSR <- 0x3B       (0x40000000)
0000008C: MCR p15,0,Rd,cr6,cr1,4:      DRACR <- 0x310      (P:RW U:RW; Non-shareable Device; )
00000094: MCR p15,0,Rd,cr6,cr2,0:       RGNR <- 0x2       
0000009C: MCR p15,0,Rd,cr6,cr1,0:      DRBAR <- 0x1E00000 
000000A4: MCR p15,0,Rd,cr6,cr1,2:       DRSR <- 0x27       (0x100000)
000000AC: MCR p15,0,Rd,cr6,cr1,4:      DRACR <- 0x30B      (P:RW U:RW; Outer and Inner write-back, write-allocate; Non-shared)
000000B4: MCR p15,0,Rd,cr6,cr2,0:       RGNR <- 0x3       
000000BC: MCR p15,0,Rd,cr6,cr1,0:      DRBAR <- 0x1F00000 
000000C4: MCR p15,0,Rd,cr6,cr1,2:       DRSR <- 0x21       (0x20000)
000000CC: MCR p15,0,Rd,cr6,cr1,4:      DRACR <- 0x30B      (P:RW U:RW; Outer and Inner write-back, write-allocate; Non-shared)
000000D4: MCR p15,0,Rd,cr6,cr2,0:       RGNR <- 0x4       
000000DC: MCR p15,0,Rd,cr6,cr1,0:      DRBAR <- 0x1F20000 
000000E4: MCR p15,0,Rd,cr6,cr1,2:       DRSR <- 0x21       (0x20000)
000000EC: MCR p15,0,Rd,cr6,cr1,4:      DRACR <- 0x310      (P:RW U:RW; Non-shareable Device; )


Tasks:

      init:01e022cf:task_create(entry=1e021c7, 0, 0, stack=400, 0, 0, errLogTask)
      init:01e01d5f:task_create(systemtask, prio=3, stack=400, entry=1e01c47, arg=0)
      init:01e01d81:task_create(PowerMan, prio=6, stack=100, entry=1e01ca7, arg=0)
      init:01e1a69d:task_create(StillTask, prio=4, stack=1000, entry=1e1a5a3, arg=0)
      init:01e1bd95:task_create(IvaTask, prio=4, stack=1000, entry=1e1bccf, arg=0)
      init:01e1cb5d:task_create(AppTask1, prio=4, stack=1000, entry=1e1cac9, arg=0)
      init:01e420a1:task_create(ColorTask, prio=5, stack=5000, entry=1e41f9b, arg=0)


UART is set up at 0xD98, with baud = 115200, MMIO registers at 0xC0810000. It has a small debug menu (navigable in QEMU):

(http://a1ex.magiclantern.fm/bleeding-edge/eeko/eeko-console-qemu.png)

Interesting strings:


DRYOS version 2.3, release #0044
Copyright (C) 1997-2009 by CANON Inc.


- DryOS basics (tasks, meminfo, init_task, panic, message queues...)
- postman (probably communication with main CPU)
- LvWbAlgor.c, LvWbHist.c, WbAlgor.c, ObWbReg.c, ClrVignettingCorrection.c, ClrChromaAberrationCorrection.c, FaceAWB.c
- FaceAwbReliability, NormalAwbReliability, FaceSimilarity, NormalSimilarity, LuminanceSimilarity, ShootingInfoSimilarity, ColorSimilarity, TvSimilarity, AvSimilarity...
- CurveOut[NUMBER_OF_OLDFRAMECURVE_OUT - 1] = 0
- ApollonParam, SusanNrParam, AngelParam (guess: modules used for raw to jpeg conversion)
- IVA_ELDMain.c, IVA_BsBuf.c, EekoIvaEncPath.c (video encoder related)
- EekoAddRawPath.c, EekoSsrawToYuvLvPath.c, EekoYuvToVramPath.c, EekoDistortPath.c, EekoEDmacCopyPath.c, EekoEDmac.c...
- EekoJpCore.c, JpCoreEncodeComplete

That's it for now.
Title: Re: EekoAddRawPath
Post by: Andy600 on November 29, 2016, 11:53:13 AM
Can we assume that DryOS 2.3 is likely in Digic4 cams too because the copyright notice is dated pre-Digic5? Those strings look VERY interesting!  ;D
Title: Re: EekoAddRawPath
Post by: a1ex on November 29, 2016, 01:46:14 PM

500D:   DRYOS version 2.3, release #0023
50D:    DRYOS version 2.3, release #0023
5D2:    DRYOS version 2.3, release #0023
A1100:  DRYOS version 2.3, release #0031
7D:     DRYOS version 2.3, release #0039
550D:   DRYOS version 2.3, release #0043
1100D:  DRYOS version 2.3, release #0047
1200D:  DRYOS version 2.3, release #0047
600D:   DRYOS version 2.3, release #0047
60D:    DRYOS version 2.3, release #0047
5D3:    DRYOS version 2.3, release #0049
650D:   DRYOS version 2.3, release #0049
700D:   DRYOS version 2.3, release #0049
EOSM:   DRYOS version 2.3, release #0049
100D:   DRYOS version 2.3, release #0051
6D:     DRYOS version 2.3, release #0051
70D:    DRYOS version 2.3, release #0051
5D4AE:  DRYOS version 2.3, release #0055
7D2M:   DRYOS version 2.3, release #0055
7D2S:   DRYOS version 2.3, release #0055
EOSM3:  DRYOS version 2.3, release #0055
750D:   DRYOS version 2.3, release #0057
760D:   DRYOS version 2.3, release #0057
1300D:  DRYOS version 2.3, release #0058
5D4:    DRYOS version 2.3, release #0058
80D:    DRYOS version 2.3, release #0058

Eeko:
100D:   DRYOS version 2.3, release #0044
5D3:    DRYOS version 2.3, release #0044
650D:   DRYOS version 2.3, release #0044
6D:     DRYOS version 2.3, release #0044
700D:   DRYOS version 2.3, release #0044
70D:    DRYOS version 2.3, release #0044
EOSM:   DRYOS version 2.3, release #0044

Eeko maybe:
750D:   DRYOS version 2.3, release #0055
760D:   DRYOS version 2.3, release #0055
Title: Re: EekoAddRawPath
Post by: Levas on November 29, 2016, 01:56:35 PM
That last list of cameras, Eeko: Dryos...looks like digic 5
Title: Re: EekoAddRawPath
Post by: a1ex on November 29, 2016, 03:32:01 PM
Eeko dumper:

void (*EekoBltDmac_copy)(int zero, uint32_t dst, uint32_t src, uint32_t size, void* cbr, int arg)
    = (void*) 0xFF3B940C;   /* 5D3 1.1.3 */

call("StartupEeko");
void * buf = malloc(0x8000);

dump_seg((void*)0x1E00000, 0x140000, "1E00000.DMP");

EekoBltDmac_copy(0, (uint32_t)buf, 0xD0288000, 0x8000, ret_0, 0);
dump_seg(buf, 0x8000, "D0288000.DMP");

EekoBltDmac_copy(0, (uint32_t)buf, 0xD0280000, 0x8000, ret_0, 0);
dump_seg(buf, 0x8000, "D0280000.DMP");

free(buf);
call("TerminateEeko");
Title: Re: EekoAddRawPath
Post by: a1ex on November 29, 2016, 09:02:05 PM
Compared dumps before / after / 1 second after starting the Eeko core, 2 attempts:

- 1E00000-1E80000: identical in all 6 attempts
- 1E80000-1EE0000: some differences (data RAM?)
- 1F28C4C int32: changed at every experiment (timer?)
- 1F20C50 int32: before 0, after 0xFF55AA00
- 1F27840-1F28000: before FF, after 0
- D0280000 (mapped to 0x40000000)
  - before:
     - D0280000-D02801BE: identical in both attempts
     - many differences until D0288000 (noise?)
  - after:
     - D0280000-D0280080: small changes
     - D0280080-D0282120: identical (populated during startup)
     - D0282120-D0284000: noise
     - D0284000-D0288000: identical to D028C000-D0290000
- D0288000 (mapped to 0):
  - D0288000-D028EB8C: identical in all 6 attempts
  - D028EB8C-D0290000: noise
  - attempt to dump past D0289000 => camera lockup
Title: Re: EekoAddRawPath
Post by: tron on November 30, 2016, 01:51:57 PM
Sorry for being half-off topic but what is with 5D4AE vs 5D4 and 7D2S vs 7D2M ?
Title: Re: EekoAddRawPath
Post by: nikfreak on November 30, 2016, 02:02:45 PM
Just guessing:
AE - Autofocus Engine
S - Slave (CPU)
M - Master (CPU)
Title: Re: EekoAddRawPath
Post by: tron on November 30, 2016, 02:26:25 PM
Thanks
Title: Re: EekoAddRawPath
Post by: g3gg0 on December 02, 2016, 02:16:43 AM
if someone wants to run own code on EEKO, there is some example:


static void run_test()
{
    uint32_t eeko_ram = 0xD0288000;
    uint32_t eeko_ram_counter = eeko_ram + 0x10;
   
    console_clear();
    console_show();
   
    printf("Init...\n");
    MEM(0xC0223210) = 0x7E;
    MEM(0xC022320C) = 0x06;
   
    /*
      now run this code:
     
        mov r0, 0x10
        mov r1, 0

        @loop:
        add r1, #1        [1 clocks]
        str r1, r0, 0     [1 clocks]
        b @loop           [3 clocks]
       
        -> 5 cpu clocks per loop

    */
    uint32_t clocks = 5;
    memcpy32(eeko_ram, "\x10\x20\x00\x21\x01\x31\x01\x60\xFC\xE7", 10);
   
    printf("Boot...\n");
    MEM(0xC022320C) = 0x07;
   
    printf("Measure...\n");
    uint32_t old_val = 0;
    while(1)
    {
        uint32_t new_val = MEM(eeko_ram_counter);
        uint32_t delta = new_val - old_val;
        uint32_t freq = delta * clocks / 10000;
       
        printf("speed: %d/sec -> %d.%d MHz\n", delta, freq / 100, freq % 100 );
        old_val = new_val;
        msleep(1000);
    }
}


please remind that the code boots in thumb mode.

thinking about how to speed up things using eeko. not that simple because in video mode it is highly used.
probably useful to speed up raw playback, but i'd prefer a engine solution instead of CPU.
Title: Re: EekoAddRawPath
Post by: a1ex on December 09, 2017, 10:25:42 PM
I have a feeling Omar (secondary DryOS core from DIGIC 6) might be the equivalent of Eeko on D5.

Why?

- they are initialized in a similar way, from the main core, on request - unlike 7D, where both cores start at power on and they wait for each other
- communication is done using "postman" routines (same name used for Eeko)
- startupPrepareCapture is waiting for it at startup, so it must have some important role in image capture and/or processing
- interrupts used 0xd, 0x2d, 0x4d, 0x6d, 0x1c, 0x3c, 0x5c, 0x7c, 0xcd, 0x9c, 0xbc, 0xdc, 0xfc (exact meaning unknown, but they follow a similar pattern).

80D:

[ROM-DMA0] at OmarI:FE1DA5EC:FE1DA6E9 [0xD6030000] -> 0x10000000: ???
[ROM-DMA1] at OmarI:FE1DA288:FE1DA273 [0xD6030040] <- 0x46FF    : count
[ROM-DMA1] at OmarI:FE1DA292:FE1DA273 [0xD6030044] <- 0xFE88871C: srcAddr
[ROM-DMA1] at OmarI:FE1DA29C:FE1DA273 [0xD6030048] <- 0xDFF00000: dstAddr
[ROM-DMA1] at OmarI:FE1DA2A6:FE1DA273 [0xD603004C] <- 0x30F00   : ???
[ROM-DMA1] at OmarI:FE1DA2B0:FE1DA273 [0xD6030050] <- 0x30F00   : ???
[ROM-DMA1] at OmarI:FE1DA2BA:FE1DA273 [0xD6030054] <- 0x2E8301  : ???
[ROM-DMA1] at OmarI:FE1DA2CC:FE1DA2C5 [0xD6030058] <- 0x11100003: Start DMA


More stuff coming soon.
Title: Re: EekoAddRawPath
Post by: a1ex on September 14, 2018, 10:03:48 AM
Omar blobs:


5D4 112:
dd if=ROM1.BIN of=00000000.BIN bs=1 skip=$((0x8762B8)) count=$((0x54D8))
dd if=ROM1.BIN of=80000800.BIN bs=1 skip=$((0x87B798)) count=$((0xE9A8))
dd if=ROM1.BIN of=01AC0000.BIN bs=1 skip=$((0x88A148)) count=$((0x98E0))
dd if=ROM1.BIN of=01AE0000.BIN bs=1 skip=$((0x893A30)) count=$((0x2B3460))

5D4 104:
dd if=ROM1.BIN of=00000000.BIN bs=1 skip=$((0x870EB8)) count=$((0x54D8))
dd if=ROM1.BIN of=80000800.BIN bs=1 skip=$((0x876398)) count=$((0xE8A8))
dd if=ROM1.BIN of=01AC0000.BIN bs=1 skip=$((0x884C48)) count=$((0x9878))
dd if=ROM1.BIN of=01AE0000.BIN bs=1 skip=$((0x88E4C8)) count=$((0x2B2460))

80D 102:
dd if=ROM1.BIN of=00000000.BIN bs=1 skip=$((0x888824)) count=$((0x4700))
dd if=ROM1.BIN of=80000800.BIN bs=1 skip=$((0x88CF2C)) count=$((0xE258))
dd if=ROM1.BIN of=01AC0000.BIN bs=1 skip=$((0x89B18C)) count=$((0xADE8))
dd if=ROM1.BIN of=01AE0000.BIN bs=1 skip=$((0x8A5F7C)) count=$((0x2898F0))


Other D6 models:

# this identifies Omar (secondary code) firmware blobs in DIGIC 6 cameras
#
# usage: scan_omar.py ROM1.BIN <srcAddr from OmarInit>
#
# Example for 5D4 112:
# ./run_canon_fw.sh 5D4,firmware="112" -d io |& grep srcAddr -a
# ...
# [XDMAC0] at OmarIni:FE26C802:FE26C7E3 [0xD6030014] <- 0xFE8762B8: srcAddr
#                                                       ^^^^^^^^^^
# python scan_omar.py 5D4/112/ROM1.BIN 0xFE8762B8

import os, sys
from struct import unpack

def getLongLE(d, a):
   return unpack('<L',(d)[a:a+4])[0]

d = open(sys.argv[1]).read()
a = int(sys.argv[2], 16) - 8
off = 0xFE000000

for i in range(4):
    target_addr = getLongLE(d, a - off)
    copied_size = getLongLE(d, a - off + 4)
    source_addr = a + 8
    print "dd if=ROM1.BIN of=%08X.BIN bs=1 skip=$((0x%08X)) count=$((0x%X))" % (target_addr, source_addr - off, copied_size)
    a = source_addr + copied_size