:: limine / common / drivers / vga_textmode.c 11.7 KB raw

1
#if defined (BIOS)
2
3
#include <stdint.h>
4
#include <stddef.h>
5
#include <stdbool.h>
6
#include <drivers/vga_textmode.h>
7
#include <sys/cpu.h>
8
#include <lib/real.h>
9
#include <lib/libc.h>
10
#include <lib/misc.h>
11
#include <lib/term.h>
12
#include <mm/pmm.h>
13
14
#define VIDEO_BOTTOM ((VD_ROWS * VD_COLS) - 1)
15
16
static void draw_cursor(struct textmode_context *ctx) {
17
    uint8_t pal = ctx->back_buffer[ctx->cursor_offset + 1];
18
    ctx->video_mem[ctx->cursor_offset + 1] = ((pal & 0xf0) >> 4) | ((pal & 0x0f) << 4);
19
}
20
21
static void text_save_state(struct flanterm_context *_ctx) {
22
    struct textmode_context *ctx = (void *)_ctx;
23
    ctx->saved_state_text_palette = ctx->text_palette;
24
    ctx->saved_state_cursor_offset = ctx->cursor_offset;
25
}
26
27
static void text_restore_state(struct flanterm_context *_ctx) {
28
    struct textmode_context *ctx = (void *)_ctx;
29
    ctx->text_palette = ctx->saved_state_text_palette;
30
    ctx->cursor_offset = ctx->saved_state_cursor_offset;
31
}
32
33
static void text_swap_palette(struct flanterm_context *_ctx) {
34
    struct textmode_context *ctx = (void *)_ctx;
35
    ctx->text_palette = (ctx->text_palette << 4) | (ctx->text_palette >> 4);
36
}
37
38
static void text_scroll(struct flanterm_context *_ctx) {
39
    struct textmode_context *ctx = (void *)_ctx;
40
41
    // move the text up by one row
42
    for (size_t i = _ctx->scroll_top_margin * VD_COLS;
43
         i < (_ctx->scroll_bottom_margin - 1) * VD_COLS; i++) {
44
        ctx->back_buffer[i] = ctx->back_buffer[i + VD_COLS];
45
    }
46
    // clear the last line of the screen
47
    for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS;
48
         i < _ctx->scroll_bottom_margin * VD_COLS; i += 2) {
49
        ctx->back_buffer[i] = ' ';
50
        ctx->back_buffer[i + 1] = ctx->text_palette;
51
    }
52
}
53
54
static void text_revscroll(struct flanterm_context *_ctx) {
55
    struct textmode_context *ctx = (void *)_ctx;
56
57
    // move the text up by one row
58
    for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS - 2; ; i--) {
59
        ctx->back_buffer[i + VD_COLS] = ctx->back_buffer[i];
60
        if (i == _ctx->scroll_top_margin * VD_COLS) {
61
            break;
62
        }
63
    }
64
    // clear the first line of the screen
65
    for (size_t i = _ctx->scroll_top_margin * VD_COLS;
66
         i < (_ctx->scroll_top_margin + 1) * VD_COLS; i += 2) {
67
        ctx->back_buffer[i] = ' ';
68
        ctx->back_buffer[i + 1] = ctx->text_palette;
69
    }
70
}
71
72
static void text_clear(struct flanterm_context *_ctx, bool move) {
73
    struct textmode_context *ctx = (void *)_ctx;
74
75
    for (size_t i = 0; i < VIDEO_BOTTOM; i += 2) {
76
        ctx->back_buffer[i] = ' ';
77
        ctx->back_buffer[i + 1] = ctx->text_palette;
78
    }
79
    if (move) {
80
        ctx->cursor_offset = 0;
81
    }
82
}
83
84
static void text_full_refresh(struct flanterm_context *_ctx) {
85
    struct textmode_context *ctx = (void *)_ctx;
86
87
    for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
88
        ctx->video_mem[i] = ctx->front_buffer[i];
89
        ctx->back_buffer[i] = ctx->front_buffer[i];
90
    }
