Read about our upcoming Code of Conduct on this issue

eos.c 174 KB
Newer Older
Antony Pavlov's avatar
Antony Pavlov committed
1
2
3
4
5
6
#include "hw/hw.h"
#include "hw/loader.h"
#include "sysemu/sysemu.h"
#include "hw/devices.h"
#include "hw/boards.h"
#include "exec/address-spaces.h"
7
#include "exec/memory-internal.h"
a1ex's avatar
a1ex committed
8
#include "exec/ram_addr.h"
Antony Pavlov's avatar
Antony Pavlov committed
9
#include "hw/sysbus.h"
a1ex's avatar
a1ex committed
10
11
12
#include "ui/console.h"
#include "ui/pixel_ops.h"
#include "hw/display/framebuffer.h"
Niklas Fejes's avatar
Niklas Fejes committed
13
#include "hw/sd/sd.h"
14
#include "sysemu/char.h"
15
#include <hw/ide/internal.h>
16
#include "hw/arm/arm.h"
Antony Pavlov's avatar
Antony Pavlov committed
17
#include "eos.h"
18
#include "dbi/logging.h"
Antony Pavlov's avatar
Antony Pavlov committed
19

20
#include "hw/eos/model_list.h"
Niklas Fejes's avatar
Niklas Fejes committed
21
22
23
24
#include "hw/eos/eos_ml_helpers.h"
#include "hw/eos/mpu.h"
#include "hw/eos/serial_flash.h"
#include "hw/eos/eos_utils.h"
Niklas Fejes's avatar
Niklas Fejes committed
25
#include "eos_bufcon_100D.h"
26
#include "hw/eos/engine.h"
27

Niklas Fejes's avatar
Niklas Fejes committed
28
#define IGNORE_CONNECT_POLL
Niklas Fejes's avatar
Niklas Fejes committed
29

30
#define DIGIC_TIMER_STEP 0x100
31
32
#define DIGIC_TIMER20_MASK (0x000FFFFF & ~(DIGIC_TIMER_STEP-1))
#define DIGIC_TIMER32_MASK (0xFFFFFFFF & ~(DIGIC_TIMER_STEP-1))
33

Niklas Fejes's avatar
Niklas Fejes committed
34
35
36
37
/* Machine class */

typedef struct {
    MachineClass parent;
38
    struct eos_model_desc * model;
Niklas Fejes's avatar
Niklas Fejes committed
39
40
} EosMachineClass;

Niklas Fejes's avatar
Niklas Fejes committed
41
42
#define EOS_DESC_BASE    "Canon EOS"
#define TYPE_EOS_MACHINE "eos"
Niklas Fejes's avatar
Niklas Fejes committed
43
44
45
46
47
#define EOS_MACHINE_GET_CLASS(obj) \
    OBJECT_GET_CLASS(EosMachineClass, obj, TYPE_EOS_MACHINE)
#define EOS_MACHINE_CLASS(klass) \
    OBJECT_CLASS_CHECK(EosMachineClass, klass, TYPE_EOS_MACHINE)

48
static void eos_init_common(MachineState *machine);
49

Niklas Fejes's avatar
Niklas Fejes committed
50
51
52
static void eos_class_init(ObjectClass *oc, void *data)
{
    MachineClass *mc = MACHINE_CLASS(oc);
53
    mc->desc = EOS_DESC_BASE;
54
    mc->init = eos_init_common;
Niklas Fejes's avatar
Niklas Fejes committed
55
}
56
static const TypeInfo canon_eos_info = {
Niklas Fejes's avatar
Niklas Fejes committed
57
58
59
60
61
62
63
64
65
    .name = TYPE_EOS_MACHINE,
    .parent = TYPE_MACHINE,
    .abstract = true,
//  .instance_size = sizeof(MachineState), // Could probably be used for something
//  .instance_init = vexpress_instance_init,
    .class_size = sizeof(EosMachineClass),
    .class_init = eos_class_init,
};

66
67
68
69
70

static void eos_cam_class_init(ObjectClass *oc, void *data)
{
    MachineClass *mc = MACHINE_CLASS(oc);
    EosMachineClass *emc = EOS_MACHINE_CLASS(oc);
71
72
    struct eos_model_desc * model = (struct eos_model_desc*) data;
    emc->model = model;
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

    /* Create description from name */
    int desc_size = sizeof(EOS_DESC_BASE) + strlen(model->name) + 1;
    char * desc = (char*)malloc(desc_size * sizeof(char));
    if (desc) {
        snprintf(desc, desc_size, EOS_DESC_BASE " %s", model->name);
    }
    mc->desc = desc;
}

static void eos_cam_class_finalize(ObjectClass *oc, void *data)
{
    MachineClass *mc = MACHINE_CLASS(oc);
    if (mc->desc) {
        free((char*)mc->desc);
        mc->desc = NULL;
    }
}
Niklas Fejes's avatar
Niklas Fejes committed
91
92
93
94


static void eos_machine_init(void)
{
95
    /* Register base type */
96
    type_register_static(&canon_eos_info);
97
98
99
100
101
102
103
104
105

    /* Base info for camera models */
    char name[32]; // "XXXX-machine"
    TypeInfo info = {
        .name = name,
        .class_init = eos_cam_class_init,
        .class_finalize = eos_cam_class_finalize,
        .parent = TYPE_EOS_MACHINE,
    };
106
    
107
    /* Loop over all models listed in model_list.c */
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
    
    /* fill in the defaults from generic entries */
    /* note: generic entries don't have a name */
    for (const struct eos_model_desc * generic = eos_model_list; generic->digic_version; generic++)
    {
        if (!generic->name)
        {
            for (struct eos_model_desc * model = eos_model_list; model->digic_version; model++)
            {
                if (model->name && model->digic_version == generic->digic_version)
                {
                    /* copy settings from generic to model */
                    for (int i = 0; i < COUNT(model->params); i++)
                    {
                        if (model->params[i] == 0)
                        {
124
                            // fprintf(stderr, "%s: params[%d] = %x\n", model->name, i, generic->params[i]);
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
                            model->params[i] = generic->params[i];
                        }
                    }
                }
            }
        }
    }

    /* then register every supported camera model */
    for (struct eos_model_desc * model = eos_model_list; model->digic_version; model++)
    {
        if (model->name)
        {
            snprintf(name, 32, "%s" TYPE_MACHINE_SUFFIX, model->name);
            info.class_data = (void*)model;
            type_register(&info);
        }
142
    }
Niklas Fejes's avatar
Niklas Fejes committed
143
144
}

Niklas Fejes's avatar
Niklas Fejes committed
145
machine_init(eos_machine_init);
Niklas Fejes's avatar
Niklas Fejes committed
146
147
148



