6601ce81a5af9bcd00d3a3a05462b2067144356e
[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) | ((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 static inline void crtc_write_bits(int reg, unsigned char val, unsigned char mask);
51
52 extern int cursor_x, cursor_y;
53 static unsigned char txattr = 0x07;
54 static int start_line;
55
56 int con_init(void)
57 {
58 #ifdef CON_SERIAL
59         ser_open(0, 9600, SER_8N1);
60 #endif
61
62 #ifdef CON_TEXTMODE
63         con_show_cursor(1);
64         crtc_setstart(0);
65         crtc_cursor(cursor_x, cursor_y);
66         /*
67         printf("curloc: %x %x\n", (unsigned int)crtc_read(CRTC_REG_CURLOC_H),
68                         (unsigned int)crtc_read(CRTC_REG_CURLOC_L));
69         printf("curstart: %x\n", (unsigned int)crtc_read(CRTC_REG_CURSTART));
70         printf("curend: %x\n", (unsigned int)crtc_read(CRTC_REG_CUREND));
71         */
72 #endif
73
74         return 0;
75 }
76
77 void con_show_cursor(int show)
78 {
79 #ifdef CON_TEXTMODE
80         unsigned char val = show ? 0 : 0x20;
81
82         crtc_write_bits(CRTC_REG_CURSTART, val, 0x20);
83 #endif
84 }
85
86 void con_cursor(int x, int y)
87 {
88 #ifdef CON_TEXTMODE
89         cursor_x = x;
90         cursor_y = y;
91         crtc_cursor(x, y);
92 #endif
93 }
94
95 void con_fgcolor(int c)
96 {
97         txattr = (txattr & 0xf0) | c;
98 }
99
100 void con_bgcolor(int c)
101 {
102         txattr = (txattr & 0x0f) | (c << 4);
103 }
104
105 void con_clear(void)
106 {
107 #ifdef CON_TEXTMODE
108         memset(TEXT_ADDR, 0, NCOLS * NROWS * 2);
109
110         start_line = 0;
111         crtc_setstart(0);
112
113         cursor_x = cursor_y = 0;
114         crtc_cursor(0, 0);
115 #endif
116 }
117
118 static inline void linefeed(void)
119 {
120         if(++cursor_y >= NROWS) {
121                 scroll();
122                 --cursor_y;
123         }
124 }
125
126 void con_putchar(int c)
127 {
128 #ifdef CON_TEXTMODE
129         uint16_t *ptr;
130
131         switch(c) {
132         case '\n':
133                 linefeed();
134         case '\r':
135                 cursor_x = 0;
136                 crtc_cursor(cursor_x, cursor_y);
137                 break;
138
139         case '\t':
140                 cursor_x = (cursor_x & 0x7) + 8;
141                 if(cursor_x >= NCOLS) {
142                         linefeed();
143                         cursor_x = 0;
144                 }
145                 crtc_cursor(cursor_x, cursor_y);
146                 break;
147
148         default:
149                 con_putchar_scr(cursor_x, cursor_y, c);
150
151                 if(++cursor_x >= NCOLS) {
152                         linefeed();
153                         cursor_x = 0;
154                 }
155                 crtc_cursor(cursor_x, cursor_y);
156         }
157 #endif
158
159 #ifdef CON_SERIAL
160         ser_putchar(c);
161 #endif
162 }
163
164 void con_putchar_scr(int x, int y, int c)
165 {
166         uint16_t *ptr = (uint16_t*)TEXT_ADDR;
167         ptr[(y + start_line) * NCOLS + x] = VMEM_CHAR(c, txattr);
168 }
169
170 void con_printf(int x, int y, const char *fmt, ...)
171 {
172         va_list ap;
173         char buf[81];
174         char *ptr = buf;
175
176         va_start(ap, fmt);
177         vsnprintf(buf, 80, fmt, ap);
178         va_end(ap);
179
180         while(*ptr && x < 80) {
181                 con_putchar_scr(x++, y, *ptr++);
182         }
183 }
184
185 static void scroll(void)
186 {
187         int new_line;
188
189         if(++start_line > VIRT_ROWS - NROWS) {
190                 /* The bottom of the visible range reached the end of our text buffer.
191                  * Copy the rest of the lines to the top and reset start_line.
192                  */
193                 memcpy(TEXT_ADDR, TEXT_ADDR + start_line * NCOLS, (NROWS - 1) * NCOLS * 2);
194                 start_line = 0;
195         }
196
197         /* clear the next line that will be revealed by scrolling */
198         new_line = start_line + NROWS - 1;
199         memset16(TEXT_ADDR + new_line * NCOLS * 2, VMEM_CHAR(' ', txattr), NCOLS);
200         crtc_setstart(start_line);
201 }
202
203 static void crtc_cursor(int x, int y)
204 {
205         unsigned int addr;
206
207         addr = (y + start_line) * NCOLS + x;
208
209         crtc_write(CRTC_REG_CURLOC_L, addr);
210         crtc_write(CRTC_REG_CURLOC_H, addr >> 8);
211 }
212
213 static void crtc_setstart(int y)
214 {
215         unsigned int addr = y * NCOLS;
216
217         crtc_write(CRTC_REG_START_L, addr);
218         crtc_write(CRTC_REG_START_H, addr >> 8);
219 }
220
221 static inline unsigned char crtc_read(int reg)
222 {
223         outb(reg, CRTC_ADDR);
224         return inb(CRTC_DATA);
225 }
226
227 static inline void crtc_write(int reg, unsigned char val)
228 {
229         outb(reg, CRTC_ADDR);
230         outb(val, CRTC_DATA);
231 }
232
233 static inline void crtc_write_bits(int reg, unsigned char val, unsigned char mask)
234 {
235         unsigned char prev;
236         outb(reg, CRTC_ADDR);
237         prev = inb(CRTC_DATA);
238         val = (prev & ~mask) | (val & mask);
239         outb(val, CRTC_DATA);
240 }