user: Translate the below assembly code into C programming language:

```asm
<vga_font_load>:
  endbr64
  push   %rbp
  mov    %rsp,%rbp
  sub    $0x30,%rsp
  mov    %rdi,-0x18(%rbp)
  mov    %rsi,-0x20(%rbp)
  mov    %edx,-0x24(%rbp)
  mov    %ecx,-0x28(%rbp)
  mov    $0x2,%edi
  call   <read_seq_b@plt>
  and    $0xfffffff0,%eax
  mov    %al,-0x9(%rbp)
  mov    $0x3c4,%esi
  mov    $0x2,%edi
  call   <outb@plt>
  movzbl -0x9(%rbp),%eax
  or     $0x4,%eax
  movzbl %al,%eax
  mov    $0x3c5,%esi
  mov    %eax,%edi
  call   <outb@plt>
  mov    $0x4,%edi
  call   <read_seq_b@plt>
  mov    %al,-0x9(%rbp)
  mov    $0x3c4,%esi
  mov    $0x4,%edi
  call   <outb@plt>
  movzbl -0x9(%rbp),%eax
  or     $0x4,%eax
  movzbl %al,%eax
  mov    $0x3c5,%esi
  mov    %eax,%edi
  call   <outb@plt>
  mov    $0x3ce,%esi
  mov    $0x4,%edi
  call   <outb@plt>
  mov    $0x3cf,%esi
  mov    $0x2,%edi
  call   <outb@plt>
  mov    $0x5,%edi
  call   <read_gra_b@plt>
  and    $0xffffffef,%eax
  mov    %al,-0x9(%rbp)
  mov    $0x3ce,%esi
  mov    $0x5,%edi
  call   <outb@plt>
  movzbl -0x9(%rbp),%eax
  mov    $0x3cf,%esi
  mov    %eax,%edi
  call   <outb@plt>
  mov    $0x3ce,%esi
  mov    $0x6,%edi
  call   <outb@plt>
  mov    $0x3cf,%esi
  mov    $0x0,%edi
  call   <outb@plt>
  movl   $0x0,-0x8(%rbp)
  jmp    L0
L7:
  movl   $0x0,-0x4(%rbp)
  jmp    L30
L4:
  mov    -0x8(%rbp),%eax
  shl    $0x4,%eax
  mov    %eax,%edx
  mov    -0x4(%rbp),%eax
  add    %edx,%eax
  movslq %eax,%rdx
  mov    -0x20(%rbp),%rax
  add    %rdx,%rax
  mov    -0x8(%rbp),%edx
  mov    %edx,%ecx
  shl    $0x5,%ecx
  mov    -0x4(%rbp),%edx
  add    %ecx,%edx
  movslq %edx,%rcx
  mov    -0x18(%rbp),%rdx
  add    %rcx,%rdx
  movzbl (%rax),%eax
  mov    %al,(%rdx)
  addl   $0x1,-0x4(%rbp)
L30:
  mov    -0x4(%rbp),%eax
  cmp    -0x24(%rbp),%eax
  jl     L4
  addl   $0x1,-0x8(%rbp)
L0:
  mov    -0x8(%rbp),%eax
  cmp    -0x28(%rbp),%eax
  jl     L7
  mov    $0x2,%edi
  call   <read_seq_b@plt>
  and    $0xfffffff0,%eax
  mov    %al,-0x9(%rbp)
  mov    $0x3c4,%esi
  mov    $0x2,%edi
  call   <outb@plt>
  movzbl -0x9(%rbp),%eax
  or     $0x3,%eax
  movzbl %al,%eax
  mov    $0x3c5,%esi
  mov    %eax,%edi
  call   <outb@plt>
  mov    $0x4,%edi
  call   <read_seq_b@plt>
  and    $0xfffffffb,%eax
  mov    %al,-0x9(%rbp)
  mov    $0x3c4,%esi
  mov    $0x4,%edi
  call   <outb@plt>
  movzbl -0x9(%rbp),%eax
  mov    $0x3c5,%esi
  mov    %eax,%edi
  call   <outb@plt>
  mov    $0x3ce,%esi
  mov    $0x4,%edi
  call   <outb@plt>
  mov    $0x3cf,%esi
  mov    $0x0,%edi
  call   <outb@plt>
  mov    $0x5,%edi
  call   <read_gra_b@plt>
  mov    %al,-0x9(%rbp)
  mov    $0x3ce,%esi
  mov    $0x5,%edi
  call   <outb@plt>
  movzbl -0x9(%rbp),%eax
  or     $0x10,%eax
  movzbl %al,%eax
  mov    $0x3cf,%esi
  mov    %eax,%edi
  call   <outb@plt>
  mov    $0x3ce,%esi
  mov    $0x6,%edi
  call   <outb@plt>
  mov    $0x3cf,%esi
  mov    $0xe,%edi
  call   <outb@plt>
  nop
  leave
  ret
```

assistant: ```c
void vga_font_load(unsigned char *vidmem, const unsigned char *font, int height, int num_chars) {
  int i, j;
  unsigned char byte;
  byte = read_seq_b(0x02) & ~0xf;
  outb(0x02, 0x3C4);
  outb(byte | 4, 0x3C5);
  byte = read_seq_b(0x04);
  outb(0x04, 0x3C4);
  outb(byte | 4, 0x3C5);
  outb(0x04, 0x3CE);
  outb(2, 0x3CF);
  byte = read_gra_b(0x05) & ~0x10;
  outb(0x05, 0x3CE);
  outb(byte, 0x3CF);
  outb(0x06, 0x3CE);
  outb(0, 0x3CF);
  for (i = 0; i < num_chars; i++) {
    for (j = 0; j < height; j++) {
      vidmem[i * 32 + j] = font[i * 16 + j];
    }
  }
  byte = read_seq_b(0x02) & ~0xf;
  outb(0x02, 0x3C4);
  outb(byte | 3, 0x3C5);
  byte = read_seq_b(0x04) & ~0x4;
  outb(0x04, 0x3C4);
  outb(byte, 0x3C5);
  outb(0x04, 0x3CE);
  outb(0, 0x3CF);
  byte = read_gra_b(0x05);
  outb(0x05, 0x3CE);
  outb(byte | 0x10, 0x3CF);
  outb(0x06, 0x3CE);
  outb(0xe, 0x3CF);
}
```