#!/bin/sh
#
# OVERVIEW
#
#   Micro-experiment assembler.
#
# EXAMPLES
#
#     asmexpr 'mov $-4096,%rax' 'not %rax' 'bsr %rax,%rax'
#     asmexpr 'mov $0,%ecx' 'vmovd %ecx,%xmm1' 'vpbroadcastb %xmm1,%ymm1' 'mov $0x20202032489001ff,%rax' 'vmovq %rax,%xmm0' 'vpcmpgtb %ymm1,%ymm0,%ymm2'

c=/tmp/asmexpr.c
s1=/tmp/asmexpr1.s
s2=/tmp/asmexpr2.s
s3=/tmp/asmexpr3.s
x=/tmp/asmexpr.exe

cat <<EOF >$s1
	.comm	rsp,8
	.globl	funk
funk:	push	%rbp
	mov	%rsp,%rbp
	push	%rbx
	push	%r12
	push	%r13
	push	%r14
	push	%r15
	mov	%rsp,rsp(%rip)
	xor	%eax,%eax
	xor	%ebx,%ebx
	xor	%ecx,%ecx
	xor	%edx,%edx
	xor	%edi,%edi
	xor	%esi,%esi
	xor	%r8d,%r8d
	xor	%r9d,%r9d
	xor	%r10d,%r10d
	xor	%r11d,%r11d
	xor	%r12d,%r12d
	xor	%r13d,%r13d
	xor	%r14d,%r14d
	xor	%r15d,%r15d
	vzeroall
	nop
	nop
	nop
	nop
	nop
	nop
EOF

cat <<EOF >$s2
	.comm	a,8
	.comm	b,8
	.comm	c,8
	.comm	x,8
	.comm	y,8
	.comm	z,8
EOF
for i; do
  cat <<EOF >>$s2
	$i
EOF
done

cat <<EOF >$s3
	.comm	rsp,8
	.comm	regs,14*8
	.comm	flags,4
	nop
	nop
	nop
	nop
	nop
	nop
	cld
	mov	rsp(%rip),%rsp
	push	%rbx
	lea	regs(%rip),%rbx
	mov	%rax,0(%rbx)
	pop	%rax
	mov	%rax,8(%rbx)
	mov	%rcx,16(%rbx)
	mov	%rdx,24(%rbx)
	mov	%rdi,32(%rbx)
	mov	%rsi,40(%rbx)
	mov	%r8,48(%rbx)
	mov	%r9,56(%rbx)
	mov	%r10,64(%rbx)
	mov	%r11,72(%rbx)
	mov	%r12,80(%rbx)
	mov	%r13,88(%rbx)
	mov	%r14,96(%rbx)
	mov	%r15,104(%rbx)
	vmovaps	%ymm0,0x0a0(%rbx)
	vmovaps	%ymm1,0x0c0(%rbx)
	vmovaps	%ymm2,0x0e0(%rbx)
	vmovaps	%ymm3,0x100(%rbx)
	vmovaps	%ymm4,0x120(%rbx)
	vmovaps	%ymm5,0x140(%rbx)
	vmovaps	%ymm6,0x160(%rbx)
	vmovaps	%ymm7,0x180(%rbx)
	vmovaps	%ymm8,0x1a0(%rbx)
	vmovaps	%ymm9,0x1c0(%rbx)
	vmovaps	%ymm10,0x1e0(%rbx)
	vmovaps	%ymm11,0x200(%rbx)
	vmovaps	%ymm12,0x220(%rbx)
	vmovaps	%ymm13,0x240(%rbx)
	vmovaps	%ymm14,0x260(%rbx)
	vmovaps	%ymm15,0x280(%rbx)
	pushf
	pop	%rax
	mov	%eax,flags(%rip)
	pop	%r15
	pop	%r14
	pop	%r13
	pop	%r12
	pop	%rbx
	pop	%rbp
	vzeroupper
	ret
EOF

cat <<EOF >$c
#include <stdio.h>
#include <string.h>

struct GodHatesFlags {
  unsigned c : 1;  /* bit  0: carry flag */
  unsigned v : 1;  /* bit  1: V flag: was 8085 signed-number overflow */
  unsigned p : 1;  /* bit  2: parity flag */
  unsigned r : 1;  /* bit  3: always zero */
  unsigned a : 1;  /* bit  4: auxiliary flag (nibble carry) */
  unsigned k : 1;  /* bit  5: K is for Kompressor (K = V flag ⊕ sgn(result)) */
  unsigned z : 1;  /* bit  6: zero flag */
  unsigned s : 1;  /* bit  7: sign flag */
  unsigned t : 1;  /* bit  8: it's a trap flag */
  unsigned i : 1;  /* bit  9: interrupt enable flag */
  unsigned d : 1;  /* bit 10: direction flag */
  unsigned o : 1;  /* bit 11: overflow flag */
  unsigned pl : 2; /* b12-13: i/o privilege level (80286+) */
  unsigned nt : 1; /* bit 14: nested task flag (80286+) */
  unsigned pc : 1; /* bit 15: oldskool flag */
  unsigned blah : 16;
  unsigned blah2 : 32;
};

char *DescribeFlags(struct GodHatesFlags flags) {
  static char buf[256];
  buf[0] = 0;
  if (flags.c) strcat(buf, "CF ");
  if (flags.p) strcat(buf, "PF ");
  if (flags.a) strcat(buf, "AF ");
  if (flags.z) strcat(buf, "ZF ");
  if (flags.s) strcat(buf, "SF ");
  if (flags.t) strcat(buf, "TF ");
  if (flags.i) strcat(buf, "IF ");
  if (flags.d) strcat(buf, "DF ");
  if (flags.o) strcat(buf, "OF ");
  strcat(buf, "IOPL-");
  switch (flags.pl) {
    case 0:
      strcat(buf, "0");
      break;
    case 1:
      strcat(buf, "1");
      break;
    case 2:
      strcat(buf, "2");
      break;
    case 3:
      strcat(buf, "3");
      break;
    default:
      __builtin_unreachable();
  }
  strcat(buf, " ");
  if (flags.nt) strcat(buf, "NT ");
  if (flags.r || flags.k || flags.pc) {
    strcat(buf, "[WOW: ");
    if (flags.v) strcat(buf, "VF ");
    if (flags.k) strcat(buf, "KF ");
    if (flags.r) strcat(buf, "RF ");
    if (flags.pc) strcat(buf, "PC ");
    strcat(buf, "] ");
  }
  return &buf[0];
}

void funk();
struct GodHatesFlags flags;
struct {
  long gen[14];
  long __pad[6];
  unsigned long ymms[16][4];
} regs;
static const char regnames[][4] = {"rax", "rbx", "rcx", "rdx", "rdi", "rsi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"};
int main() {
  funk();
  printf("flags %45s\n\n", DescribeFlags(flags));
  for (unsigned i = 0; i < 14; ++i) {
    if (regs.gen[i]) {
      printf("%s    s   0x%08x         %20d\\n", regnames[i], (signed)(regs.gen[i]), (signed)(regs.gen[i]));
      printf("       u   0x%08x         %20u\\n", (unsigned)(regs.gen[i]), (unsigned)(regs.gen[i]));
      printf("       sll 0x%016llx %20lld\\n", (signed long long)(regs.gen[i]), (signed long long)(regs.gen[i]));
      printf("       ull 0x%016llx %20llu\\n", (unsigned long long)(regs.gen[i]), (unsigned long long)(regs.gen[i]));
      printf("\n");
    }
  }
  for (unsigned i = 0; i < 16; ++i) {
    if (regs.ymms[i][0] || regs.ymms[i][1] || regs.ymms[i][2] || regs.ymms[i][3]) {
      printf("ymm%d%s    %016lx%016lx%016lx%016lx\\n", i, i < 10 ? " " : "", regs.ymms[i][3], regs.ymms[i][2], regs.ymms[i][1], regs.ymms[i][0]);
    }
  }
  return 0;
}
EOF

cc -c -g -o $c.o $c &&
cc -c -g -o $s1.o $s1 &&
cc -c -g -o $s2.o $s2 &&
cc -c -g -o $s3.o $s3 &&
cc -g -o $x $c $s1.o $s2.o $s3.o && {
  echo
  objdump -d $s2.o | sed 1,7d
  echo
  $x
}

exit