sortof works
[com32] / src / contty.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018-2023  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_getcursor(int *x, int *y);
48 static void crtc_setstart(int y);
49 static inline unsigned char crtc_read(int reg);
50 static inline void crtc_write(int reg, unsigned char val);
51
52 static int cursor_x, cursor_y;
53 static unsigned char txattr = 0x07;
54 static int start_line;
55 static unsigned char cy0, cy1;
56 static int curvis;
57 static int scr_on = 1;
58
59 int con_init(void)
60 {
61 #ifdef CON_SERIAL
62         ser_open(1, 9600, SER_8N1);
63 #endif
64
65 #ifdef CON_TEXTMODE
66         cy0 = crtc_read(CRTC_REG_CURSTART);
67         curvis = cy0 & 0x20 ? 1 : 0;
68         cy0 &= 0x1f;
69         cy1 = crtc_read(CRTC_REG_CUREND) & 0x1f;
70
71         crtc_getcursor(&cursor_x, &cursor_y);
72         con_show_cursor(1);
73         crtc_setstart(0);
74         scr_on = 1;
75 #endif
76
77         return 0;
78 }
79
80 void con_scr_enable(void)
81 {
82         scr_on = 1;
83 }
84
85 void con_scr_disable(void)
86 {
87         scr_on = 0;
88 }
89
90 void con_show_cursor(int show)
91 {
92 #ifdef CON_TEXTMODE
93         unsigned char val = cy0 & 0x1f;
94         if(!show) {
95                 val |= 0x20;
96         }
97         crtc_write(CRTC_REG_CURSTART, val);
98         curvis = show;
99 #endif
100 }
101
102 void con_cursor(int x, int y)
103 {
104 #ifdef CON_TEXTMODE
105         cursor_x = x;
106         cursor_y = y;
107         crtc_cursor(x, y);
108 #endif
109 }
110
111 void con_curattr(int shape, int blink)
112 {
113 #ifdef CON_TEXTMODE
114         unsigned char start;
115         cy0 = (shape == CON_CURSOR_LINE) ? 0xd : 0;
116         cy1 = 0xe;
117
118         start = cy0;
119         if(curvis) {
120                 start |= 0x20;
121         }
122
123         crtc_write(CRTC_REG_CURSTART, start);
124         crtc_write(CRTC_REG_CUREND, cy0);
125 #endif
126 }
127
128 void con_fgcolor(int c)
129 {
130         txattr = (txattr & 0xf0) | c;
131 }
132
133 void con_bgcolor(int c)
134 {
135         txattr = (txattr & 0x0f) | (c << 4);
136 }
137
138 void con_setattr(unsigned char attr)
139 {
140         txattr = attr;
141 }
142
143 unsigned char con_getattr(void)
144 {
145         return txattr;
146 }
147
148 void con_clear(void)
149 {
150 #ifdef CON_TEXTMODE
151         memset16(TEXT_ADDR, VMEM_CHAR(' ', txattr), NCOLS * NROWS);
152
153         start_line = 0;
154         crtc_setstart(0);
155
156         cursor_x = cursor_y = 0;
157         crtc_cursor(0, 0);
158 #endif
159 }
160
161 static inline void linefeed(void)
162 {
163         if(++cursor_y >= NROWS) {
164                 scroll();
165                 --cursor_y;
166         }
167 }
168
169 void con_putchar(int c)
170 {
171 #ifdef CON_TEXTMODE
172         if(scr_on) {
173                 switch(c) {
174                 case '\n':
175                         linefeed();
176                 case '\r':
177                         cursor_x = 0;
178                         crtc_cursor(cursor_x, cursor_y);
179                         break;
180
181                 case '\t':
182                         cursor_x = (cursor_x & 0x7) + 8;
183                         if(cursor_x >= NCOLS) {
184                                 linefeed();
185                                 cursor_x = 0;
186                         }
187                         crtc_cursor(cursor_x, cursor_y);
188                         break;
189
190                 case '\b':
191                         if(cursor_x > 0) cursor_x--;
192                         con_putchar_scr(cursor_x, cursor_y, ' ');
193                         crtc_cursor(cursor_x, cursor_y);
194                         break;
195
196                 default:
197                         con_putchar_scr(cursor_x, cursor_y, c);
198
199                         if(++cursor_x >= NCOLS) {
200                                 linefeed();
201                                 cursor_x = 0;
202                         }
203                         crtc_cursor(cursor_x, cursor_y);
204                 }
205         }
206 #endif
207
208 #ifdef CON_SERIAL
209         ser_putchar(c);
210 #endif
211 }
212
213 void con_putchar_scr(int x, int y, int c)
214 {
215 #ifdef CON_TEXTMODE
216         uint16_t *ptr = (uint16_t*)TEXT_ADDR;
217         ptr[(y + start_line) * NCOLS + x] = VMEM_CHAR(c, txattr);
218 #endif
219 }
220
221 int con_printf(int x, int y, const char *fmt, ...)
222 {
223 #ifdef CON_TEXTMODE
224         va_list ap;
225         char buf[81];
226         char *ptr = buf;
227
228         va_start(ap, fmt);
229         vsnprintf(buf, 80, fmt, ap);
230         va_end(ap);
231
232         while(*ptr && x < 80) {
233                 con_putchar_scr(x++, y, *ptr++);
234         }
235         return ptr - buf;
236 #else
237         return 0;
238 #endif
239 }
240
241 static void scroll(void)
242 {
243         int new_line;
244
245         if(++start_line > VIRT_ROWS - NROWS) {
246                 /* The bottom of the visible range reached the end of our text buffer.
247                  * Copy the rest of the lines to the top and reset start_line.
248                  */
249                 memcpy(TEXT_ADDR, TEXT_ADDR + start_line * NCOLS, (NROWS - 1) * NCOLS * 2);
250                 start_line = 0;
251         }
252
253         /* clear the next line that will be revealed by scrolling */
254         new_line = start_line + NROWS - 1;
255         memset16(TEXT_ADDR + new_line * NCOLS * 2, VMEM_CHAR(' ', txattr), NCOLS);
256         crtc_setstart(start_line);
257 }
258
259 static void crtc_cursor(int x, int y)
260 {
261         unsigned int addr;
262
263         addr = (y + start_line) * NCOLS + x;
264
265         crtc_write(CRTC_REG_CURLOC_L, addr);
266         crtc_write(CRTC_REG_CURLOC_H, addr >> 8);
267 }
268
269 static void crtc_getcursor(int *x, int *y)
270 {
271         unsigned int addr;
272
273         addr = crtc_read(CRTC_REG_CURLOC_L);
274         addr |= (unsigned int)crtc_read(CRTC_REG_CURLOC_H) << 8;
275
276         *y = addr / NCOLS - start_line;
277         *x = addr % NCOLS;
278 }
279
280 static void crtc_setstart(int y)
281 {
282         unsigned int addr = y * NCOLS;
283
284         crtc_write(CRTC_REG_START_L, addr);
285         crtc_write(CRTC_REG_START_H, addr >> 8);
286 }
287
288 static inline unsigned char crtc_read(int reg)
289 {
290         outp(CRTC_ADDR, reg);
291         return inp(CRTC_DATA);
292 }
293
294 static inline void crtc_write(int reg, unsigned char val)
295 {
296         outp(CRTC_ADDR, reg);
297         outp(CRTC_DATA, val);
298 }