Implement graphics subsystem with VGA mode 0x13 and drawing primitives (AI)
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
14
build.log
14
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
|
||||
|
||||
@@ -33,6 +33,7 @@ add_executable(kernel
|
||||
dhcp.c
|
||||
udp.c
|
||||
tcp.c
|
||||
graphics.c
|
||||
env.c
|
||||
keyboard.c
|
||||
interrupts.S
|
||||
|
||||
450
src/graphics.c
Normal file
450
src/graphics.c
Normal file
@@ -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++;
|
||||
}
|
||||
}
|
||||
173
src/graphics.h
Normal file
173
src/graphics.h
Normal file
@@ -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 <stdint.h>
|
||||
|
||||
/** 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 */
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "pmm.h"
|
||||
#include "tcp.h"
|
||||
#include "udp.h"
|
||||
#include "graphics.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user