#ifndef __NLSYS_ASM_CODE_H_INCLUDED__
#define __NLSYS_ASM_CODE_H_INCLUDED__

#include "type.h"
#include "model.h"

#define ASM_CODE_OUT(out, args...) do { if (out) fprintf(out, args); } while (0)

typedef enum {
  ASM_CODE_ARCH_NONE = 0,
  ASM_CODE_ARCH_I386,
  ASM_CODE_ARCH_AMD64,
  ASM_CODE_ARCH_ARM,
  ASM_CODE_ARCH_MIPS,
  ASM_CODE_ARCH_POWERPC,
  ASM_CODE_ARCH_AARCH64,
  ASM_CODE_ARCH_THUMB,
  ASM_CODE_ARCH_THUMB2,
  ASM_CODE_ARCH_MIPS16,
  ASM_CODE_ARCH_OSECPU,
  ASM_CODE_ARCH_RISCV32,
  ASM_CODE_ARCH_RX,
  ASM_CODE_ARCH_NUM
} asm_code_arch_t;

#if defined(__i386__)
#define ASM_CODE_ARCH_DEFAULT ASM_CODE_ARCH_I386
#endif
#if defined(__amd64__)
#define ASM_CODE_ARCH_DEFAULT ASM_CODE_ARCH_AMD64
#endif
#if defined(__arm__)
#define ASM_CODE_ARCH_DEFAULT ASM_CODE_ARCH_ARM
#endif
#if defined(__mips__)
#define ASM_CODE_ARCH_DEFAULT ASM_CODE_ARCH_MIPS
#endif
#if defined(__PPC__)
#define ASM_CODE_ARCH_DEFAULT ASM_CODE_ARCH_POWERPC
#endif
#if defined(__aarch64__)
#define ASM_CODE_ARCH_DEFAULT ASM_CODE_ARCH_AARCH64
#endif
#if defined(__riscv) && !defined(__LP64__)
#define ASM_CODE_ARCH_DEFAULT ASM_CODE_ARCH_RISCV32
#endif
#if defined(__RX__)
#define ASM_CODE_ARCH_DEFAULT ASM_CODE_ARCH_RX
#endif

#ifndef ASM_CODE_ARCH_DEFAULT
#define ASM_CODE_ARCH_DEFAULT ASM_CODE_ARCH_AMD64
#endif

int asm_init_arch(asm_code_arch_t arch);

char *asm_info_type_mark(void);

int asm_info_word_size(void);
int asm_info_pointer_size(void);

int asm_info_funccall_args_reg_number(void);
int asm_info_funccall_args_stack_number(void);
int asm_info_tmp_reg_number(void);
int asm_info_tmp_stack_number(void);
int asm_info_function_register_number(void);
int asm_info_function_saveparam_number(void);

int asm_info_stack_align_size(void);
int asm_info_stack_correct_size(void);

void asm_code_get_value(FILE *out, long value);
void asm_code_get_value_r1(FILE *out, long value);
void asm_code_get_address_stack(FILE *out, int offset);
int asm_code_get_address_stack_r1(FILE *out, int offset);
void asm_code_get_address(FILE *out, char *label);
void asm_code_add_address(FILE *out, int offset);
void asm_code_get_r1(FILE *out);
void asm_code_set_r1(FILE *out);

void asm_code_memory_load(FILE *out, int offset, model_t model);
void asm_code_memory_store(FILE *out, int offset, model_t model);
void asm_code_stack_load(FILE *out, int offset, model_t model);
void asm_code_stack_store(FILE *out, int offset, model_t model);
void asm_code_stack_load_r1(FILE *out, int offset, model_t model);
void asm_code_stack_store_r1(FILE *out, int offset, model_t model);
void asm_code_stack_expand(FILE *out, int size);
void asm_code_stack_reduce(FILE *out, int size);

void asm_code_funccall_reg_load(FILE *out, int number);
void asm_code_funccall_reg_store(FILE *out, int number);
void asm_code_tmp_reg_load(FILE *out, int number);
void asm_code_tmp_reg_save(FILE *out, int number);
void asm_code_tmp_reg_load_r1(FILE *out, int number);
void asm_code_tmp_reg_save_r1(FILE *out, int number);

int asm_code_sign_extension_char(FILE *out);
int asm_code_sign_extension_uchar(FILE *out);
int asm_code_sign_extension_short(FILE *out);
int asm_code_sign_extension_ushort(FILE *out);
int asm_code_sign_extension_int(FILE *out);
int asm_code_sign_extension_uint(FILE *out);

int asm_code_calc_inv(FILE *out, model_t model, model_t model_arg0);
int asm_code_calc_minus(FILE *out, model_t model, model_t model_arg0);

int asm_code_calc_op1(FILE *out, c_type_t type, model_t model, model_t model_arg0);

