/*******************************************************************************
 * Copyright (c) 2007-2021 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * The Eclipse Public License is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 * You may elect to redistribute this code under either of these licenses.
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/

/*
 * This module contains definitions of target CPU registers and stack frames.
 */

#include <tcf/config.h>

#if ENABLE_DebugContext

#include <stddef.h>
#include <stdio.h>
#include <assert.h>
#include <tcf/framework/cpudefs.h>
#include <tcf/framework/errors.h>
#include <tcf/framework/context.h>
#include <tcf/framework/myalloc.h>
#include <tcf/framework/exceptions.h>
#include <tcf/services/symbols.h>

#include <tcf/framework/cpudefs-ext.h>

typedef struct ContextExtensionRegisters {
    unsigned reg_seq;
    unsigned reg_cnt;
} ContextExtensionRegisters;

static unsigned reg_defs_seq = 1;
static size_t context_extension_offset = 0;
#define EXT(ctx) ((ContextExtensionRegisters *)((char *)(ctx) + context_extension_offset))

void create_reg_children_refs(RegisterDefinition * defs) {
#if ENABLE_RegisterChildrenRefs
    RegisterDefinition * r = NULL;
    RegisterDefinition ** p = NULL;
    RegisterDefinition * c = NULL;
    unsigned size = 0;
    assert(defs->parent == NULL);
    assert(defs->children == NULL);
    for (r = defs; r->name != NULL; r++) size++;
    p = (RegisterDefinition **)tmp_alloc_zero(sizeof(RegisterDefinition *) * size);
    for (r = defs; r->name != NULL; r++) {
        assert(r->sibling == NULL);
        if (r->parent == NULL) {
            if (c != NULL) c->sibling = r;
            c = r;
        }
        else {
            RegisterDefinition ** y = p + (r->parent - defs);
            if (*y == NULL) r->parent->children = r;
            else (*y)->sibling = r;
            *y = r;
        }
    }
#endif
}

int read_reg_value(StackFrame * frame, RegisterDefinition * reg_def, uint64_t * value) {
    uint8_t buf[8];
    if (reg_def == NULL) {
        set_errno(ERR_INV_CONTEXT, "Invalid register");
        return -1;
    }
    if (frame == NULL) {
        set_errno(ERR_INV_CONTEXT, "Invalid stack frame");
        return -1;
    }
    if (reg_def->size > sizeof(buf)) {
        errno = ERR_INV_DATA_SIZE;
        return -1;
    }
    if (read_reg_bytes(frame, reg_def, 0, reg_def->size, buf) < 0) return -1;
    if (value != NULL) {
        size_t i;
        uint64_t n = 0;
        for (i = 0; i < reg_def->size; i++) {
            n = n << 8;
            n |= buf[reg_def->big_endian ? i : reg_def->size - i - 1];
        }
        *value = n;
    }
    return 0;
}

int write_reg_value(StackFrame * frame, RegisterDefinition * reg_def, uint64_t value) {
    size_t i;
    uint8_t buf[8];
    if (reg_def == NULL) {
        set_errno(ERR_INV_CONTEXT, "Invalid register");
        return -1;
    }
    if (frame == NULL) {
        set_errno(ERR_INV_CONTEXT, "Invalid stack frame");
        return -1;
    }
    if (reg_def->size > sizeof(buf)) {
        errno = ERR_INV_DATA_SIZE;
        return -1;
    }
    for (i = 0; i < reg_def->size; i++) {
        buf[reg_def->big_endian ? reg_def->size - i - 1 : i] = (uint8_t)value;
        value = value >> 8;
    }
    if (write_reg_bytes(frame, reg_def, 0, reg_def->size, buf) < 0) return -1;
    if (!frame->is_top_frame) frame->has_reg_data = 1;
    return 0;
}

ContextAddress get_regs_PC(Context * ctx) {
    ContextAddress pc = 0;
    if (get_PC(ctx, &pc) < 0) return 0;
    return pc;
}

void set_regs_PC(Context * ctx, ContextAddress pc) {
    set_PC(ctx, pc);
}

