/*
 * File: read_write.c
 *
 * Read and write operations
 */

#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include <stlink.h>
#include <stlink_backend.h>
#include "read_write.h"

#include "logging.h"

// Endianness
// https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
// These functions encode and decode little endian uint16 and uint32 values.

uint16_t read_uint16(const unsigned char *c, const int32_t pt) {
  return ((uint16_t) c[pt]) | ((uint16_t) c[pt + 1] << 8);
}

void write_uint16(unsigned char *buf, uint16_t ui) {
  buf[0] = (uint8_t) ui;
  buf[1] = (uint8_t) (ui >> 8);
}

uint32_t read_uint32(const unsigned char *c, const int32_t pt) {
  return ((uint32_t) c[pt]) | ((uint32_t) c[pt + 1] << 8) |
         ((uint32_t) c[pt + 2] << 16) | ((uint32_t) c[pt + 3] << 24);
}

void write_uint32(unsigned char *buf, uint32_t ui) {
  buf[0] = ui;
  buf[1] = ui >> 8;
  buf[2] = ui >> 16;
  buf[3] = ui >> 24;
}

int32_t stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) {
  int32_t ret;

  ret = sl->backend->read_debug32(sl, addr, data);
  if(!ret)
    DLOG("*** stlink_read_debug32  %#010x at %#010x\n", *data, addr);

  return (ret);
}

int32_t stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) {
  DLOG("*** stlink_write_debug32 %#010x to %#010x\n", data, addr);
  return sl->backend->write_debug32(sl, addr, data);
}

int32_t stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
  DLOG("*** stlink_read_mem32 ***\n");

  if(len % 4 != 0) { // !!! never ever: fw gives just wrong values
    ELOG("Data length doesn't have a 32 bit alignment: +%d byte.\n", len % 4);
    return (-1);
  }

  return (sl->backend->read_mem32(sl, addr, len));
}

int32_t stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
  DLOG("*** stlink_write_mem32 %u bytes to %#x\n", len, addr);

  if(len % 4 != 0) {
    ELOG("Data length doesn't have a 32 bit alignment: +%d byte.\n", len % 4);
    return (-1);
  }

  return (sl->backend->write_mem32(sl, addr, len));
}

int32_t stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) {
  DLOG("*** stlink_write_mem8 ***\n");
  return (sl->backend->write_mem8(sl, addr, len));
}

int32_t stlink_read_reg(stlink_t *sl, int32_t r_idx, struct stlink_reg *regp) {
  DLOG("*** stlink_read_reg\n");
  DLOG(" (%d) ***\n", r_idx);

  if(r_idx > 20 || r_idx < 0) {
    fprintf(stderr, "Error: register index must be in [0..20]\n");
    return (-1);
  }

  return (sl->backend->read_reg(sl, r_idx, regp));
}

int32_t stlink_write_reg(stlink_t *sl, uint32_t reg, int32_t idx) {
  DLOG("*** stlink_write_reg\n");
  return (sl->backend->write_reg(sl, reg, idx));
}

int32_t stlink_read_unsupported_reg(stlink_t *sl, int32_t r_idx,
                                struct stlink_reg *regp) {
  int32_t r_convert;

  DLOG("*** stlink_read_unsupported_reg\n");
  DLOG(" (%d) ***\n", r_idx);

  /* Convert to values used by STM32_REG_DCRSR */
  if(r_idx >= 0x1C &&
      r_idx <= 0x1F) { // primask, basepri, faultmask, or control
    r_convert = 0x14;
  } else if(r_idx == 0x40) { // FPSCR
    r_convert = 0x21;
  } else if(r_idx >= 0x20 && r_idx < 0x40) {
    r_convert = 0x40 + (r_idx - 0x20);
  } else {
    fprintf(stderr, "Error: register address must be in [0x1C..0x40]\n");
    return (-1);
  }

  return (sl->backend->read_unsupported_reg(sl, r_convert, regp));
}

int32_t stlink_write_unsupported_reg(stlink_t *sl, uint32_t val, int32_t r_idx,
                                 struct stlink_reg *regp) {
  int32_t r_convert;

  DLOG("*** stlink_write_unsupported_reg\n");
  DLOG(" (%d) ***\n", r_idx);

  /* Convert to values used by STM32_REG_DCRSR */
  if(r_idx >= 0x1C &&
      r_idx <= 0x1F) {        /* primask, basepri, faultmask, or control */
    r_convert = r_idx;        // the backend function handles this
  } else if(r_idx == 0x40) { // FPSCR
    r_convert = 0x21;
  } else if(r_idx >= 0x20 && r_idx < 0x40) {
    r_convert = 0x40 + (r_idx - 0x20);
  } else {
    fprintf(stderr, "Error: register address must be in [0x1C..0x40]\n");
    return (-1);
  }

  return (sl->backend->write_unsupported_reg(sl, val, r_convert, regp));
}

int32_t stlink_read_all_regs(stlink_t *sl, struct stlink_reg *regp) {
  DLOG("*** stlink_read_all_regs ***\n");
  return (sl->backend->read_all_regs(sl, regp));
}

int32_t stlink_read_all_unsupported_regs(stlink_t *sl, struct stlink_reg *regp) {
  DLOG("*** stlink_read_all_unsupported_regs ***\n");
  return (sl->backend->read_all_unsupported_regs(sl, regp));
}
