#pragma once template concept port_data_t = (is_same::value || is_same::value || is_same::value); template class Port { public: [[nodiscard]] T read(uint16_t offset = 0) { const uint16_t port = port_num + offset; T result; switch (sizeof(T)) { case 1: asm volatile("inb %1, %0" : "=a"(result) : "Nd"(port)); break; case 2: asm volatile("inw %1, %0" : "=a"(result) : "Nd"(port)); break; case 4: asm volatile("inl %1, %0" : "=a"(result) : "Nd"(port)); break; } return result; } void write(T data, uint16_t offset = 0) { const uint16_t port = port_num + offset; switch (sizeof(T)) { case 1: asm volatile("outb %0, %1" : : "a"(data), "Nd"(port)); break; case 2: asm volatile("outw %0, %1" : : "a"(data), "Nd"(port)); break; case 4: asm volatile("outl %0, %1" : : "a"(data), "Nd"(port)); break; } } }; /* Ports 0x3d0 to 0x3df are reserved for CGA; each port is 1 byte wide */ typedef Port<0x3d4, uint8_t> cga_idx_port; typedef Port<0x3d5, uint8_t> cga_dat_port; // 0x3d8 : mode select register // 0x3d8 : color control register // 0x3da : status register /* Serial ports */ typedef Port<0x3f8, uint8_t> com1_port_t; typedef Port<0x2f8, uint8_t> com2_port_t; typedef Port<0x3e8, uint8_t> com3_port_t; typedef Port<0x2e8, uint8_t> com4_port_t; /* 8259 PIC */ typedef Port<0x20, uint8_t> pic1_t; typedef Port<0xa0, uint8_t> pic2_t;