int get_PC(Context * ctx, ContextAddress * p) {
    size_t i;
    uint8_t buf[8];
    ContextAddress pc = 0;
    RegisterDefinition * def = get_PC_definition(ctx);
    if (def == NULL) {
        set_errno(ERR_OTHER, "Cannot read PC: no such register");
        return -1;
    }
    if (def->size > sizeof(buf)) {
        set_errno(ERR_OTHER, "Cannot read PC: register is too large");
        return -1;
    }
    if (context_read_reg(ctx, def, 0, def->size, buf) < 0) return -1;
    for (i = 0; i < def->size; i++) {
        pc = pc << 8;
        pc |= buf[def->big_endian ? i : def->size - i - 1];
    }
    *p = pc;
    return 0;
}

int set_PC(Context * ctx, ContextAddress pc) {
    size_t i;
    uint8_t buf[8];
    RegisterDefinition * def = get_PC_definition(ctx);
    if (def == NULL) {
        set_errno(ERR_OTHER, "Cannot write PC: no such register");
        return -1;
    }
    if (def->size > sizeof(buf)) {
        set_errno(ERR_OTHER, "Cannot write PC: register is too large");
        return -1;
    }
    for (i = 0; i < def->size; i++) {
        buf[def->big_endian ? def->size - i - 1 : i] = (uint8_t)pc;
        pc = pc >> 8;
    }
    return context_write_reg(ctx, def, 0, def->size, buf);
}

int id2frame(const char * id, Context ** ctx, int * frame) {
    int f = 0;
    Context * c = NULL;

    if (*id++ != 'F') {
        errno = ERR_INV_CONTEXT;
        return -1;
    }
    if (*id++ != 'P') {
        errno = ERR_INV_CONTEXT;
        return -1;
    }
    while (*id != '.') {
        if (*id < '0' || *id > '9') {
            errno = ERR_INV_CONTEXT;
            return -1;
        }
        f = f * 10 + (*id++ - '0');
    }
    id++;
    c = id2ctx(id);
    if (c == NULL || !context_has_state(c)) {
        errno = ERR_INV_CONTEXT;
        return -1;
    }
    *ctx = c;
    *frame = f;
    return 0;
}

const char * frame2id(Context * ctx, int frame) {
    assert(frame >= 0);
    return tmp_printf("FP%d.%s", frame, ctx->id);
}

static void update_reg_cnt(Context * ctx, RegisterDefinition * defs) {
    ContextExtensionRegisters * ext = EXT(ctx);
    assert(reg_defs_seq != 0);
    if (ext->reg_seq != reg_defs_seq) {
        ext->reg_cnt = 0;
        if (defs != NULL) {
            while (defs->name != NULL) {
                ext->reg_cnt++;
                defs++;
            }
        }
        ext->reg_seq = reg_defs_seq;
    }
}

const char * register2id(Context * ctx, int frame, RegisterDefinition * reg) {
    RegisterDefinition * defs = get_reg_definitions(ctx);
    update_reg_cnt(ctx, defs);
    assert(defs != NULL);
    assert(reg >= defs);
    assert(reg < defs + EXT(ctx)->reg_cnt);
    if (frame < 0) return tmp_printf("R%d.%s", (int)(reg - defs), ctx->id);
    return tmp_printf("R%d@%d.%s", (int)(reg - defs), frame, ctx->id);
}

int id2reg_num(const char * id, const char ** ctx_id, int * frame, unsigned * reg_num) {
    *ctx_id = NULL;
    *frame = STACK_TOP_FRAME;
    *reg_num = 0;
    if (*id++ != 'R') {
        errno = ERR_OTHER;
        return -1;
    }
    while (*id != '.' && *id != '@') {
        if (*id >= '0' && *id <= '9') {
            *reg_num = *reg_num * 10 + (*id++ - '0');
        }
        else {
            errno = ERR_OTHER;
            return -1;
        }
    }
    if (*id == '@') {
        int n = 0;
        id++;
        while (*id != '.') {
            if (*id >= '0' && *id <= '9') {
                n = n * 10 + (*id++ - '0');
            }
            else {
                errno = ERR_OTHER;
                return -1;
            }
        }
        *frame = n;
    }
    id++;
    *ctx_id = id;
    return 0;
}

