// File: /workspace/github_cpp_makefile_star2_O2/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 %r13
pushq %r12
pushq %rbx
subq $0x38, %rsp
movl %r9d, %r13d
movq %rdi, %r14
movslq 0x80(%rsp), %rbx
movl 0x88(%rsp), %r12d
testl %r12d, %r12d
cmovel %ebx, %r12d
movzwl 0x70(%rsp), %r15d
leal 0x2a(%rbx), %ebp
movzwl 0x4(%rdx), %eax
movw %ax, 0x4(%rdi)
movl (%rdx), %eax
movl %eax, (%rdi)
movl (%rsi), %eax
movl %eax, 0x6(%rdi)
movzwl 0x4(%rsi), %eax
movw %ax, 0xa(%rdi)
addq $0xe, %rdi
movl $0x450008, 0xc(%r14)    # imm = 0x450008
leal 0x1c(%rbx), %eax
rolw $0x8, %ax
movw %ax, 0x10(%r14)
movabsq $0x11ff00000000, %rax   # imm = 0x11FF00000000
movq %rax, 0x12(%r14)
movl %ecx, 0x1a(%r14)
movl %r8d, 0x1e(%r14)
pushq $0x14
popq %rsi
callq 0x39e4
rolw $0x8, %r13w
movw %ax, 0x18(%r14)
movw %r13w, 0x22(%r14)
rolw $0x8, %r15w
movw %r15w, 0x24(%r14)
leal 0x8(%rbx), %eax
rolw $0x8, %ax
movw %ax, 0x26(%r14)
andw $0x0, 0x28(%r14)
movl 0x1e(%r14), %eax
movl %eax, 0x30(%rsp)
movq 0xe(%r14), %rax
movq %rax, 0x20(%rsp)
movzwl 0x16(%r14), %eax
movw %ax, 0x28(%rsp)
movzwl 0x18(%r14), %eax
movw %ax, 0x2a(%rsp)
movl 0x1a(%r14), %eax
movl %eax, 0x2c(%rsp)
movq 0x22(%r14), %rdi
movl 0x30(%rsp), %eax
movl %eax, 0x10(%rsp)
movaps 0x20(%rsp), %xmm0
movups %xmm0, (%rsp)
movq 0x78(%rsp), %r15
movq %r15, %rsi
movl %r12d, %edx
callq 0x3a17
movw %ax, 0x28(%r14)
addq $0x2a, %r14
movq %r14, %rdi
movq %r15, %rsi
movq %rbx, %rdx
callq 0x21f0
movl %ebp, %eax
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq