From 6d0f103c167c0bb6eda12c9d131b15a64fd32f5d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 7 Apr 2018 19:45:28 +0200 Subject: [PATCH] Handle non-printable characters in vga buffer module (#425) --- .../posts/03-vga-text-buffer/index.md | 27 ++++++++++++------ .../posts/03-vga-text-buffer/vga-hello.png | Bin 7547 -> 7670 bytes src/vga_buffer.rs | 8 +++++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/blog/content/second-edition/posts/03-vga-text-buffer/index.md b/blog/content/second-edition/posts/03-vga-text-buffer/index.md index 3b30fd25..528bb0d5 100644 --- a/blog/content/second-edition/posts/03-vga-text-buffer/index.md +++ b/blog/content/second-edition/posts/03-vga-text-buffer/index.md @@ -203,12 +203,23 @@ To print whole strings, we can convert them to bytes and print them one-by-one: impl Writer { pub fn write_string(&mut self, s: &str) { for byte in s.bytes() { - self.write_byte(byte) + match byte { + // printable ASCII byte or newline + 0x20...0x7e | b'\n' => self.write_byte(byte), + // not part of printable ASCII range + _ => self.write_byte(0xfe), + } + } } } ``` +The VGA text buffer only supports ASCII and the additional bytes of [code page 437]. Rust strings are [UTF-8] by default, so they might contain bytes that are not supported by the VGA text buffer. We use a match to differentiate printable ASCII bytes (a newline or anything in between a space character and a `~` character) and unprintable bytes. For unprintable bytes, we print a `■` character, which has the hex code `0xfe` on the VGA hardware. + +[code page 437]: https://en.wikipedia.org/wiki/Code_page_437 +[UTF-8]: http://www.fileformat.info/info/unicode/utf8.htm + #### Try it out! To write some characters to the screen, you can create a temporary function: @@ -221,7 +232,8 @@ pub fn print_something() { }; writer.write_byte(b'H'); - writer.write_string("ello"); + writer.write_string("ello "); + writer.write_string("Wörld!"); } ``` It first creates a new Writer that points to the VGA buffer at `0xb8000`. The syntax for this might seem a bit strange: First, we cast the integer `0xb8000` as an mutable [raw pointer]. Then we convert it to a mutable reference by dereferencing it (through `*`) and immediately borrowing it again (through `&mut`). This conversion requires an [`unsafe` block], since the compiler can't guarantee that the raw pointer is valid. @@ -229,19 +241,16 @@ It first creates a new Writer that points to the VGA buffer at `0xb8000`. The sy [raw pointer]: https://doc.rust-lang.org/book/second-edition/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer [`unsafe` block]: https://doc.rust-lang.org/book/second-edition/ch19-01-unsafe-rust.html#unsafe-rust -Secondly, it writes the bytes `b'H'` and the string `"ello"'` to it. The `b` prefix creates a [byte literal], which represents an ASCII character. When we call `vga_buffer::print_something` in our `_start` function (in `src/main.rs`), a `Hello` should be printed in the _lower_ left corner of the screen in yellow: +Then it writes the byte `b'H'` to it. The `b` prefix creates a [byte literal], which represents an ASCII character. By writing the strings `"ello "'` and `"Wörld!"`, we test our `write_string` method and the handling of unprintable characters. When we call `vga_buffer::print_something` in our `_start` function (in `src/main.rs`), a `Hello W■■rld!` should be printed in the _lower_ left corner of the screen in yellow: [byte literal]: https://doc.rust-lang.org/reference/tokens.html#byte-literals -![QEMU output with a blue `Hello` in the lower left corner](vga-hello.png) +![QEMU output with a yellow `Hello W■■rld!` in the lower left corner](vga-hello.png) -When printing strings with some special characters like `ä` or `λ`, you'll notice that they cause weird symbols on screen. That's because they are represented by multiple bytes in [UTF-8]. By converting them to bytes, we of course get strange results. But since the VGA buffer doesn't support UTF-8, it's not possible to display these characters anyway. - -[core tracking issue]: https://github.com/rust-lang/rust/issues/27701 -[UTF-8]: http://www.fileformat.info/info/unicode/utf8.htm +Notice that the `ö` is printed as two `■` characters. That's because `ö` is represented by two bytes in [UTF-8], which both don't fall into the printable ASCII range. In fact, this is a fundamental property of UTF-8: the individual bytes of multi-byte values are never valid ASCII. ### Volatile -We just saw that our `Hello` was printed correctly. However, it might not work with future Rust compilers that optimize more aggressively. +We just saw that our message was printed correctly. However, it might not work with future Rust compilers that optimize more aggressively. The problem is that we only write to the `Buffer` and never read from it again. The compiler doesn't know that we really access VGA buffer memory (instead of normal RAM) and knows nothing about the side effect that some characters appear on the screen. So it might decide that these writes are unnecessary and can be omitted. To avoid this erroneous optimization, we need to specify these writes as _[volatile]_. This tells the compiler that the write has side effects and should not be optimized away. diff --git a/blog/content/second-edition/posts/03-vga-text-buffer/vga-hello.png b/blog/content/second-edition/posts/03-vga-text-buffer/vga-hello.png index 56f3e0e1c3e58ac9c2a5c9be2a85adf52f3c84c9..d6c3ca16bd57a8e1b5e5e8797638cc9ad35cc14a 100644 GIT binary patch delta 5981 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 O3=E#GelF{r5}E)AHo=Sl literal 7547 zcmeAS@N?(olHy`uVBq!ia0y~yV7kP>z_^x!je&u|`rBk>1_lO}VkgfK4h{~E8jh3> z1_lO+64!{5;QX|b^2DN4hTO!GRNdm_qSVy9;*9)~6VpC;F)+yTdAc};RLpsMw|dLc zbjSN2@BetP+(;8R0;7BnG$q`)pg^IsNK^br`)#?0OANdEo-uyEe=Oo^nf>p-b`>Yp?K9V~|6ILwYhd61=ZbIY z_or{)_rLkwrq=lZ591%bYK*^iDS|)u|7Dr8&VMS^Ja?V_|K!Q~ZPv#+wx8bB?bo%Ryyf5A>W<}$`rgNzUC^wT*=Bp~<@fuyD(*Jt7aiMs z>YIJx^xyw&wr~Hx(%)u@z3+0H$RFyz8Ov`UyT3*EpUvs-_YY}AOFZtWU)A;9zOdOi z|6{`M`?cTqdiwFbI;#&3mOY=#Y;PzsFdR_6w$QnIwqdO5<&Jw>U*Bo}zJh__fzE~< z6&BlT9~=(-TOiNDz%ZwG)$8@Umsx+mQ!MxHF-XC+(%4n2yl!vH+-;V7d8zkqJJX%s zK}%Pet^2<6Qt9&j@6sRtT=&}Sxb41=*N$6GUbQM{xmT$F@l{-U4puckJbuk)=U`x9 z-L=m9dTHh@v)s!mHuIL`&icCIT~K)Ft6hF~U#A+pPz|-68-M5Nl8(2#@~R@suP=Rl ztLOMxv12py{x=xjDV6kBn`z=ZYuRm{e{t)*_Fq4pVBm3eb#VCCZOyyl|LHO_JeZhg zyhuIw*yF%!Zi{2eZl>;hGHLJ4y}z60l;2w?W3^=lGyAvar{nio&;S4Dx$&Htp7j^z z&kHAkH_x4s79~GURe!cQ}>hnHX%d$5SV&eSI&so1e6I*__ z^j4vt2}t3tY2tAk&Cl<6@+W3ncKWwBGfvkY5Bv1<=;ELC@pdz|*F1Z7<{Y1~1W(ZQ z+ubi;{d&FbW!bwsA1CZ<^u3%?TB>{cwD!tXTE)eiCHdN4ey`@wyfe8Z)@`+RWRzt3 z9iQ_HzOyx-Id?X-E@J+MrjXfBo!yIaie?$EnmK2CwOzB;OSj{j{(n%3%{O~K-+cBz z&HI~AUYaNqlNWsc?U{G7dqZL;FKu?;eC6Aw;_v6q%r)3uQ}o7zZ`~HjImLNpyxn4D z%nTNq`TYIbzL%F}-rDk3rSz`<`taq~R~LVHkaX|!z3*u@ za_MOX5&YL*Zs}e3+DNYd^6RTR-|c$6IsN?NOWT&qr~m)QT>1ZZ{^=Jjg?>E`K32@( zPJi|1)8_SO?#Ui5$(_6L)=a;+m{>JIAT$Xrub+zUDJ+YE}>0QU~T`{xVW^%&(&W}Yk z=ZojhIv&4gg4ODq<;RbIo)>*@PV~P&8PCf2f|j2>^)PYY)BAtD>R!C|Gd}mJ{nKar zU$gF9eQ&hy(eY2S!sDj?i#30)UiVAZa@*qyAOC4#Men%l4EO)KTV3=ccK?*{Klkpg z+;z9|;rf3``<@@te`X&4G5>R8nPPFNk>$2opa1`>KcioNeBGUo@pU%)KWt1{K7)-x zPEqZ3uqE4H$GlxHkA3+SdTepwu9BCx9$#)>y~}H=R`X29S)S9&_dK3E)8z4&-_2J_ zx5{0dd?j@1?7Uru#)l6+vv}F(?`l^#=j4vFx9|V!+Y-Ck(r=Q_vB_`duKfJ!xs77+ zU5mFFUm5%N&zpJH%k)J5zTf(r*PrAptWP`Mr)#@qrR?g}t8Z@4uP=KOapKOM7&ha} z8FA6kr*GYg+PCSygWiqWy0rB6r}>vmBcm+M?|)lgXQscuXvUWI&p-WZm5!%}zW)rjlgpn()K6FydHT@eGGF^Y zOYhH|Zu`}?@@jm^P0Q$|S?-bZYNO&lHraYkn%i%szW3QWljxZHM{ z^p6=+r03Pv?#i1q_36oHGhg){KX&ucs#QiadyFN{Jh_!Ue`5LG*S0UOU*35x_jcNr zZPWIDUVGj%R$cqFHowt1mF9yA*W>@6Ia|1Dm-$>NBe|!~=T*A?@@C(>ZClYZ{*5In z%{PDknVr8+#r{v@`DOQCFTH;I)|#$I-Mq&i7xKy5S@piSd9&vJ_q`_J_CHstoOJ*H z=W*rR_y6m5Z9Dzw(Ze_QYU>`F^y{7dWU+hxcjd~TQx9*6-=Vtyhqw9j$=;<^nZ|qm z?EbXA{+;X3W8!~3;yzuRfA0PL=%+t+%bZ}3|8D>J+3!jJs#wBH?Td?>Q*2IJR+p{I zH=n#Z{?DB^KR0iGdg4sZuBm3z>%Mn?dUEi~)n9kGHz~*R%`b|%?D=8&e(p)x=8O&Q zP0L;y%y``SsfOG$YHqHFuvJlb%2OW5piJbNgz4UwZxBXr|AnJ>6oT zR^(YeK6+H;7toMJv)pP9e znl)>dW&HBEp0e}zu03BWr>oqYKKuXc=+EJI`{rz~dq3k@_kZ>M|9+)ExxWAZB%AN8 z_otZe`Ss02oqs}_&CQy1`we`iF3GH(dEa*WlK{Iv#~;UhUL*bak@~;HxZ2+qU(bYA ze)JVz^7jz?=jq}xvsahBIp=iu*@czu*JSJOyx-7j!tjA(L*c}SN2~SEojT<;YguId zzPmQvdmjF{`6P46w%pl!tM$*>mfxT3Xg*hIQ~7(>$k@4kn>W7hHLrX9_ZTSnQf=hc z>rcM^?2`AlXY+Qy_iPJ2Hd)O)#$x)(OB1(DE}Zma-&M8szfMg5q(8r?a9+W`ns*T~ zC)d|}dAH}$%KnqCn=j|8dAom_!~OhI{rl*X2^+Sa-*2}y`S;W7TYS^l{_?Q>wRyTq zyZFh6iDysV%wE0v=QDL%!}YKEjf^FhWU9HopSaC)`|D3NhtH|B+fVGjb3L`}-4Bbe zK69Jp|7U!w{jXYi<$7(h-U_>k)#mZ{CQtwKD*u_PI#2oY{68*r-~5kBmi?WxIos^y zzM49h0l=lbpcM}J!K%H!7J%a@nt-hOhab>|k}$!ovGe7w>Z7rWf9(D04${SCJ&e(U|M zdp{-r-nG))=~))@6DO^aIp?)|-?|6Seurd+_ob+MradzbE`Mp(Tt4S~{g3|7;qJH3 z-oIa*Kil!{vXf5dW_wS%Ds$`m#yubTzdwJeUlI53-Q;hnAO7q;DgIv}e`l?sf1J0` zotbm@&M|m*>|n0X!xwT7Dj6Eq=f+Oj_Ii@m>|ldct4^`o|9HrL=lQMd^*3#zZRfmQ z8k@hn%y!E0#~+_roIE+V>e2Pfo3CBI&)+Wn*ys4%_s3=@&R2Q+DE!loxsPk!8_%0^ zf8X2W%9~rmpPu@bpytjeXTM?9sT}Kl_h)`F)|_|CP=e=@iLcsZHTnNXmVerzEjN4X z)~#Rpjc51&JsO{8lNoe<@0Z_yH|FNWV#Q#narcrfu{#OJBPsx5`@oOrJ_* z)S05{N21f%KR-0z^KFUcrM)v}@>GY0y}J2kmbX#VsZSM?^!I(v{rRPQpN;w7&-Jr- z+<%_<|HEOQk>9jylTyr{p9_6?ZC1}QPop#EZd$#Kx)d6FYumXy=UyJ2GC6Q^+244< zC@F>?QQ>Rvrxv7En#P*Xy%rc+I`8wG^!#((_cj>DSEZ(^=9=4b@7=V=>TX)??W+Wz_G?rW3YoaDX#X+}z>zmd#A5thChD){cyfJo)O?t7q3w zpZ3n27;`A>UtSkQUVdB|BwaG6vGL?> zZ#Si%ca4mWK08s_ecN9-!--L3q6cK*4~|35x1dffkiam_pXzh(bk6ravqeKV%) zF#ogV`-;ma?Jhd4JKx)UO>pkIf=$-f_kEFnwn%u|e(&vbtnYnW_UGci#B-8rkx?gi ztiCMW_vz*A`%}u#+iEVpeQ%j!e4!@C6lk6$_J^TJVecQ*am0!5))&9SI z<^TNLQAfSHJIS9{IBxoNarWnP)sqg!-25)g@E|8R)N}v+^Z)+FpFDIZX4W##(5)+9 zo9&I;TiN*VqxAMIuhS>p{_^|po7>y7;3*2fEXByk(%a0d-)Yw~9_5RcD zcfW$BO`F#BoHx9=u5Zux{m-w<{XX^Q$#y&4|4;7Q>)LOr(OG?1Hu8n)$%i#B%>NbV z-HcDS2u>|IiJWP{iSlJ+`N5nO1a#e(}xsJR+v8in#dR^_G$Df{fPd@o@_owCWtF!B#`^ukv zRWPA$`^S6{P;r-gJJn#u$%D=9n|?QO8_khil6yO(bZXq?X#SUL)|XscnmN1V?H8SI z2c5ekZAvsgf3}{a_42#FT>7Pw%2UDf=ESX!oBzyk@9)Ihe&@aP*RNT!Zk3L%{__L> zm}kzO?>~Ksy~LAi%6o18&neVf`)bSNz;EYFPky>PyLi#;qknhq$(%IP?CmF&uL)}M zme-!{JzjNlWATKOzii&J2glamHNAW0jM()b7QH`z{(N(@I8bl@qpp~OgRLu9TYbM< zp8syoXXBgO^W&#YpWb!cZr$mOooCN!=b!jIzqoW>-Py;RmcJ`H&whW-^?g5Mf1cQ< zSA1CO@=BA~Ykh0ezSgbI@!@^;+~|Jxrjn8=o_EjpwK`TU-&^TBJuOxD__0{F&zon) z?|++Up1H|ND&1zLU(fniFZVAGa%rjdmZOX{z51 zS-S4BSuDs^FTehtDajLjJ@i&tRr2(0uRq;5URS%y>he?zKMD1dze4(7%JN+f{rM){ ze)f`6XU-%P|9$>3V@~Yj#XnnZy)BnZ=j%0>-+KI>zi5fnld0d|oq1KD7&>>$S>~1} zoNkLhnk-?Dj@5aec+FQWQd(8K?)v#BSM_Us)Z%yOuCG3w`}0NmyORr_7Ov90S1Z8K z(7w)l(mL;YgA*AhUdA)mvD(_(|8IEhp|7w1d|vfC-}T|?7S`35T3d2&r*7FcZCU2# z^7pPYXU~q7e|+Ts{rI)|^RGWyw|n25ZMnwQ);?S7Hc#ix&R#9q7xww^bD5J@4{v?B z>6ElN<4umwyQcTA;r%?@{oKi0XJUT+T|2*5uK)N;+h>N;|IJ=|Z|~E@@ar%6SIO*t ze7?y2{l%T1UWPxPd48AO{eOG3KRw;P-#&iE6-#eBPKE>5Th_h4WaisuZ0tEz#;QUi zJA3hxS3%!jmCRgsanGJTbC}uro*igpo+-&=Y%G)4mLKXHZo7I_*5{=C&FuWX)32{j@K+xh8W8&uTsH z(CLYDWpZEF6xmF^E+2bBdir_a{4%@0b@jWx+!K4Qygk?2Rynrp_q+Ds=_hYlWZ!lA zR`WNt@=Krc&c~O(rvzO$+|9)BV0HH6GsX26{HMkpoBZ+1yGyUr8&|LLTK{gT`HS>r z_7V2gHg~^YQT?=Rzgq0|6q9)=M)TJAKhFpbEq%4iZ~y(%3275{t@7{t$ZoOiW5t}0 z6?3-bUfLG>YM0mEI@|bq1CQG#nQD_uZtu91Ym~W6@$T!nxr%!idS5Teob+<)LjLa$ z85nGwN>8SBf35oa^5)aC+$Z{uzdQEp68{#vFULM#HeEEi{8Er zj#nMhQt~y{j4E84IqTEY_y4Y~Sp8+voRZ_t3=I2TnMLIkwRQjAH|@*v{T=}f3^)27 zl`dgqcrfwJoB6jG85sDR>n7d#*2r&v=Qtw+LwfJ)^}CO4I?cck;N-!~;J~QJ%fP_e zV#2^6B5;X;A;4i!3%-h5|Ghw-nSo(WXw}|%6WACU-U@+Sz2N%mrSG^i85nL%+h=@< zfnh`K<(0C3{~fNCVPGf-d-?VEIS*!r17$OnL7qJ`k&U5&Y1)2fh6W}lDFz0v8DQaG zV3D_*|Hv{hh#0sdB-VhXecm!KG%#JB0I{Yo?!FAjx@})AFI@wf_x8nCu)5pNlAIYB zG!kSp5#=_q4_udPAm+`b03ASZtP_41913=BEbyLlNI7BqmOY(WFaM@*NcH%0v8 zV_?{}G#{k%hTZA=j0_HqaqH**dT7VNz+fTG2{K$^2}mDU2iO-Jqk^LW0!z}PiD5K% zfJ(X1l3}z|q`Xvgu=m{cPStbLs;a-wxR+Pwtv35+d--4VO*RIGf7^aZTPp9JY9Mx= zfnm;BNG self.write_byte(byte), + // not part of printable ASCII range + _ => self.write_byte(0xfe), + } + } }