Antony Pavlov's avatar
Antony Pavlov committed
149
150
151
EOSRegionHandler eos_handlers[] =
{
    { "FlashControl", 0xC0000000, 0xC0001FFF, eos_handle_flashctrl, 0 },
152
153
  //{ "ROM0",         0xF8000000, 0xFFFFFFFF, eos_handle_rom, 0 },
  //{ "ROM1",         0xF0000000, 0xF7FFFFFF, eos_handle_rom, 1 },
154
155
156
157
158
159
    { "Interrupt",    0xC0200000, 0xC02000FF, eos_handle_intengine_vx, 0 }, /* mostly used on D2/3, but also 60D */
    { "Interrupt",    0xC0201000, 0xC0201FFF, eos_handle_intengine, 0 },    /* <= D5 */
    { "Interrupt",    0xD4011000, 0xD4011FFF, eos_handle_intengine, 1 },    /* D6; first core in D7 */
    { "Interrupt",    0xD5011000, 0xD5011FFF, eos_handle_intengine, 2 },    /* second core in D7 */
    { "Interrupt",    0xD02C0200, 0xD02C02FF, eos_handle_intengine, 3 },    /* 5D3 eeko */
    { "Interrupt",    0xC1000000, 0xC100FFFF, eos_handle_intengine_gic, 7 },/* D7 */
160
161
    { "Timers",       0xC0210000, 0xC0210FFF, eos_handle_timers, 0 },       /* DIGIC 4/5/6 countdown timers */
    { "Timers",       0xD02C1500, 0xD02C15FF, eos_handle_timers, 2 },       /* Eeko countdown timer */
162
    { "Timer",        0xC0242014, 0xC0242014, eos_handle_digic_timer, 0 },
163
    { "Timer",        0xD400000C, 0xD400000C, eos_handle_digic_timer, 1 },
164
165
    { "Timer",        0xD9820014, 0xD9820014, eos_handle_digic_timer, 2 },  /* D7: maybe? firmware waits for this register to change */
    { "UTimer",       0xD4000240, 0xD4000440, eos_handle_utimer, 1 },       /* D6: timers 9...16 */
166
    { "HPTimer",      0xC0243000, 0xC0243FFF, eos_handle_hptimer, 0 },      /* DIGIC 2/3/4/5/6 HPTimers */
Antony Pavlov's avatar
Antony Pavlov committed
167
    { "GPIO",         0xC0220000, 0xC022FFFF, eos_handle_gpio, 0 },
168
    { "Basic",        0xC0100000, 0xC0100FFF, eos_handle_basic, 0 },
169
    { "Basic",        0xC0400000, 0xC0400FFF, eos_handle_basic, 1 },
170
    { "Basic",        0xC0720000, 0xC0720FFF, eos_handle_basic, 2 },
171
    { "SDIO0",        0xC0C00000, 0xC0C00FFF, eos_handle_sdio, 0 },
a1ex's avatar
a1ex committed
172
173
    { "SDIO1",        0xC0C10000, 0xC0C10FFF, eos_handle_sdio, 1 },
    { "SDIO2",        0xC0C20000, 0xC0C20FFF, eos_handle_sdio, 2 },
174
    { "SFIO4",        0xC0C40000, 0xC0C40FFF, eos_handle_sfio, 4 },
175
    { "SDIO85",       0xC8050000, 0xC8050FFF, eos_handle_sdio, 0x85 },
176
    { "SDIO86",       0xC8060000, 0xC8060FFF, eos_handle_sdio, 0x86 },
177
178
    { "SFIO87",       0xC8070000, 0xC8070FFF, eos_handle_sfio, 0x87 },
    { "SFIO88",       0xC8080000, 0xC8080FFF, eos_handle_sfio, 0x88 },
179
    { "ADTGDMA",      0xC0500060, 0xC050007F, eos_handle_adtg_dma, 0 },
180
181
182
183
184
    { "UartDMA",      0xC05000C0, 0xC05000DF, eos_handle_uart_dma, 0 },
    { "CFDMA0*",      0xC0500000, 0xC05000FF, eos_handle_cfdma, 0x0F },
    { "CFDMA10",      0xC0510000, 0xC051001F, eos_handle_cfdma, 0x10 },
    { "SDDMA1*",      0xC0510000, 0xC05100FF, eos_handle_sddma, 0x1F },
    { "CFDMA30",      0xC0530000, 0xC053001F, eos_handle_cfdma, 0x30 },
185
186
  //{ "SDDMA31",      0xC0530020, 0xC053003F, eos_handle_sddma, 0x31 },
  //{ "SDDMA32",      0xC0530040, 0xC053005F, eos_handle_sddma, 0x32 },
187
    { "SFDMA33",      0xC0530060, 0xC053007F, eos_handle_sfdma, 0x33 },
188
189
    { "SDDMA3*",      0xC0530000, 0xC05300FF, eos_handle_sddma, 0x3F },
    { "SDDMA82*",     0xC8020000, 0xC80200FF, eos_handle_sddma, 0x82F },
190
191
    { "SFDMA83*",     0xC8030000, 0xC80300FF, eos_handle_sfdma, 0x83F },
    { "SFDMA84*",     0xC8040000, 0xC80400FF, eos_handle_sfdma, 0x84F },
192
193
    { "CFATA0",       0xC0600000, 0xC060FFFF, eos_handle_cfata, 0 },
    { "CFATA2",       0xC0620000, 0xC062FFFF, eos_handle_cfata, 2 },
alex@thinkpad's avatar
alex@thinkpad committed
194
    { "CFATA16",      0xC0700000, 0xC070FFFF, eos_handle_cfata, 0x10 },
195
    { "UART",         0xC0800000, 0xC08000FF, eos_handle_uart, 0 },
196
    { "UART",         0xC0810000, 0xC08100FF, eos_handle_uart, 1 },
197
    { "UART",         0xC0270000, 0xC027000F, eos_handle_uart, 2 },
alex@thinkpad's avatar
alex@thinkpad committed
198
    { "I2C",          0xC0090000, 0xC00900FF, eos_handle_i2c, 0 },
Antony Pavlov's avatar
Antony Pavlov committed
199
200
201
    { "SIO0",         0xC0820000, 0xC08200FF, eos_handle_sio, 0 },
    { "SIO1",         0xC0820100, 0xC08201FF, eos_handle_sio, 1 },
    { "SIO2",         0xC0820200, 0xC08202FF, eos_handle_sio, 2 },
a1ex's avatar
a1ex committed
202
    { "SIO3",         0xC0820300, 0xC08203FF, eos_handle_sio3, 3 },
203
    { "SIO4",         0xC0820400, 0xC08204FF, eos_handle_sio, 4 },
alex@thinkpad's avatar
alex@thinkpad committed
204
    { "SIO6",         0xC0820600, 0xC08206FF, eos_handle_sio, 6 },
205
    { "SIO7",         0xC0820700, 0xC08207FF, eos_handle_sio, 7 },
206
    { "SIO8",         0xC0820800, 0xC08208FF, eos_handle_sio, 8 },
a1ex's avatar
a1ex committed
207
    { "MREQ",         0xC0203000, 0xC02030FF, eos_handle_mreq, 0 },
Antony Pavlov's avatar
Antony Pavlov committed
208
209
210
211
    { "DMA1",         0xC0A10000, 0xC0A100FF, eos_handle_dma, 1 },
    { "DMA2",         0xC0A20000, 0xC0A200FF, eos_handle_dma, 2 },
    { "DMA3",         0xC0A30000, 0xC0A300FF, eos_handle_dma, 3 },
    { "DMA4",         0xC0A40000, 0xC0A400FF, eos_handle_dma, 4 },
212
213
214
215
    { "DMA5",         0xC0A50000, 0xC0A500FF, eos_handle_dma, 5 },
    { "DMA6",         0xC0A60000, 0xC0A600FF, eos_handle_dma, 6 },
    { "DMA7",         0xC0A70000, 0xC0A700FF, eos_handle_dma, 7 },
    { "DMA8",         0xC0A80000, 0xC0A800FF, eos_handle_dma, 8 },
216
217
218
219
    { "CHSW",         0xC0F05000, 0xC0F05FFF, eos_handle_edmac_chsw, 0 },
    { "EDMAC",        0xC0F04000, 0xC0F04FFF, eos_handle_edmac, 0 },
    { "EDMAC",        0xC0F26000, 0xC0F26FFF, eos_handle_edmac, 1 },
    { "EDMAC",        0xC0F30000, 0xC0F30FFF, eos_handle_edmac, 2 },
220
    { "PREPRO",       0xC0F08000, 0xC0F08FFF, eos_handle_prepro, 0 },
221
222
223
224
    { "HEAD",         0xC0F07048, 0xC0F0705B, eos_handle_head, 1 },
    { "HEAD",         0xC0F0705C, 0xC0F0706F, eos_handle_head, 2 },
    { "HEAD",         0xC0F07134, 0xC0F07147, eos_handle_head, 3 },
    { "HEAD",         0xC0F07148, 0xC0F0715B, eos_handle_head, 4 },
Antony Pavlov's avatar
Antony Pavlov committed
225
    { "CARTRIDGE",    0xC0F24000, 0xC0F24FFF, eos_handle_cartridge, 0 },
a1ex's avatar
a1ex committed
226
    { "ASIF",         0xC0920000, 0xC0920FFF, eos_handle_asif, 4 },
227
    { "Display",      0xC0F14000, 0xC0F14FFF, eos_handle_display, 0 },
228
    { "Display",      0xC0F31000, 0xC0F31FFF, eos_handle_display, 1 },
229
    { "Power",        0xC0F01000, 0xC0F010FF, eos_handle_power_control, 1 },
230
    { "ADC",          0xD9800000, 0xD9800068, eos_handle_adc, 0 },
231
232
233
    { "JP51",         0xC0E00000, 0xC0E0FFFF, eos_handle_jpcore, 0 },
    { "JP62",         0xC0E10000, 0xC0E1FFFF, eos_handle_jpcore, 1 },
    { "JP57",         0xC0E20000, 0xC0E2FFFF, eos_handle_jpcore, 2 },
234

235
236
    { "EEKO",         0xD02C2000, 0xD02C243F, eos_handle_eeko_comm, 0 },

237
238
    /* generic catch-all for everything unhandled from this range */
    { "ENGIO",        0xC0F00000, 0xC0FFFFFF, eos_handle_engio, 0 },
239

240
241
242
243
    { "XDMAC",        0xD6030000, 0xD603002F, eos_handle_xdmac, 0 },
    { "XDMAC",        0xD6030030, 0xD603005F, eos_handle_xdmac, 1 },
    { "XDMAC",        0xD6030060, 0xD603008F, eos_handle_xdmac, 2 },
    { "XDMAC",        0xD6030090, 0xD60300BF, eos_handle_xdmac, 3 },
244
245
246
247
248
249
250
251
252
    { "XDMAC7",       0xC9200000, 0xC920003F, eos_handle_xdmac7, 0 },
    { "XDMAC7",       0xC9200040, 0xC920007F, eos_handle_xdmac7, 1 },
    { "XDMAC7",       0xC9200080, 0xC92000BF, eos_handle_xdmac7, 2 },

    { "MEMDIV",       0xD9001600, 0xD900FFFF, eos_handle_memdiv, 0 },

    { "ROMID",        0xBFE01FD0, 0xBFE01FDF, eos_handle_rom_id, 0 },
    { "ROMID",        0xD5100010, 0xD5100010, eos_handle_rom_id, 1 },

253
254
    { "DIGIC6",       0xD0000000, 0xDFFFFFFF, eos_handle_digic6, 0 },
    { "DIGIC6",       0xC8100000, 0xC8100FFF, eos_handle_digic6, 1 },
255

256
257
    { "ML helpers",   0xCF123000, 0xCF1230FF, eos_handle_ml_helpers, 0 },
    { "ML helpers",   0xC0123400, 0xC01234FF, eos_handle_ml_helpers, 1 },
258
};
Antony Pavlov's avatar
Antony Pavlov committed
259
260
261
262

