census logo
[bootcensus] / src / contty.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdarg.h>
21 #include "contty.h"
22 #include "serial.h"
23 #include "asmops.h"
24 #include "config.h"
25
26 #define VIRT_ROWS       200
27
28 #define NCOLS           80
29 #define NROWS           25
30 #define TEXT_ADDR       ((char*)0xb8000)
31
32 #define CRTC_ADDR       0x3d4
33 #define CRTC_DATA       0x3d5
34
35 #define CRTC_REG_CURSTART       0x0a
36 #define CRTC_REG_CUREND         0x0b
37 #define CRTC_REG_START_H        0x0c
38 #define CRTC_REG_START_L        0x0d
39 #define CRTC_REG_CURLOC_H       0x0e
40 #define CRTC_REG_CURLOC_L       0x0f
41
42 #define VMEM_CHAR(c, attr) \
43         (((uint16_t)(c) & 0xff) | ((uint16_t)(attr) << 8))
44
45 static void scroll(void);
46 static void crtc_cursor(int x, int y);
47 static void crtc_setstart(int y);
48 static inline unsigned char crtc_read(int reg);
49 static inline void crtc_write(int reg, unsigned char val);
50
51 extern int cursor_x, cursor_y;
52 static unsigned char txattr = 0x07;
53 static int start_line;
54 static unsigned char cy0, cy1;
55 static int curvis;
56 static int scr_on = 1;
57
58 int con_init(void)
59 {
60 #ifdef CON_SERIAL
61         ser_open(0, 9600, SER_8N1);
62 #endif
63
64 #ifdef CON_TEXTMODE
65         cy0 = crtc_read(CRTC_REG_CURSTART);
66         curvis = cy0 & 0x20 ? 1 : 0;
67         cy0 &= 0x1f;
68         cy1 = crtc_read(CRTC_REG_CUREND) & 0x1f;
69
70         con_show_cursor(1);
71         crtc_setstart(0);
72         crtc_cursor(cursor_x, cursor_y);
73         scr_on = 1;
74 #endif
75
76         return 0;
77 }
78
79 void con_scr_enable(void)
80 {
81         scr_on = 1;
82 }
83
84 void con_scr_disable(void)
85 {
86         scr_on = 0;
87 }
88
89 void con_show_cursor(int show)
90 {
91 #ifdef CON_TEXTMODE
92         unsigned char val = cy0 & 0x1f;
93         if(!show) {
94                 val |= 0x20;
95         }
96         crtc_write(CRTC_REG_CURSTART, val);
97         curvis = show;
98 #endif
99 }
100
101 void con_cursor(int x, int y)
102 {
103 #ifdef CON_TEXTMODE
104         cursor_x = x;
105         cursor_y = y;
106         crtc_cursor(x, y);
107 #endif
108 }
109
110 void con_curattr(int shape, int blink)
111 {
112 #ifdef CON_TEXTMODE
113         unsigned char start;
114         cy0 = (shape == CON_CURSOR_LINE) ? 0xd : 0;
115         cy1 = 0xe;
116
117         start = cy0;
118         if(curvis) {
119                 start |= 0x20;
120         }
121
122         crtc_write(CRTC_REG_CURSTART, start);
123         crtc_write(CRTC_REG_CUREND, cy0);
124 #endif
125 }
126
127 void con_fgcolor(int c)
128 {
129         txattr = (txattr & 0xf0) | c;
130 }
131
132 void con_bgcolor(int c)
133 {
134         txattr = (txattr & 0x0f) | (c << 4);
135 }
136
137 void con_setattr(unsigned char attr)
138 {
139         txattr = attr;
140 }
141
142 unsigned char con_getattr(void)
143 {
144         return txattr;
145 }
146
147 void con_clear(void)
148 {
149 #ifdef CON_TEXTMODE
150         memset16(TEXT_ADDR, VMEM_CHAR(' ', txattr), NCOLS * NROWS);
151
152         start_line = 0;
153         crtc_setstart(0);
154
155         cursor_x = cursor_y = 0;
156         crtc_cursor(0, 0);
157 #endif
158 }
159
160 static inline void linefeed(void)
161 {
162         if(++cursor_y >= NROWS) {
163                 scroll();
164                 --cursor_y;
165         }
166 }
167
168 void con_putchar(int c)
169 {
170 #ifdef CON_TEXTMODE
171         if(scr_on) {
172                 switch(c) {
173                 case '\n':
174                         linefeed();
175                 case '\r':
176                         cursor_x = 0;
177                         crtc_cursor(cursor_x, cursor_y);
178                         break;
179
180                 case '\t':
181                         cursor_x = (cursor_x & 0x7) + 8;
182                         if(cursor_x >= NCOLS) {
183                                 linefeed();
184                                 cursor_x = 0;
185                         }
186                         crtc_cursor(cursor_x, cursor_y);
187                         break;
188
189                 case '\b':
190                         if(cursor_x > 0) cursor_x--;
191                         con_putchar_scr(cursor_x, cursor_y, ' ');
192                         crtc_cursor(cursor_x, cursor_y);
193                         break;
194
195                 default:
196                         con_putchar_scr(cursor_x, cursor_y, c);
197
198                         if(++cursor_x >= NCOLS) {
199                                 linefeed();
200                                 cursor_x = 0;
201                         }
202                         crtc_cursor(cursor_x, cursor_y);
203                 }
204         }
205 #endif
206
207 #ifdef CON_SERIAL
208         ser_putchar(c);
209 #endif
210 }
211
212 void con_putchar_scr(int x, int y, int c)
213 {
214 #ifdef CON_TEXTMODE
215         uint16_t *ptr = (uint16_t*)TEXT_ADDR;
216         ptr[(y + start_line) * NCOLS + x] = VMEM_CHAR(c, txattr);
217 #endif
218 }
219
220 int con_printf(int x, int y, const char *fmt, ...)
221 {
222 #ifdef CON_TEXTMODE
223         va_list ap;
224         char buf[81];
225         char *ptr = buf;
226
227         va_start(ap, fmt);
228         vsnprintf(buf, 80, fmt, ap);
229         va_end(ap);
230
231         while(*ptr && x < 80) {
232                 con_putchar_scr(x++, y, *ptr++);
233         }
234         return ptr - buf;
235 #else
236         return 0;
237 #endif
238 }
239
240 static void scroll(void)
241 {
242         int new_line;
243
244         if(++start_line > VIRT_ROWS - NROWS) {
245                 /* The bottom of the visible range reached the end of our text buffer.
246                  * Copy the rest of the lines to the top and reset start_line.
247                  */
248                 memcpy(TEXT_ADDR, TEXT_ADDR + start_line * NCOLS, (NROWS - 1) * NCOLS * 2);
249                 start_line = 0;
250         }
251
252         /* clear the next line that will be revealed by scrolling */
253         new_line = start_line + NROWS - 1;
254         memset16(TEXT_ADDR + new_line * NCOLS * 2, VMEM_CHAR(' ', txattr), NCOLS);
255         crtc_setstart(start_line);
256 }
257
258 static void crtc_cursor(int x, int y)
259 {
260         unsigned int addr;
261
262         addr = (y + start_line) * NCOLS + x;
263
264         crtc_write(CRTC_REG_CURLOC_L, addr);
265         crtc_write(CRTC_REG_CURLOC_H, addr >> 8);
266 }
267
268 static void crtc_setstart(int y)
269 {
270         unsigned int addr = y * NCOLS;
271
272         crtc_write(CRTC_REG_START_L, addr);
273         crtc_write(CRTC_REG_START_H, addr >> 8);
274 }
275
276 static inline unsigned char crtc_read(int reg)
277 {
278         outb(reg, CRTC_ADDR);
279         return inb(CRTC_DATA);
280 }
281
282 static inline void crtc_write(int reg, unsigned char val)
283 {
284         outb(reg, CRTC_ADDR);
285         outb(val, CRTC_DATA);
286 }