#include "../../Common/std.h" #include "../../Common/str.h" #include "display.h" #include #include #include #include #define WIDTH 640 #define HEIGHT 480 #define MODE_VGA_320_200_8 0x13 #define MODE_VESA_640_480_8 0x101 /* https://www.phatcode.net/res/262/files/dpmi09.html */ /* http://www.techhelpmanual.com/27-dos__bios___extensions_service_index.html */ // 8-color VGA palette gradient, lighter shades of lavender to pink #define COLOR_OFFSET 0x50 #define COLOR_NUM 8 #define COLOR_STEP (HEIGHT / COLOR_NUM) intptr_t baseaddr; /* http://www.techhelpmanual.com/649-dpmiregrec.html */ struct DpmiRegRec { uint32_t edi; uint32_t esi; uint32_t ebi; uint32_t _reserved; uint32_t ebx; uint32_t edx; uint32_t ecx; uint32_t eax; uint16_t flags; uint16_t es; uint16_t ds; uint16_t fs; uint16_t gs; uint16_t ip; uint16_t cs; uint16_t sp; uint16_t ss; } __attribute__((packed)) dpmi_regs; /* http://www.techhelpmanual.com/602-int_31h_0300h__simulate_real_mode_interrupt.html */ static inline struct DpmiRegRec *real_mode_interrupt(uint8_t interrupt) { intptr_t result; bool carry_clear; __asm__ volatile("int $0x31" : "=@ccs"(carry_clear), "=D"(result) : "a"(0x0300), "b"(interrupt), "c"(0), "D"(&dpmi_regs) : "memory", "cc"); return !carry_clear ? (struct DpmiRegRec *)(result) : NULL; } /* https://wiki.osdev.org/VESA_Video_Modes */ struct __attribute__((packed)) VbeInfoBlock { char VbeSignature[4]; // == "VESA" uint16_t VbeVersion; // == 0x0300 for VBE 3.0 uint16_t OemStringPtr[2]; // isa vbeFarPtr uint8_t Capabilities[4]; uint16_t VideoModePtr[2]; // isa vbeFarPtr uint16_t TotalMemory; // as # of 64KB blocks uint8_t Reserved[492]; }; struct __attribute__((packed)) ModeInfoBlock { uint16_t attributes; // deprecated, only bit 7 should be of interest to you, // and indicates the mode supports a linear frame buffer. uint8_t window_a; // deprecated uint8_t window_b; // deprecated uint16_t granularity; // deprecated; used while calculating bank numbers uint16_t window_size; uint16_t segment_a; uint16_t segment_b; uint32_t win_func_ptr; // deprecated; used to switch banks from protected mode // without returning to real mode uint16_t pitch; // number of bytes per horizontal line uint16_t width; // width in pixels uint16_t height; // height in pixels uint8_t w_char; // unused... uint8_t y_char; // ... uint8_t planes; uint8_t bpp; // bits per pixel in this mode uint8_t banks; // deprecated; total number of banks in this mode uint8_t memory_model; uint8_t bank_size; // deprecated; size of a bank, almost always 64 KB but may // be 16 KB... uint8_t image_pages; uint8_t reserved0; uint8_t red_mask; uint8_t red_position; uint8_t green_mask; uint8_t green_position; uint8_t blue_mask; uint8_t blue_position; uint8_t reserved_mask; uint8_t reserved_position; uint8_t direct_color_attributes; uint32_t framebuffer; // physical address of the linear frame buffer; write // here to draw to the screen uint32_t off_screen_mem_off; uint16_t off_screen_mem_size; // size of memory in the framebuffer but not // being displayed on the screen uint8_t reserved1[206]; }; // static char get_video_mode(); // #pragma aux get_video_mode = "mov ah, 0x0f" \ // "int 0x10" value[al] modify[ah]; // static void set_video_mode(unsigned char); // #pragma aux set_video_mode = "mov ah, 0x00" \ // "int 0x10" parm[al] modify[ah]; static inline void set_vesa_video_mode(uint16_t mode) { __asm__ volatile("int $0x10" ::"a"(0x4f02), "b"(mode) : "memory", "cc"); } // static void wait_for_key(); // #pragma aux wait_for_key = "mov ah, 0x00" \ // "int 0x16" modify[ah]; static inline uint16_t get_vesa_controller_info(struct VbeInfoBlock *block) { // dpmi_regs.eax = 0x4f00; // dpmi_regs.es = (intptr_t)block >> 4; // dpmi_regs.edi = (intptr_t)block & 0xF; // struct DpmiRegRec *r = real_mode_interrupt(0x10); // if (!r) { // com_std_print('c', "Error calling real mode interrupt 0x10"); // return 0; // } /* Hope and play we don't need to set ES here. */ uint16_t result; __asm__ volatile(/*"push %%es\n" "movw %0, %%es\n"*/ "int $0x10\n" // "pop %%es" : "=a"(result) : "a"(0x4f00), /*"r"((intptr_t)block >> 16), */ "di"((intptr_t)block) : "memory", "cc"); // com_std_print('u', r->eax, 'u', r->es, 'u', r->edi); // return r->eax; return result; } static inline uint16_t get_vesa_mode_info(uint16_t mode, struct ModeInfoBlock *block) { /* Hope and play we don't need to set ES here. */ uint16_t result; __asm__ volatile(/*"push %%es\n" "movw %0, %%es\n" */ "int $0x10\n" // "pop %%es" : "=a"(result) : "a"(0x4f01), "c"(mode), /*"r"((intptr_t)block >> 16),*/ "di"((intptr_t)block) : "memory", "cc"); return result; } /* https://tippach.hier-im-netz.de/wdosx/int21api.html#f09 */ static inline void print_to_console(char *const str) { __asm__ volatile("int $0x21" ::"a"(0x0900), "d"(str) : "memory", "cc", "esi", "edi", "esp"); } // static short get_vesa_controller_info(struct VbeInfoBlock *); // #pragma aux get_vesa_controller_info = "mov ax, 0x4f00" \ // "int 0x10" parm[es di] value[ax]; // static short get_vesa_mode_info(uint16_t mode, struct ModeInfoBlock *); // #pragma aux get_vesa_mode_info = "mov ax, 0x4F01" \ // "int 0x10" parm[cx][es di] value[ax]; typedef struct { uint16_t segment; uint16_t selector; intptr_t linear; } LowMemBlock; // Allocates low memory under 1MB static inline LowMemBlock allocate_low_mem(uint32_t size_in_bytes) { LowMemBlock block = {0, 0}; uint32_t paragraphs = (size_in_bytes + 15) >> 4; // Convert bytes to paragraphs uint16_t seg, sel; uint8_t failed = 0; __asm__ __volatile__("int $0x31\n" "setc %2\n" // Check Carry Flag for error : "=a"(seg), "=d"(sel), "=r"(failed) : "a"(0x0100), "b"(paragraphs) : "cc"); if (!failed) { block.segment = seg; block.selector = sel; block.linear = (sel << 4) + seg - baseaddr; } return block; } // Frees the allocated low memory (DPMI Ax=0101h) static inline void free_low_mem(uint16_t selector) { __asm__ __volatile__("int $0x31" : : "a"(0x0101), "d"(selector) : "cc"); } /* https://wiki.osdev.org/User:Omarrx024/VESA_Tutorial */ static char *init_vesa_mode(void) { set_vesa_video_mode(MODE_VESA_640_480_8 | 0x4000); LowMemBlock const mem = allocate_low_mem(sizeof(struct VbeInfoBlock) + sizeof(struct ModeInfoBlock)); struct VbeInfoBlock *ctrl = (struct VbeInfoBlock *)((intptr_t)mem.linear); struct ModeInfoBlock *inf = (struct ModeInfoBlock *)((intptr_t)mem.linear + sizeof(struct VbeInfoBlock)); uint16_t *modes; int i; static struct com_str vbe2str = com_str_lit("VBE2"); com_str_copy_to_mem(&vbe2str, ctrl->VbeSignature, false); if (get_vesa_controller_info(ctrl + baseaddr) != 0x004F) return NULL; modes = (uint16_t *)(((intptr_t)ctrl->VideoModePtr[0] << 4) + ctrl->VideoModePtr[1] - baseaddr); for (i = 0; modes[i] != 0xFFFF; ++i) { if (get_vesa_mode_info(modes[i], inf + baseaddr) != 0x004F) continue; com_std_print('c', "Mode:", 'u', modes[i] & 0xFFFFBFFF, 'u', inf->width, 'u', inf->height, 'u', inf->bpp); // Check if this is a graphics mode with linear frame buffer support if ((inf->attributes & 0x80) != 0x80) continue; // Check if this is a packed pixel or direct color mode if (inf->memory_model != 4 && inf->memory_model != 6) continue; // Check if this is exactly the mode we're looking for if (WIDTH == inf->width && HEIGHT == inf->height && 8 == inf->bpp) return (char *)(intptr_t)inf->framebuffer - baseaddr; } return NULL; } // static inline void copy_to_low_mem(LowMemBlock block, char *data, // uint32_t size) { // __asm__ volatile("movw %0, %%es\n" // "movw %1, %%di\n" // "movw %2, %%si\n" // "movw .data, %%ax\n" // "movw %3, %%cx\n" // "cld\n" // "rep movsb" ::"r"(block.rm_segment), // "r"(block.pm_selector), "r"(data), "r"(size) // : "memory", "cc", "ax", "bx", "cx", "esi", "di"); // } static inline void set_video_mode(uint16_t mode) { __asm__ volatile("int $0x10" : : "a"(mode) : "memory", "cc"); } /* http://www.techhelpmanual.com/630-int_31h_0800h__physical_address_mapping.html */ static inline void *map_physical_address(uint32_t physical_addr, uint32_t size) { uint16_t high, low; bool carry_clear; __asm__ volatile("int $0x31" : "=b"(high), "=c"(low), "=@ccs"(carry_clear) : "a"(0x0800), "b"(physical_addr >> 16), "c"(physical_addr & 0xFFFF), "S"(size >> 16), "D"(size & 0xFFFF) : "memory", "cc"); return !carry_clear ? NULL : (void *)(intptr_t)((high << 16) | low); } static inline uint16_t alloc_ldt_descriptor(void) { bool carry_clear; uint16_t selector; __asm__ volatile("int $0x31" : "=a"(selector), "=@ccs"(carry_clear) : "a"(0x0000), "c"(1) : "memory", "cc"); return !carry_clear ? selector : 0; } static inline bool set_segment_base_address(uint16_t selector, uint32_t baseaddr) { bool carry_clear; __asm__ volatile("int $0x31" : "=a"(selector), "=@ccs"(carry_clear) : "a"(0x0007), "c"(baseaddr >> 16), "d"(baseaddr & 0xFFFF) : "memory", "cc"); return !carry_clear; } static inline bool set_segment_limit(uint16_t selector, uint32_t size) { bool carry_clear; __asm__ volatile("int $0x31" : "=@ccs"(carry_clear) : "a"(0x0008), "b"(selector), "c"(size >> 16), "d"(size & 0xFFFF) : "memory", "cc"); return !carry_clear; } static inline bool set_segment_rights(uint16_t selector, bool read, bool write) { bool carry_clear; uint16_t flags = 1 | (write << 1) | (1 << 4) | (1 << 7) | (1 << 14) | (1 << 15); __asm__ volatile("int $0x31" : "=@ccs"(carry_clear) : "a"(0x0009), "b"(selector), "c"(flags) : "memory", "cc"); return !carry_clear; } static inline intptr_t get_segment_base_address(uint16_t selector) { bool carry_clear; uint16_t high, low; __asm__ volatile("int $0x31" : "=@ccs"(carry_clear), "=c"(high), "=d"(low) : "a"(0x0006), "b"(selector) : "memory", "cc"); // return carry_clear ? 0 : (((intptr_t)high << 16) | (intptr_t)low); return ((intptr_t)high << 16) | (intptr_t)low; } static inline void use_segment(uint16_t selector) { __asm__ volatile("" : "=S"(selector)::); } static uint32_t test = 0x1420124; extern int plr_display_dos_main(void) { /* Fat DS trick, see __djgpp_nearptr_enable() */ uint16_t ds_selector; asm("movw %%ds, %0" : "=r"(ds_selector)); set_segment_limit(ds_selector, 0xFFFFFFFF); baseaddr = get_segment_base_address(ds_selector); if (baseaddr == 0) return -1; char *volatile buf = (char *)((intptr_t)0xA0000 - baseaddr); // VGA memory address /* TODO: Make VESA work for sweet sweet 640x480. */ // unsigned char saved_mode = get_video_mode(); // Save original mode // set_vesa_video_mode(MODE_VESA_640_480_8); // Set our VGA mode // set_video_mode(MODE_VGA_320_200_8); // Set our VGA mode buf = init_vesa_mode(); if (!buf) return -1; /* TODO: Fetch info about the mode, to know the availability */ /* as well as base address in memory */ /* https://wiki.osdev.org/VESA_Video_Modes */ // Now draw. We draw pixel-by-pixel by directly setting the video memory // like it was an array. We step the color every 25 pixels so that we have 8 // bands of even height. // for (y = 0; y < HEIGHT; y++) { // for (x = 0; x < WIDTH; x++) { // buf[y * WIDTH + x] = COLOR_OFFSET + y / COLOR_STEP; // } // } // wait_for_key(); // set_video_mode(saved_mode); // Restore original mode int x, y; while (1) { for (y = 0; y < HEIGHT; y++) { for (x = 0; x < WIDTH; x++) { buf[y * WIDTH + x] = COLOR_OFFSET + y / COLOR_STEP; } } } // dpmi_regs = (struct dpmi_regs){.eax = 0x00004C00}; // __asm__ volatile("movw $0x0300, %%ax\n" // "movb $0x21, %%bl\n" // "xorb %%bh, %%bh\n" // "xorw %%cx, %%cx\n" // "movl $dpmi_regs, %%edi\n" // "int $0x31" :: // : "memory", "cc", "ax", "bx", "cx", "edi"); return 0; }