#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<inttypes.h>
#include<string.h>
#include "table.h"

/* Register contents after executing an TRE insn */
typedef struct {
   uint64_t addr;
   uint64_t len;
   uint64_t tabaddr;
   uint8_t testbyte;
   uint64_t cc;
} tre_regs;

uint8_t buff[40];

tre_regs tre(uint8_t *codepage, uint8_t *addr, uint64_t len, uint8_t test_byte)
{
   int cc;
   tre_regs regs;

   register uint64_t param asm("0") = test_byte;
   register uint64_t a2 asm ("4") = (uint64_t)codepage;
   register uint64_t a1 asm ("2") = (uint64_t)addr;
   register uint64_t l1 asm ("3") = len;

   asm volatile(
                " tre  %1,%2\n"
                " ipm  %0\n"
                " srl  %0,28\n"
                : "=d"(cc), "+a"(a1), "+a"(a2), "+a"(l1), "+d"(param)
                : : "cc", "memory");

   regs.addr = a1;
   regs.len = l1;
   regs.tabaddr = a2;
   regs.testbyte = param;
   regs.cc = cc;

   return regs;
}

void run_test(void *tran_table, void *srcaddr, uint64_t len, uint8_t test)
{
   tre_regs regs;
   int i;

   regs = tre(tran_table, srcaddr, len, test);

   if ((uint64_t)tran_table != regs.tabaddr)
      printf("translation table address changed\n");
   if (test != regs.testbyte)
      printf("test byte changed\n");
   if ((uint64_t)srcaddr + (len - regs.len) != regs.addr)
      printf("source address/length not updated properly\n");

   printf("Resulting cc is %"PRIu64" and the string is ", regs.cc);
   for ( i = 0; i < len; i++) {
      printf("%c", buff[i]);
   }
   
   printf("\n");
}

int main()
{

   /* Test 1: length = 0 */
   run_test(NULL, NULL, 0, 0x0);
   run_test((char *)&touppercase, &buff, 0, 0x0);
   run_test((char *)&touppercase, &buff, 0, 'b');

   /* Test 2 : length > 0 */
   memset(buff, 'a', 1);
   run_test((char *)&touppercase, &buff, 1, 'a');   //cc = 1
   run_test((char *)&touppercase, &buff, 1, 'b');

   memcpy(buff, "abcdefgh", 8);
   run_test((char *)&touppercase, &buff, 3, 'a');   //cc = 1
   run_test((char *)&touppercase, &buff, 3, 'f');   //cc = 0
   run_test((char *)&touppercase, &buff, 8, 'l');   //cc = 0

   memcpy(buff, "ABCDEFGH", 8);
   run_test((char *)&tolowercase, &buff, 3, 'A');   // cc = 1
   run_test((char *)&tolowercase, &buff, 3, 'C');   // cc = 0
   run_test((char *)&tolowercase, &buff, 8, 0x0);   // cc = 0

   memcpy(buff, "01234567", 8);
   run_test((char *)&touppercase, &buff, 8, 'A');
   run_test((char *)&tolowercase, &buff, 8, 'A');
   return 0;
}