int id2register(const char * id, Context ** ctx, int * frame, RegisterDefinition ** reg_def) {
    const char * ctx_id = NULL;
    RegisterDefinition * defs = NULL;
    unsigned reg_num = 0;

    *ctx = NULL;
    *reg_def = NULL;

    if (id2reg_num(id, &ctx_id, frame, &reg_num) < 0) {
        if (errno == ERR_OTHER) set_errno(errno, "Malformed register ID");
        return -1;
    }

    *ctx = id2ctx(ctx_id);
    if (*ctx == NULL) {
        errno = ERR_INV_CONTEXT;
        return -1;
    }
    if ((*ctx)->exited) {
        errno = ERR_ALREADY_EXITED;
        return -1;
    }
    defs = get_reg_definitions(*ctx);
    if (defs == NULL) {
        set_errno(ERR_OTHER, "Context has no registers");
        return -1;
    }
    update_reg_cnt(*ctx, defs);
    if (reg_num >= EXT(*ctx)->reg_cnt) {
        set_errno(ERR_OTHER, "Invalid register ID");
        return -1;
    }
    *reg_def = defs + reg_num;
    return 0;
}

void invalidate_register_ids(void) {
    do reg_defs_seq++;
    while (reg_defs_seq == 0);
}

static void location_expression_error(void) {
    str_exception(ERR_OTHER, "Invalid location expression");
}