/* io range access */
static uint64_t eos_io_read(void *opaque, hwaddr addr, uint32_t size)
{
263
264
    EOSState* s = (EOSState*) opaque;
    addr += MMIO_ADDR;
Antony Pavlov's avatar
Antony Pavlov committed
265
266
267
268
269
270
271
272

    uint32_t type = MODE_READ;

    return eos_handler ( opaque, addr, type, 0 );
}

static void eos_io_write(void *opaque, hwaddr addr, uint64_t val, uint32_t size)
{
273
274
    EOSState* s = (EOSState*) opaque;
    addr += MMIO_ADDR;
Antony Pavlov's avatar
Antony Pavlov committed
275
276
277
278
279
280

    uint32_t type = MODE_WRITE;

    eos_handler ( opaque, addr, type, val );
}

281
static const MemoryRegionOps mmio_ops = {
282
283
284
    .read = eos_io_read,
    .write = eos_io_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
285
286
287
288
    .valid = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
289
290
};

291
292
293
294
295
296
297
298
299
300
/* fixme: how to get this called? */
/* no luck with memory_region_rom_device_set_romd... */
static uint64_t eos_rom_read(void * opaque, hwaddr addr, uint32_t size)
{
    fprintf(stderr, "ROM read: %x %x\n", (int)addr, (int)size);
    return 0;
}

static void eos_rom_write(void * opaque, hwaddr addr, uint64_t value, uint32_t size)
{
301
    const char * msg = 0;
302
303
304
305
306
    EOSState * s = (EOSState *)((intptr_t) opaque & ~1);
    uint32_t rom_id = (intptr_t) opaque & 1;
    uint32_t rom_addr = (rom_id) ? ROM1_ADDR : ROM0_ADDR;;
    uint32_t address = rom_addr + addr;

307
308
309
310
311
312
313
314
315
316
317
318
319
320
    if (strcmp(s->model->name, "1300D") == 0)
    {
        if (address == 0xF8000000 && size == 1 && value == 6)
        {
            /* Reading flash model ID? */
            /* Startup code writes to this address, but expects to read
             * different values: C2 25 39, 20 BB 19 or 01 02 19. */
            msg = "Flash model ID?";
            uint32_t model_id = 0x003925C2;
            MEM_WRITE_ROM(address, (uint8_t *) &model_id, 4);
            goto end;
        }
    }

321
322
323
324
325
326
327
328
329
330
331
332
333
    switch(size)
    {
        case 1:
            MEM_WRITE_ROM(address, (uint8_t *) &value, 1);
            break;
        case 2:
            MEM_WRITE_ROM(address, (uint8_t *) &value, 2);
            break;
        case 4:
            MEM_WRITE_ROM(address, (uint8_t *) &value, 4);
            break;
    }

334
end:;
335
336
337
    /* log all ROM writes */
    char name[16];
    snprintf(name, sizeof(name), "ROM%d:%d", rom_id, size);
338
    io_log(name, s, address, MODE_WRITE, value, 0, msg, 0, 0);
339
340
341
342

    /* make sure we execute the latest code */
    /* fixme: shouldn't this be handled internally by QEMU?! */
    tb_invalidate_phys_addr(&address_space_memory, address);
343
344
345
346
347
348
349
}

