text: #ifndef IO_H
  #define IO_H
  
  #include <types.h>
  
  static inline void outb(u16 port, u8 val)
  {
      asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
      /* There's an outb %al, $imm8  encoding, for compile-time constant port numbers that fit in 8b.  (N constraint).
       * Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint).
       * The  outb  %al, %dx  encoding is the only option for all other cases.
       * %1 expands to %dx because  port  is a uint16_t.  %w1 could be used if we had the port number a wider C type */
  
  }
  
  static inline u8 inb(u16 port)
  {
      u8 ret;
      asm volatile ( "inb %1, %0"
                     : "=a"(ret)
                     : "Nd"(port) );
      return ret;
  }
  
  static inline void outl(u16 port, u32 val)
  {
      asm volatile ( "outl %0, %1" : : "a"(val), "Nd"(port) );
      /* There's an outb %al, $imm8  encoding, for compile-time constant port numbers that fit in 8b.  (N constraint).
       * Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint).
       * The  outb  %al, %dx  encoding is the only option for all other cases.
       * %1 expands to %dx because  port  is a uint16_t.  %w1 could be used if we had the port number a wider C type */
  
  }
  
  static inline u32 inl(u16 port)
  {
      u32 ret;
      asm volatile ( "inl %1, %0"
                     : "=a"(ret)
                     : "Nd"(port) );
      return ret;
  }
  
  static inline void io_wait(void)
  {
      /* TODO: This is probably fragile. */
      asm volatile ( "jmp 1f\n\t"
                     "1:jmp 2f\n\t"
                     "2:" );
  }
  #endif
  
  