LocationExpressionState * evaluate_location_expression(Context * ctx, StackFrame * frame,
                                     LocationExpressionCommand * cmds, unsigned cmd_cnt,
                                     uint64_t * args, unsigned args_cnt) {
    unsigned i;
    unsigned stk_pos = 0;
    unsigned stk_max = 0;
    uint64_t * stk = NULL;
    LocationExpressionState * state = (LocationExpressionState *)tmp_alloc_zero(sizeof(LocationExpressionState));

    state->ctx = ctx;
    state->stack_frame = frame;
    state->args = args;
    state->args_cnt = args_cnt;
    for (i = 0; i < cmd_cnt && state->sft_cmd == NULL; i++) {
        LocationExpressionCommand * cmd = cmds + i;
        if (stk_pos >= stk_max) {
            stk_max += 4;
            stk = (uint64_t *)tmp_realloc(stk, sizeof(uint64_t) * stk_max);
        }
        switch (cmd->cmd) {
        case SFT_CMD_NUMBER:
            stk[stk_pos++] = cmd->args.num;
            break;
        case SFT_CMD_RD_REG:
            if (read_reg_value(frame, cmd->args.reg, stk + stk_pos) < 0) exception(errno);
            stk_pos++;
            break;
        case SFT_CMD_RD_REG_PCXI_TRICORE:
            {
            uint64_t offset;
            uint64_t segment;
            uint64_t pcxi;
            if (read_reg_value(frame, cmd->args.reg, &pcxi) < 0) exception(errno);
            /* PCXI is used as CFA and needs to be decyphered into order to point
             * to the CSA (area of memory where registered were saved when CALL is made) */
            offset = (pcxi & 0xFFFF) << 6;
            segment = (pcxi & 0xF0000) << 12;
            pcxi = offset | segment;
            *(stk + stk_pos) = pcxi;
            stk_pos++;
            }
            break;
        case SFT_CMD_WR_REG:
            if (stk_pos < 1) location_expression_error();
            if (write_reg_value(frame, cmd->args.reg, *(stk + stk_pos - 1)) < 0) exception(errno);
            stk_pos--;
            break;
        case SFT_CMD_FP:
            if (frame == NULL) str_exception(ERR_INV_CONTEXT, "Invalid stack frame");
            stk[stk_pos++] = frame->fp;
            break;
        case SFT_CMD_LOAD:
            if (state->pieces_cnt > 0) {
                size_t j;
                size_t size = 0;
                uint64_t n = 0;
                void * buf = NULL;

                read_location_pieces(state->ctx, state->stack_frame,
                    state->pieces, state->pieces_cnt, cmd->args.mem.big_endian, &buf, &size);
                state->pieces_cnt = 0;
                for (j = 0; j < size; j++) {
                    n = (n << 8) | ((uint8_t *)buf)[cmd->args.mem.big_endian ? j : size - j - 1];
                }
                stk[stk_pos++] = n;
                break;
            }
            /* Fall through */
        case SFT_CMD_RD_MEM:
            if (stk_pos < 1) location_expression_error();
            {
                size_t j;
                size_t size = cmd->args.mem.size;
                uint64_t n = 0;

                if (size <= sizeof(n)) {
                    uint8_t buf[8];
                    if (context_read_mem(ctx, (ContextAddress)stk[stk_pos - 1], buf, size) < 0) exception(errno);
                    for (j = 0; j < size; j++) {
                        n = (n << 8) | buf[cmd->args.mem.big_endian ? j : size - j - 1];
                    }
                    stk[stk_pos - 1] = n;
                }
                else if (state->pieces_cnt == 0 && i + 1 == cmd_cnt && stk_pos == 1) {
                    LocationPiece * piece = NULL;
                    if (state->pieces_cnt >= state->pieces_max) {
                        state->pieces_max += 4;
                        state->pieces = (LocationPiece *)tmp_realloc(state->pieces, state->pieces_max * sizeof(LocationPiece));
                    }
                    piece = state->pieces + state->pieces_cnt++;
                    memset(piece, 0, sizeof(LocationPiece));
                    piece->addr = (ContextAddress)stk[stk_pos - 1];
                    piece->size = size;
                    stk_pos--;
                }
                else {
                    exception(ERR_INV_DATA_SIZE);
                }
            }
            break;
        case SFT_CMD_WR_MEM:
            if (stk_pos < 2) location_expression_error();
            {
                size_t j;
                size_t size = cmd->args.mem.size;
                uint64_t n = stk[stk_pos - 1];
                uint8_t buf[8];

                assert(size <= sizeof(buf));
                for (j = 0; j < size; j++) {
                    buf[cmd->args.mem.big_endian ? size - j - 1 : j] = (uint8_t)n;
                    n >>= 8;
                }
                if (context_write_mem(ctx, (ContextAddress)stk[stk_pos - 2], buf, size) < 0) exception(errno);
                stk_pos -= 2;
            }
            break;
        case SFT_CMD_ADD:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] = stk[stk_pos - 2] + stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_SUB:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] = stk[stk_pos - 2] - stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_MUL:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] = stk[stk_pos - 2] * stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_DIV:
            if (stk_pos < 2) location_expression_error();
            if (stk[stk_pos - 1] == 0) str_exception(ERR_OTHER, "Division by zero in location expression");
            stk[stk_pos - 2] = stk[stk_pos - 2] / stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_AND:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] = stk[stk_pos - 2] & stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_OR:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] = stk[stk_pos - 2] | stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_XOR:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] = stk[stk_pos - 2] ^ stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_NEG:
            if (stk_pos < 1) location_expression_error();
            stk[stk_pos - 1] = ~stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_GE:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] = stk[stk_pos - 2] >= stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_GT:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] = stk[stk_pos - 2] > stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_LE:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] = stk[stk_pos - 2] <= stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_LT:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] = stk[stk_pos - 2] < stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_SHL:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] <<= stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_SHR:
            if (stk_pos < 2) location_expression_error();
            stk[stk_pos - 2] >>= stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_ARG:
            if (cmd->args.arg_no >= args_cnt) location_expression_error();
            stk[stk_pos++] = args[cmd->args.arg_no];
            break;
        case SFT_CMD_SET_ARG:
            if (stk_pos < 1) location_expression_error();
            if (cmd->args.arg_no >= args_cnt) {
                unsigned cnt = cmd->args.arg_no + 1;
                uint64_t * buf = (uint64_t *)tmp_alloc_zero(sizeof(uint64_t) * cnt);
                memcpy(buf, args, sizeof(uint64_t) * args_cnt);
                args_cnt = cnt;
                args = buf;
            }
            args[cmd->args.arg_no] = stk[stk_pos - 1];
            stk_pos--;
            break;
        case SFT_CMD_LOCATION:
            state->stk = stk;
            state->type_stk = (uint8_t *)tmp_alloc_zero(stk_max);
            state->stk_pos = stk_pos;
            state->stk_max = stk_max;
            state->reg_id_scope = cmd->args.loc.reg_id_scope;
            state->code = cmd->args.loc.code_addr;
            state->code_len = cmd->args.loc.code_size;
            state->code_pos = 0;
            state->addr_size = cmd->args.loc.addr_size;
            state->client_op = NULL;
            if (cmd->args.loc.func(state) < 0) exception(errno);
            memset(&state->reg_id_scope, 0, sizeof(state->reg_id_scope));
            state->code = NULL;
            state->code_len = 0;
            state->code_pos = 0;
            state->addr_size = 0;
            state->client_op = NULL;
            stk_max = state->stk_max;
            stk_pos = state->stk_pos;
            stk = state->stk;
            break;
        case SFT_CMD_FCALL:
            state->sft_cmd = cmd;
            break;
        case SFT_CMD_PIECE:
            {
                LocationPiece * piece = NULL;
                if (state->pieces_cnt >= state->pieces_max) {
                    state->pieces_max += 4;
                    state->pieces = (LocationPiece *)tmp_realloc(state->pieces, state->pieces_max * sizeof(LocationPiece));
                }
                piece = state->pieces + state->pieces_cnt++;
                memset(piece, 0, sizeof(LocationPiece));
                if (cmd->args.piece.bit_offs == 0 && cmd->args.piece.bit_size % 8 == 0) {
                    piece->size = cmd->args.piece.bit_size / 8;
                }
                else {
                    piece->bit_offs = cmd->args.piece.bit_offs;
                    piece->bit_size = cmd->args.piece.bit_size;
                }
                if (cmd->args.piece.reg != NULL || cmd->args.piece.value != NULL) {
                    piece->reg = cmd->args.piece.reg;
                    piece->value = cmd->args.piece.value;
                }
                else if (stk_pos == 0) {
                    location_expression_error();
                }
                else {
                    stk_pos--;
                    piece->addr = (ContextAddress)stk[stk_pos] + piece->bit_offs / 8;
                    piece->bit_offs %= 8;
                }
            }
            break;
        default:
            location_expression_error();
            break;
        }
    }
    if (stk_pos == 2) {
        /* This looks like a bug in GNU Ada 4.9.2 20141023 for GNAT Pro 7.3.0w
         * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=449802
         */
        stk[0] += stk[1];
        stk_pos = 1;
    }
    state->stk = stk;
    state->stk_pos = stk_pos;
    state->stk_max = stk_max;
    return state;
}