static const MemoryRegionOps rom_ops = {
    .read = eos_rom_read,
    .write = eos_rom_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
};
350

351
352
353
354
355
356
void eos_mem_read(EOSState *s, hwaddr addr, void * buf, int size)
{
    cpu_physical_memory_read(addr, buf, size);

    if (qemu_loglevel_mask(EOS_LOG_MEM_R))
    {
357
358
        /* fixme: can be optimized */
        for (int i = 0; i < size; i++)
359
        {
360
            eos_log_mem(s, addr + i, *(uint8_t*)(buf + i), 1, NOCHK_LOG);
361
362
363
364
365
366
367
368
        }
    }
}

void eos_mem_write(EOSState *s, hwaddr addr, void * buf, int size)
{
    if (qemu_loglevel_mask(EOS_LOG_MEM_W))
    {
369
370
        /* fixme: can be optimized */
        for (int i = 0; i < size; i ++)
371
        {
372
            eos_log_mem(s, addr + i, *(uint8_t*)(buf + i), 1, 1 | NOCHK_LOG);
373
374
375
376
        }
    }

    cpu_physical_memory_write(addr, buf, size);
377
}
378

379
const char * eos_get_cam_path(EOSState *s, const char * file_rel)
Antony Pavlov's avatar
Antony Pavlov committed
380
{
381
    /* all files are loaded from $QEMU_EOS_WORKDIR/CAM/ */
382
    /* or $QEMU_EOS_WORKDIR/CAM/FIRM_VER/ if specified */
383
    static char file[1024];
384
385
386

    if (s->model->firmware_version)
    {
387
        /* load from the firmware version directory, if specified */
388
389
390
391
        snprintf(file, sizeof(file), "%s/%s/%d/%s", s->workdir, s->model->name, s->model->firmware_version, file_rel);
    }
    else
    {
392
        /* or from the camera directory, if no firmware version is specified */
393
394
        snprintf(file, sizeof(file), "%s/%s/%s", s->workdir, s->model->name, file_rel);
    }
395

396
397
398
    return file;
}

399
400
401
402
403
404
405
406
static int check_rom_mirroring(void * buf, int size, int full_size)
{
    if (size / 2 && memcmp(buf, buf + size / 2, size / 2) == 0)
    {
        /* identical halves? check recursively to find the lowest size with unique data */
        if (!check_rom_mirroring(buf, size / 2, full_size))
        {
            fprintf(stderr, "[EOS] mirrored data; unique 0x%X bytes repeated 0x%X times\n", size / 2, full_size / (size / 2));
407
            assert(0);
408
409
410
411
412
413
414
415
416
        }
        return 1;
    }
    else
    {
        return 0;
    }
}

417
418
419
420
void eos_load_image(EOSState *s, const char * file_rel, int offset, int max_size, uint32_t addr, int swap_endian)
{
    const char * file = eos_get_cam_path(s, file_rel);

Antony Pavlov's avatar
Antony Pavlov committed
421
422
423
    int size = get_image_size(file);
    if (size < 0)
    {
424
        fprintf(stderr, "%s: file not found '%s'\n", __func__, file);
Antony Pavlov's avatar
Antony Pavlov committed
425
426
427
        abort();
    }

428
429
430
431
432
433
    if (size < offset) {
        fprintf(stderr, "%s: file '%s': offset '%d' is too big\n", __func__,
            file, offset);
        abort();
    }

434
    fprintf(stderr, "[EOS] loading '%s'", file);
Antony Pavlov's avatar
Antony Pavlov committed
435

Antony Pavlov's avatar
Antony Pavlov committed
436
437
438
    uint8_t* buf = malloc(size);
    if (!buf)
    {
439
        fprintf(stderr, "%s: malloc error loading '%s'\n", __func__, file);
Antony Pavlov's avatar
Antony Pavlov committed
440
441
        abort();
    }
Antony Pavlov's avatar
Antony Pavlov committed
442

Antony Pavlov's avatar
Antony Pavlov committed
443
444
    if (load_image(file, buf) != size)
    {
445
        fprintf(stderr, "%s: error loading '%s'\n", __func__, file);
Antony Pavlov's avatar
Antony Pavlov committed
446
447
        abort();
    }
Antony Pavlov's avatar
Antony Pavlov committed
448

449
450
451
452
    if ((max_size > 0) && (size + offset != max_size)) {
        fprintf(stderr, " (expected size 0x%08X, got 0x%08X)", max_size, size);
    }

453
454
455
456
457
    size = size - offset;

    if ((max_size > 0) && (size > max_size)) {
        size = max_size;
    }
458
    
459
460
461
462
463
464
465
466
    fprintf(stderr, " to 0x%08X-0x%08X", addr, size + addr - 1);
    
    if (offset)
    {
        fprintf(stderr, " (offset 0x%X)", offset);
    }
    
    fprintf(stderr, "\n");
467
468
469

    check_rom_mirroring(buf + offset, size, size);

470
471
472
    if (swap_endian) {
        reverse_bytes_order(buf + offset, size);
    }
473

a1ex's avatar
a1ex committed
474
    MEM_WRITE_ROM(addr, buf + offset, size);
Antony Pavlov's avatar
Antony Pavlov committed
475

Antony Pavlov's avatar
Antony Pavlov committed
476
477
478
    free(buf);
}

479
480
481
482
static int cfdma_read_data(EOSState *s, CFState *cf);
static int cfdma_write_data(EOSState *s, CFState *cf);
static void cfdma_trigger_interrupt(EOSState *s);

Antony Pavlov's avatar
Antony Pavlov committed
483

