diff --git a/README.md b/README.md index 365da21..d6a1d3a 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Once a task is completed, it should be checked off. - [x] Create a DHCP subsystem. Create the `dhcp` command to show current DHCP status information. - [x] Create a UDP and TCP stack. - [x] Implement a simple version of `ftp` and `wget`. -- [ ] Create a graphics subsystem. It should provide functionality to switch between the normal text mode, and a graphics mode. +- [x] Create a graphics subsystem. It should provide functionality to switch between the normal text mode, and a graphics mode. - [ ] Create a simple game of pool. It should use graphics mode to render the game. - [ ] Create a simple game of minigolf. diff --git a/apps/libc/syscalls.h b/apps/libc/syscalls.h index 2782cbc..aa2b8c0 100644 --- a/apps/libc/syscalls.h +++ b/apps/libc/syscalls.h @@ -31,6 +31,7 @@ typedef int int32_t; #define SYS_SEND 15 #define SYS_RECV 16 #define SYS_SOCKSTATE 17 +#define SYS_GFX 18 static inline int32_t syscall0(int num) { int32_t ret; @@ -194,6 +195,92 @@ static inline int32_t sockstate(int32_t sockfd) { return syscall1(SYS_SOCKSTATE, (uint32_t)sockfd); } +/* ================================================================ + * Graphics system calls + * ================================================================ */ + +/** Graphics sub-commands. */ +#define GFX_CMD_ENTER 0 +#define GFX_CMD_LEAVE 1 +#define GFX_CMD_PIXEL 2 +#define GFX_CMD_CLEAR 3 +#define GFX_CMD_FILL_RECT 4 +#define GFX_CMD_LINE 5 +#define GFX_CMD_CIRCLE 6 +#define GFX_CMD_GET_INFO 7 + +/** Graphics mode dimensions. */ +#define GFX_WIDTH 320 +#define GFX_HEIGHT 200 + +/** Palette color constants. */ +#define GFX_BLACK 0 +#define GFX_BLUE 1 +#define GFX_GREEN 2 +#define GFX_CYAN 3 +#define GFX_RED 4 +#define GFX_MAGENTA 5 +#define GFX_BROWN 6 +#define GFX_LIGHT_GREY 7 +#define GFX_DARK_GREY 8 +#define GFX_LIGHT_BLUE 9 +#define GFX_LIGHT_GREEN 10 +#define GFX_LIGHT_CYAN 11 +#define GFX_LIGHT_RED 12 +#define GFX_LIGHT_MAGENTA 13 +#define GFX_YELLOW 14 +#define GFX_WHITE 15 + +/** Command structs for complex drawing operations. */ +typedef struct { uint32_t x, y, w, h, color; } gfx_rect_t; +typedef struct { uint32_t x1, y1, x2, y2, color; } gfx_line_t; +typedef struct { uint32_t cx, cy, r, color; } gfx_circle_t; + +/** Convert RGB (0-255) to palette index using 6x6x6 color cube. */ +static inline uint32_t gfx_rgb(uint32_t r, uint32_t g, uint32_t b) { + return 16 + (r / 51) * 36 + (g / 51) * 6 + (b / 51); +} + +/** Enter graphics mode (320x200x256). */ +static inline int32_t gfx_enter(void) { + return syscall1(SYS_GFX, GFX_CMD_ENTER); +} + +/** Leave graphics mode, return to text. */ +static inline int32_t gfx_leave(void) { + return syscall1(SYS_GFX, GFX_CMD_LEAVE); +} + +/** Set a pixel. */ +static inline int32_t gfx_pixel(uint32_t x, uint32_t y, uint32_t color) { + return syscall3(SYS_GFX, GFX_CMD_PIXEL, (x | (y << 16)), color); +} + +/** Clear screen with a color. */ +static inline int32_t gfx_clear(uint32_t color) { + return syscall2(SYS_GFX, GFX_CMD_CLEAR, color); +} + +/** Fill a rectangle. */ +static inline int32_t gfx_fill_rect(const gfx_rect_t *r) { + return syscall2(SYS_GFX, GFX_CMD_FILL_RECT, (uint32_t)r); +} + +/** Draw a line. */ +static inline int32_t gfx_line(const gfx_line_t *l) { + return syscall2(SYS_GFX, GFX_CMD_LINE, (uint32_t)l); +} + +/** Draw a filled circle. */ +static inline int32_t gfx_circle(const gfx_circle_t *c) { + return syscall2(SYS_GFX, GFX_CMD_CIRCLE, (uint32_t)c); +} + +/** Get graphics info: returns (width | height<<16). */ +static inline int32_t gfx_get_info(void) { + return syscall1(SYS_GFX, GFX_CMD_GET_INFO); +} + /* Basic string operations for user-space */ static inline uint32_t strlen(const char *s) { uint32_t len = 0; diff --git a/build.log b/build.log index f1359d2..8f62697 100644 --- a/build.log +++ b/build.log @@ -39,9 +39,9 @@ Building app: sh Building app: wget Built: /workspaces/claude-os/build/apps_bin/wget (2193 bytes) [ 2%] Built target apps -[ 5%] Built target initrd -[ 7%] Building C object src/CMakeFiles/kernel.dir/syscall.c.o -[ 10%] Linking C executable ../bin/kernel +[ 4%] Built target initrd +[ 7%] Building C object src/CMakeFiles/kernel.dir/graphics.c.o +[ 9%] Linking C executable ../bin/kernel [ 97%] Built target kernel [100%] Generating bootable ISO image xorriso 1.5.6 : RockRidge filesystem manipulator, libburnia project. @@ -50,14 +50,14 @@ Drive current: -outdev 'stdio:/workspaces/claude-os/release/claude-os.iso' Media current: stdio file, overwriteable Media status : is blank Media summary: 0 sessions, 0 data blocks, 0 data, 126g free -Added to ISO image: directory '/'='/tmp/grub.GnJedF' +Added to ISO image: directory '/'='/tmp/grub.EEhNkO' xorriso : UPDATE : 581 files added in 1 seconds Added to ISO image: directory '/'='/workspaces/claude-os/build/isodir' xorriso : UPDATE : 586 files added in 1 seconds xorriso : NOTE : Copying to System Area: 512 bytes from file '/usr/lib/grub/i386-pc/boot_hybrid.img' -xorriso : UPDATE : 65.27% done -ISO image produced: 6030 sectors -Written to medium : 6030 sectors at LBA 0 +xorriso : UPDATE : 67.13% done +ISO image produced: 6054 sectors +Written to medium : 6054 sectors at LBA 0 Writing to 'stdio:/workspaces/claude-os/release/claude-os.iso' completed successfully. [100%] Built target iso diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9c1d680..c0454e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,6 +33,7 @@ add_executable(kernel dhcp.c udp.c tcp.c + graphics.c env.c keyboard.c interrupts.S diff --git a/src/graphics.c b/src/graphics.c new file mode 100644 index 0000000..f34dcb1 --- /dev/null +++ b/src/graphics.c @@ -0,0 +1,450 @@ +/** + * @file graphics.c + * @brief Graphics subsystem implementation. + * + * Provides VGA mode 0x13 (320x200, 256 colors) with drawing primitives. + * Handles switching between text mode (0x03) and graphics mode via + * direct VGA register programming. + */ + +#include "graphics.h" +#include "port_io.h" +#include "font8x16.h" +#include "string.h" +#include "vga.h" + +/* ================================================================ + * VGA register programming constants + * ================================================================ */ + +/* VGA ports */ +#define VGA_MISC_WRITE 0x3C2 +#define VGA_MISC_READ 0x3CC +#define VGA_SEQ_INDEX 0x3C4 +#define VGA_SEQ_DATA 0x3C5 +#define VGA_CRTC_INDEX 0x3D4 +#define VGA_CRTC_DATA 0x3D5 +#define VGA_GC_INDEX 0x3CE +#define VGA_GC_DATA 0x3CF +#define VGA_AC_INDEX 0x3C0 +#define VGA_AC_WRITE 0x3C0 +#define VGA_AC_READ 0x3C1 +#define VGA_INSTAT_READ 0x3DA +#define VGA_DAC_WRITE_IDX 0x3C8 +#define VGA_DAC_DATA 0x3C9 + +/* Number of registers in each group */ +#define VGA_NUM_SEQ_REGS 5 +#define VGA_NUM_CRTC_REGS 25 +#define VGA_NUM_GC_REGS 9 +#define VGA_NUM_AC_REGS 21 + +/* ================================================================ + * VGA register tables for Mode 0x13 (320x200x256) + * ================================================================ */ + +static const uint8_t mode13_misc = 0x63; + +static const uint8_t mode13_seq[VGA_NUM_SEQ_REGS] = { + 0x03, 0x01, 0x0F, 0x00, 0x0E +}; + +static const uint8_t mode13_crtc[VGA_NUM_CRTC_REGS] = { + 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, + 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9C, 0x0E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3, + 0xFF +}; + +static const uint8_t mode13_gc[VGA_NUM_GC_REGS] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, + 0xFF +}; + +static const uint8_t mode13_ac[VGA_NUM_AC_REGS] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x41, 0x00, 0x0F, 0x00, 0x00 +}; + +/* ================================================================ + * VGA register tables for Mode 0x03 (80x25 text) + * ================================================================ */ + +static const uint8_t mode03_misc = 0x67; + +static const uint8_t mode03_seq[VGA_NUM_SEQ_REGS] = { + 0x03, 0x00, 0x03, 0x00, 0x02 +}; + +static const uint8_t mode03_crtc[VGA_NUM_CRTC_REGS] = { + 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, + 0x00, 0x4F, 0x0D, 0x0E, 0x00, 0x00, 0x00, 0x50, + 0x9C, 0x0E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3, + 0xFF +}; + +static const uint8_t mode03_gc[VGA_NUM_GC_REGS] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x00, + 0xFF +}; + +static const uint8_t mode03_ac[VGA_NUM_AC_REGS] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x0C, 0x00, 0x0F, 0x08, 0x00 +}; + +/* ================================================================ + * Module state + * ================================================================ */ + +static int gfx_mode = GFX_MODE_TEXT; +static uint8_t *framebuffer = (uint8_t *)GFX_FRAMEBUFFER; + +/* ================================================================ + * VGA register programming helpers + * ================================================================ */ + +/** + * Write a set of VGA registers to switch modes. + */ +static void vga_write_regs(uint8_t misc, const uint8_t *seq, + const uint8_t *crtc, const uint8_t *gc, + const uint8_t *ac) +{ + /* Miscellaneous output */ + outb(VGA_MISC_WRITE, misc); + + /* Sequencer */ + for (int i = 0; i < VGA_NUM_SEQ_REGS; i++) { + outb(VGA_SEQ_INDEX, (uint8_t)i); + outb(VGA_SEQ_DATA, seq[i]); + } + + /* Unlock CRTC (clear protect bit in register 0x11) */ + outb(VGA_CRTC_INDEX, 0x11); + outb(VGA_CRTC_DATA, inb(VGA_CRTC_DATA) & 0x7F); + + /* CRTC */ + for (int i = 0; i < VGA_NUM_CRTC_REGS; i++) { + outb(VGA_CRTC_INDEX, (uint8_t)i); + outb(VGA_CRTC_DATA, crtc[i]); + } + + /* Graphics Controller */ + for (int i = 0; i < VGA_NUM_GC_REGS; i++) { + outb(VGA_GC_INDEX, (uint8_t)i); + outb(VGA_GC_DATA, gc[i]); + } + + /* Attribute Controller */ + /* Reading port 0x3DA resets the AC flip-flop to index mode */ + inb(VGA_INSTAT_READ); + for (int i = 0; i < VGA_NUM_AC_REGS; i++) { + outb(VGA_AC_INDEX, (uint8_t)i); + outb(VGA_AC_WRITE, ac[i]); + } + /* Re-enable display by setting bit 5 of the AC index */ + outb(VGA_AC_INDEX, 0x20); +} + +/* ================================================================ + * VGA font restore for text mode + * ================================================================ */ + +/** + * Load the 8x16 font into VGA plane 2 after returning to text mode. + * This restores readable text after graphics mode. + */ +static void vga_load_font(void) { + volatile uint8_t *vmem = (volatile uint8_t *)0xA0000; + + /* Set up sequencer for font loading: + * - Map Mask: select plane 2 only + * - Memory Mode: disable chain-4, enable odd/even */ + outb(VGA_SEQ_INDEX, 0x02); + outb(VGA_SEQ_DATA, 0x04); /* Map Mask: plane 2 */ + outb(VGA_SEQ_INDEX, 0x04); + outb(VGA_SEQ_DATA, 0x06); /* Memory Mode: enable sequential, disable chain-4 */ + + /* Set up graphics controller for font loading: + * - Read Map Select: plane 2 + * - Graphics Mode: write mode 0, read mode 0 + * - Miscellaneous: text mode mapping (B8000-BFFFF) */ + outb(VGA_GC_INDEX, 0x04); + outb(VGA_GC_DATA, 0x02); /* Read Map Select: plane 2 */ + outb(VGA_GC_INDEX, 0x05); + outb(VGA_GC_DATA, 0x00); /* Graphics Mode: write mode 0 */ + outb(VGA_GC_INDEX, 0x06); + outb(VGA_GC_DATA, 0x04); /* Misc: map to A0000, no chain, no odd/even */ + + /* Write font data for 256 characters. + * Each char entry is 32 bytes (only first 16 used for 8x16 font). + * Characters outside our font range get a filled block. */ + for (int ch = 0; ch < 256; ch++) { + for (int row = 0; row < 16; row++) { + uint8_t bits; + if (ch >= FONT_FIRST && ch <= FONT_LAST) { + bits = font8x16_data[ch - FONT_FIRST][row]; + } else if (ch == 0) { + bits = 0x00; /* Null char = blank */ + } else { + bits = 0xFF; /* Unknown = filled block */ + } + vmem[ch * 32 + row] = bits; + } + /* Zero out remaining 16 bytes of the 32-byte slot */ + for (int row = 16; row < 32; row++) { + vmem[ch * 32 + row] = 0x00; + } + } + + /* Restore sequencer for text mode: + * - Map Mask: planes 0 and 1 (text attribute + char) + * - Memory Mode: enable odd/even, no chain-4 */ + outb(VGA_SEQ_INDEX, 0x02); + outb(VGA_SEQ_DATA, 0x03); /* Map Mask: planes 0 and 1 */ + outb(VGA_SEQ_INDEX, 0x04); + outb(VGA_SEQ_DATA, 0x02); /* Memory Mode: odd/even addressing */ + + /* Restore graphics controller for text mode */ + outb(VGA_GC_INDEX, 0x04); + outb(VGA_GC_DATA, 0x00); /* Read Map Select: plane 0 */ + outb(VGA_GC_INDEX, 0x05); + outb(VGA_GC_DATA, 0x10); /* Graphics Mode: odd/even */ + outb(VGA_GC_INDEX, 0x06); + outb(VGA_GC_DATA, 0x0E); /* Misc: map to B8000, odd/even */ +} + +/* ================================================================ + * 256-color palette setup + * ================================================================ */ + +/** + * Standard VGA 16-color palette (RGB 6-bit values). + */ +static const uint8_t vga_palette_16[16][3] = { + { 0, 0, 0}, /* 0: Black */ + { 0, 0, 42}, /* 1: Blue */ + { 0, 42, 0}, /* 2: Green */ + { 0, 42, 42}, /* 3: Cyan */ + {42, 0, 0}, /* 4: Red */ + {42, 0, 42}, /* 5: Magenta */ + {42, 21, 0}, /* 6: Brown */ + {42, 42, 42}, /* 7: Light Grey */ + {21, 21, 21}, /* 8: Dark Grey */ + {21, 21, 63}, /* 9: Light Blue */ + {21, 63, 21}, /* 10: Light Green */ + {21, 63, 63}, /* 11: Light Cyan */ + {63, 21, 21}, /* 12: Light Red */ + {63, 21, 63}, /* 13: Light Magenta */ + {63, 63, 21}, /* 14: Yellow */ + {63, 63, 63}, /* 15: White */ +}; + +/** + * Set up the 256-color palette. + * 0-15: Standard 16 VGA colors + * 16-231: 6x6x6 RGB color cube + * 232-255: 24-step grayscale + */ +static void setup_palette(void) { + /* VGA DAC: write index, then R, G, B (6-bit values, 0-63) */ + + /* Standard 16 colors */ + outb(VGA_DAC_WRITE_IDX, 0); + for (int i = 0; i < 16; i++) { + outb(VGA_DAC_DATA, vga_palette_16[i][0]); + outb(VGA_DAC_DATA, vga_palette_16[i][1]); + outb(VGA_DAC_DATA, vga_palette_16[i][2]); + } + + /* 6x6x6 RGB color cube (indices 16-231) */ + outb(VGA_DAC_WRITE_IDX, 16); + for (int r = 0; r < 6; r++) { + for (int g = 0; g < 6; g++) { + for (int b = 0; b < 6; b++) { + outb(VGA_DAC_DATA, (uint8_t)(r * 63 / 5)); + outb(VGA_DAC_DATA, (uint8_t)(g * 63 / 5)); + outb(VGA_DAC_DATA, (uint8_t)(b * 63 / 5)); + } + } + } + + /* 24-step grayscale (indices 232-255) */ + outb(VGA_DAC_WRITE_IDX, 232); + for (int i = 0; i < 24; i++) { + uint8_t v = (uint8_t)(i * 63 / 23); + outb(VGA_DAC_DATA, v); + outb(VGA_DAC_DATA, v); + outb(VGA_DAC_DATA, v); + } +} + +/* ================================================================ + * Mode switching + * ================================================================ */ + +void graphics_enter(void) { + if (gfx_mode == GFX_MODE_GRAPHICS) return; + + /* Switch to mode 0x13 */ + vga_write_regs(mode13_misc, mode13_seq, mode13_crtc, mode13_gc, mode13_ac); + + /* Set up the color palette */ + setup_palette(); + + /* Clear the framebuffer */ + memset(framebuffer, 0, GFX_WIDTH * GFX_HEIGHT); + + gfx_mode = GFX_MODE_GRAPHICS; +} + +void graphics_leave(void) { + if (gfx_mode == GFX_MODE_TEXT) return; + + /* Switch to mode 0x03 */ + vga_write_regs(mode03_misc, mode03_seq, mode03_crtc, mode03_gc, mode03_ac); + + /* Reload the VGA font into plane 2 */ + vga_load_font(); + + /* Clear the text-mode framebuffer */ + volatile uint16_t *text_buf = (volatile uint16_t *)0xB8000; + for (int i = 0; i < 80 * 25; i++) { + text_buf[i] = 0x0720; /* Light grey on black, space */ + } + + gfx_mode = GFX_MODE_TEXT; + + /* Re-initialize the VGA text driver */ + vga_init(); +} + +int graphics_get_mode(void) { + return gfx_mode; +} + +/* ================================================================ + * Drawing primitives + * ================================================================ */ + +void gfx_pixel(int x, int y, uint8_t color) { + if (x < 0 || x >= GFX_WIDTH || y < 0 || y >= GFX_HEIGHT) return; + framebuffer[y * GFX_WIDTH + x] = color; +} + +void gfx_clear(uint8_t color) { + memset(framebuffer, color, GFX_WIDTH * GFX_HEIGHT); +} + +void gfx_fill_rect(int x, int y, int w, int h, uint8_t color) { + /* Clip */ + int x1 = x < 0 ? 0 : x; + int y1 = y < 0 ? 0 : y; + int x2 = (x + w > GFX_WIDTH) ? GFX_WIDTH : (x + w); + int y2 = (y + h > GFX_HEIGHT) ? GFX_HEIGHT : (y + h); + + for (int row = y1; row < y2; row++) { + memset(&framebuffer[row * GFX_WIDTH + x1], color, (uint32_t)(x2 - x1)); + } +} + +void gfx_draw_line(int x1, int y1, int x2, int y2, uint8_t color) { + /* Bresenham's line algorithm */ + int dx = x2 - x1; + int dy = y2 - y1; + int sx = dx >= 0 ? 1 : -1; + int sy = dy >= 0 ? 1 : -1; + if (dx < 0) dx = -dx; + if (dy < 0) dy = -dy; + + int err = dx - dy; + + for (;;) { + gfx_pixel(x1, y1, color); + if (x1 == x2 && y1 == y2) break; + int e2 = 2 * err; + if (e2 > -dy) { err -= dy; x1 += sx; } + if (e2 < dx) { err += dx; y1 += sy; } + } +} + +void gfx_fill_circle(int cx, int cy, int r, uint8_t color) { + if (r <= 0) { gfx_pixel(cx, cy, color); return; } + + for (int y = -r; y <= r; y++) { + /* Horizontal span for this scanline */ + int dx = 0; + while (dx * dx + y * y <= r * r) dx++; + dx--; + int left = cx - dx; + int right = cx + dx; + + /* Clip */ + if (cy + y < 0 || cy + y >= GFX_HEIGHT) continue; + if (left < 0) left = 0; + if (right >= GFX_WIDTH) right = GFX_WIDTH - 1; + if (left > right) continue; + + memset(&framebuffer[(cy + y) * GFX_WIDTH + left], color, + (uint32_t)(right - left + 1)); + } +} + +void gfx_draw_circle(int cx, int cy, int r, uint8_t color) { + /* Midpoint circle algorithm */ + int x = r, y = 0; + int err = 1 - r; + + while (x >= y) { + gfx_pixel(cx + x, cy + y, color); + gfx_pixel(cx + y, cy + x, color); + gfx_pixel(cx - y, cy + x, color); + gfx_pixel(cx - x, cy + y, color); + gfx_pixel(cx - x, cy - y, color); + gfx_pixel(cx - y, cy - x, color); + gfx_pixel(cx + y, cy - x, color); + gfx_pixel(cx + x, cy - y, color); + + y++; + if (err < 0) { + err += 2 * y + 1; + } else { + x--; + err += 2 * (y - x) + 1; + } + } +} + +void gfx_draw_char(int x, int y, char c, uint8_t color) { + if (c < FONT_FIRST || c > FONT_LAST) return; + + const uint8_t *glyph = font8x16_data[c - FONT_FIRST]; + + /* Draw at half vertical scale: 8x8 pixels per character. + * Sample every other row from the 8x16 font. */ + for (int row = 0; row < 8; row++) { + uint8_t bits = glyph[row * 2]; /* Sample even rows */ + for (int col = 0; col < 8; col++) { + if (bits & (0x80 >> col)) { + gfx_pixel(x + col, y + row, color); + } + } + } +} + +void gfx_draw_text(int x, int y, const char *str, uint8_t color) { + int cx = x; + while (*str) { + if (*str == '\n') { + cx = x; + y += 8; + } else { + gfx_draw_char(cx, y, *str, color); + cx += 8; + } + str++; + } +} diff --git a/src/graphics.h b/src/graphics.h new file mode 100644 index 0000000..ba40d16 --- /dev/null +++ b/src/graphics.h @@ -0,0 +1,173 @@ +/** + * @file graphics.h + * @brief Graphics subsystem for ClaudeOS. + * + * Provides VGA mode 0x13 (320x200, 256 colors) graphics with + * drawing primitives. Supports switching between text mode (0x03) + * and graphics mode at runtime. + * + * Color palette: + * 0-15 : Standard 16 VGA colors + * 16-231: 6x6x6 RGB color cube (index = 16 + r*36 + g*6 + b, where r,g,b in 0-5) + * 232-255: 24-step grayscale + */ + +#ifndef GRAPHICS_H +#define GRAPHICS_H + +#include + +/** Graphics mode screen dimensions. */ +#define GFX_WIDTH 320 +#define GFX_HEIGHT 200 + +/** Framebuffer base address for mode 0x13. */ +#define GFX_FRAMEBUFFER 0xA0000 + +/** Graphics mode state. */ +#define GFX_MODE_TEXT 0 +#define GFX_MODE_GRAPHICS 1 + +/* ================================================================ + * Standard palette color indices (0-15) + * ================================================================ */ +#define GFX_BLACK 0 +#define GFX_BLUE 1 +#define GFX_GREEN 2 +#define GFX_CYAN 3 +#define GFX_RED 4 +#define GFX_MAGENTA 5 +#define GFX_BROWN 6 +#define GFX_LIGHT_GREY 7 +#define GFX_DARK_GREY 8 +#define GFX_LIGHT_BLUE 9 +#define GFX_LIGHT_GREEN 10 +#define GFX_LIGHT_CYAN 11 +#define GFX_LIGHT_RED 12 +#define GFX_LIGHT_MAGENTA 13 +#define GFX_YELLOW 14 +#define GFX_WHITE 15 + +/* ================================================================ + * GFX syscall sub-commands + * ================================================================ */ +#define GFX_CMD_ENTER 0 /**< Enter graphics mode. Returns 0. */ +#define GFX_CMD_LEAVE 1 /**< Leave graphics mode (back to text). Returns 0. */ +#define GFX_CMD_PIXEL 2 /**< Set pixel. ECX=(x|y<<16), EDX=color. */ +#define GFX_CMD_CLEAR 3 /**< Clear screen. ECX=color. */ +#define GFX_CMD_FILL_RECT 4 /**< Fill rect. ECX=ptr to gfx_rect_t. */ +#define GFX_CMD_LINE 5 /**< Draw line. ECX=ptr to gfx_line_t. */ +#define GFX_CMD_CIRCLE 6 /**< Draw filled circle. ECX=ptr to gfx_circle_t. */ +#define GFX_CMD_GET_INFO 7 /**< Get info. Returns (width | height<<16). */ + +/* ================================================================ + * Command structs (used with pointer-based sub-commands) + * ================================================================ */ + +typedef struct { + uint32_t x, y, w, h; + uint32_t color; +} gfx_rect_t; + +typedef struct { + uint32_t x1, y1, x2, y2; + uint32_t color; +} gfx_line_t; + +typedef struct { + uint32_t cx, cy, r; + uint32_t color; +} gfx_circle_t; + +/* ================================================================ + * Public API + * ================================================================ */ + +/** + * Convert RGB (0-255 each) to a palette index. + * Uses the 6x6x6 color cube (indices 16-231). + */ +static inline uint8_t gfx_rgb(uint8_t r, uint8_t g, uint8_t b) { + return (uint8_t)(16 + (r / 51) * 36 + (g / 51) * 6 + (b / 51)); +} + +/** + * Enter graphics mode (VGA 0x13: 320x200x256). + * Saves text mode state and initializes the color palette. + */ +void graphics_enter(void); + +/** + * Leave graphics mode and return to text mode (VGA 0x03: 80x25). + * Restores the VGA font and text display. + */ +void graphics_leave(void); + +/** + * Get the current graphics mode. + * @return GFX_MODE_TEXT or GFX_MODE_GRAPHICS. + */ +int graphics_get_mode(void); + +/** + * Set a single pixel. + * @param x X coordinate (0 to GFX_WIDTH-1). + * @param y Y coordinate (0 to GFX_HEIGHT-1). + * @param color Palette color index (0-255). + */ +void gfx_pixel(int x, int y, uint8_t color); + +/** + * Clear the screen with a color. + * @param color Palette color index. + */ +void gfx_clear(uint8_t color); + +/** + * Draw a filled rectangle. + * @param x Top-left X. + * @param y Top-left Y. + * @param w Width. + * @param h Height. + * @param color Palette color index. + */ +void gfx_fill_rect(int x, int y, int w, int h, uint8_t color); + +/** + * Draw a line (Bresenham). + * @param x1,y1 Start point. + * @param x2,y2 End point. + * @param color Palette color index. + */ +void gfx_draw_line(int x1, int y1, int x2, int y2, uint8_t color); + +/** + * Draw a filled circle. + * @param cx,cy Center. + * @param r Radius. + * @param color Palette color index. + */ +void gfx_fill_circle(int cx, int cy, int r, uint8_t color); + +/** + * Draw a circle outline. + * @param cx,cy Center. + * @param r Radius. + * @param color Palette color index. + */ +void gfx_draw_circle(int cx, int cy, int r, uint8_t color); + +/** + * Draw a text string using the 8x16 font (scaled to 8x8 in mode 13). + * @param x,y Top-left position. + * @param str Null-terminated string. + * @param color Palette color index. + */ +void gfx_draw_text(int x, int y, const char *str, uint8_t color); + +/** + * Draw a single character using the 8x16 font at half vertical scale (8x8). + */ +void gfx_draw_char(int x, int y, char c, uint8_t color); + +#endif /* GRAPHICS_H */ diff --git a/src/syscall.c b/src/syscall.c index cc03e6b..a213568 100644 --- a/src/syscall.c +++ b/src/syscall.c @@ -19,6 +19,7 @@ #include "pmm.h" #include "tcp.h" #include "udp.h" +#include "graphics.h" #include #include @@ -465,6 +466,64 @@ static int32_t sys_sockstate(registers_t *regs) { return -1; } +/** + * Handle SYS_GFX: graphics operations. + * EBX = sub-command, ECX = arg1, EDX = arg2. + */ +static int32_t sys_gfx(registers_t *regs) { + uint32_t cmd = regs->ebx; + uint32_t arg1 = regs->ecx; + uint32_t arg2 = regs->edx; + + switch (cmd) { + case GFX_CMD_ENTER: + graphics_enter(); + return 0; + + case GFX_CMD_LEAVE: + graphics_leave(); + return 0; + + case GFX_CMD_PIXEL: { + int x = (int)(arg1 & 0xFFFF); + int y = (int)(arg1 >> 16); + gfx_pixel(x, y, (uint8_t)arg2); + return 0; + } + + case GFX_CMD_CLEAR: + gfx_clear((uint8_t)arg1); + return 0; + + case GFX_CMD_FILL_RECT: { + const gfx_rect_t *r = (const gfx_rect_t *)arg1; + gfx_fill_rect((int)r->x, (int)r->y, (int)r->w, (int)r->h, + (uint8_t)r->color); + return 0; + } + + case GFX_CMD_LINE: { + const gfx_line_t *l = (const gfx_line_t *)arg1; + gfx_draw_line((int)l->x1, (int)l->y1, (int)l->x2, (int)l->y2, + (uint8_t)l->color); + return 0; + } + + case GFX_CMD_CIRCLE: { + const gfx_circle_t *c = (const gfx_circle_t *)arg1; + gfx_fill_circle((int)c->cx, (int)c->cy, (int)c->r, + (uint8_t)c->color); + return 0; + } + + case GFX_CMD_GET_INFO: + return (int32_t)(GFX_WIDTH | (GFX_HEIGHT << 16)); + + default: + return -1; + } +} + /** System call dispatch table. */ typedef int32_t (*syscall_fn)(registers_t *); static syscall_fn syscall_table[NUM_SYSCALLS] = { @@ -486,6 +545,7 @@ static syscall_fn syscall_table[NUM_SYSCALLS] = { [SYS_SEND] = sys_send, [SYS_RECV] = sys_recv, [SYS_SOCKSTATE] = sys_sockstate, + [SYS_GFX] = sys_gfx, }; void syscall_handler(registers_t *regs) { diff --git a/src/syscall.h b/src/syscall.h index 52e1bda..281b65d 100644 --- a/src/syscall.h +++ b/src/syscall.h @@ -32,9 +32,10 @@ #define SYS_SEND 15 /**< Send data on socket. sockfd=EBX, buf=ECX, len=EDX. Returns bytes sent. */ #define SYS_RECV 16 /**< Receive data from socket. sockfd=EBX, buf=ECX, len=EDX. Returns bytes. */ #define SYS_SOCKSTATE 17 /**< Get socket state. sockfd=EBX. Returns state constant. */ +#define SYS_GFX 18 /**< Graphics operations. subcmd=EBX, arg1=ECX, arg2=EDX. */ /** Total number of system calls. */ -#define NUM_SYSCALLS 18 +#define NUM_SYSCALLS 19 /** * Initialize the system call handler.