#define bit_mask(n) (1u << (big_endian ? 7 - (n) % 8 : (n) % 8))

void read_location_pieces_local(Context * ctx, StackFrame * frame, void * loc_buf, size_t loc_size,
            LocationPiece * pieces, unsigned pieces_cnt, int big_endian,
            void ** value, size_t * size) {
    /* Note: 'big_endian' should match endianness of LocationPiece.value in 'pieces' */
    uint8_t * bf = NULL;
    size_t bf_size = 0;
    unsigned bf_bits = 0;
    unsigned bf_offs = 0;
    unsigned n = 0;
    unsigned i = 0;
    while (i < pieces_cnt) {
        LocationPiece * piece = pieces + i++;
        if (piece->optimized_away) {
            str_exception(ERR_OTHER, "Cannot get symbol value: optimized away");
        }
        else if (piece->implicit_pointer) {
            if (piece->implicit_pointer == 1 &&
                    piece->reg == NULL && piece->value == NULL &&
                    piece->bit_offs == 0 && piece->bit_size == 0) {
                bf_bits += context_word_size(ctx) * 8;
            }
            else {
                str_exception(ERR_OTHER, "Cannot get symbol value: implicit pointer");
            }
        }
        else {
            bf_bits += piece->bit_size ? piece->bit_size : piece->size * 8;
        }
    }
    bf_size = (size_t)((bf_bits + 7) / 8);
    bf = (uint8_t *)tmp_alloc_zero(bf_size);
    if (big_endian) bf_offs = bf_size * 8 - bf_bits;
    while (n < pieces_cnt) {
        LocationPiece * piece = pieces + n++;
        unsigned piece_size = piece->size ? piece->size : (piece->bit_offs + piece->bit_size + 7) / 8;
        unsigned piece_bits = piece->bit_size ? piece->bit_size : piece->size * 8;
        uint8_t * pbf = NULL;
        uint8_t * rbf = NULL;
        if (piece->implicit_pointer) {
            piece_size = context_word_size(ctx);
            piece_bits = piece_size * 8;
            pbf = (uint8_t *)tmp_alloc(piece_size);
            rbf = (uint8_t *)&piece->addr;
            if (sizeof(piece->addr) > piece_size && big_endian_host()) rbf += sizeof(piece->addr) - piece_size;
            memcpy(pbf, rbf, piece_size);
            if (!big_endian_host() != !big_endian) swap_bytes(pbf, piece_size);
        }
        else if (piece->reg) {
            if (piece->reg->size < piece_size) {
                rbf = pbf = (uint8_t *)tmp_alloc_zero(piece_size);
                if (big_endian) rbf += piece_size - piece->reg->size;
            }
            else {
                rbf = pbf = (uint8_t *)tmp_alloc(piece->reg->size);
            }
            if (frame == NULL) {
                if (context_read_reg(ctx, piece->reg, 0, piece->reg->size, rbf) < 0) exception(errno);
            }
            else {
                if (read_reg_bytes(frame, piece->reg, 0, piece->reg->size, rbf) < 0) exception(errno);
            }
            if (!piece->reg->big_endian != !big_endian) swap_bytes(rbf, piece->reg->size);
        }
        else if (piece->value) {
            pbf = (uint8_t *)piece->value;
        }
        else {
            pbf = (uint8_t *)tmp_alloc(piece_size);
            if (loc_buf == NULL) {
                if (context_read_mem(ctx, piece->addr, pbf, piece_size) < 0) exception(errno);
            }
            else {
                if (piece->addr + piece_size > loc_size) {
                    str_exception(ERR_OTHER, "Cannot get symbol value: invalid location expression");
                }
                memcpy(pbf, (uint8_t *)loc_buf + piece->addr, piece_size);
            }
        }
        for (i = piece->bit_offs; i < piece->bit_offs + piece_bits;  i++) {
            if (pbf[i / 8] & bit_mask(i)) bf[bf_offs / 8] |= bit_mask(bf_offs);
            bf_offs++;
        }
    }

    *value = bf;
    *size = bf_size;
}