484
485
static void eos_interrupt_timer_body(EOSState *s)
{
Antony Pavlov's avatar
Antony Pavlov committed
486
487
488
    {
        uint32_t pos;

Niklas Fejes's avatar
Niklas Fejes committed
489
        /* don't loop thread if cpu stopped in gdb */
490
        if (s->cpu0 && cpu_is_stopped(CPU(s->cpu0))) {
491
            return;
492
493
494
        }

        if (s->cpu1 && cpu_is_stopped(CPU(s->cpu1))) {
495
            return;
Niklas Fejes's avatar
Niklas Fejes committed
496
497
        }

498
499
500
501
502
        s->digic_timer20 += DIGIC_TIMER_STEP;
        s->digic_timer20 &= DIGIC_TIMER20_MASK;
        s->digic_timer32 += DIGIC_TIMER_STEP;
        s->digic_timer32 &= DIGIC_TIMER32_MASK;

503
        for (pos = 0; pos < COUNT(s->timer_enabled); pos++)
504
505
506
        {
            if (s->timer_enabled[pos])
            {
507
                s->timer_current_value[pos] += DIGIC_TIMER_STEP;
508
509
510
511
512

                if (s->timer_current_value[pos] > s->timer_reload_value[pos])
                {
                    s->timer_current_value[pos] = 0;
                }
513
514
            }
        }
Antony Pavlov's avatar
Antony Pavlov committed
515
516

        /* go through all interrupts and check if they are pending/scheduled */
517
        for(pos = INT_ENTRIES-1; pos > 0; pos--)
Antony Pavlov's avatar
Antony Pavlov committed
518
519
520
521
522
523
524
525
        {
            /* it is pending, so trigger int and set to 0 */
            if(s->irq_schedule[pos] == 1)
            {
                /* wait, its not enabled. keep it pending */
                if(s->irq_enabled[pos] && !s->irq_id)
                {
                    /* timer interrupt will re-fire periodically */
526
                    if(pos == TIMER_INTERRUPT)
Antony Pavlov's avatar
Antony Pavlov committed
527
                    {
528
529
530
531
                        if (qemu_loglevel_mask(CPU_LOG_INT) &&
                            qemu_loglevel_mask(EOS_LOG_VERBOSE))
                        {
                            /* timer interrupt, quiet */
532
                            fprintf(stderr, "[EOS] trigger int 0x%02X (delayed)\n", pos);    /* quiet */
533
                        }
534
                        s->irq_schedule[pos] = s->timer_reload_value[DRYOS_TIMER_ID] >> 8;
Antony Pavlov's avatar
Antony Pavlov committed
535
536
537
                    }
                    else
                    {
538
                        if (qemu_loglevel_mask(CPU_LOG_INT)) {
539
                            fprintf(stderr, "[EOS] trigger int 0x%02X (delayed)\n", pos);
540
                        }
Antony Pavlov's avatar
Antony Pavlov committed
541
542
543
544
                        s->irq_schedule[pos] = 0;
                    }

                    s->irq_id = pos;
545
                    s->irq_enabled[s->irq_id] = 0;
546

547
                    cpu_interrupt(CPU(CURRENT_CPU), CPU_INTERRUPT_HARD);
Antony Pavlov's avatar
Antony Pavlov committed
548
549
550
551
552
553
554
555
556
                }
            }

            /* still counting down? */
            if(s->irq_schedule[pos] > 1)
            {
                s->irq_schedule[pos]--;
            }
        }
557

558
559
560
561
562
563
564
        /* check all UTimers */
        int utimer_interrupts[COUNT(s->UTimers)] = {
            0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E,
        };

        for (int id = 0; id < COUNT(s->UTimers); id++)
        {
565
            if (s->UTimers[id].active && s->UTimers[id].output_compare == s->digic_timer32)
566
567
568
569
570
571
572
573
574
            {
                if (qemu_loglevel_mask(EOS_LOG_IO)) {
                    fprintf(stderr, "[TIMER] Firing UTimer #%d\n", id);
                }
                s->UTimers[id].triggered = 1;
                eos_trigger_int(s, utimer_interrupts[id], 0);
            }
        }

575
576
        /* also check all HPTimers */
        /* note: we can trigger multiple HPTimers on a single interrupt */
577
578
        int trigger_hptimers[64] = {0};
        int hptimer_interrupts[COUNT(s->HPTimers)] = {
579
            0x18, 0x1A, 0x1C, 0x1E, 0, 0,
580
581
582
583
584
            HPTIMER_INTERRUPT, HPTIMER_INTERRUPT, HPTIMER_INTERRUPT, HPTIMER_INTERRUPT,
            HPTIMER_INTERRUPT, HPTIMER_INTERRUPT, HPTIMER_INTERRUPT, HPTIMER_INTERRUPT,
        };
        
        for (pos = 0; pos < COUNT(s->HPTimers); pos++)
585
        {
586
            if (s->HPTimers[pos].active && s->HPTimers[pos].output_compare == s->digic_timer20)
587
            {
588
                if (qemu_loglevel_mask(EOS_LOG_IO)) {
589
                    fprintf(stderr, "[HPTimer] Firing HPTimer #%d\n", pos);
590
                }
591
                s->HPTimers[pos].triggered = 1;
592
593
594
595
                int interrupt = hptimer_interrupts[pos];
                assert(interrupt > 0);
                assert(interrupt < COUNT(trigger_hptimers));
                trigger_hptimers[hptimer_interrupts[pos]] = 1;
596
597
598
            }
        }

599
        for (int i = 1; i < COUNT(trigger_hptimers); i++)
600
        {
601
602
603
604
            if (trigger_hptimers[i])
            {
                eos_trigger_int(s, i, 0);
            }
605
        }
606

607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
        if (s->cf.dma_read_request)
        {
            s->cf.dma_read_request = cfdma_read_data(s, &s->cf);
        }

        if (s->cf.dma_write_request)
        {
            s->cf.dma_write_request = cfdma_write_data(s, &s->cf);
        }
        
        if (s->cf.pending_interrupt && s->cf.interrupt_enabled == 1)
        {
            cfdma_trigger_interrupt(s);
            s->cf.pending_interrupt = 0;
        }
Antony Pavlov's avatar
Antony Pavlov committed
622
623
    }

624
625
626
627
628
629
630
}

static void eos_interrupt_timer_cb(void *parm)
{
    EOSState *s = (EOSState *)parm;
    eos_interrupt_timer_body(s);
    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
631
    timer_mod_anticipate_ns(s->interrupt_timer, now + DIGIC_TIMER_STEP*1000); // DIGIC_TIMER_STEP us
Antony Pavlov's avatar
Antony Pavlov committed
632
633
}

Niklas Fejes's avatar
Niklas Fejes committed
634
635
636
637
638
639
640
641



/** FRAMEBUFFER & DISPLAY (move to separate file?) **/




642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
// precompute some parts of YUV to RGB computations
static int yuv2rgb_RV[256];
static int yuv2rgb_GU[256];
static int yuv2rgb_GV[256];
static int yuv2rgb_BU[256];

/** http://www.martinreddy.net/gfx/faqs/colorconv.faq
 * BT 601:
 * R'= Y' + 0.000*U' + 1.403*V'
 * G'= Y' - 0.344*U' - 0.714*V'
 * B'= Y' + 1.773*U' + 0.000*V'
 * 
 * BT 709:
 * R'= Y' + 0.0000*Cb + 1.5701*Cr
 * G'= Y' - 0.1870*Cb - 0.4664*Cr
 * B'= Y' - 1.8556*Cb + 0.0000*Cr
 */

