From 4688bdccb0259975ecd93948dd310af07ddc602a Mon Sep 17 00:00:00 2001 From: Jeremy Penner Date: Fri, 18 Jan 2019 23:02:50 -0500 Subject: [PATCH] Implement map scrolling --- testbed.c | 173 ++++++++++++++++++++++++++++++++++------------------ testbed.exe | Bin 19260 -> 30331 bytes 2 files changed, 112 insertions(+), 61 deletions(-) diff --git a/testbed.c b/testbed.c index 3693951..62a37ee 100755 --- a/testbed.c +++ b/testbed.c @@ -26,42 +26,37 @@ void vid_cleanup() { setTextMode(); } -typedef struct { - unsigned int b[16]; - unsigned int g[16]; - unsigned int r[16]; - unsigned int i[16]; -} Tile_t; - -unsigned int PAGE[] = { 0x04, 0x24 }; - #define setWriteMode(m) outport(REG_GDC, 0x05 | m << 8) void setSplitScreen(unsigned int y) { -// TODO: VGA registers?? - outport(REG_CRTC, 0x1018);// | y << 8); - outport(REG_CRTC, 0x0107); + int val; + outport(REG_CRTC, 0x18 | (y << 8)); + outp(REG_CRTC, 7); + val = inp(REG_CRTC + 1); + val &= ~0x10; + val |= (y & 0x100) >> 4; + outp(REG_CRTC + 1, val); + outp(REG_CRTC, 9); + val = inp(REG_CRTC + 1); + val &= ~0x40; + outp(REG_CRTC + 1, val); } + void unsetSplitScreen() { outport(REG_CRTC, 0xff18); outport(REG_CRTC, 0x1107); + outport(REG_CRTC, 0x0f09); } -void prepareEgaMemCopy() { - setAllPlanes(); - setWriteMode(1); -} -#define PAGE_STRIDE 40 -void blitTile(unsigned int offsetFrom, unsigned int offsetTo) { - int y; - for (y = 0; y < 16; y ++) { - VID[offsetTo] = VID[offsetFrom ++]; - VID[offsetTo + 1] = VID[offsetFrom ++]; - offsetTo += PAGE_STRIDE; - } -} #define flipPage(p) outport(REG_CRTC, 0x0c | (p << 8)) +void setDisplayOffset(unsigned int offset) { + outport(REG_CRTC, 0x0c | (offset & 0xff00)); + outport(REG_CRTC, 0x0d | (offset << 8)); +} + +#define setLogicalWidth(w) outport(REG_CRTC, 0x13 | (w << 8)) + /*** K E Y B O A R D ***/ #define KBD_INT 0x09 void interrupt (*oldKbdISR)() = NULL; @@ -325,7 +320,7 @@ fail: #define MAX_WIDTH 320 -int tifLoadEGA(FILE *f, TifImageMeta_t meta, unsigned int vidOffset, int maxY) { +int tifLoadEGA(FILE *f, TifImageMeta_t meta, unsigned int vidOffset, int maxY, unsigned int w) { int istrip; int irow; int ipixelpair; @@ -376,6 +371,7 @@ int tifLoadEGA(FILE *f, TifImageMeta_t meta, unsigned int vidOffset, int maxY) { if (y == maxY) { return y; } + out += (w - meta.width) >> 3; } } return y; @@ -439,30 +435,87 @@ int tifLoad(FILE *f, TifImageMeta_t meta, unsigned int *planeBuf, int maxY, int return y; } -/*** S C R A T C H ***/ -void paintPattern(int r, int g, int b, int t) { - int i; +/*** T I L E S ***/ +void prepareEgaMemCopy() { + setAllPlanes(); + setWriteMode(1); +} - setPlane(PLANE_R); - for (i = 0; i < 8000; i ++) { - VID[i] = r; +#define PAGE_STRIDE 42 + +void tile_init() { + setLogicalWidth(PAGE_STRIDE >> 1); +} + +void blitTile(unsigned int offsetFrom, unsigned int offsetTo) { + int y; + for (y = 0; y < 16; y ++) { + VID[offsetTo] = VID[offsetFrom ++]; + VID[offsetTo + 1] = VID[offsetFrom ++]; + offsetTo += PAGE_STRIDE; } - setPlane(PLANE_G); - for (i = 0; i < 8000; i ++) { - VID[i] = g; +} + +typedef struct { + unsigned int w; + unsigned int h; + int scrollX; + int scrollY; + unsigned int pageOffset; + unsigned char *tiles; +} TilePage_t; + +#define OFF_TILES 0x4840 + +void scrollPage(TilePage_t *page, int x, int y) { + x = min(max(x, 0), (page->w << 4) - 320); + y = min(max(y, 0), (page->h << 4) - 176); + page->scrollX = x; + page->scrollY = y; +} + +void drawPage(TilePage_t *page) { + unsigned int startX = page->scrollX >> 4; + unsigned int startY = page->scrollY >> 4; + unsigned int offsetX = page->scrollX - (startX << 4); + unsigned int offsetY = page->scrollY - (startY << 4); + unsigned int drawOffset = page->pageOffset; + unsigned int scrollOffset = drawOffset + (offsetX >> 3) + (offsetY * PAGE_STRIDE); + unsigned int x, y; + + prepareEgaMemCopy(); + + for (y = startY; y < startY + 13; y ++) { + for (x = startX; x < startX + 21; x ++) { + blitTile(OFF_TILES + page->tiles[x + (y * page->w)], drawOffset); + drawOffset += 2; + } + drawOffset += PAGE_STRIDE * 15; } - setPlane(PLANE_B); - for (i = 0; i < 8000; i ++) { - VID[i] = b; - } - setPlane(PLANE_I); - for (i = 0; i < 8000; i ++) { - VID[i] = t; + + setDisplayOffset(scrollOffset); +} + +/*** S C R A T C H ***/ + +unsigned char tiles[10000]; +TilePage_t pages[2] = { + { 100, 100, 0, 0, 0x0400, tiles }, + { 100, 100, 0, 0, 0x2620, tiles } +}; + +void fillTiles() { + unsigned int x, y, z; + z = 0; + + for (y = 0; y < 100; y ++) { + for (x = 0; x < 100; x ++) { + tiles[x + (y * 100)] = (((x + y + z) >> 2) % 3) << 5; + } } } int main() { - Tile_t tiles[16]; FILE *f; TifImageMeta_t meta; int plane; @@ -472,41 +525,39 @@ int main() { unsigned int drawOffset; unsigned int page = 0; - #define OFF_TILES 0x4200 - setEGAMode(); atexit(vid_cleanup); f = fopen("FOOTER.TIF", "rb"); meta = tifLoadMeta(f); - tifLoadEGA(f, meta, 0, 24); + tifLoadEGA(f, meta, 0, 24, 336); fclose(f); f = fopen("TILES.TIF", "rb"); meta = tifLoadMeta(f); - tifLoadEGA(f, meta, OFF_TILES, 256); + tifLoadEGA(f, meta, OFF_TILES, 256, 16); fclose(f); - mouse_init(); kbd_init(); + tile_init(); + setSplitScreen(351); + + fillTiles(); while (!keyPressed(K_ESC)) { page ^= 1; - prepareEgaMemCopy(); - drawOffset = PAGE[page] << 8; - for (y = 0; y < 11; y ++) { - for (x = 0; x < 20; x ++) { - blitTile(OFF_TILES + ((((x + y + z) >> 2) % 3) << 5), drawOffset); - drawOffset += 2; - } - drawOffset += 600; // 40 bytes per line * 15 more lines - } + x = pages[page].scrollX; + y = pages[page].scrollY; + if (keyPressed(K_LEFT)) x -= 4; + if (keyPressed(K_RIGHT)) x += 4; + if (keyPressed(K_UP)) y -= 4; + if (keyPressed(K_DOWN)) y += 4; + scrollPage(&pages[0], x, y); + scrollPage(&pages[1], x, y); + drawPage(&pages[page]); - flipPage(PAGE[page]); - setSplitScreen(16); - - kbd_wait(); +// kbd_wait(); z++; } diff --git a/testbed.exe b/testbed.exe index 4b806a8116816105b625e2cc361da91af68b9e31..0ccbaaf12abef0385d2a2d84272e2ae66a82fb99 100755 GIT binary patch delta 8360 zcmcJT4R}n~)_~XA=gi-kp9J}t1QAID6M{ygsFYxWs*b-fLHzg9rc@-+s+wqAj478M zb;vJ= zAmvEEl>9bQMzP3G*d~AVOIG(^@?%OU0}2c!8O7>i(LFX-+SM;5<;$8|o43Q~vEHZ^ z;I*xUwCnz5>P6u&erI+Cj$27-BsEcN7>o)hX^f?*k{vw*2l|}yHse>9%Isi1KE!`Y zas_glyWM4{q^*VyCQ= zbsNE~-wXwD!Su}#Oy3B86Sx8pQ*uauM8A>x_XyszLN0CBEe;Slh(q*|oyPdn)v5-c(XnuL`cG7@kRzY$zZoK>#C<2H4J zK0f+e+aK}-=iPP%B5dk;eN%DAs#fXOiW%|h8bh2I9IuucI!9*-zmCFuK~>)#<8~i$ z)&g9K7V0vk>7Zh(7EIP&B-aJ=TgXWD2^Libi*5&tQ4l`&a0FW zEV{}imn_H#x&reorPGW$OzmcOyAS17EO!2qyvX1evde#nUY%5Ly{=hB$ZE5z>r4@% zDq7uX>e|ccbH=7{bpAtUMbKH}bJncxo^6>aUFlm}XzcD(h`QGXowYt^EoFq0`rTd5 zun~QwCMe$iKP&&DO>t~|piTSO5^B>+NNv%Y<+|UTYo^lM%6fG81xfzqlb}gtXf<4nP&dg=kAvz~GPgE-&i=ZUR5Di&b9EHGT%_&y<;uQjT8}@>yV?`G zAD9{TKyJB(^9_`LseMoMI`t)Ujape_ec2HrRCp_d3jZOy+{%SLS?qDC{Z`rnjBIe~ zy;-sbMsJp0t8&S;a5iMgIfb^2Y8rxj7Cl?4v;s!2v(o3R z4h@zsyHYytb5`y4IAF+~@*IiWZU^SkDAjnK73EnJ)$6=j-j1de7dG6PR^iLONkvE* zt4QY-8@7Y4XFpfKQ|oq@I4ecad6UZMBB8tU=B%UhoRvjns-SQ>E14>0eMmDi@OY)W zzLT%%k2tGo@keoGAsFu4WNIQAw);8VAA1Kc5NnBB1Zmo27> zgGDv|y;SRNjqg$o>kLQ9AXu-Du49*ZcisN6NL%79#_Y}=bC7rPawX&!{OfJUItvDE z7Nlt9ylI8sRCB`8v8}p1>@-HGQ^I%H=ZIFY-(2N8vI&X{VWF(d!EJ^OB=5c-IfYbw?kCAPTh3rkzPU1|w)#9s$}H*S6s@tLn=n-z$?G zDkFe*B&|%6`eJnZ#03HhZS!iAu+Vr!zd-N+mSoH)qh(%g1Qyb{K41MubP|51RzxRS zZMY_ecfP`?5rL^{ynUGAk(9$S_!p}$+FRgK^>uqpM0!e)W&*uPq7FfQddj-=6!pLD zU2xc|F>yFdZ4pyOqk1l;C8aen+sU#mHkQ(3vE#_nzUdX=Oi}I49*T9QSPOZ4dP2dr zl1bv?T4$)2{1NJ;W)ovph{f|)idDXf@UHR@n6tb5!@wOQ*PQKKc_EZJKqd*|p69?_4cTkt_&`e=1}>u4OU zE^OV(I8uUw7QVRA>h{*XQk*wOjcSEZU`jhVgr*c}BVB3dhq?mo33~*C1rffpUaFNh zE=P@T6B(heGe`N(uFH|US%NeAx0zzgkqX1ShM++@NQFnK``Rpz$mJXOA0M$lN}bep zgWNBrU&`{qE$x$FQYc%_mFhV@Iag9UIl6i-*!T~?B<_cZKmdj2Z2gl&D8zk-&ZPGi z_g~wG^h_bwyTdT8Xz-pssexW_U-xKE^Y0rpeMJL(Lj!$#1N~3~{Zs?}as&PLJv}Q_ zNm*;${TfF!(32YIog3(VN^SKvKhy2%W&Qp%GCM$pMB|B$pYI;Zdk;OwbxS!lot!ZAi=!l(dB|Fp$APGLa#L zodqP9#>3ozjO$o@7WPS-{YlavaWHfz>C5ma$wY=8B>kIGl0h@0p1HFF`X+3!0o+Np?1tl(Ozz&ANBnKFVkbJ=~l%yn{l3^roG7Kj<&5%tZ^C*uX z`3H~WNGjJjnZQjF$&gbIqbRErSv)YBOkFr{Og-chjAP4Kk`Ap|BU!)`U_8kgh6yA$ z7$%YcEEn3=na?np^dg3*2xc?;f?*!PFhTG#!5oGXg1HRy35ps1Ot6Sy z0S8#Yw2(|I85R+YlnA^8ISh*lMl<*bav5H!hb1)UG|N(&;L{wG^%q(eHyDesM{tQ@KfwdS~ET)(E8&a1X_O_BhdQe-?hi{ zFB|kZ6??GtU(jydu=NS#MaBQK*6*ZE|7wutA2qsFsP%_w*RN}I>rh+2q&~kHYIK|b zpwa(_W_V~6>)LUgUZ#!f2?DKU+_75k{p&1kew`s7t<+gMnC_MOH(6)tgsSsVLM`;1 zsc+&p1ln+(BhV)5c>--*za`Md^*aJ>TrUu4HMH& zfMXk3PKNJ*Zn7=(YxWSOY!0f!?Tr-uOY? z15F+@&~Ex*1tJ^h^bz{N6Wu_!H_&4m=&=vzv<{m-V4&?7qTXpUD2DEbvGAy93cW-e zT@mALqU7=D!Y!Am9N2L@^$Dd z-+(9N3K%Zmf-&-K7$^S*6XZ&mEZ>1CauqxySHrV%Gn^qOcyOVdh^ysR_@SJPyX7|c zvD_Aq%MScWPQmMPI~2P1Xws!(n63lbbsaHI*9lwe9>RxpX_%qwj01HK<8WO%j@Na; zXLXNYk$>AIT@QR$mw{V#kKtimZ~RP`g(q|a@U-p;ysR69H+3$&qw@?wSw9pl z`eE2kKOB4MN8nKXNF1ei<3xQ9KCd5z3-qILseTN;q0hyg`muOYKMsGL`*PD z!iNl#G1KrA4lzu@35KU}hT$1pXvo7i4bNh!VHzGaOvht}QFzXfj{(C>EH@P3EkhAj z8AhPNV;q9<#uu=waW-Zc=U}F>82cMbaFB66jxzokCm9#u4C6waYxJVexENO(m*88* zrTDJ#FL>Cv49^>1#p^~De>1K?tLZh2G_Ax$(_b;ev6N3Oi|v)j>;D7u6%&~ zln-&Z@)1r@Ud8Fk5?rK|;yPtJzN_rS&59pCPvax zCFN86r*asw@oDonxY2wLx0%mlnfW{1W4?d~%op)X z^CkS&{5{?<|A1im5lxn#G1(Hpww5c{$#NCDTFNn(c!K3RK1cRKO9hr#e#OO>-*Kts zF0QZyakT}+S__Kz==UV)=Pk0hY|)E=#UgH4JT`I55-zGNjl^9`6QQ$4h)8RsNV7(X zAy&I^TVupjYphskZ7SZgHWwdS1T(Y(i<<_>M(&`Xs zOA!`ZJJHP6UbM4y5Rcj(5`An5BFpx$7-CBo+5CB?Jza&OUhQG^te`!apWb3Jyhhu% zMBGn|W0Ur5q#cDH9m)H&%g|1)>8EICSw(Ch?F{WW^^Yut{OKzg3{u;AGi@%@U^T@z zlO1)&} z94&JidK_{VyiUzx&yI30c&n}fGh{cu4IVv2Q^qWLDE>`rBhdHq^I{v_Rk>OxbTh<`QTi>onT|aEkXx!Bh@aH?XBwx7W8}{VW`E;&y2t6YQ#8`_J6S@{W~GG&@sxg` z@Ie}Sj(U{F8YNJAObLT;wG$NRd)x&VsMbkjzoa}4-)jRypS|YgaG5xn?5XCZ@Xxx| z>@lB&0GPBd<^$$qT1-xsT5{l5@^=uguP7*wvF_KQ-t5^LA5vp`B_~j!L=Ijovoqa~nuYXwJD@tC zR|iuszEjeLC{um8SEmf>*zneLF`!=>R!4&p@;yCNW>82s)V&GRZM~w^ie63B(q48C zeO*!5b$mN{hkJ27g{AK-vi*(2zsm#n9_1EqWZP!;d!Lgn)Ue_YxOE@0`VqTqWpx{? z|HVlutJ~S{?@WBLhLfCp#i34d@^u91#i#lI5_}1xwL$77bya3w_{_-#eWuQQE^qt` zPd@b&DQb(}vz_^qpBtY)x$mFGKQZ~)-qW9Z0mc?g&6_-S>a?i^Fm^)T)PiB8!q}M; z^QY(KxyJvAcvAlOSs{9AYF-}a&n(^0J6=*hbH?hPBbS-#56)dWjx$qToi#!!8lMN# zL$8$j-Naey?S3QGq5V6n6s~=| c3dZLbxI;f9VYd24|73mMWUg`P-u@o^AL&nz*8l(j delta 7244 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