void read_location_pieces(Context * ctx, StackFrame * frame,
        LocationPiece * pieces, unsigned pieces_cnt, int big_endian,
        void ** value, size_t * size) {
    read_location_pieces_local(ctx, frame, NULL, 0, pieces, pieces_cnt, big_endian, value, size);
}

void write_location_pieces(Context * ctx, StackFrame * frame,
            LocationPiece * pieces, unsigned pieces_cnt, int big_endian,
            void * value, size_t size) {
    uint8_t * bf = (uint8_t *)value;
    size_t bf_size = 0;
    unsigned bf_bits = 0;
    unsigned bf_offs = 0;
    unsigned n = 0;
    unsigned i = 0;
    while (i < pieces_cnt) {
        LocationPiece * piece = pieces + i++;
        bf_bits += piece->bit_size ? piece->bit_size : piece->size * 8;
    }
    bf_size = (size_t)((bf_bits + 7) / 8);
    if (big_endian && size >= bf_size) bf_offs = size * 8 - bf_bits;
    while (n < pieces_cnt) {
        LocationPiece * piece = pieces + n++;
        unsigned piece_size = piece->size ? piece->size : (piece->bit_offs + piece->bit_size + 7) / 8;
        unsigned piece_bits = piece->bit_size ? piece->bit_size : piece->size * 8;
        uint8_t * pbf = NULL;
        uint8_t * rbf = NULL;
        if (piece->optimized_away) {
            set_errno(ERR_OTHER, "Cannot set symbol value: optimized away");
            exception(errno);
        }
        if (piece->implicit_pointer) {
            set_errno(ERR_OTHER, "Cannot set symbol value: implicit pointer");
            exception(errno);
        }
        if (piece->reg) {
            if (piece->reg->size < piece_size) {
                rbf = pbf = (uint8_t *)tmp_alloc_zero(piece_size);
                if (big_endian) rbf += piece_size - piece->reg->size;
            }
            else {
                rbf = pbf = (uint8_t *)tmp_alloc(piece->reg->size);
            }
            if (frame == NULL) {
                if (context_read_reg(ctx, piece->reg, 0, piece->reg->size, rbf) < 0) exception(errno);
            }
            else {
                if (read_reg_bytes(frame, piece->reg, 0, piece->reg->size, rbf) < 0) exception(errno);
            }
            if (!piece->reg->big_endian != !big_endian) swap_bytes(rbf, piece->reg->size);
        }
        else if (piece->value) {
            str_exception(ERR_OTHER, "Cannot write to a constant value");
        }
        else {
            pbf = (uint8_t *)tmp_alloc(piece_size);
            if (context_read_mem(ctx, piece->addr, pbf, piece_size) < 0) exception(errno);
        }
        for (i = piece->bit_offs; i < piece->bit_offs + piece_bits;  i++) {
            if (bf_offs / 8 >= size) {
                /* Leave unchanged? */
            }
            else if (bf[bf_offs / 8] & bit_mask(bf_offs)) {
                pbf[i / 8] |=  bit_mask(i);
            }
            else {
                pbf[i / 8] &= ~bit_mask(i);
            }
            bf_offs++;
        }
        if (piece->reg) {
            if (!piece->reg->big_endian != !big_endian) swap_bytes(rbf, piece->reg->size);
            if (frame == NULL) {
                if (context_write_reg(ctx, piece->reg, 0, piece->reg->size, rbf) < 0) exception(errno);
            }
            else {
                if (write_reg_bytes(frame, piece->reg, 0, piece->reg->size, rbf) < 0) exception(errno);
                if (!frame->is_top_frame) frame->has_reg_data = 1;
            }
        }
        else if (piece->value) {
            assert(0);
        }
        else {
            if (context_write_mem(ctx, piece->addr, pbf, piece_size) < 0) exception(errno);
        }
    }
}