static void precompute_yuv2rgb(int rec709)
{
    int u, v;
    if (rec709)
    {
        /*
        *R = *Y + 1608 * V / 1024;
        *G = *Y -  191 * U / 1024 - 478 * V / 1024;
        *B = *Y + 1900 * U / 1024;
        */
        for (u = 0; u < 256; u++)
        {
            int8_t U = u;
            yuv2rgb_GU[u] = (-191 * U) >> 10;
            yuv2rgb_BU[u] = (1900 * U) >> 10;
        }

        for (v = 0; v < 256; v++)
        {
            int8_t V = v;
            yuv2rgb_RV[v] = (1608 * V) >> 10;
            yuv2rgb_GV[v] = (-478 * V) >> 10;
        }
    }
    else // REC 601
    {
        /*
        *R = *Y + ((1437 * V) >> 10);
        *G = *Y -  ((352 * U) >> 10) - ((731 * V) >> 10);
        *B = *Y + ((1812 * U) >> 10);
        */
        for (u = 0; u < 256; u++)
        {
            int8_t U = u;
            yuv2rgb_GU[u] = (-352 * U) >> 10;
            yuv2rgb_BU[u] = (1812 * U) >> 10;
        }

        for (v = 0; v < 256; v++)
        {
            int8_t V = v;
            yuv2rgb_RV[v] = (1437 * V) >> 10;
            yuv2rgb_GV[v] = (-731 * V) >> 10;
        }
    }
}

#define COERCE(x,lo,hi) MAX(MIN((x),(hi)),(lo))

static void yuv2rgb(int Y, int U, int V, int* R, int* G, int* B)
{
    const int v_and_ff = V & 0xFF;
    const int u_and_ff = U & 0xFF;
    int v = Y + yuv2rgb_RV[v_and_ff];
    *R = COERCE(v, 0, 255);
    v = Y + yuv2rgb_GU[u_and_ff] + yuv2rgb_GV[v_and_ff];
    *G = COERCE(v, 0, 255);
    v = Y + yuv2rgb_BU[u_and_ff];
    *B = COERCE(v, 0, 255);
}

#define UYVY_GET_Y1(uyvy) (((uyvy) >>  8) & 0xFF)
#define UYVY_GET_Y2(uyvy) (((uyvy) >> 24) & 0xFF)
#define UYVY_GET_U(uyvy)  (((uyvy)      ) & 0xFF)
#define UYVY_GET_V(uyvy)  (((uyvy) >> 16) & 0xFF)


a1ex's avatar
a1ex committed
727
728
/* todo: supoort other bith depths */

729
typedef void (*drawfn_bmp_yuv)(void *, uint8_t *, const uint8_t *, const uint8_t*, int, int, int);
730

731
732
733
734
static void draw_line8_32(void *opaque,
                uint8_t *d, const uint8_t *s, int width, int deststep)
{
    uint8_t v, r, g, b;
a1ex's avatar
a1ex committed
735
    EOSState* ws = (EOSState*) opaque;
736
737
    
    do {
a1ex's avatar
a1ex committed
738
        v = ldub_p((void *) s);
739
740
        if (v)
        {
741
742
743
            r = ws->disp.palette_8bit[v].R;
            g = ws->disp.palette_8bit[v].G;
            b = ws->disp.palette_8bit[v].B;
744
745
746
747
            ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
        }
        else
        {
748
            r = g = b = 128;
749
750
751
752
753
754
755
            ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
        }
        s ++;
        d += 4;
    } while (-- width != 0);
}

756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
static uint8_t clip_yuv(int v) {
    if (v<0) return 0;
    if (v>255) return 255;
    return v;
}

static uint8_t yuv_to_r(uint8_t y, int8_t v) {
    return clip_yuv(((y<<12) +          v*5743 + 2048)>>12);
}

static uint8_t yuv_to_g(uint8_t y, int8_t u, int8_t v) {
    return clip_yuv(((y<<12) - u*1411 - v*2925 + 2048)>>12);
}

static uint8_t yuv_to_b(uint8_t y, int8_t u) {
    return clip_yuv(((y<<12) + u*7258          + 2048)>>12);
}

static void draw_line_YUV8B_32(void *opaque,
                uint8_t *d, const uint8_t *s, int width, int deststep)
{
    uint8_t v, r, g, b;
    width = width / 2;
    do {
        v = ldub_p((void *) s);
        if (v)
        {
            uint8_t p2 = s[2] - 0x80;
            uint8_t p0 = s[0] - 0x80;
            r = yuv_to_r(s[1],p2);
            g = yuv_to_g(s[1],p0,p2);
            b = yuv_to_b(s[1],p0);
            ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
                        
            d += 4;
            r = yuv_to_r(s[3],p2);
            g = yuv_to_g(s[3],p0,p2);
            b = yuv_to_b(s[3],p0);
            ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
            d += 4;
        }
        else
        {
            r = g = b = 128;
            ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
            d += 4;
            ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
            d += 4;
        }
        s +=4;
    } while (-- width != 0);
}

809
810
811
812
static void draw_line4_32(void *opaque,
                uint8_t *d, const uint8_t *s, int width, int deststep)
{
    uint8_t v, r, g, b;
813
    EOSState* ws = (EOSState*) opaque;
814
    void * d0 = d;
815
816
    
    do {
a1ex's avatar
a1ex committed
817
        v = ldub_p((void *) s);
818
819
        v = ((uintptr_t)d/4 % 2) ? (v >> 4) & 0xF : v & 0xF;
        
820
821
822
        r = ws->disp.palette_4bit[v].R;
        g = ws->disp.palette_4bit[v].G;
        b = ws->disp.palette_4bit[v].B;
823
        ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
824
825
826
827

        if ((uintptr_t)d/4 % 2) s ++;
        d += 4;
    } while (-- width != 0);
828
829
830
831
832
833

    if (ws->model->digic_version < 4)
    {
        /* double each line */
        memcpy(d, d0, (void *) d - d0);
    }
834
835
}

836
static void draw_line8_32_bmp_yuv(void *opaque,
837
                uint8_t *d, const uint8_t *bmp, const uint8_t *yuv, int width, int deststep, int yuvstep)
a1ex's avatar
a1ex committed
838
{
a1ex's avatar
a1ex committed
839
    EOSState* ws = (EOSState*) opaque;
840
    
a1ex's avatar
a1ex committed
841
    do {
842
843
844
845
846
847
848
        uint8_t v = ldub_p((void *) bmp);
        int r = ws->disp.palette_8bit[v].R;
        int g = ws->disp.palette_8bit[v].G;
        int b = ws->disp.palette_8bit[v].B;
        int o = ws->disp.palette_8bit[v].opacity;

        if (o == 3)
849
        {
850
            /* opaque */
851
852
853
854
            ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
        }
        else
        {
855
            /* some sort of transparency */
a1ex's avatar
a1ex committed
856
            uint32_t uyvy =  ldl_p((void*)((uintptr_t)yuv & ~3));
857
            int Y = (uintptr_t)yuv & 3 ? UYVY_GET_Y2(uyvy) : UYVY_GET_Y1(uyvy);
858
859
860
861
            int U = UYVY_GET_U(uyvy);
            int V = UYVY_GET_V(uyvy);
            int R, G, B;
            yuv2rgb(Y, U, V, &R, &G, &B);
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
            
            if (o == 0 && r == 255 && g == 255 && b == 255)
            {
                /* fully transparent (nothing to do) */
                /* is this an edge case in Canon firmware? */
            }
            else
            {
                /* assume semi-transparent */
                /* 5D3: 2 bits, 4 transparency levels
                 * 0 = somewhat transparent, 3 = opaque,
                 * 0 with Y=255 (R=G=B=255) = fully transparent
                 * black image => bitmap overlay looks as if it were opaque
                 * (colors not altered, except for the fully transparent special case)
                 * white image => bitmap overlay washed out (except for o=3)
                 * red image => bitmap overlay hue-shifted (except for o=3)
                 * the following is just a rough approximation that looks reasonably well */
                int bmp_weight = (o & 3) + 2;
                int yuv_weight = 5 - bmp_weight;
                R = (R * yuv_weight + r * bmp_weight) / 5;
                G = (G * yuv_weight + g * bmp_weight) / 5;
                B = (B * yuv_weight + b * bmp_weight) / 5;
            }

886
            ((uint32_t *) d)[0] = rgb_to_pixel32(R, G, B);
887
        }
888
        bmp ++;
889
        yuv += yuvstep;
a1ex's avatar
a1ex committed
890
891
892
893
        d += 4;
    } while (-- width != 0);
}

894
895
/* similar to QEMU's framebuffer_update_display, but with two image planes */
/* main plane is BMP (8-bit, same size as output), secondary plane is YUV (scaled to match the BMP one) */
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
static void framebuffer_update_display_bmp_yuv(
    DisplaySurface *ds,
    MemoryRegion *address_space,
    hwaddr base_bmp,
    hwaddr base_yuv,
    int cols, /* Width in pixels.  */
    int rows_bmp, /* Height in pixels.  */
    int rows_yuv,
    int src_width_bmp, /* Length of source line, in bytes.  */
    int src_width_yuv,
    int dest_row_pitch, /* Bytes between adjacent horizontal output pixels.  */
    int dest_col_pitch, /* Bytes between adjacent vertical output pixels.  */
    int invalidate, /* nonzero to redraw the whole image.  */
    drawfn_bmp_yuv fn,
    void *opaque,
    int *first_row, /* Input and output.  */
    int *last_row /* Output only */)
{
    hwaddr src_len_bmp;
    hwaddr src_len_yuv;
    uint8_t *dest;
    uint8_t *src_bmp;
    uint8_t *src_yuv;
    uint8_t *src_base_bmp;
    uint8_t *src_base_yuv;
    int first, last = 0;
    int dirty;
    int i;
    ram_addr_t addr_bmp;
    ram_addr_t addr_yuv;
926
    ram_addr_t addr_base_yuv;
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
    MemoryRegionSection mem_section_bmp;
    MemoryRegionSection mem_section_yuv;
    MemoryRegion *mem_bmp;
    MemoryRegion *mem_yuv;

    i = *first_row;
    *first_row = -1;
    src_len_bmp = src_width_bmp * rows_bmp;
    src_len_yuv = src_width_yuv * rows_yuv;

    mem_section_bmp = memory_region_find(address_space, base_bmp, src_len_bmp);
    mem_section_yuv = memory_region_find(address_space, base_yuv, src_len_yuv);
    mem_bmp = mem_section_bmp.mr;
    mem_yuv = mem_section_yuv.mr;
    if (int128_get64(mem_section_bmp.size) != src_len_bmp ||
            !memory_region_is_ram(mem_section_bmp.mr)) {
        goto out;
    }
    assert(mem_bmp);
    assert(mem_section_bmp.offset_within_address_space == base_bmp);

    if (int128_get64(mem_section_yuv.size) != src_len_yuv ||
            !memory_region_is_ram(mem_section_yuv.mr)) {
        goto out;
    }
    assert(mem_yuv);
    assert(mem_section_yuv.offset_within_address_space == base_yuv);

    memory_region_sync_dirty_bitmap(mem_bmp);
    memory_region_sync_dirty_bitmap(mem_yuv);
    src_base_bmp = cpu_physical_memory_map(base_bmp, &src_len_bmp, 0);
    src_base_yuv = cpu_physical_memory_map(base_yuv, &src_len_yuv, 0);
    /* If we can't map the framebuffer then bail.  We could try harder,
       but it's not really worth it as dirty flag tracking will probably
       already have failed above.  */
    if (!src_base_bmp)
        goto out;
    if (!src_base_yuv)
        goto out;
    if (src_len_bmp != src_width_bmp * rows_bmp) {
        cpu_physical_memory_unmap(src_base_bmp, src_len_bmp, 0, 0);
        goto out;
    }
    if (src_len_yuv != src_width_yuv * rows_yuv) {
        cpu_physical_memory_unmap(src_base_yuv, src_len_yuv, 0, 0);
        goto out;
    }
    src_bmp = src_base_bmp;
    src_yuv = src_base_yuv;
    dest = surface_data(ds);
    if (dest_col_pitch < 0)
        dest -= dest_col_pitch * (cols - 1);
    if (dest_row_pitch < 0) {
        dest -= dest_row_pitch * (rows_bmp - 1);
    }
    first = -1;
    addr_bmp = mem_section_bmp.offset_within_region;
    addr_yuv = mem_section_yuv.offset_within_region;
985
    addr_base_yuv = addr_yuv;
986

987
    int j = i * rows_yuv / rows_bmp;
988
989
    addr_bmp += i * src_width_bmp;
    src_bmp += i * src_width_bmp;
990
991
    addr_yuv = addr_base_yuv + j * src_width_yuv;
    src_yuv = src_base_yuv + j * src_width_yuv;
992
    dest += i * dest_row_pitch;
993
994
995
    
    /* fixme: only works for integer factors */
    int src_yuv_pitch = src_width_yuv / cols;
996
997
998
999
1000
1001
1002

    for (; i < rows_bmp; i++) {
        dirty = memory_region_get_dirty(mem_bmp, addr_bmp, src_width_bmp,
                                             DIRTY_MEMORY_VGA);
        dirty |= memory_region_get_dirty(mem_yuv, addr_yuv, src_width_yuv,
                                             DIRTY_MEMORY_VGA);
        if (dirty || invalidate) {
1003
            fn(opaque, dest, src_bmp, src_yuv, cols, dest_col_pitch, src_yuv_pitch);
1004
1005
1006
1007
            if (first == -1)
                first = i;
            last = i;
        }
1008
1009

        int j = i * rows_yuv / rows_bmp;
1010
1011
        addr_bmp += src_width_bmp;
        src_bmp += src_width_bmp;
1012
1013
        addr_yuv = addr_base_yuv + j * src_width_yuv;
        src_yuv = src_base_yuv + j * src_width_yuv;
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
        dest += dest_row_pitch;
    }
    cpu_physical_memory_unmap(src_base_bmp, src_len_bmp, 0, 0);
    cpu_physical_memory_unmap(src_base_yuv, src_len_yuv, 0, 0);
    if (first < 0) {
        goto out;
    }
    memory_region_reset_dirty(mem_bmp, mem_section_bmp.offset_within_region, src_len_bmp,
                              DIRTY_MEMORY_VGA);
    memory_region_reset_dirty(mem_yuv, mem_section_yuv.offset_within_region, src_len_yuv,
                              DIRTY_MEMORY_VGA);
    *first_row = first;
    *last_row = last;
out:
    memory_region_unref(mem_bmp);
    memory_region_unref(mem_yuv);
}

