diff fluid-mnf/machine.c @ 311:9cecc930d78f

fluid-mnf: original source from TI, defenestrated line endings and rearranged directory structure, but no *.[ch] source file content changes yet
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 29 Feb 2020 05:36:07 +0000
parents
children 435e041897f2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fluid-mnf/machine.c	Sat Feb 29 05:36:07 2020 +0000
@@ -0,0 +1,2635 @@
+/******************************************************************************
+ * FLUID (Flash Loader Utility Independent of Device)
+ *
+ * Copyright Texas Instruments, 2001.
+ * Mads Meisner-Jensen, mmj@ti.com.
+ * Original state-machine logic by Delta Technologies, Copyright, 2001.
+ *
+ * Core functionality. State machines and support functions.
+ *
+ * $Id: machine.c 1.35.1.37 Mon, 28 Apr 2003 08:49:16 +0200 tsj $
+ *
+ ******************************************************************************/
+
+#include "fluid.h"
+#include "flash.h"
+#include "target.h"
+#include "fileio.h"
+#include "../target/protocol.h"
+#include "misc.h"
+#include "lz.h"
+#include "trace.h"
+// Secure Calypso Plus
+#include "../inc/ram_load.h"
+#include "../inc/secure_types.h"
+
+#include <stdio.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+
+/******************************************************************************
+ * Constants and typedefs
+ ******************************************************************************/
+
+// Due to memory wrap/mirror used in flash_read_machine(), IMAGE_SIZE_MAX
+// *must* be a power of two.
+#define IMAGE_SIZE_MAX (8 * 1024 * 1024)
+
+#define IMAGE_CHUNK_SIZE 8192
+
+#define ERASE_LIST_SIZE_MAX 256
+#define SECTOR_MAP_SIZE_MAX 256
+
+#define TARGET_PROGRAM_SIZE_MAX (8 * 1024)
+
+#define READ_LIST_SIZE_MAX 40
+
+#define RETRIES_MAX 30
+
+// Default target receive delay in milli-seconds.
+#define TARGET_RECV_DELAY 250
+#define TARGET_RECV_LONG_DELAY 1000
+
+// Default target reset delay in milli-seconds.
+#define TARGET_RESET_DELAY 200
+
+// CS_MODE 0: Flash at 0x04000000 @ CS5 on E-Sample
+#define CALP_OFFSET 0x04000000
+
+/******************************************************************************
+ * Globals
+ ******************************************************************************/
+
+// Secure Calypso Plus
+T_RAM_LOADER d_ram_loader;
+T_FRAME      d_frame;
+UWORD16      d_certificate_length = 0;
+char         a_certified_cmd_file_name[] = "cmdp_cert.m0";
+
+struct target_s {
+    uint32 cmd_load_addr;
+    uint32 method_load_addr;
+    const char type;
+    const char *name;
+    const char *prefix;
+};
+// Note that the ROM bootloader of Calypso rev. A devices are unable to
+// write the bottom 128kB of internal RAM. Therefore the address have been
+// changed from 0x804000 to 0x0820000
+const struct target_s target[] =
+{
+    { 0x0000000, 0x0000000, 'u', "Unknown",  "oops"  },
+    { 0x0000000, 0x0000000, 'u', "Unknown1", "oops1" },
+    { 0x0000000, 0x0000000, 'G', "Gemini?",  "gem?"  },
+    { 0x3004000, 0x3000000, 'U', "Ulysses",  "uly"   },
+    { 0x0820000, 0x0800000, 'C', "Calypso",      "cal"   },
+    { 0x0804000, 0x0800000, 'c', "Calypso Lite", "cal_l" },
+    { 0x8020000, 0x8000100, 'P', "Calypso Plus", "cal_p" }
+};
+int target_type;
+int target_clk; // 13000000 or 14745600
+
+struct device_s *device;
+
+uint8 target_program[TARGET_PROGRAM_SIZE_MAX];
+int   target_program_size;
+int   target_fifo_size;
+
+uint8 image[IMAGE_SIZE_MAX];
+int   image_size;           // size of image buffer
+int   image_chunk_size;     // number of bytes in each chunk
+int   image_size_in_chunks; // size of image buffer in chunks
+
+// Each byte in the image_map represents one chunk of the image. Each byte
+// can have one of the following values:
+// '\0' = no data in this chunk
+//  's' = skip this chunk when programming
+//  'c' = chunk checksum ok, skip this chunk when programming
+//  'x' = used
+uint8 image_map[IMAGE_SIZE_MAX / IMAGE_CHUNK_SIZE];
+int   image_map_size;       // number of entries in image_map
+
+// Each byte in the sector_map represents one sector. Each byte can have one
+// of the following values:
+// '\0' = sector unused (don't erase)
+//  'c' = sector already empty/erased (don't erase) (not used)
+//  's' = skip this sector when erasing (due to erase override)
+//  'X' = force erase this sector (due to erase override)
+//  'x' = erase this sector (normal case --- sector is covered by image)
+int sector_map[SECTOR_MAP_SIZE_MAX];
+int sector_map_size;
+
+int erase_list[ERASE_LIST_SIZE_MAX];
+int erase_list_size;
+
+struct image_part_s read_list[READ_LIST_SIZE_MAX];
+int read_list_size;
+int read_total_size;
+
+int image_map_count_used_chunks(void);
+int image_map_update(void);
+int sector_map_init(void);
+
+void image_map_show(void);
+void sector_map_show(void);
+void target_timers_show(void);
+
+void parse_arg_erase_override(char *p);
+void parse_arg_read(char *p);
+void parse_arg_write(char *p);
+
+void error_proto(char ch, char ch_expected);
+
+int target_type_set(uint16 code);
+
+// Secure Calypso Plus
+void f_print_signalling_response(int level);
+void f_print_certificate_platform_data(int level, T_MANUFACTURER_CERTIFICATE_PLATFORM_DATA *p_certificate);
+void f_print_certificate(int level, T_MANUFACTURER_CERTIFICATE_FLASH_PROGRAMMER *p_certificate);
+void f_print_parameter_nack_status(int level, UWORD8 d_parameter_nack_sts);
+void f_print_write_nack_status(int level, UWORD8 d_write_nack_sts);
+UWORD8 f_convert_uart_baud_rate(UWORD32 d_baud_rate);
+void target_imei_protection(void);
+
+/******************************************************************************
+ * Main
+ ******************************************************************************/
+
+void bootloader_machine(void);
+void cmd_machine(void);
+void flash_checksum_machine(void);
+void flash_detect_machine(void);
+void method_download_machine(void);
+void flash_program_machine(void);
+void flash_read_machine(void);
+void target_reset_machine(void);
+
+void fluid_machine(void)
+{
+    int error;
+
+    // If user has specifically asked fluid just to reset the target, we do
+    // that and only that! This way, we can use fluid just to reset the
+    // target without doing anything else!
+    if (arg_target_reset == 2 && arg_dry_run) {
+        flowf(VERBOSE, "Resetting target: ");
+        target_reset(0);
+        target_wait(0, TARGET_RESET_DELAY);
+        target_reset(1);
+        flowf(VERBOSE, "ok\n");
+    }
+
+    file_read_devices("devices.txt");
+
+    if (arg_list_devices) {
+        devices_list();
+        exit(0);
+    }
+
+    image_size = IMAGE_SIZE_MAX;
+    image_chunk_size = IMAGE_CHUNK_SIZE;
+    image_map_size = IMAGE_SIZE_MAX / image_chunk_size;
+    image_size_in_chunks =
+        (image_size + image_chunk_size - 1) / image_chunk_size;
+
+    // Read the flash image file(s) unless user wants to read from the flash
+    if (*arg_read == 0) {
+        file_read_image(image, IMAGE_SIZE_MAX, image_map, image_chunk_size);
+
+        // Optionally overwrite image with values given on the command-line
+        parse_arg_write(arg_write);
+
+        if (arg_image_map_show)
+            image_map_show();
+    }
+
+    if ((error = target_driver_init(arg_uart_port, 115200,
+                                    arg_uart_flowcontrol)) < 0)
+        main_fatal(error);
+
+    if (target_type_set(arg_target_type) != 0)
+        flowf(VERBOSE, "Target type '%s' selected.\n", target[target_type].name);
+
+    // Conditionally disable tracing
+    if (arg_debug_trace_pe)
+        tr_enable(0);
+
+    if (arg_debug_resume) {
+        // If resuming in command interpreter, we should set the baudrate
+        // because we do not pass through the code that normally sets it.
+        if ((error = target_driver_baudrate(arg_uart_baudrate)) < 0)
+            main_fatal(error);
+    }
+    else {
+        bootloader_machine();
+        cmd_machine();
+    }
+    if (*arg_read != 0) {
+        parse_arg_read(arg_read);
+        flash_read_machine();
+        file_write_image(image, image_size, read_list);
+    }
+    else {
+        if (arg_checksum)
+            flash_checksum_machine();
+        flash_detect_machine();
+        if (!arg_debug_resume) {
+            method_download_machine();
+        }
+        tr_enable(1); // Ensure full tracing is on
+        flash_program_machine();
+        if (*arg_imeisv && *arg_platform_certificate_addr)
+            target_imei_protection();
+        if (arg_timers_show)
+            target_timers_show();
+        if (arg_target_reset == 1)
+            target_reset_machine();
+    }
+}
+
+
+/******************************************************************************
+ * Bootloader (TI Target Monitor) Access
+ ******************************************************************************/
+
+// Invoke the Fluid bootloader embedded in the TI bootloader/target-monitor
+// code. The command to enter the loader is 0xAA 0x01 0xDD, which must be
+// received by the phone within 50ms from reset.
+
+int bootloader_is_rom;
+int bootloader_is_secure_rom;   // Secure Calypso Plus
+int bootloader_blocksize_max;
+
+void bootloader_fluid_init(void);
+void bootloader_rom_init(void);
+void bootloader_secure_rom_init(void);   // Secure Calypso Plus
+
+void bootloader_machine(void)
+{
+    // FIXME: why do we have to send an additional char (0) ?
+    uint8 sendbuf_rom[3] = { '<', 'i', 0x0 };
+    uint8 sendbuf_fluid[3] = { 0xAA, 0x01, 0xDD };
+    int i, error;
+    char ch1, ch2;
+    // Secure Calypso Plus
+    UWORD16 d_j;
+
+    target_trace_enable(0);
+
+    flowf(NORMAL, "Bootloader: ");
+
+    // Wait a short while with power removed, then flush receive buffer.
+    target_power(1);
+    target_reset(0);
+    target_wait(0, TARGET_RESET_DELAY);
+    target_reset(1);
+    target_recv_reset();
+
+    // Continuously send Fluid Bootloader escape sequence until we get an
+    // acknowledgement. Note that we have to establish contact within 50ms
+    // from reset, otherwise we lose our chance! Note that we actually wait
+    // up to 500ms because the target may take some time to reset.
+    i = 0;
+    flowf(DEBUG, "(ROM/fluid-delay = %d/%d, ",
+          arg_boot_delay_rom, arg_boot_delay_fluid);
+
+    switch (arg_rom_bootloader) {
+    case -1:
+        if ((error = target_driver_baudrate(115200)) < 0)
+            main_fatal(error);
+        break;
+    case +1:
+        if ((error = target_driver_baudrate(19200)) < 0)
+            main_fatal(error);
+        break;
+    }
+    while (1)
+    {
+        target_recv_reset();
+
+        // If we are allowed to try fluid bootloader...
+        if (arg_rom_bootloader != +1) {
+            // If we should ONLY try fluid bootloader...
+            flowf(DEBUG, "F");
+            if (arg_rom_bootloader == 0)
+                if ((error = target_driver_baudrate(115200)) < 0)
+                    main_fatal(error);
+            target_send(sendbuf_fluid, 3);
+            if (target_wait(1, arg_boot_delay_fluid) > 0 &&
+                target_getchar() == PROTO_HELLO) {
+                bootloader_is_rom = 0;
+                bootloader_is_secure_rom = 0;   // Secure Calypso Plus
+                break;
+            }
+        }
+        // If we are allowed to try ROM bootloader...
+        if (arg_rom_bootloader != -1) {
+            flowf(DEBUG, "R");
+            if (arg_rom_bootloader == 0)
+                if ((error = target_driver_baudrate(19200)) < 0)
+                    main_fatal(error);
+            target_send(sendbuf_rom, 3);
+
+            if (target_wait(1, arg_boot_delay_rom) >= 1) {
+                ch1 = target_getchar();
+                if (ch1 == '>') {
+                    if (target_wait(1, arg_boot_delay_rom) >= 1) {
+                        ch2 = target_getchar();
+                        if (ch2 == 'i') {
+                            bootloader_is_rom = 1;
+
+                            // Secure Calypso Plus
+                            if (target_wait(2, arg_boot_delay_rom) < 2)
+                                flowf(DEBUG, ", Non-secure boot ROM code");
+                            else {
+                                flowf(DEBUG, ", Secure Calypso Plus ROM)");
+                                ch1 = target_getchar();
+                                ch2 = target_getchar();
+                                d_ram_loader.d_romcode_version = (UWORD16)(ch1) & 0xFF;
+                                d_ram_loader.d_romcode_version |= ((UWORD16)(ch2 & 0xFF)) << 8;
+                                //d_ram_loader.d_romcode_version |= (((UWORD16)ch2) << 8) & 0xFF00;
+
+                                if (d_ram_loader.d_romcode_version == 0x0410 || d_ram_loader.d_romcode_version == 0x0411) {
+                                    bootloader_is_secure_rom = 1;
+
+                                    if (target_wait((C_WORD32LGB * C_MD5HASHLG), TARGET_RECV_DELAY) < (C_WORD32LGB * C_MD5HASHLG))
+                                        main_fatal(E_RECV_TIMEOUT);
+
+                                    for (d_j = 0 ; d_j < (C_WORD32LGB * C_MD5HASHLG) ; d_j++)
+                                        d_ram_loader.a_hash_man_pub_key[d_j] = target_getchar();
+
+                                    if (target_wait((C_WORD32LGB * C_DIE_ID_SIZE), TARGET_RECV_DELAY) < (C_WORD32LGB * C_DIE_ID_SIZE))
+                                        main_fatal(E_RECV_TIMEOUT);
+
+                                    for (d_j = 0 ; d_j < (C_WORD32LGB * C_DIE_ID_SIZE) ; d_j++)
+                                        d_ram_loader.a_die_id[d_j] = target_getchar();
+
+                                    // SIGNALLING RESPONSE
+                                    f_print_signalling_response(VERBOSE);
+
+                                    if (*arg_die_id_file_name != 0) {
+                                        if ((error = file_write_die_id(&d_ram_loader.a_die_id[0], arg_die_id_file_name)) < 0) {
+                                            flowf(NORMAL, "\n");
+                                            main_fatal(error);
+                                        }
+
+                                        flowf(NORMAL, "\nDie id retrieved and written to %s.\n", arg_die_id_file_name);
+                                        exit(0);
+                                    }
+                                }
+                                else {
+                                    flowf(NORMAL, "\nSecure Calypso Plus ROM Code Version 0x%4.4x is not supported by FLUID.\n", d_ram_loader.d_romcode_version);
+                                    main_fatal(E_BOOTLOADER);
+                                }
+                            }
+                            // End Secure Calypso Plus
+
+                            break;
+                        }
+                        else
+                            flowf(DEBUG, "?");
+                    }
+                }
+            }
+        }
+        // If target is still not responding, we could not control the reset
+        // line, so we ask the user to reset the target
+        if (i++ == RETRIES_MAX)
+            flowf (NORMAL, "(reset target) ");
+    }
+    if (!bootloader_is_secure_rom)
+        flowf(DEBUG, ") ");
+
+    // Read the command interpreter image file.
+    if (bootloader_is_secure_rom)
+        target_program_size = file_read_cmd(target_program, TARGET_PROGRAM_SIZE_MAX, a_certified_cmd_file_name);
+    else
+        target_program_size = file_read_cmd(target_program, TARGET_PROGRAM_SIZE_MAX, "cmd.m0");
+
+    if (target_program_size == 0)
+        main_error(E_FILE_EMPTY);
+
+    if (bootloader_is_secure_rom)
+        bootloader_secure_rom_init();
+    else if (bootloader_is_rom)
+        bootloader_rom_init();
+    else
+        bootloader_fluid_init();
+
+    flowf(NORMAL, ") ok\n");
+}
+
+void bootloader_fluid_init(void)
+{
+    int divider;
+    char version;
+    uint8 data[4];
+    uint16 chip_id_code;
+
+    flowf(NORMAL, "(fluid");
+
+    target_clk = 13000000;
+
+    // Now send baudrate to make the target stop sending 'H'ello.
+    divider = target_uart_baudrate_divider_get(target_clk, 115200);
+    flowf(DEBUG, ", baudrate = ");
+    target_putchar(0);
+    target_putchar((char) divider);
+    flowf(DEBUG, "%d, ", 115200);
+
+    // Wait a short while before flushing receive buffer. Then make sure
+    // that the target is indeed silent
+    target_wait(0, 10);
+    target_recv_reset();
+    if (target_wait(1, 50) > 0)
+        main_fatal(E_RECV_ANTITIMEOUT);
+
+    // NOTEME: The sequence below to query the Bootloader VERSION *has* to
+    // have 100ms + 100ms delays. It seems as unnecessarily large delays but
+    // practice shows that they cannot be shorter. To be investigated...
+
+    // Get version of bootloader
+    target_putchar(PROTO_VERSION);
+    if (target_wait(1, TARGET_RECV_DELAY) < 1)
+        main_fatal(E_RECV_TIMEOUT);
+    version = target_getchar();
+    flowf(NORMAL, ", version %c", version);
+
+    // Bootloader revision 3 and upwards supports generic query
+    if (version < '3') {
+        main_fatal(E_BOOTLOADER);
+    }
+
+    target_trace_enable(1);
+    target_putchar(PROTO_QUERY);
+    target_putchar(PROTO_QUERY_CHIP);
+    if (target_wait(4, TARGET_RECV_DELAY) < 4)
+        main_fatal(E_RECV_TIMEOUT);
+    target_recv(data, 4);
+    chip_id_code = data[0] + (data[1] << 8);
+    if (target_type_set(chip_id_code) == 0 || arg_verbose >= VERBOSE) {
+        flowf(VERBOSE, ", chipid = 0x%04X", chip_id_code);
+        if (target_type_set(chip_id_code) == 0)
+            main_fatal(E_TARGET_TYPE);
+    }
+    flowf(VERBOSE, ", %s", target[target_type].name);
+}
+
+void bootloader_rom_init(void)
+{
+    int error;
+    char ch, version = '?';
+
+    // Initialization: 115200 bps, 39 MHz DPLL, no timeout
+    uint8 sendbuf[11] = { '<',  'p',  0x00, 0x0D, 0x14, 0x25,
+                           0x22, 0x00, 0x00, 0x00, 0x00};
+    // Initialization: 115200 bps, 13 MHz DPLL, no timeout.
+    //uint8 sendbuf[11] = { '<',  'p',  0x00, 0x00, 0x1C, 0xE7,
+    //                    0x22, 0x00, 0x00, 0x00, 0x00 };
+
+    flowf(NORMAL, "(ROM");
+
+    // Configure/init the ROM bootloader
+    flowf(VERBOSE, ", baudrate = %d", 115200);
+    target_send(sendbuf, 11);
+
+    // Wait until DPLL is settled and target responds
+    if (target_wait(4, 300) < 4)
+        main_fatal(E_RECV_TIMEOUT);
+    if ((ch = target_getchar ()) != '>')
+        error_proto(ch, '>');
+    if ((ch = target_getchar ()) != 'p')
+        error_proto(ch, 'p');
+
+    // Receive maximum blocksize
+    bootloader_blocksize_max  = target_getchar();
+    bootloader_blocksize_max += target_getchar() << 8;
+    bootloader_blocksize_max -= 10; // Subtract the block header
+    flowf(DEBUG, ", blocksize = %iB", bootloader_blocksize_max);
+
+    if ((error = target_driver_baudrate (115200)) < 0)
+        main_fatal(error);
+
+    flowf(NORMAL, ", version %c", version);
+
+    // NOTEME: Can we be sure that it is always a Calypso type?
+    target_type_set('c');
+}
+
+// Secure Calypso Plus
+void bootloader_secure_rom_init(void)
+{
+    int error;
+    UWORD8 d_char;
+    UWORD16 d_j;
+
+    // cmdp_cert.m0 has been 16 bit aligned using hex470, so the memory width
+    // parameter is set to 2 bytes.
+    buffer_endian_convert(target_program, target_program_size, 2);
+
+    // Check if firmware manufacturer certificate stored in flash is requested
+    if (arg_request_certificate) {
+        // Clear the request
+        arg_request_certificate = 0;
+        d_ram_loader.b_certificate_request = C_FALSE;
+
+        // Certificate Request
+        d_frame.a_data[0] = '<';
+        d_frame.a_data[1] = 'c';
+        d_frame.d_max_byte = 2;
+
+        target_send(&d_frame.a_data[0], d_frame.d_max_byte);
+
+        // Certificate Response
+        target_expect_char('>', arg_boot_delay_rom);
+        target_expect_char('c', arg_boot_delay_rom);
+
+        if (target_wait(2, TARGET_RECV_DELAY) < 2)
+            main_fatal(E_RECV_TIMEOUT);
+
+        d_ram_loader.u_firm_cert.a_firm_cert[0] = target_getchar ();
+        d_ram_loader.u_firm_cert.a_firm_cert[1] = target_getchar ();
+
+        d_certificate_length = (UWORD16)(d_ram_loader.u_firm_cert.a_firm_cert[0]) & 0xFF;
+        d_certificate_length |= ((UWORD16)(d_ram_loader.u_firm_cert.a_firm_cert[1]) << 8) & 0xFF00;
+
+        // We have already read two bytes of the certificate for the CERT_SIZE
+        // and the certificate signature is handled later.
+        for (d_j = 2 ; d_j < d_certificate_length - (C_WORD32LGB * C_MANUF_SIG_SIZE); d_j++) {
+            if (target_wait(1, arg_boot_delay_rom * 4) > 0)
+                d_ram_loader.u_firm_cert.a_firm_cert[d_j] = target_getchar ();
+            else
+                main_fatal(E_RECV_TIMEOUT);
+        }
+
+        for (d_j = 0; d_j < (C_WORD32LGB * C_MANUF_SIG_SIZE); d_j++) {
+            if (target_wait(1, arg_boot_delay_rom * 4) > 0)
+                d_ram_loader.a_Certsig[d_j] = target_getchar ();
+            else
+                main_fatal(E_RECV_TIMEOUT);
+
+            d_ram_loader.u_firm_cert.a_firm_cert[d_j + sizeof(T_MANUFACTURER_CERTIFICATE_PLATFORM_DATA) - (C_WORD32LGB * C_MANUF_SIG_SIZE)] = d_ram_loader.a_Certsig[d_j];
+        }
+
+        f_print_certificate_platform_data(NORMAL, &d_ram_loader.u_firm_cert.d_firm_cert);
+    }
+
+    // Parameter Request
+    d_ram_loader.d_baud_rate = f_convert_uart_baud_rate(arg_uart_baudrate_during_cmd_download);
+    d_ram_loader.d_uart_timeout = arg_uart_timeout_configuration;   // TODO: Verify that MSB byte is sent in first position.
+
+    d_frame.a_data[0] = '<';
+    d_frame.a_data[1] = 'p';
+    d_frame.a_data[2] = d_ram_loader.d_baud_rate;
+    memcpy(&d_frame.a_data[3], &d_ram_loader.d_uart_timeout, sizeof(UWORD32));
+
+    d_frame.d_max_byte = 3 + sizeof(UWORD32);
+
+    d_certificate_length = (UWORD16)(target_program[0]) & 0xFF;
+    d_certificate_length |= ((UWORD16)(target_program[1]) & 0xFF) << 8;
+
+    for (d_j = 0 ; d_j < d_certificate_length; d_j++)
+        d_ram_loader.u_code_certificate.a_code_certificate[d_j] = target_program[d_j];
+
+    f_print_certificate(DEBUG, &d_ram_loader.u_code_certificate.d_code_certificate);
+
+    // Send the data
+    target_send(&d_frame.a_data[0], d_frame.d_max_byte);
+    target_send(&target_program[0], d_certificate_length);
+
+    // Parameter Response
+    if (target_wait(2, TARGET_RECV_DELAY) < 2)
+        main_fatal(E_RECV_TIMEOUT);
+
+    if ((d_char = target_getchar ()) != '>')
+        error_proto(d_char, '>');
+
+    if ((d_char = target_getchar ()) != 'p') {
+        if (d_char == 'P') {
+            // PARAMETER_NACK_RESPONSE
+            if (target_wait(1, arg_boot_delay_rom) >= 1) {
+                d_ram_loader.d_param_req_sts = target_getchar();
+                f_print_parameter_nack_status(NORMAL, d_ram_loader.d_param_req_sts);
+            }
+        }
+        error_proto(d_char, 'p');
+    }
+
+    // PARAMETER_ACK_RESPONSE
+
+    flowf(NORMAL, "\n(Secure ROM");
+
+    if ((error = target_driver_baudrate (arg_uart_baudrate_during_cmd_download)) < 0)
+        main_fatal(error);
+
+    // Configure/init the ROM bootloader
+    flowf(VERBOSE, ", UART baud rate during download of flash programmer, %s = %d Kbps", a_certified_cmd_file_name, arg_uart_baudrate_during_cmd_download);
+}
+// End Secure Calypso Plus
+
+/******************************************************************************
+ * Command Interpreter Download
+ ******************************************************************************/
+
+int cmd_baudrate(int baudrate);
+void cmd_machine_fluid(void);
+void cmd_machine_rom(void);
+void cmd_machine_secure_rom(void);   // Secure Calypso Plus
+
+void cmd_machine(void)
+{
+    char ramcs0, ch;
+    uint8 count = 0;
+    uint16 chip_id;
+
+    flowf(VERBOSE, "Command Interpreter: (");
+
+    target_trace_enable(0);
+
+    // Secure Calypso Plus
+    if (bootloader_is_secure_rom)
+        cmd_machine_secure_rom();
+    else if (bootloader_is_rom)
+        cmd_machine_rom();
+    else
+        cmd_machine_fluid();
+
+    // Now send 'H'ello command to target and get the response, such as
+    // hardware type etc.
+    while (count < RETRIES_MAX) {
+        target_putchar(PROTO_HELLO);
+        if (target_wait(1, TARGET_RECV_DELAY) >= 1) {
+            ch = target_getchar();
+            flowf(DEBUG, ", received %c (0x%2.2x)", ch, ch);
+            if (ch == PROTO_READY)
+                break;
+        }
+        count++;
+    }
+
+    if (count == RETRIES_MAX)
+        main_fatal(E_RECV_TIMEOUT);
+
+    if (target_wait(5, TARGET_RECV_DELAY) < 4)
+        main_fatal(E_RECV_TIMEOUT);
+
+    chip_id  = (target_getchar() & 0xFF);
+    chip_id |= (target_getchar() & 0xFF) << 8;
+    ramcs0   = (target_getchar() == 'R');
+    ch       = target_getchar();
+    flowf(BLABBER, ", SRAM = %dk", (1 << ch) / 1024);
+    target_fifo_size = target_getchar();
+    flowf(DEBUG, ", fifo = %d", target_fifo_size);
+
+    if (target_type_set(chip_id) == 0 || arg_verbose >= VERBOSE) {
+        flowf(BLABBER, ", ");
+        flowf(VERBOSE, "chipid = 0x%04X", chip_id);
+        if (target_type_set(chip_id) == 0)
+            main_fatal(E_TARGET_TYPE);
+    }
+    flowf(VERBOSE, ", %s", target[target_type].name);
+
+    if (ramcs0)
+        flowf(VERBOSE, ", RAM");
+
+    // Configure target
+    target_putchar(PROTO_INIT);
+    target_putchar(target[target_type].type);
+    target_expect_char(PROTO_READY, TARGET_RECV_DELAY);
+
+    // Change baudrate. First try to change to new baudrate. If this fails,
+    // try 115.2 Kbps. If this also fails, panic and bail out.
+    if (arg_uart_baudrate != 115200) {
+        flowf(VERBOSE, ", baudrate = ");
+        flowf(VERBOSE, "%d", arg_uart_baudrate);
+        if (!cmd_baudrate(arg_uart_baudrate)) {
+            arg_uart_baudrate = 115200;
+            flowf(VERBOSE, " %d", arg_uart_baudrate);
+            if (!cmd_baudrate(arg_uart_baudrate))
+                main_fatal(E_RECV_TIMEOUT);
+        }
+    }
+    flowf(VERBOSE, ") ");
+    flowf(VERBOSE, "ok\n");
+
+    // Secure Calypso Plus
+
+    // There is a bug in ROM code 0x0410 which means that the secure boot
+    // loader cannot boot if the firmware certificate size is greater than the
+    // max size of 0xFFF8. A workaround is to change the memory mapping on CS5
+    // by initially setting DIP switches 9 and 10 to ON and thereby exchanging
+    // the mapping of RAM and flash. While waiting, set the DIP switches back
+    // to OFF. See SECURITY.txt and BUG03314 in CALPLUS228.
+    if (arg_delay_for_changing_cs5 != 0) {
+        flowf(NORMAL, "\nWaiting %d seconds for changing memory mapping on CS5 or connecting via JTAG...\n", arg_delay_for_changing_cs5);
+        target_wait(0, arg_delay_for_changing_cs5 * 1000);
+    }
+}
+
+int cmd_baudrate(int baudrate)
+{
+    int error;
+    int divider;
+    char xxo;
+
+    target_putchar(PROTO_BAUDRATE);
+    if (baudrate == 230400 || baudrate == 460800 || baudrate == 921600) {
+        // Changing the target clock frequency to 14 MHz
+        target_clk = 14745600;
+        // If we are using a 14 MHz compatible baud rate, we should enable the
+        // eXternal Xtal Oscillator of the target
+        xxo = PROTO_BAUDRATE_XXO;
+    }
+    else {
+        target_clk = 13000000;
+        xxo = 0;
+    }
+
+    divider = target_uart_baudrate_divider_get(target_clk, baudrate);
+    target_putchar(xxo);
+    target_putchar((char) divider);
+
+    if ((error = target_driver_baudrate(baudrate)) < 0)
+        main_fatal(error);
+
+    // Wait for acknowledgement
+    if (target_wait(1, 2 * TARGET_RECV_DELAY) >= 1 &&
+        target_getchar() == PROTO_READY) {
+        return 1;
+    }
+    else {
+        if ((error = target_driver_baudrate(115200)) < 0)
+            main_fatal(error);
+        return 0;
+    }
+}
+
+void cmd_machine_fluid(void)
+{
+    uint8 sendbuf[1+4+2];
+    int error;
+
+    // Send 'Download' command header. Then wait for acknowledgement.
+    buf_put1(&sendbuf[0],   PROTO_DOWNLOAD);
+    buf_put4(&sendbuf[1],   target[target_type].cmd_load_addr);
+    buf_put2(&sendbuf[1+4], (uint16) target_program_size / 2);
+    target_send(sendbuf, 1+4+2);
+    flowf(BLABBER, "0x%X", target[target_type].cmd_load_addr);
+    if ((error = target_expect_char(PROTO_READY, TARGET_RECV_DELAY)) < 0)
+        main_fatal(error);
+
+    // Send data. Then wait for acknowledgement.
+    target_trace_enable(1);
+    target_send(target_program, target_program_size);
+    flowf(BLABBER, ", %d", target_program_size);
+}
+
+void cmd_machine_rom(void)
+{
+    uint8 sendbuf[10], *buf;
+    int error, i;
+    int block_addr;
+    int block_offset = 0;
+    int block_size;
+    uint8 ch, blksum, cksum = 0;
+
+    // NOTEME: Can we be sure that it is always a Calypso type?
+    target_type_set('c');
+
+    block_addr = target[target_type].cmd_load_addr;
+
+    buffer_endian_convert(target_program, target_program_size, 2);
+
+    flowf(BLABBER, "0x%X, ", target[target_type].cmd_load_addr);
+
+    // Transfer the target program as blocks
+    while (block_offset < target_program_size)
+    {
+        block_size = target_program_size - block_offset;
+        if (block_size > bootloader_blocksize_max)
+            block_size = bootloader_blocksize_max;
+
+        // Initialize block transfer
+        buf  = sendbuf;
+        buf += buf_put1(buf, '<');
+        buf += buf_put1(buf, 'w');
+        buf += buf_put1(buf, 0x01); // block index - just use #1 - not important
+        buf += buf_put1(buf, 0x01); // block number - just use #1 - not important
+        buf += buf_put2no(buf, (uint16) block_size);
+        buf += buf_put4no(buf, block_addr + block_offset);
+        target_send(sendbuf, 10);
+
+        // Send the data
+        target_send(&target_program[block_offset], block_size);
+
+        // Calculate block check-sum
+        blksum  = 5;
+        blksum += block_size & 0x00ff;
+        blksum += (((block_addr + block_offset) & 0xff000000) >> 24);
+        blksum += (((block_addr + block_offset) & 0x00ff0000) >> 16);
+        blksum += (((block_addr + block_offset) & 0x0000ff00) >> 8);
+        blksum += ( (block_addr + block_offset) & 0x000000ff);
+        for (i = block_offset; i < (block_offset + block_size); i++)
+            blksum += target_program[i];
+        cksum += ~blksum;
+
+        block_offset = block_offset + block_size;
+
+        if ((error = target_expect_char ('>', TARGET_RECV_DELAY)) < 0)
+            main_fatal (error);
+        if ((error = target_expect_char ('w', TARGET_RECV_DELAY)) < 0)
+            main_fatal (error);
+
+        flowf(BLABBER, ".");
+    }
+    flowf(BLABBER, ", %d", target_program_size);
+    target_wait(0, 100);
+
+    // Request compare of checksum
+    target_putchar('<');
+    target_putchar('c');
+    target_putchar((char) ~cksum);
+
+    if ((error = target_expect_char('>', TARGET_RECV_DELAY)) < 0)
+        main_fatal(error);
+    if ((error = target_expect_char('c', TARGET_RECV_DELAY)) < 0)
+        main_fatal(error);
+    if ((error = target_wait(1, TARGET_RECV_DELAY)) < 1)
+        main_fatal(error);
+    ch = target_getchar();
+    flowf(DEBUG, ", cksum = 0x%x (0x%x)", ch, cksum);
+    if (ch != cksum)
+        main_fatal(E_SEND_CHECKSUM);
+
+    // Branch to code
+    buf  = sendbuf;
+    buf += buf_put1(buf, '<');
+    buf += buf_put1(buf, 'b');
+    buf += buf_put4no(buf, target[target_type].cmd_load_addr);
+    target_send(sendbuf, 6);
+
+    if (target_wait(2, TARGET_RECV_DELAY) < 2)
+        main_fatal(E_RECV_TIMEOUT);
+    if ((ch = target_getchar()) != '>')
+        error_proto(ch, '>');
+    if ((ch = target_getchar()) != 'b')
+        error_proto(ch, 'b');
+
+    target_wait(0, 100);
+}
+
+// Secure Calypso Plus
+void cmd_machine_secure_rom(void)
+{
+    UWORD8 d_char;
+    UWORD16 d_i;
+
+    target_type_set('p');
+    flowf(BLABBER, "0x%X", d_ram_loader.u_code_certificate.d_code_certificate.d_Addcode);
+    flowf(DEBUG, ")");
+
+    // Prepare the write command
+    d_ram_loader.d_nb_byte_in_block = (UWORD32) arg_block_size;
+    d_ram_loader.d_nb_byte_sent = 0;
+    d_ram_loader.d_block_address = d_ram_loader.u_code_certificate.d_code_certificate.d_Addcode;
+
+    while (d_ram_loader.d_nb_byte_sent < d_ram_loader.u_code_certificate.d_code_certificate.d_Codesize) {
+        flowf(DEBUG, "\nCurrent write parameters:\n");
+        flowf(DEBUG, "        Max Number of Bytes in Block: %ld\n", d_ram_loader.d_nb_byte_in_block);
+        flowf(DEBUG, "        Block Address               : 0x%8.8lx\n", d_ram_loader.d_block_address);
+        flowf(DEBUG, "        Code Size                   : %ld\n", d_ram_loader.u_code_certificate.d_code_certificate.d_Codesize);
+        flowf(DEBUG, "        Bytes sent                  : %ld\n", d_ram_loader.d_nb_byte_sent);
+
+        // Set block size
+        d_ram_loader.d_block_size = d_ram_loader.u_code_certificate.d_code_certificate.d_Codesize - d_ram_loader.d_nb_byte_sent;
+        if (d_ram_loader.d_block_size > d_ram_loader.d_nb_byte_in_block)
+            d_ram_loader.d_block_size = d_ram_loader.d_nb_byte_in_block;
+
+        flowf(DEBUG, "        Block Size                  : %ld\n", d_ram_loader.d_block_size);
+
+        // Write Request
+        d_frame.a_data[0] = '<';
+        d_frame.a_data[1] = 'w';
+
+        // Send block size
+        for (d_i = 0; d_i < 32; d_i += 8)
+            d_frame.a_data[2 + d_i / 8] = (UWORD8)(d_ram_loader.d_block_size >> (24 - d_i));
+
+        // Send block address
+        for (d_i = 0; d_i < 32; d_i += 8)
+            d_frame.a_data[2 + sizeof(UWORD32) + d_i / 8] = (UWORD8)(d_ram_loader.d_block_address >> (24 - d_i));
+
+        d_frame.d_max_byte = 2 + sizeof(UWORD32) + sizeof(UWORD32);
+
+        // Send the data
+        target_send(&d_frame.a_data[0], d_frame.d_max_byte);
+        target_send(&target_program[d_certificate_length + d_ram_loader.d_nb_byte_sent], d_ram_loader.d_block_size);
+
+        // Update next block address
+        d_ram_loader.d_block_address += d_ram_loader.d_block_size;
+        d_ram_loader.d_nb_byte_sent  += d_ram_loader.d_block_size;
+
+        // Write Response
+        if (target_wait(2, TARGET_RECV_DELAY) < 2)
+            main_fatal(E_RECV_TIMEOUT);
+
+        if ((d_char = target_getchar ()) != '>')
+            error_proto(d_char, '>');
+
+        if ((d_char = target_getchar ()) != 'w') {
+            if (d_char == 'W') {
+                // WRITE_NACK_RESPONSE
+                if (target_wait(1, arg_boot_delay_rom) >= 1) {
+                    d_ram_loader.d_write_status = target_getchar();
+                    f_print_write_nack_status(NORMAL, d_ram_loader.d_write_status);
+                }
+            }
+            error_proto(d_char, 'w');
+        }
+
+        // WRITE_ACK_RESPONSE
+    }  // End while
+    if (arg_verbose == BLABBER)
+        flowf(BLABBER, ", ");
+    else
+        flowf(DEBUG, "(");
+
+    flowf(BLABBER, "%d", d_ram_loader.d_nb_byte_sent);
+    target_wait(0, 100);
+
+    // Abort Request
+    d_frame.a_data[0] = '<';
+    d_frame.a_data[1] = 'a';
+    d_frame.d_max_byte = 2;
+
+    target_send(&d_frame.a_data[0], d_frame.d_max_byte);
+    target_wait(0, 100);
+}
+
+void f_print_signalling_response(int level) {
+    UWORD16 d_i;
+
+    flowf(level, "\nSignalling Response:\n");
+
+    flowf(level, "        ROM Code Version: 0x%4.4x\n", d_ram_loader.d_romcode_version);
+
+    flowf(level, "        Hash (ManPubKey): ");
+    for (d_i = 0; d_i < (C_WORD32LGB * C_MD5HASHLG); d_i++) {
+        if (d_i == (C_WORD32LGB * C_MD5HASHLG) / 2)
+            flowf(level, "\n                          ");
+        flowf(level, "0x%2.2x ", d_ram_loader.a_hash_man_pub_key[d_i]);
+    }
+    flowf(level, "\n");
+
+    flowf(level, "        Die Id          : ");
+    for (d_i = 0; d_i < (C_WORD32LGB * C_DIE_ID_SIZE); d_i++)
+        flowf(level, "0x%2.2x ", d_ram_loader.a_die_id[d_i]);
+    flowf(level, "\n");
+} /* f_print_signalling_response() */
+
+void f_print_certificate_platform_data(int level, T_MANUFACTURER_CERTIFICATE_PLATFORM_DATA *p_certificate) {
+    UWORD16 d_i, d_j, d_max = 4;
+
+    flowf(level, "\nFirmware Manufacturer Certificate:\n");
+    flowf(level, "----------------------------------\n");
+
+    flowf(level, "        Size of Certificate: %d bytes\n", p_certificate->d_manufacturer_certificate.d_Certsize);
+    flowf(level, "        Type of Certificate: 0x%2.2x\n", p_certificate->d_manufacturer_certificate.d_Certtype);
+    flowf(level, "        Emulation Request  : 0x%2.2x\n", p_certificate->d_manufacturer_certificate.d_Debugrequest);
+    flowf(level, "        Address of Code    : 0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_Addcode);
+    flowf(level, "        Size of Code       : %ld bytes\n", p_certificate->d_manufacturer_certificate.d_Codesize);
+    flowf(level, "        Entry Point Address: 0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_CodeStartAdd);
+
+    flowf(level, "        Manufacturer Public Key:\n");
+    flowf(level, "                Public Modulus:");
+    d_j = d_max;
+    for (d_i = 0; d_i < (C_RSAKEYLG); d_i++) {
+        if (d_j++ == d_max) {
+            flowf(level, "\n                        ");
+            d_j = 1;
+        }
+        flowf(level, "0x%8.8lx ", p_certificate->d_manufacturer_certificate.d_Manpubkey.a_Modulus[d_i]);
+    }
+    flowf(level, "\n");
+    flowf(level, "                Public Modulus Length:\n");
+    flowf(level, "                        0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_Manpubkey.d_ModulusLength);
+    flowf(level, "                Public Exponent:\n");
+    flowf(level, "                        0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_Manpubkey.d_Exponent);
+
+    flowf(level, "        Originator Public Key:\n");
+    flowf(level, "                Public Modulus:");
+    d_j = d_max;
+    for (d_i = 0; d_i < (C_RSAKEYLG); d_i++) {
+        if (d_j++ == d_max) {
+            flowf(level, "\n                        ");
+            d_j = 1;
+        }
+        flowf(level, "0x%8.8lx ", p_certificate->d_manufacturer_certificate.d_Origpubkey.a_Modulus[d_i]);
+    }
+    flowf(level, "\n");
+    flowf(level, "                Public Modulus Length:\n");
+    flowf(level, "                        0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_Origpubkey.d_ModulusLength);
+    flowf(level, "                Public Exponent:\n");
+    flowf(level, "                        0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_Origpubkey.d_Exponent);
+
+    flowf(level, "        Originator Public Key Signature:");
+    d_j = d_max;
+    for (d_i = 0; d_i < (C_RSASIGLG); d_i++) {
+        if (d_j++ == d_max) {
+            flowf(level, "\n                ");
+            d_j = 1;
+        }
+        flowf(level, "0x%8.8lx ", p_certificate->d_manufacturer_certificate.a_Origpubkeysig[d_i]);
+    }
+    flowf(level, "\n");
+
+    flowf(level, "        Software Signature:");
+    d_j = d_max;
+    for (d_i = 0; d_i < (C_RSASIGLG); d_i++) {
+        if (d_j++ == d_max) {
+            flowf(level, "\n                ");
+            d_j = 1;
+        }
+        flowf(level, "0x%8.8lx ", p_certificate->d_manufacturer_certificate.a_Swsig[d_i]);
+    }
+    flowf(level, "\n");
+
+    flowf(level, "        Configuration Parameters:\n");
+    flowf(level, "                CONF_CS5 register: %4.4x\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_conf_cs5);
+    flowf(level, "                EXWS_CS5 register: %4.4x\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_exws_cs5);
+    flowf(level, "                EX_CTRL register : %4.4x\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_ex_ctrl);
+    flowf(level, "                CS image request : %4.4x\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_cs_img_req);
+    flowf(level, "                Flash size       : %ld bytes\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_flash_size);
+    flowf(level, "                Granularity      : %ld words\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_granularity);
+
+    flowf(level, "        Die Id: ");
+    for(d_i = 0; d_i < (C_DIE_ID_SIZE); d_i++) {
+        flowf(level, "0x%8.8lx ", p_certificate->d_manufacturer_certificate.a_die_id[d_i]);
+    }
+    flowf(level, "\n");
+
+    if (p_certificate->d_manufacturer_certificate.d_Certsize > (sizeof(T_MANUFACTURER_CERTIFICATE) + (C_WORD32LGB * C_MANUF_SIG_SIZE))) {
+        flowf(level, "        Platform Data:");
+        d_j = d_max;
+        for(d_i = 0; d_i < (p_certificate->d_manufacturer_certificate.d_Certsize - sizeof(T_MANUFACTURER_CERTIFICATE) - (C_WORD32LGB * C_MANUF_SIG_SIZE)) / sizeof(UWORD32); d_i++) {
+            if (d_j++ == d_max) {
+                flowf(level, "\n                ");
+                d_j = 1;
+            }
+            flowf(level, "0x%8.8lx ", p_certificate->a_platform_data[d_i]);
+        }
+        flowf(level, "\n");
+    }
+
+    flowf(level, "        Certificate Signature:");
+    d_j = d_max;
+    for (d_i = 0; d_i < (C_MANUF_SIG_SIZE); d_i++) {
+        if (d_j++ == d_max) {
+            flowf(level, "\n                ");
+            d_j = 1;
+        }
+        flowf(level, "0x%8.8lx ", p_certificate->a_Certsig[d_i]);
+    }
+    flowf(level, "\n");
+} /* f_print_certificate_platform_data() */
+
+void f_print_certificate(int level, T_MANUFACTURER_CERTIFICATE_FLASH_PROGRAMMER *p_certificate) {
+    UWORD16 d_i, d_j, d_max = 4;
+
+    flowf(level, "\nFlash Programmer Manufacturer Certificate:\n");
+    flowf(level, "------------------------------------------\n");
+
+    flowf(level, "        Size of Certificate: %d bytes\n", p_certificate->d_Certsize);
+    flowf(level, "        Type of Certificate: 0x%2.2x\n", p_certificate->d_Certtype);
+    flowf(level, "        Emulation Request  : 0x%2.2x\n", p_certificate->d_Debugrequest);
+    flowf(level, "        Address of Code    : 0x%8.8lx\n", p_certificate->d_Addcode);
+    flowf(level, "        Size of Code       : %ld bytes\n", p_certificate->d_Codesize);
+    flowf(level, "        Entry Point Address: 0x%8.8lx\n", p_certificate->d_CodeStartAdd);
+
+    flowf(level, "        Manufacturer Public key:\n");
+    flowf(level, "                Public Modulus:");
+    d_j = d_max;
+    for (d_i = 0; d_i < (C_RSAKEYLG); d_i++) {
+        if (d_j++ == d_max) {
+            flowf(level, "\n                        ");
+            d_j = 1;
+        }
+        flowf(level, "0x%8.8lx ", p_certificate->d_Manpubkey.a_Modulus[d_i]);
+    }
+    flowf(level, "\n");
+    flowf(level, "                Public Modulus Length:\n");
+    flowf(level, "                        0x%8.8lx\n", p_certificate->d_Manpubkey.d_ModulusLength);
+    flowf(level, "                Public Exponent:\n");
+    flowf(level, "                        0x%8.8lx\n", p_certificate->d_Manpubkey.d_Exponent);
+
+    flowf(level, "        Originator Public key:\n");
+    flowf(level, "                Public Modulus:");
+    d_j = d_max;
+    for (d_i = 0; d_i < (C_RSAKEYLG); d_i++) {
+        if (d_j++ == d_max) {
+            flowf(level, "\n                        ");
+            d_j = 1;
+        }
+        flowf(level, "0x%8.8lx ", p_certificate->d_Origpubkey.a_Modulus[d_i]);
+    }
+    flowf(level, "\n");
+    flowf(level, "                Public Modulus Length:\n");
+    flowf(level, "                        0x%8.8lx\n", p_certificate->d_Origpubkey.d_ModulusLength);
+    flowf(level, "                Public Exponent:\n");
+    flowf(level, "                        0x%8.8lx\n", p_certificate->d_Origpubkey.d_Exponent);
+
+    flowf(level, "        Originator Public Key Signature:");
+    d_j = d_max;
+    for (d_i = 0; d_i < (C_RSASIGLG); d_i++) {
+        if (d_j++ == d_max) {
+            flowf(level, "\n                ");
+            d_j = 1;
+        }
+        flowf(level, "0x%8.8lx ", p_certificate->a_Origpubkeysig[d_i]);
+    }
+    flowf(level, "\n");
+
+    flowf(level, "        Software Signature:");
+    d_j = d_max;
+    for (d_i = 0; d_i < (C_RSASIGLG); d_i++) {
+        if (d_j++ == d_max) {
+            flowf(level, "\n                ");
+            d_j = 1;
+        }
+        flowf(level, "0x%8.8lx ", p_certificate->a_Swsig[d_i]);
+    }
+    flowf(level, "\n");
+
+    flowf(level, "        Configuration Parameters:\n");
+    flowf(level, "                CONF_CS5 register: %4.4x\n",p_certificate->d_Confparam.d_conf_cs5);
+    flowf(level, "                EXWS_CS5 register: %4.4x\n",p_certificate->d_Confparam.d_exws_cs5);
+    flowf(level, "                EX_CTRL register : %4.4x\n",p_certificate->d_Confparam.d_ex_ctrl);
+    flowf(level, "                CS image request : %4.4x\n",p_certificate->d_Confparam.d_cs_img_req);
+    flowf(level, "                Flash size       : %ld bytes\n",p_certificate->d_Confparam.d_flash_size);
+    flowf(level, "                Granularity      : %ld words\n",p_certificate->d_Confparam.d_granularity);
+
+    flowf(level, "        Die Id: ");
+    for(d_i = 0; d_i < (C_DIE_ID_SIZE); d_i++) {
+        flowf(level, "0x%8.8lx ", p_certificate->a_die_id[d_i]);
+    }
+    flowf(level, "\n");
+
+    flowf(level, "        Certificate Signature:");
+    d_j = d_max;
+    for (d_i = 0; d_i < (C_MANUF_SIG_SIZE); d_i++) {
+        if (d_j++ == d_max) {
+            flowf(level, "\n                ");
+            d_j = 1;
+        }
+        flowf(level, "0x%8.8lx ", p_certificate->a_Certsig[d_i]);
+    }
+    flowf(level, "\n");
+} /* f_print_certificate() */
+
+void f_print_parameter_nack_status(int level, UWORD8 d_parameter_nack_sts) {
+    flowf(level, "\nParameter NAck Response Status:\n");
+    flowf(level, "        0x%2.2x", d_parameter_nack_sts);
+
+    switch(d_parameter_nack_sts) {
+        case 0x01 : { flowf(level, "    Incorrect baud rate\n"); break; }
+        case 0x02 : { flowf(level, "    Incorrect certificate\n"); break; }
+        case 0x03 : { flowf(level, "    Incorrect code address\n"); break; }
+    }
+
+    flowf(level, "\n");
+} /* f_print_parameter_nack_status() */
+
+void f_print_write_nack_status(int level, UWORD8 d_write_nack_sts) {
+    flowf(level, "\nWrite NAck Response Status:\n");
+    flowf(level, "        0x%2.2x", d_write_nack_sts);
+
+    switch(d_write_nack_sts) {
+        case 0x01 : { flowf(level, "    Incorrect block address\n"); break; }
+        case 0x02 : { flowf(level, "    Non-64 bytes block size\n"); break; }
+        case 0x03 : { flowf(level, "    First block is not code address\n"); break; }
+        case 0x04 : { flowf(level, "    Error in firmware signature check\n"); break; }
+        case 0x05 : { flowf(level, "    Received code size does not match the code size in certificate\n"); break; }
+        case 0x06 : { flowf(level, "    Error during block hashing\n"); break; }
+    }
+
+    flowf(level, "\n");
+} /* f_print_write_nack_status() */
+
+UWORD8 f_convert_uart_baud_rate(UWORD32 d_baud_rate) {
+    UWORD8 d_converted_baud_rate;
+
+    switch (d_baud_rate) {
+        case 0:
+        case 812:
+        case 812500: {
+            d_converted_baud_rate = 0x00;
+            arg_uart_baudrate_during_cmd_download = 812500;
+            break;
+        }
+        case 1:
+        case 406:
+        case 406250: {
+            d_converted_baud_rate = 0x01;
+            arg_uart_baudrate_during_cmd_download = 406250;
+            break;
+        }
+        case 2:
+        case 203:
+        case 203125: {
+            d_converted_baud_rate = 0x02;
+            arg_uart_baudrate_during_cmd_download = 203125;
+            break;
+        }
+        case 3:
+        case 115:
+        case 115200: {
+            d_converted_baud_rate = 0x03;
+            arg_uart_baudrate_during_cmd_download = 115200;
+            break;
+        }
+        case 4:
+        case 57:
+        case 57600: {
+            d_converted_baud_rate = 0x04;
+            arg_uart_baudrate_during_cmd_download = 57600;
+            break;
+        }
+        case 5:
+        case 38:
+        case 38400: {
+            d_converted_baud_rate = 0x05;
+            arg_uart_baudrate_during_cmd_download = 38400;
+            break;
+        }
+        case 6:
+        case 28:
+        case 28800: {
+            d_converted_baud_rate = 0x06;
+            arg_uart_baudrate_during_cmd_download = 28800;
+            break;
+        }
+        case 7:
+        case 19:
+        case 19200: {
+            d_converted_baud_rate = 0x07;
+            arg_uart_baudrate_during_cmd_download = 19200;
+            break;
+        }
+        default: {
+            d_converted_baud_rate = 0x03;
+            arg_uart_baudrate_during_cmd_download = 115200;
+            break;
+        }
+    }
+
+    return d_converted_baud_rate;
+} /* f_convert_uart_baud_rate() */
+
+void target_imei_protection(void)
+{
+    uint8 a_imeisv[C_IMEISV_BYTES];
+    uint8 a_platform_cert_addr[C_PLATFORM_CERT_ADDR_BYTES];
+    uint8 d_i, d_digit, d_temp;
+    uint8 d_error;
+
+    flowf(NORMAL, "\nIMEI protection: ");
+
+    if (arg_dry_run) {
+        flowf(NORMAL, "(dry-run) ok\n");
+        return;
+    }
+
+    target_putchar(PROTO_IMEI_PROTECT);
+
+    // Send IMEI-SV
+    for (d_i = 0; d_i < C_IMEISV_DIGITS; d_i++) {
+        sscanf(arg_imeisv++, "%1d", &d_digit);
+        if (!(d_i & 1))
+            d_temp = d_digit << 4;
+        else {
+            d_temp |= d_digit;
+            a_imeisv[d_i / 2] = d_temp;
+        }
+    }
+
+    target_send(a_imeisv, C_IMEISV_BYTES);
+    target_expect_char(PROTO_READY, TARGET_RECV_DELAY);
+
+    // Send platform certificate address
+    for (d_i = 0; d_i < C_PLATFORM_CERT_ADDR_DIGITS; d_i++) {
+        sscanf(arg_platform_certificate_addr++, "%1x", &d_digit);
+        if (!(d_i & 1))
+            d_temp = d_digit << 4;
+        else {
+            d_temp |= d_digit;
+            a_platform_cert_addr[d_i / 2] = d_temp;
+        }
+    }
+
+    target_send(a_platform_cert_addr, C_PLATFORM_CERT_ADDR_BYTES);
+    target_expect_char(PROTO_READY, TARGET_RECV_DELAY);
+
+    // Check if address range is erased
+    if (target_wait(1, TARGET_RECV_DELAY) < 1)
+        main_fatal(E_RECV_TIMEOUT);
+
+    d_error = target_getchar();
+    if (d_error == PROTO_ERROR_VERIFY) {
+        flowf(NORMAL, "\n The address range for platform certificate and IMEI-SV is not erased.\n");
+        main_fatal(E_FLASH_VERIFY);
+    }
+
+    if (d_error != PROTO_READY)
+        error_proto(d_error, PROTO_READY);
+
+    // Verify that the binding service call in target has succeeded
+    if (target_wait(1, TARGET_RECV_DELAY) < 1)
+        main_fatal(E_RECV_TIMEOUT);
+
+    d_error = target_getchar();
+    if (d_error == PROTO_ERROR_ROM_SSERVICE) {
+        flowf(NORMAL, "\n The Run-Time Loader (Binding) Service failed.\n");
+        main_fatal(E_ROM_SSERVICE);
+    }
+
+    if (d_error != PROTO_FLASH_START)
+        error_proto(d_error, PROTO_FLASH_START);
+
+    // Receive acknowledgement for programming the platform certificate
+    if (target_wait(1, TARGET_RECV_DELAY) < 1)
+        main_fatal(E_RECV_TIMEOUT);
+
+    d_error = target_getchar();
+    if (d_error != PROTO_PROGRAM) {
+        flowf(NORMAL, "\n Platform certificate was not stored correctly in flash.\n");
+        error_proto(d_error, PROTO_PROGRAM);
+    }
+
+    // Receive acknowledgement for programming the IMEI-SV
+    if (target_wait(1, TARGET_RECV_DELAY) < 1)
+        main_fatal(E_RECV_TIMEOUT);
+
+    d_error = target_getchar();
+    if (d_error != PROTO_PROGRAM) {
+        flowf(NORMAL, "\n IMEI-SV was not stored correctly in flash.\n");
+        error_proto(d_error, PROTO_PROGRAM);
+    }
+
+    flowf(NORMAL, "Platform certificate and IMEI-SV stored in flash.\n");
+}
+
+// End Secure Calypso Plus
+
+/******************************************************************************
+ * Flash Detect
+ ******************************************************************************/
+
+void flash_detect_machine(void)
+{
+    uint8  data[12];
+    uint16 m0, d0, m1, d1, d1ex1, d1ex2;
+
+    flowf(NORMAL, "Flash Detect: ");
+    target_trace_enable(0);
+
+    if (arg_device_id0 != -1 || arg_device_id1 != -1) {
+        // Device auto-detection is disabled
+        m0 = m1 = arg_device_id0;
+        d0 = d1 = arg_device_id1;
+        flowf(VERBOSE, "(ID override) ");
+    }
+    else {
+        target_putchar(PROTO_DETECT);
+        if (target_wait(12, TARGET_RECV_DELAY) < 12)
+            main_fatal(E_RECV_TIMEOUT);
+
+        target_recv(data, 12);
+        m0 = data[0] + (data[1] << 8); // Intel manufacturer id
+        d0 = data[2] + (data[3] << 8); // Intel device id
+        m1 = data[4] + (data[5] << 8); // AMD manufacturer id
+        d1 = data[6] + (data[7] << 8); // AMD device di
+        d1ex1 = data[8] + (data[9] << 8);   // AMD extended device id
+        d1ex2 = data[10] + (data[11] << 8); // AMD extended device id
+    }
+
+    // Lookup multi-id device
+    if ((m1 == MANUFACT_AMD || m1 == MANUFACT_FUJITSU) && d1 == 0x227E) {
+        flowf(NORMAL, "Multi-id device detected: (0x%04X, 0x%04X, 0x%04X)\n"
+              , d1, d1ex1, d1ex2);
+        d1 = (d1ex1 << 8) | (d1ex2 & 0xFF);
+        flowf(DEBUG, "Multi-id converted to: 0x%04X\n", d1);
+        
+        if ((device = device_lookup_by_id(m1, d1)) == NULL) {
+            flowf(NORMAL, "Id not found, lookup default multi-id conf.\n");
+            // Backward compatible/keep default multi-id configuration
+            device = device_lookup_by_id(m1, 0x227E);
+        }
+    }
+    
+    // Lookup device with AMD and Intel ids
+    else if ((device = device_lookup_by_id(m1, d1)) == NULL)
+        device = device_lookup_by_id(m0, d0);
+
+    if (device == NULL || arg_verbose >= DEBUG) {
+        flowf(NORMAL, "(0x%02X, 0x%04X, 0x%02X, 0x%04X) ", m0, d0, m1, d1);
+        if (device == NULL)
+            main_fatal(E_FLASH_UNKNOWN);
+    }
+
+    if (arg_verbose >= NORMAL) {
+        device_print(device, 's');
+        flowf(NORMAL, " ok\n");
+    }
+
+    // Note that device_id is only 0x227E if the Multi-id configuration not
+    // is found in device.txt (else device_id will be the 'converted' id)
+    if (arg_device_id0 == -1 && arg_device_id1 == -1 &&
+        (device->manufacturer_id == MANUFACT_FUJITSU || 
+         device->manufacturer_id == MANUFACT_AMD ) 
+        && device->device_id == 0x227E) {
+        flowf(NORMAL, "Warning: Extended device codes are supported when detecting flash devices,\n");
+        flowf(NORMAL, "         but the detected multi-id configuration is not found in device.txt. \n");
+        flowf(NORMAL, "         Update the device.txt manually if the default flash device is invalid.\n");
+        flowf(NORMAL, "         Currently, %s %s is used as default.\n\n",
+            manufacturer_name_lookup_by_id(device->manufacturer_id), device->name);
+    }
+}
+
+
+/******************************************************************************
+ * Method Load
+ ******************************************************************************/
+
+void method_download_machine(void)
+{
+    char sendbuf[1+2+4];
+    int error;
+
+    target_program_size = file_read_method(target_program,
+                                           TARGET_PROGRAM_SIZE_MAX,
+                                           device);
+
+    flowf(BLABBER, "Method Download: ");
+    target_trace_enable(0);
+
+    // Send 'Download' command header. Then wait for acknowledgement.
+    buf_put1(&sendbuf[0],   PROTO_DOWNLOAD);
+    buf_put4(&sendbuf[1],   target[target_type].method_load_addr);
+    buf_put2(&sendbuf[1+4], (uint16) target_program_size / 2);
+    target_send(sendbuf, 1+4+2);
+    flowf(BLABBER, "(0x%X", target[target_type].method_load_addr);
+    if ((error = target_expect_char(PROTO_READY, TARGET_RECV_DELAY)) < 0)
+        main_fatal(error);
+
+    // Send data. Then wait for acknowledgement.
+    target_trace_enable(1);
+    target_send(target_program, target_program_size);
+    flowf(BLABBER, ", %d) ", target_program_size);
+    if ((error = target_expect_char(PROTO_READY, TARGET_RECV_DELAY)) < 0)
+        main_fatal(error);
+
+    flowf(BLABBER, "ok\n");
+}
+
+
+/******************************************************************************
+ * Flash Checksum
+ ******************************************************************************/
+
+int time_checksum;
+
+#define CHECKSUMS 8
+
+void flash_checksum_machine(void)
+{
+    unsigned char sendbuf[1+1+4+4*CHECKSUMS], *buf;
+    uint32 addr, cksum, mycksum;
+    uint16 word;
+    uint8  data[4];
+    int index = 0, chunks, n, i, j;
+    int line = -1;
+    struct {
+        uint32 index;
+        uint32 addr;
+        uint32 mycksum;
+    } block[CHECKSUMS];
+
+    chunks = image_map_count_used_chunks();
+    flowf(NORMAL, "Checksumming (%d * %dkB = %dkB): ",
+          chunks, image_chunk_size / 1024,
+          chunks * image_chunk_size / 1024);
+    target_trace_enable(0);
+
+    time_checksum = stopwatch_start();
+    while (chunks > 0)
+    {
+        n = (chunks > CHECKSUMS ? CHECKSUMS : chunks);
+
+        buf = sendbuf;
+        buf += buf_put1(buf, PROTO_CHECKSUM);
+        buf += buf_put1(buf, (unsigned char) n);
+        buf += buf_put4(buf, image_chunk_size);
+
+        for (i = 0; i < n; i++) {
+            // Find next used entry in image_map
+            while (image_map[index] != 'x' && index < image_map_size)
+                index++;
+
+            if (index == image_map_size)
+                break;
+
+            addr = index * image_chunk_size;
+            block[i].index   = index;
+            block[i].addr    = addr;
+
+            buf += buf_put4(buf, addr);
+
+            index++;
+        }
+        target_send(sendbuf, buf - sendbuf);
+
+        // Compute checksums while we wait for reply
+        for (i = 0; i < n; i++) {
+            index = block[i].index;
+            addr  = block[i].addr;
+            for (j = 0, mycksum = 0; j < image_chunk_size; j += 2) {
+                word = (image[addr + j + 1] << 8) | image[addr + j + 0];
+                mycksum += word * ((addr + j) & 0xFFFF);
+            }
+            block[i].mycksum = mycksum;
+        }
+
+        if (target_wait(4 * n, TARGET_RECV_LONG_DELAY) <= 0)
+            main_fatal(E_RECV_TIMEOUT);
+
+        for (i = 0; i < n; i++) {
+            target_recv(data, 4);
+            cksum   = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24);
+            mycksum = block[i].mycksum;
+            index   = block[i].index;
+
+            if (cksum == mycksum)
+                image_map[index] = 'c';
+
+            if (arg_checksum_show) {
+                // This is a far from perfect dump of checksums... We don't know
+                // the exact block number of each checksum in a line.
+                if (line != (int) index / 4) {
+                    flowf(NORMAL, "\n%4d:", index);
+                    line = index / 4;
+                }
+                if (cksum == mycksum)
+                    flowf(NORMAL, "%08X          ", cksum);
+                else
+                    flowf(NORMAL, "%08X/%08X ", cksum, mycksum);
+
+            }
+            else if (arg_verbose >= VERBOSE) {
+                flowf(VERBOSE, "%c", (image_map[index] == 'c' ? 'c' : '.'));
+            }
+        }
+        index++;
+        chunks -= n;
+    }
+    time_checksum = (stopwatch_stop(time_checksum) + 50) / 100;
+    flowf(BLABBER, " (%d.%ds)", time_checksum / 10, time_checksum % 10);
+    flowf(NORMAL, " ok\n");
+}
+
+
+/******************************************************************************
+ * Flash Program
+ ******************************************************************************/
+
+int time_program;
+int programs_recv, programs_send, erasures;
+int index, src_size;
+uint32 dst;
+char *src;
+
+void flash_erase_machine(void);
+int  flash_operation_wait(int delay);
+int  flash_program_next(void);
+
+void flash_program_machine(void)
+{
+    uint8 cksum, sendbuf[1+4+4];
+    int i, te, tp, tt;
+    int expected, chunks, total_size, sectors;
+    int image_chunk_size_old = image_chunk_size;
+    char ch;
+
+    programs_recv = programs_send = erasures = 0;
+    index = 0;
+
+    // Prepare erase
+    erase_list_size = sectors = sector_map_init();
+    i = image_map_count_used_chunks();
+
+    if (arg_skip_erase)
+        erase_list_size = 0;
+
+    time_compute(device, erase_list_size,
+                 i * image_chunk_size, i, &te, &tp, &tt);
+
+    flowf(BLABBER, "Estimated time (uncompressed) ");
+    if (te + tp > tt)
+        flowf(BLABBER, "(erase + program = total): %ds + %ds = %ds\n",
+              te/1000, tp/1000, te/1000 + tp/1000);
+    else
+        flowf(BLABBER, "transfer: %ds\n", tt/1000);
+
+    chunks = image_map_count_used_chunks();
+    flowf(NORMAL, "Program: (%d sectors, %d*%dk=%dk) ",
+          sectors,
+          chunks, image_chunk_size / 1024,
+          chunks * image_chunk_size / 1024);
+    target_trace_enable(1);
+
+    // Expected total number of acknowledgements
+    expected = erase_list_size + chunks;
+
+    if (arg_dry_run) {
+        flowf(NORMAL, "(dry-run) ");
+        if (arg_dry_run == 2)
+            image_chunk_size = 0;
+    }
+
+    if (arg_skip_erase)
+        flowf(NORMAL, "(skip erase) ");
+
+    // Start erase
+    flash_erase_machine();
+
+    if (arg_dry_run == 0 || arg_dry_run == 2)
+        progress_begin(expected);
+
+    // Start the flash state machine in the target
+    target_putchar(PROTO_FLASH_START);
+    target_putchar(arg_compress ? PROTO_COMPRESS : 0);
+    ch = flash_operation_wait(TARGET_RECV_DELAY);
+    if (ch != PROTO_READY)
+        error_proto(ch, PROTO_READY);
+    if (arg_compress) {
+        compress_init();
+        total_size = 0;
+        // Worst case "compressed" buffer is 9/8 of original data size.
+        if ((src = malloc(9 * image_chunk_size / 8)) == NULL)
+            main_fatal(E_MEMORY);
+    }
+
+    time_program = stopwatch_start();
+
+    if (flash_program_next() && *arg_erase_override == 0) {
+        //Nothing to program
+        target_putchar(PROTO_FLASH_END);
+        if ((ch = target_expect_char(PROTO_FLASH_END, TARGET_RECV_DELAY)) < 0)
+            main_fatal(ch);
+    }
+    else {
+        while (programs_recv + erasures < expected) {
+            total_size += src_size;
+
+            if (arg_progress == 'x')
+                flowf(NORMAL, "(0x%06X, %d) ", dst, src_size);
+
+            if (src_size > 0 &&
+                (arg_dry_run == 0 || arg_dry_run == 2))
+            {
+                buf_put1(&sendbuf[0],   PROTO_PROGRAM);
+                buf_put4(&sendbuf[1],   src_size);
+                buf_put4(&sendbuf[1+4], dst);
+                target_send(sendbuf, 1+4+4);
+
+                for (i = 0, cksum = 0; i < src_size; i++)
+                    cksum += src[i];
+                cksum = (0x100 - cksum) & 0xFF;
+
+                // Wait for acknowledgement of command start
+                //while ((ch = flash_operation_wait(4000)) != PROTO_READY)
+                //    ;
+
+                // Make sure that the target FIFO not is full
+                while (programs_send - programs_recv >= target_fifo_size)
+                    flash_operation_wait(4000);
+
+                // Send data bytes and checksum.
+                target_send(src, src_size);
+                target_putchar(cksum);
+                programs_send++;
+
+                if (arg_verbose >= DEBUG)
+                    progress_update_simple('T');
+            }
+
+            // We compress and send next block while sending
+            // current block to target
+            if (arg_dry_run == 0 || arg_dry_run == 2) {
+                if (flash_program_next()) {
+                    // Wait for remaining acknowledgements
+                    flowf(DEBUG, "W%d", expected - (programs_recv + erasures));
+                    while (programs_recv + erasures < expected)
+                        flash_operation_wait(4000);
+                    target_putchar(PROTO_FLASH_END);
+                    if ((ch = target_expect_char(PROTO_FLASH_END, TARGET_RECV_DELAY)) < 0)
+                        main_fatal(ch);
+                    break;
+                }
+                else {
+                    // Wait for acknowledgement of data transfer
+                    while ((ch = flash_operation_wait(4000)) != PROTO_READY)
+                        ;
+                }
+            }
+            else {
+                if (flash_program_next())
+                    break;
+            }
+        }
+    }
+
+    if (arg_dry_run == 0 || arg_dry_run == 2)
+        progress_end(programs_recv + erasures);
+    flowf(NORMAL, "ok\n");
+
+    time_program = stopwatch_stop(time_program);
+    flowf(VERBOSE, "Used time: ");
+    if (arg_compress && chunks) {
+        image_chunk_size = image_chunk_size_old;
+        flowf(VERBOSE, "(compressed to %d%%) ",
+              100 * total_size / (chunks * image_chunk_size));
+    }
+    flowf(VERBOSE, "%.1fs ok\n", (double) time_program / 1000);
+}
+
+
+/******************************************************************************
+ * Flash Program sub functions
+ ******************************************************************************/
+
+void flash_erase_machine(void)
+{
+    uint8 sendbuf[1+2+4*256]; // NOTEME: static limit!
+    int i;
+    char ch;
+
+    if (arg_dry_run)
+        erase_list_size = 0;
+
+    if (erase_list_size > 0) {
+        buf_put1(&sendbuf[0], PROTO_ERASE);
+        buf_put2(&sendbuf[1], (uint16) erase_list_size);
+        for (i = 0; i < erase_list_size; i++)
+            buf_put4(&sendbuf[1+2+4*i], erase_list[i]);
+        target_send(sendbuf, 1+2+4*erase_list_size);
+
+        if ((ch = target_expect_char(PROTO_READY, TARGET_RECV_DELAY)) < 0)
+            main_fatal(ch);
+    }
+}
+
+// Setup address and size of next block to transfer. Also compress the
+// block. Return non-zero if this was the last block. Otherwise return zero.
+int flash_program_next(void)
+{
+    int oldindex;
+
+    // Find next used entry in image_map
+    while (index < image_map_size && image_map[index] != 'x')
+        index++;
+
+    oldindex = index;
+
+    if (index < image_map_size) {
+        dst = index * image_chunk_size;
+        if (arg_compress) {
+            src_size = compress(src, &image[dst], image_chunk_size);
+        }
+        else {
+            src = &image[dst];
+            src_size = image_chunk_size;
+        }
+        index++;
+    }
+    else {
+        dst = 0xFFFFFFFF;
+        src_size = 0;
+    }
+
+    return (oldindex >= image_map_size);
+}
+
+int flash_operation_wait(int delay)
+{
+    int n;
+    char ch;
+
+    tr(TrMachines, "fow() ");
+
+    if ((n = target_wait(1, delay)) < 1)
+        main_fatal(E_RECV_TIMEOUT);
+
+    // Minor optimization to avoid waiting for every char...
+    while (n--) {
+        ch = target_getchar();
+
+        if (ch == PROTO_READY) {
+            if (arg_verbose >= DEBUG)
+                progress_update_simple('R');
+            if (programs_send - programs_recv >= target_fifo_size)
+                progress_update_simple('W');  // Wait target FIFO is full
+            break;
+        }
+
+        switch (ch) {
+        case PROTO_READY:
+            break; // just return the char
+        case PROTO_PROGRAM:
+            programs_recv++;
+            progress_update_simple('P');
+            progress_update(programs_recv + erasures);
+            break; // just return the char
+        case PROTO_ERASE:
+            erasures++;
+            progress_update_simple('E');
+            progress_update(programs_recv + erasures);
+            if (arg_progress == 'x')
+                flowf(NORMAL, "E ");
+            break; // just return the char
+        case PROTO_FLASH_END:
+            progress_update_simple('Z');
+            break;
+        case PROTO_ERROR_CKSUM:
+            main_fatal(E_SEND_CHECKSUM);
+        case PROTO_ERROR_MEMORY:
+            main_fatal(E_MEMORY);
+        case PROTO_ERROR_VERIFY:
+            main_fatal(E_FLASH_VERIFY);
+        case PROTO_ERROR_FLASH_TIMEOUT:
+            main_fatal(E_FLASH_TIMEOUT);
+        case PROTO_ERROR_FLASH_COMMAND:
+            main_fatal(E_FLASH_COMMAND);
+        case PROTO_ERROR_FLASH_VPP:
+            main_fatal(E_FLASH_VPPRANGE);
+        case PROTO_ERROR_FLASH_LOCKED:
+            main_fatal(E_FLASH_LOCKED);
+        case PROTO_ERROR_FLASH_UNKNOWN:
+            main_fatal(E_FLASH_ERROR);
+        case PROTO_ERROR_INVALID:
+            main_fatal(E_INVALID);
+        case PROTO_ERROR_FIFO_OVERFLOW:
+            main_fatal(E_FIFO_OVERFLOW);
+        case PROTO_ERROR:
+        default:
+            flowf(NORMAL, "flash_operation_wait() got unexpected char '%c' 0x%02X (%d chars waiting)\n", (' ' <= ch  && ch  < 127 ? ch  : '.'), ch, n);
+//            main_fatal(E_PROTO_ERROR);
+        }
+    }
+
+    return ch;
+}
+
+void target_timers_show(void)
+{
+    int data[8], i;
+    struct {
+        double erase;
+        double program;
+        double recvonly;
+        double recv;
+        double comm;
+        double setup;
+        double overhead;
+        double dezip;
+        double erase_sector;
+        double program_word;
+    } timer;
+    double tmp, total, resolution = (double) (16 * 32 * 1000 / 13e6);
+
+    // Use only 14 MHz for D-/E-Sample specific rates (not for e.g. 812.5K).
+    if ((arg_uart_baudrate == 230400) || (arg_uart_baudrate == 460800) || (arg_uart_baudrate == 921600))
+        resolution = resolution * 13 / 14.746;
+
+    flowf(NORMAL,
+          "Target Timers:\n"
+          "  (erase + program + recvonly +   recv +  comm + setup + overhead + dezip)\n");
+
+    if (arg_dry_run)
+        for (i = 0; i < 8; i++) data[i] = 0;
+    else {
+        target_putchar(PROTO_TIMERS);
+        if (target_wait(sizeof(data), TARGET_RECV_DELAY) <= 0)
+            main_fatal(E_RECV_TIMEOUT);
+        target_recv(&data, sizeof(data));
+    }
+
+    // Convert all timers to milliseconds
+    timer.erase    = resolution * data[0];
+    timer.program  = resolution * data[1];
+    timer.recvonly = resolution * data[2];
+    timer.recv     = resolution * data[3];
+    timer.comm     = resolution * data[4];
+    timer.setup    = resolution * data[5];
+    timer.overhead = resolution * data[6];
+    timer.dezip    = resolution * data[7];
+
+    // Because we do erase-while-transfer in the target the reported erase
+    // time has some inherent tolerance, especially at low baudrates. It
+    // might make sense to adjust the sector erase time by half the chunk
+    // transfer time, although this is just another approximation!?
+
+//    total = timer.erase + timer.program + timer.recvonly + timer.recv + timer.comm + timer.setup;
+    flowf(NORMAL, "%8.0f + %7.0f + %8.0f + %6.0f + %5.0f + %5.0f + %8.0f + %5.0f\n",
+          timer.erase, timer.program, timer.recvonly, timer.recv,
+          timer.comm, timer.setup, timer.overhead, timer.dezip);
+
+    flowf(DEBUG, "resolution = %f, %d, %d, %d, %d, %d, %d, %d, %d\n",
+          resolution, data[0], data[1], data[2], data[3], data[4],
+          data[5], data[6], data[7]);
+
+    if (arg_timers_extended_show) {
+        int chunks, size_total, not_zero = 1;
+        flowf(NORMAL, "Target Timers Extended:\n");
+
+        if (erase_list_size == 0 || arg_dry_run)
+            flowf(NORMAL, "  Erase time         = 0.0ms/sector\n");
+        else {
+            timer.erase_sector = timer.erase / erase_list_size / 1000;
+            flowf(NORMAL, "  Erase time         = %.1fs / %d sectors = %.0fms/sector\n",
+                  timer.erase / 1000, erase_list_size, timer.erase_sector * 1000);
+        }
+
+        chunks = image_map_count_used_chunks();
+        size_total = chunks * image_chunk_size;
+        if (size_total == 0 || arg_dry_run) {
+            size_total = 1;
+            not_zero = 0;
+            flowf(NORMAL, "  Program time       = 0.0us/word = 0ms/MB\n");
+        }
+        else {
+            timer.program_word = timer.program / 1000 / (size_total / 2);
+            flowf(NORMAL, "  Program time       = %.1fs / %dkwords = %.2fus/word = %.0fms/MB\n",
+                  timer.program / 1000, size_total / 1024 / 2,
+                  timer.program_word * 1000000,
+                  timer.program_word * 1000 * 512 * 1024);
+        }
+
+        flowf(NORMAL, "  Receive-only time  = %.1fs = %.0fms/MB\n",
+              timer.recvonly / 1000,
+              not_zero * timer.recvonly * 1024 * 1024 / size_total);
+
+        flowf(NORMAL, "  Overhead time      = %.1fs = %.0fms/MB\n",
+              timer.overhead / 1000,
+              not_zero * timer.overhead * 1024 * 1024 / size_total);
+
+        flowf(NORMAL, "  Setup time         = %.1fs = %.0fms/MB\n",
+              timer.setup / 1000,
+              not_zero * timer.setup * 1024 * 1024 / size_total);
+
+        total = timer.erase + timer.program + timer.recvonly + timer.overhead +
+            timer.setup;
+        flowf(NORMAL,
+              "Total time: (erase + prog + recvonly + overhead + setup) = %.1fs\n",
+              total / 1000);
+
+        flowf(NORMAL, "  Receive time       = %.1fs = %.0fms/MB\n",
+              timer.recv / 1000, timer.recv * 1024 * 1024 / size_total);
+
+        flowf(NORMAL, "  Communication time = %.1fs = %.0fms/MB\n",
+              timer.comm / 1000, timer.comm * 1024 * 1024 / size_total);
+
+        flowf(NORMAL, "  Dezip time         = %.1fs = %.0fms/MB\n",
+              timer.dezip / 1000, timer.dezip * 1024 * 1024 / size_total);
+
+        // Now compute the overall performance. Note that the theoretical
+        // limit compuation is somewhat flawed in that it assumes 64kB
+        // sector sizes.
+
+        tmp = (double) not_zero * total / 1000 * 1024 * 1024 / size_total;
+        flowf(NORMAL, "  Performance = %.0fs/MB\n", tmp);
+#if 0
+        flowf(BLABBER, " (%.1f * min possible, %.1f * theoretical limit)",
+              time_program / (timer.program + timer.erase),
+              tmp / (16 * timer.erase_sector + 512 * 1024 * timer.program_word));
+        flowf(NORMAL, "\n");
+#endif
+    }
+}
+
+
+/******************************************************************************
+ * Flash Read
+ ******************************************************************************/
+
+int time_read;
+
+void flash_read_machine(void)
+{
+    int i;
+    unsigned char sendbuf[1+4+4];
+    int size, read_size, done_size = 0;
+    int read_size_max = image_chunk_size;
+    uint32 addr, index = 0;
+    uint8 cksum, mycksum;
+
+    flowf(NORMAL, "Reading Flash: (%dkB) ", read_total_size / 1024);
+    target_trace_enable(0);
+
+    //read_size_max = 256;
+    time_read = stopwatch_start();
+    progress_begin(read_total_size / read_size_max);
+
+    while (read_list[index].size > 0)
+    {
+        // Find next range to read
+        size = read_list[index].size;
+        addr = read_list[index].addr;
+        index++;
+
+        while (size > 0)
+        {
+            read_size = (size > read_size_max ? read_size_max : size);
+
+            // Read address interval is [MIN..MAX[
+            // If odd MAX address was specified round up to next even
+            if ((read_size % 2 == 1) && (read_size < read_size_max)) read_size++;
+
+            if (arg_progress == 'x')
+                flowf(DEBUG, "(0x%06X, %d) ", addr, read_size);
+
+            if (!arg_dry_run)
+            {
+                buf_put1(&sendbuf[0],   PROTO_READ);
+                buf_put4(&sendbuf[1]  , read_size);
+                buf_put4(&sendbuf[1+4], addr);
+                target_send(sendbuf, 1+4+4);
+
+                if (target_wait(read_size, TARGET_RECV_LONG_DELAY) <= 0)
+                    main_fatal(E_RECV_TIMEOUT);
+                // Note that we wrap/mirror memory each 'image_size' bytes
+                target_recv(&image[addr & (image_size - 1)], read_size);
+                for (i = 0, mycksum = 0; i < read_size; i++)
+                    mycksum ^= image[(addr + i) & (image_size - 1)];
+
+                if (target_wait(1, TARGET_RECV_DELAY) <= 0)
+                    main_fatal(E_RECV_TIMEOUT);
+                cksum = target_getchar();
+
+                if (cksum != mycksum)
+                    main_fatal(E_RECV_CHECKSUM);
+            }
+            done_size += read_size;
+            size -= read_size;
+            addr += read_size;
+
+            progress_update(done_size / read_size_max);
+            progress_update_simple('0' + read_size / 1024);
+        }
+    }
+    progress_end(done_size / read_size_max);
+    flowf(NORMAL, " ok\n");
+
+    time_read = stopwatch_stop(time_read);
+    flowf(VERBOSE, "Used time: %ds ok\n", time_read);
+}
+
+
+/******************************************************************************
+ * Flash Reset
+ ******************************************************************************/
+
+void target_reset_machine(void)
+{
+    int error;
+
+    flowf(VERBOSE, "Resetting target: ");
+
+    if (arg_dry_run) {
+        flowf(VERBOSE, "(dry-run) ");
+    }
+    else {
+        target_putchar(PROTO_RESET);
+        target_expect_char(PROTO_READY, TARGET_RECV_DELAY);
+    }
+    flowf(VERBOSE, "ok\n");
+}
+
+
+/******************************************************************************
+ * Show Functions (dump internal data structures)
+ ******************************************************************************/
+
+void image_map_show(void)
+{
+    uint32 addr;
+    int i;
+
+    flowf(ALWAYS, "image map of %d * %dkB chunks (x = used, s = skip, c = checksum ok):",
+           image_size_in_chunks, image_chunk_size / 1024);
+
+    // For each chunk of the image usage map...
+    for (i = 0, addr = 0; i < image_size_in_chunks; i++)
+    {
+        if ((i % 64) == 0) {
+            flowf(ALWAYS, "\n%4dkB: ", (int) addr >> 10);
+        }
+        putchar(image_map[i] != 0 ? image_map[i] : '.');
+        addr += image_chunk_size;
+    }
+    putchar('\n');
+}
+
+void sector_map_show(void)
+{
+    struct sector_s *sectors = device->memmap->sectors;
+    int i;
+
+    char n;
+    uint32 addr = 0;
+
+    flowf(ALWAYS, "sector map (x = used, s = skip, X = force erase):");
+
+        // For each sector of the device definition...
+    for (i = 0; i < device->memmap->size; i++)
+    {
+        if ((addr & 0xFFFFF) == 0) {
+            flowf(ALWAYS, "\n%2dMB: ", (int) addr >> 20);
+        }
+        n = (sector_map[i] ? sector_map[i] : '.');
+        putchar(n);
+
+        addr += sectors[i].size;
+    }
+    putchar('\n');
+}
+
+
+/******************************************************************************
+ * Utility Functions
+ ******************************************************************************/
+
+int image_is_within(int start, int end)
+{
+    return !(start < 0 || image_size <= start ||
+             end < 0   || image_size < end ||
+             end < start);
+}
+
+// Set the image usage map in the range [start..end[ as used
+int image_map_set(int start, int end)
+{
+    if (!image_is_within(start, end))
+        return -1;
+
+    do {
+        image_map[start / image_chunk_size] = 'x';
+        start += image_chunk_size;
+    } while (start < end);
+
+    return 0;
+}
+
+int target_type_set(uint16 code)
+{
+    // If there is an override from the command-line, use that
+    if (arg_target_type != 0)
+        code = arg_target_type;
+
+    switch (code)
+    {
+    case 'h': // Hercules
+    case 'u': // Ulysses
+    case '3': // Chipset 3
+    case CHIP_ID_ULYSSES_0:
+    case CHIP_ID_ULYSSES_A:
+    case CHIP_ID_HERCULES_A:
+    case CHIP_ID_HERCULES_B:
+        target_type = 3;
+        break;
+
+    case 'c': // Calypso
+    case '4': // Chipset 4
+    // case 's': // Samson
+    case CHIP_ID_CALYPSO_A:
+    case CHIP_ID_CALYPSO_B:
+    case CHIP_ID_CALYPSO_C:
+        target_type = 4;
+        break;
+
+    case 'l':  // Calypso Lite
+    case CHIP_ID_CALYPSO_L:
+        target_type = 5;
+        break;
+
+    case 'p':  // Calypso Plus
+    case CHIP_ID_CALYPSO_PLUS:
+    case CHIP_ID_CALYPSO_PLUS_A:
+        target_type = 6;
+        break;
+
+    default:
+        target_type = 0;
+    }
+
+    return target_type;
+}
+
+int image_map_count_used_chunks(void)
+{
+    int used, i;
+
+    // For each image chunk
+    for (i = 0, used = 0; i < image_size_in_chunks; i++) {
+        if (image_map[i] == 'x')
+            used++;
+    }
+    return used;
+}
+
+// When the sector_map have been changed, we have to update the image_map
+// such that we don't attempt to program within sectors that are not going
+// to be programmed anyway (due to the erase override). We also have to
+// program chunks contained in sectors that *are* going to be erased, even
+// though the chunks have been check-summed ok.
+int image_map_update(void)
+{
+    struct sector_s *sectors = device->memmap->sectors;
+    int changed, chunks, i, j;
+    int map_index;
+
+    map_index = 0;
+    changed = 0;
+
+    // For each sector of the device definition...
+    for (i = 0; i < device->memmap->size; i++) {
+        chunks = sectors[i].size / image_chunk_size;
+        // For each image--map-chunk contained in current sector...
+        for (j = 0; j < chunks; j++) {
+            if (!(sector_map[i] == 'x' || sector_map[i] == 'X')  &&
+                 image_map[map_index + j] == 'x') {
+                image_map[map_index + j] = 's'; // skip
+                changed++;
+            }
+            else if ((sector_map[i] == 'x' || sector_map[i] == 'X')
+                     && image_map[map_index + j] == 'c') {
+                image_map[map_index + j] = 'x'; // include!
+                changed++;
+            }
+        }
+        map_index += chunks;
+    }
+    return changed;
+}
+
+int sector_map_init(void)
+{
+    struct sector_s *sectors = device->memmap->sectors;
+    int map_index;
+    int used_list_index;
+    int used, chunks, i, j;
+
+    sector_map_size = SECTOR_MAP_SIZE_MAX;
+
+    memset(sector_map, 0, sector_map_size);
+
+    // Generate the sector_map from the image_map
+    map_index = 0;
+
+    // For each sector of the device definition...
+    for (i = 0; i < device->memmap->size; i++) {
+
+        chunks = sectors[i].size / image_chunk_size;
+
+        // For each image-usage-map-chunk contained in current sector...
+        for (j = 0, used = 0; j < chunks; j++)
+            if (image_map[map_index + j] == 'x')
+                used++;
+
+        sector_map[i] = (used ? 'x' : 0);
+        map_index += chunks;
+    }
+
+    parse_arg_erase_override(arg_erase_override);
+
+    // Generate the erase_list
+    // For each sector of the device definition...
+    used_list_index = 0;
+    for (i = 0; i < device->memmap->size; i++) {
+        if (sector_map[i] == 'x' || sector_map[i] == 'X')
+            erase_list[used_list_index++] = sectors[i].addr;
+    }
+
+    if (arg_sector_map_show)
+        sector_map_show();
+
+    if (arg_sector_list_show) {
+        flowf(ALWAYS, "sector used list: ");
+        for (i = 0; i < used_list_index; i++)
+            flowf(ALWAYS, "0x%06X ", erase_list[i]);
+        putchar('\n');
+    }
+
+    return used_list_index;
+}
+
+int parse_range(char *p, char **p_end, int *n1, int *n2)
+{
+    char *my_end;
+    int read_offset_calp = (*arg_read != 0 && target[target_type].type == 'P');
+
+    *n2 = 0;
+
+    *n1 = strtol(p, &my_end, 0);
+    if (p == my_end)
+        return 0; // error: no chars converted
+
+    if (*my_end == 'k' || *my_end == 'K') {
+        *n1 <<= 10;
+        if (read_offset_calp) *n1 += CALP_OFFSET;
+        my_end++;
+    }
+    else if (*my_end == 'M') {
+        *n1 <<= 20;
+        if (read_offset_calp) *n1 += CALP_OFFSET;
+        my_end++;
+    }
+
+    *p_end = my_end;
+    if (my_end[0] != '.' || my_end[1] != '.')
+        return 1;
+    p = my_end + 2;
+
+    *n2 = strtol(p, &my_end, 0);
+    if (p == my_end)
+        return 0; // error: no chars converted
+
+    if (*my_end == 'k' || *my_end == 'K') {
+        *n2 <<= 10;
+        if (read_offset_calp) *n2 += CALP_OFFSET;
+        my_end++;
+    }
+    else if (*my_end == 'M') {
+        *n2 <<= 20;
+        if (read_offset_calp) *n2 += CALP_OFFSET;
+        my_end++;
+    }
+
+    *p_end = my_end;
+    return 2;
+}
+
+// Parse and decode the erase override command line option string. The
+// sector_map is updated accordingly.
+void parse_arg_erase_override(char *p)
+{
+    struct sector_s *sectors = device->memmap->sectors;
+    int sector_bottom, sector_top;
+    int changed, i;
+
+    tr(TrBegin| TrArgParser, "erase_override:\n");
+    while (*p)
+    {
+        char *p_end;
+        int num, sign, n1 = 0, n2 = 0;
+
+        if (*p != '-' && *p != '+')
+            main_error(E_ERASE_SPEC);
+
+        sign = (*p == '-' ? -1 : +1);
+        p++;
+
+        if (*p == '*') {
+            sign = sign * 2;
+            p++;
+        }
+        else {
+            num = parse_range(p, &p_end, &n1, &n2);
+            if (num == 0)
+                main_error(E_ERASE_SPEC);
+            p = p_end;
+        }
+
+        switch (sign) {
+        case -1:
+        case +1:
+            if (num == 1) {
+                // Support for absolute addresses on Calypso Plus
+                if (n1 >= CALP_OFFSET) n1 -= CALP_OFFSET;
+
+                if (n1 > sector_map_size) {
+                    // For all sectors...
+                    for (i = 0; i < device->memmap->size; i++) {
+                        sector_bottom = sectors[i].addr;
+                        sector_top    = sector_bottom + sectors[i].size - 1;
+
+                        if (n1 >= sector_bottom && n1 < sector_top) {
+                            sector_map[i] = (sign > 0 ? 'X' : 's');
+                            break;
+                        }
+                    }
+                }
+                else
+                    sector_map[n1] = (sign > 0 ? 'X' : 's');
+                tr(TrArgParser, "%c%d\n", sign > 0 ? '+' : '-', n1);
+            }
+            else {
+                // Support for absolute addresses on Calypso Plus
+                if (n1 >= CALP_OFFSET && n2 >= CALP_OFFSET) {
+                    n1 -= CALP_OFFSET;
+                    n2 -= CALP_OFFSET;
+                }
+
+                if (n1 > sector_map_size || n2 > sector_map_size) {
+                    // n1 and n2 must respresent an address range
+                    if (n1 >= n2)
+                        main_error(E_ERASE_SPEC);
+
+                    // For all sectors...
+                    for (i = 0; i < device->memmap->size; i++)
+                    {
+                        sector_bottom = sectors[i].addr;
+                        sector_top    = sector_bottom + sectors[i].size - 1;
+
+                        // If either sector bottom or top is contained in
+                        // [n1..n2[
+                        if ((n1 <= sector_bottom && sector_bottom < n2) ||
+                            (n1 <= sector_top    && sector_top    < n2)) {
+                            sector_map[i] = (sign > 0 ? 'X' : 's');
+                        }
+                    }
+                }
+                else {
+                    // n1 and n2 must respresent a sector range
+                    for (i = n1; i < n2; i++)
+                        sector_map[i] = (sign > 0 ? 'X' : 's');
+                }
+                tr(TrArgParser,
+                   "%c%d..%d\n", sign > 0 ? '+' : '-', n1, n2);
+            }
+            break;
+        case -2:
+        case +2:
+            // Fill whole sector_map...
+            for (i = 0; i < device->memmap->size; i++)
+                sector_map[i] = (sign > 0 ? 'X' : 's');
+            tr(TrArgParser, "%c*\n", sign > 0 ? '+' : '-');
+            break;
+        }
+        // skip optional comma
+        if (*p == ',')
+            p++;
+    }
+    tr(TrEnd| TrArgParser, "");
+
+    changed = image_map_update();
+
+    // If image map was changed, we trace it again.
+    if (changed && arg_image_map_show)
+        image_map_show();
+}
+
+void parse_arg_read(char *p)
+{
+    read_total_size = 0;
+    read_list_size  = 0;
+
+    tr(TrArgParser, "parse_arg_read() {\n");
+    while (*p)
+    {
+        char *p_end;
+        int num, n1, n2;
+
+        if (*p == '*') {
+            n1 = 0;
+            n2 = 0x1000000; // sufficiently large (16MB)
+            p++;
+        }
+        else {
+            num = parse_range(p, &p_end, &n1, &n2);
+            tr(TrArgParser, "parse_range('%s', ...)\n"
+               "    { #%d, p_end = '%s', n1 = 0x%x, n2 = 0x%x } %d\n",
+               p, read_list_size, p_end, n1, n2, num);
+            if (num != 2 || n1 > n2)
+                main_error(E_ADDR_RANGE);
+            p = p_end;
+        }
+        read_list[read_list_size].addr = n1;
+        read_list[read_list_size].size = n2 - n1;
+        read_total_size += n2 - n1;
+        read_list_size++;
+        if (read_list_size >= READ_LIST_SIZE_MAX)
+            main_error(E_ARG_TOOMANY);
+
+        if (*p == 0)
+            break;
+
+        if (*p == ',')
+            p++;
+        else
+            main_error(E_READ_SPEC);
+    }
+    // Terminate the read_list with zeroes.
+    read_list[read_list_size].addr = 0;
+    read_list[read_list_size].size = 0;
+
+    tr(TrArgParser, "}\n");
+}
+
+void parse_arg_write(char *p)
+{
+    tr(TrArgParser, "parse_arg_write() {\n");
+
+    while (*p)
+    {
+        char bytes[1024];
+        char *p_end;
+        int num, n1, n2;
+        int index, value, size, i;
+
+        num = parse_range(p, &p_end, &n1, &n2);
+        tr(TrArgParser, "parse_range('%s', ...)\n"
+           "    { p_end = '%s', n1 = 0x%x, n2 = 0x%x } %d\n",
+           p, p_end, n1, n2, num);
+
+        if (num < 1 || num > 2)
+            main_error(E_ADDR_RANGE);
+
+        p = p_end;
+
+        if (*p++ != '=')
+            main_error(E_WRITE_SPEC);
+
+        tr(TrArgParser, "    { ");
+        if (*p == 0)
+            main_error(E_WRITE_SPEC);
+
+        index = 0;
+        while (*p) {
+            if (*p == '\"') {
+                // Collect text string
+                p++;
+                tr(TrCont|TrArgParser, "'");
+                while (*p) {
+                    if (*p == '\"')
+                        break;
+                    if (p[0] == '\\' && p[1] == '\"')
+                        p++; // skip leading backslash
+                    if (p[0] == '\\' && p[1] == '\\')
+                        p++; // skip leading backslash
+                    tr(TrCont|TrArgParser, "%c", *p);
+                    bytes[index] = *p++;
+                    if (index++ >= sizeof(bytes))
+                        main_error(E_WRITE_SPEC);
+                }
+                if (*p++ != '\"')
+                    main_error(E_WRITE_SPEC);
+                tr(TrCont|TrArgParser, "' ");
+            }
+            else {
+                // Collect byte string
+                value = strtol(p, &p_end, 0);
+                if (p == p_end)
+                    main_error(E_WRITE_SPEC);
+                if (value < 0 || 255 < value)
+                    main_error(E_WRITE_SPEC);
+                bytes[index] = value;
+                if (index++ >= sizeof(bytes))
+                    main_error(E_WRITE_SPEC);
+                p = p_end;
+
+                tr(TrCont|TrArgParser, "0x%x ", value);
+            }
+            if (*p == ':' || *p == 0)
+                break;
+            if (*p == ',')
+                p++;
+        }
+        size = index;
+        tr(TrArgParser, "} %d\n", size);
+
+        if (num == 1) {
+            // Support for absolute addresses on Calypso Plus
+            if (n1 >= CALP_OFFSET) n1 -= CALP_OFFSET;
+
+            n2 = n1 + size;
+        }
+        else if (n1 >= CALP_OFFSET && n2 >= CALP_OFFSET) {
+            n1 -= CALP_OFFSET;
+            n2 -= CALP_OFFSET;
+        }
+
+        if (image_map_set(n1, n2) < 0)
+            main_error(E_ADDR_RANGE);
+
+        index = 0;
+        for (i = n1; i < n2; i++) {
+            image[i] = bytes[index++];
+            if (index >= size)
+                index = 0;
+        }
+        if (*p == ':')
+            p++;
+    }
+    tr(TrArgParser, "}\n");
+}