#if !defined(ENABLE_external_stepping_mode) || !ENABLE_external_stepping_mode
int cpu_enable_stepping_mode(Context * ctx, uint32_t * is_cont) {
    * is_cont = 0;
    return 0;
}

int cpu_disable_stepping_mode(Context * ctx) {
    return 0;
}
#endif

#if !defined(ENABLE_HardwareBreakpoints) || !ENABLE_HardwareBreakpoints
int cpu_bp_get_capabilities(Context * ctx) {
    return 0;
}

int cpu_bp_plant(ContextBreakpoint * bp) {
    errno = ERR_UNSUPPORTED;
    return -1;
}

int cpu_bp_remove(ContextBreakpoint * bp) {
    errno = ERR_UNSUPPORTED;
    return -1;
}

int cpu_bp_on_resume(Context * ctx, int * single_step) {
    return 0;
}

int cpu_bp_on_suspend(Context * ctx, int * triggered) {
    return 0;
}
#endif

void ini_cpu_disassembler(Context * cpu) {
#if defined(ENABLE_add_cpudefs_disassembler) && ENABLE_add_cpudefs_disassembler
    add_cpudefs_disassembler(cpu);
#endif
}

void ini_cpudefs(void) {
    context_extension_offset = context_extension(sizeof(ContextExtensionRegisters));
#if defined(ENABLE_ini_cpudefs_mdep) && ENABLE_ini_cpudefs_mdep
    ini_cpudefs_mdep();
#endif
}

#endif /* ENABLE_DebugContext */
