// File: /workspace/github_cpp_makefile_star2_O3/yahoo[P]UDPing/src/protocol.cc
// Function: buildFrame(frame*, unsigned char*, unsigned char*, unsigned int, unsigned int, int, int, char const*, int, int)
int buildFrame (struct frame *etherFrame, uint8_t *srcMac, uint8_t *dstMac, uint32_t srcIp, uint32_t dstIp, int srcPort, int dstPort, const char *message, int datalen, int checksumLength) {
    if (checksumLength == 0) {
        checksumLength = datalen;
    }

    // Fill in the Ethernet part
    int frameLength = 6 + 6 + 2 + IP4_HDRLEN + UDP_HDRLEN + datalen;
    memcpy (&(etherFrame->dstMac), dstMac, 6 * sizeof (uint8_t));
    memcpy (&(etherFrame->srcMac), srcMac, 6 * sizeof (uint8_t));
    etherFrame->proto[0] = ETH_P_IP / 256;
    etherFrame->proto[1] = ETH_P_IP % 256;

    // IP Header
    etherFrame->iphdr.ip_hl = IP4_HDRLEN / sizeof (uint32_t);
    etherFrame->iphdr.ip_v = 4;
    etherFrame->iphdr.ip_tos = 0;
    etherFrame->iphdr.ip_len = htons (IP4_HDRLEN + UDP_HDRLEN + datalen);
    etherFrame->iphdr.ip_id = htons (0);
    etherFrame->iphdr.ip_off = 0;
    etherFrame->iphdr.ip_ttl = 255;
    etherFrame->iphdr.ip_p = IPPROTO_UDP;
    etherFrame->iphdr.ip_src.s_addr = srcIp;
    etherFrame->iphdr.ip_dst.s_addr = dstIp;

    etherFrame->iphdr.ip_sum = 0;
    etherFrame->iphdr.ip_sum = checksum ((uint16_t *) &(etherFrame->iphdr), IP4_HDRLEN);

    // UDP Header
    etherFrame->udphdr.source = htons (srcPort);
    etherFrame->udphdr.dest = htons (dstPort);
    etherFrame->udphdr.len = htons (UDP_HDRLEN + datalen);

    etherFrame->udphdr.check = 0;
    // Because the padding at the end of the message is always zeroes, only calculate the checksum on the 
    // header portion of the outbound message
    // Also, use rewritten (zero copy) version of udp checksum.
    uint16_t udpcs2 = udp4_checksum2 (etherFrame->iphdr, etherFrame->udphdr, (uint16_t *)message, checksumLength);
    etherFrame->udphdr.check = udpcs2;
    
    // Payload
    memcpy (&(etherFrame->message), message, datalen);

    return frameLength;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %rbx
subq $0x38, %rsp
movq %rdx, %rax
movq %rdi, %rbx
movl 0x78(%rsp), %edx
movl 0x70(%rsp), %r15d
movq 0x68(%rsp), %r14
movl 0x60(%rsp), %edi
movzwl 0x4(%rax), %r10d
movw %r10w, 0x4(%rbx)
movl (%rax), %eax
movl %eax, (%rbx)
movl (%rsi), %eax
movl %eax, 0x6(%rbx)
movzwl 0x4(%rsi), %eax
movw %ax, 0xa(%rbx)
leaq 0xe(%rbx), %rax
movl $0x450008, 0xc(%rbx)    # imm = 0x450008
leal 0x1c(%r15), %esi
rolw $0x8, %si
movw %si, 0x10(%rbx)
movabsq $0x11ff00000000, %rsi   # imm = 0x11FF00000000
movq %rsi, 0x12(%rbx)
movl %ecx, 0x1a(%rbx)
movl %r8d, 0x1e(%rbx)
xorl %ecx, %ecx
movl $0x16, %esi
movq %rax, %r8
movzwl (%r8), %r10d
addq $0x2, %r8
addl %r10d, %ecx
addl $-0x2, %esi
cmpl $0x3, %esi
ja 0x3eb8
cmpl $0x10000, %ecx          # imm = 0x10000
jb 0x3ee8
movl $0xffff, %esi           # imm = 0xFFFF
movl %ecx, %r8d
shrl $0x10, %r8d
andl %esi, %ecx
addl %r8d, %ecx
cmpl %esi, %ecx
ja 0x3ed8
leal 0x2a(%r15), %ebp
testl %edx, %edx
cmovel %r15d, %edx
notl %ecx
movw %cx, 0x18(%rbx)
rolw $0x8, %r9w
rolw $0x8, %di
movw %r9w, 0x22(%rbx)
leal 0x8(%r15), %ecx
rolw $0x8, %cx
movw %di, 0x24(%rbx)
movw %cx, 0x26(%rbx)
movw $0x0, 0x28(%rbx)
movl 0x10(%rax), %ecx
movl %ecx, 0x30(%rsp)
movups (%rax), %xmm0
movaps %xmm0, 0x20(%rsp)
movq 0x22(%rbx), %rdi
movl 0x30(%rsp), %eax
movl %eax, 0x10(%rsp)
movaps 0x20(%rsp), %xmm0
movups %xmm0, (%rsp)
movq %r14, %rsi
callq 0x3fb1
movw %ax, 0x28(%rbx)
addq $0x2a, %rbx
movslq %r15d, %rdx
movq %rbx, %rdi
movq %r14, %rsi
callq 0x2200
movl %ebp, %eax
addq $0x38, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq