From e041004cc9aafe9b643ff5a2701d7e72e25e6e48 Mon Sep 17 00:00:00 2001 From: Joppe Blondel Date: Wed, 1 Apr 2026 18:41:10 +0200 Subject: [PATCH] working text mode --- CMakeLists.txt | 2 +- cvideo.h | 6 ++ font/mbf_big_00.png | Bin 0 -> 2480 bytes font/mbf_big_00_black_bg.png | Bin 0 -> 2490 bytes font/mbf_big_04.png | Bin 0 -> 1734 bytes font/mbf_big_04_black_bg.png | Bin 0 -> 1819 bytes font/mbf_small_00.png | Bin 0 -> 1336 bytes font/mbf_small_00_black_bg.png | Bin 0 -> 1398 bytes font/mbf_small_04.png | Bin 0 -> 988 bytes font/mbf_small_04_black_bg.png | Bin 0 -> 1084 bytes main.c | 125 ++++++++++--------------------- text_mode.c | 131 +++++++++++++++++++++++++++++++++ text_mode.h | 12 +++ 13 files changed, 190 insertions(+), 86 deletions(-) create mode 100644 font/mbf_big_00.png create mode 100644 font/mbf_big_00_black_bg.png create mode 100644 font/mbf_big_04.png create mode 100644 font/mbf_big_04_black_bg.png create mode 100644 font/mbf_small_00.png create mode 100644 font/mbf_small_00_black_bg.png create mode 100644 font/mbf_small_04.png create mode 100644 font/mbf_small_04_black_bg.png create mode 100644 text_mode.c create mode 100644 text_mode.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b45699..7e28f30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ pico_generate_pio_header(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/video_dma.pio ) -target_sources(picopal PRIVATE main.c cvideo.c) +target_sources(picopal PRIVATE main.c cvideo.c text_mode.c) pico_set_program_name(picopal "picopal") diff --git a/cvideo.h b/cvideo.h index 4f719d0..821fba1 100644 --- a/cvideo.h +++ b/cvideo.h @@ -10,6 +10,12 @@ #define WORDS_PER_LINE (VIDEO_WIDTH / 32) #define LINES_PER_FIELD (VIDEO_HEIGHT / 2) +// Active and visible field in the framebuffer +#define START_X 35 +#define END_X (VIDEO_WIDTH-40) +#define START_Y 20 +#define END_Y (VIDEO_HEIGHT-40) + typedef uint32_t video_framebuffer_t[VIDEO_HEIGHT][WORDS_PER_LINE]; typedef uint32_t (*video_framebuffer_ptr_t)[WORDS_PER_LINE]; diff --git a/font/mbf_big_00.png b/font/mbf_big_00.png new file mode 100644 index 0000000000000000000000000000000000000000..ac183cb99521da19950b4d0b53bce7c297dc8e89 GIT binary patch literal 2480 zcmbVOdo&Y#AGcPN+zQ#o5YI#6?J;A8JVcmF=#hJh*euuFGI!O}J4S>QA(wTM+FXWm zTOpUZH#XO~)U+*gX)cMPcRJ@e=RNOxdY^ONKYsV`=X}23^E+4Nx@jrT2iu`Z~ykvl(7sPSeYhDxP}T8;&s)K%U4DzQ$d0IEkX*C&s#%TfGDWS7QLG8M7me<6VdR zw*rmC6sNMG!px(L<`)qy&IN^Y8W46V?r>9+N$76*;jb0gC%FSaLP>}UZ}>9XoQC8eEa z1sT^%kzH3J>oCp7aO@6uIa!`nw%cm?x|TQP-L;v!1eDu-RJ>E>0~R;+^Sn*;=Y#!@ zm$R0_iC7G@Ceme~R&Su<<)(DIv`E%C7b>zMgBu{k1zaZc>OR-K9HqyD0furGUCtwm z8oluyNi|QdDV2e$P|Z$2i$(^rFA}csQ0XxIa8xIJln3@lw@|^uVpLab8Gyi-3XUd7 z^9H{_p;F+oB^ycCaV`nz0xJN`y^;yjMnhGEMLAc@Pcj0 z^JSsl|8tK~XbXj(bLX^o%U2L@QYY&mMAiQu(iVRpM-0>*?w4RFhAs4q%!5wh>jtar zz@H4d>a(N3QZUzAVP}qkw@KqtK6z{Z8CL)M$^lJ?R5=i(!mP-ZK}nlQd|4D;h!=t&YgVVDuIij+&7NXjc4vh7>4ab%ys2ln6(l}78Z%nd$RGu^~Li09HYv%V$i za9ZN_oq9I`UgC*#Aea3JXJIgtQ(~%d%gtfqW08AowEfUn~mW>A)VcG>!AC5+u&B zA~l1xxoXPBdZ!~7BNY}wC@~NPG~U7kJ)C=5oNQI3O^Z*L@W+ zyIpLq$_GWR*~&J2ByX0i9(&_Ui;uny@E9$g0(wsenp}7Bsir_IzMh08W&>1lFAi5S z3N!oBb8=J0FWlf9Tqg*k9VVxW!>OeZV>w_L7^)ezLD)oXpxPH^bzsF3^^M85Q|b~C z#rrge#&trAyi_)oE42kxDalS8@6VfTr6q>J{7$s}Tu}c_D2;)X4E=CJ{a!Lr(uQK5 zFVNxFkN>9{{{*Z#e4G#z(Mfg>Uxp>EfYZ01>aTMRoGn9cI04ap%=c(v$cGN?StCV= z58@Fe_#O|R&Zs8LQVV5Zty4tm1o9Def(GLD>o%p7vYtbEe1kD)Rozjl9*M7bD?;x+ zfVPfisb(t+&x3Dxzp{mrjO-ZvdjQLYK5Z%#$Ci_w2=!wTPUYP)>mQ9hY+k9vpvfBs zo6z?n%S^i*94&R>8DxN^9tq;`3OH~WpSySQScCZ?a;YR<7>X{u>W0xDN)?$4w$ zzQ&Gt5}?|{dT&8UPf|5E+V@&3zDlb%rhIG>wv}u)t(RN5UtLMh-e|}&099{$LdrOH{1UB4 zcmb;s0-g6KA`|i&81j!#%6%yHsTrH{MQg2AKteNt4H+e#dRZLngpJJ{EQUm>djvYD zxkWu&&)wp>YYI0HP#8(1YFZt4Vt-VT4}w@yy8J8m>mFXF3~V-D@q_b9(;#X38i_wW zQ)572t}h?K(IL+CBrzx5S!5t^dVJ)ys>H{e6zt^bl|T9D0HdoW&It=x@T%wc84q;> z1bkM@*|kSNW3H$k0{0q;b2$PN(LvzluI_iFygSqke{kQ<_+}=S%CtUNz_*V^rN%sQ zfh+UazFqmG+w8VvKfz<$w#4*)hj6xIF-5WRGY7xOO60^q=LLVfy7(9iJ*vSd8R%BW zHUg(B?_4Z%z!|6)&0YnfITz*~ICIjqfB=Czc!ZeUo}fmGMn2tL=+5@t4S8x-m~H^&#DuKO?ao%1dfogNkA3f} literal 0 HcmV?d00001 diff --git a/font/mbf_big_00_black_bg.png b/font/mbf_big_00_black_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..e31abd556c467252989d9721c2270d06ae8e6b8d GIT binary patch literal 2490 zcmcImYdF-~8ee~gVKiGxxwH*w9NC=BkwIvNm>4^TR4(nJwp=TEyvDRw4h z7;-li(?zb;lP+#!GD^AZa-WQ8#>9}E;nVqWp1seP^I@&^KJQxVectuH@9($Xi$31& zXp}w*0DvZWxb0G`EX8P6Q&r4qjuEE-P){Yfx$I8Jo4US_@!58jYZCvRbMn2WFf8@B6f-hI&SrZzWl0xGR3e*7i*1Ac#sq$BGnEuP2CA9d~JhB}eb`Uat)N$S&k zKP#Qp-d$ujFx2Ey4BUdlOhBO?8>*cPdxtd~&R~XKhI|}5*H#V~d3YCIxW(ci8Gg78 z*2IQss4LcWhcrO=1#vuw*QZ0O_A=~n0w%N}UR__S7K-{0ID%{pF4G-h-xeqRhQt_V zRp1CBZ^S+NYn$$>d`xpWEo6#v0Czf#J|eArU<1bl`ZG08tGsZycv4BN@{gv-{^2`j zW{((xwn3}Oh+-tGLm;CKSJXfI^d@sA&j^1yE5SV&l6JD>SCvxh^ z+;%GoYX9q7STrrb(7g&bWn<@vRmh@t*Q}F@#G5m&hv1{hpCxMD70iQ2252UVP^v& znpNt&oS>D$UCC5aN+>n*PTQEgs!OsJU-R)8v+%IZ?9hs51_BjC(|M|I)E5I zDm$uIwISCPl@aQ_>8uRY+T3=4nVC|Dewz{&w{rx#cFbcvtQOL(U+-PL3K-yyWyrM7 zbcCyX&IFB6r41-;VpraTmcCf*am!*svbwYolI1PnI#P5uZJNw+l~#0qf%q< z!gZ9s(=?B}i!q!C&5QL$J-BNyVNwI#ER7HIhT=T}M zqNipcl}zT;>P9P@WL{}IFLcdT!bnVhw=L6?RHP@4jnXzLHfLJ^@nut zZuA9Fpyr4bbVP~ufc#i*wq5qw3jvSkaa51jnlY}b<@$ZBxrI6)Bte%QxRt^kK&{eT zS&Su-xuvpGoW}~ldz!C3qdc_Wz6||r4D7IP0tzt@+oPswf4DQkZ!b~gnbITTGRfSI zss3njN$zw@TfS?T97{~|f|H}(#&hBbL69!5Isdnu?Krv!9>SP~2-SC< z@3^bqJD`Ie8g)2?%Aqi@t3Mnh3IsM(RaX3`FudmK%7vWW=a^6EfjW(*o z^dTp>>(nsf$VpvUfNv`vXa!*5C|1|8Hhv;AGpU@I)LkXI@sgeOF2DiajCesx&noaq zatSbKE@4)J90KH@ilwtk%0stcZ;Zw(d2%)`@&$YeqCQO)H%!2toLQQT%f1CO{aiJM z_=|mVi`7w3T=gc^X1!HpX`8}xi;#l&1jv5!Qas+$q$jnhC<>JkwKtA$f}j3QoN5)t zx5y#=4h78QYyGz78oh|nx<}i?J{F)5F})2LHPmq6Zwm? z#npH-c#m+i$lTCsU2`rCtZ6=KI<$D;C3W58iqgP&iu+T%_N<~dLDIr2H-{G3C^vPzM*WpgW)``nMR3seYvKbS__61T2=6R!+;6vXV;>=crhG?25WjQx;9 zaaMY(=qW literal 0 HcmV?d00001 diff --git a/font/mbf_big_04.png b/font/mbf_big_04.png new file mode 100644 index 0000000000000000000000000000000000000000..be03d98c69696125086db74b1385535780690062 GIT binary patch literal 1734 zcmeAS@N?(olHy`uVBq!ia0vp^3xN0l2OE%Vw$3PLU|{R@ba4!+nDcfbxr(!wJ+E2|NnFQm-Ir-zws`MUx(OO z?65x&rlB{J4kKUmJSVY^bw zyWijTgnU}cAii_jJMF6(rmK3Y-^&*+|MZtFJ+fi3(u{{uwaUvL<(K5AEuDGt@LmS{ z-(_dGoj4R*1gaKplIPVmlg>Y$X8pTxV(<3m({)!ucD^Y-u_fL7zqY)XxBL=o?nIID zv^#H_Pk%Z8Z1>@K)&3LpU-$Vip3{+<`rPnK;M^BENmGv)CkHeX{?B3zyCtW8z~g4M zTmIcQ&8ux}o-*}3Tl&b**nIJg#Szh51t}j=82fIhbAOO~-^q05&%H2?#`cRfXZCqy zI?p_k_UN9k+uxhhRfO}erKg>lAJ$-;d(~zd*N4g6+PehIh1I3|nPT)0+?-xhcBjzY z`R1e3N7jnOi|GGm+g=oLGF@b~>Cc-RSN{#|U;HO6?%vGl9JMb_o_u&vp+&%nqw@7W z&H78+RV#nI*r8c15@@Tvs_xr+i)|+>c5hz0FK18OoT&9^VxDG z5(_4Hq^kZnyH@N_di4AEzh_Lf+4MXqbe*mC&%+O{?Kva%;G1}6>d{*VxBiOCDe*5& z+LZrnoq6rF*FSeXmk>=bDZ3i~zP!^r{N=mG=8aJ~*Y2-#%J?|*-mygny?se+)&JbwxA{Z)kLZ)hCV5Ab)AFLe zrzHJ7!6c)4rR(EcraLe5&HB&0&utZ`kT`q%O?QYLd7s|{9vG!niqU7Y$SaLu*DXZm8_uCMATS|j!{$Fbi!Zi)Ky2VXz`|HbJ5 zOoIE?pE~x#{Qj@qQv7`H_&0=meE;Tgv!bBD^usl~YI&JEC5&lof&LPH-HRC>$~|4i z^`Xucl7)5GHt4PdW$&q6A2!#1v$NQ-l&NRQJcBu6*Y943W#N`My|1cL=Vnj7zvNJ5 z@h7&2dn_xP3+~O?<7koZcTZuCxq7|;P}ZE|`Pq}wEuWvQYrDtFUOvg0W1+ZrZD-r( zXHU#m;!*yD7i#Lx)eFlH9-R9`!0E-&p!h{VYkvA_VVDkAj&Q`gtQWBb1?rZ-thn*Y z^bDY@>LznR9fL(V!p*y-s>EgPc=9Q>#OaKBk;<`H<@29$t-;-c3Pu_4f%OxEr>mdK II;Vst0NRLbdjJ3c literal 0 HcmV?d00001 diff --git a/font/mbf_big_04_black_bg.png b/font/mbf_big_04_black_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..21fe41c0b37207c23654a1a19bd6b05ee6106072 GIT binary patch literal 1819 zcmeAS@N?(olHy`uVBq!ia0vp^3xN0l2OE%Vw$3PLU|@UX>EaktG3V`!gOeU>2)O*c z{F%A>=J|WJB|a5v1cOAUD9qjC?6%_~^CTtT#Q3j-Es~VMBRyCIIprV!9q9hPrLzR%WSy=%H9kQr?D6a z2Tf!>oLTtze-o#|oh=M*U`6X{SM{=e$g{E75pEX4A)39j@okBXYS{0{HT%1ZL#Mph z|MJEDPcJIcbKY0fZ`*43{oL%e$zLkV;+2YBZ*cxL40~~r>03{@U`rT7bWEA-)R^-N zFI3fC{r1I5>dxoJNXY}MKPoZy^wmFGKkM~c-`66)UNOXpu5Vw>_u%fH^qiD`XZBqD z<+@|V`rQFv+e)Jt)cxl0B`onT*`v-Z5t%>bbj?F0#y#2fRrUGI7KM+NGUm+XxOm~* z8t=5dEFT&fxOF7^!@^xpQm zXEaTdcBszY$+%@Zi1GTl^B4ESlG2H*^lo327r*#XYsZWJSvul(+dZnE9FHr@KmW^R z^97y+R{-ee_?*v?kpI?=`9^U@%D2S_zlc_C@tAo*;J{ACGh1}|AIx1;yKZ0U zUg_yKZJsjRyQ=!wFlhFRI+^*?Sw5V4FqyHZEuoCT*s(&ZVd8%7CV_jCeHfU(c1w5Z ztTAsWUuYTaqP9kv@ywM6vzc#Po)&vN0I2o_XIVZ2M?x+`_5LSwKR+{ja3;ewVN>z< z7UiHf^S;LPmWKU#ap&gq$o{?6)mzuSnf{{G?BKdnHG*@V#jSbDcHv%B9>d$U5JrNv ztp3y0e|4@EF-K=TmX~#}6;t_qOXbsN>))IAKicYhBwCwe;#LlNtZup>d?4e4ar^|2AMcoEaJ~-y{d+6po4;xI z)+%mNocMA6n$s`pQnI$HG#uycZbih4Qsz{ndf@LM`CeuZ7PFtYnKLaqAL#bMVH9{q8t#=r1MSt}E zk}s=2HT}G{-qV8Ug3|YY-u>Fjb13)hQ|239f2Qk5_;sIVH8AS@(e!YO1rx{jRz{mH zhC{x*4<@QM6a+K0eK2XQwG6hpu%VUqo#hkGo4 zdTY%wSI-wXH+!-<$Mdr%rCUBfd%}CDviK7lP`0wU;NF})ju!dv?q0aGQCTKN@%v1_ z7Bz54Kk3h~$oKmi4=l)T|!4?j`6OnBi*P$Ja*sVoJwbpH|zyAZk% zPP)gdD{Fn|plH$jmo6EbKsnX>Uq>6zsroCSF2tq_?vb74&fdrS3JO~9q+1Ej8uk8# gdjl8yGk?j~NZZYxU?!*otn?T>UHx3vIVCg!0E^RvX8-^I literal 0 HcmV?d00001 diff --git a/font/mbf_small_00.png b/font/mbf_small_00.png new file mode 100644 index 0000000000000000000000000000000000000000..088a737228b5e9dc80a62afc740fd149643d0e4b GIT binary patch literal 1336 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 CN1I9j literal 0 HcmV?d00001 diff --git a/font/mbf_small_04.png b/font/mbf_small_04.png new file mode 100644 index 0000000000000000000000000000000000000000..ec9232c0130a0e3df19297d3b333630d95c39926 GIT binary patch literal 988 zcmeAS@N?(olHy`uVBq!ia0vp^1t8491|*L?_~OmLz`WSg#WAE}&fDn+^X?e%xP1Qq zVy{E}HdlVem~fT$FIC1}_Zb|o)h%<0yfow24}mX_pRd1v{Qn7`di7_2#5ZQjys`Us z|C|+j9m}gD2j3;9KFIpMllRfd9@!n1kEM55I%nk9u6wpNymX6hDEHeVskLE$*Z$K^ zKCy0U^<6`aw<+&+ckO@j>3Gq_7b@>gsQ(SMURKqZbuY{Qrqiik!Rot`QWtE}T6EBL z6Ms_*lF%x5=GYs0`HM@W3~jOH*QD0^U)v&aa^B2+ z0Z;!;DOC@UoPDW&$*P1&=0{|Oe^k~7^=N!o+g)YVs9nBNX8G%k(Ak;0BiqB(3?6&U zpBp>v>-SZ%D~|YouDQwAW3?(Q#9XbrdtHBS*{73_!hCK@UY3p6dF12Fp7qD}&V46! zVtb{df8bM*+u2E+FV4;qG0Zv|5}JHBW>we5+380%?b~G6|9w^d(o6R~) z^tkL*93Pj&qt;y$;FY|@3DhvF&Y4eTHcrTxHcCF-owGCabH%(g+N_nd;j5NjG?Z=y9|2 z`&XN-7ZZ&8Y^D>=j8?3rIq<6ASylG?Lyi-kkcPVZ%)KxDC@?CYl zB<_Xf@)dK>E!tt#cyaU9y&C5`e8qxqdP>bW5+B?8{QmP9es+;}x-@s^?qXgp7WMAH z)$Se9;lKB4zh7S^zg=^)Rf@vZSyqJ`zHi+0Da7V?4R5vlWag=zZIXTuif+#Wrkpij z?)5vo|5g>T-|we-1fK(k>5~-p4UMhy-hPzf_?#AY^1!b%T9Og-BY#@veBgEAs9iee z4Z|_RM}-hkRp!^m+V<^*Jq{VhmNyxDl8-!|5o)x-ZnGELwe+w$zQ+=c7t)G0F-sip zIBvK)C80KLD$knpYyQX_Gh|wL7HE}0pJ0D7vMoKyEKajep5#lAQIVwVnPBri z6-ylFXfc~SS@wX1n@wLN$YQX0PoD#g;K)@|w{9rxsgQwK4083U^NBLb0=Inp$`~F? ve6)aA3^H%({KFP*3R`Ak_ejN*e+;j$rXRdzJ{_1{85lfW{an^LB{Ts52KePu literal 0 HcmV?d00001 diff --git a/font/mbf_small_04_black_bg.png b/font/mbf_small_04_black_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..37ccd27fd397c9569723ec5ec86d60493cfbbcf9 GIT binary patch literal 1084 zcmeAS@N?(olHy`uVBq!ia0vp^1t8491|*L?_~OmLz{2e5;uumf=WY1GK3PSc>i_HS zX@5=M-oC0SRk84pfyBe+uVu#!RA$V0^ZfJAn#vzvf1lqT-^lg9B{Q)5dbRvq^*qV% zn>XF+RQ{^``H1f0Kb?DDDSJOk^AoMGSZ@8HU|oFdA;TMT2g?4~pS@P<*fYsRpYe*5 z^5-?Cd%v#X-JY~@-o)~YG85iM{1&_6|3|4;<;C1IZs6dZk5A&E}cSS(T+ElH;Q&d z%qg8A@VaZ=_N>jy_g#vAC@v^4*~YGLhHFNy%!S=LN&B_m^FNvAJZr{+qphF$;v#>h zsmvBAnx5)f7383@eMQ5)IY(;uxH&x8(rc!>+jHjE3&%39Wb`I=Th03J`*`=yJRw=( zn>qQ$6HUWI6^!hqpC^lU%$p(ExTi8~l5PN_`{yc|Ajz9tUt$)?G0Vm6-Zi7~o~h3L z*p|19IqlCyo*WQ96See1Q^QO(wzo6(X&idt;+xJ@{6FBF&Ruz{Y|8E2g1dju&AC{Me1vlPFeGwa4 zzxnU&-_LS_6H9wC?!@i87q|HP;|<(j&5m(t3F}-kbpwXTfxtN@8t3YAXfI#aFca+c z0PiDDIOmELm|UxV{?@l7egRj{oEc}*7A@bPz9{wPy<0@v{|8g6fRxAFucm6EjD9 z6_;Cllqen4;p%y~7nFZ2VRW>mfN^-Ad z-**1d3(HjrM?4R_w$QQ{7k}Qh>d?d+at#LB3KL~JPDf75e|ZB^cA%a9o6pkysDedC3ygx@-S^A>Zt&0*QoxA;?WVd1(JS~8t) z896pH3Egbm(bIa3`F48Y7nx9lM=B3WWv0&W?k;u>edv+H?=VAj!U?`3sXANaZrF7% geolIj@YgdwHfd~~^(${CuvB31boFyt=akR{04ERvQvd(} literal 0 HcmV?d00001 diff --git a/main.c b/main.c index 25b6ed3..21a8452 100644 --- a/main.c +++ b/main.c @@ -5,112 +5,67 @@ #include "pico/multicore.h" #include "cvideo.h" +#include "text_mode.h" -#include "font.xbm" -static video_framebuffer_t fb0, fb1; - -#define START_X 35 -#define END_X (VIDEO_WIDTH-40) -#define START_Y 20 -#define END_Y (VIDEO_HEIGHT-40) - -#define CHARS_PER_LINE 32 -#define CHAR_LINES 20 - -static char text_mode_buffer[20][32]; - -static void clear(video_framebuffer_t fb){ - memset(fb, 0, sizeof(video_framebuffer_t)); -} - -static void set_bit(video_framebuffer_t fb, uint x, uint y, bool value) { - if(x>=VIDEO_WIDTH || y>=VIDEO_HEIGHT) - return; - uint index_x = x / 32; - uint pos_x = x % 32; - - uint flag = value << (31-pos_x); - if(value) - fb[y][index_x] |= flag; - else - fb[y][index_x] &= ~flag; -} - -#define CHAR_WIDTH font_width / 16 -#define CHAR_HEIGHT font_height / 16 - -void draw_character(video_framebuffer_t fb, unsigned int x, unsigned int y, unsigned int scale, char character) { - uint8_t row = character / 16; - uint8_t column = character % 16; - uint32_t font_x = column * 10; - uint32_t font_y = row * 12; - - for (int j = font_y; j < font_y + CHAR_HEIGHT; j++) { - for (int i = font_x; i < font_x + CHAR_WIDTH; i++) { - // XBM images pad rows with zeros - uint32_t array_position; - if (font_width %8 != 0) { - array_position = (j * (font_width + (8 - font_width % 8))) + i; - } - else { - array_position = (j * font_width) + i; - } - uint32_t array_index = array_position / 8; - // XBM image, low bits are first - uint32_t byte_position = array_position % 8; - - uint8_t value = font_bits[array_index] >> byte_position & 1 ; - - for (int sx = 0; sx < scale; sx++) { - for (int sy = 0; sy < scale; sy++) { - set_bit(fb, x + ((i - font_x) * scale) + sx, y + ((j- font_y) * scale) + sy, !value); - } - } - } - } -} +// -------------------------------- +// CORE 1 +// -------------------------------- void core1_entry() { - gpio_init(25); - gpio_set_dir(25, GPIO_OUT); - while (true) { - gpio_put(25, 1); - sleep_ms(500); - gpio_put(25, 0); - sleep_ms(500); - } + // Nothing yet } +// -------------------------------- + + +// -------------------------------- +// CORE 0 +// -------------------------------- + +static video_framebuffer_t fb0, fb1; +static text_mode_buffer_t text_mode_buffer; + +static bool framebuffer_switched = true; +static bool text_mode = true; void framebuffer_switched_cb(){ + framebuffer_switched = true; } void main() { stdio_init_all(); multicore_launch_core1(core1_entry); - clear(fb0); - clear(fb1); - video_init(fb0); + init_font_cache(); + video_set_framebuffer_switched_cb(framebuffer_switched_cb); + video_init(fb0); + + memset(text_mode_buffer, 0, CHAR_LINES*CHARS_PER_LINE); + strcpy(&text_mode_buffer[0][0], "PicoPAL text mode"); + strcpy(&text_mode_buffer[1][0], "-----------------"); + + gpio_init(25); + gpio_set_dir(25, GPIO_OUT); int fbnum = 1; video_framebuffer_ptr_t fbs[] = {fb0, fb1}; - int i = 0; while (true) { - clear(fbs[fbnum]); - for(int y=0; y<20; y++){ - for(int x=0; x<32; x++){ - draw_character(fbs[fbnum], START_X+x*(CHAR_WIDTH+1)*2, START_Y+y*(CHAR_HEIGHT+1)*2, 2, '0'+(i+x+y)%10); - } - } - video_set_framebuffer(fbs[fbnum]); - fbnum = (fbnum+1)%2; - i = (i+1)%10; - sleep_ms(500); + // Check if need to draw new frame + if(framebuffer_switched){ + gpio_put(25, fbnum); + + if(text_mode){ + draw_text_mode(fbs[fbnum], text_mode_buffer); + } + fbnum = (fbnum+1)%2; + framebuffer_switched = false; + } tight_loop_contents(); } } + +// -------------------------------- diff --git a/text_mode.c b/text_mode.c new file mode 100644 index 0000000..8ca5fc5 --- /dev/null +++ b/text_mode.c @@ -0,0 +1,131 @@ +#include "text_mode.h" + +#include +#include +#include "pico/stdlib.h" + +#include "font.xbm" + +static void clear(video_framebuffer_t fb){ + memset(fb, 0, sizeof(video_framebuffer_t)); +} + +#define CHAR_WIDTH font_width / 16 +#define CHAR_HEIGHT font_height / 16 + +static uint16_t glyph_rows[256][CHAR_HEIGHT]; +static uint32_t glyph_rows_x2[256][CHAR_HEIGHT]; + +void init_font_cache() { + for (int ch = 0; ch < 256; ++ch) { + uint8_t row = ch / 16; + uint8_t column = ch % 16; + uint32_t font_x = column * CHAR_WIDTH; + uint32_t font_y = row * CHAR_HEIGHT; + + for (int j = 0; j < CHAR_HEIGHT; ++j) { + uint16_t packed = 0; + + for (int i = 0; i < CHAR_WIDTH; ++i) { + uint32_t src_x = font_x + i; + uint32_t src_y = font_y + j; + uint32_t array_position; + + if ((font_width % 8) != 0) { + array_position = (src_y * (font_width + (8 - font_width % 8))) + src_x; + } else { + array_position = (src_y * font_width) + src_x; + } + + uint32_t array_index = array_position / 8; + uint32_t byte_position = array_position % 8; + uint8_t value = (font_bits[array_index] >> byte_position) & 1u; + + if (!value) { + packed |= (uint16_t)(1u << (CHAR_WIDTH - 1 - i)); + } + } + + glyph_rows[ch][j] = packed; + + uint32_t packed_x2 = 0; + for (int i = 0; i < CHAR_WIDTH; ++i) { + if (packed & (1u << (CHAR_WIDTH - 1 - i))) { + packed_x2 |= (uint32_t)(3u << ((CHAR_WIDTH - 1 - i) * 2)); + } + } + glyph_rows_x2[ch][j] = packed_x2; + } + } +} + +static inline void blit_row_bits(video_framebuffer_t fb, unsigned int x, unsigned int y, uint64_t bits, unsigned int width) { + if (y >= VIDEO_HEIGHT || x >= VIDEO_WIDTH || width == 0) { + return; + } + + if (x + width > VIDEO_WIDTH) { + unsigned int old_width = width; + width = VIDEO_WIDTH - x; + bits >>= (old_width - width); + } + + uint word = x >> 5; + uint shift = x & 31u; + uint64_t aligned = bits << (64u - width - shift); + fb[y][word] |= (uint32_t)(aligned >> 32); + if (shift + width > 32u && word + 1u < WORDS_PER_LINE) { + fb[y][word + 1u] |= (uint32_t)aligned; + } +} + +void draw_character(video_framebuffer_t fb, unsigned int x, unsigned int y, unsigned int scale, char character) { + if(character==0) + return; + unsigned char glyph = (unsigned char)character; + + if (scale == 1) { + for (int j = 0; j < CHAR_HEIGHT; ++j) { + blit_row_bits(fb, x, y + j, glyph_rows[glyph][j], CHAR_WIDTH); + } + return; + } + + if (scale == 2) { + for (int j = 0; j < CHAR_HEIGHT; ++j) { + uint32_t row_bits = glyph_rows_x2[glyph][j]; + blit_row_bits(fb, x, y + (j * 2u), row_bits, CHAR_WIDTH * 2u); + blit_row_bits(fb, x, y + (j * 2u) + 1u, row_bits, CHAR_WIDTH * 2u); + } + return; + } + + for (int j = 0; j < CHAR_HEIGHT; ++j) { + uint16_t row_bits = glyph_rows[glyph][j]; + for (int sy = 0; sy < (int)scale; ++sy) { + for (int i = 0; i < CHAR_WIDTH; ++i) { + if (row_bits & (1u << (CHAR_WIDTH - 1 - i))) { + for (int sx = 0; sx < (int)scale; ++sx) { + unsigned int px = x + (unsigned int)(i * scale + sx); + unsigned int py = y + (unsigned int)(j * scale + sy); + if (px < VIDEO_WIDTH && py < VIDEO_HEIGHT) { + uint word = px >> 5; + uint bit = 31u - (px & 31u); + fb[py][word] |= (1u << bit); + } + } + } + } + } + } +} + +void draw_text_mode(video_framebuffer_t fb, text_mode_buffer_t buffer){ + clear(fb); + for(int y=0; y<20; y++){ + for(int x=0; x<32; x++){ + draw_character(fb, START_X+x*(CHAR_WIDTH+1)*2, START_Y+y*(CHAR_HEIGHT+1)*2, 2, buffer[y][x]); + } + } + video_set_framebuffer(fb); +} diff --git a/text_mode.h b/text_mode.h new file mode 100644 index 0000000..f54c8c9 --- /dev/null +++ b/text_mode.h @@ -0,0 +1,12 @@ +#pragma once + +#include "cvideo.h" + +// Text mode config +#define CHARS_PER_LINE 32 +#define CHAR_LINES 20 + +typedef char text_mode_buffer_t[CHAR_LINES][CHARS_PER_LINE]; + +void init_font_cache(); +void draw_text_mode(video_framebuffer_t fb, text_mode_buffer_t buffer);