VirtualConsole.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. #include "VirtualConsole.h"
  2. #include "kmalloc.h"
  3. #include "i386.h"
  4. #include "IO.h"
  5. #include "StdLib.h"
  6. #include "Keyboard.h"
  7. #include <AK/AKString.h>
  8. static byte* s_vga_buffer;
  9. static VirtualConsole* s_consoles[6];
  10. static int s_active_console;
  11. void VirtualConsole::get_vga_cursor(byte& row, byte& column)
  12. {
  13. word value;
  14. IO::out8(0x3d4, 0x0e);
  15. value = IO::in8(0x3d5) << 8;
  16. IO::out8(0x3d4, 0x0f);
  17. value |= IO::in8(0x3d5);
  18. row = value / columns();
  19. column = value % columns();
  20. }
  21. void VirtualConsole::flush_vga_cursor()
  22. {
  23. word value = m_current_vga_start_address + (m_cursor_row * columns() + m_cursor_column);
  24. IO::out8(0x3d4, 0x0e);
  25. IO::out8(0x3d5, MSB(value));
  26. IO::out8(0x3d4, 0x0f);
  27. IO::out8(0x3d5, LSB(value));
  28. }
  29. void VirtualConsole::initialize()
  30. {
  31. s_vga_buffer = (byte*)0xb8000;
  32. memset(s_consoles, 0, sizeof(s_consoles));
  33. s_active_console = -1;
  34. }
  35. VirtualConsole::VirtualConsole(unsigned index, InitialContents initial_contents)
  36. : TTY(4, index)
  37. , m_index(index)
  38. {
  39. set_size(80, 25);
  40. m_horizontal_tabs = static_cast<byte*>(kmalloc(columns()));
  41. for (unsigned i = 0; i < columns(); ++i)
  42. m_horizontal_tabs[i] = (i % 8) == 0;
  43. // Rightmost column is always last tab on line.
  44. m_horizontal_tabs[columns() - 1] = 1;
  45. s_consoles[index] = this;
  46. m_buffer = (byte*)kmalloc_eternal(rows() * columns() * 2);
  47. if (initial_contents == AdoptCurrentVGABuffer) {
  48. memcpy(m_buffer, s_vga_buffer, rows() * columns() * 2);
  49. get_vga_cursor(m_cursor_row, m_cursor_column);
  50. } else {
  51. word* line_mem = reinterpret_cast<word*>(m_buffer);
  52. for (word i = 0; i < rows() * columns(); ++i)
  53. line_mem[i] = 0x0720;
  54. }
  55. }
  56. VirtualConsole::~VirtualConsole()
  57. {
  58. kfree(m_horizontal_tabs);
  59. m_horizontal_tabs = nullptr;
  60. }
  61. void VirtualConsole::clear()
  62. {
  63. word* linemem = m_active ? (word*)s_vga_buffer : (word*)m_buffer;
  64. for (word i = 0; i < rows() * columns(); ++i)
  65. linemem[i] = 0x0720;
  66. if (m_active)
  67. set_vga_start_row(0);
  68. set_cursor(0, 0);
  69. }
  70. void VirtualConsole::switch_to(unsigned index)
  71. {
  72. if ((int)index == s_active_console)
  73. return;
  74. dbgprintf("VC: Switch to %u (%p)\n", index, s_consoles[index]);
  75. ASSERT(index < 6);
  76. ASSERT(s_consoles[index]);
  77. InterruptDisabler disabler;
  78. if (s_active_console != -1)
  79. s_consoles[s_active_console]->set_active(false);
  80. s_active_console = index;
  81. s_consoles[s_active_console]->set_active(true);
  82. Console::the().setImplementation(s_consoles[s_active_console]);
  83. }
  84. void VirtualConsole::set_active(bool b)
  85. {
  86. if (b == m_active)
  87. return;
  88. InterruptDisabler disabler;
  89. m_active = b;
  90. if (!m_active) {
  91. memcpy(m_buffer, m_current_vga_window, rows() * columns() * 2);
  92. return;
  93. }
  94. memcpy(s_vga_buffer, m_buffer, rows() * columns() * 2);
  95. set_vga_start_row(0);
  96. flush_vga_cursor();
  97. Keyboard::the().set_client(this);
  98. }
  99. inline bool is_valid_parameter_character(byte ch)
  100. {
  101. return ch >= 0x30 && ch <= 0x3f;
  102. }
  103. inline bool is_valid_intermediate_character(byte ch)
  104. {
  105. return ch >= 0x20 && ch <= 0x2f;
  106. }
  107. inline bool is_valid_final_character(byte ch)
  108. {
  109. return ch >= 0x40 && ch <= 0x7e;
  110. }
  111. unsigned parseUInt(const String& str, bool& ok)
  112. {
  113. unsigned value = 0;
  114. for (size_t i = 0; i < str.length(); ++i) {
  115. if (str[i] < '0' || str[i] > '9') {
  116. ok = false;
  117. return 0;
  118. }
  119. value = value * 10;
  120. value += str[i] - '0';
  121. }
  122. ok = true;
  123. return value;
  124. }
  125. enum class VGAColor : byte {
  126. Black = 0,
  127. Blue,
  128. Green,
  129. Cyan,
  130. Red,
  131. Magenta,
  132. Brown,
  133. LightGray,
  134. DarkGray,
  135. BrightBlue,
  136. BrightGreen,
  137. BrightCyan,
  138. BrightRed,
  139. BrightMagenta,
  140. Yellow,
  141. White,
  142. };
  143. enum class ANSIColor : byte {
  144. Black = 0,
  145. Red,
  146. Green,
  147. Brown,
  148. Blue,
  149. Magenta,
  150. Cyan,
  151. LightGray,
  152. DarkGray,
  153. BrightRed,
  154. BrightGreen,
  155. Yellow,
  156. BrightBlue,
  157. BrightMagenta,
  158. BrightCyan,
  159. White,
  160. };
  161. static inline VGAColor ansi_color_to_vga(ANSIColor color)
  162. {
  163. switch (color) {
  164. case ANSIColor::Black: return VGAColor::Black;
  165. case ANSIColor::Red: return VGAColor::Red;
  166. case ANSIColor::Brown: return VGAColor::Brown;
  167. case ANSIColor::Blue: return VGAColor::Blue;
  168. case ANSIColor::Magenta: return VGAColor::Magenta;
  169. case ANSIColor::Green: return VGAColor::Green;
  170. case ANSIColor::Cyan: return VGAColor::Cyan;
  171. case ANSIColor::LightGray: return VGAColor::LightGray;
  172. case ANSIColor::DarkGray: return VGAColor::DarkGray;
  173. case ANSIColor::BrightRed: return VGAColor::BrightRed;
  174. case ANSIColor::BrightGreen: return VGAColor::BrightGreen;
  175. case ANSIColor::Yellow: return VGAColor::Yellow;
  176. case ANSIColor::BrightBlue: return VGAColor::BrightBlue;
  177. case ANSIColor::BrightMagenta: return VGAColor::BrightMagenta;
  178. case ANSIColor::BrightCyan: return VGAColor::BrightCyan;
  179. case ANSIColor::White: return VGAColor::White;
  180. }
  181. ASSERT_NOT_REACHED();
  182. return VGAColor::LightGray;
  183. }
  184. static inline byte ansi_color_to_vga(byte color)
  185. {
  186. return (byte)ansi_color_to_vga((ANSIColor)color);
  187. }
  188. void VirtualConsole::escape$m(const Vector<unsigned>& params)
  189. {
  190. for (auto param : params) {
  191. switch (param) {
  192. case 0:
  193. // Reset
  194. m_current_attribute = 0x07;
  195. break;
  196. case 1:
  197. // Bold
  198. m_current_attribute |= 8;
  199. break;
  200. case 30:
  201. case 31:
  202. case 32:
  203. case 33:
  204. case 34:
  205. case 35:
  206. case 36:
  207. case 37:
  208. // Foreground color
  209. m_current_attribute &= ~0x7;
  210. m_current_attribute |= ansi_color_to_vga(param - 30);
  211. break;
  212. case 40:
  213. case 41:
  214. case 42:
  215. case 43:
  216. case 44:
  217. case 45:
  218. case 46:
  219. case 47:
  220. // Background color
  221. m_current_attribute &= ~0x70;
  222. m_current_attribute |= ansi_color_to_vga(param - 30) << 8;
  223. break;
  224. }
  225. }
  226. }
  227. void VirtualConsole::escape$s(const Vector<unsigned>&)
  228. {
  229. m_saved_cursor_row = m_cursor_row;
  230. m_saved_cursor_column = m_cursor_column;
  231. }
  232. void VirtualConsole::escape$u(const Vector<unsigned>&)
  233. {
  234. set_cursor(m_saved_cursor_row, m_saved_cursor_column);
  235. }
  236. void VirtualConsole::escape$H(const Vector<unsigned>& params)
  237. {
  238. unsigned row = 1;
  239. unsigned col = 1;
  240. if (params.size() >= 1)
  241. row = params[0];
  242. if (params.size() >= 2)
  243. col = params[1];
  244. set_cursor(row - 1, col - 1);
  245. }
  246. void VirtualConsole::escape$A(const Vector<unsigned>& params)
  247. {
  248. int num = 1;
  249. if (params.size() >= 1)
  250. num = params[0];
  251. int new_row = (int)m_cursor_row - num;
  252. if (new_row < 0)
  253. new_row = 0;
  254. set_cursor(new_row, m_cursor_column);
  255. }
  256. void VirtualConsole::escape$D(const Vector<unsigned>& params)
  257. {
  258. int num = 1;
  259. if (params.size() >= 1)
  260. num = params[0];
  261. int new_column = (int)m_cursor_column - num;
  262. if (new_column < 0)
  263. new_column = 0;
  264. set_cursor(m_cursor_row, new_column);
  265. }
  266. void VirtualConsole::escape$J(const Vector<unsigned>& params)
  267. {
  268. int mode = 0;
  269. if (params.size() >= 1)
  270. mode = params[0];
  271. switch (mode) {
  272. case 0:
  273. // FIXME: Clear from cursor to end of screen.
  274. notImplemented();
  275. break;
  276. case 1:
  277. // FIXME: Clear from cursor to beginning of screen.
  278. notImplemented();
  279. break;
  280. case 2:
  281. clear();
  282. break;
  283. case 3:
  284. // FIXME: <esc>[3J should also clear the scrollback buffer.
  285. clear();
  286. break;
  287. }
  288. }
  289. void VirtualConsole::execute_escape_sequence(byte final)
  290. {
  291. auto paramparts = String((const char*)m_parameters.data(), m_parameters.size()).split(';');
  292. Vector<unsigned> params;
  293. for (auto& parampart : paramparts) {
  294. bool ok;
  295. unsigned value = parseUInt(parampart, ok);
  296. if (!ok) {
  297. // FIXME: Should we do something else?
  298. return;
  299. }
  300. params.append(value);
  301. }
  302. switch (final) {
  303. case 'A': escape$A(params); break;
  304. case 'D': escape$D(params); break;
  305. case 'H': escape$H(params); break;
  306. case 'J': escape$J(params); break;
  307. case 'm': escape$m(params); break;
  308. case 's': escape$s(params); break;
  309. case 'u': escape$u(params); break;
  310. default: break;
  311. }
  312. m_parameters.clear();
  313. m_intermediates.clear();
  314. }
  315. void VirtualConsole::clear_vga_row(word row)
  316. {
  317. word* linemem = (word*)&m_current_vga_window[row * 160];
  318. for (word i = 0; i < columns(); ++i)
  319. linemem[i] = 0x0720;
  320. }
  321. void VirtualConsole::scroll_up()
  322. {
  323. if (m_cursor_row == (rows() - 1)) {
  324. if (m_active) {
  325. if (m_vga_start_row >= 160) {
  326. memcpy(s_vga_buffer, m_current_vga_window + 160, (rows() - 1) * columns() * 2);
  327. set_vga_start_row(0);
  328. clear_vga_row(24);
  329. } else {
  330. set_vga_start_row(m_vga_start_row + 1);
  331. clear_vga_row(24);
  332. }
  333. } else {
  334. memcpy(m_buffer, m_buffer + 160, 160 * 24);
  335. word* linemem = (word*)&m_buffer[24 * 160];
  336. for (word i = 0; i < columns(); ++i)
  337. linemem[i] = 0x0720;
  338. }
  339. } else {
  340. ++m_cursor_row;
  341. }
  342. m_cursor_column = 0;
  343. }
  344. void VirtualConsole::set_cursor(unsigned row, unsigned column)
  345. {
  346. ASSERT(row < rows());
  347. ASSERT(column < columns());
  348. m_cursor_row = row;
  349. m_cursor_column = column;
  350. if (m_active)
  351. flush_vga_cursor();
  352. }
  353. void VirtualConsole::put_character_at(unsigned row, unsigned column, byte ch)
  354. {
  355. ASSERT(row < rows());
  356. ASSERT(column < columns());
  357. word cur = (row * 160) + (column * 2);
  358. if (m_active) {
  359. word cur = (row * 160) + (column * 2);
  360. m_current_vga_window[cur] = ch;
  361. m_current_vga_window[cur + 1] = m_current_attribute;
  362. } else {
  363. m_buffer[cur] = ch;
  364. m_buffer[cur + 1] = m_current_attribute;
  365. }
  366. }
  367. void VirtualConsole::on_char(byte ch)
  368. {
  369. switch (m_escape_state) {
  370. case ExpectBracket:
  371. if (ch == '[')
  372. m_escape_state = ExpectParameter;
  373. else
  374. m_escape_state = Normal;
  375. return;
  376. case ExpectParameter:
  377. if (is_valid_parameter_character(ch)) {
  378. m_parameters.append(ch);
  379. return;
  380. }
  381. m_escape_state = ExpectIntermediate;
  382. // fall through
  383. case ExpectIntermediate:
  384. if (is_valid_intermediate_character(ch)) {
  385. m_intermediates.append(ch);
  386. return;
  387. }
  388. m_escape_state = ExpectFinal;
  389. // fall through
  390. case ExpectFinal:
  391. if (is_valid_final_character(ch)) {
  392. m_escape_state = Normal;
  393. execute_escape_sequence(ch);
  394. return;
  395. }
  396. m_escape_state = Normal;
  397. return;
  398. case Normal:
  399. break;
  400. }
  401. switch (ch) {
  402. case '\0':
  403. return;
  404. case '\033':
  405. m_escape_state = ExpectBracket;
  406. return;
  407. case 8: // Backspace
  408. if (m_cursor_column) {
  409. set_cursor(m_cursor_row, m_cursor_column - 1);
  410. put_character_at(m_cursor_row, m_cursor_column, ' ');
  411. return;
  412. }
  413. break;
  414. case '\a':
  415. // FIXME: Bell!
  416. return;
  417. case '\t': {
  418. for (unsigned i = m_cursor_column; i < columns(); ++i) {
  419. if (m_horizontal_tabs[i]) {
  420. set_cursor(m_cursor_row, i);
  421. return;
  422. }
  423. }
  424. return;
  425. }
  426. case '\n':
  427. scroll_up();
  428. set_cursor(m_cursor_row, m_cursor_column);
  429. return;
  430. }
  431. put_character_at(m_cursor_row, m_cursor_column, ch);
  432. ++m_cursor_column;
  433. if (m_cursor_column >= columns())
  434. scroll_up();
  435. set_cursor(m_cursor_row, m_cursor_column);
  436. }
  437. void VirtualConsole::on_key_pressed(Keyboard::Key key)
  438. {
  439. if (key.ctrl()) {
  440. if (key.character >= 'a' && key.character <= 'z') {
  441. emit(key.character - 'a' + 1);
  442. return;
  443. } else if (key.character == '\\') {
  444. emit(0x1c);
  445. return;
  446. }
  447. }
  448. emit(key.character);
  449. }
  450. void VirtualConsole::on_sysconsole_receive(byte ch)
  451. {
  452. InterruptDisabler disabler;
  453. auto old_attribute = m_current_attribute;
  454. m_current_attribute = 0x03;
  455. on_char(ch);
  456. m_current_attribute = old_attribute;
  457. }
  458. void VirtualConsole::on_tty_write(const byte* data, size_t size)
  459. {
  460. InterruptDisabler disabler;
  461. for (size_t i = 0; i < size; ++i)
  462. on_char(data[i]);
  463. }
  464. String VirtualConsole::tty_name() const
  465. {
  466. char buf[16];
  467. ksprintf(buf, "/dev/tty%u", m_index);
  468. return String(buf);
  469. }
  470. void VirtualConsole::set_vga_start_row(word row)
  471. {
  472. m_vga_start_row = row;
  473. m_current_vga_start_address = row * columns();
  474. m_current_vga_window = s_vga_buffer + row * 160;
  475. IO::out8(0x3d4, 0x0c);
  476. IO::out8(0x3d5, MSB(m_current_vga_start_address));
  477. IO::out8(0x3d4, 0x0d);
  478. IO::out8(0x3d5, LSB(m_current_vga_start_address));
  479. }