1// Copyright 2016 The Fuchsia Authors
2// Copyright (c) 2009 Corey Tabaka
3// Copyright (c) 2016 Travis Geiselbrecht
4//
5// Use of this source code is governed by a MIT-style
6// license that can be found in the LICENSE file or at
7// https://opensource.org/licenses/MIT
8
9#if WITH_LEGACY_PC_CONSOLE
10
11#include <arch/x86.h>
12#include <lib/io.h>
13#include <platform/console.h>
14#include <platform/pc.h>
15#include <stdarg.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20/* memory mapped framebuffer */
21#define FB (0xB8000U + KERNEL_ASPACE_BASE)
22
23/* CGA values */
24#define CURSOR_START 0x0A
25#define CURSOR_END 0x0B
26#define VIDEO_ADDRESS_MSB 0x0C
27#define VIDEO_ADDRESS_LSB 0x0D
28#define CURSOR_POS_MSB 0x0E
29#define CURSOR_POS_LSB 0x0F
30
31/* curr settings */
32static unsigned char curr_x;
33static unsigned char curr_y;
34static unsigned char curr_start;
35static unsigned char curr_end;
36static unsigned char curr_attr = 0x7;
37
38/* video page buffer */
39#define VPAGE_SIZE 2048
40#define PAGE_MAX 8
41
42static int active_page = 0;
43static int visual_page = 0;
44
45static int curs_x[PAGE_MAX];
46static int curs_y[PAGE_MAX];
47
48static struct {
49    int x1, y1, x2, y2;
50} view_window = {
51    0, 0, 79, 24};
52
53void platform_init_console(void) {
54    curr_save();
55    window(0, 0, 79, 24);
56    clear();
57    place(0, 0);
58}
59
60void set_visual_page(int page) {
61    unsigned short page_offset = page * VPAGE_SIZE;
62    visual_page = page;
63
64    outp(CGA_INDEX_REG, VIDEO_ADDRESS_LSB);
65    outp(CGA_DATA_REG, page_offset & 0xFF);
66    outp(CGA_INDEX_REG, VIDEO_ADDRESS_MSB);
67    outp(CGA_DATA_REG, (page_offset >> 8) & 0xFF);
68}
69
70void set_active_page(int page) {
71    curs_x[active_page] = curr_x;
72    curs_y[active_page] = curr_y;
73    curr_x = curs_x[page];
74    curr_y = curs_y[page];
75    active_page = page;
76}
77
78int get_visual_page(void) {
79    return visual_page;
80}
81
82int get_active_page(void) {
83    return active_page;
84}
85
86void place(int x, int y) {
87    unsigned short cursor_word = x + y * 80 + active_page * VPAGE_SIZE;
88
89    /*
90     * program CGA using index reg, then data reg
91     */
92    outp(CGA_INDEX_REG, CURSOR_POS_LSB);
93    outp(CGA_DATA_REG, cursor_word & 0xFF);
94    outp(CGA_INDEX_REG, CURSOR_POS_MSB);
95    outp(CGA_DATA_REG, (cursor_word >> 8) & 0xFF);
96
97    curr_x = x;
98    curr_y = y;
99}
100
101void cursor(int start, int end) {
102    outp(CGA_INDEX_REG, CURSOR_START);
103    outp(CGA_DATA_REG, start);
104    outp(CGA_INDEX_REG, CURSOR_END);
105    outp(CGA_DATA_REG, end);
106}
107
108void curr_save(void) {
109#if 0
110    /* grab some info from the bios data area (these should be defined in memmap.h */
111    curr_attr = *((unsigned char *)FB + 159);
112    curr_x = *((unsigned char *)0x00450);
113    curr_y = *((unsigned char *)0x00451);
114    curr_end = *((unsigned char *)0x00460);
115    curr_start = *((unsigned char *)0x00461);
116#endif
117    active_page = visual_page = 0;
118}
119
120void curr_restore(void) {
121#if 0
122    *((unsigned char *)0x00450) = curr_x;
123    *((unsigned char *)0x00451) = curr_y;
124#endif
125
126    place(curr_x, curr_y);
127    cursor(curr_start, curr_end);
128}
129
130void window(int x1, int y1, int x2, int y2) {
131    view_window.x1 = x1;
132    view_window.y1 = y1;
133    view_window.x2 = x2;
134    view_window.y2 = y2;
135
136    //place(x1, y1);
137}
138
139void _clear(char c, char attr, int x1, int y1, int x2, int y2) {
140    register int i, j;
141    unsigned short w = attr;
142
143    w <<= 8;
144    w |= c;
145    for (i = x1; i <= x2; i++) {
146        for (j = y1; j <= y2; j++) {
147            *((unsigned short*)(uintptr_t)(FB + 2 * i + 160 * j + 2 * active_page * VPAGE_SIZE)) = w;
148        }
149    }
150
151    place(x1, y1);
152    curr_y = y1;
153    curr_x = x1;
154}
155
156void clear() {
157    _clear(' ', curr_attr, view_window.x1, view_window.y1, view_window.x2,
158           view_window.y2);
159}
160
161void _scroll(char attr, int x1, int y1, int x2, int y2) {
162    register int x, y;
163    unsigned short xattr = attr << 8, w;
164    unsigned char* v = (unsigned char*)(uintptr_t)(FB + active_page * (2 * VPAGE_SIZE));
165
166    for (y = y1 + 1; y <= y2; y++) {
167        for (x = x1; x <= x2; x++) {
168            w = *((unsigned short*)(v + 2 * (y * 80 + x)));
169            *((unsigned short*)(v + 2 * ((y - 1) * 80 + x))) = w;
170        }
171    }
172
173    for (x = x1; x <= x2; x++) {
174        *((unsigned short*)(v + 2 * ((y - 1) * 80 + x))) = xattr;
175    }
176}
177
178void scroll(void) {
179    _scroll(curr_attr, view_window.x1, view_window.y1, view_window.x2,
180            view_window.y2);
181}
182
183void cputc(char c) {
184    static unsigned short scan_x, x, y;
185    unsigned char* v = (unsigned char*)(uintptr_t)(FB + active_page * (2 * VPAGE_SIZE));
186    x = curr_x;
187    y = curr_y;
188
189    switch (c) {
190    case '\t':
191        x += 8;
192        if (x >= view_window.x2 + 1) {
193            x = view_window.x1;
194            if (y == view_window.y2) {
195                scroll();
196            } else {
197                y++;
198            }
199        } else {
200            scan_x = 0;
201
202            while ((scan_x + 8) < x) {
203                scan_x += 8;
204            }
205
206            x = scan_x;
207        }
208        break;
209
210    case '\r':
211        x = view_window.x1;
212        break;
213
214    case '\n':
215        if (y == view_window.y2) {
216            scroll();
217        } else {
218            y++;
219        }
220        break;
221
222    case '\b':
223        x--;
224        break;
225
226    default:
227        *(v + 2 * (x + y * 80)) = c;
228        x++;
229
230        if (x >= view_window.x2 + 1) {
231            x = view_window.x1;
232            if (y == view_window.y2) {
233                scroll();
234            } else {
235                y++;
236            }
237        }
238    }
239
240    place(x, y);
241}
242
243void cputs(char* s) {
244    char c;
245    while (*s != '\0') {
246        c = *s++;
247        cputc(c);
248    }
249}
250
251void puts_xy(int x, int y, char attr, char* s) {
252    unsigned char* v = (unsigned char*)(uintptr_t)(FB + (80 * y + x) * 2 + active_page * (2 * VPAGE_SIZE));
253    while (*s != 0) {
254        *v = *s;
255        s++;
256        v++;
257        *v = attr;
258        v++;
259    }
260}
261
262void putc_xy(int x, int y, char attr, char c) {
263    unsigned char* v = (unsigned char*)(uintptr_t)(FB + (80 * y + x) * 2 + active_page * (2 * VPAGE_SIZE));
264    *v = c;
265    v++;
266    *v = attr;
267}
268
269int printf_xy(int x, int y, char attr, char* fmt, ...) {
270    char cbuf[200];
271    va_list parms;
272    int result;
273
274    va_start(parms, fmt);
275    result = vsnprintf(cbuf, sizeof(cbuf), fmt, parms);
276    va_end(parms);
277
278    puts_xy(x, y, attr, cbuf);
279
280    return result;
281}
282
283#endif
284