/* * VBMouse - VGA/EGA/CGA support using VGA BIOS * Copyright (C) 2022 Javier S. Pedro * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef INT10_H #define INT10_H #include #include #define BIOS_DATA_AREA_SEGMENT 0x40 static inline uint32_t bda_get_dword(unsigned int offset) { uint32_t __far *p = MK_FP(BIOS_DATA_AREA_SEGMENT, offset); return *p; } static inline uint16_t bda_get_word(unsigned int offset) { uint16_t __far *p = MK_FP(BIOS_DATA_AREA_SEGMENT, offset); return *p; } static inline uint8_t bda_get_byte(unsigned int offset) { uint8_t __far *p = MK_FP(BIOS_DATA_AREA_SEGMENT, offset); return *p; } #define bda_get_ebda_segment() bda_get_word(0x0e) #define bda_get_video_mode() bda_get_byte(0x49) #define bda_get_num_columns() bda_get_word(0x4a) #define bda_get_video_page_size() bda_get_word(0x4c) #define bda_get_cur_video_page() bda_get_word(0x62) #define bda_get_last_row() bda_get_byte(0x84) #define bda_get_char_height() bda_get_word(0x85) #define bda_get_tick_count() bda_get_dword(0x6c) #define bda_get_tick_count_lo() bda_get_word(0x6c) enum videotype { VIDEO_UNKNOWN, VIDEO_TEXT, VIDEO_CGA, VIDEO_EGA, VIDEO_VGA }; struct modeinfo { uint8_t mode; uint8_t page; enum videotype type; uint16_t pixels_width, pixels_height; uint16_t bytes_per_line; uint16_t odd_scanline_offset; uint8_t bits_per_pixel; uint8_t num_planes; /** Pointer to video memory. */ uint8_t __far * begin; }; static void get_current_video_mode_info(struct modeinfo *info) { uint16_t segment; info->mode = bda_get_video_mode() & ~0x80; info->page = bda_get_cur_video_page(); info->odd_scanline_offset = 0; switch (info->mode) { case 0: case 1: case 2: case 3: // CGA text modes with 25 rows and variable columns case 7: // MDA Mono text mode info->type = VIDEO_TEXT; info->pixels_width = bda_get_num_columns() * 8; info->pixels_height = (bda_get_last_row()+1) * 8; info->bytes_per_line = bda_get_num_columns() * 2; info->bits_per_pixel = 2 * 8; info->num_planes = 1; segment = 0xB800; break; case 4: case 5: // CGA 320x200 4-color info->type = VIDEO_CGA; info->pixels_width = 320; info->pixels_height = 200; info->bytes_per_line = 80; info->odd_scanline_offset = 0x2000; info->bits_per_pixel = 2; info->num_planes = 1; segment = 0xB800; break; case 6: // CGA 640x200 2-color info->type = VIDEO_CGA; info->pixels_width = 640; info->pixels_height = 200; info->bytes_per_line = 80; info->odd_scanline_offset = 0x2000; info->bits_per_pixel = 1; info->num_planes = 1; segment = 0xB800; break; case 0xd: // EGA 320x200 16-color info->type = VIDEO_EGA; info->pixels_width = 320; info->pixels_height = 200; info->bytes_per_line = 40; info->bits_per_pixel = 1; info->num_planes = 4; segment = 0xA000; break; case 0xe: // EGA 640x200 16-color info->type = VIDEO_EGA; info->pixels_width = 640; info->pixels_height = 200; info->bytes_per_line = 80; info->bits_per_pixel = 1; info->num_planes = 4; segment = 0xA000; break; case 0xf: // EGA 640x350 4-color info->type = VIDEO_EGA; info->pixels_width = 640; info->pixels_height = 350; info->bytes_per_line = 80; info->bits_per_pixel = 1; info->num_planes = 2; segment = 0xA000; break; case 0x10: // EGA 640x350 16-color info->type = VIDEO_EGA; info->pixels_width = 640; info->pixels_height = 350; info->bytes_per_line = 80; info->bits_per_pixel = 1; info->num_planes = 4; segment = 0xA000; break; case 0x11: // VGA 640x480 2-color case 0x12: // VGA 640x480 16-color info->type = VIDEO_VGA; info->pixels_width = 640; info->pixels_height = 480; info->bytes_per_line = 80; info->bits_per_pixel = 1; info->num_planes = 4; segment = 0xA000; break; case 0x13: // VGA 320x200 256-color info->type = VIDEO_VGA; info->pixels_width = 320; info->pixels_height = 200; info->bytes_per_line = 320; info->bits_per_pixel = 8; info->num_planes = 1; segment = 0xA000; break; default: info->type = VIDEO_UNKNOWN; // Let's put in some default coordinates at leas info->pixels_width = 640; info->pixels_height = 200; segment = 0; } info->begin = MK_FP(segment, info->page * bda_get_video_page_size()); } static inline uint16_t __far * get_video_char(const struct modeinfo *info, unsigned int x, unsigned int y) { return (uint16_t __far *) (info->begin + (y * info->bytes_per_line) + (x * 2)); } static inline uint8_t __far * get_video_scanline(const struct modeinfo *info, unsigned int y) { if (info->odd_scanline_offset) { return info->begin + (y%2 * info->odd_scanline_offset) + (y/2 * info->bytes_per_line); } else { return info->begin + (y * info->bytes_per_line); } } enum vga_io_ports { VGA_PORT_SC_ADDRESS = 0x3c4, VGA_PORT_SC_DATA = 0x3c5, VGA_PORT_GC_ADDRESS = 0x3ce, VGA_PORT_GC_DATA = 0x3cf }; enum vga_sc_regs { VGA_SC_REG_MAP_MASK = 2 }; enum vga_gc_regs { VGA_GC_REG_SET_RESET = 0, VGA_GC_REG_ENABLE_SET_RESET = 1, VGA_GC_REG_DATA_ROTATE = 3, VGA_GC_REG_READ_MAP = 4, VGA_GC_REG_GRAPHICS_MODE = 5, VGA_GC_REG_BIT_MASK = 8 }; struct videoregs { uint8_t sc_reg; uint8_t sc_map_mask; uint8_t gc_reg; uint8_t gc_enable_set_reset; uint8_t gc_data_rotate; uint8_t gc_graphics_mode; uint8_t gc_read_map; uint8_t gc_bit_mask; }; static inline uint8_t vga_sc_reg_read(uint8_t reg) { outp(VGA_PORT_SC_ADDRESS, reg); return inp(VGA_PORT_SC_DATA); } static inline void vga_sc_reg_write(uint8_t reg, uint8_t val) { // Do one 16-bit write, the higher part will be the data while the lower byte the register. outpw(VGA_PORT_SC_ADDRESS, (val << 8) | reg); } static inline uint8_t vga_gc_reg_read(uint8_t reg) { outp(VGA_PORT_GC_ADDRESS, reg); return inp(VGA_PORT_GC_DATA); } static inline void vga_gc_reg_write(uint8_t reg, uint8_t val) { outpw(VGA_PORT_GC_ADDRESS, (val << 8) | reg); } static void vga_save_registers(struct videoregs __far *regs) { regs->sc_reg = inp(VGA_PORT_SC_ADDRESS); regs->sc_map_mask = vga_sc_reg_read(VGA_SC_REG_MAP_MASK); regs->gc_reg = inp(VGA_PORT_GC_ADDRESS); regs->gc_enable_set_reset = vga_gc_reg_read(VGA_GC_REG_ENABLE_SET_RESET); regs->gc_data_rotate = vga_gc_reg_read(VGA_GC_REG_DATA_ROTATE); regs->gc_read_map = vga_gc_reg_read(VGA_GC_REG_READ_MAP); regs->gc_graphics_mode = vga_gc_reg_read(VGA_GC_REG_GRAPHICS_MODE); regs->gc_bit_mask = vga_gc_reg_read(VGA_GC_REG_BIT_MASK); } static void vga_restore_register(struct videoregs __far *regs) { vga_sc_reg_write(VGA_SC_REG_MAP_MASK, regs->sc_map_mask); outp(VGA_PORT_SC_ADDRESS, regs->sc_reg); vga_gc_reg_write(VGA_GC_REG_ENABLE_SET_RESET, regs->gc_enable_set_reset); vga_gc_reg_write(VGA_GC_REG_DATA_ROTATE, regs->gc_data_rotate); vga_gc_reg_write(VGA_GC_REG_READ_MAP, regs->gc_read_map); vga_gc_reg_write(VGA_GC_REG_GRAPHICS_MODE, regs->gc_graphics_mode); vga_gc_reg_write(VGA_GC_REG_BIT_MASK, regs->gc_bit_mask); outp(VGA_PORT_GC_ADDRESS, regs->gc_reg); } static void vga_set_graphics_mode(struct videoregs __far *regs, unsigned read, unsigned write) { vga_gc_reg_write(VGA_GC_REG_GRAPHICS_MODE, (regs->gc_graphics_mode & ~(8|3)) | ((read << 3) & 8) | write & 3); if (write == 0) { // Set registers to a reasonable default ... vga_gc_reg_write(VGA_GC_REG_ENABLE_SET_RESET, 0); vga_gc_reg_write(VGA_GC_REG_DATA_ROTATE, 0); vga_gc_reg_write(VGA_GC_REG_BIT_MASK, 0xFF); } } static inline void vga_select_plane(unsigned plane) { vga_gc_reg_write(VGA_GC_REG_READ_MAP, plane & 0x3); vga_sc_reg_write(VGA_SC_REG_MAP_MASK, 1 << plane); } #endif