606072c72d138c2599d73a6dadbe684632e082ad
[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 "contty.h"
21 #include "serial.h"
22 #include "asmops.h"
23 #include "config.h"
24
25 #define VIRT_ROWS       200
26
27 #define NCOLS           80
28 #define NROWS           25
29 #define TEXT_ADDR       ((char*)0xb8000)
30
31 #define CRTC_ADDR       0x3d4
32 #define CRTC_DATA       0x3d5
33
34 #define CRTC_REG_CURSTART       0x0a
35 #define CRTC_REG_CUREND         0x0b
36 #define CRTC_REG_START_H        0x0c
37 #define CRTC_REG_START_L        0x0d
38 #define CRTC_REG_CURLOC_H       0x0e
39 #define CRTC_REG_CURLOC_L       0x0f
40
41 #define VMEM_CHAR(c, attr) \
42         ((uint16_t)(c) | ((uint16_t)(attr) << 8))
43
44 static void scroll(void);
45 static void crtc_cursor(int x, int y);
46 static void crtc_setstart(int y);
47 static inline unsigned char crtc_read(int reg);
48 static inline void crtc_write(int reg, unsigned char val);
49 static inline void crtc_write_bits(int reg, unsigned char val, unsigned char mask);
50
51 extern int cursor_x, cursor_y;
52 static unsigned char txattr = 0x07;
53 static int start_line;
54
55 int con_init(void)
56 {
57 #ifdef CON_SERIAL
58         ser_open(0, 9600, SER_8N1);
59 #endif
60
61 #ifdef CON_TEXTMODE
62         con_show_cursor(1);
63         crtc_setstart(0);
64         crtc_cursor(cursor_x, cursor_y);
65         /*
66         printf("curloc: %x %x\n", (unsigned int)crtc_read(CRTC_REG_CURLOC_H),
67                         (unsigned int)crtc_read(CRTC_REG_CURLOC_L));
68         printf("curstart: %x\n", (unsigned int)crtc_read(CRTC_REG_CURSTART));
69         printf("curend: %x\n", (unsigned int)crtc_read(CRTC_REG_CUREND));
70         */
71 #endif
72
73         return 0;
74 }
75
76 void con_show_cursor(int show)
77 {
78 #ifdef CON_TEXTMODE
79         unsigned char val = show ? 0 : 0x20;
80
81         crtc_write_bits(CRTC_REG_CURSTART, val, 0x20);
82 #endif
83 }
84
85 void con_cursor(int x, int y)
86 {
87 #ifdef CON_TEXTMODE
88         cursor_x = x;
89         cursor_y = y;
90         crtc_cursor(x, y);
91 #endif
92 }
93
94 void con_fgcolor(int c)
95 {
96         txattr = (txattr & 0xf0) | c;
97 }
98
99 void con_bgcolor(int c)
100 {
101         txattr = (txattr & 0x0f) | (c << 4);
102 }
103
104 void con_clear(void)
105 {
106 #ifdef CON_TEXTMODE
107         memset(TEXT_ADDR, 0, NCOLS * NROWS * 2);
108
109         start_line = 0;
110         crtc_setstart(0);
111
112         cursor_x = cursor_y = 0;
113         crtc_cursor(0, 0);
114 #endif
115 }
116
117 static inline void linefeed(void)
118 {
119         if(++cursor_y >= NROWS) {
120                 scroll();
121                 --cursor_y;
122         }
123 }
124
125 void con_putchar(int c)
126 {
127 #ifdef CON_TEXTMODE
128         uint16_t *ptr;
129
130         switch(c) {
131         case '\n':
132                 linefeed();
133         case '\r':
134                 cursor_x = 0;
135                 crtc_cursor(cursor_x, cursor_y);
136                 break;
137
138         case '\t':
139                 cursor_x = (cursor_x & 0x7) + 8;
140                 if(cursor_x >= NCOLS) {
141                         linefeed();
142                         cursor_x = 0;
143                 }
144                 crtc_cursor(cursor_x, cursor_y);
145                 break;
146
147         default:
148                 ptr = (uint16_t*)TEXT_ADDR;
149                 ptr[(cursor_y + start_line) * NCOLS + cursor_x] = VMEM_CHAR(c, txattr);
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 static void scroll(void)
165 {
166         int new_line;
167
168         if(++start_line > VIRT_ROWS - NROWS) {
169                 /* The bottom of the visible range reached the end of our text buffer.
170                  * Copy the rest of the lines to the top and reset start_line.
171                  */
172                 memcpy(TEXT_ADDR, TEXT_ADDR + start_line * NCOLS, (NROWS - 1) * NCOLS * 2);
173                 start_line = 0;
174         }
175
176         /* clear the next line that will be revealed by scrolling */
177         new_line = start_line + NROWS - 1;
178         memset16(TEXT_ADDR + new_line * NCOLS * 2, VMEM_CHAR(' ', txattr), NCOLS);
179         crtc_setstart(start_line);
180 }
181
182 static void crtc_cursor(int x, int y)
183 {
184         unsigned int addr;
185
186         addr = (y + start_line) * NCOLS + x;
187
188         crtc_write(CRTC_REG_CURLOC_L, addr);
189         crtc_write(CRTC_REG_CURLOC_H, addr >> 8);
190 }
191
192 static void crtc_setstart(int y)
193 {
194         unsigned int addr = y * NCOLS;
195
196         crtc_write(CRTC_REG_START_L, addr);
197         crtc_write(CRTC_REG_START_H, addr >> 8);
198 }
199
200 static inline unsigned char crtc_read(int reg)
201 {
202         outb(reg, CRTC_ADDR);
203         return inb(CRTC_DATA);
204 }
205
206 static inline void crtc_write(int reg, unsigned char val)
207 {
208         outb(reg, CRTC_ADDR);
209         outb(val, CRTC_DATA);
210 }
211
212 static inline void crtc_write_bits(int reg, unsigned char val, unsigned char mask)
213 {
214         unsigned char prev;
215         outb(reg, CRTC_ADDR);
216         prev = inb(CRTC_DATA);
217         val = (prev & ~mask) | (val & mask);
218         outb(val, CRTC_DATA);
219 }