a1ex's avatar
a1ex committed
1032
1033
1034
1035
static void eos_update_display(void *parm)
{
    EOSState *s = (EOSState *)parm;

1036
    DisplaySurface *surface = qemu_console_surface(s->disp.con);
1037
1038
1039
    
    /* these numbers need double-checking */
    /*                  LCD    HDMI-1080   HDMI-480    SD-PAL      SD-NTSC */
1040
1041
1042
1043
    int widths[]      = {   720,   960,        720,        720,        720     };
    int heights[]     = {   480,   540,        480,        576,        480     };
    int yuv_widths[]  = {   720,  1920,        720,        540,        540     };
    int yuv_heights[] = {   480,  1080,        480,        572,        480     };
1044
    
1045
1046
1047
1048
    int width       = widths     [s->disp.type];
    int height      = heights    [s->disp.type];
    int yuv_width   = yuv_widths [s->disp.type];
    int yuv_height  = yuv_heights[s->disp.type];
1049
1050
1051
1052
1053

    int height_multiplier = 1;
    int out_height = height;

    /* VxWorks models have 720x240 screens stretched vertically */
1054
1055
    if (s->model->digic_version < 4)
    {
1056
1057
1058
        height_multiplier = 2;
        height /= height_multiplier;
        assert(out_height == height * height_multiplier);
1059
    }
1060

1061
1062
1063
1064
1065
1066
    if (strcmp(s->model->name, "1100D") == 0)
    {
        /* half-size YUV buffer */
        yuv_height /= 2;
    }

1067
1068
1069
1070
    if (s->disp.width && s->disp.height)
    {
        /* did we manage to get them from registers? override the above stuff */
        width = s->disp.width;
1071
        out_height = height = s->disp.height;
1072
    }
a1ex's avatar
a1ex committed
1073

1074
    if (width != surface_width(surface) || out_height != surface_height(surface))
a1ex's avatar
a1ex committed
1075
    {
1076
        qemu_console_resize(s->disp.con, width, out_height);
1077
1078
        surface = qemu_console_surface(s->disp.con);
        s->disp.invalidate = 1;
a1ex's avatar
a1ex committed
1079
    }
alex@thinkpad's avatar
alex@thinkpad committed
1080
1081
1082
1083
1084
1085
1086

    if (s->card_led)
    {
        /* fixme: inefficient (redraws non-dirty areas) */
        s->disp.invalidate = 1;
    }

a1ex's avatar
a1ex committed
1087
1088
1089
    int first, last;
    
    first = 0;
1090
1091
    int linesize = surface_stride(surface) * height_multiplier;

1092
    if (s->disp.is_4bit)
1093
    {
a1ex's avatar
a1ex committed
1094
        /* bootloader config, 4 bpp */
1095
        uint64_t size = height * width / 2;
1096
1097
        MemoryRegionSection section = memory_region_find(
            s->system_mem,
1098
            s->disp.bmp_vram ? s->disp.bmp_vram : 0x08000000,
1099
1100
            size
        );
1101
1102
        framebuffer_update_display(
            surface,
Niklas Fejes's avatar
Niklas Fejes committed
1103
            &section,
1104
            width, height,
1105
            s->disp.bmp_pitch, linesize, 0, 1,
1106
            draw_line4_32, s,
1107
1108
1109
            &first, &last
        );
    }
1110
    else if (s->disp.img_vram)
1111
1112
1113
1114
    {
        framebuffer_update_display_bmp_yuv(
            surface,
            s->system_mem,
1115
1116
            s->disp.bmp_vram,
            s->disp.img_vram,
1117
            width, height, yuv_height,
1118
            s->disp.bmp_pitch, yuv_width*2, linesize, 0, s->disp.invalidate,
a1ex's avatar
a1ex committed
1119
            draw_line8_32_bmp_yuv, s,
1120
1121
1122
            &first, &last
        );
    }
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
    else if (strcmp(s->model->name, "EOSM3") == 0)
    {
        uint64_t size = height * s->disp.bmp_pitch;
        MemoryRegionSection section = memory_region_find(
            s->system_mem,
            s->disp.bmp_vram ? s->disp.bmp_vram : 0x08000000,
            size
        );
        framebuffer_update_display(
            surface,
            &section,
            width , height,
            s->disp.bmp_pitch, linesize, 0, 1,
            draw_line_YUV8B_32, s,
            &first, &last
        );
    }
1140
    else
1141
    {
1142
        uint64_t size = height * width;
1143
1144
        MemoryRegionSection section = memory_region_find(
            s->system_mem,
1145
            s->disp.bmp_vram ? s->disp.bmp_vram : 0x08000000,
1146
1147
            size
        );
1148
1149
        framebuffer_update_display(
            surface,
Niklas Fejes's avatar
Niklas Fejes committed
1150
            &section,
1151
            width, height,
1152
            s->disp.bmp_pitch, linesize, 0, 1,
a1ex's avatar
a1ex committed
1153
            draw_line8_32, s,
1154
1155
1156
            &first, &last
        );
    }
1157
1158
1159
1160
1161

    if (s->card_led)
    {
        /* draw the LED at the bottom-right corner of the screen */
        int x_led = width - 8;
1162
        int y_led = out_height - 8;
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
        uint8_t * dest = surface_data(surface);
        for (int dy = -5; dy <= 5; dy++)
        {
            for (int dx = -5; dx <= 5; dx++)
            {
                int r2 = dx*dx + dy*dy;
                if (r2 < 5*5)
                {
                    ((uint32_t *) dest)[x_led+dx + width*(y_led+dy)] =
                        (r2 >= 4*4)         ? rgb_to_pixel32(0, 0, 0)       :
                        (s->card_led == 1)  ? rgb_to_pixel32(255, 0, 0)     :
                                              rgb_to_pixel32(64, 64, 64) ;
                }
            }
        }
    }

1180
1181
1182
    first *= height_multiplier;
    last *= height_multiplier;

a1ex's avatar
a1ex committed
1183
    if (first >= 0) {
1184
        dpy_gfx_update(s->disp.con, 0, first, width, last - first + 1);
a1ex's avatar
a1ex committed
1185
1186
    }
    
1187
    s->disp.invalidate = 0;
a1ex's avatar
a1ex committed
1188
1189
1190
1191
1192
}

static void eos_invalidate_display(void *parm)
{
    EOSState *s = (EOSState *)parm;
1193
    s->disp.invalidate = 1;
a1ex's avatar
a1ex committed
1194
1195
1196
1197