diff --git a/.gitignore b/.gitignore index 3a5445f..d13b2f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # ---> C # Prerequisites *.d +deploy_image.sh # Object files *.o diff --git a/include/util.h b/include/util.h index 81c2263..c1bc16a 100644 --- a/include/util.h +++ b/include/util.h @@ -2,5 +2,9 @@ #define _UTIL_H_ void displayMessage(int n); +int check_trio_chip(); +int get_trio_memory(); +int int386x_real(int intnum, union REGS *in, union REGS *out, struct SREGS *sregs); +const char *vbe_mode_description(unsigned mode); #endif diff --git a/include/vbe12gr.h b/include/vbe12gr.h new file mode 100644 index 0000000..8c36df1 --- /dev/null +++ b/include/vbe12gr.h @@ -0,0 +1,7 @@ +#ifndef _VBE12GR_H_ +#define _VBE12GR_H_ + +int get_current_vbe_mode(unsigned *mode_out); +int get_vbe_version(void); + +#endif diff --git a/main.err b/main.err new file mode 100644 index 0000000..dff50f0 --- /dev/null +++ b/main.err @@ -0,0 +1,3 @@ +src/main.c(56): Warning! E1178: Type qualifier mismatch +src/main.c(56): Note! I2003: source conversion type is 'char const *' +src/main.c(56): Note! I2004: target conversion type is 'char *' diff --git a/src/main.c b/src/main.c index eb9662b..3ad35a8 100644 --- a/src/main.c +++ b/src/main.c @@ -1,14 +1,60 @@ #include #include "util.h" +#include "vbe12gr.h" void main() { int i = 0; + unsigned prevMode = 0xFFFF; + char *descr; printf("DSPACE starting...\n"); - for (i = 0; i < 10; i++) { - displayMessage(i + 1); + if (check_trio_chip()) { + printf("S3 Trio64 detected\n"); + } else { + printf("S3 Trio64 video card not found, exiting.\n"); + return; + } + + i = get_trio_memory(); + + printf("Video DRAM: "); + switch (i) { + case 7: + printf("0.5 MB\n"); + break; + case 6: + printf("1 MB\n"); + break; + case 4: + printf("2 MB\n"); + break; + case 0: + printf("4 MB\n"); + break; + default: + printf("Unable to determine ram\n"); + } + + i = get_vbe_version(); + if (i < 0) { + printf("Error on determining VBE version: %d\n", i); + return; + } else if (i == 0) { + printf("VBE not supported, exiting\n"); + return; + } else if (i < 200) { + printf("VBE 1.2 supported\n"); + } else { + printf("VBE 2.0 supported\n"); + } + + if (get_current_vbe_mode(&prevMode)) { + printf("Unable to get current mode\n"); + } else { + descr = vbe_mode_description(prevMode); + printf("Current mode = %s\n", descr); } return; diff --git a/src/util.c b/src/util.c index 7e20c47..682b676 100644 --- a/src/util.c +++ b/src/util.c @@ -1,5 +1,183 @@ #include +#include +#include +#include void displayMessage(int n) { printf("%02d: Hello MS-DOS!\n", n); } + +int check_trio_chip() { + unsigned char cr30_value; + + /* 1. Unlock the S3 VGA Registers (CR30-CR3F) */ + // Select the CR38 index register via the address port (0x3D4) + outp(0x3D4, 0x38); + // Write the unlocking code (0x48) to the data port (0x3D5) + outp(0x3D5, 0x48); + + /* 2. Read the Chip ID/REV register (CR30) */ + // Select the CR30 index register via the address port (0x3D4) + outp(0x3D4, 0x30); + // Read the contents of CR30 from the data port (0x3D5) + cr30_value = inp(0x3D5); + + /* 3. Check the Chip ID */ + // Mask out the lower nibble (revision status) and compare to 0xE0 + if ((cr30_value & 0xF0) == 0xE0) { + return 1; // Chip ID matches + } else { + return 0; // Chip ID does not match, abort + } +} + +int get_trio_memory() { + /* 1. Read the Chip ID/REV register (CR36) */ + // Select the CR30 index register via the address port (0x3D4) + outp(0x3D4, 0x36); + // Read the contents of CR36 from the data port (0x3D5) + return inp(0x3D5) >> 5; +} + +#pragma pack(push, 1) +typedef struct { + unsigned long edi; + unsigned long esi; + unsigned long ebp; + unsigned long reserved; + unsigned long ebx; + unsigned long edx; + unsigned long ecx; + unsigned long eax; + unsigned short flags; + unsigned short es; + unsigned short ds; + unsigned short fs; + unsigned short gs; + unsigned short ip; + unsigned short cs; + unsigned short sp; + unsigned short ss; +} RMCS; +#pragma pack(pop) + +/* + * int386x_real - simulate a real-mode interrupt via DPMI (int 0x31, AX=0x0300). + * Same calling shape as int386x(), but intnum is dispatched in real mode + * (BIOS/DOS calls), not as a protected-mode interrupt. + * + * Returns 0 on success, -1 if the DPMI call itself failed (check regs + * are otherwise untouched in that case). + */ +int int386x_real(int intnum, union REGS *in, union REGS *out, struct SREGS *sregs) +{ + RMCS rmi; + union REGS dregs; + struct SREGS dsregs; + + memset(&rmi, 0, sizeof(rmi)); + + rmi.eax = in->x.eax; + rmi.ebx = in->x.ebx; + rmi.ecx = in->x.ecx; + rmi.edx = in->x.edx; + rmi.esi = in->x.esi; + rmi.edi = in->x.edi; + rmi.es = sregs->es; + rmi.ds = sregs->ds; + rmi.fs = sregs->fs; + rmi.gs = sregs->gs; + + /* SS:SP left as 0:0 -> DPMI host provides a temporary real-mode stack */ + + memset(&dregs, 0, sizeof(dregs)); + memset(&dsregs, 0, sizeof(dsregs)); + + dregs.w.ax = 0x0300; /* DPMI: Simulate Real Mode Interrupt */ + dregs.h.bl = (unsigned char)intnum; + dregs.h.bh = 0; + dregs.w.cx = 0; /* no words to copy onto real-mode stack */ + dsregs.es = FP_SEG(&rmi); + dregs.x.edi = FP_OFF(&rmi); + + int386x(0x31, &dregs, &dregs, &dsregs); + + if (dregs.w.cflag) { + return -1; /* DPMI call itself failed */ + } + + out->x.eax = rmi.eax; + out->x.ebx = rmi.ebx; + out->x.ecx = rmi.ecx; + out->x.edx = rmi.edx; + out->x.esi = rmi.esi; + out->x.edi = rmi.edi; + out->w.cflag = rmi.flags & 0x0001; /* real-mode call's own carry flag */ + sregs->es = rmi.es; + sregs->ds = rmi.ds; + sregs->fs = rmi.fs; + sregs->gs = rmi.gs; + + return 0; +} + +const char *vbe_mode_description(unsigned mode) +{ + /* Strip VBE mode-set flag bits (bit 15 = LFB request, bit 14 = + refresh rate control) before lookup -- these aren't part of + the mode number itself. */ + unsigned base_mode = mode & 0x01FF; + + switch (base_mode) { + /* Standard VGA modes */ + case 0x00: return "40x25 16-color (mono) text"; + case 0x01: return "40x25 16-color text"; + case 0x02: return "80x25 16-color (mono) text"; + case 0x03: return "80x25 16-color text"; + case 0x04: return "320x200 4-color graphics (CGA)"; + case 0x05: return "320x200 4-color (mono) graphics"; + case 0x06: return "640x200 2-color graphics"; + case 0x07: return "80x25 monochrome text"; + case 0x0D: return "320x200 16-color graphics (EGA)"; + case 0x0E: return "640x200 16-color graphics"; + case 0x0F: return "640x350 monochrome graphics"; + case 0x10: return "640x350 16-color graphics (EGA/VGA)"; + case 0x11: return "640x480 2-color (mono) graphics (VGA)"; + case 0x12: return "640x480 16-color graphics (VGA)"; + case 0x13: return "320x200 256-color graphics (Mode 13h)"; + + /* VESA SVGA modes */ + case 0x100: return "640x400 256-color (VESA)"; + case 0x101: return "640x480 256-color (VESA)"; + case 0x102: return "800x600 16-color (VESA)"; + case 0x103: return "800x600 256-color (VESA)"; + case 0x104: return "1024x768 16-color (VESA)"; + case 0x105: return "1024x768 256-color (VESA)"; + case 0x106: return "1280x1024 16-color (VESA)"; + case 0x107: return "1280x1024 256-color (VESA)"; + case 0x108: return "80x60 text (VESA)"; + case 0x109: return "132x25 text (VESA)"; + case 0x10A: return "132x43 text (VESA)"; + case 0x10B: return "132x50 text (VESA)"; + case 0x10C: return "132x60 text (VESA)"; + + /* VBE 1.2 direct color modes */ + case 0x10D: return "320x200 15bpp direct color (VESA)"; + case 0x10E: return "320x200 16bpp direct color (VESA)"; + case 0x10F: return "320x200 24bpp direct color (VESA)"; + case 0x110: return "640x480 15bpp direct color (VESA)"; + case 0x111: return "640x480 16bpp direct color (VESA)"; + case 0x112: return "640x480 24bpp direct color (VESA)"; + case 0x113: return "800x600 15bpp direct color (VESA)"; + case 0x114: return "800x600 16bpp direct color (VESA)"; + case 0x115: return "800x600 24bpp direct color (VESA)"; + case 0x116: return "1024x768 15bpp direct color (VESA)"; + case 0x117: return "1024x768 16bpp direct color (VESA)"; + case 0x118: return "1024x768 24bpp direct color (VESA)"; + case 0x119: return "1280x1024 15bpp direct color (VESA)"; + case 0x11A: return "1280x1024 16bpp direct color (VESA)"; + case 0x11B: return "1280x1024 24bpp direct color (VESA)"; + + default: return "Unknown/unsupported mode"; + } +} diff --git a/src/vbe12gr.c b/src/vbe12gr.c new file mode 100644 index 0000000..ad5fc1f --- /dev/null +++ b/src/vbe12gr.c @@ -0,0 +1,97 @@ +#include +#include + +#include "util.h" + +#define VBE_SIG_SIZE 4 + +int get_current_vbe_mode(unsigned *mode_out) { + union REGS regs; + struct SREGS sregs; + + memset(®s, 0, sizeof(regs)); + memset(&sregs, 0, sizeof(sregs)); + + regs.w.ax = 0x4F03; + + int386x_real(0x10, ®s, ®s, &sregs); + + if (regs.w.ax != 0x004F) { + return -1; + } + + *mode_out = regs.w.bx; + return 0; +} + +int get_vbe_version(void) +{ + union REGS regs; + struct SREGS sregs; + unsigned dos_segment; + unsigned dos_selector; + unsigned char *buf; + unsigned version_bcd; + int result; + + memset(®s, 0, sizeof(regs)); + memset(&sregs, 0, sizeof(sregs)); + + regs.w.ax = 0x0100; /* DPMI: Allocate DOS Memory Block */ + regs.w.bx = 0x0020; /* 0x20 paragraphs = 512 bytes */ + int386x(0x31, ®s, ®s, &sregs); /* direct DPMI call, not _real */ + + if (regs.w.cflag) { + return -1; + } + + dos_segment = regs.w.ax; + dos_selector = regs.w.dx; + + buf = (unsigned char *)(dos_segment << 4); + + memset(buf, 0, 512); + memcpy(buf, "VBE2", 4); + + memset(®s, 0, sizeof(regs)); + memset(&sregs, 0, sizeof(sregs)); + regs.w.ax = 0x4F00; + sregs.es = dos_segment; + regs.w.di = 0x0000; + + int386x_real(0x10, ®s, ®s, &sregs); /* real BIOS call - correct as-is */ + + result = 0; + + if ((regs.w.ax & 0x00FF) != 0x4F) { + result = 0; + } else if (((regs.w.ax >> 8) & 0xFF) != 0x00) { + result = 0; + } else { + if (memcmp(buf, "VESA", 4) != 0) { + result = 0; + } else { + version_bcd = *(unsigned short *)(buf + 4); + + if (version_bcd >= 0x0200) { + result = 200; + } else if (version_bcd >= 0x0102) { + result = 102; + } else { + result = 0; + } + } + } + + memset(®s, 0, sizeof(regs)); + memset(&sregs, 0, sizeof(sregs)); + regs.w.ax = 0x0101; /* DPMI: Free DOS Memory Block */ + regs.w.dx = dos_selector; + int386x(0x31, ®s, ®s, &sregs); /* direct DPMI call, not _real */ + + if (regs.w.cflag && result == 0) { + return -2; + } + + return result; +}