on application entry:
  there is a 1MiB - 8KiB area mapped writable and not
  executable with guard pages on either side, and rsp is
  set to the top of that. all other registers are set to 0.

the ARGC environment variable holds the number of arguments to main.
the ARGV0, ARGV1, ARGV2, etc environment variables hold those arguments.

for all system calls:
  rax, rdi, rsi, rdx are in/out paramters.
  rbx, rbp, rsp, r12-r15 are preserved.
  rcx, rflags, r8-r11 are clobbered.

interrupts (including the timer!) are disabled during system calls.

stream result:
   0 = success
   1 = bad handle
   2 = io error
   3 = out of bounds
   4 = does not exist
   5 = not a regular file
   6 = not an executable
   7 = not writable
   8 = not seekable
   9 = socket id already used
  10 = socket id not in use
  11 = socket listener closed
  12 = other end closed
  13 = already exists
  14 = not sized

encode color:
  rax in: 0
  edi in: r + g * 256 + b * 65536
  eax out: encoded color

get framebuffer:
  rax in: 1
  rax out: pointer to framebuffer
  rdi out: width + height * 2 ** 32
  esi out: pitch
  framebuffer is always 32 bpp. use the encode color syscall
  to encode colors. pitch is in dwords, not in bytes.

open file:
  rax in: 2
  rdi in: pointer to file path
  rsi in: file path length
  rdx in:
    bit 0: allow creation
    bit 1: only allow creation
  rax out: stream result
  rdi out: stream handle (if rax = success)

end this thread:
  rax in: 3
  edi in: exit code (signed, only used if this was last thread)

get new pages:
  rax in: 4
  rdi in: number of pages to allocate
  rax out: start of first page
  the allocated pages are next to each other, writable, and not executable.

get input packet:
  rax in: 5
  al out:
    bit 7: was mouse packet
    if bit 7:
      bit 0: left mouse button down
      bit 1: right mouse button down
      bit 2: middle mouse button down
  if bit 7:
    di out: mouse x change (signed)
    si out: mouse y change (signed)
  else:
    edi out: key packet

create private socket:
  rax in: 6
  rax out: end 1 stream handle
  rdi out: end 2 stream handle

create socket listener:
  rax in: 7
  rdi in: pointer to id string
  rsi in: id string length
  rax out: stream result
  rdi out: listener handle (if rax = 0)

stop socket listener:
  rax in: 8
  rdi in: listener handle

accept socket connection:
  rax in: 9
  rdi in: listener handle
  rax out: stream result
  rdi out: stream handle

connect to socket:
  rax in: 10
  rdi in: pointer to id string
  rsi in: id string length
  rax out: stream result
  rdi out: stream handle (if rax = 0)
  if the listener is closed while this syscall is blocked, rax is
  set to "socket id not in use" and not "socket listener closed"

close stream:
  rax in: 11
  rdi in: stream handle

seek stream:
  returns "not seekable" for sockets
  rax in: 12
  rdi in: stream handle
  sil in:
    0 = relative to beginning
    1 = relative to end
    2 = relative to current position
  rdx in: offset (signed)
  rax out: stream result

read from stream:
  rax in: 13
  rdi in: stream handle
  rsi in: count
  rdx in: pointer to buffer
  rax out: stream result

write to stream:
  rax in: 14
  rdi in: stream handle
  rsi in: count
  rdx in: pointer to buffer
  rax out: stream result

get stream length:
  returns "not sized" for sockets
  rax in: 15
  rdi in: stream handle
  rax out: stream result
  rdi out: stream length (if rax = success)

start process:
  rax in: 16
  rdi in: pointer to process start info struct
  rax out: stream result
  rdi out: process handle (if rax = success)
  process start info struct:
    qword: file path length
    qword: pointer to file path
    qword: count of environment variables
    qword: pointer to array of environment variable structs
    qword: count of gifted stream ends
    qword: pointer to array of gifted stream structs
  environment variable struct:
    qword: name length
    qword: pointer to name
    qword: value length
    qword: pointer to value
  gifted stream struct:
    qword: stream handle here
    qword: new stream handle in child
      new handle must be < 65536

end this process:
  rax in: 17
  edi in: exit code (signed)

set stream length:
  returns "not sized" for sockets
  rax in: 18
  rdi in: stream handle
  rsi in: new length
  rax out: stream result