91
92
    if (_ctx->cursor_enabled) {
93
        draw_cursor(ctx);
94
        ctx->old_cursor_offset = ctx->cursor_offset;
95
    }
96
}
97
98
static void text_double_buffer_flush(struct flanterm_context *_ctx) {
99
    struct textmode_context *ctx = (void *)_ctx;
100
101
    if (_ctx->cursor_enabled) {
102
        draw_cursor(ctx);
103
    }
104
105
    if (ctx->cursor_offset != ctx->old_cursor_offset || _ctx->cursor_enabled == false) {
106
        ctx->video_mem[ctx->old_cursor_offset + 1] = ctx->back_buffer[ctx->old_cursor_offset + 1];
107
    }
108
109
    for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
110
        if (ctx->back_buffer[i] == ctx->front_buffer[i]) {
111
            continue;
112
        }
113
114
        ctx->front_buffer[i] = ctx->back_buffer[i];
115
116
        if (_ctx->cursor_enabled && i == ctx->cursor_offset + 1) {
117
            continue;
118
        }
119
120
        ctx->video_mem[i] = ctx->back_buffer[i];
121
    }
122
123
    if (_ctx->cursor_enabled) {
124
        ctx->old_cursor_offset = ctx->cursor_offset;
125
    }
126
}
127
128
static void text_get_cursor_pos(struct flanterm_context *_ctx, size_t *x, size_t *y) {
129
    struct textmode_context *ctx = (void *)_ctx;
130
131
    *x = (ctx->cursor_offset % VD_COLS) / 2;
132
    *y = ctx->cursor_offset / VD_COLS;
133
}
134
135
static void text_move_character(struct flanterm_context *_ctx, size_t new_x, size_t new_y, size_t old_x, size_t old_y) {
136
    struct textmode_context *ctx = (void *)_ctx;
137
138
    if (old_x >= VD_COLS / 2 || old_y >= VD_ROWS
139
     || new_x >= VD_COLS / 2 || new_y >= VD_ROWS) {
140
        return;
141
    }
142
143
    ctx->back_buffer[new_y * VD_COLS + new_x * 2] = ctx->back_buffer[old_y * VD_COLS + old_x * 2];
144
    ctx->back_buffer[new_y * VD_COLS + new_x * 2 + 1] = ctx->back_buffer[old_y * VD_COLS + old_x * 2 + 1];
145
}
146
147
static void text_set_cursor_pos(struct flanterm_context *_ctx, size_t x, size_t y) {
148
    struct textmode_context *ctx = (void *)_ctx;
149
150
    if (x >= VD_COLS / 2) {
151
        if ((int)x < 0) {
152
            x = 0;
153
        } else {
154
            x = VD_COLS / 2 - 1;
155
        }
156
    }
157
    if (y >= VD_ROWS) {
158
        if ((int)y < 0) {
159
            y = 0;
160
        } else {
161
            y = VD_ROWS - 1;
162
        }
163
    }
164
    ctx->cursor_offset = y * VD_COLS + x * 2;
165
    ctx->cursor_overflow = false;
166
}
167
168
static uint8_t ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
169
170
static void text_set_text_fg(struct flanterm_context *_ctx, size_t fg) {
171
    struct textmode_context *ctx = (void *)_ctx;
172
    ctx->text_palette = (ctx->text_palette & 0xf0) | ansi_colours[fg];
173
}
174
175
static void text_set_text_bg(struct flanterm_context *_ctx, size_t bg) {
176
    struct textmode_context *ctx = (void *)_ctx;
177
    ctx->text_palette = (ctx->text_palette & 0x0f) | (ansi_colours[bg] << 4);
178
}
179
180
static void text_set_text_fg_bright(struct flanterm_context *_ctx, size_t fg) {
181
    struct textmode_context *ctx = (void *)_ctx;
182
    ctx->text_palette = (ctx->text_palette & 0xf0) | (ansi_colours[fg] | (1 << 3));
183
}
184
185
static void text_set_text_bg_bright(struct flanterm_context *_ctx, size_t bg) {
186
    struct textmode_context *ctx = (void *)_ctx;
187
    ctx->text_palette = (ctx->text_palette & 0x0f) | ((ansi_colours[bg] | (1 << 3)) << 4);
188
}
189
190
static void text_set_text_fg_rgb(struct flanterm_context *ctx, uint32_t n) {
191
    (void)ctx;
192
    (void)n;
193
}
194
195
static void text_set_text_bg_rgb(struct flanterm_context *ctx, uint32_t n) {
196
    (void)ctx;
197
    (void)n;
198
}
199
200
static void text_set_text_fg_default(struct flanterm_context *_ctx) {
201
    struct textmode_context *ctx = (void *)_ctx;
202
    ctx->text_palette = (ctx->text_palette & 0xf0) | 7;
203
}
204
205
static void text_set_text_bg_default(struct flanterm_context *_ctx) {
206
    struct textmode_context *ctx = (void *)_ctx;
207
    ctx->text_palette &= 0x0f;
208
}
209
210
static void text_set_text_fg_default_bright(struct flanterm_context *_ctx) {
211
    struct textmode_context *ctx = (void *)_ctx;
212
    ctx->text_palette = (ctx->text_palette & 0xf0) | (7 | (1 << 3));
213
}
214
215
static void text_set_text_bg_default_bright(struct flanterm_context *_ctx) {
216
    struct textmode_context *ctx = (void *)_ctx;
217
    ctx->text_palette = (ctx->text_palette & 0x0f) | ((1 << 3) << 4);
218
}
219
220
static void text_putchar(struct flanterm_context *_ctx, uint8_t c) {
221
    struct textmode_context *ctx = (void *)_ctx;
222
223
    // Handle overflow from previous putchar
224
    if (ctx->cursor_overflow) {
225
        ctx->cursor_overflow = false;
226
        if (_ctx->wrap_enabled
227
         && (ctx->cursor_offset / VD_COLS < _ctx->scroll_bottom_margin - 1
228
             || _ctx->scroll_enabled)) {
229
            ctx->cursor_offset -= ctx->cursor_offset % VD_COLS;
230
            ctx->cursor_offset += VD_COLS;
231
            if (ctx->cursor_offset / VD_COLS == _ctx->scroll_bottom_margin) {
232
                ctx->cursor_offset -= VD_COLS;
233
                text_scroll(_ctx);
234
            }
235
            if (ctx->cursor_offset >= VD_ROWS * VD_COLS) {
236
                ctx->cursor_offset = (VD_ROWS - 1) * VD_COLS;
237
            }
238
        } else {
239
            ctx->cursor_offset = ctx->cursor_offset - (ctx->cursor_offset % VD_COLS) + VD_COLS - 2;
240
        }
241
    }
242
243
    ctx->back_buffer[ctx->cursor_offset] = c;
244
    ctx->back_buffer[ctx->cursor_offset + 1] = ctx->text_palette;
245
    if (ctx->cursor_offset % VD_COLS == VD_COLS - 2) {
246
        // At last column - flag overflow for next putchar
247
        ctx->cursor_overflow = true;
248
    } else if (ctx->cursor_offset < (VIDEO_BOTTOM - 1)) {
249
        ctx->cursor_offset += 2;
250
    }
251
}
252
253
static void text_deinit(struct flanterm_context *_ctx, void (*_free)(void *, size_t)) {
254
    struct textmode_context *ctx = (void *)_ctx;
255
256
    if (ctx->back_buffer != NULL) {
257
        _free(ctx->back_buffer, VD_ROWS * VD_COLS);
258
        ctx->back_buffer = NULL;
259
    }
260
261
    if (ctx->front_buffer != NULL) {
262
        _free(ctx->front_buffer, VD_ROWS * VD_COLS);
263
        ctx->front_buffer = NULL;
264
    }
265
266
    pmm_free(ctx, sizeof(struct textmode_context));
267
}
268
269
void vga_textmode_init(bool managed) {
270
    term_notready();
271
272
    if (quiet) {
273
        return;
274
    }
275
276
    if (current_video_mode != 0x3) {
277
        struct rm_regs r = {0};
278
        r.eax = 0x0003;
279
        rm_int(0x10, &r, &r);
280
281
        current_video_mode = 0x3;
282
    }
283
284
    terms = ext_mem_alloc(sizeof(void *));
285
    terms_i = 1;
286
287
    terms[0] = ext_mem_alloc(sizeof(struct textmode_context));
288
289
    struct flanterm_context *term = terms[0];
290
    struct textmode_context *ctx = (void *)term;
291
292
    if (ctx->back_buffer == NULL) {
293
        ctx->back_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
294
    } else {
295
        memset(ctx->back_buffer, 0, VD_ROWS * VD_COLS);
296
    }
297
    if (ctx->front_buffer == NULL) {
298
        ctx->front_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
299
    } else {
300
        memset(ctx->front_buffer, 0, VD_ROWS * VD_COLS);
301
    }
302
303
    ctx->cursor_offset = 0;
304
    ctx->cursor_overflow = false;
305
    ctx->text_palette = 0x07;
306
307
    ctx->video_mem = (volatile uint8_t *)0xb8000;
308
309
    text_clear(term, false);
310
311
    // VGA cursor code taken from: https://wiki.osdev.org/Text_Mode_Cursor
312
313
    if (!managed) {
314
        term->cursor_enabled = false;
315
316
        outb(0x3d4, 0x0a);
317
        outb(0x3d5, (inb(0x3d5) & 0xc0) | 14);
318
        outb(0x3d4, 0x0b);
319
        outb(0x3d5, (inb(0x3d5) & 0xe0) | 15);
320
        outb(0x3d4, 0x0f);
321
        outb(0x3d5, 0);
322
        outb(0x3d4, 0x0e);
323
        outb(0x3d5, 0);
324
325
        struct rm_regs r = {0};
326
        r.eax = 0x0200;
327
        rm_int(0x10, &r, &r);
328
    } else {
329
        outb(0x3d4, 0x0a);
330
        outb(0x3d5, 0x20);
331
    }
332
333
    text_double_buffer_flush(term);
334
335
    if (managed && serial) {
336
        term->cols = 80;
337
        term->rows = 24;
338
    } else {
339
        term->cols = 80;
340
        term->rows = 25;
341
    }
342
343
    term->raw_putchar = text_putchar;
344
    term->clear = text_clear;
345
    term->set_cursor_pos = text_set_cursor_pos;
346
    term->get_cursor_pos = text_get_cursor_pos;
347
    term->set_text_fg = text_set_text_fg;
348
    term->set_text_bg = text_set_text_bg;
349
    term->set_text_fg_bright = text_set_text_fg_bright;
350
    term->set_text_bg_bright = text_set_text_bg_bright;
351
    term->set_text_fg_rgb = text_set_text_fg_rgb;
352
    term->set_text_bg_rgb = text_set_text_bg_rgb;
353
    term->set_text_fg_default = text_set_text_fg_default;
354
    term->set_text_bg_default = text_set_text_bg_default;
355
    term->set_text_fg_default_bright = text_set_text_fg_default_bright;
356
    term->set_text_bg_default_bright = text_set_text_bg_default_bright;
357
    term->move_character = text_move_character;
358
    term->scroll = text_scroll;
359
    term->revscroll = text_revscroll;
360
    term->swap_palette = text_swap_palette;
361
    term->save_state = text_save_state;
362
    term->restore_state = text_restore_state;
363
    term->double_buffer_flush = text_double_buffer_flush;
364
    term->full_refresh = text_full_refresh;
365
    term->deinit = text_deinit;
366
367
    flanterm_context_reinit(term);
368
369
    if (!managed) {
370
        term->cursor_enabled = false;
371
    }
372
373
    term->full_refresh(term);
374
375
    if (!managed) {
376
        term->deinit(term, pmm_free_size_t);
377
        pmm_free(terms, sizeof(void *));
378
        terms_i = 0;
379
        terms = NULL;
380
        term_backend = _NOT_READY;
381
    } else {
382
        term_backend = TEXTMODE;
383
    }
384
}
385
386
#endif
tab: 248 wrap: offon