void asm_code_calc_add(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
void asm_code_calc_sub(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
void asm_code_calc_and(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
void asm_code_calc_or(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
int asm_code_calc_xor(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);

int asm_code_calc_mul(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
int asm_code_calc_div(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
int asm_code_calc_mod(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
int asm_code_calc_llshift(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
int asm_code_calc_rashift(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
int asm_code_calc_rlshift(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);

int asm_code_calc_op2(FILE *out, c_type_t type, model_t model, model_t model_arg0, model_t model_arg1);

void asm_code_branch(FILE *out, char *label);

int asm_code_branch_zero(FILE *out, char *label);
int asm_code_branch_nzero(FILE *out, char *label);

int asm_code_branch_cmp_eq(FILE *out, char *label, model_t model);
int asm_code_branch_cmp_ne(FILE *out, char *label, model_t model);

int asm_code_branch_cmp_lt(FILE *out, char *label, model_t model);
int asm_code_branch_cmp_gt(FILE *out, char *label, model_t model);
int asm_code_branch_cmp_le(FILE *out, char *label, model_t model);
int asm_code_branch_cmp_ge(FILE *out, char *label, model_t model);
int asm_code_branch_cmp_ult(FILE *out, char *label, model_t model);
int asm_code_branch_cmp_ugt(FILE *out, char *label, model_t model);
int asm_code_branch_cmp_ule(FILE *out, char *label, model_t model);
int asm_code_branch_cmp_uge(FILE *out, char *label, model_t model);

int asm_code_branch_cmp(FILE *out, char *label, c_type_t type, model_t model);

int asm_code_function_call(FILE *out, char *label);
int asm_code_function_call_set(FILE *out);
int asm_code_function_call_pointer(FILE *out);

void asm_code_function_start(FILE *out);
void asm_code_function_end(FILE *out);
void asm_code_function_register_save(FILE *out);
void asm_code_function_register_load(FILE *out);

struct asm_code_callback {
  char *type_mark;

  int word_size;
  int pointer_size;

  int funccall_args_reg_number;
  int funccall_args_stack_number;
  int tmp_reg_number;
  int tmp_stack_number;
  int function_register_number;
  int function_saveparam_number;

  int stack_align_size;
  int stack_correct_size;

  void (*get_value)(FILE *out, long value);
  void (*get_value_r1)(FILE *out, long value);
  void (*get_address_stack)(FILE *out, int offset);
  int (*get_address_stack_r1)(FILE *out, int offset);
  void (*get_address)(FILE *out, char *label);
  void (*add_address)(FILE *out, int offset);
  void (*get_r1)(FILE *out);
  void (*set_r1)(FILE *out);

  void (*memory_load)(FILE *out, int offset, model_t model);
  void (*memory_store)(FILE *out, int offset, model_t model);
  void (*stack_load)(FILE *out, int offset, model_t model);
  void (*stack_store)(FILE *out, int offset, model_t model);
  void (*stack_load_r1)(FILE *out, int offset, model_t model);
  void (*stack_store_r1)(FILE *out, int offset, model_t model);
  void (*stack_expand)(FILE *out, int size);
  void (*stack_reduce)(FILE *out, int size);

  void (*funccall_reg_load)(FILE *out, int number);
  void (*funccall_reg_store)(FILE *out, int number);
  void (*tmp_reg_load)(FILE *out, int number);
  void (*tmp_reg_save)(FILE *out, int number);
  void (*tmp_reg_load_r1)(FILE *out, int number);
  void (*tmp_reg_save_r1)(FILE *out, int number);

  int (*sign_extension_char)(FILE *out);
  int (*sign_extension_uchar)(FILE *out);
  int (*sign_extension_short)(FILE *out);
  int (*sign_extension_ushort)(FILE *out);
  int (*sign_extension_int)(FILE *out);
  int (*sign_extension_uint)(FILE *out);

  int (*calc_inv)(FILE *out, model_t model, model_t model_arg0);
  int (*calc_minus)(FILE *out, model_t model, model_t model_arg0);

  int (*calc_op1)(FILE *out, c_type_t type, model_t model, model_t model_arg0);

  void (*calc_add)(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
  void (*calc_sub)(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
  void (*calc_and)(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
  void (*calc_or)(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
  int (*calc_xor)(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);

  int (*calc_mul)(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
  int (*calc_div)(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
  int (*calc_mod)(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
  int (*calc_llshift)(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
  int (*calc_rashift)(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);
  int (*calc_rlshift)(FILE *out, model_t model, model_t model_arg0, model_t model_arg1);

  int (*calc_op2)(FILE *out, c_type_t type, model_t model, model_t model_arg0, model_t model_arg1);

  void (*branch)(FILE *out, char *label);

  int (*branch_zero)(FILE *out, char *label);
  int (*branch_nzero)(FILE *out, char *label);

  int (*branch_cmp_eq)(FILE *out, char *label, model_t model);
  int (*branch_cmp_ne)(FILE *out, char *label, model_t model);

  int (*branch_cmp_lt)(FILE *out, char *label, model_t model);
  int (*branch_cmp_gt)(FILE *out, char *label, model_t model);
  int (*branch_cmp_le)(FILE *out, char *label, model_t model);
  int (*branch_cmp_ge)(FILE *out, char *label, model_t model);
  int (*branch_cmp_ult)(FILE *out, char *label, model_t model);
  int (*branch_cmp_ugt)(FILE *out, char *label, model_t model);
  int (*branch_cmp_ule)(FILE *out, char *label, model_t model);
  int (*branch_cmp_uge)(FILE *out, char *label, model_t model);

  int (*branch_cmp)(FILE *out, char *label, c_type_t type, model_t model);

  int (*function_call)(FILE *out, char *label);
  int (*function_call_set)(FILE *out);
  int (*function_call_pointer)(FILE *out);

  void (*function_start)(FILE *out);
  void (*function_end)(FILE *out);
  void (*function_register_save)(FILE *out);
  void (*function_register_load)(FILE *out);
};

#endif
