From 97ca74577a01ccd22d001dbff808e5d3e354d6c5 Mon Sep 17 00:00:00 2001 From: Roman Boykov Date: Mon, 16 Feb 2026 16:08:13 +0300 Subject: [PATCH] 3 CP/M, 3/MON, 1/Test source codes --- CPM_v2.2_r8_099942fc/.gitignore | 10 + CPM_v2.2_r8_099942fc/.vscode/extensions.json | 9 + CPM_v2.2_r8_099942fc/.vscode/tasks.json | 36 + .../BIN/CPM_REL_8_099942fc.BIN | Bin 0 -> 8192 bytes CPM_v2.2_r8_099942fc/README.md | 11 + CPM_v2.2_r8_099942fc/bdos.asm | 3041 +++++++++ CPM_v2.2_r8_099942fc/bios.asm | 823 +++ CPM_v2.2_r8_099942fc/ccp_ram.asm | 1525 +++++ CPM_v2.2_r8_099942fc/ccp_rom.asm | 901 +++ CPM_v2.2_r8_099942fc/cpm.asm | 44 + CPM_v2.2_r8_099942fc/cpm_vars.inc | 219 + CPM_v2.2_r8_099942fc/equates.inc | 149 + CPM_v2.2_r8_099942fc/io.inc | 132 + CPM_v2.2_r8_099942fc/mon_entries.inc | 35 + CPM_v2.2_r8_099942fc/ram.inc | 49 + CPM_v2.2_r8_2e4a7b71/.gitignore | 10 + CPM_v2.2_r8_2e4a7b71/.vscode/extensions.json | 9 + CPM_v2.2_r8_2e4a7b71/.vscode/tasks.json | 36 + .../BIN/CPM_REL_8_2e4a7b71.BIN | Bin 0 -> 8192 bytes CPM_v2.2_r8_2e4a7b71/README.md | 11 + CPM_v2.2_r8_2e4a7b71/bdos.asm | 3041 +++++++++ CPM_v2.2_r8_2e4a7b71/bios.asm | 823 +++ CPM_v2.2_r8_2e4a7b71/ccp_ram.asm | 1525 +++++ CPM_v2.2_r8_2e4a7b71/ccp_rom.asm | 901 +++ CPM_v2.2_r8_2e4a7b71/cpm.asm | 44 + CPM_v2.2_r8_2e4a7b71/cpm_vars.inc | 219 + CPM_v2.2_r8_2e4a7b71/equates.inc | 149 + CPM_v2.2_r8_2e4a7b71/io.inc | 132 + CPM_v2.2_r8_2e4a7b71/mon_entries.inc | 35 + CPM_v2.2_r8_2e4a7b71/ram.inc | 49 + CPM_v2.2_r8_bc0695e4/.gitignore | 10 + CPM_v2.2_r8_bc0695e4/.vscode/extensions.json | 9 + CPM_v2.2_r8_bc0695e4/.vscode/tasks.json | 36 + .../BIN/CPM_REL_8_HEXREAD.BIN | Bin 0 -> 8192 bytes CPM_v2.2_r8_bc0695e4/README.md | 38 + CPM_v2.2_r8_bc0695e4/bdos.asm | 3041 +++++++++ CPM_v2.2_r8_bc0695e4/bdos.inc | 54 + CPM_v2.2_r8_bc0695e4/bios.asm | 823 +++ CPM_v2.2_r8_bc0695e4/ccp_ram.asm | 1529 +++++ CPM_v2.2_r8_bc0695e4/ccp_rom.asm | 882 +++ CPM_v2.2_r8_bc0695e4/cpm.asm | 46 + CPM_v2.2_r8_bc0695e4/cpm_vars.inc | 219 + CPM_v2.2_r8_bc0695e4/equates.inc | 149 + CPM_v2.2_r8_bc0695e4/io.inc | 132 + CPM_v2.2_r8_bc0695e4/mon_entries.inc | 35 + CPM_v2.2_r8_bc0695e4/ram.inc | 49 + CPM_v2.2_r8_ddd05ed7/.gitignore | 10 + CPM_v2.2_r8_ddd05ed7/.vscode/extensions.json | 9 + CPM_v2.2_r8_ddd05ed7/.vscode/tasks.json | 36 + .../BIN/CPM_REL_8_ddd05ed7.BIN | Bin 0 -> 8192 bytes CPM_v2.2_r8_ddd05ed7/README.md | 11 + CPM_v2.2_r8_ddd05ed7/bdos.asm | 3041 +++++++++ CPM_v2.2_r8_ddd05ed7/bios.asm | 824 +++ CPM_v2.2_r8_ddd05ed7/ccp_ram.asm | 1525 +++++ CPM_v2.2_r8_ddd05ed7/ccp_rom.asm | 901 +++ CPM_v2.2_r8_ddd05ed7/cpm.asm | 44 + CPM_v2.2_r8_ddd05ed7/cpm_vars.inc | 219 + CPM_v2.2_r8_ddd05ed7/equates.inc | 149 + CPM_v2.2_r8_ddd05ed7/io.inc | 132 + CPM_v2.2_r8_ddd05ed7/mon_entries.inc | 35 + CPM_v2.2_r8_ddd05ed7/ram.inc | 49 + MON_Turbo/.gitignore | 10 + MON_Turbo/.vscode/extensions.json | 9 + MON_Turbo/.vscode/tasks.json | 65 + MON_Turbo/BIN/MON_Turbo_bcac5ca0.BIN | Bin 0 -> 8192 bytes MON_Turbo/README.md | 15 + MON_Turbo/bios_entries.inc | 41 + MON_Turbo/equates.inc | 149 + MON_Turbo/io.inc | 132 + MON_Turbo/mon_entries.inc | 35 + MON_Turbo/ram.inc | 49 + MON_Turbo/tm_vars.inc | 90 + MON_Turbo/turbo_mon.asm | 4787 ++++++++++++++ MON_v8_3edeb015/.gitignore | 10 + MON_v8_3edeb015/BIN/MON_V8_3edeb015.BIN | Bin 0 -> 8192 bytes MON_v8_3edeb015/README.md | 17 + MON_v8_3edeb015/bios_entries.inc | 41 + MON_v8_3edeb015/equates.inc | 149 + MON_v8_3edeb015/font-6x7.inc | 165 + MON_v8_3edeb015/io.inc | 132 + MON_v8_3edeb015/m_vars.inc | 101 + MON_v8_3edeb015/monitor.asm | 5594 +++++++++++++++++ MON_v8_3edeb015/ram.inc | 49 + MON_v8_c4eec374/.gitignore | 10 + MON_v8_c4eec374/.vscode/extensions.json | 9 + MON_v8_c4eec374/.vscode/tasks.json | 34 + MON_v8_c4eec374/BIN/MON_V8_c4eec374.BIN | Bin 0 -> 8192 bytes MON_v8_c4eec374/README.md | 16 + MON_v8_c4eec374/bios_entries.inc | 41 + MON_v8_c4eec374/equates.inc | 149 + MON_v8_c4eec374/font-6x7.inc | 165 + MON_v8_c4eec374/io.inc | 132 + MON_v8_c4eec374/m_vars.inc | 101 + MON_v8_c4eec374/monitor.asm | 5593 ++++++++++++++++ MON_v8_c4eec374/ram.inc | 49 + Test/.vscode/extensions.json | 9 + Test/.vscode/launch.json | 94 + Test/.vscode/tasks.json | 41 + Test/README.md | 7 + Test/test_O240.asm | 1249 ++++ 100 files changed, 47344 insertions(+) create mode 100644 CPM_v2.2_r8_099942fc/.gitignore create mode 100644 CPM_v2.2_r8_099942fc/.vscode/extensions.json create mode 100644 CPM_v2.2_r8_099942fc/.vscode/tasks.json create mode 100644 CPM_v2.2_r8_099942fc/BIN/CPM_REL_8_099942fc.BIN create mode 100644 CPM_v2.2_r8_099942fc/README.md create mode 100644 CPM_v2.2_r8_099942fc/bdos.asm create mode 100644 CPM_v2.2_r8_099942fc/bios.asm create mode 100644 CPM_v2.2_r8_099942fc/ccp_ram.asm create mode 100644 CPM_v2.2_r8_099942fc/ccp_rom.asm create mode 100644 CPM_v2.2_r8_099942fc/cpm.asm create mode 100644 CPM_v2.2_r8_099942fc/cpm_vars.inc create mode 100644 CPM_v2.2_r8_099942fc/equates.inc create mode 100644 CPM_v2.2_r8_099942fc/io.inc create mode 100644 CPM_v2.2_r8_099942fc/mon_entries.inc create mode 100644 CPM_v2.2_r8_099942fc/ram.inc create mode 100644 CPM_v2.2_r8_2e4a7b71/.gitignore create mode 100644 CPM_v2.2_r8_2e4a7b71/.vscode/extensions.json create mode 100644 CPM_v2.2_r8_2e4a7b71/.vscode/tasks.json create mode 100644 CPM_v2.2_r8_2e4a7b71/BIN/CPM_REL_8_2e4a7b71.BIN create mode 100644 CPM_v2.2_r8_2e4a7b71/README.md create mode 100644 CPM_v2.2_r8_2e4a7b71/bdos.asm create mode 100644 CPM_v2.2_r8_2e4a7b71/bios.asm create mode 100644 CPM_v2.2_r8_2e4a7b71/ccp_ram.asm create mode 100644 CPM_v2.2_r8_2e4a7b71/ccp_rom.asm create mode 100644 CPM_v2.2_r8_2e4a7b71/cpm.asm create mode 100644 CPM_v2.2_r8_2e4a7b71/cpm_vars.inc create mode 100644 CPM_v2.2_r8_2e4a7b71/equates.inc create mode 100644 CPM_v2.2_r8_2e4a7b71/io.inc create mode 100644 CPM_v2.2_r8_2e4a7b71/mon_entries.inc create mode 100644 CPM_v2.2_r8_2e4a7b71/ram.inc create mode 100644 CPM_v2.2_r8_bc0695e4/.gitignore create mode 100644 CPM_v2.2_r8_bc0695e4/.vscode/extensions.json create mode 100644 CPM_v2.2_r8_bc0695e4/.vscode/tasks.json create mode 100644 CPM_v2.2_r8_bc0695e4/BIN/CPM_REL_8_HEXREAD.BIN create mode 100644 CPM_v2.2_r8_bc0695e4/README.md create mode 100644 CPM_v2.2_r8_bc0695e4/bdos.asm create mode 100644 CPM_v2.2_r8_bc0695e4/bdos.inc create mode 100644 CPM_v2.2_r8_bc0695e4/bios.asm create mode 100644 CPM_v2.2_r8_bc0695e4/ccp_ram.asm create mode 100644 CPM_v2.2_r8_bc0695e4/ccp_rom.asm create mode 100644 CPM_v2.2_r8_bc0695e4/cpm.asm create mode 100644 CPM_v2.2_r8_bc0695e4/cpm_vars.inc create mode 100644 CPM_v2.2_r8_bc0695e4/equates.inc create mode 100644 CPM_v2.2_r8_bc0695e4/io.inc create mode 100644 CPM_v2.2_r8_bc0695e4/mon_entries.inc create mode 100644 CPM_v2.2_r8_bc0695e4/ram.inc create mode 100644 CPM_v2.2_r8_ddd05ed7/.gitignore create mode 100644 CPM_v2.2_r8_ddd05ed7/.vscode/extensions.json create mode 100644 CPM_v2.2_r8_ddd05ed7/.vscode/tasks.json create mode 100644 CPM_v2.2_r8_ddd05ed7/BIN/CPM_REL_8_ddd05ed7.BIN create mode 100644 CPM_v2.2_r8_ddd05ed7/README.md create mode 100644 CPM_v2.2_r8_ddd05ed7/bdos.asm create mode 100644 CPM_v2.2_r8_ddd05ed7/bios.asm create mode 100644 CPM_v2.2_r8_ddd05ed7/ccp_ram.asm create mode 100644 CPM_v2.2_r8_ddd05ed7/ccp_rom.asm create mode 100644 CPM_v2.2_r8_ddd05ed7/cpm.asm create mode 100644 CPM_v2.2_r8_ddd05ed7/cpm_vars.inc create mode 100644 CPM_v2.2_r8_ddd05ed7/equates.inc create mode 100644 CPM_v2.2_r8_ddd05ed7/io.inc create mode 100644 CPM_v2.2_r8_ddd05ed7/mon_entries.inc create mode 100644 CPM_v2.2_r8_ddd05ed7/ram.inc create mode 100644 MON_Turbo/.gitignore create mode 100644 MON_Turbo/.vscode/extensions.json create mode 100644 MON_Turbo/.vscode/tasks.json create mode 100644 MON_Turbo/BIN/MON_Turbo_bcac5ca0.BIN create mode 100644 MON_Turbo/README.md create mode 100644 MON_Turbo/bios_entries.inc create mode 100644 MON_Turbo/equates.inc create mode 100644 MON_Turbo/io.inc create mode 100644 MON_Turbo/mon_entries.inc create mode 100644 MON_Turbo/ram.inc create mode 100644 MON_Turbo/tm_vars.inc create mode 100644 MON_Turbo/turbo_mon.asm create mode 100644 MON_v8_3edeb015/.gitignore create mode 100644 MON_v8_3edeb015/BIN/MON_V8_3edeb015.BIN create mode 100644 MON_v8_3edeb015/README.md create mode 100644 MON_v8_3edeb015/bios_entries.inc create mode 100644 MON_v8_3edeb015/equates.inc create mode 100644 MON_v8_3edeb015/font-6x7.inc create mode 100644 MON_v8_3edeb015/io.inc create mode 100644 MON_v8_3edeb015/m_vars.inc create mode 100644 MON_v8_3edeb015/monitor.asm create mode 100644 MON_v8_3edeb015/ram.inc create mode 100644 MON_v8_c4eec374/.gitignore create mode 100644 MON_v8_c4eec374/.vscode/extensions.json create mode 100644 MON_v8_c4eec374/.vscode/tasks.json create mode 100644 MON_v8_c4eec374/BIN/MON_V8_c4eec374.BIN create mode 100644 MON_v8_c4eec374/README.md create mode 100644 MON_v8_c4eec374/bios_entries.inc create mode 100644 MON_v8_c4eec374/equates.inc create mode 100644 MON_v8_c4eec374/font-6x7.inc create mode 100644 MON_v8_c4eec374/io.inc create mode 100644 MON_v8_c4eec374/m_vars.inc create mode 100644 MON_v8_c4eec374/monitor.asm create mode 100644 MON_v8_c4eec374/ram.inc create mode 100644 Test/.vscode/extensions.json create mode 100644 Test/.vscode/launch.json create mode 100644 Test/.vscode/tasks.json create mode 100644 Test/README.md create mode 100644 Test/test_O240.asm diff --git a/CPM_v2.2_r8_099942fc/.gitignore b/CPM_v2.2_r8_099942fc/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/CPM_v2.2_r8_099942fc/.vscode/extensions.json b/CPM_v2.2_r8_099942fc/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/CPM_v2.2_r8_099942fc/.vscode/tasks.json b/CPM_v2.2_r8_099942fc/.vscode/tasks.json new file mode 100644 index 0000000..f71ba83 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make CPM (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=cpm.sld", + "--sym=cpm.labels", + "--lstlab=sort", + "--lst=cpm.lst", + "--raw=cpm.obj", + "--fullpath", + "cpm.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + } + + ] +} \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/BIN/CPM_REL_8_099942fc.BIN b/CPM_v2.2_r8_099942fc/BIN/CPM_REL_8_099942fc.BIN new file mode 100644 index 0000000000000000000000000000000000000000..9217b19cc3032d3e903d2ece3d1452b99bb1dbb2 GIT binary patch literal 8192 zcmdT}dwf&ZeZRuCY#CcdI6!R^tZR8m5G?@(oJ6$+Ha3PB5ZEMyPaB-3c{XXxqpd8! zvPlR@SU+pJ7MiA6mPeZ`C7BZWajmPwv+hNy?zN6B*^-RkR#thVYD;KKn=IS)zURu2 zuGwGv?2k?0bI;>E z+U@n*_g3#PHr6%ORX1+gW;8PYYrh;1!!+v`iCIj5eAnZbZ29DGJ+@pCg%L%7Jnu=k zxS%PC(S-PxN1qSSX1yLI$zm+O^;Syy0#Wq1Ex+*^h*uo&=$D|WH_&1!3fej2(HEvC zaFYHy@egX}Cm#JWRQ1a#a%S=;mz9`S(4Bx4dM)LwkJQDoe;q+n;!Zvz>-)58J_LJ4j6r%X=#r(?&Wpu7YAR zrm{I6@|K4Po$?!tFrR(G=5o~@=8tOFY-^U!FR>8PQn!ZaOkSn5oQ#Z>nT5WM3^jB!g68@u8 zf`HAZJuby4mvwRYQw41j3%YEZAaabRDJ$lJig8@m6;!H%N^?+I8&s@8<;y{(GN{;s zk~-X9DB8Q8Y6+K1Hiji$vcD^tbXz|1nxFBch`lRScG{Cl6m#;kUDKQE3yT%2{9IQF zD07HsyIKKvuuz*r)^*9=E|X#w-t4N_B%bSX6lMv}chTWvVb{3EnPkhO$<; z*i}deE~@Zj6n;X5532AV0e&V8e-ZGe3MNbV9jJK5lh7)ir*y9HM%ZUDB;*oQgJV81!m0i~9t7Hx*xHcd=p= z_I5iZH0<4Z;w{~7OSWhIId8yOir-v8Gg;P2dOenNUcYm$z;ek8jaaiiobY@%pr~}i zogY*~&^8;D_U)ZeA-T|rUJ84l>%6YZ`jM9te%4FYcC-HO=H*OLk<0X8%SA^|DR&R| zHn*hrZ+e*t_V?c@V>~S5xu}`x7T@qR)>YSnpGJElqxpz-?6MoTZr@SI0H=yj*ToKc zAqJ}WTY60KbxQ8Au4`ZG+^nGMYR%5h?trrC-xK(&&lhrfB!FVflrP-U(<*5*TuV(* zO-+vkD?b5UyCk=Tcq#H=6`ZFp2!s2xR@wrwJlWfWr75!S^=KDYY=Y>X=~>0Z$VE@+ zSKV?6XZ@|WprByxUb`)qH1w<(FW@4_R>tNst_YzXNGt&1_#rby2rCLP+CyGVSMk+q zS}|v?9oRqVxqL;*5dPF-(=Bw#dd_CVl+0EoZ&=SMI$?~?6~}rm!wH2Fj{MRiKHn1& zPf^h;i-j6a)5wuGs9xZR8-*vjVKw{d2Sx-w5L?J4PbJ94l*;$^j@xKKSH>X_xJW5MDrlE0w9pT*-Mwy(^ZHnpfoRU>yL)@n z-X;bNDobF0&DKFL3=$kK>lB~$k}I8Rhj6}^T9W)MA}riubtk+C!-lw*`k$^V2AU)6 zlU}k}mH4AxmeC|r3nD-;)_4=}t0DvAY5T$P7lsdc#YAtSN;u>NNsue(Pr5+0Z3(%z zE8((CdDY$It2BsdSsK4{JYdj9NwaxP;ySOEF`QsrOF7x@%`_E?TfD6n=n>u=qw$+C z8tU9>G7;2O9cqqx(|p&!Gk4glYnd(fo$2+#W84TLdcX=cfzR))<>lq-Hr;e*&F2UI zXFch-CK`f24*o27F8FRRRaV2Nj#&=!*dfYl`P<9%{QAwj+j5k*wDRn&v7}R39d2Oj zgK7vmV7ki;_=_3wp!}RPC@+l{#G~?W_ggS|6<8zuI(Z!WQp+=%#N}F4<yNM(oB;B}Z^d8zm*Tzk-pRSa$e1>5BdU*(P27<7T@P(P*2bH==1>%+F;D zXC;ZeMLY81`Wz932}6{h#XOtK_k~qZ(JSfm(XY=Hw@JB5c`zeDS@DY_6wZ>%LrJ?# zNi7pdg4Mb9gkQ;*{j!oviyUFOjHAURDT~7Dc=4dDvxFevyOy#Nm9NbG|LHVE)Q<|h7;08@>5CNB`eE>M`iM^ln`MJONH0v z02=;8GJpk7%cfmfK-w-P{9>z|q#~3rOF(cIRA4@<<2MRFma&}4kr_zZRY;eT(JH(z zD;fyjJAO$@9K=x&646ScW`HbL@+Fc~{3i68H#1E#W_yvbB%r^Ox=sj_eIY!k1~ zAxj6$ck(IrE4;pd*6-zyvktyuo|Eq#s9fW2h1mV{UY*qsH~kYJH@l@P}Ww!Qtz_!benhnp!J2}_Xm>ZukgptG#`C&Ut*2; zhT1F|A0BX?X?+o&RKg^_KadE-k}31Ud{OOAvML?{f+MEG5n#i|@BD$i^@V!#H~17; z7l#PgIC~Gj%i6|srP$j;^|qX|cI$CIUoH)CE5zlY4hfcHSi%(PtPW^rL+J4v7)eLv zBoR%>u4M>IiI&Xmd|Q`fy@y9-b(PQ*Dm%ej5Ab$iexCM^=l$P=Kn8)iC52A-}V`rM2rJ$z0E?dVD+7Hm8x;dMa8n7%Hph&G+%9 zlzAtWi$qvr&@n}>6=Al3Dug6LK{j7iO%rc@l2=c&R1|suN#u(JzKimY9S#Y)qgale zH649PA~obf;o&t$q+@4Vj=uPiq@OpR!tjGUxtoLogb$hbp?Qig^5@Ami-&&2X1&-Dk$sR8mM;upV0oO9Wg&HJ46fK_U5 zr5By2-?g`@&3f~GUgyfbUML!rV&r2or}*D3SSH*!2qGaS7(+mBh~x)js!22q29mq9 zjJRhoKL9e5&UfT{2jK*5-#`9*v~)0G@{708wmt*)ZK~Y~ZYKRQ) z8^Utd!#u3o$;n%Xij__BrlD3u%1uK`Ua?#;bat*?_-IhvG!)oH)4s2FAj4BaAqKMkYBj*>OlG-`RvN#61l55MY% z-^_LNUt2`81VM!8=n^{p64j>8gFtosp}22`v%pH~C8Rkbj{VP!SJD$n|033c0CeWDQ^io5s?@gVJyE=tiVt zkMVuuZ$^|3p%1VTH-e+J7AFg|tRYe#l2O?iB0n5JbP&&k#H@id28Lo#c_g_cjY{bl z&KgM1(lf&PVZ>|8Azs5UoUBCbixIf7L7W(B>k<|Z+ZKT$L}{u7=tB+m)2%waHqQU6ue&kQ>@=ojVZ3$G4ep?1>lxQw%iI83(#4Zh?| z2x5UsU7X+_u7Y?f7|6j5DF#4$ie(X>=i0BhR+wBX5ZPSm>v#QS)8k+K(ps&sYlJmI z!_}GfB6E{)E2`mJYQdfPF9=IvIg`(#i)BDOH3ZNm05A<~mN;ZOk+Es12HH3!!m zq7MDWNJ8CTjTDB8;*jYejvMCfSGPc1hE@(FLb(HNr5DhimybITWxmgFvi6S5aqQ@cG%D_IoE7bGA5Z4_Nbg!M^%pz-y(>|a7X}BR^FoXNb`_* zevnRWkI>Q|S-=O6!sr+s1sme8DS03xg>|ucVoO06Hhz>8PEq0Rkg}vhqNFYv!4INT+6rBQp~nRuuYhOteU~)#QguQpSUwF{zF{U&WgHOF>h8} zHY+Zj6$@s?H%7%1qar^l{?n*t6Uzin3W<~QX>YJJHqVS^K zyUn-{|65%V@(-uu=FN=Zt~=9pScJBtB8d#oH{z%pkbBGt5O&s&#E~=ZMqZVijjv}k z?4x7kz!>OZF*!AcE6sOCtvlkD8b-Q??tB`O@fetXjV_1oG4wFeAIEJ$6Jr+67U8%p zY1zu#IxG!5JIxzD9@E^;6gy0X6!zC~4Q}b>ahqsi7&aqbreQ2dmTLJjxabO>zQEuf zvu?$`p={L9HwGHm8))a`m~~qm_m0~}4MgqTh+gX*ab=Nw_b46R!f?U^bWl{wtL;e& z?6&OmS?oS*CSw;F52Yku_|B;0mf$*0B_nL9^>Ok~Mvu*DgrAJ2VIM&@2S39!UnE)S zaPO#fmyZ*A>A0R@>wSkLb{mRE?k%hHp;V!SMHfhFl&qR{r<+=}$)bq~IK5MpDNIAP zC}qYxl6yfSt1e)Zw)?P|-08FI^}%TG@` z+kBQrpVbc1p14qz9%Og>`h+Jg0D#?-u5I(NjcRR!ODP{eb^#8v$5)BzZY-p(VMlW> znoSfVSIU)IcKFzxK39c?lYemmE3(^xpkGCLN4f@p24A22%L`N@=WiOLWNlZ;y2EGP z;j=XOta~xAVa#pWMX}Ch(A-%1XJFn}IoEes?}RvW$H4V2U#hr}9szR4tYBATZSwVV z{d6(`@+L@W^O*j1a?31;RO_e6Ir zGe(Tkrlym*+Wh&?nR!i)Hgl0KJ1Yaj>TjQKh3}8PQQv3n8+^CCr+x3U_C{aFdrj|M zo2bwDi$(u`H{pJ_m%YfZ@8Zeo3ASnCv&4E6eF{+X=ldGB@2z8tCur(FLrwi>;qZCK z1WociIr3DO@C#={38E}h=l#0r@D_$ETp=ROwi=&sfmS@ zC^Ey?LSNw1`R_>V`hRFqtSEtpumfB%t zrb3#~9uG{om!eWW9-W|%2rH({uQ1piIpKE`+6?gz6WYw3?&InuI!m}Rp^oHD+OkFc zq^;l;xp2}%yX2C|)hY7}d=Y3;b5FoeUYOK3(65@Cj;s{FKM`S1Pm))sRV0m*=DN6e zc(Te|AD0hLnzzT*0C;%vIAUhYB>S)igKT&aMVroHTtoOg%>(D>Zf&fqOQ)fc4ET0KQ`M=PiBdIQYAXzE*=cto??GA z4e7FRudN(uBa`FvDSwCpbi~qxYy~0XQ`X;jaZSyi#B`49&9EF#b)}wk7zBpBGpJMW90UltWrphh>oXN=O?Uwfunctab + LD E, A + LD D, 0 + ADD HL, DE + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) ; DE=functab(func) + LD HL, (CPM_VARS.bdos_info) ; restore input parameter + EX DE, HL + JP (HL) ; dispatch function + +; ------------------------------------------------------- +; BDOS function handlers address table +; ------------------------------------------------------- +functab: + DW BIOS.wboot_f ; fn0 + DW bdos_con_inp ; fn1 + DW bdos_outcon ; fn2 + DW bdos_aux_inp ; fn3 + DW BIOS.punch_f ; fn4 + DW BIOS.list_f ; fn5 + DW bdos_dir_con_io ; fn6 + DW bdos_get_io_byte ; fn7 + DW bdos_set_io_byte ; fn8 + DW bdos_print_str ; fn9 + DW bdos_read ; fn10 + DW bdos_get_con_st ; fn11 + DW bdos_get_version ; fn12 + DW bdos_reset_disks ; fn13 + DW bdos_select_disk ; fn14 + DW bdos_open_file ; fn15 + DW bdos_close_file ; fn16 + DW bdos_search_first ; fn17 + DW bdos_search_next ; fn18 + DW bdos_rm_file ; fn19 + DW bdos_read_file ; fn20 + DW bdos_write_file ; fn21 + DW bdos_make_file ; fn22 + DW bdos_ren_file ; fn23 + DW bdos_get_login_vec ; fn24 + DW bdos_get_cur_drive ; fn25 + DW bdos_set_dma_addr ; fn26 + DW bdos_get_alloc_addr ; fn27 + DW bdos_set_ro ; fn28 + DW bdos_get_wr_protect ; fn29 + DW bdos_set_attr ; fn30 + DW bdos_get_dpb ; fn31 + DW bdos_set_user ; fn32 + DW bdos_rand_read ; fn33 + DW bdos_rand_write ; fn34 + DW bdos_compute_fs ; fn35 + DW bdos_set_random ; fn36 + DW bdos_reset_drives ; fn37 + DW bdos_not_impl ; fn38 (Access Drive) + DW bdos_not_impl ; fn39 (Free Drive) + DW bdos_rand_write_z ; fn40 + +; ------------------------------------------------------- +; Report permanent error +; ------------------------------------------------------- +bdos_persub: + LD HL, permsg ; = 'B' + CALL bdos_print_err + CP CTRL_C + JP Z, warm_boot + RET + +; ------------------------------------------------------- +; Report select error +; ------------------------------------------------------- +bdos_selsub: + LD HL, selmsg ;= 'S' + JP bdos_wait_err + +; ------------------------------------------------------- +; Report write to read/only disk +; ------------------------------------------------------- +bdos_rodsub: + LD HL, rodmsg ;= 'R' + JP bdos_wait_err + +; ------------------------------------------------------- +; Report read/only file +; ------------------------------------------------------- +bdos_rofsub: + LD HL, rofmsg ;= 'F' + +; ------------------------------------------------------- +; Wait for response before boot +; ------------------------------------------------------- +bdos_wait_err: + CALL bdos_print_err + JP warm_boot + +; ------------------------------------------------------- +; Error messages +; ------------------------------------------------------- +dskmsg: + DB 'Bdos Err On ' +dskerr: + DB " : $" +permsg: + DB "Bad Sector$" +selmsg: + DB "Select$" +rofmsg: + DB "File " +rodmsg: + DB "R/O$" + +; ------------------------------------------------------- +; Print error to console, message address in HL +; ------------------------------------------------------- +bdos_print_err: + PUSH HL ; save second message pointer + CALL bdos_crlf + ; set drive letter to message + LD A, (CPM_VARS.bdos_curdsk) + ADD A, 'A' + LD (dskerr), A + + LD BC, dskmsg ; print first error message + CALL bdos_print + POP BC + CALL bdos_print ; print second message + +; ------------------------------------------------------- +; Console handlers +; Read console character to A +; ------------------------------------------------------- +bdos_conin: + LD HL, CPM_VARS.bdos_kbchar + LD A, (HL) + LD (HL), 0x00 + OR A + RET NZ + ;no previous keyboard character ready + JP BIOS.conin_f + +; ------------------------------------------------------- +; Read character from console with echo +; ------------------------------------------------------- +bdos_conech: + CALL bdos_conin + CALL bdos_chk_ctrl_char + RET C + PUSH AF + LD C, A + CALL bdos_outcon + POP AF + RET + +; ------------------------------------------------------- +; Check for control char +; Onp: A - character +; Out: ZF if cr, lf, tab, or backspace +; CF if code < 0x20 +; ------------------------------------------------------- +bdos_chk_ctrl_char: + CP ASCII_CR + RET Z + CP ASCII_LF + RET Z + CP ASCII_TAB + RET Z + CP ASCII_BS + RET Z + CP ' ' + RET + +; ------------------------------------------------------- +; check console during output for +; Ctrl-S and Ctrl-C +; ------------------------------------------------------- +bdos_conbrk: + LD A, (CPM_VARS.bdos_kbchar) + OR A + JP NZ, .if_key ; skip if key pressed + CALL BIOS.const_f + AND 0x1 + RET Z ; return if no char ready + CALL BIOS.conin_f + CP CTRL_S + JP NZ, .no_crtl_s + ; stop on crtl-s + CALL BIOS.conin_f + CP CTRL_C + ; reboot on crtl-c + JP Z, warm_boot + XOR A + RET +.no_crtl_s: + LD (CPM_VARS.bdos_kbchar), A ; character in A, save it +.if_key: + LD A, 0x1 ; return with true set in accumulator + RET + +; -------------------------------------------------- +; Out char C to screen (and printer) +; -------------------------------------------------- +bdos_conout: + LD A, (CPM_VARS.bdos_no_outflag) ; if set, skip output, only move cursor + OR A + JP NZ, .compout + ; check console break + PUSH BC + CALL bdos_conbrk + POP BC + ; output c + PUSH BC + CALL BIOS.conout_f + POP BC + + ; send to printer (list) device + PUSH BC + LD A, (CPM_VARS.bdos_prnflag) + OR A + CALL NZ, BIOS.list_f + POP BC + +; ------------------------------------------------------- +; Update cursor position +; ------------------------------------------------------- +.compout: + LD A, C + ; recall the character + ; and compute column position + LD HL, CPM_VARS.bdos_column + CP ASCII_DEL ; [DEL] key + RET Z ; 0x7F dont move cursor + INC (HL) ; inc column + CP ASCII_SP ; return for normal character > 0x20 + RET NC + + ; restore column position + DEC (HL) + LD A, (HL) + OR A + RET Z ; return if column=0 + + LD A, C + CP ASCII_BS + JP NZ, .not_backsp + ; backspace character + DEC (HL) ; BKSP -> column-1 + RET + +.not_backsp: + CP ASCII_LF + RET NZ + LD (HL), 0 ; LF -> column=0 + RET + +; ------------------------------------------------------- +; Send C character with possible preceding '^' char +; ------------------------------------------------------- +bdos_ctlout: + LD A, C + CALL bdos_chk_ctrl_char ; cy if not graphic (or special case) + JP NC, bdos_outcon ; normal output if non control char (tab, cr, lf) + + PUSH AF + LD C, CTRL ; '^' + CALL bdos_conout + POP AF + OR 0x40 ; convert to letter equivalent (^M, ^J and so on) + LD C, A ; + +bdos_outcon: + LD A, C + CP ASCII_TAB + JP NZ, bdos_conout + + ; Print spaces until tab stop position +.to_tabpos: + LD C, ' ' + CALL bdos_conout + LD A, (CPM_VARS.bdos_column) + AND 00000111b ; column mod 8 = 0 ? + JP NZ, .to_tabpos + RET + +; ------------------------------------------------------- +; Output backspace - erase previous character +; ------------------------------------------------------- +bdos_out_bksp: + CALL bdos_prn_bksp + LD C, ' ' + CALL BIOS.conout_f + +; ------------------------------------------------------- +; Send backspace to console +; ------------------------------------------------------- +bdos_prn_bksp: + LD C, ASCII_BS + JP BIOS.conout_f + +; ------------------------------------------------------- +; print #, cr, lf for ctlx, ctlu, ctlr functions +; then move to strtcol (starting column) +; ------------------------------------------------------- +bdos_newline: + LD C, '#' + CALL bdos_conout + CALL bdos_crlf + ; move the cursor to the starting position +.print_sp: + LD A, (CPM_VARS.bdos_column) ; a=column + LD HL, CPM_VARS.bdos_strtcol + CP (HL) + RET NC ; ret id startcol>column + LD C, ' ' ; print space + CALL bdos_conout + JP .print_sp + +; ------------------------------------------------------- +; Out carriage return line feed sequence +; ------------------------------------------------------- +bdos_crlf: + LD C, ASCII_CR + CALL bdos_conout + LD C, ASCII_LF + JP bdos_conout + +; ------------------------------------------------------- +; Print $-ended string +; Inp: BC -> str$ +; ------------------------------------------------------- +bdos_print: + LD A, (BC) + CP '$' + RET Z + INC BC + PUSH BC + LD C, A + CALL bdos_outcon + POP BC + JP bdos_print + +; ------------------------------------------------------- +; Buffered console input +; Reads characters from the keyboard into a memory buffer +; until RETURN is pressed. +; Inp: C=0Ah +; DE=address or zero +; ------------------------------------------------------- +bdos_read: + LD A, (CPM_VARS.bdos_column) + LD (CPM_VARS.bdos_strtcol), A + LD HL, (CPM_VARS.bdos_info) + LD C, (HL) + INC HL + PUSH HL + LD B, 0 + ; B = current buffer length, + ; C = maximum buffer length, + ; HL= next to fill - 1 +.readnx: + PUSH BC + PUSH HL +.readn1: + CALL bdos_conin + AND 0x7f ; strip 7th bit + POP HL + POP BC + ; end of line? + CP ASCII_CR + JP Z, .read_end + CP ASCII_LF + JP Z, .read_end + + ; backspace ? + CP ASCII_BS + JP NZ, .no_bksp + LD A, B + OR A + JP Z, .readnx ; ignore BKSP for column=0 + DEC B + LD A, (CPM_VARS.bdos_column) + LD (CPM_VARS.bdos_no_outflag), A + JP .linelen + + ; not a backspace +.no_bksp: + CP ASCII_DEL ; [DEL] Key + JP NZ, .no_del + LD A, B + OR A + JP Z, .readnx ; ignore DEL for column=0 + LD A, (HL) ; cur char + DEC B ; dec column + DEC HL ; point to previous char + JP .echo ; out previous char + +.no_del: + CP CTRL_E ; ^E - physical end of line + JP NZ, .no_ctrle + PUSH BC + PUSH HL + CALL bdos_crlf ; out CR+LF + XOR A + LD (CPM_VARS.bdos_strtcol), A ; reset start column to 0 + JP .readn1 + +.no_ctrle: + CP CTRL_P ; ^P + JP NZ, .no_ctrlp + PUSH HL + LD HL, CPM_VARS.bdos_prnflag + LD A, 0x1 + SUB (HL) ; flip printer output flag + LD (HL), A + POP HL + JP .readnx + +.no_ctrlp: + CP CTRL_X ; cancel current cmd line + JP NZ, .no_ctrlx + POP HL +.backx: + LD A, (CPM_VARS.bdos_strtcol) + LD HL, CPM_VARS.bdos_column + CP (HL) + JP NC, bdos_read + DEC (HL) + CALL bdos_out_bksp + JP .backx + +.no_ctrlx: + CP CTRL_U ; cancel current cmd line + JP NZ, .no_ctrlu + + CALL bdos_newline + POP HL + JP bdos_read + +.no_ctrlu: + CP CTRL_R ; repeats current cmd line + JP NZ, .stor_echo + + ; start new line and retype +.linelen: + PUSH BC + CALL bdos_newline + POP BC + POP HL + PUSH HL + PUSH BC +.next_char: + LD A, B + OR A + JP Z, .endof_cmd ; end of cmd line? + INC HL + LD C, (HL) + DEC B + PUSH BC + PUSH HL + CALL bdos_ctlout + POP HL + POP BC + JP .next_char +.endof_cmd: + PUSH HL + LD A, (CPM_VARS.bdos_no_outflag) + OR A + JP Z, .readn1 ; if no display, read next characters + ; update cursor pos + LD HL, CPM_VARS.bdos_column + SUB (HL) + LD (CPM_VARS.bdos_no_outflag), A + ; move back compcol-column spaces + +.backsp: + CALL bdos_out_bksp + LD HL, CPM_VARS.bdos_no_outflag + DEC (HL) + JP NZ, .backsp + JP .readn1 + +; Put and Echo normal character +.stor_echo: + INC HL + LD (HL), A + INC B +.echo: + PUSH BC + PUSH HL + LD C, A + CALL bdos_ctlout + POP HL + POP BC + + ; warm boot on ctrl+c only at start of line + LD A, (HL) + CP CTRL_C + LD A, B + JP NZ, .no_ctrlc + CP 1 + JP Z, warm_boot + +.no_ctrlc: + CP C ; buffer is full? + JP C, .readnx ; get next char in not full + + ; End of read operation, store length +.read_end: + POP HL + LD (HL), B + LD C, ASCII_CR + JP bdos_conout ; Our CR and return + +; ------------------------------------------------------- +; Console input with echo (C_READ) +; Out: A=L=character +; ------------------------------------------------------- +bdos_con_inp: + CALL bdos_conech + JP bdos_ret_a + +; ------------------------------------------------------- +; Console input chartacter +; Out: A=L=ASCII character +; ------------------------------------------------------- +bdos_aux_inp: + CALL BIOS.reader_f + JP bdos_ret_a + +; ------------------------------------------------------- +; Direct console I/O +; Inp: E=code. +; E=code. Returned values (in A) vary. +; 0xff Return a character without echoing if one is +; waiting; zero if none +; 0xfe Return console input status. Zero if no +; character is waiting +; Out: A +; ------------------------------------------------------- +bdos_dir_con_io: + LD A, C + INC A + JP Z, .dirinp + INC A + JP Z, BIOS.const_f + JP BIOS.conout_f +.dirinp: + CALL BIOS.const_f + OR A + JP Z, bdos_ret_mon + CALL BIOS.conin_f + JP bdos_ret_a + +; ------------------------------------------------------- +; Get I/O byte +; Out: A = I/O byte. +; ------------------------------------------------------- +bdos_get_io_byte: + LD A, (iobyte) + JP bdos_ret_a + +; ------------------------------------------------------- +; Set I/O byte +; Inp: C=I/O byte. +; TODO: Check, will be E ? +; ------------------------------------------------------- +bdos_set_io_byte: + LD HL, iobyte + LD (HL), C + RET + +; ------------------------------------------------------- +; Out $-ended string +; Inp: DE -> string$. +; ------------------------------------------------------- +bdos_print_str: + EX DE, HL + LD C, L + LD B, H + JP bdos_print + +; ------------------------------------------------------- +; Get console status +; Inp: A=L=status +; ------------------------------------------------------- +bdos_get_con_st: + CALL bdos_conbrk +bdos_ret_a: + LD (CPM_VARS.bdos_aret), A +bdos_not_impl: + RET + +; ------------------------------------------------------- +; Set IO Error status = 1 +; ------------------------------------------------------- +set_ioerr_1: + LD A, 1 + JP bdos_ret_a + +; ------------------------------------------------------- +; Report select error +; ------------------------------------------------------- +bdos_sel_error: + LD HL, bdos_sele_addr + +; indirect Jump to [HL] +bdos_jump_hl: + LD E, (HL) + INC HL + LD D, (HL) ; ->bdos_sele_addr+1 + EX DE, HL + JP (HL) ; ->bdos_selsub + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +bdos_move_dehl: + INC C +.move_next: + DEC C + RET Z + LD A, (DE) + LD (HL), A + INC DE + INC HL + JP .move_next + +; ------------------------------------------------------- +; Select the disk drive given by curdsk, and fill +; the base addresses curtrka - alloca, then fill +; the values of the disk parameter block +; Out: ZF - for big disk +; ------------------------------------------------------- +bdos_sel_dsk: + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + CALL BIOS.seldsk_f ; HL filled by call + ; HL = 0000 if error, otherwise -> DPH + LD A, H + OR L + RET Z ; return if not valid drive + + LD E, (HL) + INC HL + LD D, (HL) + ; DE -> disk header + + ; save pointers + INC HL + LD (CPM_VARS.bdos_cdrmax), HL + INC HL + INC HL + LD (CPM_VARS.bdos_curtrk), HL + INC HL + INC HL + LD (CPM_VARS.bdos_currec), HL + INC HL + INC HL + ; save translation vector (table) addr + EX DE, HL + LD (CPM_VARS.bdos_tranv), HL ; tran vector + LD HL, CPM_VARS.bdos_buffa ; DE=source for move, HL=dest + LD C, 8 + CALL bdos_move_dehl + + ; Fill the disk parameter block + LD HL, (CPM_VARS.bdos_dpbaddr) + EX DE, HL + LD HL, CPM_VARS.dbos_sectpt + LD C, 15 + CALL bdos_move_dehl + + ; Set single/double map mode + LD HL, (CPM_VARS.bdos_maxall) ; largest allocation number + LD A, H ; a=h>0 for large disks (>255 blocks) + LD HL, CPM_VARS.bdos_small_disk + LD (HL), TRUE ; small + OR A + JP Z, .is_small + LD (HL), FALSE ; big disk > 255 sec +.is_small: + LD A, 0xff + OR A + RET + +; ------------------------------------------------------- +; Move to home position, +; then reset pointers for track and sector +; ------------------------------------------------------- +bdos_home: + CALL BIOS.home_f ; home head + XOR A + ; set track pointer to 0x0000 + LD HL, (CPM_VARS.bdos_curtrk) + LD (HL), A + INC HL + LD (HL), A + + ; set current record pointer to 0x0000 + LD HL, (CPM_VARS.bdos_currec) + LD (HL), A + INC HL + LD (HL), A + + RET + +; ------------------------------------------------------- +; Read buffer and check condition +; ------------------------------------------------------- +bdos_rdbuff: + CALL BIOS.read_f + JP diocomp ; check for i/o errors + +; ------------------------------------------------------- +; Write buffer and check condition +; Inp: C - write type +; 0 - normal write operation +; 1 - directory write operation +; 2 - start of new block +; ------------------------------------------------------- +bdos_wrbuff: + CALL BIOS.write_f + +; ------------------------------------------------------- +; Check for disk errors +; ------------------------------------------------------- +diocomp: + OR A + RET Z + LD HL, bdos_pere_addr ; = C899h + JP bdos_jump_hl + +; ------------------------------------------------------- +; Seek the record containing the current dir entry +; ------------------------------------------------------- +bdos_seekdir: + LD HL, (CPM_VARS.bdos_dcnt) + LD C, DSK_SHF + CALL bdos_hlrotr + LD (CPM_VARS.bdos_arecord), HL + LD (CPM_VARS.bdos_drec), HL + +; ------------------------------------------------------- +; Seek the track given by arecord (actual record) +; local equates for registers +; ------------------------------------------------------- +bdos_seek: + LD HL, CPM_VARS.bdos_arecord + LD C, (HL) + INC HL + LD B, (HL) + ; BC = sector number + + LD HL, (CPM_VARS.bdos_currec) + LD E, (HL) + INC HL + LD D, (HL) + ; DE = current sector number + + LD HL, (CPM_VARS.bdos_curtrk) + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + ; HL = current track + +.sec_less_cur: + ; check specified sector before current + LD A, C + SUB E + LD A, B + SBC A, D + JP NC, .sec_ge_cur + ; yes, decrement current sectors for one track + PUSH HL + LD HL, (CPM_VARS.dbos_sectpt) ; sectors per track + LD A, E + SUB L + LD E, A + LD A, D + SBC A, H + LD D, A + ; current track-1 + POP HL + DEC HL + JP .sec_less_cur + ; specified sector >= current +.sec_ge_cur: + PUSH HL + LD HL, (CPM_VARS.dbos_sectpt) + ADD HL, DE ; current sector+track + JP C, .track_found ; jump if after current + ; sector before current + LD A, C + SUB L + LD A, B + SBC A, H + JP C, .track_found + EX DE, HL + POP HL + ; increment track + INC HL + JP .sec_ge_cur + ; determined track number with specified sector +.track_found: + POP HL + PUSH BC + PUSH DE + PUSH HL + ; Stack contains (lowest) BC=arecord, DE=currec, HL=curtrk + EX DE, HL + LD HL, (CPM_VARS.bdos_offset) ; adjust if have reserved tracks + ADD HL, DE + LD B, H + LD C, L + CALL BIOS.settrk_f ; select track + + ; save current track + POP DE ; curtrk + LD HL, (CPM_VARS.bdos_curtrk) + LD (HL), E + INC HL + LD (HL), D + + ; save current record + POP DE + LD HL, (CPM_VARS.bdos_currec) + LD (HL), E + INC HL + LD (HL), D + + ; calc relative offset between arecord and currec + POP BC + LD A, C + SUB E + LD C, A + LD A, B + SBC A, D + LD B, A + + LD HL, (CPM_VARS.bdos_tranv) ; HL -> translation table + EX DE, HL + ; translate via BIOS call + CALL BIOS.sectran_f + ; select translated sector and return + LD C, L + LD B, H + JP BIOS.setsec_f + +; ------------------------------------------------------- +; Compute block number from record number and extent +; number +; ------------------------------------------------------- +bdos_dm_position: + LD HL, CPM_VARS.bdos_blkshf + LD C, (HL) + LD A, (CPM_VARS.bdos_vrecord) ; record number + ; a = a / 2^blkshf + +dmpos_l0: + OR A + RRA + DEC C + JP NZ, dmpos_l0 + ; A = shr(vrecord, blkshf) = vrecord/2^(sect/block) + LD B, A + LD A, 8 + SUB (HL) + ; c = 8 - blkshf + LD C, A + LD A, (CPM_VARS.bdos_extval) + ; a = extval * 2^(8-blkshf) +dmpos_l1: + DEC C + JP Z, dmpos_l2 + OR A + RLA + JP dmpos_l1 +dmpos_l2: + ADD A, B + RET ; with dm_position in A + +; ------------------------------------------------------- +; Return disk map value from position given by BC +; ------------------------------------------------------- +bdos_getdm: + LD HL, (CPM_VARS.bdos_info) + LD DE, DSK_MAP + ADD HL, DE + ADD HL, BC + LD A, (CPM_VARS.bdos_small_disk) + OR A + JP Z,getdmd + LD L, (HL) + LD H, 0x00 + RET + +getdmd: + ADD HL, BC + LD E, (HL) + INC HL + LD D, (HL) + EX DE, HL + RET + +; ------------------------------------------------------- +; Compute disk block number from current FCB +; ------------------------------------------------------- +bdos_index: + CALL bdos_dm_position + LD C, A + LD B, 0 + CALL bdos_getdm + LD (CPM_VARS.bdos_arecord), HL + RET + +; ------------------------------------------------------- +; Called following index to see if block allocated +; Out: ZF if not allocated +; ------------------------------------------------------- +bdos_allocated: + LD HL, (CPM_VARS.bdos_arecord) + LD A, L + OR H + RET + +; ------------------------------------------------------- +; Compute actual record address, assuming index called +; ------------------------------------------------------- +bdos_atran: + LD A, (CPM_VARS.bdos_blkshf) + LD HL, (CPM_VARS.bdos_arecord) +bdatarn_l0: + ADD HL, HL + DEC A + JP NZ, bdatarn_l0 + LD (CPM_VARS.bdos_arecord1), HL + LD A, (CPM_VARS.bdos_blmsk) + LD C, A + LD A, (CPM_VARS.bdos_vrecord) + AND C + OR L + LD L, A + LD (CPM_VARS.bdos_arecord), HL + RET + +; ------------------------------------------------------- +; Get current extent field address to A +; ------------------------------------------------------- +bdos_getexta: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_EXT + ADD HL, DE ; HL -> .fcb(extnum) + RET + +; ------------------------------------------------------- +; Compute reccnt and nxtrec addresses for get/setfcb +; Out: HL -> record count byte in fcb +; DE -> next record number byte in fcb +; ------------------------------------------------------- +bdos_fcb_rcnr: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_RC + ADD HL, DE + EX DE, HL + LD HL, 17 ; (nxtrec-reccnt) + ADD HL, DE + RET + +; ------------------------------------------------------- +; Set variables from currently addressed fcb +; ------------------------------------------------------- +bdos_stor_fcb_var: + CALL bdos_fcb_rcnr + LD A, (HL) + LD (CPM_VARS.bdos_vrecord), A + EX DE, HL + LD A, (HL) + LD (CPM_VARS.bdos_rcount), A + CALL bdos_getexta + LD A, (CPM_VARS.bdos_extmsk) + AND (HL) + LD (CPM_VARS.bdos_extval), A + RET + +; ------------------------------------------------------- +; Set the next record to access. +; Inp: HL -> fcb next record byte +; ------------------------------------------------------- +bdos_set_nxt_rec: + CALL bdos_fcb_rcnr + LD A, (CPM_VARS.bdos_seqio) ; sequential mode flag (=1) + CP 2 + JP NZ, bsfcb_l0 + XOR A ; if 2 - no adder needed +bsfcb_l0: + LD C, A + LD A, (CPM_VARS.bdos_vrecord) ; last record number + ADD A, C + LD (HL), A + EX DE, HL + LD A, (CPM_VARS.bdos_rcount) ; next record byte + LD (HL), A + RET + +; ------------------------------------------------------- +; Rotate HL right by C bits +; ------------------------------------------------------- +bdos_hlrotr: + INC C +bhlr_nxt: + DEC C + RET Z + LD A, H + OR A + RRA + LD H, A + LD A,L + RRA + LD L, A + JP bhlr_nxt + +; ------------------------------------------------------- +; Compute checksum for current directory buffer +; Out: A - checksum +; ------------------------------------------------------- +bdos_compute_cs: + LD C, DIR_BUFF_SIZE ; size of directory buffer + LD HL, (CPM_VARS.bdos_buffa) + XOR A +bcompcs_add_nxt: + ADD A, (HL) + INC HL + DEC C + JP NZ, bcompcs_add_nxt + RET + +; ------------------------------------------------------- +; Rotate HL left by C bits +; ------------------------------------------------------- +bdos_hlrotl: + INC C +.nxt: + DEC C + RET Z + ADD HL, HL + JP .nxt + +; ------------------------------------------------------- +; Set a "1" value in curdsk position +; Inp: BC - vector +; Out: HL - vector with bit set +; ------------------------------------------------------- +bdos_set_dsk_bit: + PUSH BC + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + LD HL, 0x0001 + CALL bdos_hlrotl + POP BC + LD A, C + OR L + LD L, A + LD A, B + OR H + LD H, A + RET + +; ------------------------------------------------------- +; Get write protect status +; ------------------------------------------------------- +bdos_nowrite: + LD HL, (CPM_VARS.bdos_rodsk) + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + CALL bdos_hlrotr + LD A, L + AND 0x1 + RET + +; ------------------------------------------------------- +; Temporarily set current drive to be read-only; +; attempts to write to it will fail +; ------------------------------------------------------- +bdos_set_ro: + LD HL, CPM_VARS.bdos_rodsk + LD C, (HL) + INC HL + LD B, (HL) + + CALL bdos_set_dsk_bit + LD (CPM_VARS.bdos_rodsk), HL + ; high water mark in directory goes to max + LD HL, (CPM_VARS.bdos_dirmax) + INC HL + EX DE, HL + LD HL, (CPM_VARS.bdos_cdrmax) + LD (HL), E + INC HL + LD (HL), D + RET + +; ------------------------------------------------------- +; Check current directory element for read/only status +; ------------------------------------------------------- +bdos_check_rodir: + CALL bdos_getdptra + +; ------------------------------------------------------- +; Check current buff(dptr) or fcb(0) for r/o status +; ------------------------------------------------------- +bdos_check_rofile: + LD DE, RO_FILE + ADD HL, DE + LD A, (HL) + RLA + RET NC + LD HL, bdos_rofe_addr ; = C8B1h + JP bdos_jump_hl + +; ------------------------------------------------------- +; Check for write protected disk +; ------------------------------------------------------- +bdos_check_write: + CALL bdos_nowrite + RET Z + LD HL, bdos_rode_addr ; = C8ABh + JP bdos_jump_hl + +; ------------------------------------------------------- +; Compute the address of a directory element at +; positon dptr in the buffer +; ------------------------------------------------------- +bdos_getdptra: + LD HL, (CPM_VARS.bdos_buffa) + LD A, (CPM_VARS.bdos_dptr) + +; ------------------------------------------------------- +; HL = HL + A +; ------------------------------------------------------- +bdos_hl_add_a: + ADD A,L + LD L, A + RET NC + INC H + RET + +; ------------------------------------------------------- +; Compute the address of the s2 from fcb +; Out: A - module number +; HL -> module number +; ------------------------------------------------------- +bdos_get_s2: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_S2 + ADD HL, DE + LD A, (HL) + RET + +; ------------------------------------------------------- +; Clear the module number field for user open/make +; ------------------------------------------------------- +bdos_clear_s2: + CALL bdos_get_s2 + LD (HL), 0 + RET + +; ------------------------------------------------------- +; Set FWF (File Write Flag) +; ------------------------------------------------------- +bdos_set_fwf: + CALL bdos_get_s2 + OR FWF_MASK + LD (HL), A + RET + +; ------------------------------------------------------- +; Check for free entries in directory +; Out: CF is set - directory have more file entries +; CF is reset - directory is full +; ------------------------------------------------------- +bdos_dir_is_free: + LD HL, (CPM_VARS.bdos_dcnt) + EX DE, HL ; DE = directory counter + LD HL, (CPM_VARS.bdos_cdrmax) ; HL = cdrmax + LD A, E + SUB (HL) + INC HL + LD A, D + SBC A, (HL) + ; Condition dcnt - cdrmax produces cy if cdrmax>dcnt + RET + +; ------------------------------------------------------- +; If not (cdrmax > dcnt) then cdrmax = dcnt+1 +; ------------------------------------------------------- +bdos_setcdr: + CALL bdos_dir_is_free + RET C + INC DE + LD (HL), D + DEC HL + LD (HL), E + RET + +; ------------------------------------------------------- +; HL = DE - HL +; ------------------------------------------------------- +bdos_de_sub_hl: + LD A, E + SUB L + LD L, A + LD A, D + SBC A, H + LD H, A + RET + +; Set directory checksubm byte +bdos_newchecksum: + LD C, 0xff + + +; ------------------------------------------------------- +bdos_checksum: + LD HL, (CPM_VARS.bdos_drec) + EX DE, HL + LD HL, (CPM_VARS.bdos_alloc1) + CALL bdos_de_sub_hl + RET NC + PUSH BC + CALL bdos_compute_cs ; Compute checksum to A for current dir buffer + LD HL, (CPM_VARS.bdos_cksum) + EX DE, HL + LD HL, (CPM_VARS.bdos_drec) + ADD HL, DE + POP BC + INC C + JP Z, initial_cs + CP (HL) + RET Z + CALL bdos_dir_is_free + RET NC + CALL bdos_set_ro + RET +initial_cs: + LD (HL), A + RET + +; ------------------------------------------------------- +; Write the current directory entry, set checksum +; ------------------------------------------------------- +bdos_wrdir: + CALL bdos_newchecksum + CALL bdos_setdir + LD C, 0x1 + CALL bdos_wrbuff + JP bdos_setdata + +; ------------------------------------------------------- +; Read a directory entry into the directory buffer +; ------------------------------------------------------- +bdos_rd_dir: + CALL bdos_setdir + CALL bdos_rdbuff + +; ------------------------------------------------------- +; Set data dma address +; ------------------------------------------------------- +bdos_setdata: + LD HL, CPM_VARS.bdos_dmaad + JP bdos_set_dma + +; ------------------------------------------------------- +; Set directory dma address +; ------------------------------------------------------- +bdos_setdir: + LD HL, CPM_VARS.bdos_buffa + +; ------------------------------------------------------- +; HL -> dma address to set (i.e., buffa or dmaad) +; ------------------------------------------------------- +bdos_set_dma: + LD C, (HL) + INC HL + LD B, (HL) + JP BIOS.setdma_f + +; ------------------------------------------------------- +; Copy the directory entry to the user buffer +; after call to search or searchn by user code +; ------------------------------------------------------- +bdos_dir_to_user: + LD HL, (CPM_VARS.bdos_buffa) + EX DE, HL + LD HL, (CPM_VARS.bdos_dmaad) + LD C, DIR_BUFF_SIZE + JP bdos_move_dehl + +; ------------------------------------------------------- +; return zero flag if at end of directory, non zero +; if not at end (end of dir if dcnt = 0ffffh) +; ------------------------------------------------------- +bdos_end_of_dir: + LD HL, CPM_VARS.bdos_dcnt + LD A, (HL) + INC HL + CP (HL) + RET NZ + INC A + RET + +; ------------------------------------------------------- +; Set dcnt to the end of the directory +; ------------------------------------------------------- +bdos_set_end_dir: + LD HL, ENDDIR + LD (CPM_VARS.bdos_dcnt), HL + RET + +; ------------------------------------------------------- +; Read next directory entry, with C=true if initializing +; ------------------------------------------------------- +bdos_read_dir: + LD HL, (CPM_VARS.bdos_dirmax) + EX DE, HL + LD HL, (CPM_VARS.bdos_dcnt) + INC HL + LD (CPM_VARS.bdos_dcnt), HL + CALL bdos_de_sub_hl + JP NC, bdrd_l0 + JP bdos_set_end_dir +bdrd_l0: + LD A, (CPM_VARS.bdos_dcnt) + AND DSK_MSK + LD B, FCB_SHF +bdrd_l1: + ADD A, A + DEC B + JP NZ, bdrd_l1 + LD (CPM_VARS.bdos_dptr), A + OR A + RET NZ + PUSH BC + CALL bdos_seekdir + CALL bdos_rd_dir + POP BC + JP bdos_checksum + +; ------------------------------------------------------- +; Given allocation vector position BC, return with byte +; containing BC shifted so that the least significant +; bit is in the low order accumulator position. HL is +; the address of the byte for possible replacement in +; memory upon return, and D contains the number of shifts +; required to place the returned value back into position +; ------------------------------------------------------- +bdos_getallocbit: + LD A, C + AND 00000111b + INC A + LD E, A + LD D, A + LD A, C + RRCA + RRCA + RRCA + AND 00011111b + LD C, A + LD A, B + ADD A, A + ADD A, A + ADD A, A + ADD A, A + ADD A, A + OR C + LD C, A + LD A, B + RRCA + RRCA + RRCA + AND 00011111b + LD B, A + LD HL, (CPM_VARS.bdos_alloca) ; Base address of allocation vector + ADD HL, BC + LD A, (HL) +ga_rotl: + RLCA + DEC E + JP NZ, ga_rotl + RET + +; ------------------------------------------------------- +; BC is the bit position of ALLOC to set or reset. The +; value of the bit is in register E. +; ------------------------------------------------------- +bdos_setallocbit: + PUSH DE + CALL bdos_getallocbit + AND 11111110b + POP BC + OR C + +; ------------------------------------------------------- +; Rotate and replace +; Inp: A - Byte value from ALLOC +; D - bit position (1..8) +; HL - target ALLOC position +; ------------------------------------------------------- +bdos_sab_rotr: + RRCA + DEC D + JP NZ, bdos_sab_rotr + LD (HL), A + RET + +; ------------------------------------------------------- +; Set or clear space used bits in allocation map +; Inp: C=0 - clear, C=1 - set +; ------------------------------------------------------- +bdos_scandm: + CALL bdos_getdptra + ; HL addresses the beginning of the directory entry + LD DE, DSK_MAP + ADD HL, DE ; HL -> block number byte + PUSH BC + LD C, 17 ; fcblen-dskmap+1 + ; scan all 17 bytes +.scan_next: + POP DE + DEC C + RET Z ; return if done + + PUSH DE + ; small or bug disk? + LD A, (CPM_VARS.bdos_small_disk) + OR A + JP Z, .scan_big + PUSH BC + PUSH HL + ; small, one byte + LD C, (HL) + LD B, 0 + JP .scan_cmn +.scan_big: + ; big, two byte + DEC C + PUSH BC + LD C, (HL) + INC HL + LD B, (HL) + PUSH HL + +.scan_cmn: + ; this block used? + LD A, C + OR B + JP Z, .not_used + ; have free space on disk? + LD HL, (CPM_VARS.bdos_maxall) + LD A, L + SUB C + LD A, H + SBC A, B + CALL NC, bdos_setallocbit ; enough space, set bit + +.not_used: + POP HL ; HL -> next block in fcb + INC HL + POP BC + JP .scan_next + +; ------------------------------------------------------- +; Initialize the current disk +; lret = false, set to true if $ file exists +; compute the length of the allocation vector - 2 +; ------------------------------------------------------- +bdos_initialize: + ; compute size of alloc table + LD HL, (CPM_VARS.bdos_maxall) + LD C, 3 + CALL bdos_hlrotr ; HL = disk size / 8 + 1 + INC HL + LD B, H + LD C, L ; BC = alloc table size + LD HL, (CPM_VARS.bdos_alloca) ; address of allocation table + ; fill with zeroes +.alt_fill_0: + LD (HL), 0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .alt_fill_0 + + LD HL, (CPM_VARS.bdos_dirblk) ; DE = initial space, used by directory + EX DE, HL + LD HL, (CPM_VARS.bdos_alloca) ; HL -> allocation map + LD (HL), E + INC HL + LD (HL), D ; [HL] = DE + CALL bdos_home ; home drive, set initial head, track, sector + + LD HL, (CPM_VARS.bdos_cdrmax) + LD (HL), 3 ; next dir read + INC HL + LD (HL), 0 + CALL bdos_set_end_dir ; mark end of dir + +.rd_next_f: + ; read next file name + LD C, 0xff + CALL bdos_read_dir + CALL bdos_end_of_dir ; is have another file? + RET Z + ; get params of file, and check deleted + CALL bdos_getdptra + LD A, FILE_DELETED + CP (HL) + JP Z, .rd_next_f + ; check user code + LD A, (CPM_VARS.bdos_userno) + CP (HL) + JP NZ, .set_as_used + INC HL + LD A, (HL) + SUB '$' ; check for $name file + JP NZ, .set_as_used + ; dollar file found, mark in lret + DEC A + LD (CPM_VARS.bdos_aret), A + +.set_as_used: + LD C, 1 + CALL bdos_scandm + CALL bdos_setcdr + JP .rd_next_f + +; ------------------------------------------------------- +; Return directory location as lret +; used in delete, rename, ... +; ------------------------------------------------------- +bdos_ret_dirloc: + LD A, (CPM_VARS.bdos_dirloc) + JP bdos_ret_a + +; ------------------------------------------------------- +; Compare extent# in A with that in C, return nonzero +; if they do not match +; ------------------------------------------------------- +bdos_compext: + PUSH BC + PUSH AF + LD A, (CPM_VARS.bdos_extmsk) + CPL + LD B, A + LD A, C + AND B + LD C, A + POP AF + AND B + SUB C + AND MAX_EXT ; check only bits [4:0] (MAX_EXT) + POP BC + RET + +; ------------------------------------------------------- +; Search for directory element of length C at info +; ------------------------------------------------------- +bdos_search: + LD A, 0xff + LD (CPM_VARS.bdos_dirloc), A + LD HL, CPM_VARS.bdos_searchl ; length in bytes to match + LD (HL), C + LD HL, (CPM_VARS.bdos_info) ; address filename to match + LD (CPM_VARS.bdos_searcha), HL + CALL bdos_set_end_dir + CALL bdos_home + +; ------------------------------------------------------- +; Search for the next directory element, assuming +; a previous call on search which sets searcha and +; searchl +; ------------------------------------------------------- +bdos_search_nxt: + LD C, 0 + CALL bdos_read_dir ; get next filename entry + CALL bdos_end_of_dir ; at end? pos=0xffff + JP Z, .not_found ; jump at end + + LD HL, (CPM_VARS.bdos_searcha) + EX DE, HL + ; skip if file deleted + LD A, (DE) + CP FILE_DELETED + JP Z, .search_next + + PUSH DE + CALL bdos_dir_is_free + POP DE + JP NC, .not_found ; Is full, no more file entries + +.search_next: + CALL bdos_getdptra ; get address of FCB + LD A, (CPM_VARS.bdos_searchl) ; BC=length to compare + LD C, A + LD B, 0 + + ; compare loop +.search_loop: + LD A, C + OR A + JP Z, .search_end ; search completed? + ; check byte + LD A, (DE) + CP '?' + JP Z, .search_ok ; match if wildcard + LD A, B + CP 13 ; ignore 13th byte + JP Z, .search_ok + CP FCB_EXT ; extent byte + LD A, (DE) + JP Z, .search_ext + SUB (HL) ; compare bytes + AND 0x7f ; mask 7th bit + JP NZ, bdos_search_nxt ; if not match, check next file entry + JP .search_ok + + ; compare ext +.search_ext: + PUSH BC + LD C, (HL) + CALL bdos_compext + POP BC + JP NZ, bdos_search_nxt ; if not match, check next file entry + + ; current bytes matches, increment pointers, decrement counter +.search_ok: + INC DE + INC HL + INC B + DEC C + JP .search_loop ; compare next byte + + ; entiry name matches, return dir position +.search_end: + LD A, (CPM_VARS.bdos_dcnt) + AND 0x3 + LD (CPM_VARS.bdos_aret), A + LD HL, CPM_VARS.bdos_dirloc + LD A, (HL) + RLA + RET NC + XOR A + LD (HL), A + RET + + ; end of directory, or empty name +.not_found: + CALL bdos_set_end_dir + LD A, 0xff + JP bdos_ret_a + +; ------------------------------------------------------- +; Delete the currently addressed file +; ------------------------------------------------------- +bdos_era_file: + CALL bdos_check_write ; check write rotection + LD C, 12 ; length of filename + CALL bdos_search ; search file in directory (with wildcards) +.del_next: + CALL bdos_end_of_dir + RET Z ; no more dir entries - exit + CALL bdos_check_rodir ; check file RO + CALL bdos_getdptra ; get file info + LD (HL), FILE_DELETED ; set deleted marker at first symbol + LD C, 0 + CALL bdos_scandm ; clear space at map + CALL bdos_wrdir ; write directory to disk + CALL bdos_search_nxt ; find next file + JP .del_next + +; ------------------------------------------------------- +; Given allocation vector position BC, find the zero bit +; closest to this position by searching left and right. +; if found, set the bit to one and return the bit position +; in hl. if not found (i.e., we pass 0 on the left, or +; maxall on the right), return 0000 in hl +; ------------------------------------------------------- +bdos_get_block: + LD D, B + LD E, C +.prev_test: + LD A, C + OR B + JP Z, .next_test ; jump if block 0 specified + ; check previous block + DEC BC + PUSH DE + PUSH BC + CALL bdos_getallocbit + RRA + JP NC, .ret_block ; block is empty, use it + + ; not empty, check more + POP BC + POP DE + + ; look at next block +.next_test: + ; check for free space on disk + LD HL, (CPM_VARS.bdos_maxall) + LD A, E + SUB L + LD A, D + SBC A, H + JP NC, .ret_no_block + ; have space, move to next block + INC DE + PUSH BC + PUSH DE + LD B, D + LD C, E + CALL bdos_getallocbit + RRA + JP NC, .ret_block ; block is empty, use it + POP DE + POP BC + JP .prev_test + + ; mark block as used and return in HL +.ret_block: + RLA + INC A + CALL bdos_sab_rotr + POP HL + POP DE + RET + + ; no free blocks found +.ret_no_block: + LD A, C + OR B + JP NZ, .prev_test ; if BC != 0 try to find before + LD HL, 0x00 ; not found + RET + +; ------------------------------------------------------- +; Copy the entire file control block +; ------------------------------------------------------- +bdos_copy_fcb: + LD C, 0x00 + LD E, FCB_LEN +; ------------------------------------------------------- +; copy fcb information starting at C for E bytes +; into the currently addressed directory entry +; ------------------------------------------------------- +dbos_copy_dir: + PUSH DE + LD B, 0x00 + LD HL, (CPM_VARS.bdos_info) + ADD HL, BC + EX DE, HL + CALL bdos_getdptra + POP BC + CALL bdos_move_dehl + +; ------------------------------------------------------- +; Enter from close to seek and copy current element +; ------------------------------------------------------- +bdos_seek_copy: + CALL bdos_seekdir + JP bdos_wrdir + +; ------------------------------------------------------- +; Rename the file described by the first half of +; the currently addressed FCB to new name, contained in +; the last half of the currently addressed FCB. +; ------------------------------------------------------- +bdos_rename: + CALL bdos_check_write ; check for RO disk + LD C, 12 ; use 12 symbols of name to compare + CALL bdos_search ; find first file + LD HL, (CPM_VARS.bdos_info) ; file info + LD A, (HL) ; user number + LD DE, 16 ; shift to place second file info + ADD HL, DE + LD (HL), A ; set same user no + +.ren_next: + CALL bdos_end_of_dir + RET Z ; return if end of directory + + CALL bdos_check_rodir ; check file RO flag + LD C, 16 ; start from 16th byte + LD E, FN_LEN ; and copy 12 byte + CALL dbos_copy_dir + CALL bdos_search_nxt ; search next file + JP .ren_next + +; ------------------------------------------------------- +; Update file attributes for current fcb +; ------------------------------------------------------- +bdos_update_attrs: + LD C, FN_LEN + CALL bdos_search ; search file by 12 bytes of name +.set_next: + CALL bdos_end_of_dir + RET Z ; return if not found + + LD C, 0 + LD E, FN_LEN + CALL dbos_copy_dir ; copy name to FCB and save dir + CALL bdos_search_nxt + JP .set_next ; ; do it for next file + +; -------------------------------------------------- +; Open file, name specified in FCB +; ------------------------------------------------------- +open: + LD C, FCB_INFO_LEN + CALL bdos_search ; search file + CALL bdos_end_of_dir + RET Z ; return if not found + +bdos_open_copy: + CALL bdos_getexta + LD A, (HL) ; get extent byte + PUSH AF ; save ext + PUSH HL ; and it's address + CALL bdos_getdptra + EX DE, HL + ; move to user space + LD HL, (CPM_VARS.bdos_info) + LD C, 32 + PUSH DE + CALL bdos_move_dehl + CALL bdos_set_fwf ; set 7th bit s2 "unmodified" flag + + POP DE + LD HL, FCB_EXT + ADD HL, DE + LD C, (HL) ; C = extent byte + + LD HL, FCB_RC + ADD HL, DE + LD B, (HL) ; B = record count + + POP HL + POP AF + LD (HL), A + LD A, C + CP (HL) ; cmp extent bytes + LD A, B + JP Z, .set_rec_cnt + ; user specified extent is not same, reset record count to 0 + LD A, 0 + JP C, .set_rec_cnt + ; set to maximum + LD A, 128 + +.set_rec_cnt: + ; set record count in user FCB to A + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_RC + ADD HL, DE + LD (HL), A + RET + +; -------------------------------------------------- +; HL = .fcb1(i), DE = .fcb2(i), +; if fcb1(i) = 0 then fcb1(i) := fcb2(i) +; -------------------------------------------------- +bdos_mergezero: + LD A, (HL) + INC HL + OR (HL) + DEC HL + RET NZ + LD A, (DE) + LD (HL), A + INC DE + INC HL + LD A, (DE) + LD (HL), A + DEC DE + DEC HL + RET + +; -------------------------------------------------- +; Close file specified by FCB +; -------------------------------------------------- +bdos_close: + XOR A + ; clear status and file position bytes + LD (CPM_VARS.bdos_aret), A + LD (CPM_VARS.bdos_dcnt), A + LD (CPM_VARS.bdos_dcnt+1), A + CALL bdos_nowrite ; get write protection + RET NZ ; return if set + + CALL bdos_get_s2 + AND FWF_MASK + RET NZ ; return if not modified flag set + ; search file + LD C, FCB_INFO_LEN + CALL bdos_search + CALL bdos_end_of_dir + RET Z ; return if not found + + LD BC, DSK_MAP ; offset of records used + CALL bdos_getdptra + ADD HL, BC + EX DE, HL + + LD HL, (CPM_VARS.bdos_info) ; same for user FCB + ADD HL, BC + LD C, 16 ; bytes in extent + +bdos_merge0: + LD A, (CPM_VARS.bdos_small_disk) ; small/big disk flag + OR A + JP Z, bdos_merge ; jump if big (16 bit) + ; small disk (8 bit) + LD A, (HL) ; from user FCB + OR A + LD A, (DE) ; from DIR FCB + JP NZ, bdm_fcbnzero + LD (HL), A ; user is 0, set from directory fcb + +bdm_fcbnzero: + OR A + JP NZ, bdm_buffnzero + ; dir is 0, set from user fcb + LD A, (HL) + LD (DE), A + +bdm_buffnzero: + CP (HL) + JP NZ, bdm_mergerr ; if both non zero, close error + JP bdm_dmset ; merged ok, go to next fcb byte + +bdos_merge: + CALL bdos_mergezero ; update user fcb if it is zero + EX DE, HL + CALL bdos_mergezero ; update dir fcb if it is zero + EX DE, HL + LD A, (DE) + CP (HL) + JP NZ, bdm_mergerr ; if both is same, close error + + ; next byte + INC DE + INC HL + LD A, (DE) + CP (HL) + JP NZ, bdm_mergerr ; if both is same, close error + DEC C + +bdm_dmset: + ; next + INC DE + INC HL + DEC C + JP NZ, bdos_merge0 ; merge next if C>0 + + LD BC, 0xffec ; -(fcblen-extnum) (-20) + ADD HL, BC + EX DE, HL + ADD HL, BC + LD A, (DE) + CP (HL) + JP C, bdm_endmerge ; directory extent > user extent -> end + ; update record count in dir FCB + LD (HL), A + LD BC, 0x3 ; (reccnt-extnum) + ADD HL, BC + EX DE, HL + ADD HL, BC + LD A, (HL) ; get from user FCB + LD (DE), A ; set to directory FCB + +bdm_endmerge: + LD A, 0xff + LD (CPM_VARS.bdos_fcb_closed), A ; set was open and closed flag + JP bdos_seek_copy ; update directory + + ; set return status and return +bdm_mergerr: + LD HL, CPM_VARS.bdos_aret + DEC (HL) + RET + +; ------------------------------------------------------- +; Create a new file by creating a directory entry +; then opening the file +; ------------------------------------------------------- +bdos_make: + CALL bdos_check_write ; check Write Protection + LD HL, (CPM_VARS.bdos_info) ; save user FCB + PUSH HL ; on stack + LD HL, CPM_VARS.bdos_efcb ; empty FCB + ; Save FCB address, look for 0xE5 + LD (CPM_VARS.bdos_info), HL ; set empty FCB + ; search firs empty slot in directory + LD C, 0x1 ; compare one byte 0xE5 + CALL bdos_search + CALL bdos_end_of_dir ; found flag + POP HL + LD (CPM_VARS.bdos_info), HL ; restore user FCB address + RET Z ; return if not found (no space in dir) + EX DE, HL + LD HL, FCB_RC ; number or record for this file + ADD HL, DE + + ; clear FCB tail + LD C, 17 ; fcblen-namlen + XOR A +.fcb_set_0: + LD (HL), A + INC HL + DEC C + JP NZ, .fcb_set_0 + + LD HL, FCB_S1 + ADD HL, DE + LD (HL), A ; Current record within extent? + CALL bdos_setcdr + CALL bdos_copy_fcb + JP bdos_set_fwf + +; ------------------------------------------------------- +; Close the current extent, and open the next one to read +; if possible. RMF is true if in read mod +; ------------------------------------------------------- +bdos_open_reel: + XOR A + LD (CPM_VARS.bdos_fcb_closed), A ; clear close flag + CALL bdos_close ; close extent + CALL bdos_end_of_dir ; check space + RET Z ; ret if no more space + ; get extent byte from user FCB + LD HL, (CPM_VARS.bdos_info) + LD BC, FCB_EXT + ADD HL, BC + LD A, (HL) + ; and increment it, mask to 0..31 and store + INC A + AND MAX_EXT + LD (HL), A + JP Z, .overflow + ; mask extent byte + LD B, A + LD A, (CPM_VARS.bdos_extmsk) + AND B + LD HL, CPM_VARS.bdos_fcb_closed + AND (HL) + JP Z, .read_nxt_extent ; read netx extent + JP .extent_in_mem + +.overflow: + LD BC, 0x2 ; S2 + ADD HL, BC + INC (HL) + LD A, (HL) + AND MAX_MOD + JP Z, .error + +.read_nxt_extent: + LD C, FCB_INFO_LEN + CALL bdos_search + CALL bdos_end_of_dir + JP NZ, .extent_in_mem ; jump if success + ; not extent found + LD A, (CPM_VARS.bdos_rfm) + INC A + JP Z, .error ; can not get extent + CALL bdos_make ; make new empty dir entry for extent + CALL bdos_end_of_dir ; chk no space + JP Z, .error ; jmp to error if no space in directory + JP .almost_done +.extent_in_mem: + ; open extent + CALL bdos_open_copy +.almost_done: + CALL bdos_stor_fcb_var ; move updated data to new extent + XOR A ; clear error flag + JP bdos_ret_a +.error: + CALL set_ioerr_1 + JP bdos_set_fwf ; clear bit 7 in s2 + +; ------------------------------------------------------- +; Sequential disk read operation +; ------------------------------------------------------- +bdos_seq_disk_read: + LD A, 0x1 + LD (CPM_VARS.bdos_seqio), A + ; drop through to diskread + +bdos_disk_read: + LD A, TRUE + LD (CPM_VARS.bdos_rfm), A ; dont allow read unwritten data + CALL bdos_stor_fcb_var + LD A, (CPM_VARS.bdos_vrecord) ; next record to read + LD HL, CPM_VARS.bdos_rcount ; number of records in extent + ; in this extent? + CP (HL) + JP C, .recordok + ; no, in next extent, check this exctent fully used + CP 128 + JP NZ, .error_opn + ;open next extent + CALL bdos_open_reel + XOR A + ; reset record number to read + LD (CPM_VARS.bdos_vrecord), A + ; check open status + LD A, (CPM_VARS.bdos_aret) + OR A + JP NZ, .error_opn +.recordok: + ; compute block number to read + CALL bdos_index + ; check if it in bounds + CALL bdos_allocated + JP Z, .error_opn + + CALL bdos_atran ; convert to logical sector + CALL bdos_seek ; seec track and sector + CALL bdos_rdbuff ; read sector + JP bdos_set_nxt_rec + +.error_opn: + JP set_ioerr_1 + +; ------------------------------------------------------- +; Sequential write disk +; ------------------------------------------------------- +bdos_seq_disk_write: + LD A, 0x1 + LD (CPM_VARS.bdos_seqio), A +bdos_disk_write: + LD A, FALSE + LD (CPM_VARS.bdos_rfm), A ; allow open new extent + CALL bdos_check_write ; check write protection + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + CALL bdos_check_rofile ; check RO file + CALL bdos_stor_fcb_var + LD A, (CPM_VARS.bdos_vrecord) ; get record number to write to + CP 128 ; lstrec+1 + JP NC, set_ioerr_1 ; not in range - error + CALL bdos_index ; compute block number + CALL bdos_allocated ; check number + LD C, 0 + JP NZ, .disk_wr1 + CALL bdos_dm_position ; get next block number within FCB + LD (CPM_VARS.bdos_dminx), A ; and store it + LD BC, 0x00 ; start looking for space from start + OR A + JP Z, .nop_block ; zero - not allocated + ; extract previous blk from fcb + LD C, A + DEC BC + CALL bdos_getdm + LD B, H + LD C, L +.nop_block: + CALL bdos_get_block ; get next free nearest block + LD A, L + OR H + JP NZ, .block_ok + ; error, no more space + LD A, 2 + JP bdos_ret_a +.block_ok: + LD (CPM_VARS.bdos_arecord), HL ; set record to access + EX DE, HL ; to DE + LD HL, (CPM_VARS.bdos_info) ; + LD BC, FCB_AL ; 16 + ADD HL, BC + ; small/big disk + LD A, (CPM_VARS.bdos_small_disk) + OR A + LD A, (CPM_VARS.bdos_dminx) ; rel block + JP Z, .alloc_big ; jump for 16bit + CALL bdos_hl_add_a ; HL = HL + A + LD (HL), E ; save record to access + JP .disk_wru + +.alloc_big: + ; save record to acces 16bit + LD C, A + LD B, 0 + ADD HL, BC + ADD HL, BC + LD (HL), E + INC HL + LD (HL), D + + ; Disk write to previously unallocated block +.disk_wru: + LD C, 2 ; C=2 - write to unused disk space +.disk_wr1: + LD A, (CPM_VARS.bdos_aret) ; check status + OR A + RET NZ ; return on error + + PUSH BC ; store C write flag for BIOS + CALL bdos_atran ; convert block number to logical sector + LD A, (CPM_VARS.bdos_seqio) ; get access mode (0=random, 1=sequential, 2=special) + DEC A + DEC A + JP NZ, .normal_wr + ; special + POP BC + PUSH BC + LD A, C ; A = write status flag (2=write unused space) + DEC A + DEC A + JP NZ, .normal_wr + ; fill buffer with zeroes + PUSH HL + LD HL, (CPM_VARS.bdos_buffa) + LD D, A +.fill0: + LD (HL), A + INC HL + INC D + JP P, .fill0 + CALL bdos_setdir ; Tell BIOS buffer address to directory access + LD HL, (CPM_VARS.bdos_arecord1) ; get sector that starts current block + LD C, 2 ; write status flag (2=write unused space) +.fill1: + LD (CPM_VARS.bdos_arecord), HL ; set sector to write + PUSH BC + CALL bdos_seek ; seek track and sector number + POP BC + CALL bdos_wrbuff ; write zeroes + LD HL, (CPM_VARS.bdos_arecord) ; get sector number + LD C, 0 ; normal write flag + LD A, (CPM_VARS.bdos_blmsk) ; block mask + ; write entire block? + LD B, A + AND L + CP B + INC HL + JP NZ, .fill1 ; continue until (BLKMSK+1) sectors written + ; reset sector number + POP HL + LD (CPM_VARS.bdos_arecord), HL + CALL bdos_setdata ; reset DMA address + + ; Normal write. +.normal_wr: + CALL bdos_seek ; Set track and sector + POP BC ; restore C - write status flag + PUSH BC + CALL bdos_wrbuff ; write + POP BC + LD A, (CPM_VARS.bdos_vrecord) ; last file record + LD HL, CPM_VARS.bdos_rcount ; last written record + CP (HL) + JP C, .disk_wr2 + ; update record count + LD (HL), A + INC (HL) + LD C, 2 +.disk_wr2: + DEC C ; patch: NOP + DEC C ; patch: NOP + JP NZ, .no_update ; patch: LD HL,0 + PUSH AF + CALL bdos_get_s2 ; set 'extent written to' flag + AND 0x7f ; clear 7th bit + LD (HL), A + POP AF ; record count + +.no_update: + ; it is full + CP 127 + JP NZ, .set_nxt_rec + ; sequential mode? + LD A, (CPM_VARS.bdos_seqio) + CP 1 + JP NZ, .set_nxt_rec + + CALL bdos_set_nxt_rec ; set next record + CALL bdos_open_reel ; get space in dir + LD HL, CPM_VARS.bdos_aret ; check status + LD A, (HL) + OR A + JP NZ, .no_space ; no more space + + DEC A + LD (CPM_VARS.bdos_vrecord), A ; -1 + +.no_space: + LD (HL), 0x00 ; clear status + +.set_nxt_rec: + JP bdos_set_nxt_rec + +; ------------------------------------------------------- +; Random access seek operation, C=0ffh if read mode +; FCB is assumed to address an active file control block +; (modnum has been set to 1100$0000b if previous bad seek) +; ------------------------------------------------------- +bdos_rseek: + XOR A + LD (CPM_VARS.bdos_seqio), A ; set random mode + +bdos_rseek1: + PUSH BC + LD HL, (CPM_VARS.bdos_info) + EX DE, HL + LD HL, FCB_RN ; Random access record number + ADD HL, DE + LD A, (HL) + AND 7Fh ; [6:0] bits record number to access + PUSH AF + ; get bit 7 + LD A, (HL) + RLA + INC HL + ; get high byte [3:0] bits + LD A, (HL) + RLA + AND 00011111b + ; get extent byte + LD C, A + LD A, (HL) + RRA + RRA + RRA + RRA + AND 0x0f + LD B, A + POP AF + INC HL + ; get next byte + LD L, (HL) + ; check overflow + INC L + DEC L + LD L, 6 + JP NZ, .seek_err ; error 6 if overflow + + ; save current record in FCB + LD HL, FCB_CR + ADD HL, DE + LD (HL), A + + ; check extent byte + LD HL, FCB_EXT + ADD HL, DE + LD A, C + SUB (HL) + JP NZ, .close ; not same as previous + ; check extra s2 byte + LD HL, FCB_S2 + ADD HL, DE + LD A, B + SUB (HL) + AND 0x7f + JP Z, .seek_ok ; same, is ok + +.close: + PUSH BC + PUSH DE + CALL bdos_close ; close current extent + POP DE + POP BC + LD L, 3 ; Cannot close error #3 + LD A, (CPM_VARS.bdos_aret) ; check status + INC A + JP Z, .bad_seek ; status != 0 - error + ; set extent byte to FCB + LD HL, FCB_EXT + ADD HL, DE + LD (HL), C + ; set S2 - extra extent byte + LD HL, FCB_S2 + ADD HL, DE + LD (HL), B + + CALL open + LD A, (CPM_VARS.bdos_aret) + INC A + JP NZ, .seek_ok + POP BC + PUSH BC + LD L, 0x4 ; Seek to unwritten extent #4 + INC C + JP Z, .bad_seek + CALL bdos_make + LD L, 0x5 ; Cannot create new extent #5 + LD A, (CPM_VARS.bdos_aret) + INC A + JP Z, .bad_seek +.seek_ok: + POP BC + XOR A + JP bdos_ret_a +.bad_seek: + PUSH HL + CALL bdos_get_s2 + LD (HL), 11000000b + POP HL +.seek_err: + POP BC + LD A,L + LD (CPM_VARS.bdos_aret), A + JP bdos_set_fwf + +; ------------------------------------------------------- +; Random disk read operation +; ------------------------------------------------------- +bdos_rand_disk_read: + LD C, 0xff ; mode + CALL bdos_rseek ; positioning + CALL Z, bdos_disk_read ; read if ok + RET + +; ------------------------------------------------------- +; Random disk write operation +; ------------------------------------------------------- +bdos_rand_disk_write: + LD C, 0 ; mode + CALL bdos_rseek ; positioning + CALL Z, bdos_disk_write ; read if ok + RET + +; ------------------------------------------------------- +; Compute random record position +; Inp: HL -> FCB +; DE - relative location of record number +; Out: C - r0 byte +; B - r1 byte +; A - r2 byte +; ZF set - Ok, reset - overflow +; ------------------------------------------------------- +bdos_compute_rr: + EX DE, HL ; DE -> FCB + ADD HL, DE ; HL = FCB+DE + LD C, (HL) ; get record number + LD B, 0 ; in BC + LD HL, FCB_EXT + ADD HL, DE + LD A, (HL) ; A = extent + ; calculate BC = recnum + extent*128 + RRCA ; A[0] -> A[7] + AND 0x80 ; ignore other bits + ADD A, C ; add to record number + LD C, A ; C=r0 + LD A, 0 + ADC A, B + LD B, A ; B=r1 + ; + LD A, (HL) ; A = extent + RRCA + AND 0x0f ; A = EXT[4:1] + ADD A, B ; add to r1 byte + LD B, A + + LD HL, FCB_S2 + ADD HL, DE + LD A, (HL) ; A = extra extent bits + ADD A, A ; *2 + ADD A, A ; *4 + ADD A, A ; *8 + ADD A, A ; *16 + PUSH AF ; save flags (need only CF) + ADD A, B ; add to r1 + LD B, A + PUSH AF ; save flags + POP HL ; flags on L + LD A, L ; A bit 0 - CF + POP HL + OR L ; merge both carry flags + AND 0x1 ; mask only CF + RET + +; ------------------------------------------------------- +; Compute logical file size for current FCB. +; Setup r0, r1, r2 bytes - maximum record number +; ------------------------------------------------------- +bdos_get_file_size: + LD C, FN_LEN + CALL bdos_search ; get first dir record (first extent) + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + ; zeroing r0, r1, r2 + LD DE, FCB_RN ; D=0 + ADD HL, DE + PUSH HL + LD (HL), D + INC HL + LD (HL), D + INC HL + LD (HL), D + ; +.get_extent: + CALL bdos_end_of_dir + JP Z, .done ; if not more extents found, return + CALL bdos_getdptra ; HL -> FCB + LD DE, FCB_RC + CALL bdos_compute_rr ; compute random access parameters + POP HL + PUSH HL + ; Now let's compare these values ​​with those indicated + LD E, A + LD A, C + SUB (HL) + INC HL + LD A, B + SBC A, (HL) + INC HL + LD A, E + SBC A, (HL) + JP C, .less_size + ; found larger extent (size), save it to fcb + LD (HL), E + DEC HL + LD (HL), B + DEC HL + LD (HL), C +.less_size: + CALL bdos_search_nxt + JP .get_extent +.done: + POP HL + RET + +; ------------------------------------------------------- +; Set the random record count bytes of the FCB to the number +; of the last record read/written by the sequential I/O calls. +; ------------------------------------------------------- +bdos_set_random: + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + LD DE, FCB_CR ; Current record within extent + CALL bdos_compute_rr ; Compute random access parameters + LD HL, FCB_RN + ADD HL, DE ; HL -> Random access record number + ; set r0, r1, r2 + LD (HL), C + INC HL + LD (HL), B + INC HL + LD (HL), A + RET + +; ------------------------------------------------------- +; Select disk to bdos_curdsk for subsequent input or +; output ops +; ------------------------------------------------------- +bdos_select: + LD HL, (CPM_VARS.bdos_dlog) ; login vector + LD A, (CPM_VARS.bdos_curdsk) ; current drive + LD C, A + CALL bdos_hlrotr ; shift active bit for this drive + PUSH HL + EX DE, HL + CALL bdos_sel_dsk ; select drive + POP HL + CALL Z, bdos_sel_error ; check for error drive + ; new active drive? + LD A, L + RRA + RET C ; no, return + ; yes, update login vector + LD HL, (CPM_VARS.bdos_dlog) + LD C, L + LD B, H + CALL bdos_set_dsk_bit ; set bits in vector + LD (CPM_VARS.bdos_dlog), HL ; store new vector + + JP bdos_initialize + +; ------------------------------------------------------- +; Select disc +; Inp: C=0x0E +; E=drive number 0-A, 1-B .. 15-P +; Out: L=A=0 - ok or 0xFF - error +; ------------------------------------------------------- +bdos_select_disk: + ; check disk change + LD A, (CPM_VARS.bdos_linfo) + LD HL, CPM_VARS.bdos_curdsk + CP (HL) + RET Z ; is same, return + ; login new disk + LD (HL), A + JP bdos_select + +; ------------------------------------------------------- +; Auto select disk by FCB_DR +; ------------------------------------------------------- +bdos_reselect: + LD A, TRUE + LD (CPM_VARS.bdos_resel), A ; set flag + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + LD A, (HL) ; get specified disk + AND 0x1f ; A = A[4:0] + DEC A ; adjust for 0-A, 1-B and so on + LD (CPM_VARS.bdos_linfo), A ; set as parameter + CP 0x1e ; no change? + JP NC, .no_select + ; change, but save current to old + LD A, (CPM_VARS.bdos_curdsk) + LD (CPM_VARS.bdos_olddsk), A + ; save FCB_DR byte + LD A, (HL) + LD (CPM_VARS.bdos_fcbdsk), A + ; clear FCB_DR[4:0] bits - user code + AND 11100000b + LD (HL), A + CALL bdos_select_disk + +.no_select: + ; set user to FCB_DR low bits + LD A, (CPM_VARS.bdos_userno) + LD HL, (CPM_VARS.bdos_info) + OR (HL) + LD (HL), A + RET + +; ------------------------------------------------------- +; Return version number +; ------------------------------------------------------- +bdos_get_version: + LD A, CPM_VERSION ; 0x22 - v2.2 + JP bdos_ret_a + +; ------------------------------------------------------- +; Reset disk system - initialize to disk 0 +; ------------------------------------------------------- +bdos_reset_disks: + LD HL, 0x00 + LD (CPM_VARS.bdos_rodsk), HL ; clear ro flags + LD (CPM_VARS.bdos_dlog), HL ; clear login vector + XOR A ; disk 0 + LD (CPM_VARS.bdos_curdsk), A ; set disk 'A' as current + LD HL, dma_buffer ; set 'DMA' buffer address to default + LD (CPM_VARS.bdos_dmaad), HL + CALL bdos_setdata + JP bdos_select ; select drive 'A' + +; ------------------------------------------------------- +; Open file +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_open_file: + CALL bdos_clear_s2 + CALL bdos_reselect + JP open + +; ------------------------------------------------------- +; Close file +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_close_file: + CALL bdos_reselect + JP bdos_close + +; ------------------------------------------------------- +; Search for first occurrence of a file in directory +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_search_first: + LD C, 0x00 ; special search + EX DE, HL + LD A, (HL) + CP '?' ; '?' is first byte? + JP Z, .qselect ; return first matched entry + + CALL bdos_getexta ; get extent byte from FCB + LD A, (HL) + CP '?' ; it is '?' + CALL NZ, bdos_clear_s2 ; set S2 to 0 if not + CALL bdos_reselect ; select disk by FCB + LD C, FCB_INFO_LEN ; match 1first 15 bytes +.qselect: + CALL bdos_search ; search first entry + JP bdos_dir_to_user ; move entry to user space + +; ------------------------------------------------------- +; Search for next occurrence of a file name +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_search_next: + LD HL, (CPM_VARS.bdos_searcha) ; restore FCB address + LD (CPM_VARS.bdos_info), HL ; set as parameter + CALL bdos_reselect ; select drive by FCB + CALL bdos_search_nxt ; search next + JP bdos_dir_to_user ; move entry to user space + +; ------------------------------------------------------- +; Remove directory +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_rm_file: + CALL bdos_reselect ; Select drive by FCB + CALL bdos_era_file ; Delete file + JP bdos_ret_dirloc ; Return directory location in A + +; ------------------------------------------------------- +; Read next 128b record +; Inp: DE -> FCB +; Out: BA and HL - error. +; A=0 - Ok, +; 1 - end of file, +; 9 - invalid FCB, +; 10 - media changed, +; 0FFh - hardware error. +; ------------------------------------------------------- +bdos_read_file: + CALL bdos_reselect ; select drive + JP bdos_seq_disk_read ; and read + +; ------------------------------------------------------- +; Write next 128b record +; Inp: DE -> FCB +; Out: BA and HL - error. +; A=0 - Ok, +; 1 - directory full, +; 2 - disc full, +; 9 - invalid FCB, +; 10 - media changed, +; 0FFh - hardware error. +; ------------------------------------------------------- +bdos_write_file: + CALL bdos_reselect ; select drive + JP bdos_seq_disk_write ; and write + +; ------------------------------------------------------- +; Create file +; Inp: DE -> FCB. +; Out: Error in BA and HL +; A=0 - Ok, +; 0FFh - directory is full. +; ------------------------------------------------------- +bdos_make_file: + CALL bdos_clear_s2 ; clear S2 byte + CALL bdos_reselect ; select drive + JP bdos_make ; and make file + +; ------------------------------------------------------- +; Rename file. New name, stored at FCB+16 +; Inp: DE -> FCB. +; Out: Error in BA and HL +; A=0-3 if successful; +; A=0FFh if error. +; ------------------------------------------------------- +bdos_ren_file: + CALL bdos_reselect ; select drive + CALL bdos_rename ; rename file + JP bdos_ret_dirloc ; Return directory location in A + +; ------------------------------------------------------- +; Return bitmap of logged-in drives +; Out: bitmap in HL. +; ------------------------------------------------------- +bdos_get_login_vec: + LD HL, (CPM_VARS.bdos_dlog) + JP sthl_ret + +; ------------------------------------------------------- +; Return current drive +; Out: A - currently selected drive. 0 => A:, 1 => B: etc. +; ------------------------------------------------------- +bdos_get_cur_drive: + LD A, (CPM_VARS.bdos_curdsk) + JP bdos_ret_a + +; ------------------------------------------------------- +; Set DMA address +; Inp: DE - address of DMA buffer +; ------------------------------------------------------- +bdos_set_dma_addr: + EX DE, HL + LD (CPM_VARS.bdos_dmaad), HL + JP bdos_setdata + +; ------------------------------------------------------- +; fn27: Return the address of cur disk allocation map +; Out: HL - address +; ------------------------------------------------------- +bdos_get_alloc_addr: + LD HL, (CPM_VARS.bdos_alloca) + JP sthl_ret + +; ------------------------------------------------------- +; Get write protection status +; ------------------------------------------------------- +bdos_get_wr_protect: + LD HL, (CPM_VARS.bdos_rodsk) + JP sthl_ret + +; ------------------------------------------------------- +; Set file attributes (ro, system) +; ------------------------------------------------------- +bdos_set_attr: + CALL bdos_reselect + CALL bdos_update_attrs + JP bdos_ret_dirloc + +; ------------------------------------------------------- +; Return address of disk parameter block of the +; current drive +; ------------------------------------------------------- +bdos_get_dpb: + LD HL, (CPM_VARS.bdos_dpbaddr) + +; ------------------------------------------------------- +; Common code to return Value from BDOS functions +; ------------------------------------------------------- +sthl_ret: + LD (CPM_VARS.bdos_aret), HL + RET + +; ------------------------------------------------------- +; Get/set user number +; Inp: E - 0..15 - set user. 0xFF - get user +; Out: A - returns user number +; ------------------------------------------------------- +bdos_set_user: + LD A, (CPM_VARS.bdos_linfo) + CP 0xff + JP NZ, .set_user + LD A, (CPM_VARS.bdos_userno) + JP bdos_ret_a +.set_user: + AND 0x1f ; mask for 0..31 but will be < 16 + LD (CPM_VARS.bdos_userno), A + RET + +; ------------------------------------------------------- +; Random read. Record specified in the random record +; count area of the FCB, at the DMA address +; Inp: DE -> FCB +; Out: Error codes in BA and HL. +; ------------------------------------------------------- +bdos_rand_read: + CALL bdos_reselect ; select drive by FCB + JP bdos_rand_disk_read ; and read + +; ------------------------------------------------------- +; Random access write record. +; Record specified in the random record count area of the FCB, at the DMA address +; Inp: DE -> FCB +; Out: Error codes in BA and HL. +; ------------------------------------------------------- +bdos_rand_write: + CALL bdos_reselect ; select drive by FCB + JP bdos_rand_disk_write ; and write + +; ------------------------------------------------------- +; Compute file size. +; Set the random record count bytes of the FCB to the +; number of 128-byte records in the file. +; ------------------------------------------------------- +bdos_compute_fs: + CALL bdos_reselect ; select drive by FCB + JP bdos_get_file_size ; and get file size + +; ------------------------------------------------------- +; Selectively logoff (reset) disc drives +; Out: A=0 - Ok, 0xff if error +; ------------------------------------------------------- +bdos_reset_drives: + LD HL, (CPM_VARS.bdos_info) ; HL - drives to logoff map + LD A, L + CPL + LD E, A + LD A, H + CPL + LD HL, (CPM_VARS.bdos_dlog) ; get vector + AND H ; reset in hi byte + LD D, A ; and store in D + LD A, L ; reset low byte + AND E + LD E, A ; and store in E + LD HL, (CPM_VARS.bdos_rodsk) ; HL - disk RO map + EX DE, HL + LD (CPM_VARS.bdos_dlog), HL ; store new login vector + ; reset RO flags + LD A, L + AND E + LD L, A + LD A, H + AND D + LD H, A + ; and store new value + LD (CPM_VARS.bdos_rodsk), HL + RET + +; ------------------------------------------------------- +; Reach this point at the end of processing +; to return the data to the user. +; ------------------------------------------------------- +bdos_goback: + LD A, (CPM_VARS.bdos_resel) ; check for selection drive by user FCB + OR A + JP Z, bdos_ret_mon ; return if not selected + + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB_DR + LD (HL), 0 + LD A, (CPM_VARS.bdos_fcbdsk) + OR A ; restore drive + JP Z, bdos_ret_mon ; return if default + LD (HL), A ; restore + LD A, (CPM_VARS.bdos_olddsk) ; previous drive + LD (CPM_VARS.bdos_linfo), A ; set parameter + CALL bdos_select_disk ; select previous drive + +; ------------------------------------------------------- +; Return from the disk monitor +; ------------------------------------------------------- +bdos_ret_mon: + LD HL, (CPM_VARS.bdos_usersp) ; return user stack + LD SP, HL + LD HL, (CPM_VARS.bdos_aret) ; get return value + LD A, L ; ver 1.4 CP/M compatibility + LD B, H + RET + +; ------------------------------------------------------- +; Random disk write with zero fill of +; unallocated block +; ------------------------------------------------------- +bdos_rand_write_z: + CALL bdos_reselect ; select drive + LD A, 2 ; special write mode + LD (CPM_VARS.bdos_seqio), A + LD C, FALSE ; write indicator + CALL bdos_rseek1 ; seek position in a file + CALL Z, bdos_disk_write ; write if no errors + RET + +; ------------------------------------------------------- +; Unuser initialized data +; ------------------------------------------------------- +filler: + DB 0xF1, 0xE1 ; POP AF, POP HL + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xC800 +FILL_SIZE EQU 0xE00-CODE_SIZE + + DISPLAY "| BDOS\t| ",/H,bdos_start," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ASSERT bdos_rand_write_z = 0xd55e + + +FILLER + DS FILL_SIZE, 0xFF + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_099942fc/bios.asm b/CPM_v2.2_r8_099942fc/bios.asm new file mode 100644 index 0000000..8673ef3 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/bios.asm @@ -0,0 +1,823 @@ +; ======================================================= +; Ocean-240.2 +; +; CP/M BIOS +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "mon_entries.inc" + + IFNDEF BUILD_ROM + OUTPUT bios.bin + ENDIF + + MODULE BIOS + + ORG 0xD600 + +; ------------------------------------------------------- +; BIOS JUMP TABLE +; ------------------------------------------------------- +boot_f: + JP bios_boot +wboot_f: + JP bios_wboot + +; ------------------------------------------------------- +; console status to reg-a +; ------------------------------------------------------- +const_f: + JP MON.non_con_status + +; ------------------------------------------------------- +; console character to reg-a +; ------------------------------------------------------- +conin_f: + JP MON.mon_con_in + +; ------------------------------------------------------- +; console character from c to console out +; ------------------------------------------------------- +conout_f: + JP MON.mon_con_out + +; ------------------------------------------------------- +; list device out +; ------------------------------------------------------- +list_f: + JP MON.mon_char_print + +; ------------------------------------------------------- +; punch device out +; ------------------------------------------------------- +punch_f: + JP MON.mon_serial_out + +; ------------------------------------------------------- +; reader character in to reg-a +; ------------------------------------------------------- +reader_f: + JP MON.mon_serial_in + +; ------------------------------------------------------- +; move to home position, treat as track 00 seek +; ------------------------------------------------------- +home_f: + JP home + +; ------------------------------------------------------- +; select disk given by register c +; ------------------------------------------------------- +seldsk_f: + JP sel_disk +settrk_f: + JP set_trk +setsec_f: + JP set_sec + +; ------------------------------------------------------- +; Set DMA address from BC +; ------------------------------------------------------- +setdma_f: + JP set_dma +read_f: + JP read +write_f: + JP write +listst_f: + JP list_st +sectran_f: + JP sec_tran + +; ------------------------------------------------------- +; Reserved +; ------------------------------------------------------- + JP warm_boot + JP warm_boot + +; ------------------------------------------------------- +; Tape read +; ------------------------------------------------------- +tape_read_f: + JP MON.mon_tape_read + +; ------------------------------------------------------- +; Tape write +; ------------------------------------------------------- +tape_write_f: + JP MON.mon_tape_write + +; ------------------------------------------------------- +; Tape wait block +; ------------------------------------------------------- +tape_wait_f: + JP MON.mon_tape_wait + + JP warm_boot ; r8 + +disk_a_size DW 0x0040 ; 64 disk A size +disk_b_size DW 0x02d0 ; 720 disk B size +disk_c_size DW 0x02d0 ; 720 disk C size +bios_var04 DB 0x50 +bios_var05 DB 0x50 + +; ------------------------------------------------------- +; cold start +; ------------------------------------------------------- +bios_boot: + LD HL, (bdos_ent_addr) ; 0xba06 + LD DE, 0x10000-CCP_RAM.bdos_enter_jump ; 0x45fa + ADD HL, DE ; 0xba06+0x45fa=10000 + LD A, H + OR L + JP Z, bios_signon + + ; >> r8 + LD HL, (bios_var0) ; if bios_var0 = 0xaaaa (initialized) + LD DE, 0x5556 + ADD HL, DE ; 0xaaaa+0x5556=0x10000 if initialized + LD A, H + OR L + JP Z, bios_signon ; if initialized, go to logon, skip init + ; << + + ; Init DMA buffer + LD HL, dma_buffer ; 0x8000 + LD B, DMA_BUFF_SIZE ; 0x80 +.init_dma_buff: + LD (HL), FILE_DELETED ; 0xE5 + INC HL + DEC B + JP NZ, .init_dma_buff + + ; Init RAM disk + LD HL, dma_buffer + LD DE, 0x0000 + LD B, 8 + +.init_ram_dsk: + PUSH BC + CALL MON.ram_disk_write + POP BC + INC DE + DEC B + JP NZ, .init_ram_dsk + + ; Init user to 0 and drive to 0 + XOR A + LD (cur_user_drv), A + + ; init bios variables + CALL bios_init_ilv ; r8 + +bios_signon: + LD SP, bios_stack + ; Print CP/M hello message + LD HL, msg_hello + CALL print_strz + JP bios_wboot + +;bios_wboot: +; LD SP, bios_stack + +; r8 >> +bios_init_ilv: + LD HL, bios_var0 + LD DE, bios_ini_vals + LD C, 13 + CALL mov_dlhe_c + LD A, (bios_var05) ; 0x50 + LD (bios_var2), A + RET +; << + +; ------------------------------------------------------- +; BIOS Warm start entry +; ------------------------------------------------------- +bios_wboot: ; r8 + + LD HL, (bios_var0) + LD DE, 0x5556 ; 0xaaaa + 0x5556 = 0x10000 + ADD HL, DE + LD A, H + OR L + CALL NZ, bios_init_ilv ; init if not initialized before + LD SP, bios_stack + + ; Move CPP from 0xC000 to 0xB200 + LD HL, CCP_DST_ADDR + LD DE, CCP_SRC_ADDR + LD BC, CCP_SIZE +.move_ccp: + LD A, (DE) + LD (HL), A + INC DE + INC HL + DEC BC + LD A, B + OR C + JP NZ, .move_ccp + + ; Init variables with zeroes + LD HL, CPM_VARS.cpm_vars_start + LD BC, CPM_VARS.ccp_vars_size ; 213 + +.clr_cpm_vars: + LD (HL), 0x0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .clr_cpm_vars + + LD A, FILE_DELETED + LD (CPM_VARS.bdos_efcb), A ; mark empty FCB + LD A, low dma_buffer ; 0x80 + LD (CPM_VARS.bdos_dmaad), A ; 0x0080 + + ; Move DPH + DPB to RAM + LD HL, CPM_VARS.DPH_RAM + LD DE, dph_disk_a + LD BC, DPB_END-dph_disk_a ; 0x39 -> 57d + +.move_dph: + LD A, (DE) + LD (HL), A + INC HL + INC DE + DEC BC + LD A, B + OR C + JP NZ, .move_dph + + LD BC, dma_buffer ; DMA default buffer addr + CALL setdma_f + + ; Setup JP to Warm boot after CPU reset + LD A, JP_OPCODE + LD (warm_boot), A + LD HL, wboot_f + LD (warm_boot_addr), HL + + ; Setup JP to BDOS entrance + LD (jp_bdos_enter), A + LD HL, CCP_RAM.bdos_enter_jump + LD (bdos_ent_addr), HL + + LD HL, CPM_VARS.DPB_A_RAM + LD C, 0xf + LD DE, dpb_ram + LD A, (disk_a_size+1) + OR A + JP Z, set_curr_dpb + LD DE, dpb_empty + +set_curr_dpb: + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_B_RAM + LD C, 0xf + LD DE, dpb_flop_360k + LD A, (disk_b_size+1) + CP 0x1 + JP Z, .l1 + LD DE, dpb_flop_720k +.l1 + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_C_RAM + LD C, 0xf + LD A, (disk_c_size+1) + CP 0x2 + JP Z, .l2 + LD DE, dpb_flop_360k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_720k + JP .l3 +.l2 + LD DE, dpb_flop_720k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_360k +.l3 + CALL mov_dlhe_c + XOR A + LD (CPM_VARS.slicer_has_data),A + LD (CPM_VARS.slicer_uninited_count),A + LD A,(cur_user_drv) + LD C,A + ; go to CCP + JP CCP_DST_ADDR + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +mov_dlhe_c: + LD A,(DE) ; [DE]->[HL] + LD (HL),A + INC HL + INC DE + DEC C + JP NZ, mov_dlhe_c + RET + +list_st: + XOR A + RET + +; ------------------------------------------------------- +; Select disk +; Inp: C - disk, +; E - active drive flag +; Out: HL -> DPH +; ------------------------------------------------------- +sel_disk: + LD HL, 0x00 + LD A, C + CP CTRL_C + RET NC + + LD (CPM_VARS.cur_disk), A + OR A + JP Z, .get_dph_addr ; skip next for disk 0 - RAM disk + LD A, E ; selected disk map + AND 0x1 ; bit 0 is set if disk already selected + JP NZ, .get_dph_addr + ; Reset disk + LD (CPM_VARS.slicer_has_data), A + LD (CPM_VARS.slicer_uninited_count), A + ; calc DPH address of new drive +.get_dph_addr: + LD L, C + LD H, 0x0 + ADD HL, HL ; *2 + ADD HL, HL ; *4 + ADD HL, HL ; *8 + ADD HL, HL ; *16 (size of DBH) + LD DE, CPM_VARS.DPH_RAM + ADD HL, DE + RET + +; ------------------------------------------------------- +; move to track 00 +; ------------------------------------------------------- +home: + LD A, (CPM_VARS.cur_disk) + OR A + JP Z, .is_default + LD A, (CPM_VARS.slicer_need_save) + OR A + JP NZ, .is_default + LD (CPM_VARS.slicer_has_data), A ; set to 0, no data + +.is_default: + LD C, 0 ; set track to 0 + +; ------------------------------------------------------- +; set track address (0..76) for subsequent read/write +; ------------------------------------------------------- +set_trk: + LD HL, CPM_VARS.curr_track + LD (HL), C + RET + +; ------------------------------------------------------- +; set sector address (1,..., 26) for subsequent read/write +; ------------------------------------------------------- +set_sec: + LD HL, CPM_VARS.curr_sec + LD (HL), C + RET + +; ------------------------------------------------------- +; set subsequent dma address (initially 80h) +; ------------------------------------------------------- +set_dma: + LD L, C + LD H, B + LD (CPM_VARS.dma_addr), HL + RET + +sec_tran: + LD L, C + LD H, B + RET + +; ------------------------------------------------------- +; read track/sector to preset dma address +; ------------------------------------------------------- +read: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ, read_phys ; for physical disk use special routine + CALL ram_disk_calc_addr + CALL MON.ram_disk_read + XOR A + RET + +; ------------------------------------------------------- +; write track/sector from preset dma address +; ------------------------------------------------------- +write: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ,write_phys + CALL ram_disk_calc_addr + CALL MON.ram_disk_write + XOR A + RET + +; ------------------------------------------------------- +; Calculate address for current sector and track +; ------------------------------------------------------- +ram_disk_calc_addr: + LD HL, CPM_VARS.curr_track + ; HL = cur_track * 16 + LD L, (HL) + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ; DE = HL + cur_sec + EX DE, HL + LD HL, CPM_VARS.curr_sec + LD L, (HL) + LD H, 0x0 + ADD HL, DE + EX DE, HL + ; store address + LD HL, (CPM_VARS.dma_addr) + RET + +read_phys: + CALL read_phys_op + RET + +write_phys: + CALL write_phys_op + RET + +read_phys_op: + XOR A + ; reset counter + LD (CPM_VARS.slicer_uninited_count), A + LD A, 0x1 + LD (CPM_VARS.tmp_slicer_operation), A ; 0 - write; 1 - read + LD (CPM_VARS.tmp_slicer_can_read), A ; enable read fron disk + LD A, 0x2 + LD (CPM_VARS.tmp_slicer_flush), A ; disable flush data to disk + JP base_read_write + +write_phys_op: + XOR A + LD (CPM_VARS.tmp_slicer_operation), A + LD A, C + LD (CPM_VARS.tmp_slicer_flush), A + CP 0x2 + JP NZ, .mode_ne_2 + LD A, 0x10 ; 2048/128 + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_uninited_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_uninited_track), A + LD A, (CPM_VARS.curr_sec) + LD (CPM_VARS.slicer_uninited_sector_128), A +.mode_ne_2: + LD A, (CPM_VARS.slicer_uninited_count) + OR A + JP Z, slicer_read_write + DEC A + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_uninited_disk + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_uninited_track + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_sec) + LD HL, CPM_VARS.slicer_uninited_sector_128 + CP (HL) + JP NZ, slicer_read_write + INC (HL) + LD A, (HL) + CP 36 ; Sectors per track + JP C, .no_inc_track + LD (HL), 0x0 + LD A, (CPM_VARS.slicer_uninited_track) + INC A + LD (CPM_VARS.slicer_uninited_track), A + +.no_inc_track: + XOR A + LD (CPM_VARS.tmp_slicer_can_read), A + JP base_read_write + +; -------------------------------------------------- +slicer_read_write: + XOR A + LD (CPM_VARS.slicer_uninited_count), A + INC A + LD (CPM_VARS.tmp_slicer_can_read), A + +; -------------------------------------------------- +base_read_write: + XOR A + LD (CPM_VARS.tmp_slicer_result), A + LD A, (CPM_VARS.curr_sec) + OR A + RRA + OR A + RRA + LD (CPM_VARS.tmp_slicer_real_sector), A + LD HL, CPM_VARS.slicer_has_data + LD A, (HL) + LD (HL), 0x1 + OR A + JP Z, .no_data + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_disk + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_track + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD HL, CPM_VARS.slicer_real_sector + CP (HL) + JP Z,calc_sec_addr_in_bfr +.pos_diff: + LD A, (CPM_VARS.slicer_need_save) + OR A + CALL NZ, slicer_save_buffer ; save buffer if needed +.no_data: + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_track), A + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD (CPM_VARS.slicer_real_sector), A + LD A, (CPM_VARS.tmp_slicer_can_read) + OR A + CALL NZ,slicer_read_buffer + XOR A + LD (CPM_VARS.slicer_need_save), A + +; -------------------------------------------------- +calc_sec_addr_in_bfr: + LD A, (CPM_VARS.curr_sec) + AND 0x3 + LD L, A + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL ; *128 + LD DE, CPM_VARS.slicer_buffer + ADD HL, DE + EX DE, HL + LD HL, (CPM_VARS.dma_addr) + LD C, 0x80 + LD A, (CPM_VARS.tmp_slicer_operation) + OR A + JP NZ, .no_save + LD A, 0x1 + LD (CPM_VARS.slicer_need_save), A + EX DE, HL +.no_save: + LD A, (DE) + INC DE + LD (HL), A + INC HL + DEC C + JP NZ, .no_save + LD A, (CPM_VARS.tmp_slicer_flush) + CP 0x1 + LD A, (CPM_VARS.tmp_slicer_result) + RET NZ + OR A + RET NZ + XOR A + LD (CPM_VARS.slicer_need_save), A + CALL slicer_save_buffer + LD A, (CPM_VARS.tmp_slicer_result) + RET + +; -------------------------------------------------- +slicer_save_buffer: + CALL slicer_get_floppy_args + LD C, 0xA6 ; VG93 CMD + CALL MON.write_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_read_buffer: + CALL slicer_get_floppy_args + LD C, 0x86 ; VG93 CMD + CALL MON.read_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_get_floppy_args: + LD HL, CPM_VARS.tmp_buff9 + LD A, (CPM_VARS.slicer_disk) + DEC A + JP Z, .non_interleave + LD HL, interleave_0 +.non_interleave + LD A, (CPM_VARS.slicer_real_sector) + ADD A, L + LD L, A + LD E, (HL) + LD A, (CPM_VARS.slicer_track) + LD D, A + LD HL, CPM_VARS.slicer_buffer + LD A, (CPM_VARS.slicer_disk) + RET + + +; ------------------------------------------------------- +; Print zerro ended string +; Inp: HL -> string +; ------------------------------------------------------- +print_strz: + LD A, (HL) + OR A + RET Z + LD C, A + PUSH HL + CALL conout_f + POP HL + INC HL + JP print_strz + +msg_hello: + DB ASCII_ESC, "60" ; Режим 32x18 60 + DB ASCII_ESC, "83" + DB ASCII_ESC, "5!%" + DB ASCII_ESC, "41" + DB ASCII_ESC, "1", 0x16, 0xe2, 0xe2, 0xfc, 0x01 + DB ASCII_ESC, "40" + DB ASCII_ESC, "1", 0x1e, 0xe6, 0xdb, 0xf8, 0x01 + DB ASCII_ESC, "43" + DB "OKEAH-240 CP/M (V2.2) REL.8 \r\n\n" + DB ASCII_ESC, "42", 0 + + +; -------------------------------------------------- +; Disk parameters blocks in ROM (DPBs) +; Tables of memory that describe the characteristics +; of discs on our system. There is one DPB for each +; disc type + +; ---------------------------------------- +; Block size | No of sectors | BSH | BLM | +; ---------------------------------------- +; 1K | 8 | 3 | 7 | +; 2K | 16 | 4 | 15 | +; 4K | 32 | 5 | 31 | +; 8K | 64 | 6 | 63 | +; 16K | 128 | 7 | 127 | +; ---------------------------------------- + +; ------------------------------------- +; Block size| Extent mask (EXM) | +; | Small disk | Large disk | +; ------------------------------------- +; 2K | 1 | 0 | +; 4K | 3 | 1 | +; 8K | 7 | 3 | +; 16K | 15 | 7 | +; ------------------------------------- +; CKS - number of dir sectors to check before write, 0 for HDD + +; For RAM-Disk 64k +dpb_ram: + DW 0010h ; SPT Sector (128b) per track (16d) + DB 03h ; BSH 1k + DB 07h ; BLM 1k; Allocation block size = (BLM + 1) * 128 = 1k + DB 00h ; EXM extent mask + DW 003Fh ; DSM Disk size blocks - 1 (63d) + DW 001Fh ; DRM Dir elements - 1 (31d) + DB 10000000b ; AL0 Dir map byte 1 + DB 00000000b ; AL1 Dir map byte 2 + DW 0008h ; CKS checksum vector size (8 sectors=1k) + DW 0000h ; OFF (tracks reserved for system) + +dpb_empty: + DS 15, 0xff + +; For FLOPPY 720k +dpb_flop_720k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 00h ; EXM extent mask + DW 359 ; DSM Disk size blocks - 1 (359d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +; For FLOPPY 360k +dpb_flop_360k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 01h ; EXM extent mask + DW 179 ; DSM Disk size blocks - 1 (179d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +bios_ini_vals: + DB 0xaa, 0xaa, 0, 0xff, 1, 8, 6, 4, 2, 9, 7, 5, 3 + +; -------------------------------------------------- +; Disk parameters headers in ROM +; -------------------------------------------------- +; Disk A RAM +dph_disk_a: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + ; 0xda00 + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_A_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_A ; Check Vector pointer + DW CPM_VARS.AL_MAP_A ; Allocation map pointer + +; Disk B Floppy +dph_disk_b: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_B_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_B ; Check Vector pointer + DW CPM_VARS.AL_MAP_B ; Allocation map pointer + +; Disk C Floppy +dph_disk_c: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_C_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_C ; Check Vector pointer + DW CPM_VARS.AL_MAP_C ; Allocation map pointer + + + +res_data: ; 0xda28 + DB 1,8,6,4,2,9,7,5,3 +DPB_END EQU $ + DB 0x0e, 3 + + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xD600 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| BIOS\t| ",/H,boot_f," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT bios_wboot = 0xd6a8 + ASSERT set_curr_dpb = 0xd722 + ASSERT sel_disk = 0xd781 + ASSERT home = 0xd7a7 + ASSERT ram_disk_calc_addr = 0xd7eb + ASSERT write_phys_op = 0xd81e + ASSERT base_read_write = 0xd88a + ASSERT calc_sec_addr_in_bfr = 0xd8e4 + ASSERT slicer_save_buffer = 0xd928 + ASSERT print_strz = 0xd95e + ASSERT msg_hello = 0xd96b + ASSERT dpb_ram = 0xd9af + ASSERT dph_disk_a = 0xd9f8 + ASSERT res_data = 0xda28 + +FILLER: + DS FILL_SIZE, 0xff + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_099942fc/ccp_ram.asm b/CPM_v2.2_r8_099942fc/ccp_ram.asm new file mode 100644 index 0000000..a2ecada --- /dev/null +++ b/CPM_v2.2_r8_099942fc/ccp_ram.asm @@ -0,0 +1,1525 @@ +; ====================================================== +; Ocean-240.2 +; CP/M V2.2 R8. +; CCP (Console Command Processor) Resident part +; ORG C000 at RPM, moved to B200-BA09 +; +; Disassembled by Romych 2026-02-01 +; ====================================================== + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + + IFNDEF BUILD_ROM + OUTPUT ccp_ram.bin + ENDIF + + MODULE CCP_RAM + + ORG 0xB200 + +ccp_ram_ent: + JP ccp_ent + JP ccp_clear_buff + +ccp_inbuff: + DB 0x7F, 0x00 + +ccp_inp_line: + DB ASCII_SP, ASCII_SP + +msg_copyright: + DB " COPYRIGHT (C) 1979, DIGITAL RESEARCH ", 0x00 + DS 73, 0x0 + +ccp_inp_line_addr: + DW ccp_inp_line + +cur_name_ptr: + DW 0h + +; --------------------------------------------------- +; Call BDOS function 2 (C_WRITE) - Console output +; Inp: A - char to output +; --------------------------------------------------- +ccp_print: + LD E, A + LD C, 2 + JP jp_bdos_enter + +; --------------------------------------------------- +; Put char to console +; Inp: A - char +; --------------------------------------------------- +ccp_putc: + PUSH BC + CALL ccp_print + POP BC + RET + +ccp_out_crlf: + LD A, ASCII_CR + CALL ccp_putc + LD A, ASCII_LF + JP ccp_putc +ccp_out_space: + LD A,' ' + JP ccp_putc + +; --------------------------------------------------- +; Out message from new line +; Inp: BC -> Message +; --------------------------------------------------- +ccp_out_crlf_msg: + PUSH BC + CALL ccp_out_crlf + POP HL + +; --------------------------------------------------- +; Out asciiz message +; Inp: HL -> Message +; --------------------------------------------------- +ccp_out_msg: + LD A, (HL) ;= "READ ERROR" + OR A + RET Z + INC HL + PUSH HL + CALL ccp_print + POP HL + JP ccp_out_msg + +; --------------------------------------------------- +; Call BDOS function 13 (DRV_ALLRESET) - Reset discs +; --------------------------------------------------- +ccp_bdos_drv_allreset: + LD C,13 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS function 14 (DRV_SET) - Select disc +; Inp: A - disk +; --------------------------------------------------- +ccp_bdos_drv_set: + LD E, A + LD C, 14 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS fn and return result +; Inp: C - fn no +; Out: A - error + 1 +; --------------------------------------------------- +ccp_call_bdos: + CALL jp_bdos_enter + LD (ccp_bdos_result_code), A + INC A + RET + +; --------------------------------------------------- +; BDOS function 15 (F_OPEN) - Open file /Dir +; Inp: DE -> FCB +; Out: A=0 for error, or 1-4 for success +; --------------------------------------------------- +ccp_bdos_open_f: + LD C, 15 + JP ccp_call_bdos + +; --------------------------------------------------- +; Open file by current FCB +; --------------------------------------------------- +ccp_open_cur_fcb: + XOR A + LD (ccp_current_fcb_cr), A ; clear current record counter + LD DE, ccp_current_fcb + JP ccp_bdos_open_f + +; --------------------------------------------------- +; BDOS function 16 (F_CLOSE) - Close file +; --------------------------------------------------- +ccp_bdos_close_f: + LD C, 16 + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 17 (F_SFIRST) - search for first +; Out: A = 0 in error, 1-4 if success +; --------------------------------------------------- +ccp_bdos_find_first: + LD C, 17 + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 18 (F_SNEXT) - search for next +; Out: A = 0 in error, 1-4 if success +; --------------------------------------------------- +ccp_bdos_find_next: + LD C, 18 + JP ccp_call_bdos ; BDOS 18 (F_SNEXT) - search for next ? + +; --------------------------------------------------- +; Call BDOS F_FIRST with current FCB +; --------------------------------------------------- +ccp_find_first: + LD DE, ccp_current_fcb + JP ccp_bdos_find_first + +; --------------------------------------------------- +; Call BDOS function 19 (F_DELETE) - delete file +; --------------------------------------------------- +ccp_bdos_era_file: + LD C,19 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS and set ZF by result +; --------------------------------------------------- +ccp_bdos_enter_zf: + CALL jp_bdos_enter + OR A + RET + +; --------------------------------------------------- +; Read next 128 bytes of file +; Inp: DE -> FCB +; Out: a = 0 - ok; +; 1 - EOF; +; 9 - invalid FCB; +; 10 - Media changed; +; 0xFF - HW error. +; --------------------------------------------------- +ccp_bdos_read_f: + LD C, 20 + JP ccp_bdos_enter_zf + +; --------------------------------------------------- +; Read file by current FCB +; --------------------------------------------------- +ccp_read_f_fcb: + LD DE, ccp_current_fcb + JP ccp_bdos_read_f + +; --------------------------------------------------- +; Call BDOS function 21 (F_WRITE) - write next record +; --------------------------------------------------- +ccp_bdos_f_write: + LD C, 21 + JP ccp_bdos_enter_zf + +; --------------------------------------------------- +; Call BDOS function 22 (F_MAKE) - create file +; --------------------------------------------------- +ccp_bdos_make_f: + LD C, 22 + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 23 (F_RENAME) - Rename file +; --------------------------------------------------- +ccp_bdos_rename_f: + LD C, 23 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS function 32 (F_USERNUM) for get user number +; Out: A - user no +; --------------------------------------------------- +ccp_bdos_get_user: + LD E, 0xff + +; --------------------------------------------------- +; Call BDOS function 32 (F_USERNUM) set user number +; Inp: E - user no 0-15, or 0xFF for get user +; Out: A - user no +; --------------------------------------------------- +ccp_bdos_set_user: + LD C, 32 + JP jp_bdos_enter + +; --------------------------------------------------- +; Get user no and store in upper nibble of sys var +; --------------------------------------------------- +ccp_set_cur_drv: + CALL ccp_bdos_get_user + ADD A, A + ADD A, A + ADD A, A + ADD A, A ; user no at upper nibble + LD HL, ccp_cur_drive + OR (HL) + LD (cur_user_drv), A + RET + +; --------------------------------------------------- +; Replace new drive in lower nibble of sys var +; reset user no +; Out: A - drive no +; --------------------------------------------------- +ccp_reset_cur_drv: + LD A, (ccp_cur_drive) + LD (cur_user_drv), A + RET + +; --------------------------------------------------- +; Uppercase character a..z +; Inp: A - character +; Out: A - character in upper case +; --------------------------------------------------- +char_to_upper: + CP 'a' + RET C + CP '{' + RET NC + ; character in a..z range + AND 0x5f + RET + +; --------------------------------------------------- +; Get user input from console or from batch +; file $$$.SUB +; --------------------------------------------------- +ccp_get_inp: + LD A, (ccp_batch) + OR A + JP Z, ccp_gin_no_batch + + ; select drive A for $$$.SUB + LD A, (ccp_cur_drive) + OR A + LD A, 0x0 + CALL NZ, ccp_bdos_drv_set + + ; open batch file + LD DE, ccp_batch_fcb + CALL ccp_bdos_open_f + JP Z, ccp_gin_no_batch ; go to con inp if file not found + + ; read last record + LD A, (ccp_batch_fcb_rc) + DEC A + LD (ccp_batch_fcb_cr), A + LD DE, ccp_batch_fcb + CALL ccp_bdos_read_f + JP NZ, ccp_gin_no_batch ; stop on EOF + + ; move data from file to buffer + LD DE, ccp_inbuff+1 + LD HL, dma_buffer + LD B, DMA_BUFF_SIZE ; 0x80 + CALL ccp_mv_hlde_b + LD HL, ccp_batch_fcb_s2 + LD (HL), 0x0 + INC HL + DEC (HL) ; decriment record count + ; close batch file + LD DE, ccp_batch_fcb + CALL ccp_bdos_close_f + JP Z, ccp_gin_no_batch + ; reselect old drive if not 0 + LD A, (ccp_cur_drive) + OR A + CALL NZ, ccp_bdos_drv_set + ; print command line + LD HL, ccp_inp_line ; ccp_inbuff+2 + CALL ccp_out_msg + CALL ccp_getkey_no_wait + JP Z, ccp_gin_nokey + ; terminate batch processing on any key + CALL ccp_del_batch + JP ccp_get_command + + ; get user input from keyboard +ccp_gin_no_batch: + CALL ccp_del_batch + CALL ccp_set_cur_drv + + LD C, 10 + LD DE, ccp_inbuff + ; Call BDOS C_READSTR DE -> inp buffer + CALL jp_bdos_enter + + CALL ccp_reset_cur_drv + +ccp_gin_nokey: + LD HL, ccp_inbuff+1 + LD B, (HL) + +ccp_gin_uppr: + INC HL + LD A, B + OR A + JP Z, ccp_gin_uppr_end + LD A, (HL) ;= 2020h + CALL char_to_upper + LD (HL), A ;= 2020h + DEC B + JP ccp_gin_uppr + +ccp_gin_uppr_end: + LD (HL), A ; set last character to 0 + LD HL, ccp_inp_line ; + LD (ccp_inp_line_addr), HL ; + RET + +; --------------------------------------------------- +; Check keyboard +; Out: A - pressed key code +; ZF set if no key pressed +; --------------------------------------------------- +ccp_getkey_no_wait: + LD C,11 + ; Call BDOS (C_STAT) - Console status + CALL jp_bdos_enter + OR A + RET Z ; ret if no character waiting + LD C,1 + ; Call BDOS (C_READ) - Console input + CALL jp_bdos_enter + OR A + RET + +; --------------------------------------------------- +; Call BDOS function 25 (DRV_GET) - Return current drive +; Out: A - drive 0-A, 1-B... +; --------------------------------------------------- +ccp_bdos_drv_get: + LD C, 25 + JP jp_bdos_enter + +; --------------------------------------------------- +; Set disk buffer address to default buffer +; --------------------------------------------------- +cpp_set_disk_buff_addr: + LD DE, dma_buffer + +; --------------------------------------------------- +; Call BDOS function 26 (F_DMAOFF) - Set DMA address +; Inp: DE - address +; --------------------------------------------------- +ccp_bdos_dma_set: + LD C, 26 + JP jp_bdos_enter + +; --------------------------------------------------- +; Delete batch file created by submit +; --------------------------------------------------- +ccp_del_batch: + LD HL, ccp_batch + LD A, (HL) + OR A + RET Z ; return if no active batch file + LD (HL), 0x0 ; mark as inactive + XOR A + CALL ccp_bdos_drv_set ; select drive 0 + LD DE, ccp_batch_fcb + CALL ccp_bdos_era_file + LD A, (ccp_cur_drive) + JP ccp_bdos_drv_set + +; -------------------------------------------------- +; Check "serial number" of CP/M +; -------------------------------------------------- +ccp_verify_pattern: + LD DE, cpm_pattern ; = F9h + LD HL, 0x0000 + LD B, 6 + +ccp_chk_patt_nex: + LD A, (DE) + CP (HL) + NOP ; JP NZ HALT was here + NOP + NOP + INC DE + INC HL + DEC B + JP NZ, ccp_chk_patt_nex + RET + +; -------------------------------------------------- +; Print syntax error indicator +; -------------------------------------------------- +print_syn_err: + CALL ccp_out_crlf + LD HL, (cur_name_ptr) +pse_next: + LD A, (HL) + CP ASCII_SP + JP Z, pse_end + OR A + JP Z, pse_end + PUSH HL + + CALL ccp_print + POP HL + INC HL + JP pse_next +pse_end: + LD A, '?' + CALL ccp_print + CALL ccp_out_crlf + CALL ccp_del_batch + JP ccp_get_command + +; -------------------------------------------------- +; Check user input characters for legal range +; Inp: [DE] - pointer to character +; -------------------------------------------------- +cpp_valid_inp: + LD A, (DE) + OR A + RET Z + CP ASCII_SP ; >= Space + JP C, print_syn_err + RET Z + CP '=' + RET Z + CP '_' + RET Z + CP '.' + RET Z + CP ':' + RET Z + CP ';' + RET Z + CP '<' + RET Z + CP '>' + RET Z + RET + +; --------------------------------------------------- +; Find non space character until end +; Inp: DE -> current character +; Out: DE -> non space +; A - character +; ZF set on EOL +; --------------------------------------------------- +ccp_find_no_space: + LD A, (DE) + OR A + RET Z + CP ASCII_SP + RET NZ + INC DE + JP ccp_find_no_space + +; --------------------------------------------------- +; HL=HL+A +; --------------------------------------------------- +sum_hl_a: + ADD A, L + LD L, A + RET NC + INC H ; inc H if CF is set + RET + +; --------------------------------------------------- +; Convert first name to fcb +; --------------------------------------------------- +ccp_cv_first_to_fcb: + LD A, 0x0 + +; Convert filename from cmd to fcb +; replace '*' to '?' +; Inp: A - offset in fcb filename +; Out: A - count of '?' in file name +; ZF is set for fegular file name +ccp_cv_fcb_filename: + LD HL, ccp_current_fcb + CALL sum_hl_a + PUSH HL + PUSH HL + XOR A + LD (ccp_chg_drive), A + LD HL, (ccp_inp_line_addr) ; HL -> input line + EX DE, HL + CALL ccp_find_no_space ; get next non blank char + EX DE, HL + LD (cur_name_ptr), HL ; save name ptr + EX DE, HL + POP HL + LD A, (DE) ; load first name char + OR A + JP Z, cur_cvf_n_end + SBC A, 'A'-1 ; 0x40 for drive letter + LD B, A + INC DE + LD A, (DE) + CP ':' ; is ':' after drive letter? + JP Z, cur_cvf_drv_ltr + DEC DE ; no, step back + +cur_cvf_n_end: + LD A, (ccp_cur_drive) + LD (HL), A + JP cur_cvf_basic_fn + +cur_cvf_drv_ltr: + LD A, B + LD (ccp_chg_drive), A ; set change drive flag + LD (HL), B + INC DE + +cur_cvf_basic_fn: + LD B, 8 ; file name length + +cur_cvf_chr_nxt: + CALL cpp_valid_inp + JP Z, cur_cvf_sp_remains + INC HL + CP '*' + JP NZ, cur_cvf_no_star + LD (HL), '?' + JP cur_cvf_nxt1 + +cur_cvf_no_star: + LD (HL), A + INC DE + +cur_cvf_nxt1: + DEC B + JP NZ, cur_cvf_chr_nxt + +cur_cvf_nxt_delim: + CALL cpp_valid_inp + JP Z, cur_cvf_ext + INC DE + JP cur_cvf_nxt_delim + + ; fill remains with spaces +cur_cvf_sp_remains: + INC HL + LD (HL), ASCII_SP + DEC B + JP NZ, cur_cvf_sp_remains + + ; file name extension +cur_cvf_ext: + LD B, 3 + CP '.' + JP NZ, cur_cvf_ext_fill_sp + INC DE + ; handle current ext char +cur_cvf_ext_nxt: + CALL cpp_valid_inp + JP Z, cur_cvf_ext_fill_sp + INC HL + ; change * to ? + CP '*' + JP NZ, cur_cvf_no_star2 + LD (HL), '?' + JP cur_cvf_nxt2 +cur_cvf_no_star2: + LD (HL), A + INC DE +cur_cvf_nxt2: + DEC B + JP NZ, cur_cvf_ext_nxt + +cur_cvf_ext_skip: + CALL cpp_valid_inp + JP Z, cur_cvf_rst_attrs + INC DE + JP cur_cvf_ext_skip + + ; skip remains ext pos with dpaces +cur_cvf_ext_fill_sp: + INC HL + LD (HL), ASCII_SP + DEC B + JP NZ, cur_cvf_ext_fill_sp + + ; set fcb extent, res1, res2 to 0 +cur_cvf_rst_attrs: + LD B, 3 + +cur_cvf_attrs_nxt: + INC HL + LD (HL), 0x0 + DEC B + JP NZ, cur_cvf_attrs_nxt + + EX DE, HL + LD (ccp_inp_line_addr), HL ; save input line pointer + + ; Check for ambigeous file name + POP HL ; -> file name in fcb + LD BC, 0x000b ; b=0, c=11 +cur_cvf_ambig: + INC HL + LD A, (HL) + CP '?' + JP NZ, cur_cvf_ambig_nxt1 + INC B +cur_cvf_ambig_nxt1: + DEC C + JP NZ, cur_cvf_ambig + LD A, B ; a = count of '?' + OR A ; set ZF if regular filename + RET + +; --------------------------------------------------- +; CP/M command table +; --------------------------------------------------- +cpm_cmd_tab: + DB "DIR " + DB "ERA " + DB "TYPE" + DB "SAVE" + DB "REN " + DB "USER" + +cpm_pattern: + DB 0xF9, 0x16, 0, 0, 0, 0x6B ; CP/M serial number? + ;LD SP, HL + ;LD D, 0x00 + ;NOP + ;NOP + ;LD L,E + +; --------------------------------------------------- +; Search for CP/M command +; Out: A - command number +; --------------------------------------------------- +ccp_search_cmd: + LD HL, cpm_cmd_tab + LD C, 0x0 + +ccp_sc_cmd_nxt: + LD A, C + CP CCP_COMMAND_CNT ; 6 + RET NC + LD DE, ccp_current_fcb_fn ; fcb filename + LD B, CCP_COMMAND_LEN ; max cmd len +ccp_sc_chr_nxt: + LD A, (DE) + CP (HL) ; compate fcb fn and command table + JP NZ, ccp_sc_no_match ; cmd not match + INC DE + INC HL + DEC B + JP NZ, ccp_sc_chr_nxt + ; last can be space for 3-letter commands + LD A, (DE) + CP ASCII_SP + JP NZ, ccp_sc_skip_cmd + LD A, C ; return command number in A + RET + + ; skip remains +ccp_sc_no_match: + INC HL + DEC B + JP NZ, ccp_sc_no_match + ; go to next cmd +ccp_sc_skip_cmd: + INC C + JP ccp_sc_cmd_nxt + +; -------------------------------------------------- +; Clear command buffer and go to command processor +; -------------------------------------------------- +ccp_clear_buff: + XOR A + LD (ccp_inbuff+1), A ; actual buffer len = 0 + +; -------------------------------------------------- +; Entrance to CCP +; Inp: C - current user * 16 +; -------------------------------------------------- +@ccp_ent: + LD SP, ccp_stack + PUSH BC ; + LD A, C + ; / 16 + RRA + RRA + RRA + RRA + ; cur user no in low nibble + AND 0x0f ; user 0..15 + LD E, A + CALL ccp_bdos_set_user + CALL ccp_bdos_drv_allreset + LD (ccp_batch), A + POP BC + LD A, C ; a = user*16 + AND 0xf ; low nibble - drive + LD (ccp_cur_drive), A + CALL ccp_bdos_drv_set + LD A, (ccp_inbuff+1) + OR A + JP NZ, ccp_process_cmd + +; -------------------------------------------------- +; Out prompt and get user command from console +; -------------------------------------------------- +ccp_get_command: + LD SP, ccp_stack ; reset stack pointer + CALL ccp_out_crlf ; from new line + CALL ccp_bdos_drv_get + ADD A, 65 ; convert drive no to character + CALL ccp_print ; print current drive letter + LD A, '>' ; and prompt + CALL ccp_print + CALL ccp_get_inp ; and wait string + +; -------------------------------------------------- +; Process command +; -------------------------------------------------- +ccp_process_cmd: + LD DE, dma_buffer + CALL ccp_bdos_dma_set ; setup buffer + CALL ccp_bdos_drv_get + LD (ccp_cur_drive), A ; store cur drive + CALL ccp_cv_first_to_fcb ; convert first command parameter to fcb + CALL NZ, print_syn_err ; if wildcard, out error message + LD A, (ccp_chg_drive) ; check drive change flag + OR A + JP NZ, ccp_unk_cmd ; if drive changed, handle as unknown command + CALL ccp_search_cmd ; ret A = command number + LD HL, ccp_cmd_addr + ; DE = command number + LD E, A + LD D, 0x0 + ADD HL, DE + ADD HL, DE ; HL = HL + 2*command_number + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + JP (HL) ; jump to command handler + +ccp_cmd_addr: + DW cmd_dir + DW cmd_erase + DW cmd_type + DW cmd_save + DW cmd_ren + DW cmd_user + DW ccp_entry + +; --------------------------------------------------- +; di+halt if serial number validation failed +; --------------------------------------------------- +cpp_halt: + LD HL, 0x76f3 + LD (ccp_ram_ent), HL + LD HL, ccp_ram_ent + JP (HL) + +ccp_type_rd_err: + LD BC, .msg_read_error ; BC -> "READ ERROR" + JP ccp_out_crlf_msg + +.msg_read_error: + DB "READ ERROR", 0 + +; --------------------------------------------------- +; Out message 'NO FILE' +; --------------------------------------------------- +ccp_out_no_file: + LD BC, .msg_no_file ; BC -> "NO FILE" + JP ccp_out_crlf_msg +.msg_no_file: + DB "NO FILE", 0 + +; --------------------------------------------------- +; Decode a command in form: "A>filename number" +; Out: A - number +; --------------------------------------------------- +ccp_decode_num: + CALL ccp_cv_first_to_fcb + LD A, (ccp_chg_drive) + OR A + JP NZ, print_syn_err ; error if drive letter specified + LD HL, ccp_current_fcb_fn + LD BC, 11 ; b=0, c=11 (filename len) + + ; decode number +ccp_dff_nxt_num: + LD A, (HL) + CP ASCII_SP + JP Z, ccp_dff_num_fin ; space - end of number + ; check for digit + INC HL + SUB '0' + CP 10 + JP NC, print_syn_err ; not a digit + LD D, A ; d = number + LD A, B + AND 0xe0 ; check B (sum) overflow + JP NZ, print_syn_err + ; A=B*10 + LD A, B + RLCA + RLCA + RLCA ; *8 + ADD A, B ; *9 + JP C, print_syn_err ; error if overflow + ADD A, B ; * 10 + JP C, print_syn_err ; error if overflow + ; B = B + B*10 + ADD A, D + JP C, print_syn_err ; error if overflow + LD B, A + ; to next number + DEC C + JP NZ, ccp_dff_nxt_num + RET + +ccp_dff_num_fin: + LD A, (HL) + CP ASCII_SP + JP NZ, print_syn_err ; will be space after number + INC HL + DEC C + JP NZ, ccp_dff_num_fin + LD A, B + RET + +; -------------------------------------------------- +; Move 3 bytes from [HL] to [DE] +; (Used only to move file extension) +; -------------------------------------------------- +ccp_mv_hlde_3: + LD B, 3 + +; -------------------------------------------------- +; Move B bytes from [HL] to [DE] +; -------------------------------------------------- +ccp_mv_hlde_b: + LD A, (HL) + LD (DE), A + INC HL + INC DE + DEC B + JP NZ, ccp_mv_hlde_b + RET + +; -------------------------------------------------- +; Return byte from address dma_buffer[A+C] +; Out: A - byte at dma_buffer[A+C] +; HL - dma_buffer+A+C +; -------------------------------------------------- +get_std_buff_ac: + LD HL, dma_buffer + ADD A, C + CALL sum_hl_a + LD A, (HL) + RET + +; -------------------------------------------------- +; Check drive change and select if needed +; Reset drive byte in fcb to 0 (use default) +; -------------------------------------------------- +ccp_drive_sel: + XOR A + LD (ccp_current_fcb), A + LD A, (ccp_chg_drive) + OR A + RET Z ; no need to change cur drive + DEC A + LD HL, ccp_cur_drive + CP (HL) + RET Z ; current and new drive is same + JP ccp_bdos_drv_set ; change + +; -------------------------------------------------- +; Restore previous drive if changed during operation +; -------------------------------------------------- +ccp_restor_drv: + LD A, (ccp_chg_drive) + OR A + RET Z ; not changed + DEC A + LD HL, ccp_cur_drive ; chk cur drive + CP (HL) + RET Z ; new and previous drive is same + LD A, (ccp_cur_drive) + JP ccp_bdos_drv_set ; restore to previous drive + +; -------------------------------------------------- +; Handle user DIR command +; -------------------------------------------------- +cmd_dir: + CALL ccp_cv_first_to_fcb + CALL ccp_drive_sel + ; check filemask specified + LD HL, ccp_current_fcb_fn + LD A, (HL) + CP ASCII_SP + JP NZ, .dir_fmask ; yes specified + + ; fill with wildcard symbol + LD B, 11 +.fill_wc: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_wc + +.dir_fmask: + LD E, 0x0 ; cursor at 0 + PUSH DE + ; find first file + CALL ccp_find_first + CALL Z, ccp_out_no_file ; no file found + +.dir_f_next: + JP Z, .dir_f_no_more + LD A, (ccp_bdos_result_code) + ; find filename pos in direntry + ; a = a * 32 + RRCA + RRCA + RRCA + AND 0x60 + + LD C, A + LD A, 0x0a + + ; get std_buff[10+pos*8] + CALL get_std_buff_ac + RLA ; CF<-[7:0]<-CF + JP C, .dir_dont_lst ; don't display sys files + POP DE + LD A, E ; a = cursor + INC E + PUSH DE ; cursor++ + AND 0x3 + PUSH AF + JP NZ, .dir_no_eol + ; eol, print new line + CALL ccp_out_crlf + ; print A: + PUSH BC + CALL ccp_bdos_drv_get + POP BC + ADD A, 'A' + CALL ccp_putc + LD A, ':' + CALL ccp_putc + JP .dir_out_sp +.dir_no_eol: + ; add space between filenames + CALL ccp_out_space + LD A, ':' + CALL ccp_putc +.dir_out_sp: + CALL ccp_out_space + LD B, 0x1 +.dir_get_one: + LD A, B + CALL get_std_buff_ac + AND 0x7f ; mask status bit + CP ASCII_SP ; name end? + JP NZ, .no_name_end + ; at end of file name + POP AF + PUSH AF + CP 0x3 + JP NZ, .dir_end_ext + LD A, 0x9 + CALL get_std_buff_ac ; chk ext + AND 0x7f ; 7bit + CP ASCII_SP + JP Z, .dir_skp_sp ; do not print space +.dir_end_ext: + LD A, ASCII_SP +.no_name_end: + CALL ccp_putc + INC B + LD A, B + CP 12 + JP NC, .dir_skp_sp ; print until end of file ext + CP 9 + JP NZ, .dir_get_one ; start of file ext? + CALL ccp_out_space ; print sep space + JP .dir_get_one +.dir_skp_sp: + POP AF +.dir_dont_lst: + ; stop if key pressed + CALL ccp_getkey_no_wait + JP NZ, .dir_f_no_more + + ; find next directory entry + CALL ccp_bdos_find_next + JP .dir_f_next + + ; no more to print +.dir_f_no_more: + POP DE + JP ccp_cmdline_back + +; -------------------------------------------------- +; Handle user ERA command +; -------------------------------------------------- +cmd_erase: + CALL ccp_cv_first_to_fcb + ; check for *.* + CP 11 + JP NZ, .era_no_wc + ; confirm erase all + LD BC, msg_all_yn ;= "ALL (Y/N)?" + CALL ccp_out_crlf_msg + CALL ccp_get_inp + LD HL, ccp_inbuff+1 + ; check user input + DEC (HL) + JP NZ, ccp_get_command + INC HL + LD A, (HL) ; user input, first letter + CP 'Y' + JP NZ, ccp_get_command ; return in not exactly 'Y' + INC HL + LD (ccp_inp_line_addr), HL + +.era_no_wc: + CALL ccp_drive_sel ; select drive + LD DE, ccp_current_fcb ; specify current fcb + CALL ccp_bdos_era_file ; and delete file + INC A + CALL Z, ccp_out_no_file + JP ccp_cmdline_back ; go back to command line + +msg_all_yn: + DB "ALL (Y/N)?", 0 + +; -------------------------------------------------- +; Handle user TYPE command +; -------------------------------------------------- +cmd_type: + CALL ccp_cv_first_to_fcb + JP NZ, print_syn_err ; error if wildcard + ; select drive and open + CALL ccp_drive_sel + CALL ccp_open_cur_fcb + JP Z, .not_found ; cant open file + CALL ccp_out_crlf + LD HL, ccp_bytes_ctr + LD (HL), 0xff ; 255>128 for read first sector + +.cont_or_read: + LD HL, ccp_bytes_ctr + LD A, (HL) + CP 128 + JP C, .out_next_char + + ; read 128 bytes + PUSH HL + CALL ccp_read_f_fcb + POP HL + JP NZ, .read_no_ok + ; clear counter + XOR A + LD (HL), A + +.out_next_char: + ; get next byte from buffer + INC (HL) + LD HL, dma_buffer + ; calc offset + CALL sum_hl_a + LD A, (HL) + CP ASCII_SUB ; Ctrl+Z end of text file + JP Z, ccp_cmdline_back ; yes, back to cmd line + CALL ccp_print ; print char to output device + ; interrupt if key pressed + CALL ccp_getkey_no_wait + JP NZ, ccp_cmdline_back + ; + JP .cont_or_read + + ; non zero result from f_read +.read_no_ok: + DEC A + JP Z, ccp_cmdline_back ; A=1 - EOF, return to cmd line + CALL ccp_type_rd_err ; else read error + +.not_found: + CALL ccp_restor_drv + JP print_syn_err + +; -------------------------------------------------- +; Handle user SAVE command +; -------------------------------------------------- +cmd_save: + CALL ccp_decode_num ; get num of pages + PUSH AF ; and store + CALL ccp_cv_first_to_fcb ; conv filename to fcb + JP NZ, print_syn_err ; error if wildcard + ; delete specified file + CALL ccp_drive_sel + LD DE, ccp_current_fcb + PUSH DE + CALL ccp_bdos_era_file + POP DE + ; create specified file + CALL ccp_bdos_make_f + JP Z, ccp_no_space ; 0xff+1 if error + XOR A + LD (ccp_current_fcb_cr), A ; curr record = 0 + POP AF ; a = num pages + LD L, A + LD H, 0 + ADD HL, HL ; HL = A * 2 - number of sectors + LD DE, tpa_start + +.write_next: + ; all sectors written? + LD A, H + OR L + JP Z, ccp_close_f_cur ; no more sectors to write + DEC HL + PUSH HL + ; set buffer address to memory to write + LD HL, 128 + ADD HL, DE + PUSH HL + CALL ccp_bdos_dma_set + ; and write sector + LD DE, ccp_current_fcb + CALL ccp_bdos_f_write + POP DE + POP HL + JP NZ, ccp_no_space ; check for no space left + JP .write_next + +; Close current file +ccp_close_f_cur: + LD DE, ccp_current_fcb + CALL ccp_bdos_close_f + INC A + JP NZ, rest_buf_ret_cmd + +; -------------------------------------------------- +; Out error message about no space left +; -------------------------------------------------- +ccp_no_space: + LD BC, msg_no_space ; BC -> "NO SPACE" + CALL ccp_out_crlf_msg + +rest_buf_ret_cmd: + CALL cpp_set_disk_buff_addr + JP ccp_cmdline_back + +msg_no_space: + DB "NO SPACE", 0 + +; -------------------------------------------------- +; Handle user REN command +; -------------------------------------------------- +cmd_ren: + CALL ccp_cv_first_to_fcb ; get first file name + JP NZ, print_syn_err ; error if wildcard + LD A, (ccp_chg_drive) ; remember drive change flag + PUSH AF + ; check file already exists + CALL ccp_drive_sel + CALL ccp_find_first + JP NZ, .file_exists + ; move filename to "second slot" + LD HL, ccp_current_fcb + LD DE, ccp_current_fcb_fn+15 + LD B, 16 + CALL ccp_mv_hlde_b + ; + LD HL, (ccp_inp_line_addr) ; restore cmd line pointer + EX DE, HL + CALL ccp_find_no_space ; skip spaces between parameters + CP '=' + JP Z, .do_rename + CP '_' + JP NZ, .rename_err + +.do_rename: + EX DE, HL + INC HL ; skip sep + LD (ccp_inp_line_addr), HL ; -> second param + CALL ccp_cv_first_to_fcb ; get second name + JP NZ, .rename_err ; error if wildcard + ; if drive specified it will be same as previous + POP AF + LD B, A + LD HL, ccp_chg_drive + LD A, (HL) + OR A + JP Z, .same_drive ; ok, it is same + CP B + LD (HL), B ; restore first drive + JP NZ, .rename_err + +.same_drive: + LD (HL), B + ; check for seacond file not exists + XOR A + LD (ccp_current_fcb), A + CALL ccp_find_first + JP Z, .second_exists + ; calll bdos to rename + LD DE, ccp_current_fcb + CALL ccp_bdos_rename_f + JP ccp_cmdline_back + +.second_exists: + CALL ccp_out_no_file + JP ccp_cmdline_back + +.rename_err: + CALL ccp_restor_drv + JP print_syn_err + +.file_exists: + LD BC, .msg_file_exists ; BC -> "FILE EXISTS" + CALL ccp_out_crlf_msg + JP ccp_cmdline_back + +.msg_file_exists: + DB "FILE EXISTS", 0 + +; -------------------------------------------------- +; Handle user USER command +; -------------------------------------------------- +cmd_user: + CALL ccp_decode_num ; get user number + ; user will be 0..15 + CP 16 + JP NC, print_syn_err ; >15 - error + LD E, A ; save in E + ; check for other parameters + LD A, (ccp_current_fcb_fn) + CP ASCII_SP + JP Z, print_syn_err ; error if other parameters specified + ; call bdos to set current user + CALL ccp_bdos_set_user + JP ccp_cmdline_back1 + +ccp_unk_cmd: + CALL ccp_verify_pattern ; check if system valid + ; check for file to execute specified + LD A, (ccp_current_fcb_fn) + CP ASCII_SP + JP NZ, .exec_file + ; drive change? + LD A, (ccp_chg_drive) + OR A + JP Z, ccp_cmdline_back1 ; no, return to cmd line + ; change drive + DEC A + LD (ccp_cur_drive), A + CALL ccp_reset_cur_drv + CALL ccp_bdos_drv_set + JP ccp_cmdline_back1 + +.exec_file: + ; check file extension + LD DE, ccp_current_fcb_ft + LD A, (DE) + CP ASCII_SP + JP NZ, print_syn_err + PUSH DE + ; select specified drive + CALL ccp_drive_sel + POP DE + ; set file ext to 'COM' + LD HL, msg_com ; HL -> 'COM' + CALL ccp_mv_hlde_3 + CALL ccp_open_cur_fcb + JP Z, cant_open_exe + LD HL, tpa_start ; load to start of TPA 0x100 + +.read_next_sec: + PUSH HL ; store start pointer + EX DE, HL + CALL ccp_bdos_dma_set + + LD DE, ccp_current_fcb + CALL ccp_bdos_read_f + JP NZ, .read_no_ok ; check for read error + + ; shift start pointer for sector size + POP HL + LD DE, 128 + ADD HL, DE + ; check for enough space in RAM + LD DE, ccp_ram_ent + LD A, L + SUB E + LD A, H + SBC A, D + JP NC, ccp_bad_load + JP .read_next_sec + +.read_no_ok: + POP HL + DEC A + JP NZ, ccp_bad_load ; it is not EOF, is error + ; ok, EOF + CALL ccp_restor_drv ; get first filename + CALL ccp_cv_first_to_fcb + LD HL, ccp_chg_drive ; hl -> buff[16] + PUSH HL + LD A, (HL) + LD (ccp_current_fcb), A ; set drive letter in current fcb + LD A, 16 + CALL ccp_cv_fcb_filename ; replace wildcards + POP HL + ; set drive for second file in reserved fcb area + LD A, (HL) + LD (ccp_current_fcb_al), A + ; clear record count + XOR A + LD (ccp_current_fcb_cr), A + ; Move current to default FCB + LD DE, fcb1 + LD HL, ccp_current_fcb + LD B, 33 + CALL ccp_mv_hlde_b + + ; move remainder of cmd line to 0x0080 + LD HL, ccp_inp_line +.skip_nosp: + LD A, (HL) + OR A + JP Z, .z_or_sp + CP ASCII_SP + JP Z, .z_or_sp + INC HL + JP .skip_nosp + +.z_or_sp: + LD B, 0 ; len of cmd line for program = 0 + LD DE, p_cmd_line ; destination address for cmd line + +.copy_cmd_line: + LD A, (HL) + LD (DE), A + OR A + JP Z, .stor_len + INC B + INC HL + INC DE + JP .copy_cmd_line + +.stor_len: + LD A, B + LD (p_cmd_line_len), A + ; next line + CALL ccp_out_crlf + ; set buffer to cmd line + CALL cpp_set_disk_buff_addr + ; set drive + CALL ccp_set_cur_drv + ; and call loaded program + CALL tpa_start + ; restore stack first + LD SP, ccp_stack + ; restore current drive + CALL ccp_reset_cur_drv + CALL ccp_bdos_drv_set + ; return back to command line mode + JP ccp_get_command + +cant_open_exe: + CALL ccp_restor_drv + JP print_syn_err + +ccp_bad_load: + LD BC, .msg_bad_load ; BC -> "BAD LOAD" + CALL ccp_out_crlf_msg + JP ccp_cmdline_back + +.msg_bad_load: + DB "BAD LOAD", 0 + +msg_com: + DB "COM" + +; -------------------------------------------------- +; Return back to command line +; -------------------------------------------------- +ccp_cmdline_back: + CALL ccp_restor_drv + +ccp_cmdline_back1: + CALL ccp_cv_first_to_fcb + LD A, (ccp_current_fcb_fn) + SUB 0x20 + LD HL, ccp_chg_drive + OR (HL) + JP NZ, print_syn_err + JP ccp_get_command + + DW 0h, 0h, 0h, 0h, 0h, 0h, 0h, 0h + +ccp_stack EQU $ + +ccp_batch: + DB 0h + +ccp_batch_fcb: + DB 0h ; drive code, 0 - default + DB "$$$ SUB" ; filename + DB 0h ; extent + DB 0h ; S1 +ccp_batch_fcb_s2: + DB 0h ; S2 Extent [6:0] bits and [7] write flag +ccp_batch_fcb_rc: + DB 0h ; sectors count +ccp_batch_fcb_al: + DS 16, 0 ; reserved by CPM use only +ccp_batch_fcb_cr: + DB 0h ; current sector to read/write + +ccp_current_fcb: + DB 0h +ccp_current_fcb_fn: + DS 8, 0 +ccp_current_fcb_ft: + DS 3, 0 +ccp_current_fcb_ex: + DB 0h ; extent + DB 0h ; s1 + DB 0h ; s2 + DB 0h ; sectors count +ccp_current_fcb_al: + DS 16, 0 ; reserved by CPM use only +ccp_current_fcb_cr: + DB 0h ; current sector to read/write + +ccp_bdos_result_code: + DB 0h + +ccp_cur_drive: + DB 0h + +ccp_chg_drive: ; change drive flag, 0 - no change + DB 0 +ccp_bytes_ctr: + DW 0 + ; reserved + DS 13, 0 + + +bdos_enter_jump EQU $+6 + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xB200 +;FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| CCP_RAM\t| ",/H,ccp_ram_ent," | ",/H,CODE_SIZE," | \t |" + + ; Check integrity + ASSERT ccp_cmdline_back = 0xb986 + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/ccp_rom.asm b/CPM_v2.2_r8_099942fc/ccp_rom.asm new file mode 100644 index 0000000..b859801 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/ccp_rom.asm @@ -0,0 +1,901 @@ +; ======================================================= +; Ocean-240.2 +; CPM CPP, ROM PART +; AT 0xDB00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + + IFNDEF BUILD_ROM + OUTPUT ccp_rom.bin + ENDIF + + MODULE CCP_ROM + + ORG 0xDB00 + +@ccp_entry: + LD HL, 0x0 ; prevent stack overflow + ADD HL, SP + LD (CPM_VARS.saved_stack_ptr), HL + LD SP, CPM_VARS.ccp_safe_stack + + CALL get_cmd_index + LD HL, ccp_commands ;= DB6Ch + LD E, A + LD D, 0x0 + ADD HL, DE + ADD HL, DE + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + JP (HL) ; jump to command + +ccp_commands_str: + DB "SDIR READ WRITE" + +; ------------------------------------------------------- +; Search user command position in available commands list +; ------------------------------------------------------- +get_cmd_index: + LD HL, ccp_commands_str ; -> 'DIR' + LD C, 0x0 +.cmd_next: + LD A, C + CP CCP_COMMAND_COUNT + RET NC + LD DE, CCP_RAM.ccp_current_fcb_fn + LD B, CCP_COMMAND_SIZE +.cmp_nxt: + LD A, (DE) + CP (HL) ; -> 'DIR' + JP NZ, .no_eq + INC DE + INC HL + DEC B + JP NZ, .cmp_nxt + LD A, (DE) + CP ASCII_SP + JP NZ, .inc_next + LD A, C + RET +.no_eq: + INC HL + DEC B + JP NZ, .no_eq +.inc_next: + INC C + JP .cmd_next + +; -------------------------------------------------- +; Command handlers ref table +; -------------------------------------------------- +ccp_commands: + DW ccp_dir + DW ccp_read + DW ccp_write + DW ccp_ret ; r8 +; DW ccp_exit1 ; r8 + +ccp_ret: + LD HL, (CPM_VARS.saved_stack_ptr) + LD SP, HL + JP CCP_RAM.ccp_unk_cmd + +;ccp_exit1: +; JP MON.mon_cold_start + +; -------------------------------------------------- +; DIR [filemask] command handler +; -------------------------------------------------- +ccp_dir: + CALL CCP_RAM.ccp_cv_first_to_fcb + CALL CCP_RAM.ccp_drive_sel + ; chech some filemask specified in command line + LD HL, CCP_RAM.ccp_current_fcb_fn + LD A, (HL) + CP ' ' + JP NZ, .has_par + + ; no filemask, fill with wildcard '?' + LD B, 11 +.fill_wildcard: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_wildcard + + ; find file by specified mask +.has_par: + CALL CCP_RAM.ccp_find_first + JP NZ, .f_found + ; no files found, print and exit + CALL CCP_RAM.ccp_out_no_file + JP CCP_RAM.ccp_cmdline_back + +.f_found: + CALL CCP_RAM.ccp_out_crlf + LD HL, 0x0 + LD (CPM_VARS.tmp_dir_total), HL + LD E, 0 + +.do_next_direntry: + PUSH DE + CALL CCP_RAM.ccp_find_first + POP DE + PUSH DE + + ; Find file with e number +.find_file_e: + DEC E + JP M, .file_out_next + PUSH DE + CALL CCP_RAM.ccp_bdos_find_next + POP DE + JP Z, .file_e_found + JP .find_file_e + +.file_out_next: + LD A, (CCP_RAM.ccp_bdos_result_code) + ; calc address of DIR entry in DMA buffer + ; A[6:5] = A[1:0] = 32*A + RRCA ; [C] -> [7:0] -> [C] + RRCA ; + RRCA ; + AND 01100000b ; mask + LD C, A + PUSH BC + CALL CCP_RAM.ccp_out_crlf ; start new line + CALL CCP_RAM.ccp_bdos_drv_get + INC A + LD (dma_buffer), A ; disk + POP BC + LD B, 0x0 + LD HL, dma_buffer+FCB_FN ; filename + LD E, L + LD D, H + ADD HL, BC + + ; copy filename to tmp FCB and out to screen + LD B, 0x1 +.copy_next: + LD A, (HL) + LD (DE), A + LD C, A + CALL BIOS.conout_f + INC HL + INC DE + INC B + LD A, B + CP FN_LEN ; >12 end of name + JP C, .copy_next + +.zero_up_36: + XOR A + LD (DE), A ; zero at end + INC B + LD A, B + CP 36 + JP C, .zero_up_36 + + ; calc file size for current entry + LD DE, dma_buffer + CALL cpp_bdos_f_size + LD HL, (fcb_ra_record_num) ; file size in blocks + + ; get disk blk size + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_dsk_a0 + LD B, 3 ; for A - blk=3 + JP .dsk_a0 + +.no_dsk_a0: + LD B, 4 ; for other disks - blk=4 +.dsk_a0: + LD C, L + + ; convert 128b OS block to disk blocks +.mul_to_dsk_blk: + XOR A + LD A, H + RRA + LD H, A + LD A, L + RRA + LD L, A + DEC B + JP NZ, .mul_to_dsk_blk + ; round up + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_dsk_a1 + LD A, 00000111b ; for A - ~(~0 << 3) + JP .ds_skip1 +.no_dsk_a1: + LD A, 00001111b ; for other dsk - ~(~0 << 4) +.ds_skip1: + AND C + JP Z, .cvt_blk_kb + INC HL + + ; Convert blocks to kilobytes (A-1k B-2k) +.cvt_blk_kb: + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP Z, .ds_skip2 + ADD HL, HL ; 2k + + ; add file size to total dir size +.ds_skip2: + EX DE, HL + LD HL, (CPM_VARS.tmp_dir_total) + ADD HL, DE + LD (CPM_VARS.tmp_dir_total), HL + + ; display size in K + LD C, ' ' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL ccp_cout_num + LD C, 'K' + CALL BIOS.conout_f + CALL CCP_RAM.ccp_getkey_no_wait + JP NZ, CCP_RAM.ccp_cmdline_back + POP DE + INC E + JP .do_next_direntry + +.file_e_found: + POP DE + LD HL, msg_free_space ;= "\r\nFREE SPACE " + + ; Out: FREE SPACE + CALL ccp_out_str_z + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_ram_dsk + LD HL, (BIOS.disk_a_size) + JP .calc_remanis_ds + +.no_ram_dsk: + DEC A + LD HL, (BIOS.disk_b_size) + JP Z, .calc_remanis_ds + + LD HL, (BIOS.disk_c_size) + LD A, (bios_var3) + OR A + JP NZ, .calc_remanis_ds + + LD A, H + CP 1 + JP Z, .d720 + LD HL, 360 + JP .calc_remanis_ds +.d720: + LD HL, 720 + + ; Disk size - Dir size = Free +.calc_remanis_ds: + EX DE, HL + LD HL, (CPM_VARS.tmp_dir_total) + LD A, E + SUB L + LD E, A + LD A, D + SBC A, H + LD D, A + CALL ccp_cout_num + LD C, 'K' + CALL BIOS.conout_f + CALL CCP_RAM.ccp_out_crlf + JP CCP_RAM.ccp_cmdline_back + +msg_free_space: + DB "\r\nFREE SPACE ",0 + +ccp_cout_num: + LD A, D + AND 11100000b + JP Z, .less_224 + LD C, '*' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + RET + +.less_224: + LD HL, 0x0 + ; copy number to BC + LD B, D + LD C, E + LD DE, 0x1 + LD A, 13 + +.bc_rra: + PUSH AF + PUSH HL + ; BC >> 1 + LD A, B + RRA + LD B, A + LD A, C + RRA + LD C, A + JP NC, .bc_rra_ex + POP HL + CALL cpp_daa16 + PUSH HL + +.bc_rra_ex: + LD L, E + LD H, D + CALL cpp_daa16 + EX DE, HL + POP HL + POP AF + DEC A + JP NZ, .bc_rra + LD D, 0x4 + LD B, 0x0 + +.next_d: + LD E, 0x4 + +.next_e: + LD A, L + RLA + LD L, A + LD A, H + RLA + LD H, A + LD A, C + RLA + LD C, A + DEC E + JP NZ, .next_e + LD A, C + AND 0xf + ADD A, '0' + CP '0' + JP NZ, .no_zero + DEC B + INC B + JP NZ, .b_no_one + LD A, D + DEC A + JP Z, .d_one + LD A, ' ' + JP .b_no_one + +.d_one: + LD A, '0' +.no_zero: + LD B, 0x1 +.b_no_one: + LD C, A + CALL BIOS.conout_f + DEC D + JP NZ, .next_d + RET + +; ------------------------------------------------------- +; ADD with correction HL=HL+DE +; ------------------------------------------------------- +cpp_daa16: + LD A, L + ADD A, E + DAA + LD L, A + LD A, H + ADC A, D + DAA + LD H, A + RET + +; ------------------------------------------------------- +; Call BDOS function 35 (F_SIZE) - Compute file size +; ------------------------------------------------------- +cpp_bdos_f_size: + LD C, 35 + JP jp_bdos_enter + +; ------------------------------------------------------- +; Read data from Tape +; ------------------------------------------------------- +ccp_read: + CALL BIOS.const_f + OR A + JP Z, .no_key ; chk key pressed + CALL BIOS.conin_f + JP ccp_read +.no_key: + CALL CCP_RAM.ccp_bdos_drv_allreset + CALL CCP_RAM.ccp_out_crlf + + ; wait tape block header + LD A, 100 + CALL BIOS.tape_wait_f + OR A + JP NZ, .key_pressed1 + + ; try to load header 8 times + LD E, 8 +.load8_cont: + LD HL, dma_buffer + CALL BIOS.tape_read_f + CP 0x4 + JP Z, .key_pressed1 + OR A + JP Z, .hdr_id_chk + DEC E + JP NZ, .load8_cont + JP .cp_load_err_what + + ; Check blk ID, will be 0x8000 +.hdr_id_chk: + LD A, B + CP 0x80 + JP NZ, .load8_cont + LD A, C + OR A + JP NZ, .load8_cont + ; set end of file name strZ + XOR A + LD (dma_buffer+12), A + LD DE, dma_buffer + LD HL, CCP_RAM.ccp_inp_line ;= 2020h + LD (CCP_RAM.ccp_inp_line_addr), HL ;= B208h + +.cp_name_char: + LD A, (DE) + OR A + JP Z, .name_copied + LD (HL), A ;= 2020h + INC HL + INC DE + JP .cp_name_char +.name_copied: + LD (HL), 0x0 ; mark end of strZ + CALL CCP_RAM.ccp_cv_first_to_fcb + JP NZ, CCP_RAM.print_syn_err + + ; Output file name to screen + LD HL, CCP_RAM.ccp_current_fcb_fn + LD B, 11 + +.name_output: + LD A, (HL) + AND 01111111b ; 7bit ASCII + LD C, A + CALL BIOS.conout_f + INC HL + DEC B + JP NZ, .name_output + ; Out ' ' + LD C, ' ' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL CCP_RAM.ccp_open_cur_fcb + JP Z, .recreate_f + LD HL,msg_ex ;= "*EX* " + CALL ccp_out_str_z + LD HL, CCP_RAM.ccp_current_fcb_fn+8 + ; '$$$' at the end of buffer + LD A, '$' + LD (HL), A + INC HL + LD (HL), A + INC HL + LD (HL), A + + ; delete file if exists and create new +.recreate_f: + LD DE, CCP_RAM.ccp_current_fcb + PUSH DE + CALL CCP_RAM.ccp_bdos_era_file + POP DE + CALL CCP_RAM.ccp_bdos_make_f + JP Z, CCP_RAM.ccp_no_space ; out NO SPACE if err + XOR A + LD (CCP_RAM.ccp_current_fcb_fn+31), A + + ; Load block with ID=1 + LD DE, 1 +.fin_load: + LD HL, dma_buffer + PUSH DE + +.blk_ld_rep: + CALL BIOS.tape_read_f + CP 4 + JP Z, .key_pressed2 + OR A + JP Z, .blk_id_chk + LD A, (CCP_RAM.ccp_current_fcb_fn+31) + AND 0x7f + JP NZ, .cp_read_cs + +.blk_id_chk: + LD A, C + OR B + JP Z, .blk_ld_rep + + ; Check id for 0xFFFF - last blk + LD A, C + AND B + CP 0xff + JP Z, .blk_id_last + ; Check ID for DE + LD A, C + CP E + JP NZ, .blk_ne_id + LD A, B + CP D + JP NZ, .blk_ne_id + ; Ok, write blk to disk + LD DE, dma_buffer + CALL CCP_RAM.ccp_bdos_dma_set + LD DE, CCP_RAM.ccp_current_fcb + CALL CCP_RAM.ccp_bdos_f_write + POP DE + JP NZ,ccp_del_f_no_space + INC DE + JP .fin_load + +.cp_load_err_what: + LD HL,msg_what ;= "WHAT?\a" + CALL ccp_out_str_z + JP .load_restart + +.cp_read_cs: + POP BC + LD HL, msg_cs ;= "\a", 0 + +; ------------------------------------------------------- +; Out zerro ended string +; In: HL -> strZ +; ------------------------------------------------------- +ccp_out_str_z: + LD A, (HL) + OR A + RET Z + LD C, A + CALL BIOS.conout_f + INC HL + JP ccp_out_str_z + +; ------------------------------------------------------- +; Delete file and out No Space message +; ------------------------------------------------------- +ccp_del_f_no_space: + LD DE, CCP_RAM.ccp_current_fcb + CALL CCP_RAM.ccp_bdos_era_file + JP CCP_RAM.ccp_no_space + +; ------------------------------------------------------- +; Read current file next block +; Out: A=0 - Ok, 0xFF - HW Error; +; ------------------------------------------------------- +cpp_read_f_blk: + LD DE, CPM_VARS.ccp_fcb ; FCB here + JP CCP_RAM.ccp_bdos_read_f + +ccp_write: + CALL CCP_RAM.ccp_cv_first_to_fcb + CALL CCP_RAM.ccp_drive_sel + LD HL, CCP_RAM.ccp_current_fcb_fn + LD A, (HL) + CP ' ' + JP NZ, .find_f + LD B, 11 + +.fill_with_wc: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_with_wc + +.find_f: + CALL CCP_RAM.ccp_find_first + JP NZ, .found_f + CALL CCP_RAM.ccp_out_no_file + JP CCP_RAM.ccp_cmdline_back + +.found_f: + LD E, 0 ; file counter + +.do_next_f1: + PUSH DE + CALL CCP_RAM.ccp_find_first + POP DE + PUSH DE + +.do_next_f2: + DEC E + JP M, .do_file + PUSH DE + CALL CCP_RAM.ccp_bdos_find_next + POP DE + JP Z, .no_more_f + JP .do_next_f2 + +.do_file: + POP BC + PUSH BC + LD A, C + OR A + JP Z, .calc_addr + LD DE, 1200 + + ; Delay with key interrupt check +.delay_1: + XOR A +.delay_2: + DEC A + JP NZ, .delay_2 + PUSH DE + CALL CCP_RAM.ccp_getkey_no_wait + POP DE + JP NZ, CCP_RAM.ccp_cmdline_back + DEC DE + LD A, D + OR E + JP NZ, .delay_1 + +.calc_addr: + LD A, (CCP_RAM.ccp_bdos_result_code) + ; A=0-3 - for Ok, calc address of DIR entry in DMA buffer + RRCA + RRCA + RRCA + AND 01100000b + LD C, A + PUSH BC + CALL CCP_RAM.ccp_out_crlf + CALL CCP_RAM.ccp_bdos_drv_get + INC A + LD (CPM_VARS.ccp_fcb_dr), A ; set drive number + POP BC + LD B, 0x0 + LD HL, dma_buffer+1 + ADD HL, BC + LD DE, CPM_VARS.ccp_fcb_fn + LD B, 0x1 + +.copy_fn: + LD A, (HL) + LD (DE), A + INC HL + INC DE + INC B + LD A, B + CP 12 + JP C, .copy_fn + +.fillz_fn: + XOR A + LD (DE), A + INC DE + INC B + LD A, B + CP 36 + JP C, .fillz_fn + LD HL, CPM_VARS.ccp_fcb_fn + CALL ccp_out_str_z + LD HL, dma_buffer + + ; Empty first 128 bytes of DMA buffer + LD B, 128 +.clear_buf: + LD (HL), 0x0 + INC HL + DEC B + JP NZ, .clear_buf + + ; Copy file name at buffer start + LD HL, dma_buffer + LD DE, CPM_VARS.ccp_fcb_fn + LD B, 8 + +.find_sp: + LD A, (DE) + CP ' ' + JP Z, .sp_rep_dot ; ' ' -> '.' + LD (HL), A + INC HL + INC DE + JP .find_sp +.sp_rep_dot: + LD (HL), '.' + INC HL + CALL CCP_RAM.ccp_find_no_space + +.cont_copy_fn: + LD A, (DE) + LD (HL), A + OR A + JP Z, .end_copy + INC HL + INC DE + JP .cont_copy_fn + +.end_copy: + LD DE, CPM_VARS.ccp_fcb + CALL CCP_RAM.ccp_bdos_open_f + LD DE, 0x8000 ; Block ID + LD HL, dma_buffer + CALL cpp_pause_tape_wr_blk + CALL cpp_pause_tape_wr_blk + LD DE, 0x1 + + ; Read file block and write to Tape +.read_f_write_t: + PUSH DE + CALL cpp_read_f_blk + ; a=0xff if error; a=1 - EOF + DEC A + JP Z, .eof + LD A, (CPM_VARS.ccp_fcb_cr) + AND 0x7f + CP 0x1 + JP NZ, .write_once + ; Write block to Tape with ID=0 twice + LD DE, 0x0 ; Block ID=0 + LD HL, dma_buffer + CALL cpp_pause_tape_wr_blk + +.write_once: + CALL CCP_RAM.ccp_getkey_no_wait + LD HL, dma_buffer + POP DE + JP NZ, CCP_RAM.ccp_cmdline_back + CALL cpp_pause_tape_wr_blk + ; Inc Block ID and continue + INC DE + JP .read_f_write_t + +.eof: + POP DE + EX DE, HL + LD (dma_buffer), HL + EX DE, HL + + ; Final block ID=0xFFFF + LD DE, 0xffff + ; Write twice + CALL cpp_pause_tape_wr_blk + CALL cpp_pause_tape_wr_blk + POP DE + INC E + JP .do_next_f1 + +.no_more_f: + POP DE + CALL CCP_RAM.ccp_out_crlf + JP CCP_RAM.ccp_cmdline_back + +; ------------------------------------------------------- +; Write block to tape after pause +; ------------------------------------------------------- +cpp_pause_tape_wr_blk: + LD BC, 3036 +.delay: + DEC BC + LD A, B + OR C + JP NZ, .delay + JP BIOS.tape_write_f + + DB 0x4c + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xDB00 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| CCP_ROM\t| ",/H,ccp_entry," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT ccp_dir = 0xdb62 + ASSERT ccp_dir.find_file_e = 0xdb97 + ASSERT ccp_dir.file_out_next = 0xdba6 + ASSERT ccp_dir.file_e_found = 0xdc3e + ASSERT ccp_dir.calc_remanis_ds = 0xdc72 + ASSERT ccp_dir.no_ram_dsk = 0xdc52 + ASSERT msg_free_space = 0xdc8a + ASSERT ccp_cout_num = 0xdc9a + +FILLER + DS FILL_SIZE-1, 0xff + DB 0xaa + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_099942fc/cpm.asm b/CPM_v2.2_r8_099942fc/cpm.asm new file mode 100644 index 0000000..c2ea78d --- /dev/null +++ b/CPM_v2.2_r8_099942fc/cpm.asm @@ -0,0 +1,44 @@ +; ====================================================== +; Ocean-240.2 +; CP/M Combine file. Includes all sources to build +; ROM 0xC000 +; +; Disassembled by Romych 2025-09-09 +; ====================================================== + + DEFINE BUILD_ROM + + DEVICE NOSLOT64K + +; +; |-----------|---------------|-----------|--------------------------------------| +; | OFFSET | SIZE | Module | Memory Address | +; |-----------|---------------|-----------|--------------------------------------| +; | 0x0000 | 2048 (0x800) | CCP_RAM | 0xC000..0xC7FF -> RAM 0xB200..0xB5FF | +; | 0x0800 | 3584 (0xE00) | BDOS | 0xC800.. | +; | 0x1600 | 1024 (0x400) | BIOS | 0xD600..D9FF | +; | 0x1B00 | 1280 (0x500) | CCP_ROM | 0xDB00..DFFF | +; |-----------|---------------|-----------|--------------------------------------| +; + + DISPLAY "| Module | Offset | Size | Free |" + DISPLAY "|-------------|---------|--------|--------|" + + + OUTPUT cpm-C000.bin + ;LABELSLIST "cpm.labels" + ;CSPECTMAP "cpm.map" + + INCLUDE "ccp_ram.asm" + INCLUDE "bdos.asm" + INCLUDE "bios.asm" + INCLUDE "ccp_rom.asm" + + OUTEND + + OUTPUT variables.bin + INCLUDE "cpm_vars.inc" + OUTEND + + +END \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/cpm_vars.inc b/CPM_v2.2_r8_099942fc/cpm_vars.inc new file mode 100644 index 0000000..ea33312 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/cpm_vars.inc @@ -0,0 +1,219 @@ +; ======================================================= +; Ocean-240.2 +; Module CPM_VARS - BIOS + BDOS + CPP variables +; RAM Range: 0xBA09-0xBF00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + IFNDEF _CPM_VARS + DEFINE _CPM_VARS + + MODULE CPM_VARS + ORG 0xBA09 + +cpm_vars_start EQU $ +; Filled by zeroes by BIOS at cold boot +; until ccp_vars_end + +; Output disabel if non zero +bdos_no_outflag DB 0 +bdos_strtcol DB 0 +bdos_column DB 0 + +; Copy output to printer in non zero +bdos_prnflag DB 0 +bdos_kbchar DB 0 + +; Stored user stack pointer on enter bdos fn +bdos_usersp DW 0 ; 0xba0e + +; BDOS Stack + DS 48, 0 +bdos_stack EQU $ + +; ------------------------------------------------------- +; Common values shared between bdosi and bdos +; ------------------------------------------------------- + +; current user number 0..15 +bdos_userno DB 0x00 ; 0xba40 + +; current disk number +bdos_curdsk DB 0x00 + +; reg DE (pointer) value to call BDOS fn +bdos_info DW 0x0000 ; 0xba42 + +; Address value to return +bdos_aret DW 0x0000 ; 0xba44 +; Empty FCB +bdos_efcb DB 0x00 ; 0xba46 = 0xE5 at init +; Read-only disks flag +bdos_rodsk DW 0x0000 ; 0xba47 +; Drive login vector +bdos_dlog DW 0x0000 ; 0xba49 +; Address of DMA buffer +bdos_dmaad DW 0x0000 ; 0xba4b = 0x80 at init +bdos_cdrmax DW 0x0000 ; 0xba4d +; Current track +bdos_curtrk DW 0x0000 ; 0xba4f +; Current record (sector) +bdos_currec DW 0x0000 ; 0xba51 +; Address of user buffer +bdos_buffa DW 0x0000 ; 0xba53 + +; Address of cur disk DPB +bdos_dpbaddr DW 0x0000 ; 0xba55 +; Calculated checksum +bdos_cksum DW 0x0000 ; 0xba57 +; Address of cur disk allocation map +bdos_alloca DW 0x0000 ; 0xba59 +; Sectors per track +dbos_sectpt DW 0x0000 ; 0xba5b +bdos_blkshf DB 0x00 ; 0xba5d +bdos_blmsk DB 0x00 ; 0xba5e +bdos_extmsk DB 0x00 ; 0xba5f +; Max allocation number (disk size) +bdos_maxall DW 0x0000 ; 0xba60 +bdos_dirmax DW 0x0000 ; 0xba62 +; Size of directory +bdos_dirblk DW 0x0000 ; 0xba64 +bdos_alloc1 DW 0x0000 ; 0xba66 +; First track offset +bdos_offset DW 0x0000 ; 0xba68 +; Disk traslation vector address +bdos_tranv DW 0x0000 ; 0xba6a +; Open and closed flag 0xff - closed +bdos_fcb_closed DB 0x00 ; 0xba6c +; Read file modified flag. 0xff - not allow read unwritten +bdos_rfm DB 0x00 ; 0xba6d +bdos_dirloc DB 0x00 ; 0xba6e +; File access mode (0=random, 1=sequential, 2=special) +bdos_seqio DB 0x00 ; 0xba6f +; Reg E value to call BDOS fn +bdos_linfo DB 0x00 ; 0xba70 +bdos_dminx DB 0x00 ; 0xba71 +; Length in bytes to match when search +bdos_searchl DB 0x00 ; 0xba72 +; Address of buffer where to search +bdos_searcha DW 0x0000 ; 0xba73 + DW 0x0000 +; 0 - big disk (>255 blk), 0xff - small disk +bdos_small_disk DB 0x00 ; 0xba77 +; Drive selected (0xff) by user specified FCB flag +bdos_resel DB 0x00 ; 0xba78 +bdos_olddsk DB 0x00 ; 0xba79 +; Saved disk of user FCB +bdos_fcbdsk DB 0x00 ; 0xba7a +; Number of records in extent +bdos_rcount DB 0x00 ; 0xba7b +bdos_extval DB 0x00 ; 0xba7c +; Record number to read +bdos_vrecord DW 0x0000 ; 0xba7d +; Record to access +bdos_arecord DW 0x0000 ; 0xba7f +bdos_arecord1 DW 0x0000 ; 0xba81 +bdos_dptr DB 0x00 ; 0xba83 +bdos_dcnt DW 0x00 ; 0xba84 +bdos_drec DS 20, 0x00 ; 0xba86 +tmp_dir_total DW 0x0000 ; 0xba9a +; Save pointer at ccp entry, restore on exit +saved_stack_ptr DW 0x0000 ; 0xba9c + DS 22, 0 +ccp_safe_stack + DW 0x0000 ; 0xbab4 + +; -------------------------------------------------- +; FCB +; -------------------------------------------------- +ccp_fcb +ccp_fcb_dr DB 0 ; 0xbab6 drive letter to write file +ccp_fcb_fn DS 8,0 ; 0xbab7 file name +ccp_fcb_ft DS 3,0 ; file type +ccp_fcb_ext DB 0 ; extent +ccp_fcb_s1 DB 0 +ccp_fcb_s2 DB 0 ; extent hi bits +ccp_fcb_rc DB 0 +ccp_fcb_al DS 16, 0 ; Second half of FCB +ccp_fcb_cr DB 0x00 ; Current record +ccp_fcb_rn DB 0x00 ; Random access record number + DW 0x00 + DW 0x00 + DW 0x00 + +ccp_vars_end EQU $ + +; -------------------------------------------------- +; Disk parameters headers/blocks in RAM +; -------------------------------------------------- +DPH_RAM DS 16, 0 ; 0xbade +DPH1_RAM DS 16, 0 ; 0xbaee +DPH2_RAM DS 16, 0 ; 0xbafe +tmp_buff9 DS 9, 0 +DPB_A_RAM DS 15, 0 ; 0xbb17 +DPB_B_RAM DS 15, 0 ; 0xbb26 +DPB_C_RAM DS 15, 0 ; 0xbb35 + +; -------------------------------------------------- +cur_disk DB 0x00 ; 0xbb44 +dma_addr DW 0x0000 ; 0xbb46 +curr_track DB 0x00 ; 0xbb47 +curr_sec DB 0x00 ; 0xbb48 +slicer_disk DB 0x00 ; 0xbb49 +slicer_track DB 0x00 ; 0xbb4a +slicer_real_sector DB 0x00 ; 0xbb4b +tmp_slicer_real_sector DB 0x00 ; 0xbb4c +slicer_has_data DB 0x00 ; 0xbb4d +slicer_need_save DB 0x00 ; 0xbb4e +slicer_uninited_count DB 0x00 ; 0xbb4f +slicer_uninited_disk DB 0x00 ; 0xbb42 +slicer_uninited_track DB 0x00 ; 0xbb43 +slicer_uninited_sector_128 DB 0x00 ; 0xbb44 +tmp_slicer_result DB 0x00 ; 0xbb45 +tmp_slicer_can_read DB 0x00 ; 0xbb46 +tmp_slicer_operation DB 0x00 ; 0xbb47 +tmp_slicer_flush DB 0x00 ; 0xbb48 + +; Directory buffer used by BDOS for directory operations +dir_buffer DS DIR_BUFF_SIZE, 0 ; 0xbb49 + +; Allocation map for drive A (Mark used allocation blocks) +AL_MAP_A DS 31, 0 ; 248 bit +; Check vector for drive A (Checksumm of dir entries, used to detect disk change) +CHK_VEC_A DS 16, 0 ; 0xbbf6 + +; Allocation map for drive B +AL_MAP_B DS 45, 0 ; 184 bit +; Check vector for drive B +CHK_VEC_B DS 32, 0 ; 0xbc33 + +; Allocation map for drive C +AL_MAP_C DS 45, 0 ; 184 bit +; Check vector for drive C +CHK_VEC_C DS 32, 0 ; 0xbc80 + DS 96, 0 + +slicer_buffer DS 512, 0 ; 0xbd00 + +cpm_vars_end EQU $ + +ccp_vars_size EQU ccp_vars_end-cpm_vars_start +cpm_vars_size EQU cpm_vars_end-cpm_vars_start + + ; check integrity of original ROM + ASSERT tmp_dir_total = 0xba9a + ASSERT saved_stack_ptr = 0xba9c + ASSERT ccp_fcb_fn = 0xbab7 + ASSERT DPH_RAM = 0xbade + ASSERT cur_disk = 0xbb44 + ASSERT slicer_uninited_count = 0xbb4f + ASSERT slicer_buffer = 0xbd00 + + DISPLAY "| CCP_VARS\t| ",/H,cpm_vars_start," | ",/H,cpm_vars_size," |\t |" + + DISPLAY "cur_disk:",/H,cur_disk + + ENDMODULE + + ENDIF diff --git a/CPM_v2.2_r8_099942fc/equates.inc b/CPM_v2.2_r8_099942fc/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/io.inc b/CPM_v2.2_r8_099942fc/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/CPM_v2.2_r8_099942fc/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/mon_entries.inc b/CPM_v2.2_r8_099942fc/mon_entries.inc new file mode 100644 index 0000000..b607096 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/mon_entries.inc @@ -0,0 +1,35 @@ +; ====================================================== +; Ocean-240.2 +; +; Monitor entries to compile CP/M modules +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _MON + DEFINE _MON + + MODULE MON + +mon_init EQU 0xe000 +mon_cold_start EQU 0xe003 +non_con_status EQU 0xe006 +mon_con_in EQU 0xe009 +mon_con_out EQU 0xe00c +mon_serial_in EQU 0xe00f +mon_serial_out EQU 0xe012 +mon_char_print EQU 0xe015 +mon_tape_read EQU 0xe018 +mon_tape_write EQU 0xe01b +ram_disk_read EQU 0xe01e +ram_disk_write EQU 0xe021 +mon_res_f1 EQU 0xe024 +mon_res_f2 EQU 0xe027 +mon_tape_wait EQU 0xe02a +mon_tape_detect EQU 0xe02d +read_floppy EQU 0xe030 +write_floppy EQU 0xe033 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/ram.inc b/CPM_v2.2_r8_099942fc/ram.inc new file mode 100644 index 0000000..8c6c08e --- /dev/null +++ b/CPM_v2.2_r8_099942fc/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +@RST1 EQU 0x0008 +@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/.gitignore b/CPM_v2.2_r8_2e4a7b71/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/CPM_v2.2_r8_2e4a7b71/.vscode/extensions.json b/CPM_v2.2_r8_2e4a7b71/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/CPM_v2.2_r8_2e4a7b71/.vscode/tasks.json b/CPM_v2.2_r8_2e4a7b71/.vscode/tasks.json new file mode 100644 index 0000000..f71ba83 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make CPM (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=cpm.sld", + "--sym=cpm.labels", + "--lstlab=sort", + "--lst=cpm.lst", + "--raw=cpm.obj", + "--fullpath", + "cpm.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + } + + ] +} \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/BIN/CPM_REL_8_2e4a7b71.BIN b/CPM_v2.2_r8_2e4a7b71/BIN/CPM_REL_8_2e4a7b71.BIN new file mode 100644 index 0000000000000000000000000000000000000000..02fad76370b30782a6787f8c2d331d53ab6b3052 GIT binary patch literal 8192 zcmdT}dw3Jql^z&UL`y&cCsA#Ijg27&1U3obYlG7?&nAs|w3P)| zHhGYQ?YB+0g{EmXn@5{$O0r7i$5^ApwPsMN8SC1TEy?(8Wh?JewI#HrP1@b@{_e<- zZnJ;ww}0#keCIySJ@>rtz2}Nw?iTOrZfA^t_1kLSbD(iY{q}vv8@H}8Zn*81+dgls z-BG_|U-eF7V_j2Sb>r6UMkDjT_RH}wOtXHGn8gIhw>^HzmQViPW6Kp$7*Pbs^PYr@ z3!0J`O^9!K^!Wg7*6UG{EXML1Z>6L!5JivM@@ubwc*OyaehHd-11*-Kpq(=wePMb6 zC+V*ff3J3a;?XZdRll4m@J!C||jt91qVmOV)E< zPCna-ZqCM>f8S$x%S#44w8!VIvV^>}{fS37+iA%5upNxHgVgk}ytjfeZK6ZtDkvsn zDx2dWZ+VE&DZkN4raHyPyHS$jPJOOH>j8IUO(&b}*=B0xWWLiy)^|Fx#B-evOe=fF zBk30dgbV65^dsquK)RuW(HG;(p7TOx6^xst>AU0sxgzQ}cC!ql)fV?be}$$j;XgVh z2-tku<5G-rSr>;tRnR7}pv$%yBFAW&vSKc%7{_&8L8U6FGzXP+LB$$Wz8F+0gNiLE zsl)w+qP^>>mTla zU4?Yuq6$Ao;U`r1pbGyH;AhhC7XfdoV6ueYf{JH63C&{0Zy6((;LDSvJNwqr~ zjdhLH#(f9&)HPM#S=U&%+jvJ)UE?RqA*x5%CEa?)sc6H7L9aHmxKFTmQ}I=H7b`|# zU$;|2!`_`I-rViBWP3K8^9G!y_{|kGlVzQx*JC;7^*iSZESJ2{h&9{83D0)}ib^-! z`9U=VZL?8n-_{8gk_(;arLYIP&g;6YA9*?9C%t4{H|y_iUd|L1xl9kVTy*r5a(8oY zb4z;vu9ul$fA_62#=|n6i<+5k@eNO7U3D$^X|y*onvZD5Zo6^Yj-7Q3aHHr7aN4lYKo{nj-s7k9KjzW{B>Yo>feYT=ax~ z*)5lF*57yw3JT`#vD=DN6<@8U z6?5j=f&G)7%U6^P;g3Bw-9neF=WIqy$!t~fhV`7H6UOLVajfSuoKPs?$S*wN^F0yq z6cxR)Sg7GNjU0J{>IIItNqD>)R&#)UU_{^pv4vdnRDx_wsouHMc;kVacCXn?g$zjn zfP_8r8ytvcFMBKIQMi3`^q((STu%JBShCJ|Cyt4?{dox%o+ouInga&=GG>GvM327qSR$}MiRoGIWfx7 z*0`rq(DZ7udnr$`P7n>VXxdY~RkaI{41HapWMA|M&R)2%Valsrf&jMQ!Ah09xmRfx zj`pTh34qO`y_f6Rj}UI;1HHY6&^$z~00`u-_tGWxbQPisD9wxJak@G>sW}?!?%L6` zuZaPJ$`aULvvtr5g9OLRI>l$br~2+P-(n(j0M{$fTvC_g6+%1h$~@u>W3`FHZiazaw%>gcv;PP94tQ1r3r-$q}Ez7~Bm z>W=nC{n4LCNi-V06dI4tMlVJ`ie|=gV@qO@k>#<>fy{w}F;)(eWKhYF8Af7bL9r{y zCe(70_KOTtb6?9*W8IM>M*Cw%qt#em^OgIIP4|EG8!bo5o9=%UmGW&5KYG8h@g{qD zl1P%Zn*X5M@B1E|c}}`4DfvMqH>l`>N)9$V z$(qk|LY+jk@i|S9=;BJDP$SVVrOho_@_Cy^vg!bIuM{9A$sZtjaeu&?$8+Zk;(jN) zm{;0^8x>=C6Lw>#k|Q{!O_Gw^U%^ODEIa(1bVYxVY?m(oVT;{~XtZ6@8_~2b=I647 zvyw#Kq8)j0eU6C2gdxh$VxG|i$P6UyDx^!vXcgX< z6%B;%9lsU?26I?S?W%=v zpe;)+FWEG-dvOrfoVu8oA1rnhn#95Zn2ep2#7}h70aMv7-efAX(_$}*RN2>fwu#s0 zkfj6WJNOj)C0<`Z>-X>{SO;G*&&l@;R4}%AP9mR+yUljqVRD%F(o!O;Ke1MP zLv0p~4-Yucw7!T>Dq#}eA4mjZ$&~pazNmH=Srv}}!BNwZ2(aPfcYe>_`a-?=YkZ2V zk3$4(oV}ahZEfSZQta)adRxv}yY&R0FPDb672@(xhXl(pEMbatRtL1RG4$9CjHIJ- zl87c`*D{2qL`&v&zO75L-p!-3rb=iEm7V0R2YEX%ztY{JLI6)5Q>5Z6V2V<#0S$6E z#`7D}Au7v_<^qkiNs* z#Jf-79MT`6BWuK9C_q)lY8dkKkl)$e(%SW)WUl8`J-!`ko6|^aJryk(43*XM=6iWl z%DjupMItOQ=$InciZEM16+#lBAe*nMrir&c!Kxt4ZOZXTspu$3A^a)uawyN`LZXW zfO|3aByU0;OASvF(NC;T@^cy5tf#Yte<7A9c&Ac%yRUYXp!FTecVxl&vq=Pi%%>=FJj^j3D12VShAXVR$U{X^!Xe5zPU%K1Yvs39`E ze+bK2kMOW+Cns+kDpoejn}=EvDK`%(dBt+Y(Al|m;iExu^H5+nkq7;#7n(Bj#pOe) zq(Kz>7!MP2Lr{KVn+H*31<~h`i9wZ_Y=Q+Uk{|(;KT8NuJvg-I03`PPp~bNPL|r=U z9`Z*Zt>b5oy*SJb6$WB84GaAdzxc$^ib}G2FcFcQ?+8x}VTYFV%qn9{%Ya2)3fTRK zKv1#>>q9iei}xL9VKhAY1HzTCwa?LJp8I3 zelyq6e{B)X5(E*Vqf6-YOH`XW7j|*Q!b@QXC;T|Q2(R6I5Nc6@|z?`1Nf8dHXg?tn2yR{0`*{u zGNL!Q9YCauu=!L{c0&4x#4i#zFp&GhRC1WD$zyb`%DGa9ye(1>TYH&(lz&~`8xdO~ znnjFmqie1}c1Eu9CA37WU*{{)L;jUwKt((fA=ihAE99n@ku`u7Y#K`o4@$qzqZ^Tq zJhD60-)<7#NB{<&or)G%BTI zIBOt1OV0=!h7qqVhj|UdaIzAyFGk?T261wztxH%uY+D415T&USpbs_JTMzT{>S3~) z92IXGhEM2p@KgOFN)7_aE*hb$ssz`tKj2iqNrb2U4%2nNwGShDnNMOz1I7So^&_TX zU(hqy5$b=^YZC4rPGE>2%waHqQU6ue&kQ>@>KEnb3$G4ep?1>lxQw%iI83(#4Zh?| z2x5UsU7X+_u7Y?f7|6j5DF#4$ie(X>=i0BhR+wBX5ZPSm>v#QS(_^3i!aA+6dxSMY z!_}GfB6E{)E2`mJYQdfPF9=dNKm{q?HdMQmP0+lA?4K%ypc!+n8tSQ7h=Erz^N< z_`T9me86u5N+246PhUgmMSkN-v;yS1sKMckto^gH{~)tNFg5 zL;S@E?6H-e7+rIvRk#Q!tA=llVh^=Ac^9q$bF&VT6GcD`^(dj;bCbzC{p^;gA5Nth`0%k>(-s z{2-m$9-*Z_vVadBh0!rO3O2-FQSv}W3hQF?#Fm0CZ2TxGoT9?r!Q(n3flp;#P$>PF zFjs2Xg5w5I-#J3dX0<#s0@m7YYCdrjkpFmp+iT6hl>&EdueF?b^+9kdSzB=$+7|;{ z1!Fy?)*V(S4{1R*WAtV4E-lSv7;piTVFqKXF%F{HM66ofUPnV&1H{ zY*t)4D;CU(Z;Xm3M@4>A{Mo2@epDo*;^e5t6pPLm+W<~QX>itZ3QFzgw zJ!ag8|5R6m{KM(Ec?)B>^Nw^K7NPB^NFu}YwK(bq(02PhLNtJJD-MRJO-v;qsyUt3_VQr$8lTG#F&M%ML2Ft zTDI}F4od^iPVu5Oje!RC2HH6}X5Ajgz2nwV15tZ7q1SqQTv;UFHA+XfGMsQf9Te5_YI~9b zyDhtX7Q4@y$=F54Ln+A@zBMYjCAf}L$p~9&eVqKG(c^O(;YXus*oTqL!Ot+w7fDt+ z+&gOB?c;=AI<9Bfdf#D*-Hzhnd&=s3C{-w7(FKwkC97uL>84g~vS?xgPVW?D3e!+6 zN|`Z_2T0ATl~YukNnqgvbOQp(4VUx35x^;KfJ8w;sx*wNgF zW)sE8m2#z)oj!J#&sCw}pl!@9CKTCQ>=3tG&h$18JPD~&h;JEJ0Q;7F>t-xmntr#M}XWhE7;Xon|%FT zKb=f~ya`g;GNyl>+&l{+)%q#2UahlwpLK_?O5Qcb)-u)x-%~9It+)I3v&{SGmT=#< zCvkJf#q8i%NyYk-TW>De#Qsgm#tkJKmVfx+ziUc1t}ofJYHs3V)NZyn)Kzc)f@R}+ zie{PgYV||wC{b|-sJ0euj##O z6ZIK?wdnuvCfx6~vKRRcT|8Mc!8T2Nnpkh5PXTKFe1GGPeRXW{1Wo;CsHy)f96oQK zpsD{`CTQx>KB1=mZ=c8$*G=3zv2yCY=`T%~U*ta#k+A>ERQIuog;e*^37T9zHL;Kq zMP?XV=nH&0{~d{4|EJd{Vn{PX(CEnC!2 z+6rEg3nxvqOD>sQlQO@+7l9@<_XPapg-Lw_{i?a?$V&0M6A||GBzbjOMbbEFu8WID zCacW#arwxkc}H9gfJY`zAZE5qvJYu6$c7hDwCNniHH6R8JaB&Qw#K@;bQ&62PUhI$ ztn8lDo9T*I|I=t$(_qjrw)`u}*3x=sseM?QOc4E4@{xN{rV^K{1n=bXOf9o2vu#Do zi4_kzSJPMlti zd%FqGXKLvZT`?ygokG+XADY4ySA7@2>xXtR4y&r}vV@MQYk~&5crbW-iv7Vf zq|3&=wsNG6Opec{{2>a^5la)Y4TOwOS%2-tH8p=4*UAs3oG&Z$a>caMClpV&@+hcD zTze7_-kvTLg(=oWml4g=e)cI^gI>gPG)^xx*ngxIn#I4QJ{PQx{ zf%GUIx7v{wZJkz1P_W-o@vfXo_Z~s7070q`4v?jIWP1Ph>V2EDnaZtAMy9f9uMziH zcwB>-G1uK?rbb}PW@rqBeoW$362w1qh8|x0#PeS~D>}X4S!9}e*6eiJvp#jGKTf9| zDmEkj+cYjMoP6Dk;hju(m9S_g2AhuH!KLI_r=Xenthe>>X>ZdRIG(!Of~#$uaWAD# zS3bUZhUIvwEA^zqATaEkL7jrtbtq0$)XnXZ!kGuAi{d1{1rW^iO$01!G+p|k?DP&_jeNtJalDZ~7HdN$nh!|4c> Y?kTTkaxp#b%*=?D@tyyDnEvU10yUO|cmMzZ literal 0 HcmV?d00001 diff --git a/CPM_v2.2_r8_2e4a7b71/README.md b/CPM_v2.2_r8_2e4a7b71/README.md new file mode 100644 index 0000000..609e076 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/README.md @@ -0,0 +1,11 @@ +# Ocean-240.2 ROM Sources CP/M (V2.2) REL.8' checksum 2e4a7b71 + +Source codes of CP/M (V2.2) REL.8' version for Ocean-240.2 with Floppy controller. +In Z80 mnemonics, but limited for i8080 instruction set. + +* 192k RAM drive + +## Compile: + sjasmplus --sld=cpm.sld --sym=cpm.labels --raw=cpm.obj --fullpath cpm.asm + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). diff --git a/CPM_v2.2_r8_2e4a7b71/bdos.asm b/CPM_v2.2_r8_2e4a7b71/bdos.asm new file mode 100644 index 0000000..3d21d38 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/bdos.asm @@ -0,0 +1,3041 @@ +; ======================================================= +; Ocean-240.2 +; CP/M BDOS at 0xC800:D5FF +; +; Disassembled by Romych 2025-09-09 +; ====================================================== + + INCLUDE "io.inc" + INCLUDE "equates.inc" + INCLUDE "ram.inc" + + IFNDEF BUILD_ROM + OUTPUT bdos.bin + ENDIF + + MODULE BDOS + + ORG 0xC800 + +bdos_start: + LD SP, HL + LD D, 0x00 + NOP + NOP + LD L, E + +; -------------------------------------------------- +; BDOS Entry point +; -------------------------------------------------- +bdos_enter: + JP bdos_entrance + +bdos_pere_addr: + DW bdos_persub ; permanent error +bdos_sele_addr: + DW bdos_selsub ; select error +bdos_rode_addr: + DW bdos_rodsub ; write to ro disk error +bdos_rofe_addr: + DW bdos_rofsub ; write to ro file error + +; ------------------------------------------------------- +; BDOS Handler +; Inp: C - func no +; DE or E - parameter +; Out: A or HL - result +; ------------------------------------------------------- +bdos_entrance: + ; store parameter DE + EX DE, HL + LD (CPM_VARS.bdos_info), HL + EX DE, HL + ; Store E + LD A, E + LD (CPM_VARS.bdos_linfo), A + ; value to return, default = 0 + LD HL, 0x00 + LD (CPM_VARS.bdos_aret), HL + + ; Save user's stack pointer, set to local stack + ADD HL, SP + LD (CPM_VARS.bdos_usersp), HL + + LD SP, CPM_VARS.bdos_stack ; local stack setup + XOR A + LD (CPM_VARS.bdos_fcbdsk), A ; fcbdsk,resel=false + LD (CPM_VARS.bdos_resel), A ; bdos_resel = FALSE + LD HL, bdos_goback + PUSH HL ; push goback address to return after jump + LD A, C + CP BDOS_NFUNCS + RET NC ; return in func no out of range + LD C, E ; store param E to C + ; calculate offset in functab + LD HL, functab ; DE=func_no, HL->functab + LD E, A + LD D, 0 + ADD HL, DE + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) ; DE=functab(func) + LD HL, (CPM_VARS.bdos_info) ; restore input parameter + EX DE, HL + JP (HL) ; dispatch function + +; ------------------------------------------------------- +; BDOS function handlers address table +; ------------------------------------------------------- +functab: + DW BIOS.wboot_f ; fn0 + DW bdos_con_inp ; fn1 + DW bdos_outcon ; fn2 + DW bdos_aux_inp ; fn3 + DW BIOS.punch_f ; fn4 + DW BIOS.list_f ; fn5 + DW bdos_dir_con_io ; fn6 + DW bdos_get_io_byte ; fn7 + DW bdos_set_io_byte ; fn8 + DW bdos_print_str ; fn9 + DW bdos_read ; fn10 + DW bdos_get_con_st ; fn11 + DW bdos_get_version ; fn12 + DW bdos_reset_disks ; fn13 + DW bdos_select_disk ; fn14 + DW bdos_open_file ; fn15 + DW bdos_close_file ; fn16 + DW bdos_search_first ; fn17 + DW bdos_search_next ; fn18 + DW bdos_rm_file ; fn19 + DW bdos_read_file ; fn20 + DW bdos_write_file ; fn21 + DW bdos_make_file ; fn22 + DW bdos_ren_file ; fn23 + DW bdos_get_login_vec ; fn24 + DW bdos_get_cur_drive ; fn25 + DW bdos_set_dma_addr ; fn26 + DW bdos_get_alloc_addr ; fn27 + DW bdos_set_ro ; fn28 + DW bdos_get_wr_protect ; fn29 + DW bdos_set_attr ; fn30 + DW bdos_get_dpb ; fn31 + DW bdos_set_user ; fn32 + DW bdos_rand_read ; fn33 + DW bdos_rand_write ; fn34 + DW bdos_compute_fs ; fn35 + DW bdos_set_random ; fn36 + DW bdos_reset_drives ; fn37 + DW bdos_not_impl ; fn38 (Access Drive) + DW bdos_not_impl ; fn39 (Free Drive) + DW bdos_rand_write_z ; fn40 + +; ------------------------------------------------------- +; Report permanent error +; ------------------------------------------------------- +bdos_persub: + LD HL, permsg ; = 'B' + CALL bdos_print_err + CP CTRL_C + JP Z, warm_boot + RET + +; ------------------------------------------------------- +; Report select error +; ------------------------------------------------------- +bdos_selsub: + LD HL, selmsg ;= 'S' + JP bdos_wait_err + +; ------------------------------------------------------- +; Report write to read/only disk +; ------------------------------------------------------- +bdos_rodsub: + LD HL, rodmsg ;= 'R' + JP bdos_wait_err + +; ------------------------------------------------------- +; Report read/only file +; ------------------------------------------------------- +bdos_rofsub: + LD HL, rofmsg ;= 'F' + +; ------------------------------------------------------- +; Wait for response before boot +; ------------------------------------------------------- +bdos_wait_err: + CALL bdos_print_err + JP warm_boot + +; ------------------------------------------------------- +; Error messages +; ------------------------------------------------------- +dskmsg: + DB 'Bdos Err On ' +dskerr: + DB " : $" +permsg: + DB "Bad Sector$" +selmsg: + DB "Select$" +rofmsg: + DB "File " +rodmsg: + DB "R/O$" + +; ------------------------------------------------------- +; Print error to console, message address in HL +; ------------------------------------------------------- +bdos_print_err: + PUSH HL ; save second message pointer + CALL bdos_crlf + ; set drive letter to message + LD A, (CPM_VARS.bdos_curdsk) + ADD A, 'A' + LD (dskerr), A + + LD BC, dskmsg ; print first error message + CALL bdos_print + POP BC + CALL bdos_print ; print second message + +; ------------------------------------------------------- +; Console handlers +; Read console character to A +; ------------------------------------------------------- +bdos_conin: + LD HL, CPM_VARS.bdos_kbchar + LD A, (HL) + LD (HL), 0x00 + OR A + RET NZ + ;no previous keyboard character ready + JP BIOS.conin_f + +; ------------------------------------------------------- +; Read character from console with echo +; ------------------------------------------------------- +bdos_conech: + CALL bdos_conin + CALL bdos_chk_ctrl_char + RET C + PUSH AF + LD C, A + CALL bdos_outcon + POP AF + RET + +; ------------------------------------------------------- +; Check for control char +; Onp: A - character +; Out: ZF if cr, lf, tab, or backspace +; CF if code < 0x20 +; ------------------------------------------------------- +bdos_chk_ctrl_char: + CP ASCII_CR + RET Z + CP ASCII_LF + RET Z + CP ASCII_TAB + RET Z + CP ASCII_BS + RET Z + CP ' ' + RET + +; ------------------------------------------------------- +; check console during output for +; Ctrl-S and Ctrl-C +; ------------------------------------------------------- +bdos_conbrk: + LD A, (CPM_VARS.bdos_kbchar) + OR A + JP NZ, .if_key ; skip if key pressed + CALL BIOS.const_f + AND 0x1 + RET Z ; return if no char ready + CALL BIOS.conin_f + CP CTRL_S + JP NZ, .no_crtl_s + ; stop on crtl-s + CALL BIOS.conin_f + CP CTRL_C + ; reboot on crtl-c + JP Z, warm_boot + XOR A + RET +.no_crtl_s: + LD (CPM_VARS.bdos_kbchar), A ; character in A, save it +.if_key: + LD A, 0x1 ; return with true set in accumulator + RET + +; -------------------------------------------------- +; Out char C to screen (and printer) +; -------------------------------------------------- +bdos_conout: + LD A, (CPM_VARS.bdos_no_outflag) ; if set, skip output, only move cursor + OR A + JP NZ, .compout + ; check console break + PUSH BC + CALL bdos_conbrk + POP BC + ; output c + PUSH BC + CALL BIOS.conout_f + POP BC + + ; send to printer (list) device + PUSH BC + LD A, (CPM_VARS.bdos_prnflag) + OR A + CALL NZ, BIOS.list_f + POP BC + +; ------------------------------------------------------- +; Update cursor position +; ------------------------------------------------------- +.compout: + LD A, C + ; recall the character + ; and compute column position + LD HL, CPM_VARS.bdos_column + CP ASCII_DEL ; [DEL] key + RET Z ; 0x7F dont move cursor + INC (HL) ; inc column + CP ASCII_SP ; return for normal character > 0x20 + RET NC + + ; restore column position + DEC (HL) + LD A, (HL) + OR A + RET Z ; return if column=0 + + LD A, C + CP ASCII_BS + JP NZ, .not_backsp + ; backspace character + DEC (HL) ; BKSP -> column-1 + RET + +.not_backsp: + CP ASCII_LF + RET NZ + LD (HL), 0 ; LF -> column=0 + RET + +; ------------------------------------------------------- +; Send C character with possible preceding '^' char +; ------------------------------------------------------- +bdos_ctlout: + LD A, C + CALL bdos_chk_ctrl_char ; cy if not graphic (or special case) + JP NC, bdos_outcon ; normal output if non control char (tab, cr, lf) + + PUSH AF + LD C, CTRL ; '^' + CALL bdos_conout + POP AF + OR 0x40 ; convert to letter equivalent (^M, ^J and so on) + LD C, A ; + +bdos_outcon: + LD A, C + CP ASCII_TAB + JP NZ, bdos_conout + + ; Print spaces until tab stop position +.to_tabpos: + LD C, ' ' + CALL bdos_conout + LD A, (CPM_VARS.bdos_column) + AND 00000111b ; column mod 8 = 0 ? + JP NZ, .to_tabpos + RET + +; ------------------------------------------------------- +; Output backspace - erase previous character +; ------------------------------------------------------- +bdos_out_bksp: + CALL bdos_prn_bksp + LD C, ' ' + CALL BIOS.conout_f + +; ------------------------------------------------------- +; Send backspace to console +; ------------------------------------------------------- +bdos_prn_bksp: + LD C, ASCII_BS + JP BIOS.conout_f + +; ------------------------------------------------------- +; print #, cr, lf for ctlx, ctlu, ctlr functions +; then move to strtcol (starting column) +; ------------------------------------------------------- +bdos_newline: + LD C, '#' + CALL bdos_conout + CALL bdos_crlf + ; move the cursor to the starting position +.print_sp: + LD A, (CPM_VARS.bdos_column) ; a=column + LD HL, CPM_VARS.bdos_strtcol + CP (HL) + RET NC ; ret id startcol>column + LD C, ' ' ; print space + CALL bdos_conout + JP .print_sp + +; ------------------------------------------------------- +; Out carriage return line feed sequence +; ------------------------------------------------------- +bdos_crlf: + LD C, ASCII_CR + CALL bdos_conout + LD C, ASCII_LF + JP bdos_conout + +; ------------------------------------------------------- +; Print $-ended string +; Inp: BC -> str$ +; ------------------------------------------------------- +bdos_print: + LD A, (BC) + CP '$' + RET Z + INC BC + PUSH BC + LD C, A + CALL bdos_outcon + POP BC + JP bdos_print + +; ------------------------------------------------------- +; Buffered console input +; Reads characters from the keyboard into a memory buffer +; until RETURN is pressed. +; Inp: C=0Ah +; DE=address or zero +; ------------------------------------------------------- +bdos_read: + LD A, (CPM_VARS.bdos_column) + LD (CPM_VARS.bdos_strtcol), A + LD HL, (CPM_VARS.bdos_info) + LD C, (HL) + INC HL + PUSH HL + LD B, 0 + ; B = current buffer length, + ; C = maximum buffer length, + ; HL= next to fill - 1 +.readnx: + PUSH BC + PUSH HL +.readn1: + CALL bdos_conin + AND 0x7f ; strip 7th bit + POP HL + POP BC + ; end of line? + CP ASCII_CR + JP Z, .read_end + CP ASCII_LF + JP Z, .read_end + + ; backspace ? + CP ASCII_BS + JP NZ, .no_bksp + LD A, B + OR A + JP Z, .readnx ; ignore BKSP for column=0 + DEC B + LD A, (CPM_VARS.bdos_column) + LD (CPM_VARS.bdos_no_outflag), A + JP .linelen + + ; not a backspace +.no_bksp: + CP ASCII_DEL ; [DEL] Key + JP NZ, .no_del + LD A, B + OR A + JP Z, .readnx ; ignore DEL for column=0 + LD A, (HL) ; cur char + DEC B ; dec column + DEC HL ; point to previous char + JP .echo ; out previous char + +.no_del: + CP CTRL_E ; ^E - physical end of line + JP NZ, .no_ctrle + PUSH BC + PUSH HL + CALL bdos_crlf ; out CR+LF + XOR A + LD (CPM_VARS.bdos_strtcol), A ; reset start column to 0 + JP .readn1 + +.no_ctrle: + CP CTRL_P ; ^P + JP NZ, .no_ctrlp + PUSH HL + LD HL, CPM_VARS.bdos_prnflag + LD A, 0x1 + SUB (HL) ; flip printer output flag + LD (HL), A + POP HL + JP .readnx + +.no_ctrlp: + CP CTRL_X ; cancel current cmd line + JP NZ, .no_ctrlx + POP HL +.backx: + LD A, (CPM_VARS.bdos_strtcol) + LD HL, CPM_VARS.bdos_column + CP (HL) + JP NC, bdos_read + DEC (HL) + CALL bdos_out_bksp + JP .backx + +.no_ctrlx: + CP CTRL_U ; cancel current cmd line + JP NZ, .no_ctrlu + + CALL bdos_newline + POP HL + JP bdos_read + +.no_ctrlu: + CP CTRL_R ; repeats current cmd line + JP NZ, .stor_echo + + ; start new line and retype +.linelen: + PUSH BC + CALL bdos_newline + POP BC + POP HL + PUSH HL + PUSH BC +.next_char: + LD A, B + OR A + JP Z, .endof_cmd ; end of cmd line? + INC HL + LD C, (HL) + DEC B + PUSH BC + PUSH HL + CALL bdos_ctlout + POP HL + POP BC + JP .next_char +.endof_cmd: + PUSH HL + LD A, (CPM_VARS.bdos_no_outflag) + OR A + JP Z, .readn1 ; if no display, read next characters + ; update cursor pos + LD HL, CPM_VARS.bdos_column + SUB (HL) + LD (CPM_VARS.bdos_no_outflag), A + ; move back compcol-column spaces + +.backsp: + CALL bdos_out_bksp + LD HL, CPM_VARS.bdos_no_outflag + DEC (HL) + JP NZ, .backsp + JP .readn1 + +; Put and Echo normal character +.stor_echo: + INC HL + LD (HL), A + INC B +.echo: + PUSH BC + PUSH HL + LD C, A + CALL bdos_ctlout + POP HL + POP BC + + ; warm boot on ctrl+c only at start of line + LD A, (HL) + CP CTRL_C + LD A, B + JP NZ, .no_ctrlc + CP 1 + JP Z, warm_boot + +.no_ctrlc: + CP C ; buffer is full? + JP C, .readnx ; get next char in not full + + ; End of read operation, store length +.read_end: + POP HL + LD (HL), B + LD C, ASCII_CR + JP bdos_conout ; Our CR and return + +; ------------------------------------------------------- +; Console input with echo (C_READ) +; Out: A=L=character +; ------------------------------------------------------- +bdos_con_inp: + CALL bdos_conech + JP bdos_ret_a + +; ------------------------------------------------------- +; Console input chartacter +; Out: A=L=ASCII character +; ------------------------------------------------------- +bdos_aux_inp: + CALL BIOS.reader_f + JP bdos_ret_a + +; ------------------------------------------------------- +; Direct console I/O +; Inp: E=code. +; E=code. Returned values (in A) vary. +; 0xff Return a character without echoing if one is +; waiting; zero if none +; 0xfe Return console input status. Zero if no +; character is waiting +; Out: A +; ------------------------------------------------------- +bdos_dir_con_io: + LD A, C + INC A + JP Z, .dirinp + INC A + JP Z, BIOS.const_f + JP BIOS.conout_f +.dirinp: + CALL BIOS.const_f + OR A + JP Z, bdos_ret_mon + CALL BIOS.conin_f + JP bdos_ret_a + +; ------------------------------------------------------- +; Get I/O byte +; Out: A = I/O byte. +; ------------------------------------------------------- +bdos_get_io_byte: + LD A, (iobyte) + JP bdos_ret_a + +; ------------------------------------------------------- +; Set I/O byte +; Inp: C=I/O byte. +; TODO: Check, will be E ? +; ------------------------------------------------------- +bdos_set_io_byte: + LD HL, iobyte + LD (HL), C + RET + +; ------------------------------------------------------- +; Out $-ended string +; Inp: DE -> string$. +; ------------------------------------------------------- +bdos_print_str: + EX DE, HL + LD C, L + LD B, H + JP bdos_print + +; ------------------------------------------------------- +; Get console status +; Inp: A=L=status +; ------------------------------------------------------- +bdos_get_con_st: + CALL bdos_conbrk +bdos_ret_a: + LD (CPM_VARS.bdos_aret), A +bdos_not_impl: + RET + +; ------------------------------------------------------- +; Set IO Error status = 1 +; ------------------------------------------------------- +set_ioerr_1: + LD A, 1 + JP bdos_ret_a + +; ------------------------------------------------------- +; Report select error +; ------------------------------------------------------- +bdos_sel_error: + LD HL, bdos_sele_addr + +; indirect Jump to [HL] +bdos_jump_hl: + LD E, (HL) + INC HL + LD D, (HL) ; ->bdos_sele_addr+1 + EX DE, HL + JP (HL) ; ->bdos_selsub + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +bdos_move_dehl: + INC C +.move_next: + DEC C + RET Z + LD A, (DE) + LD (HL), A + INC DE + INC HL + JP .move_next + +; ------------------------------------------------------- +; Select the disk drive given by curdsk, and fill +; the base addresses curtrka - alloca, then fill +; the values of the disk parameter block +; Out: ZF - for big disk +; ------------------------------------------------------- +bdos_sel_dsk: + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + CALL BIOS.seldsk_f ; HL filled by call + ; HL = 0000 if error, otherwise -> DPH + LD A, H + OR L + RET Z ; return if not valid drive + + LD E, (HL) + INC HL + LD D, (HL) + ; DE -> disk header + + ; save pointers + INC HL + LD (CPM_VARS.bdos_cdrmax), HL + INC HL + INC HL + LD (CPM_VARS.bdos_curtrk), HL + INC HL + INC HL + LD (CPM_VARS.bdos_currec), HL + INC HL + INC HL + ; save translation vector (table) addr + EX DE, HL + LD (CPM_VARS.bdos_tranv), HL ; tran vector + LD HL, CPM_VARS.bdos_buffa ; DE=source for move, HL=dest + LD C, 8 + CALL bdos_move_dehl + + ; Fill the disk parameter block + LD HL, (CPM_VARS.bdos_dpbaddr) + EX DE, HL + LD HL, CPM_VARS.dbos_sectpt + LD C, 15 + CALL bdos_move_dehl + + ; Set single/double map mode + LD HL, (CPM_VARS.bdos_maxall) ; largest allocation number + LD A, H ; a=h>0 for large disks (>255 blocks) + LD HL, CPM_VARS.bdos_small_disk + LD (HL), TRUE ; small + OR A + JP Z, .is_small + LD (HL), FALSE ; big disk > 255 sec +.is_small: + LD A, 0xff + OR A + RET + +; ------------------------------------------------------- +; Move to home position, +; then reset pointers for track and sector +; ------------------------------------------------------- +bdos_home: + CALL BIOS.home_f ; home head + XOR A + ; set track pointer to 0x0000 + LD HL, (CPM_VARS.bdos_curtrk) + LD (HL), A + INC HL + LD (HL), A + + ; set current record pointer to 0x0000 + LD HL, (CPM_VARS.bdos_currec) + LD (HL), A + INC HL + LD (HL), A + + RET + +; ------------------------------------------------------- +; Read buffer and check condition +; ------------------------------------------------------- +bdos_rdbuff: + CALL BIOS.read_f + JP diocomp ; check for i/o errors + +; ------------------------------------------------------- +; Write buffer and check condition +; Inp: C - write type +; 0 - normal write operation +; 1 - directory write operation +; 2 - start of new block +; ------------------------------------------------------- +bdos_wrbuff: + CALL BIOS.write_f + +; ------------------------------------------------------- +; Check for disk errors +; ------------------------------------------------------- +diocomp: + OR A + RET Z + LD HL, bdos_pere_addr ; = C899h + JP bdos_jump_hl + +; ------------------------------------------------------- +; Seek the record containing the current dir entry +; ------------------------------------------------------- +bdos_seekdir: + LD HL, (CPM_VARS.bdos_dcnt) + LD C, DSK_SHF + CALL bdos_hlrotr + LD (CPM_VARS.bdos_arecord), HL + LD (CPM_VARS.bdos_drec), HL + +; ------------------------------------------------------- +; Seek the track given by arecord (actual record) +; local equates for registers +; ------------------------------------------------------- +bdos_seek: + LD HL, CPM_VARS.bdos_arecord + LD C, (HL) + INC HL + LD B, (HL) + ; BC = sector number + + LD HL, (CPM_VARS.bdos_currec) + LD E, (HL) + INC HL + LD D, (HL) + ; DE = current sector number + + LD HL, (CPM_VARS.bdos_curtrk) + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + ; HL = current track + +.sec_less_cur: + ; check specified sector before current + LD A, C + SUB E + LD A, B + SBC A, D + JP NC, .sec_ge_cur + ; yes, decrement current sectors for one track + PUSH HL + LD HL, (CPM_VARS.dbos_sectpt) ; sectors per track + LD A, E + SUB L + LD E, A + LD A, D + SBC A, H + LD D, A + ; current track-1 + POP HL + DEC HL + JP .sec_less_cur + ; specified sector >= current +.sec_ge_cur: + PUSH HL + LD HL, (CPM_VARS.dbos_sectpt) + ADD HL, DE ; current sector+track + JP C, .track_found ; jump if after current + ; sector before current + LD A, C + SUB L + LD A, B + SBC A, H + JP C, .track_found + EX DE, HL + POP HL + ; increment track + INC HL + JP .sec_ge_cur + ; determined track number with specified sector +.track_found: + POP HL + PUSH BC + PUSH DE + PUSH HL + ; Stack contains (lowest) BC=arecord, DE=currec, HL=curtrk + EX DE, HL + LD HL, (CPM_VARS.bdos_offset) ; adjust if have reserved tracks + ADD HL, DE + LD B, H + LD C, L + CALL BIOS.settrk_f ; select track + + ; save current track + POP DE ; curtrk + LD HL, (CPM_VARS.bdos_curtrk) + LD (HL), E + INC HL + LD (HL), D + + ; save current record + POP DE + LD HL, (CPM_VARS.bdos_currec) + LD (HL), E + INC HL + LD (HL), D + + ; calc relative offset between arecord and currec + POP BC + LD A, C + SUB E + LD C, A + LD A, B + SBC A, D + LD B, A + + LD HL, (CPM_VARS.bdos_tranv) ; HL -> translation table + EX DE, HL + ; translate via BIOS call + CALL BIOS.sectran_f + ; select translated sector and return + LD C, L + LD B, H + JP BIOS.setsec_f + +; ------------------------------------------------------- +; Compute block number from record number and extent +; number +; ------------------------------------------------------- +bdos_dm_position: + LD HL, CPM_VARS.bdos_blkshf + LD C, (HL) + LD A, (CPM_VARS.bdos_vrecord) ; record number + ; a = a / 2^blkshf + +dmpos_l0: + OR A + RRA + DEC C + JP NZ, dmpos_l0 + ; A = shr(vrecord, blkshf) = vrecord/2^(sect/block) + LD B, A + LD A, 8 + SUB (HL) + ; c = 8 - blkshf + LD C, A + LD A, (CPM_VARS.bdos_extval) + ; a = extval * 2^(8-blkshf) +dmpos_l1: + DEC C + JP Z, dmpos_l2 + OR A + RLA + JP dmpos_l1 +dmpos_l2: + ADD A, B + RET ; with dm_position in A + +; ------------------------------------------------------- +; Return disk map value from position given by BC +; ------------------------------------------------------- +bdos_getdm: + LD HL, (CPM_VARS.bdos_info) + LD DE, DSK_MAP + ADD HL, DE + ADD HL, BC + LD A, (CPM_VARS.bdos_small_disk) + OR A + JP Z,getdmd + LD L, (HL) + LD H, 0x00 + RET + +getdmd: + ADD HL, BC + LD E, (HL) + INC HL + LD D, (HL) + EX DE, HL + RET + +; ------------------------------------------------------- +; Compute disk block number from current FCB +; ------------------------------------------------------- +bdos_index: + CALL bdos_dm_position + LD C, A + LD B, 0 + CALL bdos_getdm + LD (CPM_VARS.bdos_arecord), HL + RET + +; ------------------------------------------------------- +; Called following index to see if block allocated +; Out: ZF if not allocated +; ------------------------------------------------------- +bdos_allocated: + LD HL, (CPM_VARS.bdos_arecord) + LD A, L + OR H + RET + +; ------------------------------------------------------- +; Compute actual record address, assuming index called +; ------------------------------------------------------- +bdos_atran: + LD A, (CPM_VARS.bdos_blkshf) + LD HL, (CPM_VARS.bdos_arecord) +bdatarn_l0: + ADD HL, HL + DEC A + JP NZ, bdatarn_l0 + LD (CPM_VARS.bdos_arecord1), HL + LD A, (CPM_VARS.bdos_blmsk) + LD C, A + LD A, (CPM_VARS.bdos_vrecord) + AND C + OR L + LD L, A + LD (CPM_VARS.bdos_arecord), HL + RET + +; ------------------------------------------------------- +; Get current extent field address to A +; ------------------------------------------------------- +bdos_getexta: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_EXT + ADD HL, DE ; HL -> .fcb(extnum) + RET + +; ------------------------------------------------------- +; Compute reccnt and nxtrec addresses for get/setfcb +; Out: HL -> record count byte in fcb +; DE -> next record number byte in fcb +; ------------------------------------------------------- +bdos_fcb_rcnr: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_RC + ADD HL, DE + EX DE, HL + LD HL, 17 ; (nxtrec-reccnt) + ADD HL, DE + RET + +; ------------------------------------------------------- +; Set variables from currently addressed fcb +; ------------------------------------------------------- +bdos_stor_fcb_var: + CALL bdos_fcb_rcnr + LD A, (HL) + LD (CPM_VARS.bdos_vrecord), A + EX DE, HL + LD A, (HL) + LD (CPM_VARS.bdos_rcount), A + CALL bdos_getexta + LD A, (CPM_VARS.bdos_extmsk) + AND (HL) + LD (CPM_VARS.bdos_extval), A + RET + +; ------------------------------------------------------- +; Set the next record to access. +; Inp: HL -> fcb next record byte +; ------------------------------------------------------- +bdos_set_nxt_rec: + CALL bdos_fcb_rcnr + LD A, (CPM_VARS.bdos_seqio) ; sequential mode flag (=1) + CP 2 + JP NZ, bsfcb_l0 + XOR A ; if 2 - no adder needed +bsfcb_l0: + LD C, A + LD A, (CPM_VARS.bdos_vrecord) ; last record number + ADD A, C + LD (HL), A + EX DE, HL + LD A, (CPM_VARS.bdos_rcount) ; next record byte + LD (HL), A + RET + +; ------------------------------------------------------- +; Rotate HL right by C bits +; ------------------------------------------------------- +bdos_hlrotr: + INC C +bhlr_nxt: + DEC C + RET Z + LD A, H + OR A + RRA + LD H, A + LD A,L + RRA + LD L, A + JP bhlr_nxt + +; ------------------------------------------------------- +; Compute checksum for current directory buffer +; Out: A - checksum +; ------------------------------------------------------- +bdos_compute_cs: + LD C, DIR_BUFF_SIZE ; size of directory buffer + LD HL, (CPM_VARS.bdos_buffa) + XOR A +bcompcs_add_nxt: + ADD A, (HL) + INC HL + DEC C + JP NZ, bcompcs_add_nxt + RET + +; ------------------------------------------------------- +; Rotate HL left by C bits +; ------------------------------------------------------- +bdos_hlrotl: + INC C +.nxt: + DEC C + RET Z + ADD HL, HL + JP .nxt + +; ------------------------------------------------------- +; Set a "1" value in curdsk position +; Inp: BC - vector +; Out: HL - vector with bit set +; ------------------------------------------------------- +bdos_set_dsk_bit: + PUSH BC + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + LD HL, 0x0001 + CALL bdos_hlrotl + POP BC + LD A, C + OR L + LD L, A + LD A, B + OR H + LD H, A + RET + +; ------------------------------------------------------- +; Get write protect status +; ------------------------------------------------------- +bdos_nowrite: + LD HL, (CPM_VARS.bdos_rodsk) + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + CALL bdos_hlrotr + LD A, L + AND 0x1 + RET + +; ------------------------------------------------------- +; Temporarily set current drive to be read-only; +; attempts to write to it will fail +; ------------------------------------------------------- +bdos_set_ro: + LD HL, CPM_VARS.bdos_rodsk + LD C, (HL) + INC HL + LD B, (HL) + + CALL bdos_set_dsk_bit + LD (CPM_VARS.bdos_rodsk), HL + ; high water mark in directory goes to max + LD HL, (CPM_VARS.bdos_dirmax) + INC HL + EX DE, HL + LD HL, (CPM_VARS.bdos_cdrmax) + LD (HL), E + INC HL + LD (HL), D + RET + +; ------------------------------------------------------- +; Check current directory element for read/only status +; ------------------------------------------------------- +bdos_check_rodir: + CALL bdos_getdptra + +; ------------------------------------------------------- +; Check current buff(dptr) or fcb(0) for r/o status +; ------------------------------------------------------- +bdos_check_rofile: + LD DE, RO_FILE + ADD HL, DE + LD A, (HL) + RLA + RET NC + LD HL, bdos_rofe_addr ; = C8B1h + JP bdos_jump_hl + +; ------------------------------------------------------- +; Check for write protected disk +; ------------------------------------------------------- +bdos_check_write: + CALL bdos_nowrite + RET Z + LD HL, bdos_rode_addr ; = C8ABh + JP bdos_jump_hl + +; ------------------------------------------------------- +; Compute the address of a directory element at +; positon dptr in the buffer +; ------------------------------------------------------- +bdos_getdptra: + LD HL, (CPM_VARS.bdos_buffa) + LD A, (CPM_VARS.bdos_dptr) + +; ------------------------------------------------------- +; HL = HL + A +; ------------------------------------------------------- +bdos_hl_add_a: + ADD A,L + LD L, A + RET NC + INC H + RET + +; ------------------------------------------------------- +; Compute the address of the s2 from fcb +; Out: A - module number +; HL -> module number +; ------------------------------------------------------- +bdos_get_s2: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_S2 + ADD HL, DE + LD A, (HL) + RET + +; ------------------------------------------------------- +; Clear the module number field for user open/make +; ------------------------------------------------------- +bdos_clear_s2: + CALL bdos_get_s2 + LD (HL), 0 + RET + +; ------------------------------------------------------- +; Set FWF (File Write Flag) +; ------------------------------------------------------- +bdos_set_fwf: + CALL bdos_get_s2 + OR FWF_MASK + LD (HL), A + RET + +; ------------------------------------------------------- +; Check for free entries in directory +; Out: CF is set - directory have more file entries +; CF is reset - directory is full +; ------------------------------------------------------- +bdos_dir_is_free: + LD HL, (CPM_VARS.bdos_dcnt) + EX DE, HL ; DE = directory counter + LD HL, (CPM_VARS.bdos_cdrmax) ; HL = cdrmax + LD A, E + SUB (HL) + INC HL + LD A, D + SBC A, (HL) + ; Condition dcnt - cdrmax produces cy if cdrmax>dcnt + RET + +; ------------------------------------------------------- +; If not (cdrmax > dcnt) then cdrmax = dcnt+1 +; ------------------------------------------------------- +bdos_setcdr: + CALL bdos_dir_is_free + RET C + INC DE + LD (HL), D + DEC HL + LD (HL), E + RET + +; ------------------------------------------------------- +; HL = DE - HL +; ------------------------------------------------------- +bdos_de_sub_hl: + LD A, E + SUB L + LD L, A + LD A, D + SBC A, H + LD H, A + RET + +; Set directory checksubm byte +bdos_newchecksum: + LD C, 0xff + + +; ------------------------------------------------------- +bdos_checksum: + LD HL, (CPM_VARS.bdos_drec) + EX DE, HL + LD HL, (CPM_VARS.bdos_alloc1) + CALL bdos_de_sub_hl + RET NC + PUSH BC + CALL bdos_compute_cs ; Compute checksum to A for current dir buffer + LD HL, (CPM_VARS.bdos_cksum) + EX DE, HL + LD HL, (CPM_VARS.bdos_drec) + ADD HL, DE + POP BC + INC C + JP Z, initial_cs + CP (HL) + RET Z + CALL bdos_dir_is_free + RET NC + CALL bdos_set_ro + RET +initial_cs: + LD (HL), A + RET + +; ------------------------------------------------------- +; Write the current directory entry, set checksum +; ------------------------------------------------------- +bdos_wrdir: + CALL bdos_newchecksum + CALL bdos_setdir + LD C, 0x1 + CALL bdos_wrbuff + JP bdos_setdata + +; ------------------------------------------------------- +; Read a directory entry into the directory buffer +; ------------------------------------------------------- +bdos_rd_dir: + CALL bdos_setdir + CALL bdos_rdbuff + +; ------------------------------------------------------- +; Set data dma address +; ------------------------------------------------------- +bdos_setdata: + LD HL, CPM_VARS.bdos_dmaad + JP bdos_set_dma + +; ------------------------------------------------------- +; Set directory dma address +; ------------------------------------------------------- +bdos_setdir: + LD HL, CPM_VARS.bdos_buffa + +; ------------------------------------------------------- +; HL -> dma address to set (i.e., buffa or dmaad) +; ------------------------------------------------------- +bdos_set_dma: + LD C, (HL) + INC HL + LD B, (HL) + JP BIOS.setdma_f + +; ------------------------------------------------------- +; Copy the directory entry to the user buffer +; after call to search or searchn by user code +; ------------------------------------------------------- +bdos_dir_to_user: + LD HL, (CPM_VARS.bdos_buffa) + EX DE, HL + LD HL, (CPM_VARS.bdos_dmaad) + LD C, DIR_BUFF_SIZE + JP bdos_move_dehl + +; ------------------------------------------------------- +; return zero flag if at end of directory, non zero +; if not at end (end of dir if dcnt = 0ffffh) +; ------------------------------------------------------- +bdos_end_of_dir: + LD HL, CPM_VARS.bdos_dcnt + LD A, (HL) + INC HL + CP (HL) + RET NZ + INC A + RET + +; ------------------------------------------------------- +; Set dcnt to the end of the directory +; ------------------------------------------------------- +bdos_set_end_dir: + LD HL, ENDDIR + LD (CPM_VARS.bdos_dcnt), HL + RET + +; ------------------------------------------------------- +; Read next directory entry, with C=true if initializing +; ------------------------------------------------------- +bdos_read_dir: + LD HL, (CPM_VARS.bdos_dirmax) + EX DE, HL + LD HL, (CPM_VARS.bdos_dcnt) + INC HL + LD (CPM_VARS.bdos_dcnt), HL + CALL bdos_de_sub_hl + JP NC, bdrd_l0 + JP bdos_set_end_dir +bdrd_l0: + LD A, (CPM_VARS.bdos_dcnt) + AND DSK_MSK + LD B, FCB_SHF +bdrd_l1: + ADD A, A + DEC B + JP NZ, bdrd_l1 + LD (CPM_VARS.bdos_dptr), A + OR A + RET NZ + PUSH BC + CALL bdos_seekdir + CALL bdos_rd_dir + POP BC + JP bdos_checksum + +; ------------------------------------------------------- +; Given allocation vector position BC, return with byte +; containing BC shifted so that the least significant +; bit is in the low order accumulator position. HL is +; the address of the byte for possible replacement in +; memory upon return, and D contains the number of shifts +; required to place the returned value back into position +; ------------------------------------------------------- +bdos_getallocbit: + LD A, C + AND 00000111b + INC A + LD E, A + LD D, A + LD A, C + RRCA + RRCA + RRCA + AND 00011111b + LD C, A + LD A, B + ADD A, A + ADD A, A + ADD A, A + ADD A, A + ADD A, A + OR C + LD C, A + LD A, B + RRCA + RRCA + RRCA + AND 00011111b + LD B, A + LD HL, (CPM_VARS.bdos_alloca) ; Base address of allocation vector + ADD HL, BC + LD A, (HL) +ga_rotl: + RLCA + DEC E + JP NZ, ga_rotl + RET + +; ------------------------------------------------------- +; BC is the bit position of ALLOC to set or reset. The +; value of the bit is in register E. +; ------------------------------------------------------- +bdos_setallocbit: + PUSH DE + CALL bdos_getallocbit + AND 11111110b + POP BC + OR C + +; ------------------------------------------------------- +; Rotate and replace +; Inp: A - Byte value from ALLOC +; D - bit position (1..8) +; HL - target ALLOC position +; ------------------------------------------------------- +bdos_sab_rotr: + RRCA + DEC D + JP NZ, bdos_sab_rotr + LD (HL), A + RET + +; ------------------------------------------------------- +; Set or clear space used bits in allocation map +; Inp: C=0 - clear, C=1 - set +; ------------------------------------------------------- +bdos_scandm: + CALL bdos_getdptra + ; HL addresses the beginning of the directory entry + LD DE, DSK_MAP + ADD HL, DE ; HL -> block number byte + PUSH BC + LD C, 17 ; fcblen-dskmap+1 + ; scan all 17 bytes +.scan_next: + POP DE + DEC C + RET Z ; return if done + + PUSH DE + ; small or bug disk? + LD A, (CPM_VARS.bdos_small_disk) + OR A + JP Z, .scan_big + PUSH BC + PUSH HL + ; small, one byte + LD C, (HL) + LD B, 0 + JP .scan_cmn +.scan_big: + ; big, two byte + DEC C + PUSH BC + LD C, (HL) + INC HL + LD B, (HL) + PUSH HL + +.scan_cmn: + ; this block used? + LD A, C + OR B + JP Z, .not_used + ; have free space on disk? + LD HL, (CPM_VARS.bdos_maxall) + LD A, L + SUB C + LD A, H + SBC A, B + CALL NC, bdos_setallocbit ; enough space, set bit + +.not_used: + POP HL ; HL -> next block in fcb + INC HL + POP BC + JP .scan_next + +; ------------------------------------------------------- +; Initialize the current disk +; lret = false, set to true if $ file exists +; compute the length of the allocation vector - 2 +; ------------------------------------------------------- +bdos_initialize: + ; compute size of alloc table + LD HL, (CPM_VARS.bdos_maxall) + LD C, 3 + CALL bdos_hlrotr ; HL = disk size / 8 + 1 + INC HL + LD B, H + LD C, L ; BC = alloc table size + LD HL, (CPM_VARS.bdos_alloca) ; address of allocation table + ; fill with zeroes +.alt_fill_0: + LD (HL), 0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .alt_fill_0 + + LD HL, (CPM_VARS.bdos_dirblk) ; DE = initial space, used by directory + EX DE, HL + LD HL, (CPM_VARS.bdos_alloca) ; HL -> allocation map + LD (HL), E + INC HL + LD (HL), D ; [HL] = DE + CALL bdos_home ; home drive, set initial head, track, sector + + LD HL, (CPM_VARS.bdos_cdrmax) + LD (HL), 3 ; next dir read + INC HL + LD (HL), 0 + CALL bdos_set_end_dir ; mark end of dir + +.rd_next_f: + ; read next file name + LD C, 0xff + CALL bdos_read_dir + CALL bdos_end_of_dir ; is have another file? + RET Z + ; get params of file, and check deleted + CALL bdos_getdptra + LD A, FILE_DELETED + CP (HL) + JP Z, .rd_next_f + ; check user code + LD A, (CPM_VARS.bdos_userno) + CP (HL) + JP NZ, .set_as_used + INC HL + LD A, (HL) + SUB '$' ; check for $name file + JP NZ, .set_as_used + ; dollar file found, mark in lret + DEC A + LD (CPM_VARS.bdos_aret), A + +.set_as_used: + LD C, 1 + CALL bdos_scandm + CALL bdos_setcdr + JP .rd_next_f + +; ------------------------------------------------------- +; Return directory location as lret +; used in delete, rename, ... +; ------------------------------------------------------- +bdos_ret_dirloc: + LD A, (CPM_VARS.bdos_dirloc) + JP bdos_ret_a + +; ------------------------------------------------------- +; Compare extent# in A with that in C, return nonzero +; if they do not match +; ------------------------------------------------------- +bdos_compext: + PUSH BC + PUSH AF + LD A, (CPM_VARS.bdos_extmsk) + CPL + LD B, A + LD A, C + AND B + LD C, A + POP AF + AND B + SUB C + AND MAX_EXT ; check only bits [4:0] (MAX_EXT) + POP BC + RET + +; ------------------------------------------------------- +; Search for directory element of length C at info +; ------------------------------------------------------- +bdos_search: + LD A, 0xff + LD (CPM_VARS.bdos_dirloc), A + LD HL, CPM_VARS.bdos_searchl ; length in bytes to match + LD (HL), C + LD HL, (CPM_VARS.bdos_info) ; address filename to match + LD (CPM_VARS.bdos_searcha), HL + CALL bdos_set_end_dir + CALL bdos_home + +; ------------------------------------------------------- +; Search for the next directory element, assuming +; a previous call on search which sets searcha and +; searchl +; ------------------------------------------------------- +bdos_search_nxt: + LD C, 0 + CALL bdos_read_dir ; get next filename entry + CALL bdos_end_of_dir ; at end? pos=0xffff + JP Z, .not_found ; jump at end + + LD HL, (CPM_VARS.bdos_searcha) + EX DE, HL + ; skip if file deleted + LD A, (DE) + CP FILE_DELETED + JP Z, .search_next + + PUSH DE + CALL bdos_dir_is_free + POP DE + JP NC, .not_found ; Is full, no more file entries + +.search_next: + CALL bdos_getdptra ; get address of FCB + LD A, (CPM_VARS.bdos_searchl) ; BC=length to compare + LD C, A + LD B, 0 + + ; compare loop +.search_loop: + LD A, C + OR A + JP Z, .search_end ; search completed? + ; check byte + LD A, (DE) + CP '?' + JP Z, .search_ok ; match if wildcard + LD A, B + CP 13 ; ignore 13th byte + JP Z, .search_ok + CP FCB_EXT ; extent byte + LD A, (DE) + JP Z, .search_ext + SUB (HL) ; compare bytes + AND 0x7f ; mask 7th bit + JP NZ, bdos_search_nxt ; if not match, check next file entry + JP .search_ok + + ; compare ext +.search_ext: + PUSH BC + LD C, (HL) + CALL bdos_compext + POP BC + JP NZ, bdos_search_nxt ; if not match, check next file entry + + ; current bytes matches, increment pointers, decrement counter +.search_ok: + INC DE + INC HL + INC B + DEC C + JP .search_loop ; compare next byte + + ; entiry name matches, return dir position +.search_end: + LD A, (CPM_VARS.bdos_dcnt) + AND 0x3 + LD (CPM_VARS.bdos_aret), A + LD HL, CPM_VARS.bdos_dirloc + LD A, (HL) + RLA + RET NC + XOR A + LD (HL), A + RET + + ; end of directory, or empty name +.not_found: + CALL bdos_set_end_dir + LD A, 0xff + JP bdos_ret_a + +; ------------------------------------------------------- +; Delete the currently addressed file +; ------------------------------------------------------- +bdos_era_file: + CALL bdos_check_write ; check write rotection + LD C, 12 ; length of filename + CALL bdos_search ; search file in directory (with wildcards) +.del_next: + CALL bdos_end_of_dir + RET Z ; no more dir entries - exit + CALL bdos_check_rodir ; check file RO + CALL bdos_getdptra ; get file info + LD (HL), FILE_DELETED ; set deleted marker at first symbol + LD C, 0 + CALL bdos_scandm ; clear space at map + CALL bdos_wrdir ; write directory to disk + CALL bdos_search_nxt ; find next file + JP .del_next + +; ------------------------------------------------------- +; Given allocation vector position BC, find the zero bit +; closest to this position by searching left and right. +; if found, set the bit to one and return the bit position +; in hl. if not found (i.e., we pass 0 on the left, or +; maxall on the right), return 0000 in hl +; ------------------------------------------------------- +bdos_get_block: + LD D, B + LD E, C +.prev_test: + LD A, C + OR B + JP Z, .next_test ; jump if block 0 specified + ; check previous block + DEC BC + PUSH DE + PUSH BC + CALL bdos_getallocbit + RRA + JP NC, .ret_block ; block is empty, use it + + ; not empty, check more + POP BC + POP DE + + ; look at next block +.next_test: + ; check for free space on disk + LD HL, (CPM_VARS.bdos_maxall) + LD A, E + SUB L + LD A, D + SBC A, H + JP NC, .ret_no_block + ; have space, move to next block + INC DE + PUSH BC + PUSH DE + LD B, D + LD C, E + CALL bdos_getallocbit + RRA + JP NC, .ret_block ; block is empty, use it + POP DE + POP BC + JP .prev_test + + ; mark block as used and return in HL +.ret_block: + RLA + INC A + CALL bdos_sab_rotr + POP HL + POP DE + RET + + ; no free blocks found +.ret_no_block: + LD A, C + OR B + JP NZ, .prev_test ; if BC != 0 try to find before + LD HL, 0x00 ; not found + RET + +; ------------------------------------------------------- +; Copy the entire file control block +; ------------------------------------------------------- +bdos_copy_fcb: + LD C, 0x00 + LD E, FCB_LEN +; ------------------------------------------------------- +; copy fcb information starting at C for E bytes +; into the currently addressed directory entry +; ------------------------------------------------------- +dbos_copy_dir: + PUSH DE + LD B, 0x00 + LD HL, (CPM_VARS.bdos_info) + ADD HL, BC + EX DE, HL + CALL bdos_getdptra + POP BC + CALL bdos_move_dehl + +; ------------------------------------------------------- +; Enter from close to seek and copy current element +; ------------------------------------------------------- +bdos_seek_copy: + CALL bdos_seekdir + JP bdos_wrdir + +; ------------------------------------------------------- +; Rename the file described by the first half of +; the currently addressed FCB to new name, contained in +; the last half of the currently addressed FCB. +; ------------------------------------------------------- +bdos_rename: + CALL bdos_check_write ; check for RO disk + LD C, 12 ; use 12 symbols of name to compare + CALL bdos_search ; find first file + LD HL, (CPM_VARS.bdos_info) ; file info + LD A, (HL) ; user number + LD DE, 16 ; shift to place second file info + ADD HL, DE + LD (HL), A ; set same user no + +.ren_next: + CALL bdos_end_of_dir + RET Z ; return if end of directory + + CALL bdos_check_rodir ; check file RO flag + LD C, 16 ; start from 16th byte + LD E, FN_LEN ; and copy 12 byte + CALL dbos_copy_dir + CALL bdos_search_nxt ; search next file + JP .ren_next + +; ------------------------------------------------------- +; Update file attributes for current fcb +; ------------------------------------------------------- +bdos_update_attrs: + LD C, FN_LEN + CALL bdos_search ; search file by 12 bytes of name +.set_next: + CALL bdos_end_of_dir + RET Z ; return if not found + + LD C, 0 + LD E, FN_LEN + CALL dbos_copy_dir ; copy name to FCB and save dir + CALL bdos_search_nxt + JP .set_next ; ; do it for next file + +; -------------------------------------------------- +; Open file, name specified in FCB +; ------------------------------------------------------- +open: + LD C, FCB_INFO_LEN + CALL bdos_search ; search file + CALL bdos_end_of_dir + RET Z ; return if not found + +bdos_open_copy: + CALL bdos_getexta + LD A, (HL) ; get extent byte + PUSH AF ; save ext + PUSH HL ; and it's address + CALL bdos_getdptra + EX DE, HL + ; move to user space + LD HL, (CPM_VARS.bdos_info) + LD C, 32 + PUSH DE + CALL bdos_move_dehl + CALL bdos_set_fwf ; set 7th bit s2 "unmodified" flag + + POP DE + LD HL, FCB_EXT + ADD HL, DE + LD C, (HL) ; C = extent byte + + LD HL, FCB_RC + ADD HL, DE + LD B, (HL) ; B = record count + + POP HL + POP AF + LD (HL), A + LD A, C + CP (HL) ; cmp extent bytes + LD A, B + JP Z, .set_rec_cnt + ; user specified extent is not same, reset record count to 0 + LD A, 0 + JP C, .set_rec_cnt + ; set to maximum + LD A, 128 + +.set_rec_cnt: + ; set record count in user FCB to A + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_RC + ADD HL, DE + LD (HL), A + RET + +; -------------------------------------------------- +; HL = .fcb1(i), DE = .fcb2(i), +; if fcb1(i) = 0 then fcb1(i) := fcb2(i) +; -------------------------------------------------- +bdos_mergezero: + LD A, (HL) + INC HL + OR (HL) + DEC HL + RET NZ + LD A, (DE) + LD (HL), A + INC DE + INC HL + LD A, (DE) + LD (HL), A + DEC DE + DEC HL + RET + +; -------------------------------------------------- +; Close file specified by FCB +; -------------------------------------------------- +bdos_close: + XOR A + ; clear status and file position bytes + LD (CPM_VARS.bdos_aret), A + LD (CPM_VARS.bdos_dcnt), A + LD (CPM_VARS.bdos_dcnt+1), A + CALL bdos_nowrite ; get write protection + RET NZ ; return if set + + CALL bdos_get_s2 + AND FWF_MASK + RET NZ ; return if not modified flag set + ; search file + LD C, FCB_INFO_LEN + CALL bdos_search + CALL bdos_end_of_dir + RET Z ; return if not found + + LD BC, DSK_MAP ; offset of records used + CALL bdos_getdptra + ADD HL, BC + EX DE, HL + + LD HL, (CPM_VARS.bdos_info) ; same for user FCB + ADD HL, BC + LD C, 16 ; bytes in extent + +bdos_merge0: + LD A, (CPM_VARS.bdos_small_disk) ; small/big disk flag + OR A + JP Z, bdos_merge ; jump if big (16 bit) + ; small disk (8 bit) + LD A, (HL) ; from user FCB + OR A + LD A, (DE) ; from DIR FCB + JP NZ, bdm_fcbnzero + LD (HL), A ; user is 0, set from directory fcb + +bdm_fcbnzero: + OR A + JP NZ, bdm_buffnzero + ; dir is 0, set from user fcb + LD A, (HL) + LD (DE), A + +bdm_buffnzero: + CP (HL) + JP NZ, bdm_mergerr ; if both non zero, close error + JP bdm_dmset ; merged ok, go to next fcb byte + +bdos_merge: + CALL bdos_mergezero ; update user fcb if it is zero + EX DE, HL + CALL bdos_mergezero ; update dir fcb if it is zero + EX DE, HL + LD A, (DE) + CP (HL) + JP NZ, bdm_mergerr ; if both is same, close error + + ; next byte + INC DE + INC HL + LD A, (DE) + CP (HL) + JP NZ, bdm_mergerr ; if both is same, close error + DEC C + +bdm_dmset: + ; next + INC DE + INC HL + DEC C + JP NZ, bdos_merge0 ; merge next if C>0 + + LD BC, 0xffec ; -(fcblen-extnum) (-20) + ADD HL, BC + EX DE, HL + ADD HL, BC + LD A, (DE) + CP (HL) + JP C, bdm_endmerge ; directory extent > user extent -> end + ; update record count in dir FCB + LD (HL), A + LD BC, 0x3 ; (reccnt-extnum) + ADD HL, BC + EX DE, HL + ADD HL, BC + LD A, (HL) ; get from user FCB + LD (DE), A ; set to directory FCB + +bdm_endmerge: + LD A, 0xff + LD (CPM_VARS.bdos_fcb_closed), A ; set was open and closed flag + JP bdos_seek_copy ; update directory + + ; set return status and return +bdm_mergerr: + LD HL, CPM_VARS.bdos_aret + DEC (HL) + RET + +; ------------------------------------------------------- +; Create a new file by creating a directory entry +; then opening the file +; ------------------------------------------------------- +bdos_make: + CALL bdos_check_write ; check Write Protection + LD HL, (CPM_VARS.bdos_info) ; save user FCB + PUSH HL ; on stack + LD HL, CPM_VARS.bdos_efcb ; empty FCB + ; Save FCB address, look for 0xE5 + LD (CPM_VARS.bdos_info), HL ; set empty FCB + ; search firs empty slot in directory + LD C, 0x1 ; compare one byte 0xE5 + CALL bdos_search + CALL bdos_end_of_dir ; found flag + POP HL + LD (CPM_VARS.bdos_info), HL ; restore user FCB address + RET Z ; return if not found (no space in dir) + EX DE, HL + LD HL, FCB_RC ; number or record for this file + ADD HL, DE + + ; clear FCB tail + LD C, 17 ; fcblen-namlen + XOR A +.fcb_set_0: + LD (HL), A + INC HL + DEC C + JP NZ, .fcb_set_0 + + LD HL, FCB_S1 + ADD HL, DE + LD (HL), A ; Current record within extent? + CALL bdos_setcdr + CALL bdos_copy_fcb + JP bdos_set_fwf + +; ------------------------------------------------------- +; Close the current extent, and open the next one to read +; if possible. RMF is true if in read mod +; ------------------------------------------------------- +bdos_open_reel: + XOR A + LD (CPM_VARS.bdos_fcb_closed), A ; clear close flag + CALL bdos_close ; close extent + CALL bdos_end_of_dir ; check space + RET Z ; ret if no more space + ; get extent byte from user FCB + LD HL, (CPM_VARS.bdos_info) + LD BC, FCB_EXT + ADD HL, BC + LD A, (HL) + ; and increment it, mask to 0..31 and store + INC A + AND MAX_EXT + LD (HL), A + JP Z, .overflow + ; mask extent byte + LD B, A + LD A, (CPM_VARS.bdos_extmsk) + AND B + LD HL, CPM_VARS.bdos_fcb_closed + AND (HL) + JP Z, .read_nxt_extent ; read netx extent + JP .extent_in_mem + +.overflow: + LD BC, 0x2 ; S2 + ADD HL, BC + INC (HL) + LD A, (HL) + AND MAX_MOD + JP Z, .error + +.read_nxt_extent: + LD C, FCB_INFO_LEN + CALL bdos_search + CALL bdos_end_of_dir + JP NZ, .extent_in_mem ; jump if success + ; not extent found + LD A, (CPM_VARS.bdos_rfm) + INC A + JP Z, .error ; can not get extent + CALL bdos_make ; make new empty dir entry for extent + CALL bdos_end_of_dir ; chk no space + JP Z, .error ; jmp to error if no space in directory + JP .almost_done +.extent_in_mem: + ; open extent + CALL bdos_open_copy +.almost_done: + CALL bdos_stor_fcb_var ; move updated data to new extent + XOR A ; clear error flag + JP bdos_ret_a +.error: + CALL set_ioerr_1 + JP bdos_set_fwf ; clear bit 7 in s2 + +; ------------------------------------------------------- +; Sequential disk read operation +; ------------------------------------------------------- +bdos_seq_disk_read: + LD A, 0x1 + LD (CPM_VARS.bdos_seqio), A + ; drop through to diskread + +bdos_disk_read: + LD A, TRUE + LD (CPM_VARS.bdos_rfm), A ; dont allow read unwritten data + CALL bdos_stor_fcb_var + LD A, (CPM_VARS.bdos_vrecord) ; next record to read + LD HL, CPM_VARS.bdos_rcount ; number of records in extent + ; in this extent? + CP (HL) + JP C, .recordok + ; no, in next extent, check this exctent fully used + CP 128 + JP NZ, .error_opn + ;open next extent + CALL bdos_open_reel + XOR A + ; reset record number to read + LD (CPM_VARS.bdos_vrecord), A + ; check open status + LD A, (CPM_VARS.bdos_aret) + OR A + JP NZ, .error_opn +.recordok: + ; compute block number to read + CALL bdos_index + ; check if it in bounds + CALL bdos_allocated + JP Z, .error_opn + + CALL bdos_atran ; convert to logical sector + CALL bdos_seek ; seec track and sector + CALL bdos_rdbuff ; read sector + JP bdos_set_nxt_rec + +.error_opn: + JP set_ioerr_1 + +; ------------------------------------------------------- +; Sequential write disk +; ------------------------------------------------------- +bdos_seq_disk_write: + LD A, 0x1 + LD (CPM_VARS.bdos_seqio), A +bdos_disk_write: + LD A, FALSE + LD (CPM_VARS.bdos_rfm), A ; allow open new extent + CALL bdos_check_write ; check write protection + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + CALL bdos_check_rofile ; check RO file + CALL bdos_stor_fcb_var + LD A, (CPM_VARS.bdos_vrecord) ; get record number to write to + CP 128 ; lstrec+1 + JP NC, set_ioerr_1 ; not in range - error + CALL bdos_index ; compute block number + CALL bdos_allocated ; check number + LD C, 0 + JP NZ, .disk_wr1 + CALL bdos_dm_position ; get next block number within FCB + LD (CPM_VARS.bdos_dminx), A ; and store it + LD BC, 0x00 ; start looking for space from start + OR A + JP Z, .nop_block ; zero - not allocated + ; extract previous blk from fcb + LD C, A + DEC BC + CALL bdos_getdm + LD B, H + LD C, L +.nop_block: + CALL bdos_get_block ; get next free nearest block + LD A, L + OR H + JP NZ, .block_ok + ; error, no more space + LD A, 2 + JP bdos_ret_a +.block_ok: + LD (CPM_VARS.bdos_arecord), HL ; set record to access + EX DE, HL ; to DE + LD HL, (CPM_VARS.bdos_info) ; + LD BC, FCB_AL ; 16 + ADD HL, BC + ; small/big disk + LD A, (CPM_VARS.bdos_small_disk) + OR A + LD A, (CPM_VARS.bdos_dminx) ; rel block + JP Z, .alloc_big ; jump for 16bit + CALL bdos_hl_add_a ; HL = HL + A + LD (HL), E ; save record to access + JP .disk_wru + +.alloc_big: + ; save record to acces 16bit + LD C, A + LD B, 0 + ADD HL, BC + ADD HL, BC + LD (HL), E + INC HL + LD (HL), D + + ; Disk write to previously unallocated block +.disk_wru: + LD C, 2 ; C=2 - write to unused disk space +.disk_wr1: + LD A, (CPM_VARS.bdos_aret) ; check status + OR A + RET NZ ; return on error + + PUSH BC ; store C write flag for BIOS + CALL bdos_atran ; convert block number to logical sector + LD A, (CPM_VARS.bdos_seqio) ; get access mode (0=random, 1=sequential, 2=special) + DEC A + DEC A + JP NZ, .normal_wr + ; special + POP BC + PUSH BC + LD A, C ; A = write status flag (2=write unused space) + DEC A + DEC A + JP NZ, .normal_wr + ; fill buffer with zeroes + PUSH HL + LD HL, (CPM_VARS.bdos_buffa) + LD D, A +.fill0: + LD (HL), A + INC HL + INC D + JP P, .fill0 + CALL bdos_setdir ; Tell BIOS buffer address to directory access + LD HL, (CPM_VARS.bdos_arecord1) ; get sector that starts current block + LD C, 2 ; write status flag (2=write unused space) +.fill1: + LD (CPM_VARS.bdos_arecord), HL ; set sector to write + PUSH BC + CALL bdos_seek ; seek track and sector number + POP BC + CALL bdos_wrbuff ; write zeroes + LD HL, (CPM_VARS.bdos_arecord) ; get sector number + LD C, 0 ; normal write flag + LD A, (CPM_VARS.bdos_blmsk) ; block mask + ; write entire block? + LD B, A + AND L + CP B + INC HL + JP NZ, .fill1 ; continue until (BLKMSK+1) sectors written + ; reset sector number + POP HL + LD (CPM_VARS.bdos_arecord), HL + CALL bdos_setdata ; reset DMA address + + ; Normal write. +.normal_wr: + CALL bdos_seek ; Set track and sector + POP BC ; restore C - write status flag + PUSH BC + CALL bdos_wrbuff ; write + POP BC + LD A, (CPM_VARS.bdos_vrecord) ; last file record + LD HL, CPM_VARS.bdos_rcount ; last written record + CP (HL) + JP C, .disk_wr2 + ; update record count + LD (HL), A + INC (HL) + LD C, 2 +.disk_wr2: + DEC C ; patch: NOP + DEC C ; patch: NOP + JP NZ, .no_update ; patch: LD HL,0 + PUSH AF + CALL bdos_get_s2 ; set 'extent written to' flag + AND 0x7f ; clear 7th bit + LD (HL), A + POP AF ; record count + +.no_update: + ; it is full + CP 127 + JP NZ, .set_nxt_rec + ; sequential mode? + LD A, (CPM_VARS.bdos_seqio) + CP 1 + JP NZ, .set_nxt_rec + + CALL bdos_set_nxt_rec ; set next record + CALL bdos_open_reel ; get space in dir + LD HL, CPM_VARS.bdos_aret ; check status + LD A, (HL) + OR A + JP NZ, .no_space ; no more space + + DEC A + LD (CPM_VARS.bdos_vrecord), A ; -1 + +.no_space: + LD (HL), 0x00 ; clear status + +.set_nxt_rec: + JP bdos_set_nxt_rec + +; ------------------------------------------------------- +; Random access seek operation, C=0ffh if read mode +; FCB is assumed to address an active file control block +; (modnum has been set to 1100$0000b if previous bad seek) +; ------------------------------------------------------- +bdos_rseek: + XOR A + LD (CPM_VARS.bdos_seqio), A ; set random mode + +bdos_rseek1: + PUSH BC + LD HL, (CPM_VARS.bdos_info) + EX DE, HL + LD HL, FCB_RN ; Random access record number + ADD HL, DE + LD A, (HL) + AND 7Fh ; [6:0] bits record number to access + PUSH AF + ; get bit 7 + LD A, (HL) + RLA + INC HL + ; get high byte [3:0] bits + LD A, (HL) + RLA + AND 00011111b + ; get extent byte + LD C, A + LD A, (HL) + RRA + RRA + RRA + RRA + AND 0x0f + LD B, A + POP AF + INC HL + ; get next byte + LD L, (HL) + ; check overflow + INC L + DEC L + LD L, 6 + JP NZ, .seek_err ; error 6 if overflow + + ; save current record in FCB + LD HL, FCB_CR + ADD HL, DE + LD (HL), A + + ; check extent byte + LD HL, FCB_EXT + ADD HL, DE + LD A, C + SUB (HL) + JP NZ, .close ; not same as previous + ; check extra s2 byte + LD HL, FCB_S2 + ADD HL, DE + LD A, B + SUB (HL) + AND 0x7f + JP Z, .seek_ok ; same, is ok + +.close: + PUSH BC + PUSH DE + CALL bdos_close ; close current extent + POP DE + POP BC + LD L, 3 ; Cannot close error #3 + LD A, (CPM_VARS.bdos_aret) ; check status + INC A + JP Z, .bad_seek ; status != 0 - error + ; set extent byte to FCB + LD HL, FCB_EXT + ADD HL, DE + LD (HL), C + ; set S2 - extra extent byte + LD HL, FCB_S2 + ADD HL, DE + LD (HL), B + + CALL open + LD A, (CPM_VARS.bdos_aret) + INC A + JP NZ, .seek_ok + POP BC + PUSH BC + LD L, 0x4 ; Seek to unwritten extent #4 + INC C + JP Z, .bad_seek + CALL bdos_make + LD L, 0x5 ; Cannot create new extent #5 + LD A, (CPM_VARS.bdos_aret) + INC A + JP Z, .bad_seek +.seek_ok: + POP BC + XOR A + JP bdos_ret_a +.bad_seek: + PUSH HL + CALL bdos_get_s2 + LD (HL), 11000000b + POP HL +.seek_err: + POP BC + LD A,L + LD (CPM_VARS.bdos_aret), A + JP bdos_set_fwf + +; ------------------------------------------------------- +; Random disk read operation +; ------------------------------------------------------- +bdos_rand_disk_read: + LD C, 0xff ; mode + CALL bdos_rseek ; positioning + CALL Z, bdos_disk_read ; read if ok + RET + +; ------------------------------------------------------- +; Random disk write operation +; ------------------------------------------------------- +bdos_rand_disk_write: + LD C, 0 ; mode + CALL bdos_rseek ; positioning + CALL Z, bdos_disk_write ; read if ok + RET + +; ------------------------------------------------------- +; Compute random record position +; Inp: HL -> FCB +; DE - relative location of record number +; Out: C - r0 byte +; B - r1 byte +; A - r2 byte +; ZF set - Ok, reset - overflow +; ------------------------------------------------------- +bdos_compute_rr: + EX DE, HL ; DE -> FCB + ADD HL, DE ; HL = FCB+DE + LD C, (HL) ; get record number + LD B, 0 ; in BC + LD HL, FCB_EXT + ADD HL, DE + LD A, (HL) ; A = extent + ; calculate BC = recnum + extent*128 + RRCA ; A[0] -> A[7] + AND 0x80 ; ignore other bits + ADD A, C ; add to record number + LD C, A ; C=r0 + LD A, 0 + ADC A, B + LD B, A ; B=r1 + ; + LD A, (HL) ; A = extent + RRCA + AND 0x0f ; A = EXT[4:1] + ADD A, B ; add to r1 byte + LD B, A + + LD HL, FCB_S2 + ADD HL, DE + LD A, (HL) ; A = extra extent bits + ADD A, A ; *2 + ADD A, A ; *4 + ADD A, A ; *8 + ADD A, A ; *16 + PUSH AF ; save flags (need only CF) + ADD A, B ; add to r1 + LD B, A + PUSH AF ; save flags + POP HL ; flags on L + LD A, L ; A bit 0 - CF + POP HL + OR L ; merge both carry flags + AND 0x1 ; mask only CF + RET + +; ------------------------------------------------------- +; Compute logical file size for current FCB. +; Setup r0, r1, r2 bytes - maximum record number +; ------------------------------------------------------- +bdos_get_file_size: + LD C, FN_LEN + CALL bdos_search ; get first dir record (first extent) + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + ; zeroing r0, r1, r2 + LD DE, FCB_RN ; D=0 + ADD HL, DE + PUSH HL + LD (HL), D + INC HL + LD (HL), D + INC HL + LD (HL), D + ; +.get_extent: + CALL bdos_end_of_dir + JP Z, .done ; if not more extents found, return + CALL bdos_getdptra ; HL -> FCB + LD DE, FCB_RC + CALL bdos_compute_rr ; compute random access parameters + POP HL + PUSH HL + ; Now let's compare these values ​​with those indicated + LD E, A + LD A, C + SUB (HL) + INC HL + LD A, B + SBC A, (HL) + INC HL + LD A, E + SBC A, (HL) + JP C, .less_size + ; found larger extent (size), save it to fcb + LD (HL), E + DEC HL + LD (HL), B + DEC HL + LD (HL), C +.less_size: + CALL bdos_search_nxt + JP .get_extent +.done: + POP HL + RET + +; ------------------------------------------------------- +; Set the random record count bytes of the FCB to the number +; of the last record read/written by the sequential I/O calls. +; ------------------------------------------------------- +bdos_set_random: + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + LD DE, FCB_CR ; Current record within extent + CALL bdos_compute_rr ; Compute random access parameters + LD HL, FCB_RN + ADD HL, DE ; HL -> Random access record number + ; set r0, r1, r2 + LD (HL), C + INC HL + LD (HL), B + INC HL + LD (HL), A + RET + +; ------------------------------------------------------- +; Select disk to bdos_curdsk for subsequent input or +; output ops +; ------------------------------------------------------- +bdos_select: + LD HL, (CPM_VARS.bdos_dlog) ; login vector + LD A, (CPM_VARS.bdos_curdsk) ; current drive + LD C, A + CALL bdos_hlrotr ; shift active bit for this drive + PUSH HL + EX DE, HL + CALL bdos_sel_dsk ; select drive + POP HL + CALL Z, bdos_sel_error ; check for error drive + ; new active drive? + LD A, L + RRA + RET C ; no, return + ; yes, update login vector + LD HL, (CPM_VARS.bdos_dlog) + LD C, L + LD B, H + CALL bdos_set_dsk_bit ; set bits in vector + LD (CPM_VARS.bdos_dlog), HL ; store new vector + + JP bdos_initialize + +; ------------------------------------------------------- +; Select disc +; Inp: C=0x0E +; E=drive number 0-A, 1-B .. 15-P +; Out: L=A=0 - ok or 0xFF - error +; ------------------------------------------------------- +bdos_select_disk: + ; check disk change + LD A, (CPM_VARS.bdos_linfo) + LD HL, CPM_VARS.bdos_curdsk + CP (HL) + RET Z ; is same, return + ; login new disk + LD (HL), A + JP bdos_select + +; ------------------------------------------------------- +; Auto select disk by FCB_DR +; ------------------------------------------------------- +bdos_reselect: + LD A, TRUE + LD (CPM_VARS.bdos_resel), A ; set flag + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + LD A, (HL) ; get specified disk + AND 0x1f ; A = A[4:0] + DEC A ; adjust for 0-A, 1-B and so on + LD (CPM_VARS.bdos_linfo), A ; set as parameter + CP 0x1e ; no change? + JP NC, .no_select + ; change, but save current to old + LD A, (CPM_VARS.bdos_curdsk) + LD (CPM_VARS.bdos_olddsk), A + ; save FCB_DR byte + LD A, (HL) + LD (CPM_VARS.bdos_fcbdsk), A + ; clear FCB_DR[4:0] bits - user code + AND 11100000b + LD (HL), A + CALL bdos_select_disk + +.no_select: + ; set user to FCB_DR low bits + LD A, (CPM_VARS.bdos_userno) + LD HL, (CPM_VARS.bdos_info) + OR (HL) + LD (HL), A + RET + +; ------------------------------------------------------- +; Return version number +; ------------------------------------------------------- +bdos_get_version: + LD A, CPM_VERSION ; 0x22 - v2.2 + JP bdos_ret_a + +; ------------------------------------------------------- +; Reset disk system - initialize to disk 0 +; ------------------------------------------------------- +bdos_reset_disks: + LD HL, 0x00 + LD (CPM_VARS.bdos_rodsk), HL ; clear ro flags + LD (CPM_VARS.bdos_dlog), HL ; clear login vector + XOR A ; disk 0 + LD (CPM_VARS.bdos_curdsk), A ; set disk 'A' as current + LD HL, dma_buffer ; set 'DMA' buffer address to default + LD (CPM_VARS.bdos_dmaad), HL + CALL bdos_setdata + JP bdos_select ; select drive 'A' + +; ------------------------------------------------------- +; Open file +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_open_file: + CALL bdos_clear_s2 + CALL bdos_reselect + JP open + +; ------------------------------------------------------- +; Close file +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_close_file: + CALL bdos_reselect + JP bdos_close + +; ------------------------------------------------------- +; Search for first occurrence of a file in directory +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_search_first: + LD C, 0x00 ; special search + EX DE, HL + LD A, (HL) + CP '?' ; '?' is first byte? + JP Z, .qselect ; return first matched entry + + CALL bdos_getexta ; get extent byte from FCB + LD A, (HL) + CP '?' ; it is '?' + CALL NZ, bdos_clear_s2 ; set S2 to 0 if not + CALL bdos_reselect ; select disk by FCB + LD C, FCB_INFO_LEN ; match 1first 15 bytes +.qselect: + CALL bdos_search ; search first entry + JP bdos_dir_to_user ; move entry to user space + +; ------------------------------------------------------- +; Search for next occurrence of a file name +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_search_next: + LD HL, (CPM_VARS.bdos_searcha) ; restore FCB address + LD (CPM_VARS.bdos_info), HL ; set as parameter + CALL bdos_reselect ; select drive by FCB + CALL bdos_search_nxt ; search next + JP bdos_dir_to_user ; move entry to user space + +; ------------------------------------------------------- +; Remove directory +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_rm_file: + CALL bdos_reselect ; Select drive by FCB + CALL bdos_era_file ; Delete file + JP bdos_ret_dirloc ; Return directory location in A + +; ------------------------------------------------------- +; Read next 128b record +; Inp: DE -> FCB +; Out: BA and HL - error. +; A=0 - Ok, +; 1 - end of file, +; 9 - invalid FCB, +; 10 - media changed, +; 0FFh - hardware error. +; ------------------------------------------------------- +bdos_read_file: + CALL bdos_reselect ; select drive + JP bdos_seq_disk_read ; and read + +; ------------------------------------------------------- +; Write next 128b record +; Inp: DE -> FCB +; Out: BA and HL - error. +; A=0 - Ok, +; 1 - directory full, +; 2 - disc full, +; 9 - invalid FCB, +; 10 - media changed, +; 0FFh - hardware error. +; ------------------------------------------------------- +bdos_write_file: + CALL bdos_reselect ; select drive + JP bdos_seq_disk_write ; and write + +; ------------------------------------------------------- +; Create file +; Inp: DE -> FCB. +; Out: Error in BA and HL +; A=0 - Ok, +; 0FFh - directory is full. +; ------------------------------------------------------- +bdos_make_file: + CALL bdos_clear_s2 ; clear S2 byte + CALL bdos_reselect ; select drive + JP bdos_make ; and make file + +; ------------------------------------------------------- +; Rename file. New name, stored at FCB+16 +; Inp: DE -> FCB. +; Out: Error in BA and HL +; A=0-3 if successful; +; A=0FFh if error. +; ------------------------------------------------------- +bdos_ren_file: + CALL bdos_reselect ; select drive + CALL bdos_rename ; rename file + JP bdos_ret_dirloc ; Return directory location in A + +; ------------------------------------------------------- +; Return bitmap of logged-in drives +; Out: bitmap in HL. +; ------------------------------------------------------- +bdos_get_login_vec: + LD HL, (CPM_VARS.bdos_dlog) + JP sthl_ret + +; ------------------------------------------------------- +; Return current drive +; Out: A - currently selected drive. 0 => A:, 1 => B: etc. +; ------------------------------------------------------- +bdos_get_cur_drive: + LD A, (CPM_VARS.bdos_curdsk) + JP bdos_ret_a + +; ------------------------------------------------------- +; Set DMA address +; Inp: DE - address of DMA buffer +; ------------------------------------------------------- +bdos_set_dma_addr: + EX DE, HL + LD (CPM_VARS.bdos_dmaad), HL + JP bdos_setdata + +; ------------------------------------------------------- +; fn27: Return the address of cur disk allocation map +; Out: HL - address +; ------------------------------------------------------- +bdos_get_alloc_addr: + LD HL, (CPM_VARS.bdos_alloca) + JP sthl_ret + +; ------------------------------------------------------- +; Get write protection status +; ------------------------------------------------------- +bdos_get_wr_protect: + LD HL, (CPM_VARS.bdos_rodsk) + JP sthl_ret + +; ------------------------------------------------------- +; Set file attributes (ro, system) +; ------------------------------------------------------- +bdos_set_attr: + CALL bdos_reselect + CALL bdos_update_attrs + JP bdos_ret_dirloc + +; ------------------------------------------------------- +; Return address of disk parameter block of the +; current drive +; ------------------------------------------------------- +bdos_get_dpb: + LD HL, (CPM_VARS.bdos_dpbaddr) + +; ------------------------------------------------------- +; Common code to return Value from BDOS functions +; ------------------------------------------------------- +sthl_ret: + LD (CPM_VARS.bdos_aret), HL + RET + +; ------------------------------------------------------- +; Get/set user number +; Inp: E - 0..15 - set user. 0xFF - get user +; Out: A - returns user number +; ------------------------------------------------------- +bdos_set_user: + LD A, (CPM_VARS.bdos_linfo) + CP 0xff + JP NZ, .set_user + LD A, (CPM_VARS.bdos_userno) + JP bdos_ret_a +.set_user: + AND 0x1f ; mask for 0..31 but will be < 16 + LD (CPM_VARS.bdos_userno), A + RET + +; ------------------------------------------------------- +; Random read. Record specified in the random record +; count area of the FCB, at the DMA address +; Inp: DE -> FCB +; Out: Error codes in BA and HL. +; ------------------------------------------------------- +bdos_rand_read: + CALL bdos_reselect ; select drive by FCB + JP bdos_rand_disk_read ; and read + +; ------------------------------------------------------- +; Random access write record. +; Record specified in the random record count area of the FCB, at the DMA address +; Inp: DE -> FCB +; Out: Error codes in BA and HL. +; ------------------------------------------------------- +bdos_rand_write: + CALL bdos_reselect ; select drive by FCB + JP bdos_rand_disk_write ; and write + +; ------------------------------------------------------- +; Compute file size. +; Set the random record count bytes of the FCB to the +; number of 128-byte records in the file. +; ------------------------------------------------------- +bdos_compute_fs: + CALL bdos_reselect ; select drive by FCB + JP bdos_get_file_size ; and get file size + +; ------------------------------------------------------- +; Selectively logoff (reset) disc drives +; Out: A=0 - Ok, 0xff if error +; ------------------------------------------------------- +bdos_reset_drives: + LD HL, (CPM_VARS.bdos_info) ; HL - drives to logoff map + LD A, L + CPL + LD E, A + LD A, H + CPL + LD HL, (CPM_VARS.bdos_dlog) ; get vector + AND H ; reset in hi byte + LD D, A ; and store in D + LD A, L ; reset low byte + AND E + LD E, A ; and store in E + LD HL, (CPM_VARS.bdos_rodsk) ; HL - disk RO map + EX DE, HL + LD (CPM_VARS.bdos_dlog), HL ; store new login vector + ; reset RO flags + LD A, L + AND E + LD L, A + LD A, H + AND D + LD H, A + ; and store new value + LD (CPM_VARS.bdos_rodsk), HL + RET + +; ------------------------------------------------------- +; Reach this point at the end of processing +; to return the data to the user. +; ------------------------------------------------------- +bdos_goback: + LD A, (CPM_VARS.bdos_resel) ; check for selection drive by user FCB + OR A + JP Z, bdos_ret_mon ; return if not selected + + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB_DR + LD (HL), 0 + LD A, (CPM_VARS.bdos_fcbdsk) + OR A ; restore drive + JP Z, bdos_ret_mon ; return if default + LD (HL), A ; restore + LD A, (CPM_VARS.bdos_olddsk) ; previous drive + LD (CPM_VARS.bdos_linfo), A ; set parameter + CALL bdos_select_disk ; select previous drive + +; ------------------------------------------------------- +; Return from the disk monitor +; ------------------------------------------------------- +bdos_ret_mon: + LD HL, (CPM_VARS.bdos_usersp) ; return user stack + LD SP, HL + LD HL, (CPM_VARS.bdos_aret) ; get return value + LD A, L ; ver 1.4 CP/M compatibility + LD B, H + RET + +; ------------------------------------------------------- +; Random disk write with zero fill of +; unallocated block +; ------------------------------------------------------- +bdos_rand_write_z: + CALL bdos_reselect ; select drive + LD A, 2 ; special write mode + LD (CPM_VARS.bdos_seqio), A + LD C, FALSE ; write indicator + CALL bdos_rseek1 ; seek position in a file + CALL Z, bdos_disk_write ; write if no errors + RET + +; ------------------------------------------------------- +; Unuser initialized data +; ------------------------------------------------------- +filler: + DB 0xF1, 0xE1 ; POP AF, POP HL + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xC800 +FILL_SIZE EQU 0xE00-CODE_SIZE + + DISPLAY "| BDOS\t| ",/H,bdos_start," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ASSERT bdos_rand_write_z = 0xd55e + + +FILLER + DS FILL_SIZE, 0xFF + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_2e4a7b71/bios.asm b/CPM_v2.2_r8_2e4a7b71/bios.asm new file mode 100644 index 0000000..dedb8dc --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/bios.asm @@ -0,0 +1,823 @@ +; ======================================================= +; Ocean-240.2 +; +; CP/M BIOS +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "mon_entries.inc" + + IFNDEF BUILD_ROM + OUTPUT bios.bin + ENDIF + + MODULE BIOS + + ORG 0xD600 + +; ------------------------------------------------------- +; BIOS JUMP TABLE +; ------------------------------------------------------- +boot_f: + JP bios_boot +wboot_f: + JP bios_wboot + +; ------------------------------------------------------- +; console status to reg-a +; ------------------------------------------------------- +const_f: + JP MON.non_con_status + +; ------------------------------------------------------- +; console character to reg-a +; ------------------------------------------------------- +conin_f: + JP MON.mon_con_in + +; ------------------------------------------------------- +; console character from c to console out +; ------------------------------------------------------- +conout_f: + JP MON.mon_con_out + +; ------------------------------------------------------- +; list device out +; ------------------------------------------------------- +list_f: + JP MON.mon_char_print + +; ------------------------------------------------------- +; punch device out +; ------------------------------------------------------- +punch_f: + JP MON.mon_serial_out + +; ------------------------------------------------------- +; reader character in to reg-a +; ------------------------------------------------------- +reader_f: + JP MON.mon_serial_in + +; ------------------------------------------------------- +; move to home position, treat as track 00 seek +; ------------------------------------------------------- +home_f: + JP home + +; ------------------------------------------------------- +; select disk given by register c +; ------------------------------------------------------- +seldsk_f: + JP sel_disk +settrk_f: + JP set_trk +setsec_f: + JP set_sec + +; ------------------------------------------------------- +; Set DMA address from BC +; ------------------------------------------------------- +setdma_f: + JP set_dma +read_f: + JP read +write_f: + JP write +listst_f: + JP list_st +sectran_f: + JP sec_tran + +; ------------------------------------------------------- +; Reserved +; ------------------------------------------------------- + JP warm_boot + JP warm_boot + +; ------------------------------------------------------- +; Tape read +; ------------------------------------------------------- +tape_read_f: + JP MON.mon_tape_read + +; ------------------------------------------------------- +; Tape write +; ------------------------------------------------------- +tape_write_f: + JP MON.mon_tape_write + +; ------------------------------------------------------- +; Tape wait block +; ------------------------------------------------------- +tape_wait_f: + JP MON.mon_tape_wait + + JP warm_boot ; r8 + +disk_a_size DW 0x00C0 ; 192k disk A size +disk_b_size DW 0x02d0 ; 720 disk B size +disk_c_size DW 0x02d0 ; 720 disk C size +bios_var04 DB 0x50 +bios_var05 DB 0x50 + +; ------------------------------------------------------- +; cold start +; ------------------------------------------------------- +bios_boot: + LD HL, (bdos_ent_addr) ; 0xba06 + LD DE, 0x10000-CCP_RAM.bdos_enter_jump ; 0x45fa + ADD HL, DE ; 0xba06+0x45fa=10000 + LD A, H + OR L + JP Z, bios_signon + + ; >> r8 + LD HL, (bios_var0) ; if bios_var0 = 0xaaaa (initialized) + LD DE, 0x5556 + ADD HL, DE ; 0xaaaa+0x5556=0x10000 if initialized + LD A, H + OR L + JP Z, bios_signon ; if initialized, go to logon, skip init + ; << + + ; Init DMA buffer + LD HL, dma_buffer ; 0x8000 + LD B, DMA_BUFF_SIZE ; 0x80 +.init_dma_buff: + LD (HL), FILE_DELETED ; 0xE5 + INC HL + DEC B + JP NZ, .init_dma_buff + + ; Init RAM disk + LD HL, dma_buffer + LD DE, 0x0000 + LD B, 8 + +.init_ram_dsk: + PUSH BC + CALL MON.ram_disk_write + POP BC + INC DE + DEC B + JP NZ, .init_ram_dsk + + ; Init user to 0 and drive to 0 + XOR A + LD (cur_user_drv), A + + ; init bios variables + CALL bios_init_ilv ; r8 + +bios_signon: + LD SP, bios_stack + ; Print CP/M hello message + LD HL, msg_hello + CALL print_strz + JP bios_wboot + +;bios_wboot: +; LD SP, bios_stack + +; r8 >> +bios_init_ilv: + LD HL, bios_var0 + LD DE, bios_ini_vals + LD C, 13 + CALL mov_dlhe_c + LD A, (bios_var05) ; 0x50 + LD (bios_var2), A + RET +; << + +; ------------------------------------------------------- +; BIOS Warm start entry +; ------------------------------------------------------- +bios_wboot: ; r8 + + LD HL, (bios_var0) + LD DE, 0x5556 ; 0xaaaa + 0x5556 = 0x10000 + ADD HL, DE + LD A, H + OR L + CALL NZ, bios_init_ilv ; init if not initialized before + LD SP, bios_stack + + ; Move CPP from 0xC000 to 0xB200 + LD HL, CCP_DST_ADDR + LD DE, CCP_SRC_ADDR + LD BC, CCP_SIZE +.move_ccp: + LD A, (DE) + LD (HL), A + INC DE + INC HL + DEC BC + LD A, B + OR C + JP NZ, .move_ccp + + ; Init variables with zeroes + LD HL, CPM_VARS.cpm_vars_start + LD BC, CPM_VARS.ccp_vars_size ; 213 + +.clr_cpm_vars: + LD (HL), 0x0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .clr_cpm_vars + + LD A, FILE_DELETED + LD (CPM_VARS.bdos_efcb), A ; mark empty FCB + LD A, low dma_buffer ; 0x80 + LD (CPM_VARS.bdos_dmaad), A ; 0x0080 + + ; Move DPH + DPB to RAM + LD HL, CPM_VARS.DPH_RAM + LD DE, dph_disk_a + LD BC, DPB_END-dph_disk_a ; 0x39 -> 57d + +.move_dph: + LD A, (DE) + LD (HL), A + INC HL + INC DE + DEC BC + LD A, B + OR C + JP NZ, .move_dph + + LD BC, dma_buffer ; DMA default buffer addr + CALL setdma_f + + ; Setup JP to Warm boot after CPU reset + LD A, JP_OPCODE + LD (warm_boot), A + LD HL, wboot_f + LD (warm_boot_addr), HL + + ; Setup JP to BDOS entrance + LD (jp_bdos_enter), A + LD HL, CCP_RAM.bdos_enter_jump + LD (bdos_ent_addr), HL + + LD HL, CPM_VARS.DPB_A_RAM + LD C, 0xf + LD DE, dpb_ram + LD A, (disk_a_size+1) + OR A + JP Z, set_curr_dpb + LD DE, dpb_empty + +set_curr_dpb: + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_B_RAM + LD C, 0xf + LD DE, dpb_flop_360k + LD A, (disk_b_size+1) + CP 0x1 + JP Z, .l1 + LD DE, dpb_flop_720k +.l1 + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_C_RAM + LD C, 0xf + LD A, (disk_c_size+1) + CP 0x2 + JP Z, .l2 + LD DE, dpb_flop_360k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_720k + JP .l3 +.l2 + LD DE, dpb_flop_720k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_360k +.l3 + CALL mov_dlhe_c + XOR A + LD (CPM_VARS.slicer_has_data),A + LD (CPM_VARS.slicer_uninited_count),A + LD A,(cur_user_drv) + LD C,A + ; go to CCP + JP CCP_DST_ADDR + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +mov_dlhe_c: + LD A,(DE) ; [DE]->[HL] + LD (HL),A + INC HL + INC DE + DEC C + JP NZ, mov_dlhe_c + RET + +list_st: + XOR A + RET + +; ------------------------------------------------------- +; Select disk +; Inp: C - disk, +; E - active drive flag +; Out: HL -> DPH +; ------------------------------------------------------- +sel_disk: + LD HL, 0x00 + LD A, C + CP CTRL_C + RET NC + + LD (CPM_VARS.cur_disk), A + OR A + JP Z, .get_dph_addr ; skip next for disk 0 - RAM disk + LD A, E ; selected disk map + AND 0x1 ; bit 0 is set if disk already selected + JP NZ, .get_dph_addr + ; Reset disk + LD (CPM_VARS.slicer_has_data), A + LD (CPM_VARS.slicer_uninited_count), A + ; calc DPH address of new drive +.get_dph_addr: + LD L, C + LD H, 0x0 + ADD HL, HL ; *2 + ADD HL, HL ; *4 + ADD HL, HL ; *8 + ADD HL, HL ; *16 (size of DBH) + LD DE, CPM_VARS.DPH_RAM + ADD HL, DE + RET + +; ------------------------------------------------------- +; move to track 00 +; ------------------------------------------------------- +home: + LD A, (CPM_VARS.cur_disk) + OR A + JP Z, .is_default + LD A, (CPM_VARS.slicer_need_save) + OR A + JP NZ, .is_default + LD (CPM_VARS.slicer_has_data), A ; set to 0, no data + +.is_default: + LD C, 0 ; set track to 0 + +; ------------------------------------------------------- +; set track address (0..76) for subsequent read/write +; ------------------------------------------------------- +set_trk: + LD HL, CPM_VARS.curr_track + LD (HL), C + RET + +; ------------------------------------------------------- +; set sector address (1,..., 26) for subsequent read/write +; ------------------------------------------------------- +set_sec: + LD HL, CPM_VARS.curr_sec + LD (HL), C + RET + +; ------------------------------------------------------- +; set subsequent dma address (initially 80h) +; ------------------------------------------------------- +set_dma: + LD L, C + LD H, B + LD (CPM_VARS.dma_addr), HL + RET + +sec_tran: + LD L, C + LD H, B + RET + +; ------------------------------------------------------- +; read track/sector to preset dma address +; ------------------------------------------------------- +read: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ, read_phys ; for physical disk use special routine + CALL ram_disk_calc_addr + CALL MON.ram_disk_read + XOR A + RET + +; ------------------------------------------------------- +; write track/sector from preset dma address +; ------------------------------------------------------- +write: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ,write_phys + CALL ram_disk_calc_addr + CALL MON.ram_disk_write + XOR A + RET + +; ------------------------------------------------------- +; Calculate address for current sector and track +; ------------------------------------------------------- +ram_disk_calc_addr: + LD HL, CPM_VARS.curr_track + ; HL = cur_track * 16 + LD L, (HL) + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ; DE = HL + cur_sec + EX DE, HL + LD HL, CPM_VARS.curr_sec + LD L, (HL) + LD H, 0x0 + ADD HL, DE + EX DE, HL + ; store address + LD HL, (CPM_VARS.dma_addr) + RET + +read_phys: + CALL read_phys_op + RET + +write_phys: + CALL write_phys_op + RET + +read_phys_op: + XOR A + ; reset counter + LD (CPM_VARS.slicer_uninited_count), A + LD A, 0x1 + LD (CPM_VARS.tmp_slicer_operation), A ; 0 - write; 1 - read + LD (CPM_VARS.tmp_slicer_can_read), A ; enable read fron disk + LD A, 0x2 + LD (CPM_VARS.tmp_slicer_flush), A ; disable flush data to disk + JP base_read_write + +write_phys_op: + XOR A + LD (CPM_VARS.tmp_slicer_operation), A + LD A, C + LD (CPM_VARS.tmp_slicer_flush), A + CP 0x2 + JP NZ, .mode_ne_2 + LD A, 0x10 ; 2048/128 + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_uninited_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_uninited_track), A + LD A, (CPM_VARS.curr_sec) + LD (CPM_VARS.slicer_uninited_sector_128), A +.mode_ne_2: + LD A, (CPM_VARS.slicer_uninited_count) + OR A + JP Z, slicer_read_write + DEC A + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_uninited_disk + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_uninited_track + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_sec) + LD HL, CPM_VARS.slicer_uninited_sector_128 + CP (HL) + JP NZ, slicer_read_write + INC (HL) + LD A, (HL) + CP 36 ; Sectors per track + JP C, .no_inc_track + LD (HL), 0x0 + LD A, (CPM_VARS.slicer_uninited_track) + INC A + LD (CPM_VARS.slicer_uninited_track), A + +.no_inc_track: + XOR A + LD (CPM_VARS.tmp_slicer_can_read), A + JP base_read_write + +; -------------------------------------------------- +slicer_read_write: + XOR A + LD (CPM_VARS.slicer_uninited_count), A + INC A + LD (CPM_VARS.tmp_slicer_can_read), A + +; -------------------------------------------------- +base_read_write: + XOR A + LD (CPM_VARS.tmp_slicer_result), A + LD A, (CPM_VARS.curr_sec) + OR A + RRA + OR A + RRA + LD (CPM_VARS.tmp_slicer_real_sector), A + LD HL, CPM_VARS.slicer_has_data + LD A, (HL) + LD (HL), 0x1 + OR A + JP Z, .no_data + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_disk + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_track + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD HL, CPM_VARS.slicer_real_sector + CP (HL) + JP Z,calc_sec_addr_in_bfr +.pos_diff: + LD A, (CPM_VARS.slicer_need_save) + OR A + CALL NZ, slicer_save_buffer ; save buffer if needed +.no_data: + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_track), A + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD (CPM_VARS.slicer_real_sector), A + LD A, (CPM_VARS.tmp_slicer_can_read) + OR A + CALL NZ,slicer_read_buffer + XOR A + LD (CPM_VARS.slicer_need_save), A + +; -------------------------------------------------- +calc_sec_addr_in_bfr: + LD A, (CPM_VARS.curr_sec) + AND 0x3 + LD L, A + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL ; *128 + LD DE, CPM_VARS.slicer_buffer + ADD HL, DE + EX DE, HL + LD HL, (CPM_VARS.dma_addr) + LD C, 0x80 + LD A, (CPM_VARS.tmp_slicer_operation) + OR A + JP NZ, .no_save + LD A, 0x1 + LD (CPM_VARS.slicer_need_save), A + EX DE, HL +.no_save: + LD A, (DE) + INC DE + LD (HL), A + INC HL + DEC C + JP NZ, .no_save + LD A, (CPM_VARS.tmp_slicer_flush) + CP 0x1 + LD A, (CPM_VARS.tmp_slicer_result) + RET NZ + OR A + RET NZ + XOR A + LD (CPM_VARS.slicer_need_save), A + CALL slicer_save_buffer + LD A, (CPM_VARS.tmp_slicer_result) + RET + +; -------------------------------------------------- +slicer_save_buffer: + CALL slicer_get_floppy_args + LD C, 0xA6 ; VG93 CMD + CALL MON.write_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_read_buffer: + CALL slicer_get_floppy_args + LD C, 0x86 ; VG93 CMD + CALL MON.read_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_get_floppy_args: + LD HL, CPM_VARS.tmp_buff9 + LD A, (CPM_VARS.slicer_disk) + DEC A + JP Z, .non_interleave + LD HL, interleave_0 +.non_interleave + LD A, (CPM_VARS.slicer_real_sector) + ADD A, L + LD L, A + LD E, (HL) + LD A, (CPM_VARS.slicer_track) + LD D, A + LD HL, CPM_VARS.slicer_buffer + LD A, (CPM_VARS.slicer_disk) + RET + + +; ------------------------------------------------------- +; Print zerro ended string +; Inp: HL -> string +; ------------------------------------------------------- +print_strz: + LD A, (HL) + OR A + RET Z + LD C, A + PUSH HL + CALL conout_f + POP HL + INC HL + JP print_strz + +msg_hello: + DB ASCII_ESC, "60" ; Режим 32x18 60 + DB ASCII_ESC, "83" + DB ASCII_ESC, "5!%" + DB ASCII_ESC, "41" + DB ASCII_ESC, "1", 0x16, 0xe2, 0xe2, 0xfc, 0x01 + DB ASCII_ESC, "40" + DB ASCII_ESC, "1", 0x1e, 0xe6, 0xdb, 0xf8, 0x01 + DB ASCII_ESC, "43" + DB "OKEAH-240 CP/M (V2.2) REL.8'\r\n\n" + DB ASCII_ESC, "42", 0 + + +; -------------------------------------------------- +; Disk parameters blocks in ROM (DPBs) +; Tables of memory that describe the characteristics +; of discs on our system. There is one DPB for each +; disc type + +; ---------------------------------------- +; Block size | No of sectors | BSH | BLM | +; ---------------------------------------- +; 1K | 8 | 3 | 7 | +; 2K | 16 | 4 | 15 | +; 4K | 32 | 5 | 31 | +; 8K | 64 | 6 | 63 | +; 16K | 128 | 7 | 127 | +; ---------------------------------------- + +; ------------------------------------- +; Block size| Extent mask (EXM) | +; | Small disk | Large disk | +; ------------------------------------- +; 2K | 1 | 0 | +; 4K | 3 | 1 | +; 8K | 7 | 3 | +; 16K | 15 | 7 | +; ------------------------------------- +; CKS - number of dir sectors to check before write, 0 for HDD + +; For RAM-Disk 128k +dpb_ram: + DW 0010h ; SPT Sector (128b) per track (16d) + DB 03h ; BSH 1k + DB 07h ; BLM 1k; Allocation block size = (BLM + 1) * 128 = 1k + DB 00h ; EXM extent mask + DW 191 ; DSM Disk size blocks - 1 + DW 31 ; DRM Dir elements - 1 + DB 10000000b ; AL0 Dir map byte 1 + DB 00000000b ; AL1 Dir map byte 2 + DW 0008h ; CKS checksum vector size (8 sectors=1k) + DW 0000h ; OFF (tracks reserved for system) + +dpb_empty: + DS 15, 0xff + +; For FLOPPY 720k +dpb_flop_720k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 00h ; EXM extent mask + DW 359 ; DSM Disk size blocks - 1 (359d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +; For FLOPPY 360k +dpb_flop_360k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 01h ; EXM extent mask + DW 179 ; DSM Disk size blocks - 1 (179d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +bios_ini_vals: + DB 0xaa, 0xaa, 0, 0xff, 1, 8, 6, 4, 2, 9, 7, 5, 3 + +; -------------------------------------------------- +; Disk parameters headers in ROM +; -------------------------------------------------- +; Disk A RAM +dph_disk_a: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + ; 0xda00 + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_A_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_A ; Check Vector pointer + DW CPM_VARS.AL_MAP_A ; Allocation map pointer + +; Disk B Floppy +dph_disk_b: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_B_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_B ; Check Vector pointer + DW CPM_VARS.AL_MAP_B ; Allocation map pointer + +; Disk C Floppy +dph_disk_c: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_C_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_C ; Check Vector pointer + DW CPM_VARS.AL_MAP_C ; Allocation map pointer + + + +res_data: ; 0xda28 + DB 1,8,6,4,2,9,7,5,3 +DPB_END EQU $ + DB 0x0e, 3 + + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xD600 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| BIOS\t| ",/H,boot_f," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT bios_wboot = 0xd6a8 + ASSERT set_curr_dpb = 0xd722 + ASSERT sel_disk = 0xd781 + ASSERT home = 0xd7a7 + ASSERT ram_disk_calc_addr = 0xd7eb + ASSERT write_phys_op = 0xd81e + ASSERT base_read_write = 0xd88a + ASSERT calc_sec_addr_in_bfr = 0xd8e4 + ASSERT slicer_save_buffer = 0xd928 + ASSERT print_strz = 0xd95e + ASSERT msg_hello = 0xd96b + ASSERT dpb_ram = 0xd9af + ASSERT dph_disk_a = 0xd9f8 + ASSERT res_data = 0xda28 + +FILLER: + DS FILL_SIZE, 0xff + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_2e4a7b71/ccp_ram.asm b/CPM_v2.2_r8_2e4a7b71/ccp_ram.asm new file mode 100644 index 0000000..a2ecada --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/ccp_ram.asm @@ -0,0 +1,1525 @@ +; ====================================================== +; Ocean-240.2 +; CP/M V2.2 R8. +; CCP (Console Command Processor) Resident part +; ORG C000 at RPM, moved to B200-BA09 +; +; Disassembled by Romych 2026-02-01 +; ====================================================== + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + + IFNDEF BUILD_ROM + OUTPUT ccp_ram.bin + ENDIF + + MODULE CCP_RAM + + ORG 0xB200 + +ccp_ram_ent: + JP ccp_ent + JP ccp_clear_buff + +ccp_inbuff: + DB 0x7F, 0x00 + +ccp_inp_line: + DB ASCII_SP, ASCII_SP + +msg_copyright: + DB " COPYRIGHT (C) 1979, DIGITAL RESEARCH ", 0x00 + DS 73, 0x0 + +ccp_inp_line_addr: + DW ccp_inp_line + +cur_name_ptr: + DW 0h + +; --------------------------------------------------- +; Call BDOS function 2 (C_WRITE) - Console output +; Inp: A - char to output +; --------------------------------------------------- +ccp_print: + LD E, A + LD C, 2 + JP jp_bdos_enter + +; --------------------------------------------------- +; Put char to console +; Inp: A - char +; --------------------------------------------------- +ccp_putc: + PUSH BC + CALL ccp_print + POP BC + RET + +ccp_out_crlf: + LD A, ASCII_CR + CALL ccp_putc + LD A, ASCII_LF + JP ccp_putc +ccp_out_space: + LD A,' ' + JP ccp_putc + +; --------------------------------------------------- +; Out message from new line +; Inp: BC -> Message +; --------------------------------------------------- +ccp_out_crlf_msg: + PUSH BC + CALL ccp_out_crlf + POP HL + +; --------------------------------------------------- +; Out asciiz message +; Inp: HL -> Message +; --------------------------------------------------- +ccp_out_msg: + LD A, (HL) ;= "READ ERROR" + OR A + RET Z + INC HL + PUSH HL + CALL ccp_print + POP HL + JP ccp_out_msg + +; --------------------------------------------------- +; Call BDOS function 13 (DRV_ALLRESET) - Reset discs +; --------------------------------------------------- +ccp_bdos_drv_allreset: + LD C,13 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS function 14 (DRV_SET) - Select disc +; Inp: A - disk +; --------------------------------------------------- +ccp_bdos_drv_set: + LD E, A + LD C, 14 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS fn and return result +; Inp: C - fn no +; Out: A - error + 1 +; --------------------------------------------------- +ccp_call_bdos: + CALL jp_bdos_enter + LD (ccp_bdos_result_code), A + INC A + RET + +; --------------------------------------------------- +; BDOS function 15 (F_OPEN) - Open file /Dir +; Inp: DE -> FCB +; Out: A=0 for error, or 1-4 for success +; --------------------------------------------------- +ccp_bdos_open_f: + LD C, 15 + JP ccp_call_bdos + +; --------------------------------------------------- +; Open file by current FCB +; --------------------------------------------------- +ccp_open_cur_fcb: + XOR A + LD (ccp_current_fcb_cr), A ; clear current record counter + LD DE, ccp_current_fcb + JP ccp_bdos_open_f + +; --------------------------------------------------- +; BDOS function 16 (F_CLOSE) - Close file +; --------------------------------------------------- +ccp_bdos_close_f: + LD C, 16 + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 17 (F_SFIRST) - search for first +; Out: A = 0 in error, 1-4 if success +; --------------------------------------------------- +ccp_bdos_find_first: + LD C, 17 + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 18 (F_SNEXT) - search for next +; Out: A = 0 in error, 1-4 if success +; --------------------------------------------------- +ccp_bdos_find_next: + LD C, 18 + JP ccp_call_bdos ; BDOS 18 (F_SNEXT) - search for next ? + +; --------------------------------------------------- +; Call BDOS F_FIRST with current FCB +; --------------------------------------------------- +ccp_find_first: + LD DE, ccp_current_fcb + JP ccp_bdos_find_first + +; --------------------------------------------------- +; Call BDOS function 19 (F_DELETE) - delete file +; --------------------------------------------------- +ccp_bdos_era_file: + LD C,19 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS and set ZF by result +; --------------------------------------------------- +ccp_bdos_enter_zf: + CALL jp_bdos_enter + OR A + RET + +; --------------------------------------------------- +; Read next 128 bytes of file +; Inp: DE -> FCB +; Out: a = 0 - ok; +; 1 - EOF; +; 9 - invalid FCB; +; 10 - Media changed; +; 0xFF - HW error. +; --------------------------------------------------- +ccp_bdos_read_f: + LD C, 20 + JP ccp_bdos_enter_zf + +; --------------------------------------------------- +; Read file by current FCB +; --------------------------------------------------- +ccp_read_f_fcb: + LD DE, ccp_current_fcb + JP ccp_bdos_read_f + +; --------------------------------------------------- +; Call BDOS function 21 (F_WRITE) - write next record +; --------------------------------------------------- +ccp_bdos_f_write: + LD C, 21 + JP ccp_bdos_enter_zf + +; --------------------------------------------------- +; Call BDOS function 22 (F_MAKE) - create file +; --------------------------------------------------- +ccp_bdos_make_f: + LD C, 22 + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 23 (F_RENAME) - Rename file +; --------------------------------------------------- +ccp_bdos_rename_f: + LD C, 23 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS function 32 (F_USERNUM) for get user number +; Out: A - user no +; --------------------------------------------------- +ccp_bdos_get_user: + LD E, 0xff + +; --------------------------------------------------- +; Call BDOS function 32 (F_USERNUM) set user number +; Inp: E - user no 0-15, or 0xFF for get user +; Out: A - user no +; --------------------------------------------------- +ccp_bdos_set_user: + LD C, 32 + JP jp_bdos_enter + +; --------------------------------------------------- +; Get user no and store in upper nibble of sys var +; --------------------------------------------------- +ccp_set_cur_drv: + CALL ccp_bdos_get_user + ADD A, A + ADD A, A + ADD A, A + ADD A, A ; user no at upper nibble + LD HL, ccp_cur_drive + OR (HL) + LD (cur_user_drv), A + RET + +; --------------------------------------------------- +; Replace new drive in lower nibble of sys var +; reset user no +; Out: A - drive no +; --------------------------------------------------- +ccp_reset_cur_drv: + LD A, (ccp_cur_drive) + LD (cur_user_drv), A + RET + +; --------------------------------------------------- +; Uppercase character a..z +; Inp: A - character +; Out: A - character in upper case +; --------------------------------------------------- +char_to_upper: + CP 'a' + RET C + CP '{' + RET NC + ; character in a..z range + AND 0x5f + RET + +; --------------------------------------------------- +; Get user input from console or from batch +; file $$$.SUB +; --------------------------------------------------- +ccp_get_inp: + LD A, (ccp_batch) + OR A + JP Z, ccp_gin_no_batch + + ; select drive A for $$$.SUB + LD A, (ccp_cur_drive) + OR A + LD A, 0x0 + CALL NZ, ccp_bdos_drv_set + + ; open batch file + LD DE, ccp_batch_fcb + CALL ccp_bdos_open_f + JP Z, ccp_gin_no_batch ; go to con inp if file not found + + ; read last record + LD A, (ccp_batch_fcb_rc) + DEC A + LD (ccp_batch_fcb_cr), A + LD DE, ccp_batch_fcb + CALL ccp_bdos_read_f + JP NZ, ccp_gin_no_batch ; stop on EOF + + ; move data from file to buffer + LD DE, ccp_inbuff+1 + LD HL, dma_buffer + LD B, DMA_BUFF_SIZE ; 0x80 + CALL ccp_mv_hlde_b + LD HL, ccp_batch_fcb_s2 + LD (HL), 0x0 + INC HL + DEC (HL) ; decriment record count + ; close batch file + LD DE, ccp_batch_fcb + CALL ccp_bdos_close_f + JP Z, ccp_gin_no_batch + ; reselect old drive if not 0 + LD A, (ccp_cur_drive) + OR A + CALL NZ, ccp_bdos_drv_set + ; print command line + LD HL, ccp_inp_line ; ccp_inbuff+2 + CALL ccp_out_msg + CALL ccp_getkey_no_wait + JP Z, ccp_gin_nokey + ; terminate batch processing on any key + CALL ccp_del_batch + JP ccp_get_command + + ; get user input from keyboard +ccp_gin_no_batch: + CALL ccp_del_batch + CALL ccp_set_cur_drv + + LD C, 10 + LD DE, ccp_inbuff + ; Call BDOS C_READSTR DE -> inp buffer + CALL jp_bdos_enter + + CALL ccp_reset_cur_drv + +ccp_gin_nokey: + LD HL, ccp_inbuff+1 + LD B, (HL) + +ccp_gin_uppr: + INC HL + LD A, B + OR A + JP Z, ccp_gin_uppr_end + LD A, (HL) ;= 2020h + CALL char_to_upper + LD (HL), A ;= 2020h + DEC B + JP ccp_gin_uppr + +ccp_gin_uppr_end: + LD (HL), A ; set last character to 0 + LD HL, ccp_inp_line ; + LD (ccp_inp_line_addr), HL ; + RET + +; --------------------------------------------------- +; Check keyboard +; Out: A - pressed key code +; ZF set if no key pressed +; --------------------------------------------------- +ccp_getkey_no_wait: + LD C,11 + ; Call BDOS (C_STAT) - Console status + CALL jp_bdos_enter + OR A + RET Z ; ret if no character waiting + LD C,1 + ; Call BDOS (C_READ) - Console input + CALL jp_bdos_enter + OR A + RET + +; --------------------------------------------------- +; Call BDOS function 25 (DRV_GET) - Return current drive +; Out: A - drive 0-A, 1-B... +; --------------------------------------------------- +ccp_bdos_drv_get: + LD C, 25 + JP jp_bdos_enter + +; --------------------------------------------------- +; Set disk buffer address to default buffer +; --------------------------------------------------- +cpp_set_disk_buff_addr: + LD DE, dma_buffer + +; --------------------------------------------------- +; Call BDOS function 26 (F_DMAOFF) - Set DMA address +; Inp: DE - address +; --------------------------------------------------- +ccp_bdos_dma_set: + LD C, 26 + JP jp_bdos_enter + +; --------------------------------------------------- +; Delete batch file created by submit +; --------------------------------------------------- +ccp_del_batch: + LD HL, ccp_batch + LD A, (HL) + OR A + RET Z ; return if no active batch file + LD (HL), 0x0 ; mark as inactive + XOR A + CALL ccp_bdos_drv_set ; select drive 0 + LD DE, ccp_batch_fcb + CALL ccp_bdos_era_file + LD A, (ccp_cur_drive) + JP ccp_bdos_drv_set + +; -------------------------------------------------- +; Check "serial number" of CP/M +; -------------------------------------------------- +ccp_verify_pattern: + LD DE, cpm_pattern ; = F9h + LD HL, 0x0000 + LD B, 6 + +ccp_chk_patt_nex: + LD A, (DE) + CP (HL) + NOP ; JP NZ HALT was here + NOP + NOP + INC DE + INC HL + DEC B + JP NZ, ccp_chk_patt_nex + RET + +; -------------------------------------------------- +; Print syntax error indicator +; -------------------------------------------------- +print_syn_err: + CALL ccp_out_crlf + LD HL, (cur_name_ptr) +pse_next: + LD A, (HL) + CP ASCII_SP + JP Z, pse_end + OR A + JP Z, pse_end + PUSH HL + + CALL ccp_print + POP HL + INC HL + JP pse_next +pse_end: + LD A, '?' + CALL ccp_print + CALL ccp_out_crlf + CALL ccp_del_batch + JP ccp_get_command + +; -------------------------------------------------- +; Check user input characters for legal range +; Inp: [DE] - pointer to character +; -------------------------------------------------- +cpp_valid_inp: + LD A, (DE) + OR A + RET Z + CP ASCII_SP ; >= Space + JP C, print_syn_err + RET Z + CP '=' + RET Z + CP '_' + RET Z + CP '.' + RET Z + CP ':' + RET Z + CP ';' + RET Z + CP '<' + RET Z + CP '>' + RET Z + RET + +; --------------------------------------------------- +; Find non space character until end +; Inp: DE -> current character +; Out: DE -> non space +; A - character +; ZF set on EOL +; --------------------------------------------------- +ccp_find_no_space: + LD A, (DE) + OR A + RET Z + CP ASCII_SP + RET NZ + INC DE + JP ccp_find_no_space + +; --------------------------------------------------- +; HL=HL+A +; --------------------------------------------------- +sum_hl_a: + ADD A, L + LD L, A + RET NC + INC H ; inc H if CF is set + RET + +; --------------------------------------------------- +; Convert first name to fcb +; --------------------------------------------------- +ccp_cv_first_to_fcb: + LD A, 0x0 + +; Convert filename from cmd to fcb +; replace '*' to '?' +; Inp: A - offset in fcb filename +; Out: A - count of '?' in file name +; ZF is set for fegular file name +ccp_cv_fcb_filename: + LD HL, ccp_current_fcb + CALL sum_hl_a + PUSH HL + PUSH HL + XOR A + LD (ccp_chg_drive), A + LD HL, (ccp_inp_line_addr) ; HL -> input line + EX DE, HL + CALL ccp_find_no_space ; get next non blank char + EX DE, HL + LD (cur_name_ptr), HL ; save name ptr + EX DE, HL + POP HL + LD A, (DE) ; load first name char + OR A + JP Z, cur_cvf_n_end + SBC A, 'A'-1 ; 0x40 for drive letter + LD B, A + INC DE + LD A, (DE) + CP ':' ; is ':' after drive letter? + JP Z, cur_cvf_drv_ltr + DEC DE ; no, step back + +cur_cvf_n_end: + LD A, (ccp_cur_drive) + LD (HL), A + JP cur_cvf_basic_fn + +cur_cvf_drv_ltr: + LD A, B + LD (ccp_chg_drive), A ; set change drive flag + LD (HL), B + INC DE + +cur_cvf_basic_fn: + LD B, 8 ; file name length + +cur_cvf_chr_nxt: + CALL cpp_valid_inp + JP Z, cur_cvf_sp_remains + INC HL + CP '*' + JP NZ, cur_cvf_no_star + LD (HL), '?' + JP cur_cvf_nxt1 + +cur_cvf_no_star: + LD (HL), A + INC DE + +cur_cvf_nxt1: + DEC B + JP NZ, cur_cvf_chr_nxt + +cur_cvf_nxt_delim: + CALL cpp_valid_inp + JP Z, cur_cvf_ext + INC DE + JP cur_cvf_nxt_delim + + ; fill remains with spaces +cur_cvf_sp_remains: + INC HL + LD (HL), ASCII_SP + DEC B + JP NZ, cur_cvf_sp_remains + + ; file name extension +cur_cvf_ext: + LD B, 3 + CP '.' + JP NZ, cur_cvf_ext_fill_sp + INC DE + ; handle current ext char +cur_cvf_ext_nxt: + CALL cpp_valid_inp + JP Z, cur_cvf_ext_fill_sp + INC HL + ; change * to ? + CP '*' + JP NZ, cur_cvf_no_star2 + LD (HL), '?' + JP cur_cvf_nxt2 +cur_cvf_no_star2: + LD (HL), A + INC DE +cur_cvf_nxt2: + DEC B + JP NZ, cur_cvf_ext_nxt + +cur_cvf_ext_skip: + CALL cpp_valid_inp + JP Z, cur_cvf_rst_attrs + INC DE + JP cur_cvf_ext_skip + + ; skip remains ext pos with dpaces +cur_cvf_ext_fill_sp: + INC HL + LD (HL), ASCII_SP + DEC B + JP NZ, cur_cvf_ext_fill_sp + + ; set fcb extent, res1, res2 to 0 +cur_cvf_rst_attrs: + LD B, 3 + +cur_cvf_attrs_nxt: + INC HL + LD (HL), 0x0 + DEC B + JP NZ, cur_cvf_attrs_nxt + + EX DE, HL + LD (ccp_inp_line_addr), HL ; save input line pointer + + ; Check for ambigeous file name + POP HL ; -> file name in fcb + LD BC, 0x000b ; b=0, c=11 +cur_cvf_ambig: + INC HL + LD A, (HL) + CP '?' + JP NZ, cur_cvf_ambig_nxt1 + INC B +cur_cvf_ambig_nxt1: + DEC C + JP NZ, cur_cvf_ambig + LD A, B ; a = count of '?' + OR A ; set ZF if regular filename + RET + +; --------------------------------------------------- +; CP/M command table +; --------------------------------------------------- +cpm_cmd_tab: + DB "DIR " + DB "ERA " + DB "TYPE" + DB "SAVE" + DB "REN " + DB "USER" + +cpm_pattern: + DB 0xF9, 0x16, 0, 0, 0, 0x6B ; CP/M serial number? + ;LD SP, HL + ;LD D, 0x00 + ;NOP + ;NOP + ;LD L,E + +; --------------------------------------------------- +; Search for CP/M command +; Out: A - command number +; --------------------------------------------------- +ccp_search_cmd: + LD HL, cpm_cmd_tab + LD C, 0x0 + +ccp_sc_cmd_nxt: + LD A, C + CP CCP_COMMAND_CNT ; 6 + RET NC + LD DE, ccp_current_fcb_fn ; fcb filename + LD B, CCP_COMMAND_LEN ; max cmd len +ccp_sc_chr_nxt: + LD A, (DE) + CP (HL) ; compate fcb fn and command table + JP NZ, ccp_sc_no_match ; cmd not match + INC DE + INC HL + DEC B + JP NZ, ccp_sc_chr_nxt + ; last can be space for 3-letter commands + LD A, (DE) + CP ASCII_SP + JP NZ, ccp_sc_skip_cmd + LD A, C ; return command number in A + RET + + ; skip remains +ccp_sc_no_match: + INC HL + DEC B + JP NZ, ccp_sc_no_match + ; go to next cmd +ccp_sc_skip_cmd: + INC C + JP ccp_sc_cmd_nxt + +; -------------------------------------------------- +; Clear command buffer and go to command processor +; -------------------------------------------------- +ccp_clear_buff: + XOR A + LD (ccp_inbuff+1), A ; actual buffer len = 0 + +; -------------------------------------------------- +; Entrance to CCP +; Inp: C - current user * 16 +; -------------------------------------------------- +@ccp_ent: + LD SP, ccp_stack + PUSH BC ; + LD A, C + ; / 16 + RRA + RRA + RRA + RRA + ; cur user no in low nibble + AND 0x0f ; user 0..15 + LD E, A + CALL ccp_bdos_set_user + CALL ccp_bdos_drv_allreset + LD (ccp_batch), A + POP BC + LD A, C ; a = user*16 + AND 0xf ; low nibble - drive + LD (ccp_cur_drive), A + CALL ccp_bdos_drv_set + LD A, (ccp_inbuff+1) + OR A + JP NZ, ccp_process_cmd + +; -------------------------------------------------- +; Out prompt and get user command from console +; -------------------------------------------------- +ccp_get_command: + LD SP, ccp_stack ; reset stack pointer + CALL ccp_out_crlf ; from new line + CALL ccp_bdos_drv_get + ADD A, 65 ; convert drive no to character + CALL ccp_print ; print current drive letter + LD A, '>' ; and prompt + CALL ccp_print + CALL ccp_get_inp ; and wait string + +; -------------------------------------------------- +; Process command +; -------------------------------------------------- +ccp_process_cmd: + LD DE, dma_buffer + CALL ccp_bdos_dma_set ; setup buffer + CALL ccp_bdos_drv_get + LD (ccp_cur_drive), A ; store cur drive + CALL ccp_cv_first_to_fcb ; convert first command parameter to fcb + CALL NZ, print_syn_err ; if wildcard, out error message + LD A, (ccp_chg_drive) ; check drive change flag + OR A + JP NZ, ccp_unk_cmd ; if drive changed, handle as unknown command + CALL ccp_search_cmd ; ret A = command number + LD HL, ccp_cmd_addr + ; DE = command number + LD E, A + LD D, 0x0 + ADD HL, DE + ADD HL, DE ; HL = HL + 2*command_number + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + JP (HL) ; jump to command handler + +ccp_cmd_addr: + DW cmd_dir + DW cmd_erase + DW cmd_type + DW cmd_save + DW cmd_ren + DW cmd_user + DW ccp_entry + +; --------------------------------------------------- +; di+halt if serial number validation failed +; --------------------------------------------------- +cpp_halt: + LD HL, 0x76f3 + LD (ccp_ram_ent), HL + LD HL, ccp_ram_ent + JP (HL) + +ccp_type_rd_err: + LD BC, .msg_read_error ; BC -> "READ ERROR" + JP ccp_out_crlf_msg + +.msg_read_error: + DB "READ ERROR", 0 + +; --------------------------------------------------- +; Out message 'NO FILE' +; --------------------------------------------------- +ccp_out_no_file: + LD BC, .msg_no_file ; BC -> "NO FILE" + JP ccp_out_crlf_msg +.msg_no_file: + DB "NO FILE", 0 + +; --------------------------------------------------- +; Decode a command in form: "A>filename number" +; Out: A - number +; --------------------------------------------------- +ccp_decode_num: + CALL ccp_cv_first_to_fcb + LD A, (ccp_chg_drive) + OR A + JP NZ, print_syn_err ; error if drive letter specified + LD HL, ccp_current_fcb_fn + LD BC, 11 ; b=0, c=11 (filename len) + + ; decode number +ccp_dff_nxt_num: + LD A, (HL) + CP ASCII_SP + JP Z, ccp_dff_num_fin ; space - end of number + ; check for digit + INC HL + SUB '0' + CP 10 + JP NC, print_syn_err ; not a digit + LD D, A ; d = number + LD A, B + AND 0xe0 ; check B (sum) overflow + JP NZ, print_syn_err + ; A=B*10 + LD A, B + RLCA + RLCA + RLCA ; *8 + ADD A, B ; *9 + JP C, print_syn_err ; error if overflow + ADD A, B ; * 10 + JP C, print_syn_err ; error if overflow + ; B = B + B*10 + ADD A, D + JP C, print_syn_err ; error if overflow + LD B, A + ; to next number + DEC C + JP NZ, ccp_dff_nxt_num + RET + +ccp_dff_num_fin: + LD A, (HL) + CP ASCII_SP + JP NZ, print_syn_err ; will be space after number + INC HL + DEC C + JP NZ, ccp_dff_num_fin + LD A, B + RET + +; -------------------------------------------------- +; Move 3 bytes from [HL] to [DE] +; (Used only to move file extension) +; -------------------------------------------------- +ccp_mv_hlde_3: + LD B, 3 + +; -------------------------------------------------- +; Move B bytes from [HL] to [DE] +; -------------------------------------------------- +ccp_mv_hlde_b: + LD A, (HL) + LD (DE), A + INC HL + INC DE + DEC B + JP NZ, ccp_mv_hlde_b + RET + +; -------------------------------------------------- +; Return byte from address dma_buffer[A+C] +; Out: A - byte at dma_buffer[A+C] +; HL - dma_buffer+A+C +; -------------------------------------------------- +get_std_buff_ac: + LD HL, dma_buffer + ADD A, C + CALL sum_hl_a + LD A, (HL) + RET + +; -------------------------------------------------- +; Check drive change and select if needed +; Reset drive byte in fcb to 0 (use default) +; -------------------------------------------------- +ccp_drive_sel: + XOR A + LD (ccp_current_fcb), A + LD A, (ccp_chg_drive) + OR A + RET Z ; no need to change cur drive + DEC A + LD HL, ccp_cur_drive + CP (HL) + RET Z ; current and new drive is same + JP ccp_bdos_drv_set ; change + +; -------------------------------------------------- +; Restore previous drive if changed during operation +; -------------------------------------------------- +ccp_restor_drv: + LD A, (ccp_chg_drive) + OR A + RET Z ; not changed + DEC A + LD HL, ccp_cur_drive ; chk cur drive + CP (HL) + RET Z ; new and previous drive is same + LD A, (ccp_cur_drive) + JP ccp_bdos_drv_set ; restore to previous drive + +; -------------------------------------------------- +; Handle user DIR command +; -------------------------------------------------- +cmd_dir: + CALL ccp_cv_first_to_fcb + CALL ccp_drive_sel + ; check filemask specified + LD HL, ccp_current_fcb_fn + LD A, (HL) + CP ASCII_SP + JP NZ, .dir_fmask ; yes specified + + ; fill with wildcard symbol + LD B, 11 +.fill_wc: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_wc + +.dir_fmask: + LD E, 0x0 ; cursor at 0 + PUSH DE + ; find first file + CALL ccp_find_first + CALL Z, ccp_out_no_file ; no file found + +.dir_f_next: + JP Z, .dir_f_no_more + LD A, (ccp_bdos_result_code) + ; find filename pos in direntry + ; a = a * 32 + RRCA + RRCA + RRCA + AND 0x60 + + LD C, A + LD A, 0x0a + + ; get std_buff[10+pos*8] + CALL get_std_buff_ac + RLA ; CF<-[7:0]<-CF + JP C, .dir_dont_lst ; don't display sys files + POP DE + LD A, E ; a = cursor + INC E + PUSH DE ; cursor++ + AND 0x3 + PUSH AF + JP NZ, .dir_no_eol + ; eol, print new line + CALL ccp_out_crlf + ; print A: + PUSH BC + CALL ccp_bdos_drv_get + POP BC + ADD A, 'A' + CALL ccp_putc + LD A, ':' + CALL ccp_putc + JP .dir_out_sp +.dir_no_eol: + ; add space between filenames + CALL ccp_out_space + LD A, ':' + CALL ccp_putc +.dir_out_sp: + CALL ccp_out_space + LD B, 0x1 +.dir_get_one: + LD A, B + CALL get_std_buff_ac + AND 0x7f ; mask status bit + CP ASCII_SP ; name end? + JP NZ, .no_name_end + ; at end of file name + POP AF + PUSH AF + CP 0x3 + JP NZ, .dir_end_ext + LD A, 0x9 + CALL get_std_buff_ac ; chk ext + AND 0x7f ; 7bit + CP ASCII_SP + JP Z, .dir_skp_sp ; do not print space +.dir_end_ext: + LD A, ASCII_SP +.no_name_end: + CALL ccp_putc + INC B + LD A, B + CP 12 + JP NC, .dir_skp_sp ; print until end of file ext + CP 9 + JP NZ, .dir_get_one ; start of file ext? + CALL ccp_out_space ; print sep space + JP .dir_get_one +.dir_skp_sp: + POP AF +.dir_dont_lst: + ; stop if key pressed + CALL ccp_getkey_no_wait + JP NZ, .dir_f_no_more + + ; find next directory entry + CALL ccp_bdos_find_next + JP .dir_f_next + + ; no more to print +.dir_f_no_more: + POP DE + JP ccp_cmdline_back + +; -------------------------------------------------- +; Handle user ERA command +; -------------------------------------------------- +cmd_erase: + CALL ccp_cv_first_to_fcb + ; check for *.* + CP 11 + JP NZ, .era_no_wc + ; confirm erase all + LD BC, msg_all_yn ;= "ALL (Y/N)?" + CALL ccp_out_crlf_msg + CALL ccp_get_inp + LD HL, ccp_inbuff+1 + ; check user input + DEC (HL) + JP NZ, ccp_get_command + INC HL + LD A, (HL) ; user input, first letter + CP 'Y' + JP NZ, ccp_get_command ; return in not exactly 'Y' + INC HL + LD (ccp_inp_line_addr), HL + +.era_no_wc: + CALL ccp_drive_sel ; select drive + LD DE, ccp_current_fcb ; specify current fcb + CALL ccp_bdos_era_file ; and delete file + INC A + CALL Z, ccp_out_no_file + JP ccp_cmdline_back ; go back to command line + +msg_all_yn: + DB "ALL (Y/N)?", 0 + +; -------------------------------------------------- +; Handle user TYPE command +; -------------------------------------------------- +cmd_type: + CALL ccp_cv_first_to_fcb + JP NZ, print_syn_err ; error if wildcard + ; select drive and open + CALL ccp_drive_sel + CALL ccp_open_cur_fcb + JP Z, .not_found ; cant open file + CALL ccp_out_crlf + LD HL, ccp_bytes_ctr + LD (HL), 0xff ; 255>128 for read first sector + +.cont_or_read: + LD HL, ccp_bytes_ctr + LD A, (HL) + CP 128 + JP C, .out_next_char + + ; read 128 bytes + PUSH HL + CALL ccp_read_f_fcb + POP HL + JP NZ, .read_no_ok + ; clear counter + XOR A + LD (HL), A + +.out_next_char: + ; get next byte from buffer + INC (HL) + LD HL, dma_buffer + ; calc offset + CALL sum_hl_a + LD A, (HL) + CP ASCII_SUB ; Ctrl+Z end of text file + JP Z, ccp_cmdline_back ; yes, back to cmd line + CALL ccp_print ; print char to output device + ; interrupt if key pressed + CALL ccp_getkey_no_wait + JP NZ, ccp_cmdline_back + ; + JP .cont_or_read + + ; non zero result from f_read +.read_no_ok: + DEC A + JP Z, ccp_cmdline_back ; A=1 - EOF, return to cmd line + CALL ccp_type_rd_err ; else read error + +.not_found: + CALL ccp_restor_drv + JP print_syn_err + +; -------------------------------------------------- +; Handle user SAVE command +; -------------------------------------------------- +cmd_save: + CALL ccp_decode_num ; get num of pages + PUSH AF ; and store + CALL ccp_cv_first_to_fcb ; conv filename to fcb + JP NZ, print_syn_err ; error if wildcard + ; delete specified file + CALL ccp_drive_sel + LD DE, ccp_current_fcb + PUSH DE + CALL ccp_bdos_era_file + POP DE + ; create specified file + CALL ccp_bdos_make_f + JP Z, ccp_no_space ; 0xff+1 if error + XOR A + LD (ccp_current_fcb_cr), A ; curr record = 0 + POP AF ; a = num pages + LD L, A + LD H, 0 + ADD HL, HL ; HL = A * 2 - number of sectors + LD DE, tpa_start + +.write_next: + ; all sectors written? + LD A, H + OR L + JP Z, ccp_close_f_cur ; no more sectors to write + DEC HL + PUSH HL + ; set buffer address to memory to write + LD HL, 128 + ADD HL, DE + PUSH HL + CALL ccp_bdos_dma_set + ; and write sector + LD DE, ccp_current_fcb + CALL ccp_bdos_f_write + POP DE + POP HL + JP NZ, ccp_no_space ; check for no space left + JP .write_next + +; Close current file +ccp_close_f_cur: + LD DE, ccp_current_fcb + CALL ccp_bdos_close_f + INC A + JP NZ, rest_buf_ret_cmd + +; -------------------------------------------------- +; Out error message about no space left +; -------------------------------------------------- +ccp_no_space: + LD BC, msg_no_space ; BC -> "NO SPACE" + CALL ccp_out_crlf_msg + +rest_buf_ret_cmd: + CALL cpp_set_disk_buff_addr + JP ccp_cmdline_back + +msg_no_space: + DB "NO SPACE", 0 + +; -------------------------------------------------- +; Handle user REN command +; -------------------------------------------------- +cmd_ren: + CALL ccp_cv_first_to_fcb ; get first file name + JP NZ, print_syn_err ; error if wildcard + LD A, (ccp_chg_drive) ; remember drive change flag + PUSH AF + ; check file already exists + CALL ccp_drive_sel + CALL ccp_find_first + JP NZ, .file_exists + ; move filename to "second slot" + LD HL, ccp_current_fcb + LD DE, ccp_current_fcb_fn+15 + LD B, 16 + CALL ccp_mv_hlde_b + ; + LD HL, (ccp_inp_line_addr) ; restore cmd line pointer + EX DE, HL + CALL ccp_find_no_space ; skip spaces between parameters + CP '=' + JP Z, .do_rename + CP '_' + JP NZ, .rename_err + +.do_rename: + EX DE, HL + INC HL ; skip sep + LD (ccp_inp_line_addr), HL ; -> second param + CALL ccp_cv_first_to_fcb ; get second name + JP NZ, .rename_err ; error if wildcard + ; if drive specified it will be same as previous + POP AF + LD B, A + LD HL, ccp_chg_drive + LD A, (HL) + OR A + JP Z, .same_drive ; ok, it is same + CP B + LD (HL), B ; restore first drive + JP NZ, .rename_err + +.same_drive: + LD (HL), B + ; check for seacond file not exists + XOR A + LD (ccp_current_fcb), A + CALL ccp_find_first + JP Z, .second_exists + ; calll bdos to rename + LD DE, ccp_current_fcb + CALL ccp_bdos_rename_f + JP ccp_cmdline_back + +.second_exists: + CALL ccp_out_no_file + JP ccp_cmdline_back + +.rename_err: + CALL ccp_restor_drv + JP print_syn_err + +.file_exists: + LD BC, .msg_file_exists ; BC -> "FILE EXISTS" + CALL ccp_out_crlf_msg + JP ccp_cmdline_back + +.msg_file_exists: + DB "FILE EXISTS", 0 + +; -------------------------------------------------- +; Handle user USER command +; -------------------------------------------------- +cmd_user: + CALL ccp_decode_num ; get user number + ; user will be 0..15 + CP 16 + JP NC, print_syn_err ; >15 - error + LD E, A ; save in E + ; check for other parameters + LD A, (ccp_current_fcb_fn) + CP ASCII_SP + JP Z, print_syn_err ; error if other parameters specified + ; call bdos to set current user + CALL ccp_bdos_set_user + JP ccp_cmdline_back1 + +ccp_unk_cmd: + CALL ccp_verify_pattern ; check if system valid + ; check for file to execute specified + LD A, (ccp_current_fcb_fn) + CP ASCII_SP + JP NZ, .exec_file + ; drive change? + LD A, (ccp_chg_drive) + OR A + JP Z, ccp_cmdline_back1 ; no, return to cmd line + ; change drive + DEC A + LD (ccp_cur_drive), A + CALL ccp_reset_cur_drv + CALL ccp_bdos_drv_set + JP ccp_cmdline_back1 + +.exec_file: + ; check file extension + LD DE, ccp_current_fcb_ft + LD A, (DE) + CP ASCII_SP + JP NZ, print_syn_err + PUSH DE + ; select specified drive + CALL ccp_drive_sel + POP DE + ; set file ext to 'COM' + LD HL, msg_com ; HL -> 'COM' + CALL ccp_mv_hlde_3 + CALL ccp_open_cur_fcb + JP Z, cant_open_exe + LD HL, tpa_start ; load to start of TPA 0x100 + +.read_next_sec: + PUSH HL ; store start pointer + EX DE, HL + CALL ccp_bdos_dma_set + + LD DE, ccp_current_fcb + CALL ccp_bdos_read_f + JP NZ, .read_no_ok ; check for read error + + ; shift start pointer for sector size + POP HL + LD DE, 128 + ADD HL, DE + ; check for enough space in RAM + LD DE, ccp_ram_ent + LD A, L + SUB E + LD A, H + SBC A, D + JP NC, ccp_bad_load + JP .read_next_sec + +.read_no_ok: + POP HL + DEC A + JP NZ, ccp_bad_load ; it is not EOF, is error + ; ok, EOF + CALL ccp_restor_drv ; get first filename + CALL ccp_cv_first_to_fcb + LD HL, ccp_chg_drive ; hl -> buff[16] + PUSH HL + LD A, (HL) + LD (ccp_current_fcb), A ; set drive letter in current fcb + LD A, 16 + CALL ccp_cv_fcb_filename ; replace wildcards + POP HL + ; set drive for second file in reserved fcb area + LD A, (HL) + LD (ccp_current_fcb_al), A + ; clear record count + XOR A + LD (ccp_current_fcb_cr), A + ; Move current to default FCB + LD DE, fcb1 + LD HL, ccp_current_fcb + LD B, 33 + CALL ccp_mv_hlde_b + + ; move remainder of cmd line to 0x0080 + LD HL, ccp_inp_line +.skip_nosp: + LD A, (HL) + OR A + JP Z, .z_or_sp + CP ASCII_SP + JP Z, .z_or_sp + INC HL + JP .skip_nosp + +.z_or_sp: + LD B, 0 ; len of cmd line for program = 0 + LD DE, p_cmd_line ; destination address for cmd line + +.copy_cmd_line: + LD A, (HL) + LD (DE), A + OR A + JP Z, .stor_len + INC B + INC HL + INC DE + JP .copy_cmd_line + +.stor_len: + LD A, B + LD (p_cmd_line_len), A + ; next line + CALL ccp_out_crlf + ; set buffer to cmd line + CALL cpp_set_disk_buff_addr + ; set drive + CALL ccp_set_cur_drv + ; and call loaded program + CALL tpa_start + ; restore stack first + LD SP, ccp_stack + ; restore current drive + CALL ccp_reset_cur_drv + CALL ccp_bdos_drv_set + ; return back to command line mode + JP ccp_get_command + +cant_open_exe: + CALL ccp_restor_drv + JP print_syn_err + +ccp_bad_load: + LD BC, .msg_bad_load ; BC -> "BAD LOAD" + CALL ccp_out_crlf_msg + JP ccp_cmdline_back + +.msg_bad_load: + DB "BAD LOAD", 0 + +msg_com: + DB "COM" + +; -------------------------------------------------- +; Return back to command line +; -------------------------------------------------- +ccp_cmdline_back: + CALL ccp_restor_drv + +ccp_cmdline_back1: + CALL ccp_cv_first_to_fcb + LD A, (ccp_current_fcb_fn) + SUB 0x20 + LD HL, ccp_chg_drive + OR (HL) + JP NZ, print_syn_err + JP ccp_get_command + + DW 0h, 0h, 0h, 0h, 0h, 0h, 0h, 0h + +ccp_stack EQU $ + +ccp_batch: + DB 0h + +ccp_batch_fcb: + DB 0h ; drive code, 0 - default + DB "$$$ SUB" ; filename + DB 0h ; extent + DB 0h ; S1 +ccp_batch_fcb_s2: + DB 0h ; S2 Extent [6:0] bits and [7] write flag +ccp_batch_fcb_rc: + DB 0h ; sectors count +ccp_batch_fcb_al: + DS 16, 0 ; reserved by CPM use only +ccp_batch_fcb_cr: + DB 0h ; current sector to read/write + +ccp_current_fcb: + DB 0h +ccp_current_fcb_fn: + DS 8, 0 +ccp_current_fcb_ft: + DS 3, 0 +ccp_current_fcb_ex: + DB 0h ; extent + DB 0h ; s1 + DB 0h ; s2 + DB 0h ; sectors count +ccp_current_fcb_al: + DS 16, 0 ; reserved by CPM use only +ccp_current_fcb_cr: + DB 0h ; current sector to read/write + +ccp_bdos_result_code: + DB 0h + +ccp_cur_drive: + DB 0h + +ccp_chg_drive: ; change drive flag, 0 - no change + DB 0 +ccp_bytes_ctr: + DW 0 + ; reserved + DS 13, 0 + + +bdos_enter_jump EQU $+6 + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xB200 +;FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| CCP_RAM\t| ",/H,ccp_ram_ent," | ",/H,CODE_SIZE," | \t |" + + ; Check integrity + ASSERT ccp_cmdline_back = 0xb986 + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/ccp_rom.asm b/CPM_v2.2_r8_2e4a7b71/ccp_rom.asm new file mode 100644 index 0000000..b859801 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/ccp_rom.asm @@ -0,0 +1,901 @@ +; ======================================================= +; Ocean-240.2 +; CPM CPP, ROM PART +; AT 0xDB00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + + IFNDEF BUILD_ROM + OUTPUT ccp_rom.bin + ENDIF + + MODULE CCP_ROM + + ORG 0xDB00 + +@ccp_entry: + LD HL, 0x0 ; prevent stack overflow + ADD HL, SP + LD (CPM_VARS.saved_stack_ptr), HL + LD SP, CPM_VARS.ccp_safe_stack + + CALL get_cmd_index + LD HL, ccp_commands ;= DB6Ch + LD E, A + LD D, 0x0 + ADD HL, DE + ADD HL, DE + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + JP (HL) ; jump to command + +ccp_commands_str: + DB "SDIR READ WRITE" + +; ------------------------------------------------------- +; Search user command position in available commands list +; ------------------------------------------------------- +get_cmd_index: + LD HL, ccp_commands_str ; -> 'DIR' + LD C, 0x0 +.cmd_next: + LD A, C + CP CCP_COMMAND_COUNT + RET NC + LD DE, CCP_RAM.ccp_current_fcb_fn + LD B, CCP_COMMAND_SIZE +.cmp_nxt: + LD A, (DE) + CP (HL) ; -> 'DIR' + JP NZ, .no_eq + INC DE + INC HL + DEC B + JP NZ, .cmp_nxt + LD A, (DE) + CP ASCII_SP + JP NZ, .inc_next + LD A, C + RET +.no_eq: + INC HL + DEC B + JP NZ, .no_eq +.inc_next: + INC C + JP .cmd_next + +; -------------------------------------------------- +; Command handlers ref table +; -------------------------------------------------- +ccp_commands: + DW ccp_dir + DW ccp_read + DW ccp_write + DW ccp_ret ; r8 +; DW ccp_exit1 ; r8 + +ccp_ret: + LD HL, (CPM_VARS.saved_stack_ptr) + LD SP, HL + JP CCP_RAM.ccp_unk_cmd + +;ccp_exit1: +; JP MON.mon_cold_start + +; -------------------------------------------------- +; DIR [filemask] command handler +; -------------------------------------------------- +ccp_dir: + CALL CCP_RAM.ccp_cv_first_to_fcb + CALL CCP_RAM.ccp_drive_sel + ; chech some filemask specified in command line + LD HL, CCP_RAM.ccp_current_fcb_fn + LD A, (HL) + CP ' ' + JP NZ, .has_par + + ; no filemask, fill with wildcard '?' + LD B, 11 +.fill_wildcard: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_wildcard + + ; find file by specified mask +.has_par: + CALL CCP_RAM.ccp_find_first + JP NZ, .f_found + ; no files found, print and exit + CALL CCP_RAM.ccp_out_no_file + JP CCP_RAM.ccp_cmdline_back + +.f_found: + CALL CCP_RAM.ccp_out_crlf + LD HL, 0x0 + LD (CPM_VARS.tmp_dir_total), HL + LD E, 0 + +.do_next_direntry: + PUSH DE + CALL CCP_RAM.ccp_find_first + POP DE + PUSH DE + + ; Find file with e number +.find_file_e: + DEC E + JP M, .file_out_next + PUSH DE + CALL CCP_RAM.ccp_bdos_find_next + POP DE + JP Z, .file_e_found + JP .find_file_e + +.file_out_next: + LD A, (CCP_RAM.ccp_bdos_result_code) + ; calc address of DIR entry in DMA buffer + ; A[6:5] = A[1:0] = 32*A + RRCA ; [C] -> [7:0] -> [C] + RRCA ; + RRCA ; + AND 01100000b ; mask + LD C, A + PUSH BC + CALL CCP_RAM.ccp_out_crlf ; start new line + CALL CCP_RAM.ccp_bdos_drv_get + INC A + LD (dma_buffer), A ; disk + POP BC + LD B, 0x0 + LD HL, dma_buffer+FCB_FN ; filename + LD E, L + LD D, H + ADD HL, BC + + ; copy filename to tmp FCB and out to screen + LD B, 0x1 +.copy_next: + LD A, (HL) + LD (DE), A + LD C, A + CALL BIOS.conout_f + INC HL + INC DE + INC B + LD A, B + CP FN_LEN ; >12 end of name + JP C, .copy_next + +.zero_up_36: + XOR A + LD (DE), A ; zero at end + INC B + LD A, B + CP 36 + JP C, .zero_up_36 + + ; calc file size for current entry + LD DE, dma_buffer + CALL cpp_bdos_f_size + LD HL, (fcb_ra_record_num) ; file size in blocks + + ; get disk blk size + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_dsk_a0 + LD B, 3 ; for A - blk=3 + JP .dsk_a0 + +.no_dsk_a0: + LD B, 4 ; for other disks - blk=4 +.dsk_a0: + LD C, L + + ; convert 128b OS block to disk blocks +.mul_to_dsk_blk: + XOR A + LD A, H + RRA + LD H, A + LD A, L + RRA + LD L, A + DEC B + JP NZ, .mul_to_dsk_blk + ; round up + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_dsk_a1 + LD A, 00000111b ; for A - ~(~0 << 3) + JP .ds_skip1 +.no_dsk_a1: + LD A, 00001111b ; for other dsk - ~(~0 << 4) +.ds_skip1: + AND C + JP Z, .cvt_blk_kb + INC HL + + ; Convert blocks to kilobytes (A-1k B-2k) +.cvt_blk_kb: + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP Z, .ds_skip2 + ADD HL, HL ; 2k + + ; add file size to total dir size +.ds_skip2: + EX DE, HL + LD HL, (CPM_VARS.tmp_dir_total) + ADD HL, DE + LD (CPM_VARS.tmp_dir_total), HL + + ; display size in K + LD C, ' ' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL ccp_cout_num + LD C, 'K' + CALL BIOS.conout_f + CALL CCP_RAM.ccp_getkey_no_wait + JP NZ, CCP_RAM.ccp_cmdline_back + POP DE + INC E + JP .do_next_direntry + +.file_e_found: + POP DE + LD HL, msg_free_space ;= "\r\nFREE SPACE " + + ; Out: FREE SPACE + CALL ccp_out_str_z + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_ram_dsk + LD HL, (BIOS.disk_a_size) + JP .calc_remanis_ds + +.no_ram_dsk: + DEC A + LD HL, (BIOS.disk_b_size) + JP Z, .calc_remanis_ds + + LD HL, (BIOS.disk_c_size) + LD A, (bios_var3) + OR A + JP NZ, .calc_remanis_ds + + LD A, H + CP 1 + JP Z, .d720 + LD HL, 360 + JP .calc_remanis_ds +.d720: + LD HL, 720 + + ; Disk size - Dir size = Free +.calc_remanis_ds: + EX DE, HL + LD HL, (CPM_VARS.tmp_dir_total) + LD A, E + SUB L + LD E, A + LD A, D + SBC A, H + LD D, A + CALL ccp_cout_num + LD C, 'K' + CALL BIOS.conout_f + CALL CCP_RAM.ccp_out_crlf + JP CCP_RAM.ccp_cmdline_back + +msg_free_space: + DB "\r\nFREE SPACE ",0 + +ccp_cout_num: + LD A, D + AND 11100000b + JP Z, .less_224 + LD C, '*' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + RET + +.less_224: + LD HL, 0x0 + ; copy number to BC + LD B, D + LD C, E + LD DE, 0x1 + LD A, 13 + +.bc_rra: + PUSH AF + PUSH HL + ; BC >> 1 + LD A, B + RRA + LD B, A + LD A, C + RRA + LD C, A + JP NC, .bc_rra_ex + POP HL + CALL cpp_daa16 + PUSH HL + +.bc_rra_ex: + LD L, E + LD H, D + CALL cpp_daa16 + EX DE, HL + POP HL + POP AF + DEC A + JP NZ, .bc_rra + LD D, 0x4 + LD B, 0x0 + +.next_d: + LD E, 0x4 + +.next_e: + LD A, L + RLA + LD L, A + LD A, H + RLA + LD H, A + LD A, C + RLA + LD C, A + DEC E + JP NZ, .next_e + LD A, C + AND 0xf + ADD A, '0' + CP '0' + JP NZ, .no_zero + DEC B + INC B + JP NZ, .b_no_one + LD A, D + DEC A + JP Z, .d_one + LD A, ' ' + JP .b_no_one + +.d_one: + LD A, '0' +.no_zero: + LD B, 0x1 +.b_no_one: + LD C, A + CALL BIOS.conout_f + DEC D + JP NZ, .next_d + RET + +; ------------------------------------------------------- +; ADD with correction HL=HL+DE +; ------------------------------------------------------- +cpp_daa16: + LD A, L + ADD A, E + DAA + LD L, A + LD A, H + ADC A, D + DAA + LD H, A + RET + +; ------------------------------------------------------- +; Call BDOS function 35 (F_SIZE) - Compute file size +; ------------------------------------------------------- +cpp_bdos_f_size: + LD C, 35 + JP jp_bdos_enter + +; ------------------------------------------------------- +; Read data from Tape +; ------------------------------------------------------- +ccp_read: + CALL BIOS.const_f + OR A + JP Z, .no_key ; chk key pressed + CALL BIOS.conin_f + JP ccp_read +.no_key: + CALL CCP_RAM.ccp_bdos_drv_allreset + CALL CCP_RAM.ccp_out_crlf + + ; wait tape block header + LD A, 100 + CALL BIOS.tape_wait_f + OR A + JP NZ, .key_pressed1 + + ; try to load header 8 times + LD E, 8 +.load8_cont: + LD HL, dma_buffer + CALL BIOS.tape_read_f + CP 0x4 + JP Z, .key_pressed1 + OR A + JP Z, .hdr_id_chk + DEC E + JP NZ, .load8_cont + JP .cp_load_err_what + + ; Check blk ID, will be 0x8000 +.hdr_id_chk: + LD A, B + CP 0x80 + JP NZ, .load8_cont + LD A, C + OR A + JP NZ, .load8_cont + ; set end of file name strZ + XOR A + LD (dma_buffer+12), A + LD DE, dma_buffer + LD HL, CCP_RAM.ccp_inp_line ;= 2020h + LD (CCP_RAM.ccp_inp_line_addr), HL ;= B208h + +.cp_name_char: + LD A, (DE) + OR A + JP Z, .name_copied + LD (HL), A ;= 2020h + INC HL + INC DE + JP .cp_name_char +.name_copied: + LD (HL), 0x0 ; mark end of strZ + CALL CCP_RAM.ccp_cv_first_to_fcb + JP NZ, CCP_RAM.print_syn_err + + ; Output file name to screen + LD HL, CCP_RAM.ccp_current_fcb_fn + LD B, 11 + +.name_output: + LD A, (HL) + AND 01111111b ; 7bit ASCII + LD C, A + CALL BIOS.conout_f + INC HL + DEC B + JP NZ, .name_output + ; Out ' ' + LD C, ' ' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL CCP_RAM.ccp_open_cur_fcb + JP Z, .recreate_f + LD HL,msg_ex ;= "*EX* " + CALL ccp_out_str_z + LD HL, CCP_RAM.ccp_current_fcb_fn+8 + ; '$$$' at the end of buffer + LD A, '$' + LD (HL), A + INC HL + LD (HL), A + INC HL + LD (HL), A + + ; delete file if exists and create new +.recreate_f: + LD DE, CCP_RAM.ccp_current_fcb + PUSH DE + CALL CCP_RAM.ccp_bdos_era_file + POP DE + CALL CCP_RAM.ccp_bdos_make_f + JP Z, CCP_RAM.ccp_no_space ; out NO SPACE if err + XOR A + LD (CCP_RAM.ccp_current_fcb_fn+31), A + + ; Load block with ID=1 + LD DE, 1 +.fin_load: + LD HL, dma_buffer + PUSH DE + +.blk_ld_rep: + CALL BIOS.tape_read_f + CP 4 + JP Z, .key_pressed2 + OR A + JP Z, .blk_id_chk + LD A, (CCP_RAM.ccp_current_fcb_fn+31) + AND 0x7f + JP NZ, .cp_read_cs + +.blk_id_chk: + LD A, C + OR B + JP Z, .blk_ld_rep + + ; Check id for 0xFFFF - last blk + LD A, C + AND B + CP 0xff + JP Z, .blk_id_last + ; Check ID for DE + LD A, C + CP E + JP NZ, .blk_ne_id + LD A, B + CP D + JP NZ, .blk_ne_id + ; Ok, write blk to disk + LD DE, dma_buffer + CALL CCP_RAM.ccp_bdos_dma_set + LD DE, CCP_RAM.ccp_current_fcb + CALL CCP_RAM.ccp_bdos_f_write + POP DE + JP NZ,ccp_del_f_no_space + INC DE + JP .fin_load + +.cp_load_err_what: + LD HL,msg_what ;= "WHAT?\a" + CALL ccp_out_str_z + JP .load_restart + +.cp_read_cs: + POP BC + LD HL, msg_cs ;= "\a", 0 + +; ------------------------------------------------------- +; Out zerro ended string +; In: HL -> strZ +; ------------------------------------------------------- +ccp_out_str_z: + LD A, (HL) + OR A + RET Z + LD C, A + CALL BIOS.conout_f + INC HL + JP ccp_out_str_z + +; ------------------------------------------------------- +; Delete file and out No Space message +; ------------------------------------------------------- +ccp_del_f_no_space: + LD DE, CCP_RAM.ccp_current_fcb + CALL CCP_RAM.ccp_bdos_era_file + JP CCP_RAM.ccp_no_space + +; ------------------------------------------------------- +; Read current file next block +; Out: A=0 - Ok, 0xFF - HW Error; +; ------------------------------------------------------- +cpp_read_f_blk: + LD DE, CPM_VARS.ccp_fcb ; FCB here + JP CCP_RAM.ccp_bdos_read_f + +ccp_write: + CALL CCP_RAM.ccp_cv_first_to_fcb + CALL CCP_RAM.ccp_drive_sel + LD HL, CCP_RAM.ccp_current_fcb_fn + LD A, (HL) + CP ' ' + JP NZ, .find_f + LD B, 11 + +.fill_with_wc: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_with_wc + +.find_f: + CALL CCP_RAM.ccp_find_first + JP NZ, .found_f + CALL CCP_RAM.ccp_out_no_file + JP CCP_RAM.ccp_cmdline_back + +.found_f: + LD E, 0 ; file counter + +.do_next_f1: + PUSH DE + CALL CCP_RAM.ccp_find_first + POP DE + PUSH DE + +.do_next_f2: + DEC E + JP M, .do_file + PUSH DE + CALL CCP_RAM.ccp_bdos_find_next + POP DE + JP Z, .no_more_f + JP .do_next_f2 + +.do_file: + POP BC + PUSH BC + LD A, C + OR A + JP Z, .calc_addr + LD DE, 1200 + + ; Delay with key interrupt check +.delay_1: + XOR A +.delay_2: + DEC A + JP NZ, .delay_2 + PUSH DE + CALL CCP_RAM.ccp_getkey_no_wait + POP DE + JP NZ, CCP_RAM.ccp_cmdline_back + DEC DE + LD A, D + OR E + JP NZ, .delay_1 + +.calc_addr: + LD A, (CCP_RAM.ccp_bdos_result_code) + ; A=0-3 - for Ok, calc address of DIR entry in DMA buffer + RRCA + RRCA + RRCA + AND 01100000b + LD C, A + PUSH BC + CALL CCP_RAM.ccp_out_crlf + CALL CCP_RAM.ccp_bdos_drv_get + INC A + LD (CPM_VARS.ccp_fcb_dr), A ; set drive number + POP BC + LD B, 0x0 + LD HL, dma_buffer+1 + ADD HL, BC + LD DE, CPM_VARS.ccp_fcb_fn + LD B, 0x1 + +.copy_fn: + LD A, (HL) + LD (DE), A + INC HL + INC DE + INC B + LD A, B + CP 12 + JP C, .copy_fn + +.fillz_fn: + XOR A + LD (DE), A + INC DE + INC B + LD A, B + CP 36 + JP C, .fillz_fn + LD HL, CPM_VARS.ccp_fcb_fn + CALL ccp_out_str_z + LD HL, dma_buffer + + ; Empty first 128 bytes of DMA buffer + LD B, 128 +.clear_buf: + LD (HL), 0x0 + INC HL + DEC B + JP NZ, .clear_buf + + ; Copy file name at buffer start + LD HL, dma_buffer + LD DE, CPM_VARS.ccp_fcb_fn + LD B, 8 + +.find_sp: + LD A, (DE) + CP ' ' + JP Z, .sp_rep_dot ; ' ' -> '.' + LD (HL), A + INC HL + INC DE + JP .find_sp +.sp_rep_dot: + LD (HL), '.' + INC HL + CALL CCP_RAM.ccp_find_no_space + +.cont_copy_fn: + LD A, (DE) + LD (HL), A + OR A + JP Z, .end_copy + INC HL + INC DE + JP .cont_copy_fn + +.end_copy: + LD DE, CPM_VARS.ccp_fcb + CALL CCP_RAM.ccp_bdos_open_f + LD DE, 0x8000 ; Block ID + LD HL, dma_buffer + CALL cpp_pause_tape_wr_blk + CALL cpp_pause_tape_wr_blk + LD DE, 0x1 + + ; Read file block and write to Tape +.read_f_write_t: + PUSH DE + CALL cpp_read_f_blk + ; a=0xff if error; a=1 - EOF + DEC A + JP Z, .eof + LD A, (CPM_VARS.ccp_fcb_cr) + AND 0x7f + CP 0x1 + JP NZ, .write_once + ; Write block to Tape with ID=0 twice + LD DE, 0x0 ; Block ID=0 + LD HL, dma_buffer + CALL cpp_pause_tape_wr_blk + +.write_once: + CALL CCP_RAM.ccp_getkey_no_wait + LD HL, dma_buffer + POP DE + JP NZ, CCP_RAM.ccp_cmdline_back + CALL cpp_pause_tape_wr_blk + ; Inc Block ID and continue + INC DE + JP .read_f_write_t + +.eof: + POP DE + EX DE, HL + LD (dma_buffer), HL + EX DE, HL + + ; Final block ID=0xFFFF + LD DE, 0xffff + ; Write twice + CALL cpp_pause_tape_wr_blk + CALL cpp_pause_tape_wr_blk + POP DE + INC E + JP .do_next_f1 + +.no_more_f: + POP DE + CALL CCP_RAM.ccp_out_crlf + JP CCP_RAM.ccp_cmdline_back + +; ------------------------------------------------------- +; Write block to tape after pause +; ------------------------------------------------------- +cpp_pause_tape_wr_blk: + LD BC, 3036 +.delay: + DEC BC + LD A, B + OR C + JP NZ, .delay + JP BIOS.tape_write_f + + DB 0x4c + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xDB00 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| CCP_ROM\t| ",/H,ccp_entry," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT ccp_dir = 0xdb62 + ASSERT ccp_dir.find_file_e = 0xdb97 + ASSERT ccp_dir.file_out_next = 0xdba6 + ASSERT ccp_dir.file_e_found = 0xdc3e + ASSERT ccp_dir.calc_remanis_ds = 0xdc72 + ASSERT ccp_dir.no_ram_dsk = 0xdc52 + ASSERT msg_free_space = 0xdc8a + ASSERT ccp_cout_num = 0xdc9a + +FILLER + DS FILL_SIZE-1, 0xff + DB 0xaa + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_2e4a7b71/cpm.asm b/CPM_v2.2_r8_2e4a7b71/cpm.asm new file mode 100644 index 0000000..7f3d1c9 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/cpm.asm @@ -0,0 +1,44 @@ +; ====================================================== +; Ocean-240.2 +; CP/M Combine file. Includes all sources to build +; ROM 0xC000 +; +; Disassembled by Romych 2025-02-12 +; ====================================================== + + DEFINE BUILD_ROM + + DEVICE NOSLOT64K + +; +; |-----------|---------------|-----------|--------------------------------------| +; | OFFSET | SIZE | Module | Memory Address | +; |-----------|---------------|-----------|--------------------------------------| +; | 0x0000 | 2048 (0x800) | CCP_RAM | 0xC000..0xC7FF -> RAM 0xB200..0xB5FF | +; | 0x0800 | 3584 (0xE00) | BDOS | 0xC800.. | +; | 0x1600 | 1024 (0x400) | BIOS | 0xD600..D9FF | +; | 0x1B00 | 1280 (0x500) | CCP_ROM | 0xDB00..DFFF | +; |-----------|---------------|-----------|--------------------------------------| +; + + DISPLAY "| Module | Offset | Size | Free |" + DISPLAY "|-------------|---------|--------|--------|" + + + OUTPUT cpm-C000.bin + ;LABELSLIST "cpm.labels" + ;CSPECTMAP "cpm.map" + + INCLUDE "ccp_ram.asm" + INCLUDE "bdos.asm" + INCLUDE "bios.asm" + INCLUDE "ccp_rom.asm" + + OUTEND + + OUTPUT variables.bin + INCLUDE "cpm_vars.inc" + OUTEND + + +END \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/cpm_vars.inc b/CPM_v2.2_r8_2e4a7b71/cpm_vars.inc new file mode 100644 index 0000000..ea33312 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/cpm_vars.inc @@ -0,0 +1,219 @@ +; ======================================================= +; Ocean-240.2 +; Module CPM_VARS - BIOS + BDOS + CPP variables +; RAM Range: 0xBA09-0xBF00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + IFNDEF _CPM_VARS + DEFINE _CPM_VARS + + MODULE CPM_VARS + ORG 0xBA09 + +cpm_vars_start EQU $ +; Filled by zeroes by BIOS at cold boot +; until ccp_vars_end + +; Output disabel if non zero +bdos_no_outflag DB 0 +bdos_strtcol DB 0 +bdos_column DB 0 + +; Copy output to printer in non zero +bdos_prnflag DB 0 +bdos_kbchar DB 0 + +; Stored user stack pointer on enter bdos fn +bdos_usersp DW 0 ; 0xba0e + +; BDOS Stack + DS 48, 0 +bdos_stack EQU $ + +; ------------------------------------------------------- +; Common values shared between bdosi and bdos +; ------------------------------------------------------- + +; current user number 0..15 +bdos_userno DB 0x00 ; 0xba40 + +; current disk number +bdos_curdsk DB 0x00 + +; reg DE (pointer) value to call BDOS fn +bdos_info DW 0x0000 ; 0xba42 + +; Address value to return +bdos_aret DW 0x0000 ; 0xba44 +; Empty FCB +bdos_efcb DB 0x00 ; 0xba46 = 0xE5 at init +; Read-only disks flag +bdos_rodsk DW 0x0000 ; 0xba47 +; Drive login vector +bdos_dlog DW 0x0000 ; 0xba49 +; Address of DMA buffer +bdos_dmaad DW 0x0000 ; 0xba4b = 0x80 at init +bdos_cdrmax DW 0x0000 ; 0xba4d +; Current track +bdos_curtrk DW 0x0000 ; 0xba4f +; Current record (sector) +bdos_currec DW 0x0000 ; 0xba51 +; Address of user buffer +bdos_buffa DW 0x0000 ; 0xba53 + +; Address of cur disk DPB +bdos_dpbaddr DW 0x0000 ; 0xba55 +; Calculated checksum +bdos_cksum DW 0x0000 ; 0xba57 +; Address of cur disk allocation map +bdos_alloca DW 0x0000 ; 0xba59 +; Sectors per track +dbos_sectpt DW 0x0000 ; 0xba5b +bdos_blkshf DB 0x00 ; 0xba5d +bdos_blmsk DB 0x00 ; 0xba5e +bdos_extmsk DB 0x00 ; 0xba5f +; Max allocation number (disk size) +bdos_maxall DW 0x0000 ; 0xba60 +bdos_dirmax DW 0x0000 ; 0xba62 +; Size of directory +bdos_dirblk DW 0x0000 ; 0xba64 +bdos_alloc1 DW 0x0000 ; 0xba66 +; First track offset +bdos_offset DW 0x0000 ; 0xba68 +; Disk traslation vector address +bdos_tranv DW 0x0000 ; 0xba6a +; Open and closed flag 0xff - closed +bdos_fcb_closed DB 0x00 ; 0xba6c +; Read file modified flag. 0xff - not allow read unwritten +bdos_rfm DB 0x00 ; 0xba6d +bdos_dirloc DB 0x00 ; 0xba6e +; File access mode (0=random, 1=sequential, 2=special) +bdos_seqio DB 0x00 ; 0xba6f +; Reg E value to call BDOS fn +bdos_linfo DB 0x00 ; 0xba70 +bdos_dminx DB 0x00 ; 0xba71 +; Length in bytes to match when search +bdos_searchl DB 0x00 ; 0xba72 +; Address of buffer where to search +bdos_searcha DW 0x0000 ; 0xba73 + DW 0x0000 +; 0 - big disk (>255 blk), 0xff - small disk +bdos_small_disk DB 0x00 ; 0xba77 +; Drive selected (0xff) by user specified FCB flag +bdos_resel DB 0x00 ; 0xba78 +bdos_olddsk DB 0x00 ; 0xba79 +; Saved disk of user FCB +bdos_fcbdsk DB 0x00 ; 0xba7a +; Number of records in extent +bdos_rcount DB 0x00 ; 0xba7b +bdos_extval DB 0x00 ; 0xba7c +; Record number to read +bdos_vrecord DW 0x0000 ; 0xba7d +; Record to access +bdos_arecord DW 0x0000 ; 0xba7f +bdos_arecord1 DW 0x0000 ; 0xba81 +bdos_dptr DB 0x00 ; 0xba83 +bdos_dcnt DW 0x00 ; 0xba84 +bdos_drec DS 20, 0x00 ; 0xba86 +tmp_dir_total DW 0x0000 ; 0xba9a +; Save pointer at ccp entry, restore on exit +saved_stack_ptr DW 0x0000 ; 0xba9c + DS 22, 0 +ccp_safe_stack + DW 0x0000 ; 0xbab4 + +; -------------------------------------------------- +; FCB +; -------------------------------------------------- +ccp_fcb +ccp_fcb_dr DB 0 ; 0xbab6 drive letter to write file +ccp_fcb_fn DS 8,0 ; 0xbab7 file name +ccp_fcb_ft DS 3,0 ; file type +ccp_fcb_ext DB 0 ; extent +ccp_fcb_s1 DB 0 +ccp_fcb_s2 DB 0 ; extent hi bits +ccp_fcb_rc DB 0 +ccp_fcb_al DS 16, 0 ; Second half of FCB +ccp_fcb_cr DB 0x00 ; Current record +ccp_fcb_rn DB 0x00 ; Random access record number + DW 0x00 + DW 0x00 + DW 0x00 + +ccp_vars_end EQU $ + +; -------------------------------------------------- +; Disk parameters headers/blocks in RAM +; -------------------------------------------------- +DPH_RAM DS 16, 0 ; 0xbade +DPH1_RAM DS 16, 0 ; 0xbaee +DPH2_RAM DS 16, 0 ; 0xbafe +tmp_buff9 DS 9, 0 +DPB_A_RAM DS 15, 0 ; 0xbb17 +DPB_B_RAM DS 15, 0 ; 0xbb26 +DPB_C_RAM DS 15, 0 ; 0xbb35 + +; -------------------------------------------------- +cur_disk DB 0x00 ; 0xbb44 +dma_addr DW 0x0000 ; 0xbb46 +curr_track DB 0x00 ; 0xbb47 +curr_sec DB 0x00 ; 0xbb48 +slicer_disk DB 0x00 ; 0xbb49 +slicer_track DB 0x00 ; 0xbb4a +slicer_real_sector DB 0x00 ; 0xbb4b +tmp_slicer_real_sector DB 0x00 ; 0xbb4c +slicer_has_data DB 0x00 ; 0xbb4d +slicer_need_save DB 0x00 ; 0xbb4e +slicer_uninited_count DB 0x00 ; 0xbb4f +slicer_uninited_disk DB 0x00 ; 0xbb42 +slicer_uninited_track DB 0x00 ; 0xbb43 +slicer_uninited_sector_128 DB 0x00 ; 0xbb44 +tmp_slicer_result DB 0x00 ; 0xbb45 +tmp_slicer_can_read DB 0x00 ; 0xbb46 +tmp_slicer_operation DB 0x00 ; 0xbb47 +tmp_slicer_flush DB 0x00 ; 0xbb48 + +; Directory buffer used by BDOS for directory operations +dir_buffer DS DIR_BUFF_SIZE, 0 ; 0xbb49 + +; Allocation map for drive A (Mark used allocation blocks) +AL_MAP_A DS 31, 0 ; 248 bit +; Check vector for drive A (Checksumm of dir entries, used to detect disk change) +CHK_VEC_A DS 16, 0 ; 0xbbf6 + +; Allocation map for drive B +AL_MAP_B DS 45, 0 ; 184 bit +; Check vector for drive B +CHK_VEC_B DS 32, 0 ; 0xbc33 + +; Allocation map for drive C +AL_MAP_C DS 45, 0 ; 184 bit +; Check vector for drive C +CHK_VEC_C DS 32, 0 ; 0xbc80 + DS 96, 0 + +slicer_buffer DS 512, 0 ; 0xbd00 + +cpm_vars_end EQU $ + +ccp_vars_size EQU ccp_vars_end-cpm_vars_start +cpm_vars_size EQU cpm_vars_end-cpm_vars_start + + ; check integrity of original ROM + ASSERT tmp_dir_total = 0xba9a + ASSERT saved_stack_ptr = 0xba9c + ASSERT ccp_fcb_fn = 0xbab7 + ASSERT DPH_RAM = 0xbade + ASSERT cur_disk = 0xbb44 + ASSERT slicer_uninited_count = 0xbb4f + ASSERT slicer_buffer = 0xbd00 + + DISPLAY "| CCP_VARS\t| ",/H,cpm_vars_start," | ",/H,cpm_vars_size," |\t |" + + DISPLAY "cur_disk:",/H,cur_disk + + ENDMODULE + + ENDIF diff --git a/CPM_v2.2_r8_2e4a7b71/equates.inc b/CPM_v2.2_r8_2e4a7b71/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/io.inc b/CPM_v2.2_r8_2e4a7b71/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/mon_entries.inc b/CPM_v2.2_r8_2e4a7b71/mon_entries.inc new file mode 100644 index 0000000..b607096 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/mon_entries.inc @@ -0,0 +1,35 @@ +; ====================================================== +; Ocean-240.2 +; +; Monitor entries to compile CP/M modules +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _MON + DEFINE _MON + + MODULE MON + +mon_init EQU 0xe000 +mon_cold_start EQU 0xe003 +non_con_status EQU 0xe006 +mon_con_in EQU 0xe009 +mon_con_out EQU 0xe00c +mon_serial_in EQU 0xe00f +mon_serial_out EQU 0xe012 +mon_char_print EQU 0xe015 +mon_tape_read EQU 0xe018 +mon_tape_write EQU 0xe01b +ram_disk_read EQU 0xe01e +ram_disk_write EQU 0xe021 +mon_res_f1 EQU 0xe024 +mon_res_f2 EQU 0xe027 +mon_tape_wait EQU 0xe02a +mon_tape_detect EQU 0xe02d +read_floppy EQU 0xe030 +write_floppy EQU 0xe033 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/ram.inc b/CPM_v2.2_r8_2e4a7b71/ram.inc new file mode 100644 index 0000000..8c6c08e --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +@RST1 EQU 0x0008 +@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/.gitignore b/CPM_v2.2_r8_bc0695e4/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/CPM_v2.2_r8_bc0695e4/.vscode/extensions.json b/CPM_v2.2_r8_bc0695e4/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/CPM_v2.2_r8_bc0695e4/.vscode/tasks.json b/CPM_v2.2_r8_bc0695e4/.vscode/tasks.json new file mode 100644 index 0000000..f71ba83 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make CPM (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=cpm.sld", + "--sym=cpm.labels", + "--lstlab=sort", + "--lst=cpm.lst", + "--raw=cpm.obj", + "--fullpath", + "cpm.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + } + + ] +} \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/BIN/CPM_REL_8_HEXREAD.BIN b/CPM_v2.2_r8_bc0695e4/BIN/CPM_REL_8_HEXREAD.BIN new file mode 100644 index 0000000000000000000000000000000000000000..88deb5dd256e36b24f5d860dbde1417f89734aeb GIT binary patch literal 8192 zcmdT}dvp`mnIBGOj223 z3zG+V?4I3pTWFeQbMk1DO-WaY{E#(DTx$kV%~;o#Y*~Ikl&!o=)s~Q!Hfgrw{oN6e zKK8Gk{bN`5+_{hMe)oIdd%qFC-YMSId4e(g#czvk&;GjYwcGX@R&H5kSa<8qw|?1B zy}fq(-pZYZx|;f$%DOGv3C?;bn zm*pn!x{1&(zuiuz+Qlb3QIf)THe0WCgFCXSoy&A@H8y)>zTHXIw%aqrv+Z_FD|yZ> zu?qp>39=gclGp-}u5V-5LcF=NUdXJBagh{#7u_INM15r^$1qxLVGs0|Z_E(>*e*f9 zrWf2!#UPh-c<`u#Hi~&2)=dyOMpKj(dV-2!T-Om)DuPNwP+1dHEJ5X~L8UyXSc8%} z+?y}jI-Y3^mr7QKBVMw+dBU$o-I~gzZ$j^67Z?4TRR4np~9Yvtb zE}rRV2HfF%Z5CPEA$vQFib;5S1nCC(Ae#ZCKy$)utEr2)0ftzVgmO#USkM zbVz8}I&;MJoi1~xd)--Yz)_6fTwViN(oVYF=CfYEV=m8p(F=`OGTk2GrA|Om>4w|S zS3=NME0y-G?NA{ZZAUMKJk)p`P1baB{?3M_OhJLu_(0tln_=troiz+_st9!*T)!7$ zpo(ALWsI#=vIlh?` zCBe#1Lf1~oWhP#VJXite=?TK%{-l+%KrB!8c429X?4P={3(Gb^bkB7yXNJiIcjyMt(db5%<$a)l=Fh=K!V_lcvghG*r{M;?R)D;m= zQPC?4g({Dx&O_d&dVwQu6rSva)$FG)7!mkDY#|rj+eDDJrMn#X0|0O3*DDTOi?g$00)Q(p0LPX%p{b&?0V zL}2=~^YS&ESacYxMu@Im{@g?3-lfJHnN@m5)6ywl@~ln(WkCW)M5#+}kHm$~Jz~^D zTjTC>LDQ|t?4~>ow}WVyMg5-2Evj9BWa#TMCHsO~aCF0k^;2H$A_TB29;{Tzo4S<- z;b?bKl>pd0+I^{(`xxOy-rwDQ7|p}f3V=XU-xodm< z-g*WMDvMx$4VHc{3=$kKX&0aOlFRLChj6Z&T9W)cA}riuRXe;0!}^$)`k$_27&J$? zr@UmdD)EQC9HU7juZRG_SmTYuuL|^xyY)LQFApB^it+Avg>b|Rk|0;mpKyX|>mqVb zN8D+i@~XSZ*J%*bax{Ktxxt{7l4kW9#kF27qd&QHmco8r3)p1IRjShf z-`10gYob2*{os#+XM^ttlOqHK1u($j@a8 zXC#TdOFMF6Y?g?^h#|_)LY~X!d%`NH=#|)9^t0LGHYr;v4W2*<7jbF%Al}1UOX%-CxjbQea?*4;ybdEA>1ruu7ud0 z&5IDxB4L}H;BvtF8GBs3C@EhO8sxYon+LZ&gK=pi`Jp84mX#&K!!mhaii@y@#ll;1 z01baU5x{~k$j03nKzc%o`^9ECK}9HEl7Qe0sK9(q$8QwAFJn2QJw1@HsgO=3ty%a` zRx}X4cl@FhKa8UwB%+l>O&?hjL+L1&XXSEP8-uu@HIKY^80x}+C5=JJr7=gO449aCW|I-Xq`BSH@W9Jrem+%w@9icB9>NJ1r%$A|^Wfh$-d? za=M_H+h=;3XBUv4gi22EC8v1q1eCQEu++P3Jl*CU-)Vk%@WZ}@>1+J)(+$U7*%x0e zzO6P3#s~Wxr<-5FD;YP6ANIuq!-=HnLB61R7g-*Q0KrjXQv}%X@;km`Yks-b^i4iV z*2W+LF2>!>@3yq^o?`6np;~L!8Jner&y|Zqo@L_FP@4qH(Jx{ObQU|bvmx}@jf|wD za*~K9WYaQ)qeM%l6MSojWVxG1WmSbxA1XP?TMqCxV1B)`QH21W9Hv0Um&X(&IXxQW zNXTh!<&#eHah^OBvNZ5-nOk_;ZE56{1;Xng7e#T>p0ph2?GjYe;we7VTHGl9TZnBl z)$^`XIEVCx=*TKD7z$98aTh#%TIVzpS4%}p`a>nPyy-sP zm^AI8a*+r}^g5=%xh%}(QH79rD9Giis;TEKPx0z$mWqM}kVL*r;JYY)*JhWXJBsD_ z8RM~6B~nF3^P5&5m5!foJod^ViJdo{!tet;xr>AYgb$hap?Qig@aM?SlAa*o!{lN} zf=x6K@+QG4{&=7GFSJUY=;L4_L*v zW_rX_$OU<`n$h88iWc|BjehYK+Gf*W-^S`)K#n`m!shpd$NNq9gXsNK z0p$KZ$gqqa&WWF}^CU<}Hwune6Fu_je#>UQNAUI=ov~89D&Vw`)S7-`BO(SLWoI<&5;LO|!;p2XB(?DQ1k^B9q=Nr>=#iawP zq(Kz-7!MP2K~R2Tod;258DVqCM8C>RCcy$_36KEFpC<&U4h`(t4~czuVBv58qAnhE z4frFF*74KFUK#WZ&Ih$!Y9b-Alu0p}lA|HjQfD{4{TVhKFDE z!f)oI~Vr|ABDxhN5;OWz7#NB{<&k8Q6e^`{ zFrzOui)Dm$gNWDWBfN%TJhBq8EkxkP261wrwL@4qXk7q`5T&USpbs_JTaNJZib1l1 z92IXGgiq*n@KbgHB?p0I7md&r6@qinA8@Ge1j5rkyYaeDY=ekirjyvwfH44C{fKGU z7gz>6LhX;bjl$i7aSRcJISd9c>c7hR=|TGjc0q2g@W$X}Y9}Y`mv9yl2kCa8!JC{8 zK`ce;s#J?sD9G1(qLlou+9Y0L23)4QKYRDg0lb(YysB}%n z6*xcDW`b@By>WjlEFEe^06RYzdO8C4q?Pj#DOrFZNl`lU=Gso$tju$@sO7cQ(iNOE z{9bOWy&P|icVd@P-4g8R!A+vch(BsT14Hl=xEFbFD4u9=oG~`PYCQhxxHNQh_2Jb= zs6)R!6j%3G1BIcYIAT1EPc3ym--+}{wCEpXY zi$5QNJvP%5qjRpf0v7>g`QR;4?4f1{@5D7=Zq`n+q6o-<=(0=J60lG23bi~ixy+S3 z?wut&sCI#hY$W8msCZqNmIuQulSH~jekO{|9+h+IsOmA|y9DtV4hcZY%DZ$PX&4aC z_0y>*BDC~JuHb`5VRVd+f(`MPlsu4;#Jbo#v8A938$U`4r>Jmu@VL%M;8U3w6iPoP z%oUqAh9Udqg}rBJv~RPe;UaBO)0QCr88+BceQlyBGcc(yUlCE1G6e?`6UZ!VC87 zG2uS^hnfQ9A6|%=HZ%G=??}~Q5!$x01Ts9|jG?Ya?lCJsxLH3EM~;{ac~x>IwvN$o z508-pW1xq_5sq0C z=B>Q7&Afx>rg{CRW13r;Lc1}a!u~R*!7aTsW);m0!==SaG>jR^QY~Ks7hUFK^YpGU z%U0YQN=EcOW1xY%k#P+@3>_|Pt@Lx=(XGyQx?c~jnL68j7K;~2Sv5K+McAq zF7qy*+2*sPGd7WNQ%Z7$Z;ePU39jQ%(!%CypGW?|$niOi@Pm;Q>_f=r;HRJF3nU92 z?jEu1_IZSEI?gg&t?!7$ZA0q0(#57b3 zk|xX}*`pF!K8j7+=EG)khtIs%2cx~yC%!Q1LeqgNj^b6L)78^)=a1|SV&#NhUQ*0 z>nTQ0v8UL))5q=dImX`dJXE)=!bOYMoP8~-0#+sSNL@uJXtlt)lYnpSg)s_0@VEZzPjyuYq-J*n)**uQ~wz_eBL%e zQ~x(l(A1-CLQVbOHjyK)nYeG_`l%15zcyidh5t-M!v0TF-Nz=bq`Hqz(B$f=i7P2l zWQMVYzRaic-;v=P{_xhsFw)Qw**YnHZ{k`ioPGyG-n$%WMn8hpN#^doI<3aJ*ajmr z=F^1scwoY{7?slT=mhnB zlNF}gnA|jJ+8$E_VAEs^VrJtc_n-!YTzCORo62EaL--QS1LtOMt*fa?rJ<4KWRA_v z%C1S)L|44}Porhk4!wr4=3Y)T7uPz9ZG+NeoUl`ghwnp~j9;n{ypv1QwaoJL)@6+? z%MLk~*=`UfCh^tf{90wLkT#W(E@Vs{tB^HQ__CNWWnHV)Q0XlZ#wMlKr&ct!ys!fI zb|d|acH1w! z#6O)vE}dP9FMZ|m6H~};mn?Lf7P^ycA-d?$68aGkYoe28x-o3Yc2@wMAgEIpVO*KV zT$TZY4;t=sJbv(~VP&b|@V$o)9$l54TY9jmsj-PeB>GHI$&xlEsh^)u&4yckG#x>dfJbR2 T7t-U-%#2tb+xb6-=^y_$GNnIy literal 0 HcmV?d00001 diff --git a/CPM_v2.2_r8_bc0695e4/README.md b/CPM_v2.2_r8_bc0695e4/README.md new file mode 100644 index 0000000..ba194e1 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/README.md @@ -0,0 +1,38 @@ +# Ocean-240.2 ROM Sources CP/M (V2.2) REL.8 checksum bc0695e4 + +Source codes of CP/M (V2.2) REL.8 version for Ocean-240.2 with Floppy controller. +In Z80 mnemonics, but limited for i8080 instruction set. + +* READ command replaced with Intel HEX loader by **tnt23** +* 192k RAM drive + +## Compile + + sjasmplus --sld=cpm.sld --sym=cpm.labels --raw=cpm.obj --fullpath cpm.asm + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). + +## Convert binary file to Intel HEX + + srec_cat file_name.COM -binary -offset 0x100 -output file_name.hex -Intel + +## Send HEX from Linux console + +It is assumed that there is a USB-RS232 (USB-TTL) adapter on the /dev/ttyUSB0. + +1) At Ocean's CP/M command line: + + A>READ + +2) At Linux terminal, configure tty for 4800,8N2: + + stty -F /dev/ttyUSB0 4800 cs8 -cstopb + +3) At Linux terminal, send file: + + cat okeah.hex > /dev/ttyUSB0 + + +srec_cat - Utility from **srecord** package - collection of tools for manipulating EPROM load files. + +[Forum topic](https://zx-pk.ru/threads/35390-zagruzka-hex-fajlov-direktivoj-l-monitora.html) diff --git a/CPM_v2.2_r8_bc0695e4/bdos.asm b/CPM_v2.2_r8_bc0695e4/bdos.asm new file mode 100644 index 0000000..3cae2b1 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/bdos.asm @@ -0,0 +1,3041 @@ +; ======================================================= +; Ocean-240.2 +; CP/M BDOS at 0xC800:D5FF +; +; Disassembled by Romych 2025-09-09 +; ====================================================== + + INCLUDE "io.inc" + INCLUDE "equates.inc" + INCLUDE "ram.inc" + + IFNDEF BUILD_ROM + OUTPUT bdos.bin + ENDIF + + MODULE BDOS + + ORG 0xC800 + +bdos_start: + LD SP, HL + LD D, 0x00 + NOP + NOP + LD L, E + +; -------------------------------------------------- +; BDOS Entry point +; -------------------------------------------------- +bdos_enter: + JP bdos_entrance + +bdos_pere_addr: + DW bdos_persub ; permanent error +bdos_sele_addr: + DW bdos_selsub ; select error +bdos_rode_addr: + DW bdos_rodsub ; write to ro disk error +bdos_rofe_addr: + DW bdos_rofsub ; write to ro file error + +; ------------------------------------------------------- +; BDOS Handler +; Inp: C - func no +; DE or E - parameter +; Out: A or HL - result +; ------------------------------------------------------- +bdos_entrance: + ; store parameter DE + EX DE, HL + LD (CPM_VARS.bdos_info), HL + EX DE, HL + ; Store E + LD A, E + LD (CPM_VARS.bdos_linfo), A + ; value to return, default = 0 + LD HL, 0x00 + LD (CPM_VARS.bdos_aret), HL + + ; Save user's stack pointer, set to local stack + ADD HL, SP + LD (CPM_VARS.bdos_usersp), HL + + LD SP, CPM_VARS.bdos_stack ; local stack setup + XOR A + LD (CPM_VARS.bdos_fcbdsk), A ; fcbdsk,resel=false + LD (CPM_VARS.bdos_resel), A ; bdos_resel = FALSE + LD HL, bdos_goback + PUSH HL ; push goback address to return after jump + LD A, C + CP BDOS_NFUNCS + RET NC ; return in func no out of range + LD C, E ; store param E to C + ; calculate offset in functab + LD HL, functab ; DE=func_no, HL->functab + LD E, A + LD D, 0 + ADD HL, DE + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) ; DE=functab(func) + LD HL, (CPM_VARS.bdos_info) ; restore input parameter + EX DE, HL + JP (HL) ; dispatch function + +; ------------------------------------------------------- +; BDOS function handlers address table +; ------------------------------------------------------- +functab: + DW BIOS.wboot_f ; fn0 + DW bdos_con_inp ; fn1 + DW bdos_outcon ; fn2 + DW bdos_aux_inp ; fn3 + DW BIOS.punch_f ; fn4 + DW BIOS.list_f ; fn5 + DW bdos_dir_con_io ; fn6 + DW bdos_get_io_byte ; fn7 + DW bdos_set_io_byte ; fn8 + DW bdos_print_str ; fn9 + DW bdos_read ; fn10 0x0a + DW bdos_get_con_st ; fn11 0x0b + DW bdos_get_version ; fn12 0x0c + DW bdos_reset_disks ; fn13 0x0d + DW bdos_select_disk ; fn14 0x0e + DW bdos_open_file ; fn15 0x0f + DW bdos_close_file ; fn16 0x10 + DW bdos_search_first ; fn17 0x11 + DW bdos_search_next ; fn18 0x12 + DW bdos_rm_file ; fn19 0x13 + DW bdos_read_file ; fn20 0x14 + DW bdos_write_file ; fn21 0x15 + DW bdos_make_file ; fn22 0x16 + DW bdos_ren_file ; fn23 0x17 + DW bdos_get_login_vec ; fn24 0x18 + DW bdos_get_cur_drive ; fn25 0x19 + DW bdos_set_dma_addr ; fn26 0x1a + DW bdos_get_alloc_addr ; fn27 0x1b + DW bdos_set_ro ; fn28 0x1c + DW bdos_get_wr_protect ; fn29 0x1d + DW bdos_set_attr ; fn30 0x1e + DW bdos_get_dpb ; fn31 0x1f + DW bdos_set_user ; fn32 0x20 + DW bdos_rand_read ; fn33 0x21 + DW bdos_rand_write ; fn34 0x22 + DW bdos_compute_fs ; fn35 0x23 + DW bdos_set_random ; fn36 0x24 + DW bdos_reset_drives ; fn37 0x25 + DW bdos_not_impl ; fn38 0x26 (Access Drive) + DW bdos_not_impl ; fn39 0x27 (Free Drive) + DW bdos_rand_write_z ; fn40 0x28 + +; ------------------------------------------------------- +; Report permanent error +; ------------------------------------------------------- +bdos_persub: + LD HL, permsg ; = 'B' + CALL bdos_print_err + CP CTRL_C + JP Z, warm_boot + RET + +; ------------------------------------------------------- +; Report select error +; ------------------------------------------------------- +bdos_selsub: + LD HL, selmsg ;= 'S' + JP bdos_wait_err + +; ------------------------------------------------------- +; Report write to read/only disk +; ------------------------------------------------------- +bdos_rodsub: + LD HL, rodmsg ;= 'R' + JP bdos_wait_err + +; ------------------------------------------------------- +; Report read/only file +; ------------------------------------------------------- +bdos_rofsub: + LD HL, rofmsg ;= 'F' + +; ------------------------------------------------------- +; Wait for response before boot +; ------------------------------------------------------- +bdos_wait_err: + CALL bdos_print_err + JP warm_boot + +; ------------------------------------------------------- +; Error messages +; ------------------------------------------------------- +dskmsg: + DB 'Bdos Err On ' +dskerr: + DB " : $" +permsg: + DB "Bad Sector$" +selmsg: + DB "Select$" +rofmsg: + DB "File " +rodmsg: + DB "R/O$" + +; ------------------------------------------------------- +; Print error to console, message address in HL +; ------------------------------------------------------- +bdos_print_err: + PUSH HL ; save second message pointer + CALL bdos_crlf + ; set drive letter to message + LD A, (CPM_VARS.bdos_curdsk) + ADD A, 'A' + LD (dskerr), A + + LD BC, dskmsg ; print first error message + CALL bdos_print + POP BC + CALL bdos_print ; print second message + +; ------------------------------------------------------- +; Console handlers +; Read console character to A +; ------------------------------------------------------- +bdos_conin: + LD HL, CPM_VARS.bdos_kbchar + LD A, (HL) + LD (HL), 0x00 + OR A + RET NZ + ;no previous keyboard character ready + JP BIOS.conin_f + +; ------------------------------------------------------- +; Read character from console with echo +; ------------------------------------------------------- +bdos_conech: + CALL bdos_conin + CALL bdos_chk_ctrl_char + RET C + PUSH AF + LD C, A + CALL bdos_outcon + POP AF + RET + +; ------------------------------------------------------- +; Check for control char +; Onp: A - character +; Out: ZF if cr, lf, tab, or backspace +; CF if code < 0x20 +; ------------------------------------------------------- +bdos_chk_ctrl_char: + CP ASCII_CR + RET Z + CP ASCII_LF + RET Z + CP ASCII_TAB + RET Z + CP ASCII_BS + RET Z + CP ' ' + RET + +; ------------------------------------------------------- +; check console during output for +; Ctrl-S and Ctrl-C +; ------------------------------------------------------- +bdos_conbrk: + LD A, (CPM_VARS.bdos_kbchar) + OR A + JP NZ, .if_key ; skip if key pressed + CALL BIOS.const_f + AND 0x1 + RET Z ; return if no char ready + CALL BIOS.conin_f + CP CTRL_S + JP NZ, .no_crtl_s + ; stop on crtl-s + CALL BIOS.conin_f + CP CTRL_C + ; reboot on crtl-c + JP Z, warm_boot + XOR A + RET +.no_crtl_s: + LD (CPM_VARS.bdos_kbchar), A ; character in A, save it +.if_key: + LD A, 0x1 ; return with true set in accumulator + RET + +; -------------------------------------------------- +; Out char C to screen (and printer) +; -------------------------------------------------- +bdos_conout: + LD A, (CPM_VARS.bdos_no_outflag) ; if set, skip output, only move cursor + OR A + JP NZ, .compout + ; check console break + PUSH BC + CALL bdos_conbrk + POP BC + ; output c + PUSH BC + CALL BIOS.conout_f + POP BC + + ; send to printer (list) device + PUSH BC + LD A, (CPM_VARS.bdos_prnflag) + OR A + CALL NZ, BIOS.list_f + POP BC + +; ------------------------------------------------------- +; Update cursor position +; ------------------------------------------------------- +.compout: + LD A, C + ; recall the character + ; and compute column position + LD HL, CPM_VARS.bdos_column + CP ASCII_DEL ; [DEL] key + RET Z ; 0x7F dont move cursor + INC (HL) ; inc column + CP ASCII_SP ; return for normal character > 0x20 + RET NC + + ; restore column position + DEC (HL) + LD A, (HL) + OR A + RET Z ; return if column=0 + + LD A, C + CP ASCII_BS + JP NZ, .not_backsp + ; backspace character + DEC (HL) ; BKSP -> column-1 + RET + +.not_backsp: + CP ASCII_LF + RET NZ + LD (HL), 0 ; LF -> column=0 + RET + +; ------------------------------------------------------- +; Send C character with possible preceding '^' char +; ------------------------------------------------------- +bdos_ctlout: + LD A, C + CALL bdos_chk_ctrl_char ; cy if not graphic (or special case) + JP NC, bdos_outcon ; normal output if non control char (tab, cr, lf) + + PUSH AF + LD C, CTRL ; '^' + CALL bdos_conout + POP AF + OR 0x40 ; convert to letter equivalent (^M, ^J and so on) + LD C, A ; + +bdos_outcon: + LD A, C + CP ASCII_TAB + JP NZ, bdos_conout + + ; Print spaces until tab stop position +.to_tabpos: + LD C, ' ' + CALL bdos_conout + LD A, (CPM_VARS.bdos_column) + AND 00000111b ; column mod 8 = 0 ? + JP NZ, .to_tabpos + RET + +; ------------------------------------------------------- +; Output backspace - erase previous character +; ------------------------------------------------------- +bdos_out_bksp: + CALL bdos_prn_bksp + LD C, ' ' + CALL BIOS.conout_f + +; ------------------------------------------------------- +; Send backspace to console +; ------------------------------------------------------- +bdos_prn_bksp: + LD C, ASCII_BS + JP BIOS.conout_f + +; ------------------------------------------------------- +; print #, cr, lf for ctlx, ctlu, ctlr functions +; then move to strtcol (starting column) +; ------------------------------------------------------- +bdos_newline: + LD C, '#' + CALL bdos_conout + CALL bdos_crlf + ; move the cursor to the starting position +.print_sp: + LD A, (CPM_VARS.bdos_column) ; a=column + LD HL, CPM_VARS.bdos_strtcol + CP (HL) + RET NC ; ret id startcol>column + LD C, ' ' ; print space + CALL bdos_conout + JP .print_sp + +; ------------------------------------------------------- +; Out carriage return line feed sequence +; ------------------------------------------------------- +bdos_crlf: + LD C, ASCII_CR + CALL bdos_conout + LD C, ASCII_LF + JP bdos_conout + +; ------------------------------------------------------- +; Print $-ended string +; Inp: BC -> str$ +; ------------------------------------------------------- +bdos_print: + LD A, (BC) + CP '$' + RET Z + INC BC + PUSH BC + LD C, A + CALL bdos_outcon + POP BC + JP bdos_print + +; ------------------------------------------------------- +; Buffered console input +; Reads characters from the keyboard into a memory buffer +; until RETURN is pressed. +; Inp: C=0Ah +; DE=address or zero +; ------------------------------------------------------- +bdos_read: + LD A, (CPM_VARS.bdos_column) + LD (CPM_VARS.bdos_strtcol), A + LD HL, (CPM_VARS.bdos_info) + LD C, (HL) + INC HL + PUSH HL + LD B, 0 + ; B = current buffer length, + ; C = maximum buffer length, + ; HL= next to fill - 1 +.readnx: + PUSH BC + PUSH HL +.readn1: + CALL bdos_conin + AND 0x7f ; strip 7th bit + POP HL + POP BC + ; end of line? + CP ASCII_CR + JP Z, .read_end + CP ASCII_LF + JP Z, .read_end + + ; backspace ? + CP ASCII_BS + JP NZ, .no_bksp + LD A, B + OR A + JP Z, .readnx ; ignore BKSP for column=0 + DEC B + LD A, (CPM_VARS.bdos_column) + LD (CPM_VARS.bdos_no_outflag), A + JP .linelen + + ; not a backspace +.no_bksp: + CP ASCII_DEL ; [DEL] Key + JP NZ, .no_del + LD A, B + OR A + JP Z, .readnx ; ignore DEL for column=0 + LD A, (HL) ; cur char + DEC B ; dec column + DEC HL ; point to previous char + JP .echo ; out previous char + +.no_del: + CP CTRL_E ; ^E - physical end of line + JP NZ, .no_ctrle + PUSH BC + PUSH HL + CALL bdos_crlf ; out CR+LF + XOR A + LD (CPM_VARS.bdos_strtcol), A ; reset start column to 0 + JP .readn1 + +.no_ctrle: + CP CTRL_P ; ^P + JP NZ, .no_ctrlp + PUSH HL + LD HL, CPM_VARS.bdos_prnflag + LD A, 0x1 + SUB (HL) ; flip printer output flag + LD (HL), A + POP HL + JP .readnx + +.no_ctrlp: + CP CTRL_X ; cancel current cmd line + JP NZ, .no_ctrlx + POP HL +.backx: + LD A, (CPM_VARS.bdos_strtcol) + LD HL, CPM_VARS.bdos_column + CP (HL) + JP NC, bdos_read + DEC (HL) + CALL bdos_out_bksp + JP .backx + +.no_ctrlx: + CP CTRL_U ; cancel current cmd line + JP NZ, .no_ctrlu + + CALL bdos_newline + POP HL + JP bdos_read + +.no_ctrlu: + CP CTRL_R ; repeats current cmd line + JP NZ, .stor_echo + + ; start new line and retype +.linelen: + PUSH BC + CALL bdos_newline + POP BC + POP HL + PUSH HL + PUSH BC +.next_char: + LD A, B + OR A + JP Z, .endof_cmd ; end of cmd line? + INC HL + LD C, (HL) + DEC B + PUSH BC + PUSH HL + CALL bdos_ctlout + POP HL + POP BC + JP .next_char +.endof_cmd: + PUSH HL + LD A, (CPM_VARS.bdos_no_outflag) + OR A + JP Z, .readn1 ; if no display, read next characters + ; update cursor pos + LD HL, CPM_VARS.bdos_column + SUB (HL) + LD (CPM_VARS.bdos_no_outflag), A + ; move back compcol-column spaces + +.backsp: + CALL bdos_out_bksp + LD HL, CPM_VARS.bdos_no_outflag + DEC (HL) + JP NZ, .backsp + JP .readn1 + +; Put and Echo normal character +.stor_echo: + INC HL + LD (HL), A + INC B +.echo: + PUSH BC + PUSH HL + LD C, A + CALL bdos_ctlout + POP HL + POP BC + + ; warm boot on ctrl+c only at start of line + LD A, (HL) + CP CTRL_C + LD A, B + JP NZ, .no_ctrlc + CP 1 + JP Z, warm_boot + +.no_ctrlc: + CP C ; buffer is full? + JP C, .readnx ; get next char in not full + + ; End of read operation, store length +.read_end: + POP HL + LD (HL), B + LD C, ASCII_CR + JP bdos_conout ; Our CR and return + +; ------------------------------------------------------- +; Console input with echo (C_READ) +; Out: A=L=character +; ------------------------------------------------------- +bdos_con_inp: + CALL bdos_conech + JP bdos_ret_a + +; ------------------------------------------------------- +; Console input chartacter +; Out: A=L=ASCII character +; ------------------------------------------------------- +bdos_aux_inp: + CALL BIOS.reader_f + JP bdos_ret_a + +; ------------------------------------------------------- +; Direct console I/O +; Inp: E=code. +; E=code. Returned values (in A) vary. +; 0xff Return a character without echoing if one is +; waiting; zero if none +; 0xfe Return console input status. Zero if no +; character is waiting +; Out: A +; ------------------------------------------------------- +bdos_dir_con_io: + LD A, C + INC A + JP Z, .dirinp + INC A + JP Z, BIOS.const_f + JP BIOS.conout_f +.dirinp: + CALL BIOS.const_f + OR A + JP Z, bdos_ret_mon + CALL BIOS.conin_f + JP bdos_ret_a + +; ------------------------------------------------------- +; Get I/O byte +; Out: A = I/O byte. +; ------------------------------------------------------- +bdos_get_io_byte: + LD A, (iobyte) + JP bdos_ret_a + +; ------------------------------------------------------- +; Set I/O byte +; Inp: C=I/O byte. +; TODO: Check, will be E ? +; ------------------------------------------------------- +bdos_set_io_byte: + LD HL, iobyte + LD (HL), C + RET + +; ------------------------------------------------------- +; Out $-ended string +; Inp: DE -> string$. +; ------------------------------------------------------- +bdos_print_str: + EX DE, HL + LD C, L + LD B, H + JP bdos_print + +; ------------------------------------------------------- +; Get console status +; Inp: A=L=status +; ------------------------------------------------------- +bdos_get_con_st: + CALL bdos_conbrk +bdos_ret_a: + LD (CPM_VARS.bdos_aret), A +bdos_not_impl: + RET + +; ------------------------------------------------------- +; Set IO Error status = 1 +; ------------------------------------------------------- +set_ioerr_1: + LD A, 1 + JP bdos_ret_a + +; ------------------------------------------------------- +; Report select error +; ------------------------------------------------------- +bdos_sel_error: + LD HL, bdos_sele_addr + +; indirect Jump to [HL] +bdos_jump_hl: + LD E, (HL) + INC HL + LD D, (HL) ; ->bdos_sele_addr+1 + EX DE, HL + JP (HL) ; ->bdos_selsub + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +bdos_move_dehl: + INC C +.move_next: + DEC C + RET Z + LD A, (DE) + LD (HL), A + INC DE + INC HL + JP .move_next + +; ------------------------------------------------------- +; Select the disk drive given by curdsk, and fill +; the base addresses curtrka - alloca, then fill +; the values of the disk parameter block +; Out: ZF - for big disk +; ------------------------------------------------------- +bdos_sel_dsk: + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + CALL BIOS.seldsk_f ; HL filled by call + ; HL = 0000 if error, otherwise -> DPH + LD A, H + OR L + RET Z ; return if not valid drive + + LD E, (HL) + INC HL + LD D, (HL) + ; DE -> disk header + + ; save pointers + INC HL + LD (CPM_VARS.bdos_cdrmax), HL + INC HL + INC HL + LD (CPM_VARS.bdos_curtrk), HL + INC HL + INC HL + LD (CPM_VARS.bdos_currec), HL + INC HL + INC HL + ; save translation vector (table) addr + EX DE, HL + LD (CPM_VARS.bdos_tranv), HL ; tran vector + LD HL, CPM_VARS.bdos_buffa ; DE=source for move, HL=dest + LD C, 8 + CALL bdos_move_dehl + + ; Fill the disk parameter block + LD HL, (CPM_VARS.bdos_dpbaddr) + EX DE, HL + LD HL, CPM_VARS.bdos_sectpt + LD C, 15 + CALL bdos_move_dehl + + ; Set single/double map mode + LD HL, (CPM_VARS.bdos_maxall) ; largest allocation number + LD A, H ; a=h>0 for large disks (>255 blocks) + LD HL, CPM_VARS.bdos_small_disk + LD (HL), TRUE ; small + OR A + JP Z, .is_small + LD (HL), FALSE ; big disk > 255 sec +.is_small: + LD A, 0xff + OR A + RET + +; ------------------------------------------------------- +; Move to home position, +; then reset pointers for track and sector +; ------------------------------------------------------- +bdos_home: + CALL BIOS.home_f ; home head + XOR A + ; set track pointer to 0x0000 + LD HL, (CPM_VARS.bdos_curtrk) + LD (HL), A + INC HL + LD (HL), A + + ; set current record pointer to 0x0000 + LD HL, (CPM_VARS.bdos_currec) + LD (HL), A + INC HL + LD (HL), A + + RET + +; ------------------------------------------------------- +; Read buffer and check condition +; ------------------------------------------------------- +bdos_rdbuff: + CALL BIOS.read_f + JP diocomp ; check for i/o errors + +; ------------------------------------------------------- +; Write buffer and check condition +; Inp: C - write type +; 0 - normal write operation +; 1 - directory write operation +; 2 - start of new block +; ------------------------------------------------------- +bdos_wrbuff: + CALL BIOS.write_f + +; ------------------------------------------------------- +; Check for disk errors +; ------------------------------------------------------- +diocomp: + OR A + RET Z + LD HL, bdos_pere_addr ; = C899h + JP bdos_jump_hl + +; ------------------------------------------------------- +; Seek the record containing the current dir entry +; ------------------------------------------------------- +bdos_seekdir: + LD HL, (CPM_VARS.bdos_dcnt) + LD C, DSK_SHF + CALL bdos_hlrotr + LD (CPM_VARS.bdos_arecord), HL + LD (CPM_VARS.bdos_drec), HL + +; ------------------------------------------------------- +; Seek the track given by arecord (actual record) +; local equates for registers +; ------------------------------------------------------- +bdos_seek: + LD HL, CPM_VARS.bdos_arecord + LD C, (HL) + INC HL + LD B, (HL) + ; BC = sector number + + LD HL, (CPM_VARS.bdos_currec) + LD E, (HL) + INC HL + LD D, (HL) + ; DE = current sector number + + LD HL, (CPM_VARS.bdos_curtrk) + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + ; HL = current track + +.sec_less_cur: + ; check specified sector before current + LD A, C + SUB E + LD A, B + SBC A, D + JP NC, .sec_ge_cur + ; yes, decrement current sectors for one track + PUSH HL + LD HL, (CPM_VARS.bdos_sectpt) ; sectors per track + LD A, E + SUB L + LD E, A + LD A, D + SBC A, H + LD D, A + ; current track-1 + POP HL + DEC HL + JP .sec_less_cur + ; specified sector >= current +.sec_ge_cur: + PUSH HL + LD HL, (CPM_VARS.bdos_sectpt) + ADD HL, DE ; current sector+track + JP C, .track_found ; jump if after current + ; sector before current + LD A, C + SUB L + LD A, B + SBC A, H + JP C, .track_found + EX DE, HL + POP HL + ; increment track + INC HL + JP .sec_ge_cur + ; determined track number with specified sector +.track_found: + POP HL + PUSH BC + PUSH DE + PUSH HL + ; Stack contains (lowest) BC=arecord, DE=currec, HL=curtrk + EX DE, HL + LD HL, (CPM_VARS.bdos_offset) ; adjust if have reserved tracks + ADD HL, DE + LD B, H + LD C, L + CALL BIOS.settrk_f ; select track + + ; save current track + POP DE ; curtrk + LD HL, (CPM_VARS.bdos_curtrk) + LD (HL), E + INC HL + LD (HL), D + + ; save current record + POP DE + LD HL, (CPM_VARS.bdos_currec) + LD (HL), E + INC HL + LD (HL), D + + ; calc relative offset between arecord and currec + POP BC + LD A, C + SUB E + LD C, A + LD A, B + SBC A, D + LD B, A + + LD HL, (CPM_VARS.bdos_tranv) ; HL -> translation table + EX DE, HL + ; translate via BIOS call + CALL BIOS.sectran_f + ; select translated sector and return + LD C, L + LD B, H + JP BIOS.setsec_f + +; ------------------------------------------------------- +; Compute block number from record number and extent +; number +; ------------------------------------------------------- +bdos_dm_position: + LD HL, CPM_VARS.bdos_blkshf + LD C, (HL) + LD A, (CPM_VARS.bdos_vrecord) ; record number + ; a = a / 2^blkshf + +dmpos_l0: + OR A + RRA + DEC C + JP NZ, dmpos_l0 + ; A = shr(vrecord, blkshf) = vrecord/2^(sect/block) + LD B, A + LD A, 8 + SUB (HL) + ; c = 8 - blkshf + LD C, A + LD A, (CPM_VARS.bdos_extval) + ; a = extval * 2^(8-blkshf) +dmpos_l1: + DEC C + JP Z, dmpos_l2 + OR A + RLA + JP dmpos_l1 +dmpos_l2: + ADD A, B + RET ; with dm_position in A + +; ------------------------------------------------------- +; Return disk map value from position given by BC +; ------------------------------------------------------- +bdos_getdm: + LD HL, (CPM_VARS.bdos_info) + LD DE, DSK_MAP + ADD HL, DE + ADD HL, BC + LD A, (CPM_VARS.bdos_small_disk) + OR A + JP Z,getdmd + LD L, (HL) + LD H, 0x00 + RET + +getdmd: + ADD HL, BC + LD E, (HL) + INC HL + LD D, (HL) + EX DE, HL + RET + +; ------------------------------------------------------- +; Compute disk block number from current FCB +; ------------------------------------------------------- +bdos_index: + CALL bdos_dm_position + LD C, A + LD B, 0 + CALL bdos_getdm + LD (CPM_VARS.bdos_arecord), HL + RET + +; ------------------------------------------------------- +; Called following index to see if block allocated +; Out: ZF if not allocated +; ------------------------------------------------------- +bdos_allocated: + LD HL, (CPM_VARS.bdos_arecord) + LD A, L + OR H + RET + +; ------------------------------------------------------- +; Compute actual record address, assuming index called +; ------------------------------------------------------- +bdos_atran: + LD A, (CPM_VARS.bdos_blkshf) + LD HL, (CPM_VARS.bdos_arecord) +bdatarn_l0: + ADD HL, HL + DEC A + JP NZ, bdatarn_l0 + LD (CPM_VARS.bdos_arecord1), HL + LD A, (CPM_VARS.bdos_blmsk) + LD C, A + LD A, (CPM_VARS.bdos_vrecord) + AND C + OR L + LD L, A + LD (CPM_VARS.bdos_arecord), HL + RET + +; ------------------------------------------------------- +; Get current extent field address to A +; ------------------------------------------------------- +bdos_getexta: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_EXT + ADD HL, DE ; HL -> .fcb(extnum) + RET + +; ------------------------------------------------------- +; Compute reccnt and nxtrec addresses for get/setfcb +; Out: HL -> record count byte in fcb +; DE -> next record number byte in fcb +; ------------------------------------------------------- +bdos_fcb_rcnr: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_RC + ADD HL, DE + EX DE, HL + LD HL, 17 ; (nxtrec-reccnt) + ADD HL, DE + RET + +; ------------------------------------------------------- +; Set variables from currently addressed fcb +; ------------------------------------------------------- +bdos_stor_fcb_var: + CALL bdos_fcb_rcnr + LD A, (HL) + LD (CPM_VARS.bdos_vrecord), A + EX DE, HL + LD A, (HL) + LD (CPM_VARS.bdos_rcount), A + CALL bdos_getexta + LD A, (CPM_VARS.bdos_extmsk) + AND (HL) + LD (CPM_VARS.bdos_extval), A + RET + +; ------------------------------------------------------- +; Set the next record to access. +; Inp: HL -> fcb next record byte +; ------------------------------------------------------- +bdos_set_nxt_rec: + CALL bdos_fcb_rcnr + LD A, (CPM_VARS.bdos_seqio) ; sequential mode flag (=1) + CP 2 + JP NZ, bsfcb_l0 + XOR A ; if 2 - no adder needed +bsfcb_l0: + LD C, A + LD A, (CPM_VARS.bdos_vrecord) ; last record number + ADD A, C + LD (HL), A + EX DE, HL + LD A, (CPM_VARS.bdos_rcount) ; next record byte + LD (HL), A + RET + +; ------------------------------------------------------- +; Rotate HL right by C bits +; ------------------------------------------------------- +bdos_hlrotr: + INC C +bhlr_nxt: + DEC C + RET Z + LD A, H + OR A + RRA + LD H, A + LD A,L + RRA + LD L, A + JP bhlr_nxt + +; ------------------------------------------------------- +; Compute checksum for current directory buffer +; Out: A - checksum +; ------------------------------------------------------- +bdos_compute_cs: + LD C, DIR_BUFF_SIZE ; size of directory buffer + LD HL, (CPM_VARS.bdos_buffa) + XOR A +bcompcs_add_nxt: + ADD A, (HL) + INC HL + DEC C + JP NZ, bcompcs_add_nxt + RET + +; ------------------------------------------------------- +; Rotate HL left by C bits +; ------------------------------------------------------- +bdos_hlrotl: + INC C +.nxt: + DEC C + RET Z + ADD HL, HL + JP .nxt + +; ------------------------------------------------------- +; Set a "1" value in curdsk position +; Inp: BC - vector +; Out: HL - vector with bit set +; ------------------------------------------------------- +bdos_set_dsk_bit: + PUSH BC + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + LD HL, 0x0001 + CALL bdos_hlrotl + POP BC + LD A, C + OR L + LD L, A + LD A, B + OR H + LD H, A + RET + +; ------------------------------------------------------- +; Get write protect status +; ------------------------------------------------------- +bdos_nowrite: + LD HL, (CPM_VARS.bdos_rodsk) + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + CALL bdos_hlrotr + LD A, L + AND 0x1 + RET + +; ------------------------------------------------------- +; Temporarily set current drive to be read-only; +; attempts to write to it will fail +; ------------------------------------------------------- +bdos_set_ro: + LD HL, CPM_VARS.bdos_rodsk + LD C, (HL) + INC HL + LD B, (HL) + + CALL bdos_set_dsk_bit + LD (CPM_VARS.bdos_rodsk), HL + ; high water mark in directory goes to max + LD HL, (CPM_VARS.bdos_dirmax) + INC HL + EX DE, HL + LD HL, (CPM_VARS.bdos_cdrmax) + LD (HL), E + INC HL + LD (HL), D + RET + +; ------------------------------------------------------- +; Check current directory element for read/only status +; ------------------------------------------------------- +bdos_check_rodir: + CALL bdos_getdptra + +; ------------------------------------------------------- +; Check current buff(dptr) or fcb(0) for r/o status +; ------------------------------------------------------- +bdos_check_rofile: + LD DE, RO_FILE + ADD HL, DE + LD A, (HL) + RLA + RET NC + LD HL, bdos_rofe_addr ; = C8B1h + JP bdos_jump_hl + +; ------------------------------------------------------- +; Check for write protected disk +; ------------------------------------------------------- +bdos_check_write: + CALL bdos_nowrite + RET Z + LD HL, bdos_rode_addr ; = C8ABh + JP bdos_jump_hl + +; ------------------------------------------------------- +; Compute the address of a directory element at +; positon dptr in the buffer +; ------------------------------------------------------- +bdos_getdptra: + LD HL, (CPM_VARS.bdos_buffa) + LD A, (CPM_VARS.bdos_dptr) + +; ------------------------------------------------------- +; HL = HL + A +; ------------------------------------------------------- +bdos_hl_add_a: + ADD A,L + LD L, A + RET NC + INC H + RET + +; ------------------------------------------------------- +; Compute the address of the s2 from fcb +; Out: A - module number +; HL -> module number +; ------------------------------------------------------- +bdos_get_s2: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_S2 + ADD HL, DE + LD A, (HL) + RET + +; ------------------------------------------------------- +; Clear the module number field for user open/make +; ------------------------------------------------------- +bdos_clear_s2: + CALL bdos_get_s2 + LD (HL), 0 + RET + +; ------------------------------------------------------- +; Set FWF (File Write Flag) +; ------------------------------------------------------- +bdos_set_fwf: + CALL bdos_get_s2 + OR FWF_MASK + LD (HL), A + RET + +; ------------------------------------------------------- +; Check for free entries in directory +; Out: CF is set - directory have more file entries +; CF is reset - directory is full +; ------------------------------------------------------- +bdos_dir_is_free: + LD HL, (CPM_VARS.bdos_dcnt) + EX DE, HL ; DE = directory counter + LD HL, (CPM_VARS.bdos_cdrmax) ; HL = cdrmax + LD A, E + SUB (HL) + INC HL + LD A, D + SBC A, (HL) + ; Condition dcnt - cdrmax produces cy if cdrmax>dcnt + RET + +; ------------------------------------------------------- +; If not (cdrmax > dcnt) then cdrmax = dcnt+1 +; ------------------------------------------------------- +bdos_setcdr: + CALL bdos_dir_is_free + RET C + INC DE + LD (HL), D + DEC HL + LD (HL), E + RET + +; ------------------------------------------------------- +; HL = DE - HL +; ------------------------------------------------------- +bdos_de_sub_hl: + LD A, E + SUB L + LD L, A + LD A, D + SBC A, H + LD H, A + RET + +; Set directory checksubm byte +bdos_newchecksum: + LD C, 0xff + + +; ------------------------------------------------------- +bdos_checksum: + LD HL, (CPM_VARS.bdos_drec) + EX DE, HL + LD HL, (CPM_VARS.bdos_alloc1) + CALL bdos_de_sub_hl + RET NC + PUSH BC + CALL bdos_compute_cs ; Compute checksum to A for current dir buffer + LD HL, (CPM_VARS.bdos_cksum) + EX DE, HL + LD HL, (CPM_VARS.bdos_drec) + ADD HL, DE + POP BC + INC C + JP Z, initial_cs + CP (HL) + RET Z + CALL bdos_dir_is_free + RET NC + CALL bdos_set_ro + RET +initial_cs: + LD (HL), A + RET + +; ------------------------------------------------------- +; Write the current directory entry, set checksum +; ------------------------------------------------------- +bdos_wrdir: + CALL bdos_newchecksum + CALL bdos_setdir + LD C, 0x1 + CALL bdos_wrbuff + JP bdos_setdata + +; ------------------------------------------------------- +; Read a directory entry into the directory buffer +; ------------------------------------------------------- +bdos_rd_dir: + CALL bdos_setdir + CALL bdos_rdbuff + +; ------------------------------------------------------- +; Set data dma address +; ------------------------------------------------------- +bdos_setdata: + LD HL, CPM_VARS.bdos_dmaad + JP bdos_set_dma + +; ------------------------------------------------------- +; Set directory dma address +; ------------------------------------------------------- +bdos_setdir: + LD HL, CPM_VARS.bdos_buffa + +; ------------------------------------------------------- +; HL -> dma address to set (i.e., buffa or dmaad) +; ------------------------------------------------------- +bdos_set_dma: + LD C, (HL) + INC HL + LD B, (HL) + JP BIOS.setdma_f + +; ------------------------------------------------------- +; Copy the directory entry to the user buffer +; after call to search or searchn by user code +; ------------------------------------------------------- +bdos_dir_to_user: + LD HL, (CPM_VARS.bdos_buffa) + EX DE, HL + LD HL, (CPM_VARS.bdos_dmaad) + LD C, DIR_BUFF_SIZE + JP bdos_move_dehl + +; ------------------------------------------------------- +; return zero flag if at end of directory, non zero +; if not at end (end of dir if dcnt = 0ffffh) +; ------------------------------------------------------- +bdos_end_of_dir: + LD HL, CPM_VARS.bdos_dcnt + LD A, (HL) + INC HL + CP (HL) + RET NZ + INC A + RET + +; ------------------------------------------------------- +; Set dcnt to the end of the directory +; ------------------------------------------------------- +bdos_set_end_dir: + LD HL, ENDDIR + LD (CPM_VARS.bdos_dcnt), HL + RET + +; ------------------------------------------------------- +; Read next directory entry, with C=true if initializing +; ------------------------------------------------------- +bdos_read_dir: + LD HL, (CPM_VARS.bdos_dirmax) + EX DE, HL + LD HL, (CPM_VARS.bdos_dcnt) + INC HL + LD (CPM_VARS.bdos_dcnt), HL + CALL bdos_de_sub_hl + JP NC, bdrd_l0 + JP bdos_set_end_dir +bdrd_l0: + LD A, (CPM_VARS.bdos_dcnt) + AND DSK_MSK + LD B, FCB_SHF +bdrd_l1: + ADD A, A + DEC B + JP NZ, bdrd_l1 + LD (CPM_VARS.bdos_dptr), A + OR A + RET NZ + PUSH BC + CALL bdos_seekdir + CALL bdos_rd_dir + POP BC + JP bdos_checksum + +; ------------------------------------------------------- +; Given allocation vector position BC, return with byte +; containing BC shifted so that the least significant +; bit is in the low order accumulator position. HL is +; the address of the byte for possible replacement in +; memory upon return, and D contains the number of shifts +; required to place the returned value back into position +; ------------------------------------------------------- +bdos_getallocbit: + LD A, C + AND 00000111b + INC A + LD E, A + LD D, A + LD A, C + RRCA + RRCA + RRCA + AND 00011111b + LD C, A + LD A, B + ADD A, A + ADD A, A + ADD A, A + ADD A, A + ADD A, A + OR C + LD C, A + LD A, B + RRCA + RRCA + RRCA + AND 00011111b + LD B, A + LD HL, (CPM_VARS.bdos_alloca) ; Base address of allocation vector + ADD HL, BC + LD A, (HL) +ga_rotl: + RLCA + DEC E + JP NZ, ga_rotl + RET + +; ------------------------------------------------------- +; BC is the bit position of ALLOC to set or reset. The +; value of the bit is in register E. +; ------------------------------------------------------- +bdos_setallocbit: + PUSH DE + CALL bdos_getallocbit + AND 11111110b + POP BC + OR C + +; ------------------------------------------------------- +; Rotate and replace +; Inp: A - Byte value from ALLOC +; D - bit position (1..8) +; HL - target ALLOC position +; ------------------------------------------------------- +bdos_sab_rotr: + RRCA + DEC D + JP NZ, bdos_sab_rotr + LD (HL), A + RET + +; ------------------------------------------------------- +; Set or clear space used bits in allocation map +; Inp: C=0 - clear, C=1 - set +; ------------------------------------------------------- +bdos_scandm: + CALL bdos_getdptra + ; HL addresses the beginning of the directory entry + LD DE, DSK_MAP + ADD HL, DE ; HL -> block number byte + PUSH BC + LD C, 17 ; fcblen-dskmap+1 + ; scan all 17 bytes +.scan_next: + POP DE + DEC C + RET Z ; return if done + + PUSH DE + ; small or bug disk? + LD A, (CPM_VARS.bdos_small_disk) + OR A + JP Z, .scan_big + PUSH BC + PUSH HL + ; small, one byte + LD C, (HL) + LD B, 0 + JP .scan_cmn +.scan_big: + ; big, two byte + DEC C + PUSH BC + LD C, (HL) + INC HL + LD B, (HL) + PUSH HL + +.scan_cmn: + ; this block used? + LD A, C + OR B + JP Z, .not_used + ; have free space on disk? + LD HL, (CPM_VARS.bdos_maxall) + LD A, L + SUB C + LD A, H + SBC A, B + CALL NC, bdos_setallocbit ; enough space, set bit + +.not_used: + POP HL ; HL -> next block in fcb + INC HL + POP BC + JP .scan_next + +; ------------------------------------------------------- +; Initialize the current disk +; lret = false, set to true if $ file exists +; compute the length of the allocation vector - 2 +; ------------------------------------------------------- +bdos_initialize: + ; compute size of alloc table + LD HL, (CPM_VARS.bdos_maxall) + LD C, 3 + CALL bdos_hlrotr ; HL = disk size / 8 + 1 + INC HL + LD B, H + LD C, L ; BC = alloc table size + LD HL, (CPM_VARS.bdos_alloca) ; address of allocation table + ; fill with zeroes +.alt_fill_0: + LD (HL), 0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .alt_fill_0 + + LD HL, (CPM_VARS.bdos_dirblk) ; DE = initial space, used by directory + EX DE, HL + LD HL, (CPM_VARS.bdos_alloca) ; HL -> allocation map + LD (HL), E + INC HL + LD (HL), D ; [HL] = DE + CALL bdos_home ; home drive, set initial head, track, sector + + LD HL, (CPM_VARS.bdos_cdrmax) + LD (HL), 3 ; next dir read + INC HL + LD (HL), 0 + CALL bdos_set_end_dir ; mark end of dir + +.rd_next_f: + ; read next file name + LD C, 0xff + CALL bdos_read_dir + CALL bdos_end_of_dir ; is have another file? + RET Z + ; get params of file, and check deleted + CALL bdos_getdptra + LD A, FILE_DELETED + CP (HL) + JP Z, .rd_next_f + ; check user code + LD A, (CPM_VARS.bdos_userno) + CP (HL) + JP NZ, .set_as_used + INC HL + LD A, (HL) + SUB '$' ; check for $name file + JP NZ, .set_as_used + ; dollar file found, mark in lret + DEC A + LD (CPM_VARS.bdos_aret), A + +.set_as_used: + LD C, 1 + CALL bdos_scandm + CALL bdos_setcdr + JP .rd_next_f + +; ------------------------------------------------------- +; Return directory location as lret +; used in delete, rename, ... +; ------------------------------------------------------- +bdos_ret_dirloc: + LD A, (CPM_VARS.bdos_dirloc) + JP bdos_ret_a + +; ------------------------------------------------------- +; Compare extent# in A with that in C, return nonzero +; if they do not match +; ------------------------------------------------------- +bdos_compext: + PUSH BC + PUSH AF + LD A, (CPM_VARS.bdos_extmsk) + CPL + LD B, A + LD A, C + AND B + LD C, A + POP AF + AND B + SUB C + AND MAX_EXT ; check only bits [4:0] (MAX_EXT) + POP BC + RET + +; ------------------------------------------------------- +; Search for directory element of length C at info +; ------------------------------------------------------- +bdos_search: + LD A, 0xff + LD (CPM_VARS.bdos_dirloc), A + LD HL, CPM_VARS.bdos_searchl ; length in bytes to match + LD (HL), C + LD HL, (CPM_VARS.bdos_info) ; address filename to match + LD (CPM_VARS.bdos_searcha), HL + CALL bdos_set_end_dir + CALL bdos_home + +; ------------------------------------------------------- +; Search for the next directory element, assuming +; a previous call on search which sets searcha and +; searchl +; ------------------------------------------------------- +bdos_search_nxt: + LD C, 0 + CALL bdos_read_dir ; get next filename entry + CALL bdos_end_of_dir ; at end? pos=0xffff + JP Z, .not_found ; jump at end + + LD HL, (CPM_VARS.bdos_searcha) + EX DE, HL + ; skip if file deleted + LD A, (DE) + CP FILE_DELETED + JP Z, .search_next + + PUSH DE + CALL bdos_dir_is_free + POP DE + JP NC, .not_found ; Is full, no more file entries + +.search_next: + CALL bdos_getdptra ; get address of FCB + LD A, (CPM_VARS.bdos_searchl) ; BC=length to compare + LD C, A + LD B, 0 + + ; compare loop +.search_loop: + LD A, C + OR A + JP Z, .search_end ; search completed? + ; check byte + LD A, (DE) + CP '?' + JP Z, .search_ok ; match if wildcard + LD A, B + CP 13 ; ignore 13th byte + JP Z, .search_ok + CP FCB_EXT ; extent byte + LD A, (DE) + JP Z, .search_ext + SUB (HL) ; compare bytes + AND 0x7f ; mask 7th bit + JP NZ, bdos_search_nxt ; if not match, check next file entry + JP .search_ok + + ; compare ext +.search_ext: + PUSH BC + LD C, (HL) + CALL bdos_compext + POP BC + JP NZ, bdos_search_nxt ; if not match, check next file entry + + ; current bytes matches, increment pointers, decrement counter +.search_ok: + INC DE + INC HL + INC B + DEC C + JP .search_loop ; compare next byte + + ; entiry name matches, return dir position +.search_end: + LD A, (CPM_VARS.bdos_dcnt) + AND 0x3 + LD (CPM_VARS.bdos_aret), A + LD HL, CPM_VARS.bdos_dirloc + LD A, (HL) + RLA + RET NC + XOR A + LD (HL), A + RET + + ; end of directory, or empty name +.not_found: + CALL bdos_set_end_dir + LD A, 0xff + JP bdos_ret_a + +; ------------------------------------------------------- +; Delete the currently addressed file +; ------------------------------------------------------- +bdos_era_file: + CALL bdos_check_write ; check write rotection + LD C, 12 ; length of filename + CALL bdos_search ; search file in directory (with wildcards) +.del_next: + CALL bdos_end_of_dir + RET Z ; no more dir entries - exit + CALL bdos_check_rodir ; check file RO + CALL bdos_getdptra ; get file info + LD (HL), FILE_DELETED ; set deleted marker at first symbol + LD C, 0 + CALL bdos_scandm ; clear space at map + CALL bdos_wrdir ; write directory to disk + CALL bdos_search_nxt ; find next file + JP .del_next + +; ------------------------------------------------------- +; Given allocation vector position BC, find the zero bit +; closest to this position by searching left and right. +; if found, set the bit to one and return the bit position +; in hl. if not found (i.e., we pass 0 on the left, or +; maxall on the right), return 0000 in hl +; ------------------------------------------------------- +bdos_get_block: + LD D, B + LD E, C +.prev_test: + LD A, C + OR B + JP Z, .next_test ; jump if block 0 specified + ; check previous block + DEC BC + PUSH DE + PUSH BC + CALL bdos_getallocbit + RRA + JP NC, .ret_block ; block is empty, use it + + ; not empty, check more + POP BC + POP DE + + ; look at next block +.next_test: + ; check for free space on disk + LD HL, (CPM_VARS.bdos_maxall) + LD A, E + SUB L + LD A, D + SBC A, H + JP NC, .ret_no_block + ; have space, move to next block + INC DE + PUSH BC + PUSH DE + LD B, D + LD C, E + CALL bdos_getallocbit + RRA + JP NC, .ret_block ; block is empty, use it + POP DE + POP BC + JP .prev_test + + ; mark block as used and return in HL +.ret_block: + RLA + INC A + CALL bdos_sab_rotr + POP HL + POP DE + RET + + ; no free blocks found +.ret_no_block: + LD A, C + OR B + JP NZ, .prev_test ; if BC != 0 try to find before + LD HL, 0x00 ; not found + RET + +; ------------------------------------------------------- +; Copy the entire file control block +; ------------------------------------------------------- +bdos_copy_fcb: + LD C, 0x00 + LD E, FCB_LEN +; ------------------------------------------------------- +; copy fcb information starting at C for E bytes +; into the currently addressed directory entry +; ------------------------------------------------------- +dbos_copy_dir: + PUSH DE + LD B, 0x00 + LD HL, (CPM_VARS.bdos_info) + ADD HL, BC + EX DE, HL + CALL bdos_getdptra + POP BC + CALL bdos_move_dehl + +; ------------------------------------------------------- +; Enter from close to seek and copy current element +; ------------------------------------------------------- +bdos_seek_copy: + CALL bdos_seekdir + JP bdos_wrdir + +; ------------------------------------------------------- +; Rename the file described by the first half of +; the currently addressed FCB to new name, contained in +; the last half of the currently addressed FCB. +; ------------------------------------------------------- +bdos_rename: + CALL bdos_check_write ; check for RO disk + LD C, 12 ; use 12 symbols of name to compare + CALL bdos_search ; find first file + LD HL, (CPM_VARS.bdos_info) ; file info + LD A, (HL) ; user number + LD DE, 16 ; shift to place second file info + ADD HL, DE + LD (HL), A ; set same user no + +.ren_next: + CALL bdos_end_of_dir + RET Z ; return if end of directory + + CALL bdos_check_rodir ; check file RO flag + LD C, 16 ; start from 16th byte + LD E, FN_LEN ; and copy 12 byte + CALL dbos_copy_dir + CALL bdos_search_nxt ; search next file + JP .ren_next + +; ------------------------------------------------------- +; Update file attributes for current fcb +; ------------------------------------------------------- +bdos_update_attrs: + LD C, FN_LEN + CALL bdos_search ; search file by 12 bytes of name +.set_next: + CALL bdos_end_of_dir + RET Z ; return if not found + + LD C, 0 + LD E, FN_LEN + CALL dbos_copy_dir ; copy name to FCB and save dir + CALL bdos_search_nxt + JP .set_next ; ; do it for next file + +; -------------------------------------------------- +; Open file, name specified in FCB +; ------------------------------------------------------- +open: + LD C, FCB_INFO_LEN + CALL bdos_search ; search file + CALL bdos_end_of_dir + RET Z ; return if not found + +bdos_open_copy: + CALL bdos_getexta + LD A, (HL) ; get extent byte + PUSH AF ; save ext + PUSH HL ; and it's address + CALL bdos_getdptra + EX DE, HL + ; move to user space + LD HL, (CPM_VARS.bdos_info) + LD C, 32 + PUSH DE + CALL bdos_move_dehl + CALL bdos_set_fwf ; set 7th bit s2 "unmodified" flag + + POP DE + LD HL, FCB_EXT + ADD HL, DE + LD C, (HL) ; C = extent byte + + LD HL, FCB_RC + ADD HL, DE + LD B, (HL) ; B = record count + + POP HL + POP AF + LD (HL), A + LD A, C + CP (HL) ; cmp extent bytes + LD A, B + JP Z, .set_rec_cnt + ; user specified extent is not same, reset record count to 0 + LD A, 0 + JP C, .set_rec_cnt + ; set to maximum + LD A, 128 + +.set_rec_cnt: + ; set record count in user FCB to A + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_RC + ADD HL, DE + LD (HL), A + RET + +; -------------------------------------------------- +; HL = .fcb1(i), DE = .fcb2(i), +; if fcb1(i) = 0 then fcb1(i) := fcb2(i) +; -------------------------------------------------- +bdos_mergezero: + LD A, (HL) + INC HL + OR (HL) + DEC HL + RET NZ + LD A, (DE) + LD (HL), A + INC DE + INC HL + LD A, (DE) + LD (HL), A + DEC DE + DEC HL + RET + +; -------------------------------------------------- +; Close file specified by FCB +; -------------------------------------------------- +bdos_close: + XOR A + ; clear status and file position bytes + LD (CPM_VARS.bdos_aret), A + LD (CPM_VARS.bdos_dcnt), A + LD (CPM_VARS.bdos_dcnt+1), A + CALL bdos_nowrite ; get write protection + RET NZ ; return if set + + CALL bdos_get_s2 + AND FWF_MASK + RET NZ ; return if not modified flag set + ; search file + LD C, FCB_INFO_LEN + CALL bdos_search + CALL bdos_end_of_dir + RET Z ; return if not found + + LD BC, DSK_MAP ; offset of records used + CALL bdos_getdptra + ADD HL, BC + EX DE, HL + + LD HL, (CPM_VARS.bdos_info) ; same for user FCB + ADD HL, BC + LD C, 16 ; bytes in extent + +bdos_merge0: + LD A, (CPM_VARS.bdos_small_disk) ; small/big disk flag + OR A + JP Z, bdos_merge ; jump if big (16 bit) + ; small disk (8 bit) + LD A, (HL) ; from user FCB + OR A + LD A, (DE) ; from DIR FCB + JP NZ, bdm_fcbnzero + LD (HL), A ; user is 0, set from directory fcb + +bdm_fcbnzero: + OR A + JP NZ, bdm_buffnzero + ; dir is 0, set from user fcb + LD A, (HL) + LD (DE), A + +bdm_buffnzero: + CP (HL) + JP NZ, bdm_mergerr ; if both non zero, close error + JP bdm_dmset ; merged ok, go to next fcb byte + +bdos_merge: + CALL bdos_mergezero ; update user fcb if it is zero + EX DE, HL + CALL bdos_mergezero ; update dir fcb if it is zero + EX DE, HL + LD A, (DE) + CP (HL) + JP NZ, bdm_mergerr ; if both is same, close error + + ; next byte + INC DE + INC HL + LD A, (DE) + CP (HL) + JP NZ, bdm_mergerr ; if both is same, close error + DEC C + +bdm_dmset: + ; next + INC DE + INC HL + DEC C + JP NZ, bdos_merge0 ; merge next if C>0 + + LD BC, 0xffec ; -(fcblen-extnum) (-20) + ADD HL, BC + EX DE, HL + ADD HL, BC + LD A, (DE) + CP (HL) + JP C, bdm_endmerge ; directory extent > user extent -> end + ; update record count in dir FCB + LD (HL), A + LD BC, 0x3 ; (reccnt-extnum) + ADD HL, BC + EX DE, HL + ADD HL, BC + LD A, (HL) ; get from user FCB + LD (DE), A ; set to directory FCB + +bdm_endmerge: + LD A, 0xff + LD (CPM_VARS.bdos_fcb_closed), A ; set was open and closed flag + JP bdos_seek_copy ; update directory + + ; set return status and return +bdm_mergerr: + LD HL, CPM_VARS.bdos_aret + DEC (HL) + RET + +; ------------------------------------------------------- +; Create a new file by creating a directory entry +; then opening the file +; ------------------------------------------------------- +bdos_make: + CALL bdos_check_write ; check Write Protection + LD HL, (CPM_VARS.bdos_info) ; save user FCB + PUSH HL ; on stack + LD HL, CPM_VARS.bdos_efcb ; empty FCB + ; Save FCB address, look for 0xE5 + LD (CPM_VARS.bdos_info), HL ; set empty FCB + ; search firs empty slot in directory + LD C, 0x1 ; compare one byte 0xE5 + CALL bdos_search + CALL bdos_end_of_dir ; found flag + POP HL + LD (CPM_VARS.bdos_info), HL ; restore user FCB address + RET Z ; return if not found (no space in dir) + EX DE, HL + LD HL, FCB_RC ; number or record for this file + ADD HL, DE + + ; clear FCB tail + LD C, 17 ; fcblen-namlen + XOR A +.fcb_set_0: + LD (HL), A + INC HL + DEC C + JP NZ, .fcb_set_0 + + LD HL, FCB_S1 + ADD HL, DE + LD (HL), A ; Current record within extent? + CALL bdos_setcdr + CALL bdos_copy_fcb + JP bdos_set_fwf + +; ------------------------------------------------------- +; Close the current extent, and open the next one to read +; if possible. RMF is true if in read mod +; ------------------------------------------------------- +bdos_open_reel: + XOR A + LD (CPM_VARS.bdos_fcb_closed), A ; clear close flag + CALL bdos_close ; close extent + CALL bdos_end_of_dir ; check space + RET Z ; ret if no more space + ; get extent byte from user FCB + LD HL, (CPM_VARS.bdos_info) + LD BC, FCB_EXT + ADD HL, BC + LD A, (HL) + ; and increment it, mask to 0..31 and store + INC A + AND MAX_EXT + LD (HL), A + JP Z, .overflow + ; mask extent byte + LD B, A + LD A, (CPM_VARS.bdos_extmsk) + AND B + LD HL, CPM_VARS.bdos_fcb_closed + AND (HL) + JP Z, .read_nxt_extent ; read netx extent + JP .extent_in_mem + +.overflow: + LD BC, 0x2 ; S2 + ADD HL, BC + INC (HL) + LD A, (HL) + AND MAX_MOD + JP Z, .error + +.read_nxt_extent: + LD C, FCB_INFO_LEN + CALL bdos_search + CALL bdos_end_of_dir + JP NZ, .extent_in_mem ; jump if success + ; not extent found + LD A, (CPM_VARS.bdos_rfm) + INC A + JP Z, .error ; can not get extent + CALL bdos_make ; make new empty dir entry for extent + CALL bdos_end_of_dir ; chk no space + JP Z, .error ; jmp to error if no space in directory + JP .almost_done +.extent_in_mem: + ; open extent + CALL bdos_open_copy +.almost_done: + CALL bdos_stor_fcb_var ; move updated data to new extent + XOR A ; clear error flag + JP bdos_ret_a +.error: + CALL set_ioerr_1 + JP bdos_set_fwf ; clear bit 7 in s2 + +; ------------------------------------------------------- +; Sequential disk read operation +; ------------------------------------------------------- +bdos_seq_disk_read: + LD A, 0x1 + LD (CPM_VARS.bdos_seqio), A + ; drop through to diskread + +bdos_disk_read: + LD A, TRUE + LD (CPM_VARS.bdos_rfm), A ; dont allow read unwritten data + CALL bdos_stor_fcb_var + LD A, (CPM_VARS.bdos_vrecord) ; next record to read + LD HL, CPM_VARS.bdos_rcount ; number of records in extent + ; in this extent? + CP (HL) + JP C, .recordok + ; no, in next extent, check this exctent fully used + CP 128 + JP NZ, .error_opn + ;open next extent + CALL bdos_open_reel + XOR A + ; reset record number to read + LD (CPM_VARS.bdos_vrecord), A + ; check open status + LD A, (CPM_VARS.bdos_aret) + OR A + JP NZ, .error_opn +.recordok: + ; compute block number to read + CALL bdos_index + ; check if it in bounds + CALL bdos_allocated + JP Z, .error_opn + + CALL bdos_atran ; convert to logical sector + CALL bdos_seek ; seec track and sector + CALL bdos_rdbuff ; read sector + JP bdos_set_nxt_rec + +.error_opn: + JP set_ioerr_1 + +; ------------------------------------------------------- +; Sequential write disk +; ------------------------------------------------------- +bdos_seq_disk_write: + LD A, 0x1 + LD (CPM_VARS.bdos_seqio), A +bdos_disk_write: + LD A, FALSE + LD (CPM_VARS.bdos_rfm), A ; allow open new extent + CALL bdos_check_write ; check write protection + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + CALL bdos_check_rofile ; check RO file + CALL bdos_stor_fcb_var + LD A, (CPM_VARS.bdos_vrecord) ; get record number to write to + CP 128 ; lstrec+1 + JP NC, set_ioerr_1 ; not in range - error + CALL bdos_index ; compute block number + CALL bdos_allocated ; check number + LD C, 0 + JP NZ, .disk_wr1 + CALL bdos_dm_position ; get next block number within FCB + LD (CPM_VARS.bdos_dminx), A ; and store it + LD BC, 0x00 ; start looking for space from start + OR A + JP Z, .nop_block ; zero - not allocated + ; extract previous blk from fcb + LD C, A + DEC BC + CALL bdos_getdm + LD B, H + LD C, L +.nop_block: + CALL bdos_get_block ; get next free nearest block + LD A, L + OR H + JP NZ, .block_ok + ; error, no more space + LD A, 2 + JP bdos_ret_a +.block_ok: + LD (CPM_VARS.bdos_arecord), HL ; set record to access + EX DE, HL ; to DE + LD HL, (CPM_VARS.bdos_info) ; + LD BC, FCB_AL ; 16 + ADD HL, BC + ; small/big disk + LD A, (CPM_VARS.bdos_small_disk) + OR A + LD A, (CPM_VARS.bdos_dminx) ; rel block + JP Z, .alloc_big ; jump for 16bit + CALL bdos_hl_add_a ; HL = HL + A + LD (HL), E ; save record to access + JP .disk_wru + +.alloc_big: + ; save record to acces 16bit + LD C, A + LD B, 0 + ADD HL, BC + ADD HL, BC + LD (HL), E + INC HL + LD (HL), D + + ; Disk write to previously unallocated block +.disk_wru: + LD C, 2 ; C=2 - write to unused disk space +.disk_wr1: + LD A, (CPM_VARS.bdos_aret) ; check status + OR A + RET NZ ; return on error + + PUSH BC ; store C write flag for BIOS + CALL bdos_atran ; convert block number to logical sector + LD A, (CPM_VARS.bdos_seqio) ; get access mode (0=random, 1=sequential, 2=special) + DEC A + DEC A + JP NZ, .normal_wr + ; special + POP BC + PUSH BC + LD A, C ; A = write status flag (2=write unused space) + DEC A + DEC A + JP NZ, .normal_wr + ; fill buffer with zeroes + PUSH HL + LD HL, (CPM_VARS.bdos_buffa) + LD D, A +.fill0: + LD (HL), A + INC HL + INC D + JP P, .fill0 + CALL bdos_setdir ; Tell BIOS buffer address to directory access + LD HL, (CPM_VARS.bdos_arecord1) ; get sector that starts current block + LD C, 2 ; write status flag (2=write unused space) +.fill1: + LD (CPM_VARS.bdos_arecord), HL ; set sector to write + PUSH BC + CALL bdos_seek ; seek track and sector number + POP BC + CALL bdos_wrbuff ; write zeroes + LD HL, (CPM_VARS.bdos_arecord) ; get sector number + LD C, 0 ; normal write flag + LD A, (CPM_VARS.bdos_blmsk) ; block mask + ; write entire block? + LD B, A + AND L + CP B + INC HL + JP NZ, .fill1 ; continue until (BLKMSK+1) sectors written + ; reset sector number + POP HL + LD (CPM_VARS.bdos_arecord), HL + CALL bdos_setdata ; reset DMA address + + ; Normal write. +.normal_wr: + CALL bdos_seek ; Set track and sector + POP BC ; restore C - write status flag + PUSH BC + CALL bdos_wrbuff ; write + POP BC + LD A, (CPM_VARS.bdos_vrecord) ; last file record + LD HL, CPM_VARS.bdos_rcount ; last written record + CP (HL) + JP C, .disk_wr2 + ; update record count + LD (HL), A + INC (HL) + LD C, 2 +.disk_wr2: + DEC C ; patch: NOP + DEC C ; patch: NOP + JP NZ, .no_update ; patch: LD HL,0 + PUSH AF + CALL bdos_get_s2 ; set 'extent written to' flag + AND 0x7f ; clear 7th bit + LD (HL), A + POP AF ; record count + +.no_update: + ; it is full + CP 127 + JP NZ, .set_nxt_rec + ; sequential mode? + LD A, (CPM_VARS.bdos_seqio) + CP 1 + JP NZ, .set_nxt_rec + + CALL bdos_set_nxt_rec ; set next record + CALL bdos_open_reel ; get space in dir + LD HL, CPM_VARS.bdos_aret ; check status + LD A, (HL) + OR A + JP NZ, .no_space ; no more space + + DEC A + LD (CPM_VARS.bdos_vrecord), A ; -1 + +.no_space: + LD (HL), 0x00 ; clear status + +.set_nxt_rec: + JP bdos_set_nxt_rec + +; ------------------------------------------------------- +; Random access seek operation, C=0ffh if read mode +; FCB is assumed to address an active file control block +; (modnum has been set to 1100$0000b if previous bad seek) +; ------------------------------------------------------- +bdos_rseek: + XOR A + LD (CPM_VARS.bdos_seqio), A ; set random mode + +bdos_rseek1: + PUSH BC + LD HL, (CPM_VARS.bdos_info) + EX DE, HL + LD HL, FCB_RN ; Random access record number + ADD HL, DE + LD A, (HL) + AND 7Fh ; [6:0] bits record number to access + PUSH AF + ; get bit 7 + LD A, (HL) + RLA + INC HL + ; get high byte [3:0] bits + LD A, (HL) + RLA + AND 00011111b + ; get extent byte + LD C, A + LD A, (HL) + RRA + RRA + RRA + RRA + AND 0x0f + LD B, A + POP AF + INC HL + ; get next byte + LD L, (HL) + ; check overflow + INC L + DEC L + LD L, 6 + JP NZ, .seek_err ; error 6 if overflow + + ; save current record in FCB + LD HL, FCB_CR + ADD HL, DE + LD (HL), A + + ; check extent byte + LD HL, FCB_EXT + ADD HL, DE + LD A, C + SUB (HL) + JP NZ, .close ; not same as previous + ; check extra s2 byte + LD HL, FCB_S2 + ADD HL, DE + LD A, B + SUB (HL) + AND 0x7f + JP Z, .seek_ok ; same, is ok + +.close: + PUSH BC + PUSH DE + CALL bdos_close ; close current extent + POP DE + POP BC + LD L, 3 ; Cannot close error #3 + LD A, (CPM_VARS.bdos_aret) ; check status + INC A + JP Z, .bad_seek ; status != 0 - error + ; set extent byte to FCB + LD HL, FCB_EXT + ADD HL, DE + LD (HL), C + ; set S2 - extra extent byte + LD HL, FCB_S2 + ADD HL, DE + LD (HL), B + + CALL open + LD A, (CPM_VARS.bdos_aret) + INC A + JP NZ, .seek_ok + POP BC + PUSH BC + LD L, 0x4 ; Seek to unwritten extent #4 + INC C + JP Z, .bad_seek + CALL bdos_make + LD L, 0x5 ; Cannot create new extent #5 + LD A, (CPM_VARS.bdos_aret) + INC A + JP Z, .bad_seek +.seek_ok: + POP BC + XOR A + JP bdos_ret_a +.bad_seek: + PUSH HL + CALL bdos_get_s2 + LD (HL), 11000000b + POP HL +.seek_err: + POP BC + LD A,L + LD (CPM_VARS.bdos_aret), A + JP bdos_set_fwf + +; ------------------------------------------------------- +; Random disk read operation +; ------------------------------------------------------- +bdos_rand_disk_read: + LD C, 0xff ; mode + CALL bdos_rseek ; positioning + CALL Z, bdos_disk_read ; read if ok + RET + +; ------------------------------------------------------- +; Random disk write operation +; ------------------------------------------------------- +bdos_rand_disk_write: + LD C, 0 ; mode + CALL bdos_rseek ; positioning + CALL Z, bdos_disk_write ; read if ok + RET + +; ------------------------------------------------------- +; Compute random record position +; Inp: HL -> FCB +; DE - relative location of record number +; Out: C - r0 byte +; B - r1 byte +; A - r2 byte +; ZF set - Ok, reset - overflow +; ------------------------------------------------------- +bdos_compute_rr: + EX DE, HL ; DE -> FCB + ADD HL, DE ; HL = FCB+DE + LD C, (HL) ; get record number + LD B, 0 ; in BC + LD HL, FCB_EXT + ADD HL, DE + LD A, (HL) ; A = extent + ; calculate BC = recnum + extent*128 + RRCA ; A[0] -> A[7] + AND 0x80 ; ignore other bits + ADD A, C ; add to record number + LD C, A ; C=r0 + LD A, 0 + ADC A, B + LD B, A ; B=r1 + ; + LD A, (HL) ; A = extent + RRCA + AND 0x0f ; A = EXT[4:1] + ADD A, B ; add to r1 byte + LD B, A + + LD HL, FCB_S2 + ADD HL, DE + LD A, (HL) ; A = extra extent bits + ADD A, A ; *2 + ADD A, A ; *4 + ADD A, A ; *8 + ADD A, A ; *16 + PUSH AF ; save flags (need only CF) + ADD A, B ; add to r1 + LD B, A + PUSH AF ; save flags + POP HL ; flags on L + LD A, L ; A bit 0 - CF + POP HL + OR L ; merge both carry flags + AND 0x1 ; mask only CF + RET + +; ------------------------------------------------------- +; Compute logical file size for current FCB. +; Setup r0, r1, r2 bytes - maximum record number +; ------------------------------------------------------- +bdos_get_file_size: + LD C, FN_LEN + CALL bdos_search ; get first dir record (first extent) + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + ; zeroing r0, r1, r2 + LD DE, FCB_RN ; D=0 + ADD HL, DE + PUSH HL + LD (HL), D + INC HL + LD (HL), D + INC HL + LD (HL), D + ; +.get_extent: + CALL bdos_end_of_dir + JP Z, .done ; if not more extents found, return + CALL bdos_getdptra ; HL -> FCB + LD DE, FCB_RC + CALL bdos_compute_rr ; compute random access parameters + POP HL + PUSH HL + ; Now let's compare these values ​​with those indicated + LD E, A + LD A, C + SUB (HL) + INC HL + LD A, B + SBC A, (HL) + INC HL + LD A, E + SBC A, (HL) + JP C, .less_size + ; found larger extent (size), save it to fcb + LD (HL), E + DEC HL + LD (HL), B + DEC HL + LD (HL), C +.less_size: + CALL bdos_search_nxt + JP .get_extent +.done: + POP HL + RET + +; ------------------------------------------------------- +; Set the random record count bytes of the FCB to the number +; of the last record read/written by the sequential I/O calls. +; ------------------------------------------------------- +bdos_set_random: + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + LD DE, FCB_CR ; Current record within extent + CALL bdos_compute_rr ; Compute random access parameters + LD HL, FCB_RN + ADD HL, DE ; HL -> Random access record number + ; set r0, r1, r2 + LD (HL), C + INC HL + LD (HL), B + INC HL + LD (HL), A + RET + +; ------------------------------------------------------- +; Select disk to bdos_curdsk for subsequent input or +; output ops +; ------------------------------------------------------- +bdos_select: + LD HL, (CPM_VARS.bdos_dlog) ; login vector + LD A, (CPM_VARS.bdos_curdsk) ; current drive + LD C, A + CALL bdos_hlrotr ; shift active bit for this drive + PUSH HL + EX DE, HL + CALL bdos_sel_dsk ; select drive + POP HL + CALL Z, bdos_sel_error ; check for error drive + ; new active drive? + LD A, L + RRA + RET C ; no, return + ; yes, update login vector + LD HL, (CPM_VARS.bdos_dlog) + LD C, L + LD B, H + CALL bdos_set_dsk_bit ; set bits in vector + LD (CPM_VARS.bdos_dlog), HL ; store new vector + + JP bdos_initialize + +; ------------------------------------------------------- +; Select disc +; Inp: C=0x0E +; E=drive number 0-A, 1-B .. 15-P +; Out: L=A=0 - ok or 0xFF - error +; ------------------------------------------------------- +bdos_select_disk: + ; check disk change + LD A, (CPM_VARS.bdos_linfo) + LD HL, CPM_VARS.bdos_curdsk + CP (HL) + RET Z ; is same, return + ; login new disk + LD (HL), A + JP bdos_select + +; ------------------------------------------------------- +; Auto select disk by FCB_DR +; ------------------------------------------------------- +bdos_reselect: + LD A, TRUE + LD (CPM_VARS.bdos_resel), A ; set flag + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + LD A, (HL) ; get specified disk + AND 0x1f ; A = A[4:0] + DEC A ; adjust for 0-A, 1-B and so on + LD (CPM_VARS.bdos_linfo), A ; set as parameter + CP 0x1e ; no change? + JP NC, .no_select + ; change, but save current to old + LD A, (CPM_VARS.bdos_curdsk) + LD (CPM_VARS.bdos_olddsk), A + ; save FCB_DR byte + LD A, (HL) + LD (CPM_VARS.bdos_fcbdsk), A + ; clear FCB_DR[4:0] bits - user code + AND 11100000b + LD (HL), A + CALL bdos_select_disk + +.no_select: + ; set user to FCB_DR low bits + LD A, (CPM_VARS.bdos_userno) + LD HL, (CPM_VARS.bdos_info) + OR (HL) + LD (HL), A + RET + +; ------------------------------------------------------- +; Return version number +; ------------------------------------------------------- +bdos_get_version: + LD A, CPM_VERSION ; 0x22 - v2.2 + JP bdos_ret_a + +; ------------------------------------------------------- +; Reset disk system - initialize to disk 0 +; ------------------------------------------------------- +bdos_reset_disks: + LD HL, 0x00 + LD (CPM_VARS.bdos_rodsk), HL ; clear ro flags + LD (CPM_VARS.bdos_dlog), HL ; clear login vector + XOR A ; disk 0 + LD (CPM_VARS.bdos_curdsk), A ; set disk 'A' as current + LD HL, dma_buffer ; set 'DMA' buffer address to default + LD (CPM_VARS.bdos_dmaad), HL + CALL bdos_setdata + JP bdos_select ; select drive 'A' + +; ------------------------------------------------------- +; Open file +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_open_file: + CALL bdos_clear_s2 + CALL bdos_reselect + JP open + +; ------------------------------------------------------- +; Close file +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_close_file: + CALL bdos_reselect + JP bdos_close + +; ------------------------------------------------------- +; Search for first occurrence of a file in directory +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_search_first: + LD C, 0x00 ; special search + EX DE, HL + LD A, (HL) + CP '?' ; '?' is first byte? + JP Z, .qselect ; return first matched entry + + CALL bdos_getexta ; get extent byte from FCB + LD A, (HL) + CP '?' ; it is '?' + CALL NZ, bdos_clear_s2 ; set S2 to 0 if not + CALL bdos_reselect ; select disk by FCB + LD C, FCB_INFO_LEN ; match 1first 15 bytes +.qselect: + CALL bdos_search ; search first entry + JP bdos_dir_to_user ; move entry to user space + +; ------------------------------------------------------- +; Search for next occurrence of a file name +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_search_next: + LD HL, (CPM_VARS.bdos_searcha) ; restore FCB address + LD (CPM_VARS.bdos_info), HL ; set as parameter + CALL bdos_reselect ; select drive by FCB + CALL bdos_search_nxt ; search next + JP bdos_dir_to_user ; move entry to user space + +; ------------------------------------------------------- +; Remove directory +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_rm_file: + CALL bdos_reselect ; Select drive by FCB + CALL bdos_era_file ; Delete file + JP bdos_ret_dirloc ; Return directory location in A + +; ------------------------------------------------------- +; Read next 128b record +; Inp: DE -> FCB +; Out: BA and HL - error. +; A=0 - Ok, +; 1 - end of file, +; 9 - invalid FCB, +; 10 - media changed, +; 0FFh - hardware error. +; ------------------------------------------------------- +bdos_read_file: + CALL bdos_reselect ; select drive + JP bdos_seq_disk_read ; and read + +; ------------------------------------------------------- +; Write next 128b record +; Inp: DE -> FCB +; Out: BA and HL - error. +; A=0 - Ok, +; 1 - directory full, +; 2 - disc full, +; 9 - invalid FCB, +; 10 - media changed, +; 0FFh - hardware error. +; ------------------------------------------------------- +bdos_write_file: + CALL bdos_reselect ; select drive + JP bdos_seq_disk_write ; and write + +; ------------------------------------------------------- +; Create file +; Inp: DE -> FCB. +; Out: Error in BA and HL +; A=0 - Ok, +; 0FFh - directory is full. +; ------------------------------------------------------- +bdos_make_file: + CALL bdos_clear_s2 ; clear S2 byte + CALL bdos_reselect ; select drive + JP bdos_make ; and make file + +; ------------------------------------------------------- +; Rename file. New name, stored at FCB+16 +; Inp: DE -> FCB. +; Out: Error in BA and HL +; A=0-3 if successful; +; A=0FFh if error. +; ------------------------------------------------------- +bdos_ren_file: + CALL bdos_reselect ; select drive + CALL bdos_rename ; rename file + JP bdos_ret_dirloc ; Return directory location in A + +; ------------------------------------------------------- +; Return bitmap of logged-in drives +; Out: bitmap in HL. +; ------------------------------------------------------- +bdos_get_login_vec: + LD HL, (CPM_VARS.bdos_dlog) + JP sthl_ret + +; ------------------------------------------------------- +; Return current drive +; Out: A - currently selected drive. 0 => A:, 1 => B: etc. +; ------------------------------------------------------- +bdos_get_cur_drive: + LD A, (CPM_VARS.bdos_curdsk) + JP bdos_ret_a + +; ------------------------------------------------------- +; Set DMA address +; Inp: DE - address of DMA buffer +; ------------------------------------------------------- +bdos_set_dma_addr: + EX DE, HL + LD (CPM_VARS.bdos_dmaad), HL + JP bdos_setdata + +; ------------------------------------------------------- +; fn27: Return the address of cur disk allocation map +; Out: HL - address +; ------------------------------------------------------- +bdos_get_alloc_addr: + LD HL, (CPM_VARS.bdos_alloca) + JP sthl_ret + +; ------------------------------------------------------- +; Get write protection status +; ------------------------------------------------------- +bdos_get_wr_protect: + LD HL, (CPM_VARS.bdos_rodsk) + JP sthl_ret + +; ------------------------------------------------------- +; Set file attributes (ro, system) +; ------------------------------------------------------- +bdos_set_attr: + CALL bdos_reselect + CALL bdos_update_attrs + JP bdos_ret_dirloc + +; ------------------------------------------------------- +; Return address of disk parameter block of the +; current drive +; ------------------------------------------------------- +bdos_get_dpb: + LD HL, (CPM_VARS.bdos_dpbaddr) + +; ------------------------------------------------------- +; Common code to return Value from BDOS functions +; ------------------------------------------------------- +sthl_ret: + LD (CPM_VARS.bdos_aret), HL + RET + +; ------------------------------------------------------- +; Get/set user number +; Inp: E - 0..15 - set user. 0xFF - get user +; Out: A - returns user number +; ------------------------------------------------------- +bdos_set_user: + LD A, (CPM_VARS.bdos_linfo) + CP 0xff + JP NZ, .set_user + LD A, (CPM_VARS.bdos_userno) + JP bdos_ret_a +.set_user: + AND 0x1f ; mask for 0..31 but will be < 16 + LD (CPM_VARS.bdos_userno), A + RET + +; ------------------------------------------------------- +; Random read. Record specified in the random record +; count area of the FCB, at the DMA address +; Inp: DE -> FCB +; Out: Error codes in BA and HL. +; ------------------------------------------------------- +bdos_rand_read: + CALL bdos_reselect ; select drive by FCB + JP bdos_rand_disk_read ; and read + +; ------------------------------------------------------- +; Random access write record. +; Record specified in the random record count area of the FCB, at the DMA address +; Inp: DE -> FCB +; Out: Error codes in BA and HL. +; ------------------------------------------------------- +bdos_rand_write: + CALL bdos_reselect ; select drive by FCB + JP bdos_rand_disk_write ; and write + +; ------------------------------------------------------- +; Compute file size. +; Set the random record count bytes of the FCB to the +; number of 128-byte records in the file. +; ------------------------------------------------------- +bdos_compute_fs: + CALL bdos_reselect ; select drive by FCB + JP bdos_get_file_size ; and get file size + +; ------------------------------------------------------- +; Selectively logoff (reset) disc drives +; Out: A=0 - Ok, 0xff if error +; ------------------------------------------------------- +bdos_reset_drives: + LD HL, (CPM_VARS.bdos_info) ; HL - drives to logoff map + LD A, L + CPL + LD E, A + LD A, H + CPL + LD HL, (CPM_VARS.bdos_dlog) ; get vector + AND H ; reset in hi byte + LD D, A ; and store in D + LD A, L ; reset low byte + AND E + LD E, A ; and store in E + LD HL, (CPM_VARS.bdos_rodsk) ; HL - disk RO map + EX DE, HL + LD (CPM_VARS.bdos_dlog), HL ; store new login vector + ; reset RO flags + LD A, L + AND E + LD L, A + LD A, H + AND D + LD H, A + ; and store new value + LD (CPM_VARS.bdos_rodsk), HL + RET + +; ------------------------------------------------------- +; Reach this point at the end of processing +; to return the data to the user. +; ------------------------------------------------------- +bdos_goback: + LD A, (CPM_VARS.bdos_resel) ; check for selection drive by user FCB + OR A + JP Z, bdos_ret_mon ; return if not selected + + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB_DR + LD (HL), 0 + LD A, (CPM_VARS.bdos_fcbdsk) + OR A ; restore drive + JP Z, bdos_ret_mon ; return if default + LD (HL), A ; restore + LD A, (CPM_VARS.bdos_olddsk) ; previous drive + LD (CPM_VARS.bdos_linfo), A ; set parameter + CALL bdos_select_disk ; select previous drive + +; ------------------------------------------------------- +; Return from the disk monitor +; ------------------------------------------------------- +bdos_ret_mon: + LD HL, (CPM_VARS.bdos_usersp) ; return user stack + LD SP, HL + LD HL, (CPM_VARS.bdos_aret) ; get return value + LD A, L ; ver 1.4 CP/M compatibility + LD B, H + RET + +; ------------------------------------------------------- +; Random disk write with zero fill of +; unallocated block +; ------------------------------------------------------- +bdos_rand_write_z: + CALL bdos_reselect ; select drive + LD A, 2 ; special write mode + LD (CPM_VARS.bdos_seqio), A + LD C, FALSE ; write indicator + CALL bdos_rseek1 ; seek position in a file + CALL Z, bdos_disk_write ; write if no errors + RET + +; ------------------------------------------------------- +; Unuser initialized data +; ------------------------------------------------------- +filler: + DB 0xF1, 0xE1 ; POP AF, POP HL + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xC800 +FILL_SIZE EQU 0xE00-CODE_SIZE + + DISPLAY "| BDOS\t| ",/H,bdos_start," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ASSERT bdos_rand_write_z = 0xd55e + + +FILLER + DS FILL_SIZE, 0xFF + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_bc0695e4/bdos.inc b/CPM_v2.2_r8_bc0695e4/bdos.inc new file mode 100644 index 0000000..c5ae655 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/bdos.inc @@ -0,0 +1,54 @@ +; ======================================================= +; Ocean-240.2 +; CP/M v2.2. Equates to call BDOS functions +; +; Created by Romych 2026-02-16 +; ======================================================= + + + IFNDEF _BDOS_INC + DEFINE _BDOS_INC + +P_TERMCPM EQU 0 ; System Reset +C_READ EQU 1 ; Console input +C_WRITE EQU 2 ; Console output +A_READ EQU 3 ; Auxiliary (Reader) input +A_WRITE EQU 4 ; Auxiliary (Punch) output +L_WRITE EQU 5 ; Printer output +C_RAWIO EQU 6 ; Direct console I/O +A_STATIN EQU 7 ; Auxiliary Input status +A_STATOUT EQU 8 ; Auxiliary Output status +C_WRITESTR EQU 9 ; Output string +C_READSTR EQU 10 ; Buffered console input +C_STAT EQU 11 ; Console status +S_BDOSVER EQU 12 ; Return version number +DRV_ALLRESET EQU 13 ; Reset discs +DRV_SET EQU 14 ; Select disc +F_OPEN EQU 15 ; Open file +F_CLOSE EQU 16 ; Close file +F_SFIRST EQU 17 ; search for first +F_SNEXT EQU 18 ; search for next +F_DELETE EQU 19 ; delete file +F_READ EQU 20 ; read next record +F_WRITE EQU 21 ; write next record +F_MAKE EQU 22 ; create file +F_RENAME EQU 23 ; Rename file +DRV_LOGINVEC EQU 24 ; Return bitmap of logged-in drives +DRV_GET EQU 25 ; Return current drive +F_DMAOFF EQU 26 ; Set DMA address +DRV_ALLOCVEC EQU 27 ; Return address of allocation map +DRV_SETRO EQU 28 ; Software write-protect current disc +DRV_ROVEC EQU 29 ; Return bitmap of read-only drives +F_ATTRIB EQU 30 ; set file attributes +DRV_DPB EQU 31 ; get DPB address +F_USERNUM EQU 32 ; get/set user number +F_READRAND EQU 33 ; Random access read record +F_WRITERAND EQU 34 ; Random access write record +F_SIZE EQU 35 ; Compute file size +F_RANDREC EQU 36 ; Update random access pointer +DRV_RESET EQU 37 ; Selectively reset disc drives +DRV_ACCESS EQU 38 ; Access drives +DRV_FREE EQU 39 ; Free drive +F_WRITEZF EQU 40 ; Write random with zero fill + + ENDIF diff --git a/CPM_v2.2_r8_bc0695e4/bios.asm b/CPM_v2.2_r8_bc0695e4/bios.asm new file mode 100644 index 0000000..dedb8dc --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/bios.asm @@ -0,0 +1,823 @@ +; ======================================================= +; Ocean-240.2 +; +; CP/M BIOS +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "mon_entries.inc" + + IFNDEF BUILD_ROM + OUTPUT bios.bin + ENDIF + + MODULE BIOS + + ORG 0xD600 + +; ------------------------------------------------------- +; BIOS JUMP TABLE +; ------------------------------------------------------- +boot_f: + JP bios_boot +wboot_f: + JP bios_wboot + +; ------------------------------------------------------- +; console status to reg-a +; ------------------------------------------------------- +const_f: + JP MON.non_con_status + +; ------------------------------------------------------- +; console character to reg-a +; ------------------------------------------------------- +conin_f: + JP MON.mon_con_in + +; ------------------------------------------------------- +; console character from c to console out +; ------------------------------------------------------- +conout_f: + JP MON.mon_con_out + +; ------------------------------------------------------- +; list device out +; ------------------------------------------------------- +list_f: + JP MON.mon_char_print + +; ------------------------------------------------------- +; punch device out +; ------------------------------------------------------- +punch_f: + JP MON.mon_serial_out + +; ------------------------------------------------------- +; reader character in to reg-a +; ------------------------------------------------------- +reader_f: + JP MON.mon_serial_in + +; ------------------------------------------------------- +; move to home position, treat as track 00 seek +; ------------------------------------------------------- +home_f: + JP home + +; ------------------------------------------------------- +; select disk given by register c +; ------------------------------------------------------- +seldsk_f: + JP sel_disk +settrk_f: + JP set_trk +setsec_f: + JP set_sec + +; ------------------------------------------------------- +; Set DMA address from BC +; ------------------------------------------------------- +setdma_f: + JP set_dma +read_f: + JP read +write_f: + JP write +listst_f: + JP list_st +sectran_f: + JP sec_tran + +; ------------------------------------------------------- +; Reserved +; ------------------------------------------------------- + JP warm_boot + JP warm_boot + +; ------------------------------------------------------- +; Tape read +; ------------------------------------------------------- +tape_read_f: + JP MON.mon_tape_read + +; ------------------------------------------------------- +; Tape write +; ------------------------------------------------------- +tape_write_f: + JP MON.mon_tape_write + +; ------------------------------------------------------- +; Tape wait block +; ------------------------------------------------------- +tape_wait_f: + JP MON.mon_tape_wait + + JP warm_boot ; r8 + +disk_a_size DW 0x00C0 ; 192k disk A size +disk_b_size DW 0x02d0 ; 720 disk B size +disk_c_size DW 0x02d0 ; 720 disk C size +bios_var04 DB 0x50 +bios_var05 DB 0x50 + +; ------------------------------------------------------- +; cold start +; ------------------------------------------------------- +bios_boot: + LD HL, (bdos_ent_addr) ; 0xba06 + LD DE, 0x10000-CCP_RAM.bdos_enter_jump ; 0x45fa + ADD HL, DE ; 0xba06+0x45fa=10000 + LD A, H + OR L + JP Z, bios_signon + + ; >> r8 + LD HL, (bios_var0) ; if bios_var0 = 0xaaaa (initialized) + LD DE, 0x5556 + ADD HL, DE ; 0xaaaa+0x5556=0x10000 if initialized + LD A, H + OR L + JP Z, bios_signon ; if initialized, go to logon, skip init + ; << + + ; Init DMA buffer + LD HL, dma_buffer ; 0x8000 + LD B, DMA_BUFF_SIZE ; 0x80 +.init_dma_buff: + LD (HL), FILE_DELETED ; 0xE5 + INC HL + DEC B + JP NZ, .init_dma_buff + + ; Init RAM disk + LD HL, dma_buffer + LD DE, 0x0000 + LD B, 8 + +.init_ram_dsk: + PUSH BC + CALL MON.ram_disk_write + POP BC + INC DE + DEC B + JP NZ, .init_ram_dsk + + ; Init user to 0 and drive to 0 + XOR A + LD (cur_user_drv), A + + ; init bios variables + CALL bios_init_ilv ; r8 + +bios_signon: + LD SP, bios_stack + ; Print CP/M hello message + LD HL, msg_hello + CALL print_strz + JP bios_wboot + +;bios_wboot: +; LD SP, bios_stack + +; r8 >> +bios_init_ilv: + LD HL, bios_var0 + LD DE, bios_ini_vals + LD C, 13 + CALL mov_dlhe_c + LD A, (bios_var05) ; 0x50 + LD (bios_var2), A + RET +; << + +; ------------------------------------------------------- +; BIOS Warm start entry +; ------------------------------------------------------- +bios_wboot: ; r8 + + LD HL, (bios_var0) + LD DE, 0x5556 ; 0xaaaa + 0x5556 = 0x10000 + ADD HL, DE + LD A, H + OR L + CALL NZ, bios_init_ilv ; init if not initialized before + LD SP, bios_stack + + ; Move CPP from 0xC000 to 0xB200 + LD HL, CCP_DST_ADDR + LD DE, CCP_SRC_ADDR + LD BC, CCP_SIZE +.move_ccp: + LD A, (DE) + LD (HL), A + INC DE + INC HL + DEC BC + LD A, B + OR C + JP NZ, .move_ccp + + ; Init variables with zeroes + LD HL, CPM_VARS.cpm_vars_start + LD BC, CPM_VARS.ccp_vars_size ; 213 + +.clr_cpm_vars: + LD (HL), 0x0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .clr_cpm_vars + + LD A, FILE_DELETED + LD (CPM_VARS.bdos_efcb), A ; mark empty FCB + LD A, low dma_buffer ; 0x80 + LD (CPM_VARS.bdos_dmaad), A ; 0x0080 + + ; Move DPH + DPB to RAM + LD HL, CPM_VARS.DPH_RAM + LD DE, dph_disk_a + LD BC, DPB_END-dph_disk_a ; 0x39 -> 57d + +.move_dph: + LD A, (DE) + LD (HL), A + INC HL + INC DE + DEC BC + LD A, B + OR C + JP NZ, .move_dph + + LD BC, dma_buffer ; DMA default buffer addr + CALL setdma_f + + ; Setup JP to Warm boot after CPU reset + LD A, JP_OPCODE + LD (warm_boot), A + LD HL, wboot_f + LD (warm_boot_addr), HL + + ; Setup JP to BDOS entrance + LD (jp_bdos_enter), A + LD HL, CCP_RAM.bdos_enter_jump + LD (bdos_ent_addr), HL + + LD HL, CPM_VARS.DPB_A_RAM + LD C, 0xf + LD DE, dpb_ram + LD A, (disk_a_size+1) + OR A + JP Z, set_curr_dpb + LD DE, dpb_empty + +set_curr_dpb: + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_B_RAM + LD C, 0xf + LD DE, dpb_flop_360k + LD A, (disk_b_size+1) + CP 0x1 + JP Z, .l1 + LD DE, dpb_flop_720k +.l1 + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_C_RAM + LD C, 0xf + LD A, (disk_c_size+1) + CP 0x2 + JP Z, .l2 + LD DE, dpb_flop_360k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_720k + JP .l3 +.l2 + LD DE, dpb_flop_720k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_360k +.l3 + CALL mov_dlhe_c + XOR A + LD (CPM_VARS.slicer_has_data),A + LD (CPM_VARS.slicer_uninited_count),A + LD A,(cur_user_drv) + LD C,A + ; go to CCP + JP CCP_DST_ADDR + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +mov_dlhe_c: + LD A,(DE) ; [DE]->[HL] + LD (HL),A + INC HL + INC DE + DEC C + JP NZ, mov_dlhe_c + RET + +list_st: + XOR A + RET + +; ------------------------------------------------------- +; Select disk +; Inp: C - disk, +; E - active drive flag +; Out: HL -> DPH +; ------------------------------------------------------- +sel_disk: + LD HL, 0x00 + LD A, C + CP CTRL_C + RET NC + + LD (CPM_VARS.cur_disk), A + OR A + JP Z, .get_dph_addr ; skip next for disk 0 - RAM disk + LD A, E ; selected disk map + AND 0x1 ; bit 0 is set if disk already selected + JP NZ, .get_dph_addr + ; Reset disk + LD (CPM_VARS.slicer_has_data), A + LD (CPM_VARS.slicer_uninited_count), A + ; calc DPH address of new drive +.get_dph_addr: + LD L, C + LD H, 0x0 + ADD HL, HL ; *2 + ADD HL, HL ; *4 + ADD HL, HL ; *8 + ADD HL, HL ; *16 (size of DBH) + LD DE, CPM_VARS.DPH_RAM + ADD HL, DE + RET + +; ------------------------------------------------------- +; move to track 00 +; ------------------------------------------------------- +home: + LD A, (CPM_VARS.cur_disk) + OR A + JP Z, .is_default + LD A, (CPM_VARS.slicer_need_save) + OR A + JP NZ, .is_default + LD (CPM_VARS.slicer_has_data), A ; set to 0, no data + +.is_default: + LD C, 0 ; set track to 0 + +; ------------------------------------------------------- +; set track address (0..76) for subsequent read/write +; ------------------------------------------------------- +set_trk: + LD HL, CPM_VARS.curr_track + LD (HL), C + RET + +; ------------------------------------------------------- +; set sector address (1,..., 26) for subsequent read/write +; ------------------------------------------------------- +set_sec: + LD HL, CPM_VARS.curr_sec + LD (HL), C + RET + +; ------------------------------------------------------- +; set subsequent dma address (initially 80h) +; ------------------------------------------------------- +set_dma: + LD L, C + LD H, B + LD (CPM_VARS.dma_addr), HL + RET + +sec_tran: + LD L, C + LD H, B + RET + +; ------------------------------------------------------- +; read track/sector to preset dma address +; ------------------------------------------------------- +read: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ, read_phys ; for physical disk use special routine + CALL ram_disk_calc_addr + CALL MON.ram_disk_read + XOR A + RET + +; ------------------------------------------------------- +; write track/sector from preset dma address +; ------------------------------------------------------- +write: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ,write_phys + CALL ram_disk_calc_addr + CALL MON.ram_disk_write + XOR A + RET + +; ------------------------------------------------------- +; Calculate address for current sector and track +; ------------------------------------------------------- +ram_disk_calc_addr: + LD HL, CPM_VARS.curr_track + ; HL = cur_track * 16 + LD L, (HL) + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ; DE = HL + cur_sec + EX DE, HL + LD HL, CPM_VARS.curr_sec + LD L, (HL) + LD H, 0x0 + ADD HL, DE + EX DE, HL + ; store address + LD HL, (CPM_VARS.dma_addr) + RET + +read_phys: + CALL read_phys_op + RET + +write_phys: + CALL write_phys_op + RET + +read_phys_op: + XOR A + ; reset counter + LD (CPM_VARS.slicer_uninited_count), A + LD A, 0x1 + LD (CPM_VARS.tmp_slicer_operation), A ; 0 - write; 1 - read + LD (CPM_VARS.tmp_slicer_can_read), A ; enable read fron disk + LD A, 0x2 + LD (CPM_VARS.tmp_slicer_flush), A ; disable flush data to disk + JP base_read_write + +write_phys_op: + XOR A + LD (CPM_VARS.tmp_slicer_operation), A + LD A, C + LD (CPM_VARS.tmp_slicer_flush), A + CP 0x2 + JP NZ, .mode_ne_2 + LD A, 0x10 ; 2048/128 + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_uninited_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_uninited_track), A + LD A, (CPM_VARS.curr_sec) + LD (CPM_VARS.slicer_uninited_sector_128), A +.mode_ne_2: + LD A, (CPM_VARS.slicer_uninited_count) + OR A + JP Z, slicer_read_write + DEC A + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_uninited_disk + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_uninited_track + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_sec) + LD HL, CPM_VARS.slicer_uninited_sector_128 + CP (HL) + JP NZ, slicer_read_write + INC (HL) + LD A, (HL) + CP 36 ; Sectors per track + JP C, .no_inc_track + LD (HL), 0x0 + LD A, (CPM_VARS.slicer_uninited_track) + INC A + LD (CPM_VARS.slicer_uninited_track), A + +.no_inc_track: + XOR A + LD (CPM_VARS.tmp_slicer_can_read), A + JP base_read_write + +; -------------------------------------------------- +slicer_read_write: + XOR A + LD (CPM_VARS.slicer_uninited_count), A + INC A + LD (CPM_VARS.tmp_slicer_can_read), A + +; -------------------------------------------------- +base_read_write: + XOR A + LD (CPM_VARS.tmp_slicer_result), A + LD A, (CPM_VARS.curr_sec) + OR A + RRA + OR A + RRA + LD (CPM_VARS.tmp_slicer_real_sector), A + LD HL, CPM_VARS.slicer_has_data + LD A, (HL) + LD (HL), 0x1 + OR A + JP Z, .no_data + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_disk + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_track + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD HL, CPM_VARS.slicer_real_sector + CP (HL) + JP Z,calc_sec_addr_in_bfr +.pos_diff: + LD A, (CPM_VARS.slicer_need_save) + OR A + CALL NZ, slicer_save_buffer ; save buffer if needed +.no_data: + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_track), A + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD (CPM_VARS.slicer_real_sector), A + LD A, (CPM_VARS.tmp_slicer_can_read) + OR A + CALL NZ,slicer_read_buffer + XOR A + LD (CPM_VARS.slicer_need_save), A + +; -------------------------------------------------- +calc_sec_addr_in_bfr: + LD A, (CPM_VARS.curr_sec) + AND 0x3 + LD L, A + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL ; *128 + LD DE, CPM_VARS.slicer_buffer + ADD HL, DE + EX DE, HL + LD HL, (CPM_VARS.dma_addr) + LD C, 0x80 + LD A, (CPM_VARS.tmp_slicer_operation) + OR A + JP NZ, .no_save + LD A, 0x1 + LD (CPM_VARS.slicer_need_save), A + EX DE, HL +.no_save: + LD A, (DE) + INC DE + LD (HL), A + INC HL + DEC C + JP NZ, .no_save + LD A, (CPM_VARS.tmp_slicer_flush) + CP 0x1 + LD A, (CPM_VARS.tmp_slicer_result) + RET NZ + OR A + RET NZ + XOR A + LD (CPM_VARS.slicer_need_save), A + CALL slicer_save_buffer + LD A, (CPM_VARS.tmp_slicer_result) + RET + +; -------------------------------------------------- +slicer_save_buffer: + CALL slicer_get_floppy_args + LD C, 0xA6 ; VG93 CMD + CALL MON.write_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_read_buffer: + CALL slicer_get_floppy_args + LD C, 0x86 ; VG93 CMD + CALL MON.read_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_get_floppy_args: + LD HL, CPM_VARS.tmp_buff9 + LD A, (CPM_VARS.slicer_disk) + DEC A + JP Z, .non_interleave + LD HL, interleave_0 +.non_interleave + LD A, (CPM_VARS.slicer_real_sector) + ADD A, L + LD L, A + LD E, (HL) + LD A, (CPM_VARS.slicer_track) + LD D, A + LD HL, CPM_VARS.slicer_buffer + LD A, (CPM_VARS.slicer_disk) + RET + + +; ------------------------------------------------------- +; Print zerro ended string +; Inp: HL -> string +; ------------------------------------------------------- +print_strz: + LD A, (HL) + OR A + RET Z + LD C, A + PUSH HL + CALL conout_f + POP HL + INC HL + JP print_strz + +msg_hello: + DB ASCII_ESC, "60" ; Режим 32x18 60 + DB ASCII_ESC, "83" + DB ASCII_ESC, "5!%" + DB ASCII_ESC, "41" + DB ASCII_ESC, "1", 0x16, 0xe2, 0xe2, 0xfc, 0x01 + DB ASCII_ESC, "40" + DB ASCII_ESC, "1", 0x1e, 0xe6, 0xdb, 0xf8, 0x01 + DB ASCII_ESC, "43" + DB "OKEAH-240 CP/M (V2.2) REL.8'\r\n\n" + DB ASCII_ESC, "42", 0 + + +; -------------------------------------------------- +; Disk parameters blocks in ROM (DPBs) +; Tables of memory that describe the characteristics +; of discs on our system. There is one DPB for each +; disc type + +; ---------------------------------------- +; Block size | No of sectors | BSH | BLM | +; ---------------------------------------- +; 1K | 8 | 3 | 7 | +; 2K | 16 | 4 | 15 | +; 4K | 32 | 5 | 31 | +; 8K | 64 | 6 | 63 | +; 16K | 128 | 7 | 127 | +; ---------------------------------------- + +; ------------------------------------- +; Block size| Extent mask (EXM) | +; | Small disk | Large disk | +; ------------------------------------- +; 2K | 1 | 0 | +; 4K | 3 | 1 | +; 8K | 7 | 3 | +; 16K | 15 | 7 | +; ------------------------------------- +; CKS - number of dir sectors to check before write, 0 for HDD + +; For RAM-Disk 128k +dpb_ram: + DW 0010h ; SPT Sector (128b) per track (16d) + DB 03h ; BSH 1k + DB 07h ; BLM 1k; Allocation block size = (BLM + 1) * 128 = 1k + DB 00h ; EXM extent mask + DW 191 ; DSM Disk size blocks - 1 + DW 31 ; DRM Dir elements - 1 + DB 10000000b ; AL0 Dir map byte 1 + DB 00000000b ; AL1 Dir map byte 2 + DW 0008h ; CKS checksum vector size (8 sectors=1k) + DW 0000h ; OFF (tracks reserved for system) + +dpb_empty: + DS 15, 0xff + +; For FLOPPY 720k +dpb_flop_720k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 00h ; EXM extent mask + DW 359 ; DSM Disk size blocks - 1 (359d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +; For FLOPPY 360k +dpb_flop_360k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 01h ; EXM extent mask + DW 179 ; DSM Disk size blocks - 1 (179d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +bios_ini_vals: + DB 0xaa, 0xaa, 0, 0xff, 1, 8, 6, 4, 2, 9, 7, 5, 3 + +; -------------------------------------------------- +; Disk parameters headers in ROM +; -------------------------------------------------- +; Disk A RAM +dph_disk_a: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + ; 0xda00 + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_A_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_A ; Check Vector pointer + DW CPM_VARS.AL_MAP_A ; Allocation map pointer + +; Disk B Floppy +dph_disk_b: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_B_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_B ; Check Vector pointer + DW CPM_VARS.AL_MAP_B ; Allocation map pointer + +; Disk C Floppy +dph_disk_c: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_C_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_C ; Check Vector pointer + DW CPM_VARS.AL_MAP_C ; Allocation map pointer + + + +res_data: ; 0xda28 + DB 1,8,6,4,2,9,7,5,3 +DPB_END EQU $ + DB 0x0e, 3 + + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xD600 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| BIOS\t| ",/H,boot_f," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT bios_wboot = 0xd6a8 + ASSERT set_curr_dpb = 0xd722 + ASSERT sel_disk = 0xd781 + ASSERT home = 0xd7a7 + ASSERT ram_disk_calc_addr = 0xd7eb + ASSERT write_phys_op = 0xd81e + ASSERT base_read_write = 0xd88a + ASSERT calc_sec_addr_in_bfr = 0xd8e4 + ASSERT slicer_save_buffer = 0xd928 + ASSERT print_strz = 0xd95e + ASSERT msg_hello = 0xd96b + ASSERT dpb_ram = 0xd9af + ASSERT dph_disk_a = 0xd9f8 + ASSERT res_data = 0xda28 + +FILLER: + DS FILL_SIZE, 0xff + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_bc0695e4/ccp_ram.asm b/CPM_v2.2_r8_bc0695e4/ccp_ram.asm new file mode 100644 index 0000000..1d24e8b --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/ccp_ram.asm @@ -0,0 +1,1529 @@ +; ====================================================== +; Ocean-240.2 +; CP/M V2.2 R8. +; CCP (Console Command Processor) Resident part +; ORG C000 at RPM, moved to B200-BA09 +; +; Disassembled by Romych 2026-02-01 +; ====================================================== + + INCLUDE "ram.inc" + + IFNDEF BUILD_ROM + OUTPUT ccp_ram.bin + ENDIF + + MODULE CCP_RAM + + ORG 0xB200 + +ccp_ram_ent: + JP ccp_ent + JP ccp_clear_buff + +ccp_inbuff: + DB 0x7F, 0x00 + +ccp_inp_line: +hex_length: + DB ASCII_SP, ASCII_SP + +msg_copyright: +hex_sectors: + DB " " +hex_buff: + + DB " COPYRIGHT (C) 1979, DIGITAL RESEARCH ", 0x00 + DS 73, 0x0 + +ccp_inp_line_addr: + DW ccp_inp_line + +cur_name_ptr: + DW 0h + +; --------------------------------------------------- +; Call BDOS function 2 (C_WRITE) - Console output +; Inp: A - char to output +; --------------------------------------------------- +ccp_print: + LD E, A + LD C, C_WRITE + JP jp_bdos_enter + +; --------------------------------------------------- +; Put char to console +; Inp: A - char +; --------------------------------------------------- +ccp_putc: + PUSH BC + CALL ccp_print + POP BC + RET + +ccp_out_crlf: + LD A, ASCII_CR + CALL ccp_putc + LD A, ASCII_LF + JP ccp_putc +ccp_out_space: + LD A,' ' + JP ccp_putc + +; --------------------------------------------------- +; Out message from new line +; Inp: BC -> Message +; --------------------------------------------------- +ccp_out_crlf_msg: + PUSH BC + CALL ccp_out_crlf + POP HL + +; --------------------------------------------------- +; Out asciiz message +; Inp: HL -> Message +; --------------------------------------------------- +ccp_out_msg: + LD A, (HL) ;= "READ ERROR" + OR A + RET Z + INC HL + PUSH HL + CALL ccp_print + POP HL + JP ccp_out_msg + +; --------------------------------------------------- +; Call BDOS function 13 (DRV_ALLRESET) - Reset discs +; --------------------------------------------------- +ccp_bdos_drv_allreset: + LD C, DRV_ALLRESET + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS function 14 (DRV_SET) - Select disc +; Inp: A - disk +; --------------------------------------------------- +ccp_bdos_drv_set: + LD E, A + LD C, DRV_SET + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS fn and return result +; Inp: C - fn no +; Out: A - error + 1 +; --------------------------------------------------- +ccp_call_bdos: + CALL jp_bdos_enter + LD (ccp_bdos_result_code), A + INC A + RET + +; --------------------------------------------------- +; BDOS function 15 (F_OPEN) - Open file /Dir +; Inp: DE -> FCB +; Out: A=0 for error, or 1-4 for success +; --------------------------------------------------- +ccp_bdos_open_f: + LD C, 15 + JP ccp_call_bdos + +; --------------------------------------------------- +; Open file by current FCB +; --------------------------------------------------- +ccp_open_cur_fcb: + XOR A + LD (ccp_current_fcb_cr), A ; clear current record counter + LD DE, ccp_current_fcb + JP ccp_bdos_open_f + +; --------------------------------------------------- +; BDOS function 16 (F_CLOSE) - Close file +; --------------------------------------------------- +ccp_bdos_close_f: + LD C, 16 + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 17 (F_SFIRST) - search for first +; Out: A = 0 in error, 1-4 if success +; --------------------------------------------------- +ccp_bdos_find_first: + LD C, 17 + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 18 (F_SNEXT) - search for next +; Out: A = 0 in error, 1-4 if success +; --------------------------------------------------- +ccp_bdos_find_next: + LD C, 18 + JP ccp_call_bdos ; BDOS 18 (F_SNEXT) - search for next ? + +; --------------------------------------------------- +; Call BDOS F_FIRST with current FCB +; --------------------------------------------------- +ccp_find_first: + LD DE, ccp_current_fcb + JP ccp_bdos_find_first + +; --------------------------------------------------- +; Call BDOS function 19 (F_DELETE) - delete file +; --------------------------------------------------- +ccp_bdos_era_file: + LD C, F_DELETE + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS and set ZF by result +; --------------------------------------------------- +ccp_bdos_enter_zf: + CALL jp_bdos_enter + OR A + RET + +; --------------------------------------------------- +; Read next 128 bytes of file +; Inp: DE -> FCB +; Out: a = 0 - ok; +; 1 - EOF; +; 9 - invalid FCB; +; 10 - Media changed; +; 0xFF - HW error. +; --------------------------------------------------- +ccp_bdos_read_f: + LD C, F_READ + JP ccp_bdos_enter_zf + +; --------------------------------------------------- +; Read file by current FCB +; --------------------------------------------------- +ccp_read_f_fcb: + LD DE, ccp_current_fcb + JP ccp_bdos_read_f + +; --------------------------------------------------- +; Call BDOS function 21 (F_WRITE) - write next record +; --------------------------------------------------- +ccp_bdos_f_write: + LD C, F_WRITE + JP ccp_bdos_enter_zf + +; --------------------------------------------------- +; Call BDOS function 22 (F_MAKE) - create file +; --------------------------------------------------- +ccp_bdos_make_f: + LD C, F_MAKE + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 23 (F_RENAME) - Rename file +; --------------------------------------------------- +ccp_bdos_rename_f: + LD C, F_RENAME + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS function 32 (F_USERNUM) for get user number +; Out: A - user no +; --------------------------------------------------- +ccp_bdos_get_user: + LD E, 0xff + +; --------------------------------------------------- +; Call BDOS function 32 (F_USERNUM) set user number +; Inp: E - user no 0-15, or 0xFF for get user +; Out: A - user no +; --------------------------------------------------- +ccp_bdos_set_user: + LD C, F_USERNUM + JP jp_bdos_enter + +; --------------------------------------------------- +; Get user no and store in upper nibble of sys var +; --------------------------------------------------- +ccp_set_cur_drv: + CALL ccp_bdos_get_user + ADD A, A + ADD A, A + ADD A, A + ADD A, A ; user no at upper nibble + LD HL, ccp_cur_drive + OR (HL) + LD (cur_user_drv), A + RET + +; --------------------------------------------------- +; Replace new drive in lower nibble of sys var +; reset user no +; Out: A - drive no +; --------------------------------------------------- +ccp_reset_cur_drv: + LD A, (ccp_cur_drive) + LD (cur_user_drv), A + RET + +; --------------------------------------------------- +; Uppercase character a..z +; Inp: A - character +; Out: A - character in upper case +; --------------------------------------------------- +char_to_upper: + CP 'a' + RET C + CP '{' + RET NC + ; character in a..z range + AND 0x5f + RET + +; --------------------------------------------------- +; Get user input from console or from batch +; file $$$.SUB +; --------------------------------------------------- +ccp_get_inp: + LD A, (ccp_batch) + OR A + JP Z, ccp_gin_no_batch + + ; select drive A for $$$.SUB + LD A, (ccp_cur_drive) + OR A + LD A, 0x0 + CALL NZ, ccp_bdos_drv_set + + ; open batch file + LD DE, ccp_batch_fcb + CALL ccp_bdos_open_f + JP Z, ccp_gin_no_batch ; go to con inp if file not found + + ; read last record + LD A, (ccp_batch_fcb_rc) + DEC A + LD (ccp_batch_fcb_cr), A + LD DE, ccp_batch_fcb + CALL ccp_bdos_read_f + JP NZ, ccp_gin_no_batch ; stop on EOF + + ; move data from file to buffer + LD DE, ccp_inbuff+1 + LD HL, dma_buffer + LD B, DMA_BUFF_SIZE ; 0x80 + CALL ccp_mv_hlde_b + LD HL, ccp_batch_fcb_s2 + LD (HL), 0x0 + INC HL + DEC (HL) ; decriment record count + ; close batch file + LD DE, ccp_batch_fcb + CALL ccp_bdos_close_f + JP Z, ccp_gin_no_batch + ; reselect old drive if not 0 + LD A, (ccp_cur_drive) + OR A + CALL NZ, ccp_bdos_drv_set + ; print command line + LD HL, ccp_inp_line ; ccp_inbuff+2 + CALL ccp_out_msg + CALL ccp_getkey_no_wait + JP Z, ccp_gin_nokey + ; terminate batch processing on any key + CALL ccp_del_batch + JP ccp_get_command + + ; get user input from keyboard +ccp_gin_no_batch: + CALL ccp_del_batch + CALL ccp_set_cur_drv + + LD C, C_READSTR + LD DE, ccp_inbuff + ; Call BDOS C_READSTR DE -> inp buffer + CALL jp_bdos_enter + + CALL ccp_reset_cur_drv + +ccp_gin_nokey: + LD HL, ccp_inbuff+1 + LD B, (HL) + +ccp_gin_uppr: + INC HL + LD A, B + OR A + JP Z, ccp_gin_uppr_end + LD A, (HL) ;= 2020h + CALL char_to_upper + LD (HL), A ;= 2020h + DEC B + JP ccp_gin_uppr + +ccp_gin_uppr_end: + LD (HL), A ; set last character to 0 + LD HL, ccp_inp_line ; + LD (ccp_inp_line_addr), HL ; + RET + +; --------------------------------------------------- +; Check keyboard +; Out: A - pressed key code +; ZF set if no key pressed +; --------------------------------------------------- +ccp_getkey_no_wait: + LD C, C_STAT + ; Call BDOS (C_STAT) - Console status + CALL jp_bdos_enter + OR A + RET Z ; ret if no character waiting + LD C, C_READ + ; Call BDOS (C_READ) - Console input + CALL jp_bdos_enter + OR A + RET + +; --------------------------------------------------- +; Call BDOS function 25 (DRV_GET) - Return current drive +; Out: A - drive 0-A, 1-B... +; --------------------------------------------------- +ccp_bdos_drv_get: + LD C, DRV_GET + JP jp_bdos_enter + +; --------------------------------------------------- +; Set disk buffer address to default buffer +; --------------------------------------------------- +cpp_set_disk_buff_addr: + LD DE, dma_buffer + +; --------------------------------------------------- +; Call BDOS function 26 (F_DMAOFF) - Set DMA address +; Inp: DE - address +; --------------------------------------------------- +ccp_bdos_dma_set: + LD C, F_DMAOFF + JP jp_bdos_enter + +; --------------------------------------------------- +; Delete batch file created by submit +; --------------------------------------------------- +ccp_del_batch: + LD HL, ccp_batch + LD A, (HL) + OR A + RET Z ; return if no active batch file + LD (HL), 0x0 ; mark as inactive + XOR A + CALL ccp_bdos_drv_set ; select drive 0 + LD DE, ccp_batch_fcb + CALL ccp_bdos_era_file + LD A, (ccp_cur_drive) + JP ccp_bdos_drv_set + +; -------------------------------------------------- +; Check "serial number" of CP/M +; -------------------------------------------------- +ccp_verify_pattern: + LD DE, cpm_pattern ; = F9h + LD HL, 0x0000 + LD B, 6 + +ccp_chk_patt_nex: + LD A, (DE) + CP (HL) + NOP ; JP NZ HALT was here + NOP + NOP + INC DE + INC HL + DEC B + JP NZ, ccp_chk_patt_nex + RET + +; -------------------------------------------------- +; Print syntax error indicator +; -------------------------------------------------- +print_syn_err: + CALL ccp_out_crlf + LD HL, (cur_name_ptr) +pse_next: + LD A, (HL) + CP ASCII_SP + JP Z, pse_end + OR A + JP Z, pse_end + PUSH HL + + CALL ccp_print + POP HL + INC HL + JP pse_next +pse_end: + LD A, '?' + CALL ccp_print + CALL ccp_out_crlf + CALL ccp_del_batch + JP ccp_get_command + +; -------------------------------------------------- +; Check user input characters for legal range +; Inp: [DE] - pointer to character +; -------------------------------------------------- +cpp_valid_inp: + LD A, (DE) + OR A + RET Z + CP ASCII_SP ; >= Space + JP C, print_syn_err + RET Z + CP '=' + RET Z + CP '_' + RET Z + CP '.' + RET Z + CP ':' + RET Z + CP ';' + RET Z + CP '<' + RET Z + CP '>' + RET Z + RET + +; --------------------------------------------------- +; Find non space character until end +; Inp: DE -> current character +; Out: DE -> non space +; A - character +; ZF set on EOL +; --------------------------------------------------- +ccp_find_no_space: + LD A, (DE) + OR A + RET Z + CP ASCII_SP + RET NZ + INC DE + JP ccp_find_no_space + +; --------------------------------------------------- +; HL=HL+A +; --------------------------------------------------- +sum_hl_a: + ADD A, L + LD L, A + RET NC + INC H ; inc H if CF is set + RET + +; --------------------------------------------------- +; Convert first name to fcb +; --------------------------------------------------- +ccp_cv_first_to_fcb: + LD A, 0x0 + +; Convert filename from cmd to fcb +; replace '*' to '?' +; Inp: A - offset in fcb filename +; Out: A - count of '?' in file name +; ZF is set for fegular file name +ccp_cv_fcb_filename: + LD HL, ccp_current_fcb + CALL sum_hl_a + PUSH HL + PUSH HL + XOR A + LD (ccp_chg_drive), A + LD HL, (ccp_inp_line_addr) ; HL -> input line + EX DE, HL + CALL ccp_find_no_space ; get next non blank char + EX DE, HL + LD (cur_name_ptr), HL ; save name ptr + EX DE, HL + POP HL + LD A, (DE) ; load first name char + OR A + JP Z, cur_cvf_n_end + SBC A, 'A'-1 ; 0x40 for drive letter + LD B, A + INC DE + LD A, (DE) + CP ':' ; is ':' after drive letter? + JP Z, cur_cvf_drv_ltr + DEC DE ; no, step back + +cur_cvf_n_end: + LD A, (ccp_cur_drive) + LD (HL), A + JP cur_cvf_basic_fn + +cur_cvf_drv_ltr: + LD A, B + LD (ccp_chg_drive), A ; set change drive flag + LD (HL), B + INC DE + +cur_cvf_basic_fn: + LD B, 8 ; file name length + +cur_cvf_chr_nxt: + CALL cpp_valid_inp + JP Z, cur_cvf_sp_remains + INC HL + CP '*' + JP NZ, cur_cvf_no_star + LD (HL), '?' + JP cur_cvf_nxt1 + +cur_cvf_no_star: + LD (HL), A + INC DE + +cur_cvf_nxt1: + DEC B + JP NZ, cur_cvf_chr_nxt + +cur_cvf_nxt_delim: + CALL cpp_valid_inp + JP Z, cur_cvf_ext + INC DE + JP cur_cvf_nxt_delim + + ; fill remains with spaces +cur_cvf_sp_remains: + INC HL + LD (HL), ASCII_SP + DEC B + JP NZ, cur_cvf_sp_remains + + ; file name extension +cur_cvf_ext: + LD B, 3 + CP '.' + JP NZ, cur_cvf_ext_fill_sp + INC DE + ; handle current ext char +cur_cvf_ext_nxt: + CALL cpp_valid_inp + JP Z, cur_cvf_ext_fill_sp + INC HL + ; change * to ? + CP '*' + JP NZ, cur_cvf_no_star2 + LD (HL), '?' + JP cur_cvf_nxt2 +cur_cvf_no_star2: + LD (HL), A + INC DE +cur_cvf_nxt2: + DEC B + JP NZ, cur_cvf_ext_nxt + +cur_cvf_ext_skip: + CALL cpp_valid_inp + JP Z, cur_cvf_rst_attrs + INC DE + JP cur_cvf_ext_skip + + ; skip remains ext pos with dpaces +cur_cvf_ext_fill_sp: + INC HL + LD (HL), ASCII_SP + DEC B + JP NZ, cur_cvf_ext_fill_sp + + ; set fcb extent, res1, res2 to 0 +cur_cvf_rst_attrs: + LD B, 3 + +cur_cvf_attrs_nxt: + INC HL + LD (HL), 0x0 + DEC B + JP NZ, cur_cvf_attrs_nxt + + EX DE, HL + LD (ccp_inp_line_addr), HL ; save input line pointer + + ; Check for ambigeous file name + POP HL ; -> file name in fcb + LD BC, 0x000b ; b=0, c=11 +cur_cvf_ambig: + INC HL + LD A, (HL) + CP '?' + JP NZ, cur_cvf_ambig_nxt1 + INC B +cur_cvf_ambig_nxt1: + DEC C + JP NZ, cur_cvf_ambig + LD A, B ; a = count of '?' + OR A ; set ZF if regular filename + RET + +; --------------------------------------------------- +; CP/M command table +; --------------------------------------------------- +cpm_cmd_tab: + DB "DIR " + DB "ERA " + DB "TYPE" + DB "SAVE" + DB "REN " + DB "USER" + +cpm_pattern: + DB 0xF9, 0x16, 0, 0, 0, 0x6B ; CP/M serial number? + ;LD SP, HL + ;LD D, 0x00 + ;NOP + ;NOP + ;LD L,E + +; --------------------------------------------------- +; Search for CP/M command +; Out: A - command number +; --------------------------------------------------- +ccp_search_cmd: + LD HL, cpm_cmd_tab + LD C, 0x0 + +ccp_sc_cmd_nxt: + LD A, C + CP CCP_COMMAND_CNT ; 6 + RET NC + LD DE, ccp_current_fcb_fn ; fcb filename + LD B, CCP_COMMAND_LEN ; max cmd len +ccp_sc_chr_nxt: + LD A, (DE) + CP (HL) ; compate fcb fn and command table + JP NZ, ccp_sc_no_match ; cmd not match + INC DE + INC HL + DEC B + JP NZ, ccp_sc_chr_nxt + ; last can be space for 3-letter commands + LD A, (DE) + CP ASCII_SP + JP NZ, ccp_sc_skip_cmd + LD A, C ; return command number in A + RET + + ; skip remains +ccp_sc_no_match: + INC HL + DEC B + JP NZ, ccp_sc_no_match + ; go to next cmd +ccp_sc_skip_cmd: + INC C + JP ccp_sc_cmd_nxt + +; -------------------------------------------------- +; Clear command buffer and go to command processor +; -------------------------------------------------- +ccp_clear_buff: + XOR A + LD (ccp_inbuff+1), A ; actual buffer len = 0 + +; -------------------------------------------------- +; Entrance to CCP +; Inp: C - current user * 16 +; -------------------------------------------------- +@ccp_ent: + LD SP, ccp_stack + PUSH BC ; + LD A, C + ; / 16 + RRA + RRA + RRA + RRA + ; cur user no in low nibble + AND 0x0f ; user 0..15 + LD E, A + CALL ccp_bdos_set_user + CALL ccp_bdos_drv_allreset + LD (ccp_batch), A + POP BC + LD A, C ; a = user*16 + AND 0xf ; low nibble - drive + LD (ccp_cur_drive), A + CALL ccp_bdos_drv_set + LD A, (ccp_inbuff+1) + OR A + JP NZ, ccp_process_cmd + +; -------------------------------------------------- +; Out prompt and get user command from console +; -------------------------------------------------- +ccp_get_command: + LD SP, ccp_stack ; reset stack pointer + CALL ccp_out_crlf ; from new line + CALL ccp_bdos_drv_get + ADD A, 65 ; convert drive no to character + CALL ccp_print ; print current drive letter + LD A, '>' ; and prompt + CALL ccp_print + CALL ccp_get_inp ; and wait string + +; -------------------------------------------------- +; Process command +; -------------------------------------------------- +ccp_process_cmd: + LD DE, dma_buffer + CALL ccp_bdos_dma_set ; setup buffer + CALL ccp_bdos_drv_get + LD (ccp_cur_drive), A ; store cur drive + CALL ccp_cv_first_to_fcb ; convert first command parameter to fcb + CALL NZ, print_syn_err ; if wildcard, out error message + LD A, (ccp_chg_drive) ; check drive change flag + OR A + JP NZ, ccp_unk_cmd ; if drive changed, handle as unknown command + CALL ccp_search_cmd ; ret A = command number + LD HL, ccp_cmd_addr + ; DE = command number + LD E, A + LD D, 0x0 + ADD HL, DE + ADD HL, DE ; HL = HL + 2*command_number + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + JP (HL) ; jump to command handler + +ccp_cmd_addr: + DW cmd_dir + DW cmd_erase + DW cmd_type + DW cmd_save + DW cmd_ren + DW cmd_user + DW ccp_entry + +; --------------------------------------------------- +; di+halt if serial number validation failed +; --------------------------------------------------- +cpp_halt: + LD HL, 0x76f3 + LD (ccp_ram_ent), HL + LD HL, ccp_ram_ent + JP (HL) + +ccp_type_rd_err: + LD BC, .msg_read_error ; BC -> "READ ERROR" + JP ccp_out_crlf_msg + +.msg_read_error: + DB "READ ERROR", 0 + +; --------------------------------------------------- +; Out message 'NO FILE' +; --------------------------------------------------- +ccp_out_no_file: + LD BC, .msg_no_file ; BC -> "NO FILE" + JP ccp_out_crlf_msg +.msg_no_file: + DB "NO FILE", 0 + +; --------------------------------------------------- +; Decode a command in form: "A>filename number" +; Out: A - number +; --------------------------------------------------- +ccp_decode_num: + CALL ccp_cv_first_to_fcb + LD A, (ccp_chg_drive) + OR A + JP NZ, print_syn_err ; error if drive letter specified + LD HL, ccp_current_fcb_fn + LD BC, 11 ; b=0, c=11 (filename len) + + ; decode number +ccp_dff_nxt_num: + LD A, (HL) + CP ASCII_SP + JP Z, ccp_dff_num_fin ; space - end of number + ; check for digit + INC HL + SUB '0' + CP 10 + JP NC, print_syn_err ; not a digit + LD D, A ; d = number + LD A, B + AND 0xe0 ; check B (sum) overflow + JP NZ, print_syn_err + ; A=B*10 + LD A, B + RLCA + RLCA + RLCA ; *8 + ADD A, B ; *9 + JP C, print_syn_err ; error if overflow + ADD A, B ; * 10 + JP C, print_syn_err ; error if overflow + ; B = B + B*10 + ADD A, D + JP C, print_syn_err ; error if overflow + LD B, A + ; to next number + DEC C + JP NZ, ccp_dff_nxt_num + RET + +ccp_dff_num_fin: + LD A, (HL) + CP ASCII_SP + JP NZ, print_syn_err ; will be space after number + INC HL + DEC C + JP NZ, ccp_dff_num_fin + LD A, B + RET + +; -------------------------------------------------- +; Move 3 bytes from [HL] to [DE] +; (Used only to move file extension) +; -------------------------------------------------- +ccp_mv_hlde_3: + LD B, 3 + +; -------------------------------------------------- +; Move B bytes from [HL] to [DE] +; -------------------------------------------------- +ccp_mv_hlde_b: + LD A, (HL) + LD (DE), A + INC HL + INC DE + DEC B + JP NZ, ccp_mv_hlde_b + RET + +; -------------------------------------------------- +; Return byte from address dma_buffer[A+C] +; Out: A - byte at dma_buffer[A+C] +; HL - dma_buffer+A+C +; -------------------------------------------------- +get_std_buff_ac: + LD HL, dma_buffer + ADD A, C + CALL sum_hl_a + LD A, (HL) + RET + +; -------------------------------------------------- +; Check drive change and select if needed +; Reset drive byte in fcb to 0 (use default) +; -------------------------------------------------- +ccp_drive_sel: + XOR A + LD (ccp_current_fcb), A + LD A, (ccp_chg_drive) + OR A + RET Z ; no need to change cur drive + DEC A + LD HL, ccp_cur_drive + CP (HL) + RET Z ; current and new drive is same + JP ccp_bdos_drv_set ; change + +; -------------------------------------------------- +; Restore previous drive if changed during operation +; -------------------------------------------------- +ccp_restor_drv: + LD A, (ccp_chg_drive) + OR A + RET Z ; not changed + DEC A + LD HL, ccp_cur_drive ; chk cur drive + CP (HL) + RET Z ; new and previous drive is same + LD A, (ccp_cur_drive) + JP ccp_bdos_drv_set ; restore to previous drive + +; -------------------------------------------------- +; Handle user DIR command +; -------------------------------------------------- +cmd_dir: + CALL ccp_cv_first_to_fcb + CALL ccp_drive_sel + ; check filemask specified + LD HL, ccp_current_fcb_fn + LD A, (HL) + CP ASCII_SP + JP NZ, .dir_fmask ; yes specified + + ; fill with wildcard symbol + LD B, 11 +.fill_wc: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_wc + +.dir_fmask: + LD E, 0x0 ; cursor at 0 + PUSH DE + ; find first file + CALL ccp_find_first + CALL Z, ccp_out_no_file ; no file found + +.dir_f_next: + JP Z, .dir_f_no_more + LD A, (ccp_bdos_result_code) + ; find filename pos in direntry + ; a = a * 32 + RRCA + RRCA + RRCA + AND 0x60 + + LD C, A + LD A, 0x0a + + ; get std_buff[10+pos*8] + CALL get_std_buff_ac + RLA ; CF<-[7:0]<-CF + JP C, .dir_dont_lst ; don't display sys files + POP DE + LD A, E ; a = cursor + INC E + PUSH DE ; cursor++ + AND 0x3 + PUSH AF + JP NZ, .dir_no_eol + ; eol, print new line + CALL ccp_out_crlf + ; print A: + PUSH BC + CALL ccp_bdos_drv_get + POP BC + ADD A, 'A' + CALL ccp_putc + LD A, ':' + CALL ccp_putc + JP .dir_out_sp +.dir_no_eol: + ; add space between filenames + CALL ccp_out_space + LD A, ':' + CALL ccp_putc +.dir_out_sp: + CALL ccp_out_space + LD B, 0x1 +.dir_get_one: + LD A, B + CALL get_std_buff_ac + AND 0x7f ; mask status bit + CP ASCII_SP ; name end? + JP NZ, .no_name_end + ; at end of file name + POP AF + PUSH AF + CP 0x3 + JP NZ, .dir_end_ext + LD A, 0x9 + CALL get_std_buff_ac ; chk ext + AND 0x7f ; 7bit + CP ASCII_SP + JP Z, .dir_skp_sp ; do not print space +.dir_end_ext: + LD A, ASCII_SP +.no_name_end: + CALL ccp_putc + INC B + LD A, B + CP 12 + JP NC, .dir_skp_sp ; print until end of file ext + CP 9 + JP NZ, .dir_get_one ; start of file ext? + CALL ccp_out_space ; print sep space + JP .dir_get_one +.dir_skp_sp: + POP AF +.dir_dont_lst: + ; stop if key pressed + CALL ccp_getkey_no_wait + JP NZ, .dir_f_no_more + + ; find next directory entry + CALL ccp_bdos_find_next + JP .dir_f_next + + ; no more to print +.dir_f_no_more: + POP DE + JP ccp_cmdline_back + +; -------------------------------------------------- +; Handle user ERA command +; -------------------------------------------------- +cmd_erase: + CALL ccp_cv_first_to_fcb + ; check for *.* + CP 11 + JP NZ, .era_no_wc + ; confirm erase all + LD BC, msg_all_yn ;= "ALL (Y/N)?" + CALL ccp_out_crlf_msg + CALL ccp_get_inp + LD HL, ccp_inbuff+1 + ; check user input + DEC (HL) + JP NZ, ccp_get_command + INC HL + LD A, (HL) ; user input, first letter + CP 'Y' + JP NZ, ccp_get_command ; return in not exactly 'Y' + INC HL + LD (ccp_inp_line_addr), HL + +.era_no_wc: + CALL ccp_drive_sel ; select drive + LD DE, ccp_current_fcb ; specify current fcb + CALL ccp_bdos_era_file ; and delete file + INC A + CALL Z, ccp_out_no_file + JP ccp_cmdline_back ; go back to command line + +msg_all_yn: + DB "ALL (Y/N)?", 0 + +; -------------------------------------------------- +; Handle user TYPE command +; -------------------------------------------------- +cmd_type: + CALL ccp_cv_first_to_fcb + JP NZ, print_syn_err ; error if wildcard + ; select drive and open + CALL ccp_drive_sel + CALL ccp_open_cur_fcb + JP Z, .not_found ; cant open file + CALL ccp_out_crlf + LD HL, ccp_bytes_ctr + LD (HL), 0xff ; 255>128 for read first sector + +.cont_or_read: + LD HL, ccp_bytes_ctr + LD A, (HL) + CP 128 + JP C, .out_next_char + + ; read 128 bytes + PUSH HL + CALL ccp_read_f_fcb + POP HL + JP NZ, .read_no_ok + ; clear counter + XOR A + LD (HL), A + +.out_next_char: + ; get next byte from buffer + INC (HL) + LD HL, dma_buffer + ; calc offset + CALL sum_hl_a + LD A, (HL) + CP ASCII_SUB ; Ctrl+Z end of text file + JP Z, ccp_cmdline_back ; yes, back to cmd line + CALL ccp_print ; print char to output device + ; interrupt if key pressed + CALL ccp_getkey_no_wait + JP NZ, ccp_cmdline_back + ; + JP .cont_or_read + + ; non zero result from f_read +.read_no_ok: + DEC A + JP Z, ccp_cmdline_back ; A=1 - EOF, return to cmd line + CALL ccp_type_rd_err ; else read error + +.not_found: + CALL ccp_restor_drv + JP print_syn_err + +; -------------------------------------------------- +; Handle user SAVE command +; -------------------------------------------------- +cmd_save: + CALL ccp_decode_num ; get num of pages + PUSH AF ; and store + CALL ccp_cv_first_to_fcb ; conv filename to fcb + JP NZ, print_syn_err ; error if wildcard + ; delete specified file + CALL ccp_drive_sel + LD DE, ccp_current_fcb + PUSH DE + CALL ccp_bdos_era_file + POP DE + ; create specified file + CALL ccp_bdos_make_f + JP Z, ccp_no_space ; 0xff+1 if error + XOR A + LD (ccp_current_fcb_cr), A ; curr record = 0 + POP AF ; a = num pages + LD L, A + LD H, 0 + ADD HL, HL ; HL = A * 2 - number of sectors + LD DE, tpa_start + +.write_next: + ; all sectors written? + LD A, H + OR L + JP Z, ccp_close_f_cur ; no more sectors to write + DEC HL + PUSH HL + ; set buffer address to memory to write + LD HL, 128 + ADD HL, DE + PUSH HL + CALL ccp_bdos_dma_set + ; and write sector + LD DE, ccp_current_fcb + CALL ccp_bdos_f_write + POP DE + POP HL + JP NZ, ccp_no_space ; check for no space left + JP .write_next + +; Close current file +ccp_close_f_cur: + LD DE, ccp_current_fcb + CALL ccp_bdos_close_f + INC A + JP NZ, rest_buf_ret_cmd + +; -------------------------------------------------- +; Out error message about no space left +; -------------------------------------------------- +ccp_no_space: + LD BC, msg_no_space ; BC -> "NO SPACE" + CALL ccp_out_crlf_msg + +rest_buf_ret_cmd: + CALL cpp_set_disk_buff_addr + JP ccp_cmdline_back + +msg_no_space: + DB "NO SPACE", 0 + +; -------------------------------------------------- +; Handle user REN command +; -------------------------------------------------- +cmd_ren: + CALL ccp_cv_first_to_fcb ; get first file name + JP NZ, print_syn_err ; error if wildcard + LD A, (ccp_chg_drive) ; remember drive change flag + PUSH AF + ; check file already exists + CALL ccp_drive_sel + CALL ccp_find_first + JP NZ, .file_exists + ; move filename to "second slot" + LD HL, ccp_current_fcb + LD DE, ccp_current_fcb_fn+15 + LD B, 16 + CALL ccp_mv_hlde_b + ; + LD HL, (ccp_inp_line_addr) ; restore cmd line pointer + EX DE, HL + CALL ccp_find_no_space ; skip spaces between parameters + CP '=' + JP Z, .do_rename + CP '_' + JP NZ, .rename_err + +.do_rename: + EX DE, HL + INC HL ; skip sep + LD (ccp_inp_line_addr), HL ; -> second param + CALL ccp_cv_first_to_fcb ; get second name + JP NZ, .rename_err ; error if wildcard + ; if drive specified it will be same as previous + POP AF + LD B, A + LD HL, ccp_chg_drive + LD A, (HL) + OR A + JP Z, .same_drive ; ok, it is same + CP B + LD (HL), B ; restore first drive + JP NZ, .rename_err + +.same_drive: + LD (HL), B + ; check for seacond file not exists + XOR A + LD (ccp_current_fcb), A + CALL ccp_find_first + JP Z, .second_exists + ; calll bdos to rename + LD DE, ccp_current_fcb + CALL ccp_bdos_rename_f + JP ccp_cmdline_back + +.second_exists: + CALL ccp_out_no_file + JP ccp_cmdline_back + +.rename_err: + CALL ccp_restor_drv + JP print_syn_err + +.file_exists: + LD BC, .msg_file_exists ; BC -> "FILE EXISTS" + CALL ccp_out_crlf_msg + JP ccp_cmdline_back + +.msg_file_exists: + DB "FILE EXISTS", 0 + +; -------------------------------------------------- +; Handle user USER command +; -------------------------------------------------- +cmd_user: + CALL ccp_decode_num ; get user number + ; user will be 0..15 + CP 16 + JP NC, print_syn_err ; >15 - error + LD E, A ; save in E + ; check for other parameters + LD A, (ccp_current_fcb_fn) + CP ASCII_SP + JP Z, print_syn_err ; error if other parameters specified + ; call bdos to set current user + CALL ccp_bdos_set_user + JP ccp_cmdline_back1 + +ccp_unk_cmd: + CALL ccp_verify_pattern ; check if system valid + ; check for file to execute specified + LD A, (ccp_current_fcb_fn) + CP ASCII_SP + JP NZ, .exec_file + ; drive change? + LD A, (ccp_chg_drive) + OR A + JP Z, ccp_cmdline_back1 ; no, return to cmd line + ; change drive + DEC A + LD (ccp_cur_drive), A + CALL ccp_reset_cur_drv + CALL ccp_bdos_drv_set + JP ccp_cmdline_back1 + +.exec_file: + ; check file extension + LD DE, ccp_current_fcb_ft + LD A, (DE) + CP ASCII_SP + JP NZ, print_syn_err + PUSH DE + ; select specified drive + CALL ccp_drive_sel + POP DE + ; set file ext to 'COM' + LD HL, msg_com ; HL -> 'COM' + CALL ccp_mv_hlde_3 + CALL ccp_open_cur_fcb + JP Z, cant_open_exe + LD HL, tpa_start ; load to start of TPA 0x100 + +.read_next_sec: + PUSH HL ; store start pointer + EX DE, HL + CALL ccp_bdos_dma_set + + LD DE, ccp_current_fcb + CALL ccp_bdos_read_f + JP NZ, .read_no_ok ; check for read error + + ; shift start pointer for sector size + POP HL + LD DE, 128 + ADD HL, DE + ; check for enough space in RAM + LD DE, ccp_ram_ent + LD A, L + SUB E + LD A, H + SBC A, D + JP NC, ccp_bad_load + JP .read_next_sec + +.read_no_ok: + POP HL + DEC A + JP NZ, ccp_bad_load ; it is not EOF, is error + ; ok, EOF + CALL ccp_restor_drv ; get first filename + CALL ccp_cv_first_to_fcb + LD HL, ccp_chg_drive ; hl -> buff[16] + PUSH HL + LD A, (HL) + LD (ccp_current_fcb), A ; set drive letter in current fcb + LD A, 16 + CALL ccp_cv_fcb_filename ; replace wildcards + POP HL + ; set drive for second file in reserved fcb area + LD A, (HL) + LD (ccp_current_fcb_al), A + ; clear record count + XOR A + LD (ccp_current_fcb_cr), A + ; Move current to default FCB + LD DE, fcb1 + LD HL, ccp_current_fcb + LD B, 33 + CALL ccp_mv_hlde_b + + ; move remainder of cmd line to 0x0080 + LD HL, ccp_inp_line +.skip_nosp: + LD A, (HL) + OR A + JP Z, .z_or_sp + CP ASCII_SP + JP Z, .z_or_sp + INC HL + JP .skip_nosp + +.z_or_sp: + LD B, 0 ; len of cmd line for program = 0 + LD DE, p_cmd_line ; destination address for cmd line + +.copy_cmd_line: + LD A, (HL) + LD (DE), A + OR A + JP Z, .stor_len + INC B + INC HL + INC DE + JP .copy_cmd_line + +.stor_len: + LD A, B + LD (p_cmd_line_len), A + ; next line + CALL ccp_out_crlf + ; set buffer to cmd line + CALL cpp_set_disk_buff_addr + ; set drive + CALL ccp_set_cur_drv + ; and call loaded program + CALL tpa_start + ; restore stack first + LD SP, ccp_stack + ; restore current drive + CALL ccp_reset_cur_drv + CALL ccp_bdos_drv_set + ; return back to command line mode + JP ccp_get_command + +cant_open_exe: + CALL ccp_restor_drv + JP print_syn_err + +ccp_bad_load: + LD BC, .msg_bad_load ; BC -> "BAD LOAD" + CALL ccp_out_crlf_msg + JP ccp_cmdline_back + +.msg_bad_load: + DB "BAD LOAD", 0 + +msg_com: + DB "COM" + +; -------------------------------------------------- +; Return back to command line +; -------------------------------------------------- +ccp_cmdline_back: + CALL ccp_restor_drv + +ccp_cmdline_back1: + CALL ccp_cv_first_to_fcb + LD A, (ccp_current_fcb_fn) + SUB 0x20 + LD HL, ccp_chg_drive + OR (HL) + JP NZ, print_syn_err + JP ccp_get_command + + DW 0h, 0h, 0h, 0h, 0h, 0h, 0h, 0h + +ccp_stack EQU $ + +ccp_batch: + DB 0h + +ccp_batch_fcb: + DB 0h ; drive code, 0 - default + DB "$$$ SUB" ; filename + DB 0h ; extent + DB 0h ; S1 +ccp_batch_fcb_s2: + DB 0h ; S2 Extent [6:0] bits and [7] write flag +ccp_batch_fcb_rc: + DB 0h ; sectors count +ccp_batch_fcb_al: + DS 16, 0 ; reserved by CPM use only +ccp_batch_fcb_cr: + DB 0h ; current sector to read/write + +ccp_current_fcb: + DB 0h +ccp_current_fcb_fn: + DS 8, 0 +ccp_current_fcb_ft: + DS 3, 0 +ccp_current_fcb_ex: + DB 0h ; extent + DB 0h ; s1 + DB 0h ; s2 + DB 0h ; sectors count +ccp_current_fcb_al: + DS 16, 0 ; reserved by CPM use only +ccp_current_fcb_cr: + DB 0h ; current sector to read/write + +ccp_bdos_result_code: + DB 0h + +ccp_cur_drive: + DB 0h + +ccp_chg_drive: ; change drive flag, 0 - no change + DB 0 +ccp_bytes_ctr: + DW 0 + ; reserved + DS 13, 0 + + +bdos_enter_jump EQU $+6 + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xB200 +;FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| CCP_RAM\t| ",/H,ccp_ram_ent," | ",/H,CODE_SIZE," | \t |" + + ; Check integrity + ASSERT ccp_cmdline_back = 0xb986 + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/ccp_rom.asm b/CPM_v2.2_r8_bc0695e4/ccp_rom.asm new file mode 100644 index 0000000..75e64ad --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/ccp_rom.asm @@ -0,0 +1,882 @@ +; ======================================================= +; Ocean-240.2 +; CPM CPP, ROM PART +; AT 0xDB00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + + IFNDEF BUILD_ROM + OUTPUT ccp_rom.bin + ENDIF + + MODULE CCP_ROM + + ORG 0xDB00 + +@ccp_entry: + LD HL, 0x0 ; prevent stack overflow + ADD HL, SP + LD (CPM_VARS.saved_stack_ptr), HL + LD SP, CPM_VARS.ccp_safe_stack + + CALL get_cmd_index + LD HL, ccp_commands ;= DB6Ch + LD E, A + LD D, 0x0 + ADD HL, DE + ADD HL, DE + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + JP (HL) ; jump to command + +ccp_commands_str: + DB "SDIR READ WRITE" + +; ------------------------------------------------------- +; Search user command position in available commands list +; ------------------------------------------------------- +get_cmd_index: + LD HL, ccp_commands_str ; -> 'DIR' + LD C, 0x0 +.cmd_next: + LD A, C + CP CCP_COMMAND_COUNT + RET NC + LD DE, CCP_RAM.ccp_current_fcb_fn + LD B, CCP_COMMAND_SIZE +.cmp_nxt: + LD A, (DE) + CP (HL) ; -> 'DIR' + JP NZ, .no_eq + INC DE + INC HL + DEC B + JP NZ, .cmp_nxt + LD A, (DE) + CP ASCII_SP + JP NZ, .inc_next + LD A, C + RET +.no_eq: + INC HL + DEC B + JP NZ, .no_eq +.inc_next: + INC C + JP .cmd_next + +; -------------------------------------------------- +; Command handlers ref table +; -------------------------------------------------- +ccp_commands: + DW ccp_dir + DW ccp_read + DW ccp_write + DW ccp_ret ; r8 +; DW ccp_exit1 ; r8 + +ccp_ret: + LD HL, (CPM_VARS.saved_stack_ptr) + LD SP, HL + JP CCP_RAM.ccp_unk_cmd + +;ccp_exit1: +; JP MON.mon_hexb +; -------------------------------------------------- +; DIR [filemask] command handler +; -------------------------------------------------- +ccp_dir: + CALL CCP_RAM.ccp_cv_first_to_fcb + CALL CCP_RAM.ccp_drive_sel + ; chech some filemask specified in command line + LD HL, CCP_RAM.ccp_current_fcb_fn + LD A, (HL) + CP ' ' + JP NZ, .has_par + + ; no filemask, fill with wildcard '?' + LD B, 11 +.fill_wildcard: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_wildcard + + ; find file by specified mask +.has_par: + CALL CCP_RAM.ccp_find_first + JP NZ, .f_found + ; no files found, print and exit + CALL CCP_RAM.ccp_out_no_file + JP CCP_RAM.ccp_cmdline_back + +.f_found: + CALL CCP_RAM.ccp_out_crlf + LD HL, 0x0 + LD (CPM_VARS.tmp_dir_total), HL + LD E, 0 + +.do_next_direntry: + PUSH DE + CALL CCP_RAM.ccp_find_first + POP DE + PUSH DE + + ; Find file with e number +.find_file_e: + DEC E + JP M, .file_out_next + PUSH DE + CALL CCP_RAM.ccp_bdos_find_next + POP DE + JP Z, .file_e_found + JP .find_file_e + +.file_out_next: + LD A, (CCP_RAM.ccp_bdos_result_code) + ; calc address of DIR entry in DMA buffer + ; A[6:5] = A[1:0] = 32*A + RRCA ; [C] -> [7:0] -> [C] + RRCA ; + RRCA ; + AND 01100000b ; mask + LD C, A + PUSH BC + CALL CCP_RAM.ccp_out_crlf ; start new line + CALL CCP_RAM.ccp_bdos_drv_get + INC A + LD (dma_buffer), A ; disk + POP BC + LD B, 0x0 + LD HL, dma_buffer+FCB_FN ; filename + LD E, L + LD D, H + ADD HL, BC + + ; copy filename to tmp FCB and out to screen + LD B, 0x1 +.copy_next: + LD A, (HL) + LD (DE), A + LD C, A + CALL BIOS.conout_f + INC HL + INC DE + INC B + LD A, B + CP FN_LEN ; >12 end of name + JP C, .copy_next + +.zero_up_36: + XOR A + LD (DE), A ; zero at end + INC B + LD A, B + CP 36 + JP C, .zero_up_36 + + ; calc file size for current entry + LD DE, dma_buffer + CALL cpp_bdos_f_size + LD HL, (fcb_ra_record_num) ; file size in blocks + + ; get disk blk size + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_dsk_a0 + LD B, 3 ; for A - blk=3 + JP .dsk_a0 + +.no_dsk_a0: + LD B, 4 ; for other disks - blk=4 +.dsk_a0: + LD C, L + + ; convert 128b OS block to disk blocks +.mul_to_dsk_blk: + XOR A + LD A, H + RRA + LD H, A + LD A, L + RRA + LD L, A + DEC B + JP NZ, .mul_to_dsk_blk + ; round up + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_dsk_a1 + LD A, 00000111b ; for A - ~(~0 << 3) + JP .ds_skip1 +.no_dsk_a1: + LD A, 00001111b ; for other dsk - ~(~0 << 4) +.ds_skip1: + AND C + JP Z, .cvt_blk_kb + INC HL + + ; Convert blocks to kilobytes (A-1k B-2k) +.cvt_blk_kb: + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP Z, .ds_skip2 + ADD HL, HL ; 2k + + ; add file size to total dir size +.ds_skip2: + EX DE, HL + LD HL, (CPM_VARS.tmp_dir_total) + ADD HL, DE + LD (CPM_VARS.tmp_dir_total), HL + + ; display size in K + LD C, ' ' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL ccp_cout_num + LD C, 'K' + CALL BIOS.conout_f + CALL CCP_RAM.ccp_getkey_no_wait + JP NZ, CCP_RAM.ccp_cmdline_back + POP DE + INC E + JP .do_next_direntry + +.file_e_found: + POP DE + LD HL, msg_free_space ;= "\r\nFREE SPACE " + + ; Out: FREE SPACE + CALL ccp_out_str_z + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_ram_dsk + LD HL, (BIOS.disk_a_size) + JP .calc_remanis_ds + +.no_ram_dsk: + DEC A + LD HL, (BIOS.disk_b_size) + JP Z, .calc_remanis_ds + + LD HL, (BIOS.disk_c_size) + LD A, (bios_var3) + OR A + JP NZ, .calc_remanis_ds + + LD A, H + CP 1 + JP Z, .d720 + LD HL, 360 + JP .calc_remanis_ds +.d720: + LD HL, 720 + + ; Disk size - Dir size = Free +.calc_remanis_ds: + EX DE, HL + LD HL, (CPM_VARS.tmp_dir_total) + LD A, E + SUB L + LD E, A + LD A, D + SBC A, H + LD D, A + CALL ccp_cout_num + LD C, 'K' + CALL BIOS.conout_f + CALL CCP_RAM.ccp_out_crlf + JP CCP_RAM.ccp_cmdline_back + +msg_free_space: + DB "\r\nFREE SPACE ",0 + +ccp_cout_num: + LD A, D + AND 11100000b + JP Z, .less_224 + LD C, '*' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + RET + +.less_224: + LD HL, 0x0 + ; copy number to BC + LD B, D + LD C, E + LD DE, 0x1 + LD A, 13 + +.bc_rra: + PUSH AF + PUSH HL + ; BC >> 1 + LD A, B + RRA + LD B, A + LD A, C + RRA + LD C, A + JP NC, .bc_rra_ex + POP HL + CALL cpp_daa16 + PUSH HL + +.bc_rra_ex: + LD L, E + LD H, D + CALL cpp_daa16 + EX DE, HL + POP HL + POP AF + DEC A + JP NZ, .bc_rra + LD D, 4 + LD B, 0 + +.next_d: + LD E, 4 + +.next_e: + LD A, L + RLA + LD L, A + LD A, H + RLA + LD H, A + LD A, C + RLA + LD C, A + DEC E + JP NZ, .next_e + LD A, C + AND 0xf + ADD A, '0' + CP '0' + JP NZ, .no_zero + DEC B + INC B + JP NZ, .b_no_one + LD A, D + DEC A + JP Z, .d_one + LD A, ' ' + JP .b_no_one + +.d_one: + LD A, '0' +.no_zero: + LD B, 0x1 +.b_no_one: + LD C, A + CALL BIOS.conout_f + DEC D + JP NZ, .next_d + RET + +; ------------------------------------------------------- +; ADD with correction HL=HL+DE +; ------------------------------------------------------- +cpp_daa16: + LD A, L + ADD A, E + DAA + LD L, A + LD A, H + ADC A, D + DAA + LD H, A + RET + +; ------------------------------------------------------- +; Call BDOS function 35 (F_SIZE) - Compute file size +; ------------------------------------------------------- +cpp_bdos_f_size: + LD C, F_SIZE + JP jp_bdos_enter + +; ------------------------------------------------------- +; Read Intel HEX data from serial port +; ------------------------------------------------------- +ccp_read: + LD DE, msg_read_hex + CALL out_dollar_str + LD HL, 0x0 + LD (CCP_RAM.hex_length), HL + + ; Wait for start of Intel HEX line +.wait_colon: + CALL MON.mon_serial_in + CP ':' + JP NZ, .wait_colon + + ; Init checksum + XOR A + LD D, A + + CALL ser_read_hexb ; read byte_count + JP Z, .end_of_file + LD E, A + CALL ser_read_hexb ; read address hi + LD H, A + CALL ser_read_hexb ; read address lo + LD L, A ; HL - dst address + CALL ser_read_hexb ; read rec type + + ; calculate length += byte_count + PUSH HL + LD HL, (CCP_RAM.hex_length) + LD A, L + ADD A, E + LD L, A + LD A, H + ADC A, 0 + LD H, A + LD (CCP_RAM.hex_length), HL + POP HL + + LD C, E + + ; receive next E=byte_count bytes +.receive_rec: + CALL ser_read_hexb + LD (HL), A + INC HL + DEC E + JP NZ, .receive_rec + + CALL ser_read_hexb ; receive checksum + JP NZ, .load_error ; jump if error + JP .wait_colon ; jump to wait next line + +.end_of_file: + ; read tail 4 bytes: 00 00 01 ff + CALL ser_read_hexb + CALL ser_read_hexb + CALL ser_read_hexb + CALL ser_read_hexb + JP Z, .load_complete + +.load_error: + LD DE, .msg_error + JP out_dollar_str + +.load_complete: + ; Out message with length of received file + LD HL, (CCP_RAM.hex_length) + LD A, H + CALL MON.mon_hexb + LD A, L + CALL MON.mon_hexb + LD DE, .msg_bytes + CALL out_dollar_str + ; Calculate number of pages + LD HL, (CCP_RAM.hex_length) + LD A, L + ADD A, 0xff + LD A, H + ADC A, 0x0 + RLA + LD (CCP_RAM.hex_sectors), A + ; Out message with number of pages + CALL MON.mon_hexb + LD DE, .msg_pages + CALL out_dollar_str + + ; Check for file name specified in cmd line + CALL CCP_RAM.ccp_cv_first_to_fcb + CALL CCP_RAM.ccp_drive_sel + LD A, (CCP_RAM.ccp_current_fcb_fn) + CP ASCII_SP + JP Z, .warm_boot + + ; Create file + LD DE, CCP_RAM.ccp_current_fcb + LD C, F_MAKE + CALL jp_bdos_enter + INC A + JP Z, .load_error + LD HL, tpa_start + LD (CCP_RAM.hex_buff), HL + +.wr_sector: + ; set source buffer address + LD HL, (CCP_RAM.hex_buff) + EX DE, HL + LD C, F_DMAOFF + CALL jp_bdos_enter + ; write source buffer to disk + LD DE, CCP_RAM.ccp_current_fcb + LD C, F_WRITE + CALL jp_bdos_enter + ; check errors + OR A + JP NZ, .load_error + + ; rewind forward to next sector + LD HL, (CCP_RAM.hex_buff) + LD DE, 128 ; sector size + ADD HL, DE + LD (CCP_RAM.hex_buff), HL + + ; decrement sector count + LD A, (CCP_RAM.hex_sectors) + DEC A + LD (CCP_RAM.hex_sectors), A + JP NZ, .wr_sector ; jump if remains sectors + + ; close file + LD DE, CCP_RAM.ccp_current_fcb + LD C, F_CLOSE + CALL jp_bdos_enter + ; check errors + CP 0xff + JP Z, .load_error + +.warm_boot: + LD C, P_TERMCPM + JP jp_bdos_enter + +.msg_bytes: + DB "h bytes ($" + +.msg_pages: + DB " pages)\n\r$" + +.msg_error: + DB "error!\n\r$" + +; --------------------------------------------------- +; Read next two symbols from serial and convert to +; byte +; Out: A - byte +; CF set if error +; --------------------------------------------------- +ser_read_hexb: + PUSH BC + CALL MON.mon_serial_in + CALL hex_to_nibble + RLCA + RLCA + RLCA + RLCA + LD C, A + CALL MON.mon_serial_in + CALL hex_to_nibble + OR C + LD C, A + ADD A, D + LD D, A + LD A, C + POP BC + RET + +; --------------------------------------------------- +; Convert hex symbol to byte +; Inp: A - '0'..'F' +; Out: A - 0..15 +; CF set if error +; --------------------------------------------------- +hex_to_nibble: + SUB '0' ; < '0' - error + RET C + ADD A, 233 ; F -> 255 + RET C ; > F - error + ADD A, 6 + JP P, .l1 + ADD A, 7 + RET C +.l1: + ADD A, 10 + OR A + RET + +; --------------------------------------------------- +; Out $ ended string +; Inp: DE -> string$ +; --------------------------------------------------- +out_dollar_str: + LD C, C_WRITESTR + JP jp_bdos_enter + +msg_read_hex: + DB "\n\rRead HEX from RS232... $" + +filler1: + DS 62, 0 + +; ------------------------------------------------------- +; Out zerro ended string +; In: HL -> strZ +; ------------------------------------------------------- +ccp_out_str_z: + LD A, (HL) + OR A + RET Z + LD C, A + CALL BIOS.conout_f + INC HL + JP ccp_out_str_z + +; ------------------------------------------------------- +; Delete file and out No Space message +; ------------------------------------------------------- +ccp_del_f_no_space: + LD DE, CCP_RAM.ccp_current_fcb + CALL CCP_RAM.ccp_bdos_era_file + JP CCP_RAM.ccp_no_space + +; ------------------------------------------------------- +; Read current file next block +; Out: A=0 - Ok, 0xFF - HW Error; +; ------------------------------------------------------- +cpp_read_f_blk: + LD DE, CPM_VARS.ccp_fcb ; FCB here + JP CCP_RAM.ccp_bdos_read_f + +ccp_write: + CALL CCP_RAM.ccp_cv_first_to_fcb + CALL CCP_RAM.ccp_drive_sel + LD HL, CCP_RAM.ccp_current_fcb_fn + LD A, (HL) + CP ' ' + JP NZ, .find_f + LD B, 11 + +.fill_with_wc: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_with_wc + +.find_f: + CALL CCP_RAM.ccp_find_first + JP NZ, .found_f + CALL CCP_RAM.ccp_out_no_file + JP CCP_RAM.ccp_cmdline_back + +.found_f: + LD E, 0 ; file counter + +.do_next_f1: + PUSH DE + CALL CCP_RAM.ccp_find_first + POP DE + PUSH DE + +.do_next_f2: + DEC E + JP M, .do_file + PUSH DE + CALL CCP_RAM.ccp_bdos_find_next + POP DE + JP Z, .no_more_f + JP .do_next_f2 + +.do_file: + POP BC + PUSH BC + LD A, C + OR A + JP Z, .calc_addr + LD DE, 1200 + + ; Delay with key interrupt check +.delay_1: + XOR A +.delay_2: + DEC A + JP NZ, .delay_2 + PUSH DE + CALL CCP_RAM.ccp_getkey_no_wait + POP DE + JP NZ, CCP_RAM.ccp_cmdline_back + DEC DE + LD A, D + OR E + JP NZ, .delay_1 + +.calc_addr: + LD A, (CCP_RAM.ccp_bdos_result_code) + ; A=0-3 - for Ok, calc address of DIR entry in DMA buffer + RRCA + RRCA + RRCA + AND 01100000b + LD C, A + PUSH BC + CALL CCP_RAM.ccp_out_crlf + CALL CCP_RAM.ccp_bdos_drv_get + INC A + LD (CPM_VARS.ccp_fcb_dr), A ; set drive number + POP BC + LD B, 0x0 + LD HL, dma_buffer+1 + ADD HL, BC + LD DE, CPM_VARS.ccp_fcb_fn + LD B, 0x1 + +.copy_fn: + LD A, (HL) + LD (DE), A + INC HL + INC DE + INC B + LD A, B + CP 12 + JP C, .copy_fn + +.fillz_fn: + XOR A + LD (DE), A + INC DE + INC B + LD A, B + CP 36 + JP C, .fillz_fn + LD HL, CPM_VARS.ccp_fcb_fn + CALL ccp_out_str_z + LD HL, dma_buffer + + ; Empty first 128 bytes of DMA buffer + LD B, 128 +.clear_buf: + LD (HL), 0x0 + INC HL + DEC B + JP NZ, .clear_buf + + ; Copy file name at buffer start + LD HL, dma_buffer + LD DE, CPM_VARS.ccp_fcb_fn + LD B, 8 + +.find_sp: + LD A, (DE) + CP ' ' + JP Z, .sp_rep_dot ; ' ' -> '.' + LD (HL), A + INC HL + INC DE + JP .find_sp +.sp_rep_dot: + LD (HL), '.' + INC HL + CALL CCP_RAM.ccp_find_no_space + +.cont_copy_fn: + LD A, (DE) + LD (HL), A + OR A + JP Z, .end_copy + INC HL + INC DE + JP .cont_copy_fn + +.end_copy: + LD DE, CPM_VARS.ccp_fcb + CALL CCP_RAM.ccp_bdos_open_f + LD DE, 0x8000 ; Block ID + LD HL, dma_buffer + CALL cpp_pause_tape_wr_blk + CALL cpp_pause_tape_wr_blk + LD DE, 0x1 + + ; Read file block and write to Tape +.read_f_write_t: + PUSH DE + CALL cpp_read_f_blk + ; a=0xff if error; a=1 - EOF + DEC A + JP Z, .eof + LD A, (CPM_VARS.ccp_fcb_cr) + AND 0x7f + CP 0x1 + JP NZ, .write_once + ; Write block to Tape with ID=0 twice + LD DE, 0x0 ; Block ID=0 + LD HL, dma_buffer + CALL cpp_pause_tape_wr_blk + +.write_once: + CALL CCP_RAM.ccp_getkey_no_wait + LD HL, dma_buffer + POP DE + JP NZ, CCP_RAM.ccp_cmdline_back + CALL cpp_pause_tape_wr_blk + ; Inc Block ID and continue + INC DE + JP .read_f_write_t + +.eof: + POP DE + EX DE, HL + LD (dma_buffer), HL + EX DE, HL + + ; Final block ID=0xFFFF + LD DE, 0xffff + ; Write twice + CALL cpp_pause_tape_wr_blk + CALL cpp_pause_tape_wr_blk + POP DE + INC E + JP .do_next_f1 + +.no_more_f: + POP DE + CALL CCP_RAM.ccp_out_crlf + JP CCP_RAM.ccp_cmdline_back + +; ------------------------------------------------------- +; Write block to tape after pause +; ------------------------------------------------------- +cpp_pause_tape_wr_blk: + LD BC, 3036 +.delay: + DEC BC + LD A, B + OR C + JP NZ, .delay + JP BIOS.tape_write_f + + DB 0x4c + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xDB00 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| CCP_ROM\t| ",/H,ccp_entry," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT ccp_dir = 0xdb62 + ASSERT ccp_dir.find_file_e = 0xdb97 + ASSERT ccp_dir.file_out_next = 0xdba6 + ASSERT ccp_dir.file_e_found = 0xdc3e + ASSERT ccp_dir.calc_remanis_ds = 0xdc72 + ASSERT ccp_dir.no_ram_dsk = 0xdc52 + ASSERT msg_free_space = 0xdc8a + ASSERT ccp_cout_num = 0xdc9a + +FILLER + DS FILL_SIZE-1, 0xff + DB 0xaa + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_bc0695e4/cpm.asm b/CPM_v2.2_r8_bc0695e4/cpm.asm new file mode 100644 index 0000000..6a473a0 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/cpm.asm @@ -0,0 +1,46 @@ +; ====================================================== +; Ocean-240.2 +; CP/M Combine file. Includes all sources to build +; ROM 0xC000 +; +; Disassembled by Romych 2025-02-12 +; ====================================================== + + DEFINE BUILD_ROM + + DEVICE NOSLOT64K + +; +; |-----------|---------------|-----------|--------------------------------------| +; | OFFSET | SIZE | Module | Memory Address | +; |-----------|---------------|-----------|--------------------------------------| +; | 0x0000 | 2048 (0x800) | CCP_RAM | 0xC000..0xC7FF -> RAM 0xB200..0xB5FF | +; | 0x0800 | 3584 (0xE00) | BDOS | 0xC800.. | +; | 0x1600 | 1024 (0x400) | BIOS | 0xD600..D9FF | +; | 0x1B00 | 1280 (0x500) | CCP_ROM | 0xDB00..DFFF | +; |-----------|---------------|-----------|--------------------------------------| +; + + DISPLAY "| Module | Offset | Size | Free |" + DISPLAY "|-------------|---------|--------|--------|" + + + OUTPUT cpm-C000.bin + ;LABELSLIST "cpm.labels" + ;CSPECTMAP "cpm.map" + INCLUDE "equates.inc" + INCLUDE "bdos.inc" + + INCLUDE "ccp_ram.asm" + INCLUDE "bdos.asm" + INCLUDE "bios.asm" + INCLUDE "ccp_rom.asm" + + OUTEND + + OUTPUT variables.bin + INCLUDE "cpm_vars.inc" + OUTEND + + +END \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/cpm_vars.inc b/CPM_v2.2_r8_bc0695e4/cpm_vars.inc new file mode 100644 index 0000000..ec00df2 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/cpm_vars.inc @@ -0,0 +1,219 @@ +; ======================================================= +; Ocean-240.2 +; Module CPM_VARS - BIOS + BDOS + CPP variables +; RAM Range: 0xBA09-0xBF00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + IFNDEF _CPM_VARS + DEFINE _CPM_VARS + + MODULE CPM_VARS + ORG 0xBA09 + +cpm_vars_start EQU $ +; Filled by zeroes by BIOS at cold boot +; until ccp_vars_end + +; Output disabel if non zero +bdos_no_outflag DB 0 +bdos_strtcol DB 0 +bdos_column DB 0 + +; Copy output to printer in non zero +bdos_prnflag DB 0 +bdos_kbchar DB 0 + +; Stored user stack pointer on enter bdos fn +bdos_usersp DW 0 ; 0xba0e + +; BDOS Stack + DS 48, 0 +bdos_stack EQU $ + +; ------------------------------------------------------- +; Common values shared between bdosi and bdos +; ------------------------------------------------------- + +; current user number 0..15 +bdos_userno DB 0x00 ; 0xba40 + +; current disk number +bdos_curdsk DB 0x00 + +; reg DE (pointer) value to call BDOS fn +bdos_info DW 0x0000 ; 0xba42 + +; Address value to return +bdos_aret DW 0x0000 ; 0xba44 +; Empty FCB +bdos_efcb DB 0x00 ; 0xba46 = 0xE5 at init +; Read-only disks flag +bdos_rodsk DW 0x0000 ; 0xba47 +; Drive login vector +bdos_dlog DW 0x0000 ; 0xba49 +; Address of DMA buffer +bdos_dmaad DW 0x0000 ; 0xba4b = 0x80 at init +bdos_cdrmax DW 0x0000 ; 0xba4d +; Current track +bdos_curtrk DW 0x0000 ; 0xba4f +; Current record (sector) +bdos_currec DW 0x0000 ; 0xba51 +; Address of user buffer +bdos_buffa DW 0x0000 ; 0xba53 + +; Address of cur disk DPB +bdos_dpbaddr DW 0x0000 ; 0xba55 +; Calculated checksum +bdos_cksum DW 0x0000 ; 0xba57 +; Address of cur disk allocation map +bdos_alloca DW 0x0000 ; 0xba59 +; Sectors per track +bdos_sectpt DW 0x0000 ; 0xba5b +bdos_blkshf DB 0x00 ; 0xba5d +bdos_blmsk DB 0x00 ; 0xba5e +bdos_extmsk DB 0x00 ; 0xba5f +; Max allocation number (disk size) +bdos_maxall DW 0x0000 ; 0xba60 +bdos_dirmax DW 0x0000 ; 0xba62 +; Size of directory +bdos_dirblk DW 0x0000 ; 0xba64 +bdos_alloc1 DW 0x0000 ; 0xba66 +; First track offset +bdos_offset DW 0x0000 ; 0xba68 +; Disk traslation vector address +bdos_tranv DW 0x0000 ; 0xba6a +; Open and closed flag 0xff - closed +bdos_fcb_closed DB 0x00 ; 0xba6c +; Read file modified flag. 0xff - not allow read unwritten +bdos_rfm DB 0x00 ; 0xba6d +bdos_dirloc DB 0x00 ; 0xba6e +; File access mode (0=random, 1=sequential, 2=special) +bdos_seqio DB 0x00 ; 0xba6f +; Reg E value to call BDOS fn +bdos_linfo DB 0x00 ; 0xba70 +bdos_dminx DB 0x00 ; 0xba71 +; Length in bytes to match when search +bdos_searchl DB 0x00 ; 0xba72 +; Address of buffer where to search +bdos_searcha DW 0x0000 ; 0xba73 + DW 0x0000 +; 0 - big disk (>255 blk), 0xff - small disk +bdos_small_disk DB 0x00 ; 0xba77 +; Drive selected (0xff) by user specified FCB flag +bdos_resel DB 0x00 ; 0xba78 +bdos_olddsk DB 0x00 ; 0xba79 +; Saved disk of user FCB +bdos_fcbdsk DB 0x00 ; 0xba7a +; Number of records in extent +bdos_rcount DB 0x00 ; 0xba7b +bdos_extval DB 0x00 ; 0xba7c +; Record number to read +bdos_vrecord DW 0x0000 ; 0xba7d +; Record to access +bdos_arecord DW 0x0000 ; 0xba7f +bdos_arecord1 DW 0x0000 ; 0xba81 +bdos_dptr DB 0x00 ; 0xba83 +bdos_dcnt DW 0x00 ; 0xba84 +bdos_drec DS 20, 0x00 ; 0xba86 +tmp_dir_total DW 0x0000 ; 0xba9a +; Save pointer at ccp entry, restore on exit +saved_stack_ptr DW 0x0000 ; 0xba9c + DS 22, 0 +ccp_safe_stack + DW 0x0000 ; 0xbab4 + +; -------------------------------------------------- +; FCB +; -------------------------------------------------- +ccp_fcb +ccp_fcb_dr DB 0 ; 0xbab6 drive letter to write file +ccp_fcb_fn DS 8,0 ; 0xbab7 file name +ccp_fcb_ft DS 3,0 ; file type +ccp_fcb_ext DB 0 ; extent +ccp_fcb_s1 DB 0 +ccp_fcb_s2 DB 0 ; extent hi bits +ccp_fcb_rc DB 0 +ccp_fcb_al DS 16, 0 ; Second half of FCB +ccp_fcb_cr DB 0x00 ; Current record +ccp_fcb_rn DB 0x00 ; Random access record number + DW 0x00 + DW 0x00 + DW 0x00 + +ccp_vars_end EQU $ + +; -------------------------------------------------- +; Disk parameters headers/blocks in RAM +; -------------------------------------------------- +DPH_RAM DS 16, 0 ; 0xbade +DPH1_RAM DS 16, 0 ; 0xbaee +DPH2_RAM DS 16, 0 ; 0xbafe +tmp_buff9 DS 9, 0 +DPB_A_RAM DS 15, 0 ; 0xbb17 +DPB_B_RAM DS 15, 0 ; 0xbb26 +DPB_C_RAM DS 15, 0 ; 0xbb35 + +; -------------------------------------------------- +cur_disk DB 0x00 ; 0xbb44 +dma_addr DW 0x0000 ; 0xbb46 +curr_track DB 0x00 ; 0xbb47 +curr_sec DB 0x00 ; 0xbb48 +slicer_disk DB 0x00 ; 0xbb49 +slicer_track DB 0x00 ; 0xbb4a +slicer_real_sector DB 0x00 ; 0xbb4b +tmp_slicer_real_sector DB 0x00 ; 0xbb4c +slicer_has_data DB 0x00 ; 0xbb4d +slicer_need_save DB 0x00 ; 0xbb4e +slicer_uninited_count DB 0x00 ; 0xbb4f +slicer_uninited_disk DB 0x00 ; 0xbb42 +slicer_uninited_track DB 0x00 ; 0xbb43 +slicer_uninited_sector_128 DB 0x00 ; 0xbb44 +tmp_slicer_result DB 0x00 ; 0xbb45 +tmp_slicer_can_read DB 0x00 ; 0xbb46 +tmp_slicer_operation DB 0x00 ; 0xbb47 +tmp_slicer_flush DB 0x00 ; 0xbb48 + +; Directory buffer used by BDOS for directory operations +dir_buffer DS DIR_BUFF_SIZE, 0 ; 0xbb49 + +; Allocation map for drive A (Mark used allocation blocks) +AL_MAP_A DS 31, 0 ; 248 bit +; Check vector for drive A (Checksumm of dir entries, used to detect disk change) +CHK_VEC_A DS 16, 0 ; 0xbbf6 + +; Allocation map for drive B +AL_MAP_B DS 45, 0 ; 184 bit +; Check vector for drive B +CHK_VEC_B DS 32, 0 ; 0xbc33 + +; Allocation map for drive C +AL_MAP_C DS 45, 0 ; 184 bit +; Check vector for drive C +CHK_VEC_C DS 32, 0 ; 0xbc80 + DS 96, 0 + +slicer_buffer DS 512, 0 ; 0xbd00 + +cpm_vars_end EQU $ + +ccp_vars_size EQU ccp_vars_end-cpm_vars_start +cpm_vars_size EQU cpm_vars_end-cpm_vars_start + + ; check integrity of original ROM + ASSERT tmp_dir_total = 0xba9a + ASSERT saved_stack_ptr = 0xba9c + ASSERT ccp_fcb_fn = 0xbab7 + ASSERT DPH_RAM = 0xbade + ASSERT cur_disk = 0xbb44 + ASSERT slicer_uninited_count = 0xbb4f + ASSERT slicer_buffer = 0xbd00 + + DISPLAY "| CCP_VARS\t| ",/H,cpm_vars_start," | ",/H,cpm_vars_size," |\t |" + + ;DISPLAY "cur_disk:",/H,cur_disk + + ENDMODULE + + ENDIF diff --git a/CPM_v2.2_r8_bc0695e4/equates.inc b/CPM_v2.2_r8_bc0695e4/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/io.inc b/CPM_v2.2_r8_bc0695e4/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/mon_entries.inc b/CPM_v2.2_r8_bc0695e4/mon_entries.inc new file mode 100644 index 0000000..3bb3149 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/mon_entries.inc @@ -0,0 +1,35 @@ +; ====================================================== +; Ocean-240.2 +; +; Monitor entries to compile CP/M modules +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _MON + DEFINE _MON + + MODULE MON + +mon_init EQU 0xe000 +mon_hexb EQU 0xe003 +non_con_status EQU 0xe006 +mon_con_in EQU 0xe009 +mon_con_out EQU 0xe00c +mon_serial_in EQU 0xe00f +mon_serial_out EQU 0xe012 +mon_char_print EQU 0xe015 +mon_tape_read EQU 0xe018 +mon_tape_write EQU 0xe01b +ram_disk_read EQU 0xe01e +ram_disk_write EQU 0xe021 +mon_res_f1 EQU 0xe024 +mon_res_f2 EQU 0xe027 +mon_tape_wait EQU 0xe02a +mon_tape_detect EQU 0xe02d +read_floppy EQU 0xe030 +write_floppy EQU 0xe033 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/ram.inc b/CPM_v2.2_r8_bc0695e4/ram.inc new file mode 100644 index 0000000..38997e0 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +;@RST1 EQU 0x0008 +;@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/.gitignore b/CPM_v2.2_r8_ddd05ed7/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/CPM_v2.2_r8_ddd05ed7/.vscode/extensions.json b/CPM_v2.2_r8_ddd05ed7/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/CPM_v2.2_r8_ddd05ed7/.vscode/tasks.json b/CPM_v2.2_r8_ddd05ed7/.vscode/tasks.json new file mode 100644 index 0000000..f71ba83 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make CPM (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=cpm.sld", + "--sym=cpm.labels", + "--lstlab=sort", + "--lst=cpm.lst", + "--raw=cpm.obj", + "--fullpath", + "cpm.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + } + + ] +} \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/BIN/CPM_REL_8_ddd05ed7.BIN b/CPM_v2.2_r8_ddd05ed7/BIN/CPM_REL_8_ddd05ed7.BIN new file mode 100644 index 0000000000000000000000000000000000000000..ff9500176c31cdc7d19f01197014e74f5fcf9de8 GIT binary patch literal 8192 zcmdT}dvp`mnIBz&UL`y&cCsA#Ijg28Z1U3obw83ebXOqS}+R6ef zn>G1e&YS~DosjCF0vmSp_4x|MgS+7i-|hUV;ee|Ka^ zH#z&yp8aE2;5+y6-S2+yd+&F}uXl-eb+s|Z&;2&rckgf5R=;(x@y5-ojO%W@<+d*y zYq!;J+grWE*ihG4SKY9AtI^2(ul;g74AZ1vAZ9TE@_mnAvgMP%_tS3LS9sOpze$YmH_F{d|DsKSn?@nbJ~yDgcFWWDI6ALSe8m1E)ACdqo% z%gJXt(9PMHbMJc$Z+S_-hxYiqRhE#Kwy$`EGaZI(58KXY+eu9~%X=#r(*`;;rh;NJ zrm{I6@|K4P9r7C;WU@niybC2M?$GBNv>tFrR&}u1o-L*pPUbsYWNn8dOFY}*z_hYw zJd%DPK)9e@LqC$f2&5a@8GSLn>{%~lR>8PQn!bx3kSn5oV;9RXT5WMJ^jB!g68@t@ zf`HAZJTAp3mvwUZQw41j3p#BZA##kSDJ$lJig8TW8C0r*N>fl-6I850<*PxZGN{;s zk~-X1DB3%pY7Uo6Hiji$vcEH#bXz|3nxFBch`lpacFL1V6m#;kol~3Y3yT%2{9I=V zD07HsI$HpDuuz*r)^^I?PLpC5-t4T{D4y+f6lMv}chc^@&O*`MX{s=03Eoa^hO$Pu z&{;?aE~xOM6nl&^sg{U537j@|wr=krT2E5wL;$FetMa5UyRje3= zyY{)*En^;x}8+M3!`r9*^a$*YBJyuw3*)Bi3vWCp_N;C@S4> z#|PCAw9Q7PeOm`qNX~blm%<+CJg4ile(2?dU-XbQU97*WX(>}w)h8eJ0s{iTe7pW+o5dw_c;FQ^M#yl37{A=qnUa={b& zZI@iaS^wZIC@7e{$8O6d+q;*I6>yPb%VV>e%R;Cd5(_{$e#i_F!iqwSc9U1rReZIY z7R;G#1NJN3mo6(A!k@csy7?|y&)JNalG&o<4e2>WCydg$;%N6JIH6F&k>7a4=er}~ zNh*3}p-{tV8aVO>)e9VPgYbA4tY$y`z=*&HVhg$GsRY@WQoUn`@y7i(?OL^w3K^0D z0112KH#iW@Uh-DVp>QefV^4EQqIeR*%beS>9>SxtQ;KCQ3JU-oC%xi#xGLHn?II6! zi@@|r*X3(Cv1kmdMu@IM{*5-;#MbtvM327qSR$}L=wWMoEYV3 zYs^zAXnHi+J(Q4d+3sSx(ZPRl;%bAI9(l`)EtdVOwv*bw(p|I>BGKy!qB z!b>)(5`Vc!q6eFnCMAV35UEO333JfNf)TLEh6`H zCR~#xEwkCaGrc}|j2l5jH(0?Y@Ws8gyu4i9rW^05`QqUJ ztS24UL__eW!CwT=2Hy>)%4+!3G0Q<7J49J6e_NTJU$=>OTaNOU7M{H&mUJqs!rR&U zpc;Y>nC>(K{z67PC_g6+$cy6z@u>WJ`H%7^azaw%>gbkePP8fdQ1r3r-$q}Ez7~Bm z>W=nA{n1}VNi-V07#fStL@z`?jAq7iV~b*u;ia+6{>=UZF;)(eWKhYF8Af7bL9sK) zCe(6*_KOTtb6@ijW8LAyM*Cw%qt#em^NstAjrV`^JI#m78}EMDHQ8&q^bB?p_G zWXi-eh+o8f}&IMl`Jp`PppY zj3kk_Xh&XLpCh6$VTkgxkY{uG-mnTPdL?~6`t`ZuRw-904`u`?D}Hg9!dY^8C~21| zsbvC5usYk8@GJSUUsiHyks~aXakRK7Wl>liFCLVYHsOYJpDSy%_@=C63Af0YD3elCf-WMzr)h)mv<5+bZ&vGBSa zK*OI%2C(2M*|aMQNZX`@Uu=<+RD|*+2?)-B3e0D9{08BtGL|zrG6PAw3h7cZT7>sy zMFZh`$1X~VgE$I8B3en*^phoVl+H?dMy`~#afl0A2a6qrCb6&|CSxZh@#9@|z*M%AH<`-pwAh0pRrW2OZRGVi zWO2Xw4nDO=6^w0;lgO9iZnK?tm>lLkw3NuoxajI9<~SE* zbwM$|-~1%6UqIdtm9_C@CwaCF%Gv^0>Roo8Zu8C`wY)I&et**ZHU8M?rlT+JORN^( zP@6?#L;cRvEidAeN|?m=`xAj!GG%^UJfaKv;t0&MvBojAg^)xj$mXl6Y2>X>@ak!nib4+{iF}#BcTs-a?vS86isjfD z)6thCQbW!c9$tM!I(EAG=!*|Z`Z@DS3_rk=yGS@d_>g%YnkV@pf1dm_=?wxtOfH5b z*hCW{uMwQ$kM)cHK&#}t{p@yLe4W6`K(kmL2fr!vc3$5uF79WagkALZRZ8sKT-lRQ zz`YoIk~g7_rG_Sm=qJ`E`PmF@*3((SzY@z6Jl7W>C;Q1yh+q5$an5E}HtloL16HZM zgLQ zGUA?r`~b*II^U7+8GsYC{_yzo(b9p0$uHhY+xiUHx2bj~kmC-tu;pFhI|JtXK=gj9 z0CHbHWLQBD=fqF+b0kPe4+@splD+cj0qZ8dSMUy)T=8;zs^GMc)S3ZeCn5$PYXp!FTMQ3*-~5~=PZx$>|y>8^j3C+2VShAXVR$UeS_vBe5zPU%K3vas39`6 zZxG8_5A(2UCns+iELJwk8wXnuDK`!(dBt+Y;F;Mr;llxO<6vMHkq7*!7n(Bj#ifI) zq(Kz>7!MP2Lr{KVn*&i~8PVsF@d1^YY=Q+Uk{|(;KT8NuJvg{~KP2|!!G*B^L|r=M z9`r{btz)N;zBt4U76xK9+vocue({OHWtC**Kq4YJ-w~b|#11X#nH9#EmH~^p6tMdd zfuLj&*ymTqh>PmXF;9Yi(TY5BZjdhi+rewJ0nOFkj4ag)8PM;LT&C{TeKd>~J4#k#)2QVwPx6+hc=%Nx z{ARYj@7f}oMF=89N0-p)7pXRNHtgbxg_pt(PWXA)wYXRKby(~RlYQY7{lm5E*I{6$ zpZr4j|8rr@@8)zVU2*T&SlFS-U{H50hWa1n#os_-2vNLj(FQGYN{Wf2T9`bJ#11jR-2)RB?Tp>5LjI4gFVAEKdc~JUo9^Hs^ z>@mJ~?9GVMF7yI6;zn?^*5YJ=mNi7`LozBmL*&Q(hz{cEkeJn<#=uYvDvuO(Hz)bC|CC$UcPVWj=u&4HyHU)sL8l zeL>G)N2vcrk4d) z4O0tto)L0~S;X@exV2m}9Ed^H5&xPDa9AnV4^x;!bo?N>E=>D`nqhxnO=cd(pwcy2 z^KgEu%_Q9t`V#(DSUS{-0QSL9=*bA&lU6>6NU0(ONs7`{Fx!5@ZeyOQN3Ec}p041c z;rDWT{pCb!q6@o}>Xu-~0B(|pP54I*XmA*Q0{0>h4JVRK&NHT#mrTcA8k2^PtUkE< z5OwG`h7;=kYNRk!6o*U)aojL(ySfG9GPJxu5z6gvEj^Fkowal)+{TL!3|MjCujYG$ z4)Hg`u*Vj9Vsy=xR^cL`tQfjAiapfg4dt`xXyeXaTUs}F)x$=ZU`(B2r} zDj4fgweGMwc}NSgG3)s&=32hBmSVOofNjDIWYr8VCFcHb{luMd@t@+Nc1G0AhVt!QG4g|I&R6rJMT!>VG-K)iX<{T--@GdK<+UoK-d{S5=YLs8+lc7Ccci* zu#b$A1EZja#pKi|t~B2pvF?alY8dGnx^rnr#$#amG`bwR$I!z>UmUjuO^lg8TZH4b zq-6_lYqxCY*(u)e$*ATwrr2RBq_Dq>Yj8_1kK05G!>}3gG7V!vvQ*2L!9|z(^aTd@ zsC5hO4P_&S-ciuN-atDiMy*@pxOdz-Vjybo2J~8Qk1Gr0yGH2fW`+~)r-PzeUTsfM zV7Fzb&tmskGa0+ccqk?L!uLibw*=R5Dj8u*t&fv`HgarMBm8V64f`;%Irtf-_#(+l zhkHh>yL_C`L&xm3kh?kKq4a1oF+$`xj>hlm4RXPx6ilC_pxup_gVV}-_7r7-}|h+!Pov?<9pX8 z>N7sK=>P8~-0!xs7x{IaJXtl)HjaOmSZ}0H0c!qyU&FS&b!_oCP5o!6ssAh-K5rkV zssCHXY3k8FuBQHPAI}rljNd!HeDb}iuZ^2uNA2%j@GYq@g3SZ9@FX__b6xeFsC{yAo+eKZ4Z>=I*^Zt;V(34kI%a z(uDR{VBEbJmGZIZIDJG|Hfer^!S=`rAB}4>#6OK|Gk3a=tC#33;qtgTk~d+?7WEUh zf>-3i2@~y-OD0yO%rEdoph?X=0Y7KX_zqA#l^!D zRp$D*e0ajVEv^Q@!xP65Gn*&ahcp;u!wV?dbPnSh!slroI6rqwLtR}u4UH@(b98n_ zc2DTdbj7RxX|$}_ZqP8c{L9If(t2m9eMp)}5dCEGk$X|55|^q3@5ItfEwduCby@TA zWe+-+*{>JIC-Ca>!CGalkTIE+DP&C^t&%m9cv;Mvw5`=@sPvWyqZ3l=6DymKpIV7~ zy9v){YVK&&B+VTypTz48WN*8VY>XqyPp!zoMn-OnE1B~BQ=Ghe@_NBKDP~T!;OK6h zbaE)%mhaP-xb!n!F()6HMAR1_n#2`XeHXy%hjt+jtE%p@g!aj6f(E;IFnD{C{mB%h z%f`L7a-@w+jxVPCAqvnDOB1pMgp5sEfA7ULHGc}%$`2-;FDr9$#gx-06i>DAD5yzX zdlC@do+=cDN!CS|5zSM6_93l3YQj6zHZv^PNus`STG%fO-JzHQgXCI&`f{P+q(I*x9JQVPu*?a)z(kD7gMJz zAKN(1ay-?QdeUJK81_!1PQh~!Amrw0s{3zFSFADN1+>_~$?enV`0g|~!LzY}X{Y8i zF^&IFX^eDFRmuKoYn+EXHNrd7I5N!x2pyfunctab + LD E, A + LD D, 0 + ADD HL, DE + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) ; DE=functab(func) + LD HL, (CPM_VARS.bdos_info) ; restore input parameter + EX DE, HL + JP (HL) ; dispatch function + +; ------------------------------------------------------- +; BDOS function handlers address table +; ------------------------------------------------------- +functab: + DW BIOS.wboot_f ; fn0 + DW bdos_con_inp ; fn1 + DW bdos_outcon ; fn2 + DW bdos_aux_inp ; fn3 + DW BIOS.punch_f ; fn4 + DW BIOS.list_f ; fn5 + DW bdos_dir_con_io ; fn6 + DW bdos_get_io_byte ; fn7 + DW bdos_set_io_byte ; fn8 + DW bdos_print_str ; fn9 + DW bdos_read ; fn10 + DW bdos_get_con_st ; fn11 + DW bdos_get_version ; fn12 + DW bdos_reset_disks ; fn13 + DW bdos_select_disk ; fn14 + DW bdos_open_file ; fn15 + DW bdos_close_file ; fn16 + DW bdos_search_first ; fn17 + DW bdos_search_next ; fn18 + DW bdos_rm_file ; fn19 + DW bdos_read_file ; fn20 + DW bdos_write_file ; fn21 + DW bdos_make_file ; fn22 + DW bdos_ren_file ; fn23 + DW bdos_get_login_vec ; fn24 + DW bdos_get_cur_drive ; fn25 + DW bdos_set_dma_addr ; fn26 + DW bdos_get_alloc_addr ; fn27 + DW bdos_set_ro ; fn28 + DW bdos_get_wr_protect ; fn29 + DW bdos_set_attr ; fn30 + DW bdos_get_dpb ; fn31 + DW bdos_set_user ; fn32 + DW bdos_rand_read ; fn33 + DW bdos_rand_write ; fn34 + DW bdos_compute_fs ; fn35 + DW bdos_set_random ; fn36 + DW bdos_reset_drives ; fn37 + DW bdos_not_impl ; fn38 (Access Drive) + DW bdos_not_impl ; fn39 (Free Drive) + DW bdos_rand_write_z ; fn40 + +; ------------------------------------------------------- +; Report permanent error +; ------------------------------------------------------- +bdos_persub: + LD HL, permsg ; = 'B' + CALL bdos_print_err + CP CTRL_C + JP Z, warm_boot + RET + +; ------------------------------------------------------- +; Report select error +; ------------------------------------------------------- +bdos_selsub: + LD HL, selmsg ;= 'S' + JP bdos_wait_err + +; ------------------------------------------------------- +; Report write to read/only disk +; ------------------------------------------------------- +bdos_rodsub: + LD HL, rodmsg ;= 'R' + JP bdos_wait_err + +; ------------------------------------------------------- +; Report read/only file +; ------------------------------------------------------- +bdos_rofsub: + LD HL, rofmsg ;= 'F' + +; ------------------------------------------------------- +; Wait for response before boot +; ------------------------------------------------------- +bdos_wait_err: + CALL bdos_print_err + JP warm_boot + +; ------------------------------------------------------- +; Error messages +; ------------------------------------------------------- +dskmsg: + DB 'Bdos Err On ' +dskerr: + DB " : $" +permsg: + DB "Bad Sector$" +selmsg: + DB "Select$" +rofmsg: + DB "File " +rodmsg: + DB "R/O$" + +; ------------------------------------------------------- +; Print error to console, message address in HL +; ------------------------------------------------------- +bdos_print_err: + PUSH HL ; save second message pointer + CALL bdos_crlf + ; set drive letter to message + LD A, (CPM_VARS.bdos_curdsk) + ADD A, 'A' + LD (dskerr), A + + LD BC, dskmsg ; print first error message + CALL bdos_print + POP BC + CALL bdos_print ; print second message + +; ------------------------------------------------------- +; Console handlers +; Read console character to A +; ------------------------------------------------------- +bdos_conin: + LD HL, CPM_VARS.bdos_kbchar + LD A, (HL) + LD (HL), 0x00 + OR A + RET NZ + ;no previous keyboard character ready + JP BIOS.conin_f + +; ------------------------------------------------------- +; Read character from console with echo +; ------------------------------------------------------- +bdos_conech: + CALL bdos_conin + CALL bdos_chk_ctrl_char + RET C + PUSH AF + LD C, A + CALL bdos_outcon + POP AF + RET + +; ------------------------------------------------------- +; Check for control char +; Onp: A - character +; Out: ZF if cr, lf, tab, or backspace +; CF if code < 0x20 +; ------------------------------------------------------- +bdos_chk_ctrl_char: + CP ASCII_CR + RET Z + CP ASCII_LF + RET Z + CP ASCII_TAB + RET Z + CP ASCII_BS + RET Z + CP ' ' + RET + +; ------------------------------------------------------- +; check console during output for +; Ctrl-S and Ctrl-C +; ------------------------------------------------------- +bdos_conbrk: + LD A, (CPM_VARS.bdos_kbchar) + OR A + JP NZ, .if_key ; skip if key pressed + CALL BIOS.const_f + AND 0x1 + RET Z ; return if no char ready + CALL BIOS.conin_f + CP CTRL_S + JP NZ, .no_crtl_s + ; stop on crtl-s + CALL BIOS.conin_f + CP CTRL_C + ; reboot on crtl-c + JP Z, warm_boot + XOR A + RET +.no_crtl_s: + LD (CPM_VARS.bdos_kbchar), A ; character in A, save it +.if_key: + LD A, 0x1 ; return with true set in accumulator + RET + +; -------------------------------------------------- +; Out char C to screen (and printer) +; -------------------------------------------------- +bdos_conout: + LD A, (CPM_VARS.bdos_no_outflag) ; if set, skip output, only move cursor + OR A + JP NZ, .compout + ; check console break + PUSH BC + CALL bdos_conbrk + POP BC + ; output c + PUSH BC + CALL BIOS.conout_f + POP BC + + ; send to printer (list) device + PUSH BC + LD A, (CPM_VARS.bdos_prnflag) + OR A + CALL NZ, BIOS.list_f + POP BC + +; ------------------------------------------------------- +; Update cursor position +; ------------------------------------------------------- +.compout: + LD A, C + ; recall the character + ; and compute column position + LD HL, CPM_VARS.bdos_column + CP ASCII_DEL ; [DEL] key + RET Z ; 0x7F dont move cursor + INC (HL) ; inc column + CP ASCII_SP ; return for normal character > 0x20 + RET NC + + ; restore column position + DEC (HL) + LD A, (HL) + OR A + RET Z ; return if column=0 + + LD A, C + CP ASCII_BS + JP NZ, .not_backsp + ; backspace character + DEC (HL) ; BKSP -> column-1 + RET + +.not_backsp: + CP ASCII_LF + RET NZ + LD (HL), 0 ; LF -> column=0 + RET + +; ------------------------------------------------------- +; Send C character with possible preceding '^' char +; ------------------------------------------------------- +bdos_ctlout: + LD A, C + CALL bdos_chk_ctrl_char ; cy if not graphic (or special case) + JP NC, bdos_outcon ; normal output if non control char (tab, cr, lf) + + PUSH AF + LD C, CTRL ; '^' + CALL bdos_conout + POP AF + OR 0x40 ; convert to letter equivalent (^M, ^J and so on) + LD C, A ; + +bdos_outcon: + LD A, C + CP ASCII_TAB + JP NZ, bdos_conout + + ; Print spaces until tab stop position +.to_tabpos: + LD C, ' ' + CALL bdos_conout + LD A, (CPM_VARS.bdos_column) + AND 00000111b ; column mod 8 = 0 ? + JP NZ, .to_tabpos + RET + +; ------------------------------------------------------- +; Output backspace - erase previous character +; ------------------------------------------------------- +bdos_out_bksp: + CALL bdos_prn_bksp + LD C, ' ' + CALL BIOS.conout_f + +; ------------------------------------------------------- +; Send backspace to console +; ------------------------------------------------------- +bdos_prn_bksp: + LD C, ASCII_BS + JP BIOS.conout_f + +; ------------------------------------------------------- +; print #, cr, lf for ctlx, ctlu, ctlr functions +; then move to strtcol (starting column) +; ------------------------------------------------------- +bdos_newline: + LD C, '#' + CALL bdos_conout + CALL bdos_crlf + ; move the cursor to the starting position +.print_sp: + LD A, (CPM_VARS.bdos_column) ; a=column + LD HL, CPM_VARS.bdos_strtcol + CP (HL) + RET NC ; ret id startcol>column + LD C, ' ' ; print space + CALL bdos_conout + JP .print_sp + +; ------------------------------------------------------- +; Out carriage return line feed sequence +; ------------------------------------------------------- +bdos_crlf: + LD C, ASCII_CR + CALL bdos_conout + LD C, ASCII_LF + JP bdos_conout + +; ------------------------------------------------------- +; Print $-ended string +; Inp: BC -> str$ +; ------------------------------------------------------- +bdos_print: + LD A, (BC) + CP '$' + RET Z + INC BC + PUSH BC + LD C, A + CALL bdos_outcon + POP BC + JP bdos_print + +; ------------------------------------------------------- +; Buffered console input +; Reads characters from the keyboard into a memory buffer +; until RETURN is pressed. +; Inp: C=0Ah +; DE=address or zero +; ------------------------------------------------------- +bdos_read: + LD A, (CPM_VARS.bdos_column) + LD (CPM_VARS.bdos_strtcol), A + LD HL, (CPM_VARS.bdos_info) + LD C, (HL) + INC HL + PUSH HL + LD B, 0 + ; B = current buffer length, + ; C = maximum buffer length, + ; HL= next to fill - 1 +.readnx: + PUSH BC + PUSH HL +.readn1: + CALL bdos_conin + AND 0x7f ; strip 7th bit + POP HL + POP BC + ; end of line? + CP ASCII_CR + JP Z, .read_end + CP ASCII_LF + JP Z, .read_end + + ; backspace ? + CP ASCII_BS + JP NZ, .no_bksp + LD A, B + OR A + JP Z, .readnx ; ignore BKSP for column=0 + DEC B + LD A, (CPM_VARS.bdos_column) + LD (CPM_VARS.bdos_no_outflag), A + JP .linelen + + ; not a backspace +.no_bksp: + CP ASCII_DEL ; [DEL] Key + JP NZ, .no_del + LD A, B + OR A + JP Z, .readnx ; ignore DEL for column=0 + LD A, (HL) ; cur char + DEC B ; dec column + DEC HL ; point to previous char + JP .echo ; out previous char + +.no_del: + CP CTRL_E ; ^E - physical end of line + JP NZ, .no_ctrle + PUSH BC + PUSH HL + CALL bdos_crlf ; out CR+LF + XOR A + LD (CPM_VARS.bdos_strtcol), A ; reset start column to 0 + JP .readn1 + +.no_ctrle: + CP CTRL_P ; ^P + JP NZ, .no_ctrlp + PUSH HL + LD HL, CPM_VARS.bdos_prnflag + LD A, 0x1 + SUB (HL) ; flip printer output flag + LD (HL), A + POP HL + JP .readnx + +.no_ctrlp: + CP CTRL_X ; cancel current cmd line + JP NZ, .no_ctrlx + POP HL +.backx: + LD A, (CPM_VARS.bdos_strtcol) + LD HL, CPM_VARS.bdos_column + CP (HL) + JP NC, bdos_read + DEC (HL) + CALL bdos_out_bksp + JP .backx + +.no_ctrlx: + CP CTRL_U ; cancel current cmd line + JP NZ, .no_ctrlu + + CALL bdos_newline + POP HL + JP bdos_read + +.no_ctrlu: + CP CTRL_R ; repeats current cmd line + JP NZ, .stor_echo + + ; start new line and retype +.linelen: + PUSH BC + CALL bdos_newline + POP BC + POP HL + PUSH HL + PUSH BC +.next_char: + LD A, B + OR A + JP Z, .endof_cmd ; end of cmd line? + INC HL + LD C, (HL) + DEC B + PUSH BC + PUSH HL + CALL bdos_ctlout + POP HL + POP BC + JP .next_char +.endof_cmd: + PUSH HL + LD A, (CPM_VARS.bdos_no_outflag) + OR A + JP Z, .readn1 ; if no display, read next characters + ; update cursor pos + LD HL, CPM_VARS.bdos_column + SUB (HL) + LD (CPM_VARS.bdos_no_outflag), A + ; move back compcol-column spaces + +.backsp: + CALL bdos_out_bksp + LD HL, CPM_VARS.bdos_no_outflag + DEC (HL) + JP NZ, .backsp + JP .readn1 + +; Put and Echo normal character +.stor_echo: + INC HL + LD (HL), A + INC B +.echo: + PUSH BC + PUSH HL + LD C, A + CALL bdos_ctlout + POP HL + POP BC + + ; warm boot on ctrl+c only at start of line + LD A, (HL) + CP CTRL_C + LD A, B + JP NZ, .no_ctrlc + CP 1 + JP Z, warm_boot + +.no_ctrlc: + CP C ; buffer is full? + JP C, .readnx ; get next char in not full + + ; End of read operation, store length +.read_end: + POP HL + LD (HL), B + LD C, ASCII_CR + JP bdos_conout ; Our CR and return + +; ------------------------------------------------------- +; Console input with echo (C_READ) +; Out: A=L=character +; ------------------------------------------------------- +bdos_con_inp: + CALL bdos_conech + JP bdos_ret_a + +; ------------------------------------------------------- +; Console input chartacter +; Out: A=L=ASCII character +; ------------------------------------------------------- +bdos_aux_inp: + CALL BIOS.reader_f + JP bdos_ret_a + +; ------------------------------------------------------- +; Direct console I/O +; Inp: E=code. +; E=code. Returned values (in A) vary. +; 0xff Return a character without echoing if one is +; waiting; zero if none +; 0xfe Return console input status. Zero if no +; character is waiting +; Out: A +; ------------------------------------------------------- +bdos_dir_con_io: + LD A, C + INC A + JP Z, .dirinp + INC A + JP Z, BIOS.const_f + JP BIOS.conout_f +.dirinp: + CALL BIOS.const_f + OR A + JP Z, bdos_ret_mon + CALL BIOS.conin_f + JP bdos_ret_a + +; ------------------------------------------------------- +; Get I/O byte +; Out: A = I/O byte. +; ------------------------------------------------------- +bdos_get_io_byte: + LD A, (iobyte) + JP bdos_ret_a + +; ------------------------------------------------------- +; Set I/O byte +; Inp: C=I/O byte. +; TODO: Check, will be E ? +; ------------------------------------------------------- +bdos_set_io_byte: + LD HL, iobyte + LD (HL), C + RET + +; ------------------------------------------------------- +; Out $-ended string +; Inp: DE -> string$. +; ------------------------------------------------------- +bdos_print_str: + EX DE, HL + LD C, L + LD B, H + JP bdos_print + +; ------------------------------------------------------- +; Get console status +; Inp: A=L=status +; ------------------------------------------------------- +bdos_get_con_st: + CALL bdos_conbrk +bdos_ret_a: + LD (CPM_VARS.bdos_aret), A +bdos_not_impl: + RET + +; ------------------------------------------------------- +; Set IO Error status = 1 +; ------------------------------------------------------- +set_ioerr_1: + LD A, 1 + JP bdos_ret_a + +; ------------------------------------------------------- +; Report select error +; ------------------------------------------------------- +bdos_sel_error: + LD HL, bdos_sele_addr + +; indirect Jump to [HL] +bdos_jump_hl: + LD E, (HL) + INC HL + LD D, (HL) ; ->bdos_sele_addr+1 + EX DE, HL + JP (HL) ; ->bdos_selsub + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +bdos_move_dehl: + INC C +.move_next: + DEC C + RET Z + LD A, (DE) + LD (HL), A + INC DE + INC HL + JP .move_next + +; ------------------------------------------------------- +; Select the disk drive given by curdsk, and fill +; the base addresses curtrka - alloca, then fill +; the values of the disk parameter block +; Out: ZF - for big disk +; ------------------------------------------------------- +bdos_sel_dsk: + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + CALL BIOS.seldsk_f ; HL filled by call + ; HL = 0000 if error, otherwise -> DPH + LD A, H + OR L + RET Z ; return if not valid drive + + LD E, (HL) + INC HL + LD D, (HL) + ; DE -> disk header + + ; save pointers + INC HL + LD (CPM_VARS.bdos_cdrmax), HL + INC HL + INC HL + LD (CPM_VARS.bdos_curtrk), HL + INC HL + INC HL + LD (CPM_VARS.bdos_currec), HL + INC HL + INC HL + ; save translation vector (table) addr + EX DE, HL + LD (CPM_VARS.bdos_tranv), HL ; tran vector + LD HL, CPM_VARS.bdos_buffa ; DE=source for move, HL=dest + LD C, 8 + CALL bdos_move_dehl + + ; Fill the disk parameter block + LD HL, (CPM_VARS.bdos_dpbaddr) + EX DE, HL + LD HL, CPM_VARS.dbos_sectpt + LD C, 15 + CALL bdos_move_dehl + + ; Set single/double map mode + LD HL, (CPM_VARS.bdos_maxall) ; largest allocation number + LD A, H ; a=h>0 for large disks (>255 blocks) + LD HL, CPM_VARS.bdos_small_disk + LD (HL), TRUE ; small + OR A + JP Z, .is_small + LD (HL), FALSE ; big disk > 255 sec +.is_small: + LD A, 0xff + OR A + RET + +; ------------------------------------------------------- +; Move to home position, +; then reset pointers for track and sector +; ------------------------------------------------------- +bdos_home: + CALL BIOS.home_f ; home head + XOR A + ; set track pointer to 0x0000 + LD HL, (CPM_VARS.bdos_curtrk) + LD (HL), A + INC HL + LD (HL), A + + ; set current record pointer to 0x0000 + LD HL, (CPM_VARS.bdos_currec) + LD (HL), A + INC HL + LD (HL), A + + RET + +; ------------------------------------------------------- +; Read buffer and check condition +; ------------------------------------------------------- +bdos_rdbuff: + CALL BIOS.read_f + JP diocomp ; check for i/o errors + +; ------------------------------------------------------- +; Write buffer and check condition +; Inp: C - write type +; 0 - normal write operation +; 1 - directory write operation +; 2 - start of new block +; ------------------------------------------------------- +bdos_wrbuff: + CALL BIOS.write_f + +; ------------------------------------------------------- +; Check for disk errors +; ------------------------------------------------------- +diocomp: + OR A + RET Z + LD HL, bdos_pere_addr ; = C899h + JP bdos_jump_hl + +; ------------------------------------------------------- +; Seek the record containing the current dir entry +; ------------------------------------------------------- +bdos_seekdir: + LD HL, (CPM_VARS.bdos_dcnt) + LD C, DSK_SHF + CALL bdos_hlrotr + LD (CPM_VARS.bdos_arecord), HL + LD (CPM_VARS.bdos_drec), HL + +; ------------------------------------------------------- +; Seek the track given by arecord (actual record) +; local equates for registers +; ------------------------------------------------------- +bdos_seek: + LD HL, CPM_VARS.bdos_arecord + LD C, (HL) + INC HL + LD B, (HL) + ; BC = sector number + + LD HL, (CPM_VARS.bdos_currec) + LD E, (HL) + INC HL + LD D, (HL) + ; DE = current sector number + + LD HL, (CPM_VARS.bdos_curtrk) + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + ; HL = current track + +.sec_less_cur: + ; check specified sector before current + LD A, C + SUB E + LD A, B + SBC A, D + JP NC, .sec_ge_cur + ; yes, decrement current sectors for one track + PUSH HL + LD HL, (CPM_VARS.dbos_sectpt) ; sectors per track + LD A, E + SUB L + LD E, A + LD A, D + SBC A, H + LD D, A + ; current track-1 + POP HL + DEC HL + JP .sec_less_cur + ; specified sector >= current +.sec_ge_cur: + PUSH HL + LD HL, (CPM_VARS.dbos_sectpt) + ADD HL, DE ; current sector+track + JP C, .track_found ; jump if after current + ; sector before current + LD A, C + SUB L + LD A, B + SBC A, H + JP C, .track_found + EX DE, HL + POP HL + ; increment track + INC HL + JP .sec_ge_cur + ; determined track number with specified sector +.track_found: + POP HL + PUSH BC + PUSH DE + PUSH HL + ; Stack contains (lowest) BC=arecord, DE=currec, HL=curtrk + EX DE, HL + LD HL, (CPM_VARS.bdos_offset) ; adjust if have reserved tracks + ADD HL, DE + LD B, H + LD C, L + CALL BIOS.settrk_f ; select track + + ; save current track + POP DE ; curtrk + LD HL, (CPM_VARS.bdos_curtrk) + LD (HL), E + INC HL + LD (HL), D + + ; save current record + POP DE + LD HL, (CPM_VARS.bdos_currec) + LD (HL), E + INC HL + LD (HL), D + + ; calc relative offset between arecord and currec + POP BC + LD A, C + SUB E + LD C, A + LD A, B + SBC A, D + LD B, A + + LD HL, (CPM_VARS.bdos_tranv) ; HL -> translation table + EX DE, HL + ; translate via BIOS call + CALL BIOS.sectran_f + ; select translated sector and return + LD C, L + LD B, H + JP BIOS.setsec_f + +; ------------------------------------------------------- +; Compute block number from record number and extent +; number +; ------------------------------------------------------- +bdos_dm_position: + LD HL, CPM_VARS.bdos_blkshf + LD C, (HL) + LD A, (CPM_VARS.bdos_vrecord) ; record number + ; a = a / 2^blkshf + +dmpos_l0: + OR A + RRA + DEC C + JP NZ, dmpos_l0 + ; A = shr(vrecord, blkshf) = vrecord/2^(sect/block) + LD B, A + LD A, 8 + SUB (HL) + ; c = 8 - blkshf + LD C, A + LD A, (CPM_VARS.bdos_extval) + ; a = extval * 2^(8-blkshf) +dmpos_l1: + DEC C + JP Z, dmpos_l2 + OR A + RLA + JP dmpos_l1 +dmpos_l2: + ADD A, B + RET ; with dm_position in A + +; ------------------------------------------------------- +; Return disk map value from position given by BC +; ------------------------------------------------------- +bdos_getdm: + LD HL, (CPM_VARS.bdos_info) + LD DE, DSK_MAP + ADD HL, DE + ADD HL, BC + LD A, (CPM_VARS.bdos_small_disk) + OR A + JP Z,getdmd + LD L, (HL) + LD H, 0x00 + RET + +getdmd: + ADD HL, BC + LD E, (HL) + INC HL + LD D, (HL) + EX DE, HL + RET + +; ------------------------------------------------------- +; Compute disk block number from current FCB +; ------------------------------------------------------- +bdos_index: + CALL bdos_dm_position + LD C, A + LD B, 0 + CALL bdos_getdm + LD (CPM_VARS.bdos_arecord), HL + RET + +; ------------------------------------------------------- +; Called following index to see if block allocated +; Out: ZF if not allocated +; ------------------------------------------------------- +bdos_allocated: + LD HL, (CPM_VARS.bdos_arecord) + LD A, L + OR H + RET + +; ------------------------------------------------------- +; Compute actual record address, assuming index called +; ------------------------------------------------------- +bdos_atran: + LD A, (CPM_VARS.bdos_blkshf) + LD HL, (CPM_VARS.bdos_arecord) +bdatarn_l0: + ADD HL, HL + DEC A + JP NZ, bdatarn_l0 + LD (CPM_VARS.bdos_arecord1), HL + LD A, (CPM_VARS.bdos_blmsk) + LD C, A + LD A, (CPM_VARS.bdos_vrecord) + AND C + OR L + LD L, A + LD (CPM_VARS.bdos_arecord), HL + RET + +; ------------------------------------------------------- +; Get current extent field address to A +; ------------------------------------------------------- +bdos_getexta: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_EXT + ADD HL, DE ; HL -> .fcb(extnum) + RET + +; ------------------------------------------------------- +; Compute reccnt and nxtrec addresses for get/setfcb +; Out: HL -> record count byte in fcb +; DE -> next record number byte in fcb +; ------------------------------------------------------- +bdos_fcb_rcnr: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_RC + ADD HL, DE + EX DE, HL + LD HL, 17 ; (nxtrec-reccnt) + ADD HL, DE + RET + +; ------------------------------------------------------- +; Set variables from currently addressed fcb +; ------------------------------------------------------- +bdos_stor_fcb_var: + CALL bdos_fcb_rcnr + LD A, (HL) + LD (CPM_VARS.bdos_vrecord), A + EX DE, HL + LD A, (HL) + LD (CPM_VARS.bdos_rcount), A + CALL bdos_getexta + LD A, (CPM_VARS.bdos_extmsk) + AND (HL) + LD (CPM_VARS.bdos_extval), A + RET + +; ------------------------------------------------------- +; Set the next record to access. +; Inp: HL -> fcb next record byte +; ------------------------------------------------------- +bdos_set_nxt_rec: + CALL bdos_fcb_rcnr + LD A, (CPM_VARS.bdos_seqio) ; sequential mode flag (=1) + CP 2 + JP NZ, bsfcb_l0 + XOR A ; if 2 - no adder needed +bsfcb_l0: + LD C, A + LD A, (CPM_VARS.bdos_vrecord) ; last record number + ADD A, C + LD (HL), A + EX DE, HL + LD A, (CPM_VARS.bdos_rcount) ; next record byte + LD (HL), A + RET + +; ------------------------------------------------------- +; Rotate HL right by C bits +; ------------------------------------------------------- +bdos_hlrotr: + INC C +bhlr_nxt: + DEC C + RET Z + LD A, H + OR A + RRA + LD H, A + LD A,L + RRA + LD L, A + JP bhlr_nxt + +; ------------------------------------------------------- +; Compute checksum for current directory buffer +; Out: A - checksum +; ------------------------------------------------------- +bdos_compute_cs: + LD C, DIR_BUFF_SIZE ; size of directory buffer + LD HL, (CPM_VARS.bdos_buffa) + XOR A +bcompcs_add_nxt: + ADD A, (HL) + INC HL + DEC C + JP NZ, bcompcs_add_nxt + RET + +; ------------------------------------------------------- +; Rotate HL left by C bits +; ------------------------------------------------------- +bdos_hlrotl: + INC C +.nxt: + DEC C + RET Z + ADD HL, HL + JP .nxt + +; ------------------------------------------------------- +; Set a "1" value in curdsk position +; Inp: BC - vector +; Out: HL - vector with bit set +; ------------------------------------------------------- +bdos_set_dsk_bit: + PUSH BC + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + LD HL, 0x0001 + CALL bdos_hlrotl + POP BC + LD A, C + OR L + LD L, A + LD A, B + OR H + LD H, A + RET + +; ------------------------------------------------------- +; Get write protect status +; ------------------------------------------------------- +bdos_nowrite: + LD HL, (CPM_VARS.bdos_rodsk) + LD A, (CPM_VARS.bdos_curdsk) + LD C, A + CALL bdos_hlrotr + LD A, L + AND 0x1 + RET + +; ------------------------------------------------------- +; Temporarily set current drive to be read-only; +; attempts to write to it will fail +; ------------------------------------------------------- +bdos_set_ro: + LD HL, CPM_VARS.bdos_rodsk + LD C, (HL) + INC HL + LD B, (HL) + + CALL bdos_set_dsk_bit + LD (CPM_VARS.bdos_rodsk), HL + ; high water mark in directory goes to max + LD HL, (CPM_VARS.bdos_dirmax) + INC HL + EX DE, HL + LD HL, (CPM_VARS.bdos_cdrmax) + LD (HL), E + INC HL + LD (HL), D + RET + +; ------------------------------------------------------- +; Check current directory element for read/only status +; ------------------------------------------------------- +bdos_check_rodir: + CALL bdos_getdptra + +; ------------------------------------------------------- +; Check current buff(dptr) or fcb(0) for r/o status +; ------------------------------------------------------- +bdos_check_rofile: + LD DE, RO_FILE + ADD HL, DE + LD A, (HL) + RLA + RET NC + LD HL, bdos_rofe_addr ; = C8B1h + JP bdos_jump_hl + +; ------------------------------------------------------- +; Check for write protected disk +; ------------------------------------------------------- +bdos_check_write: + CALL bdos_nowrite + RET Z + LD HL, bdos_rode_addr ; = C8ABh + JP bdos_jump_hl + +; ------------------------------------------------------- +; Compute the address of a directory element at +; positon dptr in the buffer +; ------------------------------------------------------- +bdos_getdptra: + LD HL, (CPM_VARS.bdos_buffa) + LD A, (CPM_VARS.bdos_dptr) + +; ------------------------------------------------------- +; HL = HL + A +; ------------------------------------------------------- +bdos_hl_add_a: + ADD A,L + LD L, A + RET NC + INC H + RET + +; ------------------------------------------------------- +; Compute the address of the s2 from fcb +; Out: A - module number +; HL -> module number +; ------------------------------------------------------- +bdos_get_s2: + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_S2 + ADD HL, DE + LD A, (HL) + RET + +; ------------------------------------------------------- +; Clear the module number field for user open/make +; ------------------------------------------------------- +bdos_clear_s2: + CALL bdos_get_s2 + LD (HL), 0 + RET + +; ------------------------------------------------------- +; Set FWF (File Write Flag) +; ------------------------------------------------------- +bdos_set_fwf: + CALL bdos_get_s2 + OR FWF_MASK + LD (HL), A + RET + +; ------------------------------------------------------- +; Check for free entries in directory +; Out: CF is set - directory have more file entries +; CF is reset - directory is full +; ------------------------------------------------------- +bdos_dir_is_free: + LD HL, (CPM_VARS.bdos_dcnt) + EX DE, HL ; DE = directory counter + LD HL, (CPM_VARS.bdos_cdrmax) ; HL = cdrmax + LD A, E + SUB (HL) + INC HL + LD A, D + SBC A, (HL) + ; Condition dcnt - cdrmax produces cy if cdrmax>dcnt + RET + +; ------------------------------------------------------- +; If not (cdrmax > dcnt) then cdrmax = dcnt+1 +; ------------------------------------------------------- +bdos_setcdr: + CALL bdos_dir_is_free + RET C + INC DE + LD (HL), D + DEC HL + LD (HL), E + RET + +; ------------------------------------------------------- +; HL = DE - HL +; ------------------------------------------------------- +bdos_de_sub_hl: + LD A, E + SUB L + LD L, A + LD A, D + SBC A, H + LD H, A + RET + +; Set directory checksubm byte +bdos_newchecksum: + LD C, 0xff + + +; ------------------------------------------------------- +bdos_checksum: + LD HL, (CPM_VARS.bdos_drec) + EX DE, HL + LD HL, (CPM_VARS.bdos_alloc1) + CALL bdos_de_sub_hl + RET NC + PUSH BC + CALL bdos_compute_cs ; Compute checksum to A for current dir buffer + LD HL, (CPM_VARS.bdos_cksum) + EX DE, HL + LD HL, (CPM_VARS.bdos_drec) + ADD HL, DE + POP BC + INC C + JP Z, initial_cs + CP (HL) + RET Z + CALL bdos_dir_is_free + RET NC + CALL bdos_set_ro + RET +initial_cs: + LD (HL), A + RET + +; ------------------------------------------------------- +; Write the current directory entry, set checksum +; ------------------------------------------------------- +bdos_wrdir: + CALL bdos_newchecksum + CALL bdos_setdir + LD C, 0x1 + CALL bdos_wrbuff + JP bdos_setdata + +; ------------------------------------------------------- +; Read a directory entry into the directory buffer +; ------------------------------------------------------- +bdos_rd_dir: + CALL bdos_setdir + CALL bdos_rdbuff + +; ------------------------------------------------------- +; Set data dma address +; ------------------------------------------------------- +bdos_setdata: + LD HL, CPM_VARS.bdos_dmaad + JP bdos_set_dma + +; ------------------------------------------------------- +; Set directory dma address +; ------------------------------------------------------- +bdos_setdir: + LD HL, CPM_VARS.bdos_buffa + +; ------------------------------------------------------- +; HL -> dma address to set (i.e., buffa or dmaad) +; ------------------------------------------------------- +bdos_set_dma: + LD C, (HL) + INC HL + LD B, (HL) + JP BIOS.setdma_f + +; ------------------------------------------------------- +; Copy the directory entry to the user buffer +; after call to search or searchn by user code +; ------------------------------------------------------- +bdos_dir_to_user: + LD HL, (CPM_VARS.bdos_buffa) + EX DE, HL + LD HL, (CPM_VARS.bdos_dmaad) + LD C, DIR_BUFF_SIZE + JP bdos_move_dehl + +; ------------------------------------------------------- +; return zero flag if at end of directory, non zero +; if not at end (end of dir if dcnt = 0ffffh) +; ------------------------------------------------------- +bdos_end_of_dir: + LD HL, CPM_VARS.bdos_dcnt + LD A, (HL) + INC HL + CP (HL) + RET NZ + INC A + RET + +; ------------------------------------------------------- +; Set dcnt to the end of the directory +; ------------------------------------------------------- +bdos_set_end_dir: + LD HL, ENDDIR + LD (CPM_VARS.bdos_dcnt), HL + RET + +; ------------------------------------------------------- +; Read next directory entry, with C=true if initializing +; ------------------------------------------------------- +bdos_read_dir: + LD HL, (CPM_VARS.bdos_dirmax) + EX DE, HL + LD HL, (CPM_VARS.bdos_dcnt) + INC HL + LD (CPM_VARS.bdos_dcnt), HL + CALL bdos_de_sub_hl + JP NC, bdrd_l0 + JP bdos_set_end_dir +bdrd_l0: + LD A, (CPM_VARS.bdos_dcnt) + AND DSK_MSK + LD B, FCB_SHF +bdrd_l1: + ADD A, A + DEC B + JP NZ, bdrd_l1 + LD (CPM_VARS.bdos_dptr), A + OR A + RET NZ + PUSH BC + CALL bdos_seekdir + CALL bdos_rd_dir + POP BC + JP bdos_checksum + +; ------------------------------------------------------- +; Given allocation vector position BC, return with byte +; containing BC shifted so that the least significant +; bit is in the low order accumulator position. HL is +; the address of the byte for possible replacement in +; memory upon return, and D contains the number of shifts +; required to place the returned value back into position +; ------------------------------------------------------- +bdos_getallocbit: + LD A, C + AND 00000111b + INC A + LD E, A + LD D, A + LD A, C + RRCA + RRCA + RRCA + AND 00011111b + LD C, A + LD A, B + ADD A, A + ADD A, A + ADD A, A + ADD A, A + ADD A, A + OR C + LD C, A + LD A, B + RRCA + RRCA + RRCA + AND 00011111b + LD B, A + LD HL, (CPM_VARS.bdos_alloca) ; Base address of allocation vector + ADD HL, BC + LD A, (HL) +ga_rotl: + RLCA + DEC E + JP NZ, ga_rotl + RET + +; ------------------------------------------------------- +; BC is the bit position of ALLOC to set or reset. The +; value of the bit is in register E. +; ------------------------------------------------------- +bdos_setallocbit: + PUSH DE + CALL bdos_getallocbit + AND 11111110b + POP BC + OR C + +; ------------------------------------------------------- +; Rotate and replace +; Inp: A - Byte value from ALLOC +; D - bit position (1..8) +; HL - target ALLOC position +; ------------------------------------------------------- +bdos_sab_rotr: + RRCA + DEC D + JP NZ, bdos_sab_rotr + LD (HL), A + RET + +; ------------------------------------------------------- +; Set or clear space used bits in allocation map +; Inp: C=0 - clear, C=1 - set +; ------------------------------------------------------- +bdos_scandm: + CALL bdos_getdptra + ; HL addresses the beginning of the directory entry + LD DE, DSK_MAP + ADD HL, DE ; HL -> block number byte + PUSH BC + LD C, 17 ; fcblen-dskmap+1 + ; scan all 17 bytes +.scan_next: + POP DE + DEC C + RET Z ; return if done + + PUSH DE + ; small or bug disk? + LD A, (CPM_VARS.bdos_small_disk) + OR A + JP Z, .scan_big + PUSH BC + PUSH HL + ; small, one byte + LD C, (HL) + LD B, 0 + JP .scan_cmn +.scan_big: + ; big, two byte + DEC C + PUSH BC + LD C, (HL) + INC HL + LD B, (HL) + PUSH HL + +.scan_cmn: + ; this block used? + LD A, C + OR B + JP Z, .not_used + ; have free space on disk? + LD HL, (CPM_VARS.bdos_maxall) + LD A, L + SUB C + LD A, H + SBC A, B + CALL NC, bdos_setallocbit ; enough space, set bit + +.not_used: + POP HL ; HL -> next block in fcb + INC HL + POP BC + JP .scan_next + +; ------------------------------------------------------- +; Initialize the current disk +; lret = false, set to true if $ file exists +; compute the length of the allocation vector - 2 +; ------------------------------------------------------- +bdos_initialize: + ; compute size of alloc table + LD HL, (CPM_VARS.bdos_maxall) + LD C, 3 + CALL bdos_hlrotr ; HL = disk size / 8 + 1 + INC HL + LD B, H + LD C, L ; BC = alloc table size + LD HL, (CPM_VARS.bdos_alloca) ; address of allocation table + ; fill with zeroes +.alt_fill_0: + LD (HL), 0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .alt_fill_0 + + LD HL, (CPM_VARS.bdos_dirblk) ; DE = initial space, used by directory + EX DE, HL + LD HL, (CPM_VARS.bdos_alloca) ; HL -> allocation map + LD (HL), E + INC HL + LD (HL), D ; [HL] = DE + CALL bdos_home ; home drive, set initial head, track, sector + + LD HL, (CPM_VARS.bdos_cdrmax) + LD (HL), 3 ; next dir read + INC HL + LD (HL), 0 + CALL bdos_set_end_dir ; mark end of dir + +.rd_next_f: + ; read next file name + LD C, 0xff + CALL bdos_read_dir + CALL bdos_end_of_dir ; is have another file? + RET Z + ; get params of file, and check deleted + CALL bdos_getdptra + LD A, FILE_DELETED + CP (HL) + JP Z, .rd_next_f + ; check user code + LD A, (CPM_VARS.bdos_userno) + CP (HL) + JP NZ, .set_as_used + INC HL + LD A, (HL) + SUB '$' ; check for $name file + JP NZ, .set_as_used + ; dollar file found, mark in lret + DEC A + LD (CPM_VARS.bdos_aret), A + +.set_as_used: + LD C, 1 + CALL bdos_scandm + CALL bdos_setcdr + JP .rd_next_f + +; ------------------------------------------------------- +; Return directory location as lret +; used in delete, rename, ... +; ------------------------------------------------------- +bdos_ret_dirloc: + LD A, (CPM_VARS.bdos_dirloc) + JP bdos_ret_a + +; ------------------------------------------------------- +; Compare extent# in A with that in C, return nonzero +; if they do not match +; ------------------------------------------------------- +bdos_compext: + PUSH BC + PUSH AF + LD A, (CPM_VARS.bdos_extmsk) + CPL + LD B, A + LD A, C + AND B + LD C, A + POP AF + AND B + SUB C + AND MAX_EXT ; check only bits [4:0] (MAX_EXT) + POP BC + RET + +; ------------------------------------------------------- +; Search for directory element of length C at info +; ------------------------------------------------------- +bdos_search: + LD A, 0xff + LD (CPM_VARS.bdos_dirloc), A + LD HL, CPM_VARS.bdos_searchl ; length in bytes to match + LD (HL), C + LD HL, (CPM_VARS.bdos_info) ; address filename to match + LD (CPM_VARS.bdos_searcha), HL + CALL bdos_set_end_dir + CALL bdos_home + +; ------------------------------------------------------- +; Search for the next directory element, assuming +; a previous call on search which sets searcha and +; searchl +; ------------------------------------------------------- +bdos_search_nxt: + LD C, 0 + CALL bdos_read_dir ; get next filename entry + CALL bdos_end_of_dir ; at end? pos=0xffff + JP Z, .not_found ; jump at end + + LD HL, (CPM_VARS.bdos_searcha) + EX DE, HL + ; skip if file deleted + LD A, (DE) + CP FILE_DELETED + JP Z, .search_next + + PUSH DE + CALL bdos_dir_is_free + POP DE + JP NC, .not_found ; Is full, no more file entries + +.search_next: + CALL bdos_getdptra ; get address of FCB + LD A, (CPM_VARS.bdos_searchl) ; BC=length to compare + LD C, A + LD B, 0 + + ; compare loop +.search_loop: + LD A, C + OR A + JP Z, .search_end ; search completed? + ; check byte + LD A, (DE) + CP '?' + JP Z, .search_ok ; match if wildcard + LD A, B + CP 13 ; ignore 13th byte + JP Z, .search_ok + CP FCB_EXT ; extent byte + LD A, (DE) + JP Z, .search_ext + SUB (HL) ; compare bytes + AND 0x7f ; mask 7th bit + JP NZ, bdos_search_nxt ; if not match, check next file entry + JP .search_ok + + ; compare ext +.search_ext: + PUSH BC + LD C, (HL) + CALL bdos_compext + POP BC + JP NZ, bdos_search_nxt ; if not match, check next file entry + + ; current bytes matches, increment pointers, decrement counter +.search_ok: + INC DE + INC HL + INC B + DEC C + JP .search_loop ; compare next byte + + ; entiry name matches, return dir position +.search_end: + LD A, (CPM_VARS.bdos_dcnt) + AND 0x3 + LD (CPM_VARS.bdos_aret), A + LD HL, CPM_VARS.bdos_dirloc + LD A, (HL) + RLA + RET NC + XOR A + LD (HL), A + RET + + ; end of directory, or empty name +.not_found: + CALL bdos_set_end_dir + LD A, 0xff + JP bdos_ret_a + +; ------------------------------------------------------- +; Delete the currently addressed file +; ------------------------------------------------------- +bdos_era_file: + CALL bdos_check_write ; check write rotection + LD C, 12 ; length of filename + CALL bdos_search ; search file in directory (with wildcards) +.del_next: + CALL bdos_end_of_dir + RET Z ; no more dir entries - exit + CALL bdos_check_rodir ; check file RO + CALL bdos_getdptra ; get file info + LD (HL), FILE_DELETED ; set deleted marker at first symbol + LD C, 0 + CALL bdos_scandm ; clear space at map + CALL bdos_wrdir ; write directory to disk + CALL bdos_search_nxt ; find next file + JP .del_next + +; ------------------------------------------------------- +; Given allocation vector position BC, find the zero bit +; closest to this position by searching left and right. +; if found, set the bit to one and return the bit position +; in hl. if not found (i.e., we pass 0 on the left, or +; maxall on the right), return 0000 in hl +; ------------------------------------------------------- +bdos_get_block: + LD D, B + LD E, C +.prev_test: + LD A, C + OR B + JP Z, .next_test ; jump if block 0 specified + ; check previous block + DEC BC + PUSH DE + PUSH BC + CALL bdos_getallocbit + RRA + JP NC, .ret_block ; block is empty, use it + + ; not empty, check more + POP BC + POP DE + + ; look at next block +.next_test: + ; check for free space on disk + LD HL, (CPM_VARS.bdos_maxall) + LD A, E + SUB L + LD A, D + SBC A, H + JP NC, .ret_no_block + ; have space, move to next block + INC DE + PUSH BC + PUSH DE + LD B, D + LD C, E + CALL bdos_getallocbit + RRA + JP NC, .ret_block ; block is empty, use it + POP DE + POP BC + JP .prev_test + + ; mark block as used and return in HL +.ret_block: + RLA + INC A + CALL bdos_sab_rotr + POP HL + POP DE + RET + + ; no free blocks found +.ret_no_block: + LD A, C + OR B + JP NZ, .prev_test ; if BC != 0 try to find before + LD HL, 0x00 ; not found + RET + +; ------------------------------------------------------- +; Copy the entire file control block +; ------------------------------------------------------- +bdos_copy_fcb: + LD C, 0x00 + LD E, FCB_LEN +; ------------------------------------------------------- +; copy fcb information starting at C for E bytes +; into the currently addressed directory entry +; ------------------------------------------------------- +dbos_copy_dir: + PUSH DE + LD B, 0x00 + LD HL, (CPM_VARS.bdos_info) + ADD HL, BC + EX DE, HL + CALL bdos_getdptra + POP BC + CALL bdos_move_dehl + +; ------------------------------------------------------- +; Enter from close to seek and copy current element +; ------------------------------------------------------- +bdos_seek_copy: + CALL bdos_seekdir + JP bdos_wrdir + +; ------------------------------------------------------- +; Rename the file described by the first half of +; the currently addressed FCB to new name, contained in +; the last half of the currently addressed FCB. +; ------------------------------------------------------- +bdos_rename: + CALL bdos_check_write ; check for RO disk + LD C, 12 ; use 12 symbols of name to compare + CALL bdos_search ; find first file + LD HL, (CPM_VARS.bdos_info) ; file info + LD A, (HL) ; user number + LD DE, 16 ; shift to place second file info + ADD HL, DE + LD (HL), A ; set same user no + +.ren_next: + CALL bdos_end_of_dir + RET Z ; return if end of directory + + CALL bdos_check_rodir ; check file RO flag + LD C, 16 ; start from 16th byte + LD E, FN_LEN ; and copy 12 byte + CALL dbos_copy_dir + CALL bdos_search_nxt ; search next file + JP .ren_next + +; ------------------------------------------------------- +; Update file attributes for current fcb +; ------------------------------------------------------- +bdos_update_attrs: + LD C, FN_LEN + CALL bdos_search ; search file by 12 bytes of name +.set_next: + CALL bdos_end_of_dir + RET Z ; return if not found + + LD C, 0 + LD E, FN_LEN + CALL dbos_copy_dir ; copy name to FCB and save dir + CALL bdos_search_nxt + JP .set_next ; ; do it for next file + +; -------------------------------------------------- +; Open file, name specified in FCB +; ------------------------------------------------------- +open: + LD C, FCB_INFO_LEN + CALL bdos_search ; search file + CALL bdos_end_of_dir + RET Z ; return if not found + +bdos_open_copy: + CALL bdos_getexta + LD A, (HL) ; get extent byte + PUSH AF ; save ext + PUSH HL ; and it's address + CALL bdos_getdptra + EX DE, HL + ; move to user space + LD HL, (CPM_VARS.bdos_info) + LD C, 32 + PUSH DE + CALL bdos_move_dehl + CALL bdos_set_fwf ; set 7th bit s2 "unmodified" flag + + POP DE + LD HL, FCB_EXT + ADD HL, DE + LD C, (HL) ; C = extent byte + + LD HL, FCB_RC + ADD HL, DE + LD B, (HL) ; B = record count + + POP HL + POP AF + LD (HL), A + LD A, C + CP (HL) ; cmp extent bytes + LD A, B + JP Z, .set_rec_cnt + ; user specified extent is not same, reset record count to 0 + LD A, 0 + JP C, .set_rec_cnt + ; set to maximum + LD A, 128 + +.set_rec_cnt: + ; set record count in user FCB to A + LD HL, (CPM_VARS.bdos_info) + LD DE, FCB_RC + ADD HL, DE + LD (HL), A + RET + +; -------------------------------------------------- +; HL = .fcb1(i), DE = .fcb2(i), +; if fcb1(i) = 0 then fcb1(i) := fcb2(i) +; -------------------------------------------------- +bdos_mergezero: + LD A, (HL) + INC HL + OR (HL) + DEC HL + RET NZ + LD A, (DE) + LD (HL), A + INC DE + INC HL + LD A, (DE) + LD (HL), A + DEC DE + DEC HL + RET + +; -------------------------------------------------- +; Close file specified by FCB +; -------------------------------------------------- +bdos_close: + XOR A + ; clear status and file position bytes + LD (CPM_VARS.bdos_aret), A + LD (CPM_VARS.bdos_dcnt), A + LD (CPM_VARS.bdos_dcnt+1), A + CALL bdos_nowrite ; get write protection + RET NZ ; return if set + + CALL bdos_get_s2 + AND FWF_MASK + RET NZ ; return if not modified flag set + ; search file + LD C, FCB_INFO_LEN + CALL bdos_search + CALL bdos_end_of_dir + RET Z ; return if not found + + LD BC, DSK_MAP ; offset of records used + CALL bdos_getdptra + ADD HL, BC + EX DE, HL + + LD HL, (CPM_VARS.bdos_info) ; same for user FCB + ADD HL, BC + LD C, 16 ; bytes in extent + +bdos_merge0: + LD A, (CPM_VARS.bdos_small_disk) ; small/big disk flag + OR A + JP Z, bdos_merge ; jump if big (16 bit) + ; small disk (8 bit) + LD A, (HL) ; from user FCB + OR A + LD A, (DE) ; from DIR FCB + JP NZ, bdm_fcbnzero + LD (HL), A ; user is 0, set from directory fcb + +bdm_fcbnzero: + OR A + JP NZ, bdm_buffnzero + ; dir is 0, set from user fcb + LD A, (HL) + LD (DE), A + +bdm_buffnzero: + CP (HL) + JP NZ, bdm_mergerr ; if both non zero, close error + JP bdm_dmset ; merged ok, go to next fcb byte + +bdos_merge: + CALL bdos_mergezero ; update user fcb if it is zero + EX DE, HL + CALL bdos_mergezero ; update dir fcb if it is zero + EX DE, HL + LD A, (DE) + CP (HL) + JP NZ, bdm_mergerr ; if both is same, close error + + ; next byte + INC DE + INC HL + LD A, (DE) + CP (HL) + JP NZ, bdm_mergerr ; if both is same, close error + DEC C + +bdm_dmset: + ; next + INC DE + INC HL + DEC C + JP NZ, bdos_merge0 ; merge next if C>0 + + LD BC, 0xffec ; -(fcblen-extnum) (-20) + ADD HL, BC + EX DE, HL + ADD HL, BC + LD A, (DE) + CP (HL) + JP C, bdm_endmerge ; directory extent > user extent -> end + ; update record count in dir FCB + LD (HL), A + LD BC, 0x3 ; (reccnt-extnum) + ADD HL, BC + EX DE, HL + ADD HL, BC + LD A, (HL) ; get from user FCB + LD (DE), A ; set to directory FCB + +bdm_endmerge: + LD A, 0xff + LD (CPM_VARS.bdos_fcb_closed), A ; set was open and closed flag + JP bdos_seek_copy ; update directory + + ; set return status and return +bdm_mergerr: + LD HL, CPM_VARS.bdos_aret + DEC (HL) + RET + +; ------------------------------------------------------- +; Create a new file by creating a directory entry +; then opening the file +; ------------------------------------------------------- +bdos_make: + CALL bdos_check_write ; check Write Protection + LD HL, (CPM_VARS.bdos_info) ; save user FCB + PUSH HL ; on stack + LD HL, CPM_VARS.bdos_efcb ; empty FCB + ; Save FCB address, look for 0xE5 + LD (CPM_VARS.bdos_info), HL ; set empty FCB + ; search firs empty slot in directory + LD C, 0x1 ; compare one byte 0xE5 + CALL bdos_search + CALL bdos_end_of_dir ; found flag + POP HL + LD (CPM_VARS.bdos_info), HL ; restore user FCB address + RET Z ; return if not found (no space in dir) + EX DE, HL + LD HL, FCB_RC ; number or record for this file + ADD HL, DE + + ; clear FCB tail + LD C, 17 ; fcblen-namlen + XOR A +.fcb_set_0: + LD (HL), A + INC HL + DEC C + JP NZ, .fcb_set_0 + + LD HL, FCB_S1 + ADD HL, DE + LD (HL), A ; Current record within extent? + CALL bdos_setcdr + CALL bdos_copy_fcb + JP bdos_set_fwf + +; ------------------------------------------------------- +; Close the current extent, and open the next one to read +; if possible. RMF is true if in read mod +; ------------------------------------------------------- +bdos_open_reel: + XOR A + LD (CPM_VARS.bdos_fcb_closed), A ; clear close flag + CALL bdos_close ; close extent + CALL bdos_end_of_dir ; check space + RET Z ; ret if no more space + ; get extent byte from user FCB + LD HL, (CPM_VARS.bdos_info) + LD BC, FCB_EXT + ADD HL, BC + LD A, (HL) + ; and increment it, mask to 0..31 and store + INC A + AND MAX_EXT + LD (HL), A + JP Z, .overflow + ; mask extent byte + LD B, A + LD A, (CPM_VARS.bdos_extmsk) + AND B + LD HL, CPM_VARS.bdos_fcb_closed + AND (HL) + JP Z, .read_nxt_extent ; read netx extent + JP .extent_in_mem + +.overflow: + LD BC, 0x2 ; S2 + ADD HL, BC + INC (HL) + LD A, (HL) + AND MAX_MOD + JP Z, .error + +.read_nxt_extent: + LD C, FCB_INFO_LEN + CALL bdos_search + CALL bdos_end_of_dir + JP NZ, .extent_in_mem ; jump if success + ; not extent found + LD A, (CPM_VARS.bdos_rfm) + INC A + JP Z, .error ; can not get extent + CALL bdos_make ; make new empty dir entry for extent + CALL bdos_end_of_dir ; chk no space + JP Z, .error ; jmp to error if no space in directory + JP .almost_done +.extent_in_mem: + ; open extent + CALL bdos_open_copy +.almost_done: + CALL bdos_stor_fcb_var ; move updated data to new extent + XOR A ; clear error flag + JP bdos_ret_a +.error: + CALL set_ioerr_1 + JP bdos_set_fwf ; clear bit 7 in s2 + +; ------------------------------------------------------- +; Sequential disk read operation +; ------------------------------------------------------- +bdos_seq_disk_read: + LD A, 0x1 + LD (CPM_VARS.bdos_seqio), A + ; drop through to diskread + +bdos_disk_read: + LD A, TRUE + LD (CPM_VARS.bdos_rfm), A ; dont allow read unwritten data + CALL bdos_stor_fcb_var + LD A, (CPM_VARS.bdos_vrecord) ; next record to read + LD HL, CPM_VARS.bdos_rcount ; number of records in extent + ; in this extent? + CP (HL) + JP C, .recordok + ; no, in next extent, check this exctent fully used + CP 128 + JP NZ, .error_opn + ;open next extent + CALL bdos_open_reel + XOR A + ; reset record number to read + LD (CPM_VARS.bdos_vrecord), A + ; check open status + LD A, (CPM_VARS.bdos_aret) + OR A + JP NZ, .error_opn +.recordok: + ; compute block number to read + CALL bdos_index + ; check if it in bounds + CALL bdos_allocated + JP Z, .error_opn + + CALL bdos_atran ; convert to logical sector + CALL bdos_seek ; seec track and sector + CALL bdos_rdbuff ; read sector + JP bdos_set_nxt_rec + +.error_opn: + JP set_ioerr_1 + +; ------------------------------------------------------- +; Sequential write disk +; ------------------------------------------------------- +bdos_seq_disk_write: + LD A, 0x1 + LD (CPM_VARS.bdos_seqio), A +bdos_disk_write: + LD A, FALSE + LD (CPM_VARS.bdos_rfm), A ; allow open new extent + CALL bdos_check_write ; check write protection + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + CALL bdos_check_rofile ; check RO file + CALL bdos_stor_fcb_var + LD A, (CPM_VARS.bdos_vrecord) ; get record number to write to + CP 128 ; lstrec+1 + JP NC, set_ioerr_1 ; not in range - error + CALL bdos_index ; compute block number + CALL bdos_allocated ; check number + LD C, 0 + JP NZ, .disk_wr1 + CALL bdos_dm_position ; get next block number within FCB + LD (CPM_VARS.bdos_dminx), A ; and store it + LD BC, 0x00 ; start looking for space from start + OR A + JP Z, .nop_block ; zero - not allocated + ; extract previous blk from fcb + LD C, A + DEC BC + CALL bdos_getdm + LD B, H + LD C, L +.nop_block: + CALL bdos_get_block ; get next free nearest block + LD A, L + OR H + JP NZ, .block_ok + ; error, no more space + LD A, 2 + JP bdos_ret_a +.block_ok: + LD (CPM_VARS.bdos_arecord), HL ; set record to access + EX DE, HL ; to DE + LD HL, (CPM_VARS.bdos_info) ; + LD BC, FCB_AL ; 16 + ADD HL, BC + ; small/big disk + LD A, (CPM_VARS.bdos_small_disk) + OR A + LD A, (CPM_VARS.bdos_dminx) ; rel block + JP Z, .alloc_big ; jump for 16bit + CALL bdos_hl_add_a ; HL = HL + A + LD (HL), E ; save record to access + JP .disk_wru + +.alloc_big: + ; save record to acces 16bit + LD C, A + LD B, 0 + ADD HL, BC + ADD HL, BC + LD (HL), E + INC HL + LD (HL), D + + ; Disk write to previously unallocated block +.disk_wru: + LD C, 2 ; C=2 - write to unused disk space +.disk_wr1: + LD A, (CPM_VARS.bdos_aret) ; check status + OR A + RET NZ ; return on error + + PUSH BC ; store C write flag for BIOS + CALL bdos_atran ; convert block number to logical sector + LD A, (CPM_VARS.bdos_seqio) ; get access mode (0=random, 1=sequential, 2=special) + DEC A + DEC A + JP NZ, .normal_wr + ; special + POP BC + PUSH BC + LD A, C ; A = write status flag (2=write unused space) + DEC A + DEC A + JP NZ, .normal_wr + ; fill buffer with zeroes + PUSH HL + LD HL, (CPM_VARS.bdos_buffa) + LD D, A +.fill0: + LD (HL), A + INC HL + INC D + JP P, .fill0 + CALL bdos_setdir ; Tell BIOS buffer address to directory access + LD HL, (CPM_VARS.bdos_arecord1) ; get sector that starts current block + LD C, 2 ; write status flag (2=write unused space) +.fill1: + LD (CPM_VARS.bdos_arecord), HL ; set sector to write + PUSH BC + CALL bdos_seek ; seek track and sector number + POP BC + CALL bdos_wrbuff ; write zeroes + LD HL, (CPM_VARS.bdos_arecord) ; get sector number + LD C, 0 ; normal write flag + LD A, (CPM_VARS.bdos_blmsk) ; block mask + ; write entire block? + LD B, A + AND L + CP B + INC HL + JP NZ, .fill1 ; continue until (BLKMSK+1) sectors written + ; reset sector number + POP HL + LD (CPM_VARS.bdos_arecord), HL + CALL bdos_setdata ; reset DMA address + + ; Normal write. +.normal_wr: + CALL bdos_seek ; Set track and sector + POP BC ; restore C - write status flag + PUSH BC + CALL bdos_wrbuff ; write + POP BC + LD A, (CPM_VARS.bdos_vrecord) ; last file record + LD HL, CPM_VARS.bdos_rcount ; last written record + CP (HL) + JP C, .disk_wr2 + ; update record count + LD (HL), A + INC (HL) + LD C, 2 +.disk_wr2: + DEC C ; patch: NOP + DEC C ; patch: NOP + JP NZ, .no_update ; patch: LD HL,0 + PUSH AF + CALL bdos_get_s2 ; set 'extent written to' flag + AND 0x7f ; clear 7th bit + LD (HL), A + POP AF ; record count + +.no_update: + ; it is full + CP 127 + JP NZ, .set_nxt_rec + ; sequential mode? + LD A, (CPM_VARS.bdos_seqio) + CP 1 + JP NZ, .set_nxt_rec + + CALL bdos_set_nxt_rec ; set next record + CALL bdos_open_reel ; get space in dir + LD HL, CPM_VARS.bdos_aret ; check status + LD A, (HL) + OR A + JP NZ, .no_space ; no more space + + DEC A + LD (CPM_VARS.bdos_vrecord), A ; -1 + +.no_space: + LD (HL), 0x00 ; clear status + +.set_nxt_rec: + JP bdos_set_nxt_rec + +; ------------------------------------------------------- +; Random access seek operation, C=0ffh if read mode +; FCB is assumed to address an active file control block +; (modnum has been set to 1100$0000b if previous bad seek) +; ------------------------------------------------------- +bdos_rseek: + XOR A + LD (CPM_VARS.bdos_seqio), A ; set random mode + +bdos_rseek1: + PUSH BC + LD HL, (CPM_VARS.bdos_info) + EX DE, HL + LD HL, FCB_RN ; Random access record number + ADD HL, DE + LD A, (HL) + AND 7Fh ; [6:0] bits record number to access + PUSH AF + ; get bit 7 + LD A, (HL) + RLA + INC HL + ; get high byte [3:0] bits + LD A, (HL) + RLA + AND 00011111b + ; get extent byte + LD C, A + LD A, (HL) + RRA + RRA + RRA + RRA + AND 0x0f + LD B, A + POP AF + INC HL + ; get next byte + LD L, (HL) + ; check overflow + INC L + DEC L + LD L, 6 + JP NZ, .seek_err ; error 6 if overflow + + ; save current record in FCB + LD HL, FCB_CR + ADD HL, DE + LD (HL), A + + ; check extent byte + LD HL, FCB_EXT + ADD HL, DE + LD A, C + SUB (HL) + JP NZ, .close ; not same as previous + ; check extra s2 byte + LD HL, FCB_S2 + ADD HL, DE + LD A, B + SUB (HL) + AND 0x7f + JP Z, .seek_ok ; same, is ok + +.close: + PUSH BC + PUSH DE + CALL bdos_close ; close current extent + POP DE + POP BC + LD L, 3 ; Cannot close error #3 + LD A, (CPM_VARS.bdos_aret) ; check status + INC A + JP Z, .bad_seek ; status != 0 - error + ; set extent byte to FCB + LD HL, FCB_EXT + ADD HL, DE + LD (HL), C + ; set S2 - extra extent byte + LD HL, FCB_S2 + ADD HL, DE + LD (HL), B + + CALL open + LD A, (CPM_VARS.bdos_aret) + INC A + JP NZ, .seek_ok + POP BC + PUSH BC + LD L, 0x4 ; Seek to unwritten extent #4 + INC C + JP Z, .bad_seek + CALL bdos_make + LD L, 0x5 ; Cannot create new extent #5 + LD A, (CPM_VARS.bdos_aret) + INC A + JP Z, .bad_seek +.seek_ok: + POP BC + XOR A + JP bdos_ret_a +.bad_seek: + PUSH HL + CALL bdos_get_s2 + LD (HL), 11000000b + POP HL +.seek_err: + POP BC + LD A,L + LD (CPM_VARS.bdos_aret), A + JP bdos_set_fwf + +; ------------------------------------------------------- +; Random disk read operation +; ------------------------------------------------------- +bdos_rand_disk_read: + LD C, 0xff ; mode + CALL bdos_rseek ; positioning + CALL Z, bdos_disk_read ; read if ok + RET + +; ------------------------------------------------------- +; Random disk write operation +; ------------------------------------------------------- +bdos_rand_disk_write: + LD C, 0 ; mode + CALL bdos_rseek ; positioning + CALL Z, bdos_disk_write ; read if ok + RET + +; ------------------------------------------------------- +; Compute random record position +; Inp: HL -> FCB +; DE - relative location of record number +; Out: C - r0 byte +; B - r1 byte +; A - r2 byte +; ZF set - Ok, reset - overflow +; ------------------------------------------------------- +bdos_compute_rr: + EX DE, HL ; DE -> FCB + ADD HL, DE ; HL = FCB+DE + LD C, (HL) ; get record number + LD B, 0 ; in BC + LD HL, FCB_EXT + ADD HL, DE + LD A, (HL) ; A = extent + ; calculate BC = recnum + extent*128 + RRCA ; A[0] -> A[7] + AND 0x80 ; ignore other bits + ADD A, C ; add to record number + LD C, A ; C=r0 + LD A, 0 + ADC A, B + LD B, A ; B=r1 + ; + LD A, (HL) ; A = extent + RRCA + AND 0x0f ; A = EXT[4:1] + ADD A, B ; add to r1 byte + LD B, A + + LD HL, FCB_S2 + ADD HL, DE + LD A, (HL) ; A = extra extent bits + ADD A, A ; *2 + ADD A, A ; *4 + ADD A, A ; *8 + ADD A, A ; *16 + PUSH AF ; save flags (need only CF) + ADD A, B ; add to r1 + LD B, A + PUSH AF ; save flags + POP HL ; flags on L + LD A, L ; A bit 0 - CF + POP HL + OR L ; merge both carry flags + AND 0x1 ; mask only CF + RET + +; ------------------------------------------------------- +; Compute logical file size for current FCB. +; Setup r0, r1, r2 bytes - maximum record number +; ------------------------------------------------------- +bdos_get_file_size: + LD C, FN_LEN + CALL bdos_search ; get first dir record (first extent) + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + ; zeroing r0, r1, r2 + LD DE, FCB_RN ; D=0 + ADD HL, DE + PUSH HL + LD (HL), D + INC HL + LD (HL), D + INC HL + LD (HL), D + ; +.get_extent: + CALL bdos_end_of_dir + JP Z, .done ; if not more extents found, return + CALL bdos_getdptra ; HL -> FCB + LD DE, FCB_RC + CALL bdos_compute_rr ; compute random access parameters + POP HL + PUSH HL + ; Now let's compare these values ​​with those indicated + LD E, A + LD A, C + SUB (HL) + INC HL + LD A, B + SBC A, (HL) + INC HL + LD A, E + SBC A, (HL) + JP C, .less_size + ; found larger extent (size), save it to fcb + LD (HL), E + DEC HL + LD (HL), B + DEC HL + LD (HL), C +.less_size: + CALL bdos_search_nxt + JP .get_extent +.done: + POP HL + RET + +; ------------------------------------------------------- +; Set the random record count bytes of the FCB to the number +; of the last record read/written by the sequential I/O calls. +; ------------------------------------------------------- +bdos_set_random: + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + LD DE, FCB_CR ; Current record within extent + CALL bdos_compute_rr ; Compute random access parameters + LD HL, FCB_RN + ADD HL, DE ; HL -> Random access record number + ; set r0, r1, r2 + LD (HL), C + INC HL + LD (HL), B + INC HL + LD (HL), A + RET + +; ------------------------------------------------------- +; Select disk to bdos_curdsk for subsequent input or +; output ops +; ------------------------------------------------------- +bdos_select: + LD HL, (CPM_VARS.bdos_dlog) ; login vector + LD A, (CPM_VARS.bdos_curdsk) ; current drive + LD C, A + CALL bdos_hlrotr ; shift active bit for this drive + PUSH HL + EX DE, HL + CALL bdos_sel_dsk ; select drive + POP HL + CALL Z, bdos_sel_error ; check for error drive + ; new active drive? + LD A, L + RRA + RET C ; no, return + ; yes, update login vector + LD HL, (CPM_VARS.bdos_dlog) + LD C, L + LD B, H + CALL bdos_set_dsk_bit ; set bits in vector + LD (CPM_VARS.bdos_dlog), HL ; store new vector + + JP bdos_initialize + +; ------------------------------------------------------- +; Select disc +; Inp: C=0x0E +; E=drive number 0-A, 1-B .. 15-P +; Out: L=A=0 - ok or 0xFF - error +; ------------------------------------------------------- +bdos_select_disk: + ; check disk change + LD A, (CPM_VARS.bdos_linfo) + LD HL, CPM_VARS.bdos_curdsk + CP (HL) + RET Z ; is same, return + ; login new disk + LD (HL), A + JP bdos_select + +; ------------------------------------------------------- +; Auto select disk by FCB_DR +; ------------------------------------------------------- +bdos_reselect: + LD A, TRUE + LD (CPM_VARS.bdos_resel), A ; set flag + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB + LD A, (HL) ; get specified disk + AND 0x1f ; A = A[4:0] + DEC A ; adjust for 0-A, 1-B and so on + LD (CPM_VARS.bdos_linfo), A ; set as parameter + CP 0x1e ; no change? + JP NC, .no_select + ; change, but save current to old + LD A, (CPM_VARS.bdos_curdsk) + LD (CPM_VARS.bdos_olddsk), A + ; save FCB_DR byte + LD A, (HL) + LD (CPM_VARS.bdos_fcbdsk), A + ; clear FCB_DR[4:0] bits - user code + AND 11100000b + LD (HL), A + CALL bdos_select_disk + +.no_select: + ; set user to FCB_DR low bits + LD A, (CPM_VARS.bdos_userno) + LD HL, (CPM_VARS.bdos_info) + OR (HL) + LD (HL), A + RET + +; ------------------------------------------------------- +; Return version number +; ------------------------------------------------------- +bdos_get_version: + LD A, CPM_VERSION ; 0x22 - v2.2 + JP bdos_ret_a + +; ------------------------------------------------------- +; Reset disk system - initialize to disk 0 +; ------------------------------------------------------- +bdos_reset_disks: + LD HL, 0x00 + LD (CPM_VARS.bdos_rodsk), HL ; clear ro flags + LD (CPM_VARS.bdos_dlog), HL ; clear login vector + XOR A ; disk 0 + LD (CPM_VARS.bdos_curdsk), A ; set disk 'A' as current + LD HL, dma_buffer ; set 'DMA' buffer address to default + LD (CPM_VARS.bdos_dmaad), HL + CALL bdos_setdata + JP bdos_select ; select drive 'A' + +; ------------------------------------------------------- +; Open file +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_open_file: + CALL bdos_clear_s2 + CALL bdos_reselect + JP open + +; ------------------------------------------------------- +; Close file +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_close_file: + CALL bdos_reselect + JP bdos_close + +; ------------------------------------------------------- +; Search for first occurrence of a file in directory +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_search_first: + LD C, 0x00 ; special search + EX DE, HL + LD A, (HL) + CP '?' ; '?' is first byte? + JP Z, .qselect ; return first matched entry + + CALL bdos_getexta ; get extent byte from FCB + LD A, (HL) + CP '?' ; it is '?' + CALL NZ, bdos_clear_s2 ; set S2 to 0 if not + CALL bdos_reselect ; select disk by FCB + LD C, FCB_INFO_LEN ; match 1first 15 bytes +.qselect: + CALL bdos_search ; search first entry + JP bdos_dir_to_user ; move entry to user space + +; ------------------------------------------------------- +; Search for next occurrence of a file name +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_search_next: + LD HL, (CPM_VARS.bdos_searcha) ; restore FCB address + LD (CPM_VARS.bdos_info), HL ; set as parameter + CALL bdos_reselect ; select drive by FCB + CALL bdos_search_nxt ; search next + JP bdos_dir_to_user ; move entry to user space + +; ------------------------------------------------------- +; Remove directory +; Inp: DE -> FCB +; Out: BA and HL - error. +; ------------------------------------------------------- +bdos_rm_file: + CALL bdos_reselect ; Select drive by FCB + CALL bdos_era_file ; Delete file + JP bdos_ret_dirloc ; Return directory location in A + +; ------------------------------------------------------- +; Read next 128b record +; Inp: DE -> FCB +; Out: BA and HL - error. +; A=0 - Ok, +; 1 - end of file, +; 9 - invalid FCB, +; 10 - media changed, +; 0FFh - hardware error. +; ------------------------------------------------------- +bdos_read_file: + CALL bdos_reselect ; select drive + JP bdos_seq_disk_read ; and read + +; ------------------------------------------------------- +; Write next 128b record +; Inp: DE -> FCB +; Out: BA and HL - error. +; A=0 - Ok, +; 1 - directory full, +; 2 - disc full, +; 9 - invalid FCB, +; 10 - media changed, +; 0FFh - hardware error. +; ------------------------------------------------------- +bdos_write_file: + CALL bdos_reselect ; select drive + JP bdos_seq_disk_write ; and write + +; ------------------------------------------------------- +; Create file +; Inp: DE -> FCB. +; Out: Error in BA and HL +; A=0 - Ok, +; 0FFh - directory is full. +; ------------------------------------------------------- +bdos_make_file: + CALL bdos_clear_s2 ; clear S2 byte + CALL bdos_reselect ; select drive + JP bdos_make ; and make file + +; ------------------------------------------------------- +; Rename file. New name, stored at FCB+16 +; Inp: DE -> FCB. +; Out: Error in BA and HL +; A=0-3 if successful; +; A=0FFh if error. +; ------------------------------------------------------- +bdos_ren_file: + CALL bdos_reselect ; select drive + CALL bdos_rename ; rename file + JP bdos_ret_dirloc ; Return directory location in A + +; ------------------------------------------------------- +; Return bitmap of logged-in drives +; Out: bitmap in HL. +; ------------------------------------------------------- +bdos_get_login_vec: + LD HL, (CPM_VARS.bdos_dlog) + JP sthl_ret + +; ------------------------------------------------------- +; Return current drive +; Out: A - currently selected drive. 0 => A:, 1 => B: etc. +; ------------------------------------------------------- +bdos_get_cur_drive: + LD A, (CPM_VARS.bdos_curdsk) + JP bdos_ret_a + +; ------------------------------------------------------- +; Set DMA address +; Inp: DE - address of DMA buffer +; ------------------------------------------------------- +bdos_set_dma_addr: + EX DE, HL + LD (CPM_VARS.bdos_dmaad), HL + JP bdos_setdata + +; ------------------------------------------------------- +; fn27: Return the address of cur disk allocation map +; Out: HL - address +; ------------------------------------------------------- +bdos_get_alloc_addr: + LD HL, (CPM_VARS.bdos_alloca) + JP sthl_ret + +; ------------------------------------------------------- +; Get write protection status +; ------------------------------------------------------- +bdos_get_wr_protect: + LD HL, (CPM_VARS.bdos_rodsk) + JP sthl_ret + +; ------------------------------------------------------- +; Set file attributes (ro, system) +; ------------------------------------------------------- +bdos_set_attr: + CALL bdos_reselect + CALL bdos_update_attrs + JP bdos_ret_dirloc + +; ------------------------------------------------------- +; Return address of disk parameter block of the +; current drive +; ------------------------------------------------------- +bdos_get_dpb: + LD HL, (CPM_VARS.bdos_dpbaddr) + +; ------------------------------------------------------- +; Common code to return Value from BDOS functions +; ------------------------------------------------------- +sthl_ret: + LD (CPM_VARS.bdos_aret), HL + RET + +; ------------------------------------------------------- +; Get/set user number +; Inp: E - 0..15 - set user. 0xFF - get user +; Out: A - returns user number +; ------------------------------------------------------- +bdos_set_user: + LD A, (CPM_VARS.bdos_linfo) + CP 0xff + JP NZ, .set_user + LD A, (CPM_VARS.bdos_userno) + JP bdos_ret_a +.set_user: + AND 0x1f ; mask for 0..31 but will be < 16 + LD (CPM_VARS.bdos_userno), A + RET + +; ------------------------------------------------------- +; Random read. Record specified in the random record +; count area of the FCB, at the DMA address +; Inp: DE -> FCB +; Out: Error codes in BA and HL. +; ------------------------------------------------------- +bdos_rand_read: + CALL bdos_reselect ; select drive by FCB + JP bdos_rand_disk_read ; and read + +; ------------------------------------------------------- +; Random access write record. +; Record specified in the random record count area of the FCB, at the DMA address +; Inp: DE -> FCB +; Out: Error codes in BA and HL. +; ------------------------------------------------------- +bdos_rand_write: + CALL bdos_reselect ; select drive by FCB + JP bdos_rand_disk_write ; and write + +; ------------------------------------------------------- +; Compute file size. +; Set the random record count bytes of the FCB to the +; number of 128-byte records in the file. +; ------------------------------------------------------- +bdos_compute_fs: + CALL bdos_reselect ; select drive by FCB + JP bdos_get_file_size ; and get file size + +; ------------------------------------------------------- +; Selectively logoff (reset) disc drives +; Out: A=0 - Ok, 0xff if error +; ------------------------------------------------------- +bdos_reset_drives: + LD HL, (CPM_VARS.bdos_info) ; HL - drives to logoff map + LD A, L + CPL + LD E, A + LD A, H + CPL + LD HL, (CPM_VARS.bdos_dlog) ; get vector + AND H ; reset in hi byte + LD D, A ; and store in D + LD A, L ; reset low byte + AND E + LD E, A ; and store in E + LD HL, (CPM_VARS.bdos_rodsk) ; HL - disk RO map + EX DE, HL + LD (CPM_VARS.bdos_dlog), HL ; store new login vector + ; reset RO flags + LD A, L + AND E + LD L, A + LD A, H + AND D + LD H, A + ; and store new value + LD (CPM_VARS.bdos_rodsk), HL + RET + +; ------------------------------------------------------- +; Reach this point at the end of processing +; to return the data to the user. +; ------------------------------------------------------- +bdos_goback: + LD A, (CPM_VARS.bdos_resel) ; check for selection drive by user FCB + OR A + JP Z, bdos_ret_mon ; return if not selected + + LD HL, (CPM_VARS.bdos_info) ; HL -> FCB_DR + LD (HL), 0 + LD A, (CPM_VARS.bdos_fcbdsk) + OR A ; restore drive + JP Z, bdos_ret_mon ; return if default + LD (HL), A ; restore + LD A, (CPM_VARS.bdos_olddsk) ; previous drive + LD (CPM_VARS.bdos_linfo), A ; set parameter + CALL bdos_select_disk ; select previous drive + +; ------------------------------------------------------- +; Return from the disk monitor +; ------------------------------------------------------- +bdos_ret_mon: + LD HL, (CPM_VARS.bdos_usersp) ; return user stack + LD SP, HL + LD HL, (CPM_VARS.bdos_aret) ; get return value + LD A, L ; ver 1.4 CP/M compatibility + LD B, H + RET + +; ------------------------------------------------------- +; Random disk write with zero fill of +; unallocated block +; ------------------------------------------------------- +bdos_rand_write_z: + CALL bdos_reselect ; select drive + LD A, 2 ; special write mode + LD (CPM_VARS.bdos_seqio), A + LD C, FALSE ; write indicator + CALL bdos_rseek1 ; seek position in a file + CALL Z, bdos_disk_write ; write if no errors + RET + +; ------------------------------------------------------- +; Unuser initialized data +; ------------------------------------------------------- +filler: + DB 0xF1, 0xE1 ; POP AF, POP HL + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xC800 +FILL_SIZE EQU 0xE00-CODE_SIZE + + DISPLAY "| BDOS\t| ",/H,bdos_start," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ASSERT bdos_rand_write_z = 0xd55e + + +FILLER + DS FILL_SIZE, 0xFF + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_ddd05ed7/bios.asm b/CPM_v2.2_r8_ddd05ed7/bios.asm new file mode 100644 index 0000000..e12ac0c --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/bios.asm @@ -0,0 +1,824 @@ +; ======================================================= +; Ocean-240.2 +; +; CP/M BIOS +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "mon_entries.inc" + + IFNDEF BUILD_ROM + OUTPUT bios.bin + ENDIF + + MODULE BIOS + + ORG 0xD600 + +; ------------------------------------------------------- +; BIOS JUMP TABLE +; ------------------------------------------------------- +boot_f: + JP bios_boot +wboot_f: + JP bios_wboot + +; ------------------------------------------------------- +; console status to reg-a +; ------------------------------------------------------- +const_f: + JP MON.non_con_status + +; ------------------------------------------------------- +; console character to reg-a +; ------------------------------------------------------- +conin_f: + JP MON.mon_con_in + +; ------------------------------------------------------- +; console character from c to console out +; ------------------------------------------------------- +conout_f: + JP MON.mon_con_out + +; ------------------------------------------------------- +; list device out +; ------------------------------------------------------- +list_f: + JP MON.mon_char_print + +; ------------------------------------------------------- +; punch device out +; ------------------------------------------------------- +punch_f: + JP MON.mon_serial_out + +; ------------------------------------------------------- +; reader character in to reg-a +; ------------------------------------------------------- +reader_f: + JP MON.mon_serial_in + +; ------------------------------------------------------- +; move to home position, treat as track 00 seek +; ------------------------------------------------------- +home_f: + JP home + +; ------------------------------------------------------- +; select disk given by register c +; ------------------------------------------------------- +seldsk_f: + JP sel_disk +settrk_f: + JP set_trk +setsec_f: + JP set_sec + +; ------------------------------------------------------- +; Set DMA address from BC +; ------------------------------------------------------- +setdma_f: + JP set_dma +read_f: + JP read +write_f: + JP write +listst_f: + JP list_st +sectran_f: + JP sec_tran + +; ------------------------------------------------------- +; Reserved +; ------------------------------------------------------- + JP warm_boot + JP warm_boot + +; ------------------------------------------------------- +; Tape read +; ------------------------------------------------------- +tape_read_f: + JP MON.mon_tape_read + +; ------------------------------------------------------- +; Tape write +; ------------------------------------------------------- +tape_write_f: + JP MON.mon_tape_write + +; ------------------------------------------------------- +; Tape wait block +; ------------------------------------------------------- +tape_wait_f: + JP MON.mon_tape_wait + + JP warm_boot ; + +disk_a_size DW 0x0040 ; 64k disk A size +disk_b_size DW 0x02d0 ; 720k disk B size +disk_c_size DW 0x02d0 ; 720k disk C size +bios_var04 DB 0x50 +bios_var05 DB 0x50 + +; ------------------------------------------------------- +; cold start +; ------------------------------------------------------- +bios_boot: + LD HL, (bdos_ent_addr) ; 0xba06 + LD DE, 0x10000-CCP_RAM.bdos_enter_jump ; 0x45fa + ADD HL, DE ; 0xba06+0x45fa=10000 + LD A, H + OR L + JP Z, bios_signon + + ; >> r8 + LD HL, (bios_var0) ; if bios_var0 = 0xaaaa (initialized) + LD DE, 0x5556 + ADD HL, DE ; 0xaaaa+0x5556=0x10000 if initialized + LD A, H + OR L + JP Z, bios_signon ; if initialized, go to logon, skip init + ; << + + ; Init DMA buffer + LD HL, dma_buffer ; 0x8000 + LD B, DMA_BUFF_SIZE ; 0x80 +.init_dma_buff: + LD (HL), FILE_DELETED ; 0xE5 + INC HL + DEC B + JP NZ, .init_dma_buff + + ; Init RAM disk + LD HL, dma_buffer + LD DE, 0x0000 + LD B, 8 + +.init_ram_dsk: + PUSH BC + CALL MON.ram_disk_write + POP BC + INC DE + DEC B + JP NZ, .init_ram_dsk + + ; Init user to 0 and drive to 0 + XOR A + LD (cur_user_drv), A + + ; init bios variables + CALL bios_init_ilv ; r8 + +bios_signon: + LD SP, bios_stack + ; Print CP/M hello message + LD HL, msg_hello + CALL print_strz + JP bios_wboot + +;bios_wboot: +; LD SP, bios_stack + +; r8 >> +bios_init_ilv: + LD HL, bios_var0 + LD DE, bios_ini_vals + LD C, 13 + CALL mov_dlhe_c + LD A, (bios_var05) ; 0x50 + LD (bios_var2), A + RET +; << + +; ------------------------------------------------------- +; BIOS Warm start entry +; ------------------------------------------------------- +bios_wboot: ; r8 + + LD HL, (bios_var0) + LD DE, 0x5556 ; 0xaaaa + 0x5556 = 0x10000 + ADD HL, DE + LD A, H + OR L + CALL NZ, bios_init_ilv ; init if not initialized before + LD SP, bios_stack + + ; Move CPP from 0xC000 to 0xB200 + LD HL, CCP_DST_ADDR + LD DE, CCP_SRC_ADDR + LD BC, CCP_SIZE +.move_ccp: + LD A, (DE) + LD (HL), A + INC DE + INC HL + DEC BC + LD A, B + OR C + JP NZ, .move_ccp + + ; Init variables with zeroes + LD HL, CPM_VARS.cpm_vars_start + LD BC, CPM_VARS.ccp_vars_size ; 213 + +.clr_cpm_vars: + LD (HL), 0x0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .clr_cpm_vars + + LD A, FILE_DELETED + LD (CPM_VARS.bdos_efcb), A ; mark empty FCB + LD A, low dma_buffer ; 0x80 + LD (CPM_VARS.bdos_dmaad), A ; 0x0080 + + ; Move DPH + DPB to RAM + LD HL, CPM_VARS.DPH_RAM + LD DE, dph_disk_a + LD BC, DPB_END-dph_disk_a ; 0x39 -> 57d + +.move_dph: + LD A, (DE) + LD (HL), A + INC HL + INC DE + DEC BC + LD A, B + OR C + JP NZ, .move_dph + + LD BC, dma_buffer ; DMA default buffer addr + CALL setdma_f + + ; Setup JP to Warm boot after CPU reset + LD A, JP_OPCODE + LD (warm_boot), A + LD HL, wboot_f + LD (warm_boot_addr), HL + + ; Setup JP to BDOS entrance + LD (jp_bdos_enter), A + LD HL, CCP_RAM.bdos_enter_jump + LD (bdos_ent_addr), HL + + LD HL, CPM_VARS.DPB_A_RAM + LD C, 0xf + LD DE, dpb_ram + LD A, (disk_a_size+1) + OR A + JP Z, set_curr_dpb + LD DE, dpb_empty + +set_curr_dpb: + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_B_RAM + LD C, 0xf + LD DE, dpb_flop_360k + LD A, (disk_b_size+1) + CP 0x1 + JP Z, .l1 + LD DE, dpb_flop_720k +.l1 + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_C_RAM + LD C, 0xf + LD A, (disk_c_size+1) + CP 0x2 + JP Z, .l2 + LD DE, dpb_flop_360k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_720k + JP .l3 +.l2 + LD DE, dpb_flop_720k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_360k +.l3 + CALL mov_dlhe_c + XOR A + LD (CPM_VARS.slicer_has_data),A + LD (CPM_VARS.slicer_uninited_count),A + LD A,(cur_user_drv) + LD C,A + ; go to CCP + JP CCP_DST_ADDR + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +mov_dlhe_c: + LD A,(DE) ; [DE]->[HL] + LD (HL),A + INC HL + INC DE + DEC C + JP NZ, mov_dlhe_c + RET + +list_st: + XOR A + RET + +; ------------------------------------------------------- +; Select disk +; Inp: C - disk, +; E - active drive flag +; Out: HL -> DPH +; ------------------------------------------------------- +sel_disk: + LD HL, 0x00 + LD A, C + CP CTRL_C + RET NC + + LD (CPM_VARS.cur_disk), A + OR A + JP Z, .get_dph_addr ; skip next for disk 0 - RAM disk + LD A, E ; selected disk map + AND 0x1 ; bit 0 is set if disk already selected + JP NZ, .get_dph_addr + ; Reset disk + LD (CPM_VARS.slicer_has_data), A + LD (CPM_VARS.slicer_uninited_count), A + ; calc DPH address of new drive +.get_dph_addr: + LD L, C + LD H, 0x0 + ADD HL, HL ; *2 + ADD HL, HL ; *4 + ADD HL, HL ; *8 + ADD HL, HL ; *16 (size of DBH) + LD DE, CPM_VARS.DPH_RAM + ADD HL, DE + RET + +; ------------------------------------------------------- +; move to track 00 +; ------------------------------------------------------- +home: + LD A, (CPM_VARS.cur_disk) + OR A + JP Z, .is_default + LD A, (CPM_VARS.slicer_need_save) + OR A + JP NZ, .is_default + LD (CPM_VARS.slicer_has_data), A ; set to 0, no data + +.is_default: + LD C, 0 ; set track to 0 + +; ------------------------------------------------------- +; set track address (0..76) for subsequent read/write +; ------------------------------------------------------- +set_trk: + LD HL, CPM_VARS.curr_track + LD (HL), C + RET + +; ------------------------------------------------------- +; set sector address (1,..., 26) for subsequent read/write +; ------------------------------------------------------- +set_sec: + LD HL, CPM_VARS.curr_sec + LD (HL), C + RET + +; ------------------------------------------------------- +; set subsequent dma address (initially 80h) +; ------------------------------------------------------- +set_dma: + LD L, C + LD H, B + LD (CPM_VARS.dma_addr), HL + RET + +sec_tran: + LD L, C + LD H, B + RET + +; ------------------------------------------------------- +; read track/sector to preset dma address +; ------------------------------------------------------- +read: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ, read_phys ; for physical disk use special routine + CALL ram_disk_calc_addr + CALL MON.ram_disk_read + XOR A + RET + +; ------------------------------------------------------- +; write track/sector from preset dma address +; ------------------------------------------------------- +write: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ,write_phys + CALL ram_disk_calc_addr + CALL MON.ram_disk_write + XOR A + RET + +; ------------------------------------------------------- +; Calculate address for current sector and track +; ------------------------------------------------------- +ram_disk_calc_addr: + LD HL, CPM_VARS.curr_track + ; HL = cur_track * 16 + LD L, (HL) + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ; DE = HL + cur_sec + EX DE, HL + LD HL, CPM_VARS.curr_sec + LD L, (HL) + LD H, 0x0 + ADD HL, DE + EX DE, HL + ; store address + LD HL, (CPM_VARS.dma_addr) + RET + +read_phys: + CALL read_phys_op + RET + +write_phys: + CALL write_phys_op + RET + +read_phys_op: + XOR A + ; reset counter + LD (CPM_VARS.slicer_uninited_count), A + LD A, 0x1 + LD (CPM_VARS.tmp_slicer_operation), A ; 0 - write; 1 - read + LD (CPM_VARS.tmp_slicer_can_read), A ; enable read fron disk + LD A, 0x2 + LD (CPM_VARS.tmp_slicer_flush), A ; disable flush data to disk + JP base_read_write + +write_phys_op: + XOR A + LD (CPM_VARS.tmp_slicer_operation), A + LD A, C + LD (CPM_VARS.tmp_slicer_flush), A + CP 0x2 + JP NZ, .mode_ne_2 + LD A, 0x10 ; 2048/128 + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_uninited_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_uninited_track), A + LD A, (CPM_VARS.curr_sec) + LD (CPM_VARS.slicer_uninited_sector_128), A +.mode_ne_2: + LD A, (CPM_VARS.slicer_uninited_count) + OR A + JP Z, slicer_read_write + DEC A + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_uninited_disk + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_uninited_track + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_sec) + LD HL, CPM_VARS.slicer_uninited_sector_128 + CP (HL) + JP NZ, slicer_read_write + INC (HL) + LD A, (HL) + CP 36 ; Sectors per track + JP C, .no_inc_track + LD (HL), 0x0 + LD A, (CPM_VARS.slicer_uninited_track) + INC A + LD (CPM_VARS.slicer_uninited_track), A + +.no_inc_track: + XOR A + LD (CPM_VARS.tmp_slicer_can_read), A + JP base_read_write + +; -------------------------------------------------- +slicer_read_write: + XOR A + LD (CPM_VARS.slicer_uninited_count), A + INC A + LD (CPM_VARS.tmp_slicer_can_read), A + +; -------------------------------------------------- +base_read_write: + XOR A + LD (CPM_VARS.tmp_slicer_result), A + LD A, (CPM_VARS.curr_sec) + OR A + RRA + OR A + RRA + LD (CPM_VARS.tmp_slicer_real_sector), A + LD HL, CPM_VARS.slicer_has_data + LD A, (HL) + LD (HL), 0x1 + OR A + JP Z, .no_data + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_disk + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_track + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD HL, CPM_VARS.slicer_real_sector + CP (HL) + JP Z,calc_sec_addr_in_bfr +.pos_diff: + LD A, (CPM_VARS.slicer_need_save) + OR A + CALL NZ, slicer_save_buffer ; save buffer if needed +.no_data: + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_track), A + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD (CPM_VARS.slicer_real_sector), A + LD A, (CPM_VARS.tmp_slicer_can_read) + OR A + CALL NZ,slicer_read_buffer + XOR A + LD (CPM_VARS.slicer_need_save), A + +; -------------------------------------------------- +calc_sec_addr_in_bfr: + LD A, (CPM_VARS.curr_sec) + AND 0x3 + LD L, A + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL ; *128 + LD DE, CPM_VARS.slicer_buffer + ADD HL, DE + EX DE, HL + LD HL, (CPM_VARS.dma_addr) + LD C, 0x80 + LD A, (CPM_VARS.tmp_slicer_operation) + OR A + JP NZ, .no_save + LD A, 0x1 + LD (CPM_VARS.slicer_need_save), A + EX DE, HL +.no_save: + LD A, (DE) + INC DE + LD (HL), A + INC HL + DEC C + JP NZ, .no_save + LD A, (CPM_VARS.tmp_slicer_flush) + CP 0x1 + LD A, (CPM_VARS.tmp_slicer_result) + RET NZ + OR A + RET NZ + XOR A + LD (CPM_VARS.slicer_need_save), A + CALL slicer_save_buffer + LD A, (CPM_VARS.tmp_slicer_result) + RET + +; -------------------------------------------------- +slicer_save_buffer: + CALL slicer_get_floppy_args + LD C, 0xA6 ; VG93 CMD + CALL MON.write_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_read_buffer: + CALL slicer_get_floppy_args + LD C, 0x86 ; VG93 CMD + CALL MON.read_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_get_floppy_args: + LD HL, CPM_VARS.tmp_buff9 + LD A, (CPM_VARS.slicer_disk) + DEC A + JP Z, .non_interleave + LD HL, interleave_0 +.non_interleave + LD A, (CPM_VARS.slicer_real_sector) + ADD A, L + LD L, A + LD E, (HL) + LD A, (CPM_VARS.slicer_track) + LD D, A + LD HL, CPM_VARS.slicer_buffer + LD A, (CPM_VARS.slicer_disk) + RET + + +; ------------------------------------------------------- +; Print zerro ended string +; Inp: HL -> string +; ------------------------------------------------------- +print_strz: + LD A, (HL) + OR A + RET Z + LD C, A + PUSH HL + CALL conout_f + POP HL + INC HL + JP print_strz + +msg_hello: + DB ASCII_ESC, "6", "0" ; Режим 32x25 60 + DB ASCII_ESC, "8", 3 + DB ASCII_ESC, "5% " + DB ASCII_ESC, "4", "2" + DB ASCII_ESC, "1", 6, 0xE1, 0xF6, 0xFE, 0x01 + DB ASCII_ESC, "40" + DB ASCII_ESC, "1", 10, 0xE5, 0xF2, 0xF9, 0x01 + DB ASCII_ESC, "4", "1" + DB ASCII_ESC, "5", 0x21, 0x27 + DB "OKEAH-240 CP/M (V2.2) REL.8\r\n\n", 0 + + + +; -------------------------------------------------- +; Disk parameters blocks in ROM (DPBs) +; Tables of memory that describe the characteristics +; of discs on our system. There is one DPB for each +; disc type + +; ---------------------------------------- +; Block size | No of sectors | BSH | BLM | +; ---------------------------------------- +; 1K | 8 | 3 | 7 | +; 2K | 16 | 4 | 15 | +; 4K | 32 | 5 | 31 | +; 8K | 64 | 6 | 63 | +; 16K | 128 | 7 | 127 | +; ---------------------------------------- + +; ------------------------------------- +; Block size| Extent mask (EXM) | +; | Small disk | Large disk | +; ------------------------------------- +; 2K | 1 | 0 | +; 4K | 3 | 1 | +; 8K | 7 | 3 | +; 16K | 15 | 7 | +; ------------------------------------- +; CKS - number of dir sectors to check before write, 0 for HDD + +; For RAM-Disk 64k +dpb_ram: + DW 0010h ; SPT Sector (128b) per track (16d) + DB 03h ; BSH 1k + DB 07h ; BLM 1k; Allocation block size = (BLM + 1) * 128 = 1k + DB 00h ; EXM extent mask + DW 63 ; DSM Disk size blocks - 1 + DW 31 ; DRM Dir elements - 1 + DB 10000000b ; AL0 Dir map byte 1 + DB 00000000b ; AL1 Dir map byte 2 + DW 0008h ; CKS checksum vector size (8 sectors=1k) + DW 0000h ; OFF (tracks reserved for system) + +dpb_empty: + DS 15, 0xff + +; For FLOPPY 720k +dpb_flop_720k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 00h ; EXM extent mask + DW 359 ; DSM Disk size blocks - 1 (359d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +; For FLOPPY 360k +dpb_flop_360k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 01h ; EXM extent mask + DW 179 ; DSM Disk size blocks - 1 (179d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +bios_ini_vals: + DB 0xaa, 0xaa, 0, 0xff, 1, 8, 6, 4, 2, 9, 7, 5, 3 + +; -------------------------------------------------- +; Disk parameters headers in ROM +; -------------------------------------------------- +; Disk A RAM +dph_disk_a: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + ; 0xda00 + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_A_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_A ; Check Vector pointer + DW CPM_VARS.AL_MAP_A ; Allocation map pointer + +; Disk B Floppy +dph_disk_b: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_B_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_B ; Check Vector pointer + DW CPM_VARS.AL_MAP_B ; Allocation map pointer + +; Disk C Floppy +dph_disk_c: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_C_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_C ; Check Vector pointer + DW CPM_VARS.AL_MAP_C ; Allocation map pointer + + + +res_data: ; 0xda28 + DB 1,8,6,4,2,9,7,5,3 +DPB_END EQU $ + DB 0x0e, 3 + + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xD600 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| BIOS\t| ",/H,boot_f," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT bios_wboot = 0xd6a8 + ASSERT set_curr_dpb = 0xd722 + ASSERT sel_disk = 0xd781 + ASSERT home = 0xd7a7 + ASSERT ram_disk_calc_addr = 0xd7eb + ASSERT write_phys_op = 0xd81e + ASSERT base_read_write = 0xd88a + ASSERT calc_sec_addr_in_bfr = 0xd8e4 + ASSERT slicer_save_buffer = 0xd928 + ASSERT print_strz = 0xd95e + ASSERT msg_hello = 0xd96b + ASSERT dpb_ram = 0xd9af + ASSERT dph_disk_a = 0xd9f8 + ASSERT res_data = 0xda28 + +FILLER: + DS FILL_SIZE, 0xff + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_ddd05ed7/ccp_ram.asm b/CPM_v2.2_r8_ddd05ed7/ccp_ram.asm new file mode 100644 index 0000000..962f044 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/ccp_ram.asm @@ -0,0 +1,1525 @@ +; ====================================================== +; Ocean-240.2 +; CP/M V2.2 R8. +; CCP (Console Command Processor) Resident part +; ORG C000 at RPM, moved to B200-BA09 +; +; Disassembled by Romych 2026-02-01 +; ====================================================== + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + + IFNDEF BUILD_ROM + OUTPUT ccp_ram.bin + ENDIF + + MODULE CCP_RAM + + ORG 0xB200 + +ccp_ram_ent: + JP ccp_ent + JP ccp_clear_buff + +ccp_inbuff: + DB 0x7F, 0x00 + +ccp_inp_line: + DB ASCII_SP, ASCII_SP + +msg_copyright: + DB " COPYRIGHT (C) 1979, DIGITAL RESEARCH ", 0x00 + DS 73, 0x0 + +ccp_inp_line_addr: + DW ccp_inp_line + +cur_name_ptr: + DW 0h + +; --------------------------------------------------- +; Call BDOS function 2 (C_WRITE) - Console output +; Inp: A - char to output +; --------------------------------------------------- +ccp_print: + LD E, A + LD C, 2 + JP jp_bdos_enter + +; --------------------------------------------------- +; Put char to console +; Inp: A - char +; --------------------------------------------------- +ccp_putc: + PUSH BC + CALL ccp_print + POP BC + RET + +ccp_out_crlf: + LD A, ASCII_CR + CALL ccp_putc + LD A, ASCII_LF + JP ccp_putc +ccp_out_space: + LD A,' ' + JP ccp_putc + +; --------------------------------------------------- +; Out message from new line +; Inp: BC -> Message +; --------------------------------------------------- +ccp_out_crlf_msg: + PUSH BC + CALL ccp_out_crlf + POP HL + +; --------------------------------------------------- +; Out asciiz message +; Inp: HL -> Message +; --------------------------------------------------- +ccp_out_msg: + LD A, (HL) ;= "READ ERROR" + OR A + RET Z + INC HL + PUSH HL + CALL ccp_print + POP HL + JP ccp_out_msg + +; --------------------------------------------------- +; Call BDOS function 13 (DRV_ALLRESET) - Reset discs +; --------------------------------------------------- +ccp_bdos_drv_allreset: + LD C,13 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS function 14 (DRV_SET) - Select disc +; Inp: A - disk +; --------------------------------------------------- +ccp_bdos_drv_set: + LD E, A + LD C, 14 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS fn and return result +; Inp: C - fn no +; Out: A - error + 1 +; --------------------------------------------------- +ccp_call_bdos: + CALL jp_bdos_enter + LD (ccp_bdos_result_code), A + INC A + RET + +; --------------------------------------------------- +; BDOS function 15 (F_OPEN) - Open file /Dir +; Inp: DE -> FCB +; Out: A=0 for error, or 1-4 for success +; --------------------------------------------------- +ccp_bdos_open_f: + LD C, 15 + JP ccp_call_bdos + +; --------------------------------------------------- +; Open file by current FCB +; --------------------------------------------------- +ccp_open_cur_fcb: + XOR A + LD (ccp_current_fcb_cr), A ; clear current record counter + LD DE, ccp_current_fcb + JP ccp_bdos_open_f + +; --------------------------------------------------- +; BDOS function 16 (F_CLOSE) - Close file +; --------------------------------------------------- +ccp_bdos_close_f: + LD C, 16 + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 17 (F_SFIRST) - search for first +; Out: A = 0 in error, 1-4 if success +; --------------------------------------------------- +ccp_bdos_find_first: + LD C, 17 + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 18 (F_SNEXT) - search for next +; Out: A = 0 in error, 1-4 if success +; --------------------------------------------------- +ccp_bdos_find_next: + LD C, 18 + JP ccp_call_bdos ; BDOS 18 (F_SNEXT) - search for next ? + +; --------------------------------------------------- +; Call BDOS F_FIRST with current FCB +; --------------------------------------------------- +ccp_find_first: + LD DE, ccp_current_fcb + JP ccp_bdos_find_first + +; --------------------------------------------------- +; Call BDOS function 19 (F_DELETE) - delete file +; --------------------------------------------------- +ccp_bdos_era_file: + LD C,19 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS and set ZF by result +; --------------------------------------------------- +ccp_bdos_enter_zf: + CALL jp_bdos_enter + OR A + RET + +; --------------------------------------------------- +; Read next 128 bytes of file +; Inp: DE -> FCB +; Out: a = 0 - ok; +; 1 - EOF; +; 9 - invalid FCB; +; 10 - Media changed; +; 0xFF - HW error. +; --------------------------------------------------- +ccp_bdos_read_f: + LD C, 20 + JP ccp_bdos_enter_zf + +; --------------------------------------------------- +; Read file by current FCB +; --------------------------------------------------- +ccp_read_f_fcb: + LD DE, ccp_current_fcb + JP ccp_bdos_read_f + +; --------------------------------------------------- +; Call BDOS function 21 (F_WRITE) - write next record +; --------------------------------------------------- +ccp_bdos_f_write: + LD C, 21 + JP ccp_bdos_enter_zf + +; --------------------------------------------------- +; Call BDOS function 22 (F_MAKE) - create file +; --------------------------------------------------- +ccp_bdos_make_f: + LD C, 22 + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 23 (F_RENAME) - Rename file +; --------------------------------------------------- +ccp_bdos_rename_f: + LD C, 23 + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS function 32 (F_USERNUM) for get user number +; Out: A - user no +; --------------------------------------------------- +ccp_bdos_get_user: + LD E, 0xff + +; --------------------------------------------------- +; Call BDOS function 32 (F_USERNUM) set user number +; Inp: E - user no 0-15, or 0xFF for get user +; Out: A - user no +; --------------------------------------------------- +ccp_bdos_set_user: + LD C, 32 + JP jp_bdos_enter + +; --------------------------------------------------- +; Get user no and store in upper nibble of sys var +; --------------------------------------------------- +ccp_set_cur_drv: + CALL ccp_bdos_get_user + ADD A, A + ADD A, A + ADD A, A + ADD A, A ; user no at upper nibble + LD HL, ccp_cur_drive + OR (HL) + LD (cur_user_drv), A + RET + +; --------------------------------------------------- +; Replace new drive in lower nibble of sys var +; reset user no +; Out: A - drive no +; --------------------------------------------------- +ccp_reset_cur_drv: + LD A, (ccp_cur_drive) + LD (cur_user_drv), A + RET + +; --------------------------------------------------- +; Uppercase character a..z +; Inp: A - character +; Out: A - character in upper case +; --------------------------------------------------- +char_to_upper: + CP 'a' + RET C + CP '{' + RET NC + ; character in a..z range + AND 0x5f + RET + +; --------------------------------------------------- +; Get user input from console or from batch +; file $$$.SUB +; --------------------------------------------------- +ccp_get_inp: + LD A, (ccp_batch) + OR A + JP Z, ccp_gin_no_batch + + ; select drive A for $$$.SUB + LD A, (ccp_cur_drive) + OR A + LD A, 0x0 + CALL NZ, ccp_bdos_drv_set + + ; open batch file + LD DE, ccp_batch_fcb + CALL ccp_bdos_open_f + JP Z, ccp_gin_no_batch ; go to con inp if file not found + + ; read last record + LD A, (ccp_batch_fcb_rc) + DEC A + LD (ccp_batch_fcb_cr), A + LD DE, ccp_batch_fcb + CALL ccp_bdos_read_f + JP NZ, ccp_gin_no_batch ; stop on EOF + + ; move data from file to buffer + LD DE, ccp_inbuff+1 + LD HL, dma_buffer + LD B, DMA_BUFF_SIZE ; 0x80 + CALL ccp_mv_hlde_b + LD HL, ccp_batch_fcb_s2 + LD (HL), 0x0 + INC HL + DEC (HL) ; decriment record count + ; close batch file + LD DE, ccp_batch_fcb + CALL ccp_bdos_close_f + JP Z, ccp_gin_no_batch + ; reselect old drive if not 0 + LD A, (ccp_cur_drive) + OR A + CALL NZ, ccp_bdos_drv_set + ; print command line + LD HL, ccp_inp_line ; ccp_inbuff+2 + CALL ccp_out_msg + CALL ccp_getkey_no_wait + JP Z, ccp_gin_nokey + ; terminate batch processing on any key + CALL ccp_del_batch + JP ccp_get_command + + ; get user input from keyboard +ccp_gin_no_batch: + CALL ccp_del_batch + CALL ccp_set_cur_drv + + LD C, 10 + LD DE, ccp_inbuff + ; Call BDOS C_READSTR DE -> inp buffer + CALL jp_bdos_enter + + CALL ccp_reset_cur_drv + +ccp_gin_nokey: + LD HL, ccp_inbuff+1 + LD B, (HL) + +ccp_gin_uppr: + INC HL + LD A, B + OR A + JP Z, ccp_gin_uppr_end + LD A, (HL) ;= 2020h + CALL char_to_upper + LD (HL), A ;= 2020h + DEC B + JP ccp_gin_uppr + +ccp_gin_uppr_end: + LD (HL), A ; set last character to 0 + LD HL, ccp_inp_line ; + LD (ccp_inp_line_addr), HL ; + RET + +; --------------------------------------------------- +; Check keyboard +; Out: A - pressed key code +; ZF set if no key pressed +; --------------------------------------------------- +ccp_getkey_no_wait: + LD C,11 + ; Call BDOS (C_STAT) - Console status + CALL jp_bdos_enter + OR A + RET Z ; ret if no character waiting + LD C,1 + ; Call BDOS (C_READ) - Console input + CALL jp_bdos_enter + OR A + RET + +; --------------------------------------------------- +; Call BDOS function 25 (DRV_GET) - Return current drive +; Out: A - drive 0-A, 1-B... +; --------------------------------------------------- +ccp_bdos_drv_get: + LD C, 25 + JP jp_bdos_enter + +; --------------------------------------------------- +; Set disk buffer address to default buffer +; --------------------------------------------------- +cpp_set_disk_buff_addr: + LD DE, dma_buffer + +; --------------------------------------------------- +; Call BDOS function 26 (F_DMAOFF) - Set DMA address +; Inp: DE - address +; --------------------------------------------------- +ccp_bdos_dma_set: + LD C, 26 + JP jp_bdos_enter + +; --------------------------------------------------- +; Delete batch file created by submit +; --------------------------------------------------- +ccp_del_batch: + LD HL, ccp_batch + LD A, (HL) + OR A + RET Z ; return if no active batch file + LD (HL), 0x0 ; mark as inactive + XOR A + CALL ccp_bdos_drv_set ; select drive 0 + LD DE, ccp_batch_fcb + CALL ccp_bdos_era_file + LD A, (ccp_cur_drive) + JP ccp_bdos_drv_set + +; -------------------------------------------------- +; Check "serial number" of CP/M +; -------------------------------------------------- +ccp_verify_pattern: + LD DE, cpm_pattern ; = F9h + LD HL, 0x0000 + LD B, 6 + +ccp_chk_patt_nex: + LD A, (DE) + CP (HL) + NOP ; JP NZ HALT was here + NOP + NOP + INC DE + INC HL + DEC B + JP NZ, ccp_chk_patt_nex + RET + +; -------------------------------------------------- +; Print syntax error indicator +; -------------------------------------------------- +print_syn_err: + CALL ccp_out_crlf + LD HL, (cur_name_ptr) +pse_next: + LD A, (HL) + CP ASCII_SP + JP Z, pse_end + OR A + JP Z, pse_end + PUSH HL + + CALL ccp_print + POP HL + INC HL + JP pse_next +pse_end: + LD A, '?' + CALL ccp_print + CALL ccp_out_crlf + CALL ccp_del_batch + JP ccp_get_command + +; -------------------------------------------------- +; Check user input characters for legal range +; Inp: [DE] - pointer to character +; -------------------------------------------------- +cpp_valid_inp: + LD A, (DE) + OR A + RET Z + CP ASCII_SP ; >= Space + JP C, print_syn_err + RET Z + CP '=' + RET Z + CP '_' + RET Z + CP '.' + RET Z + CP ':' + RET Z + CP ';' + RET Z + CP '<' + RET Z + CP '>' + RET Z + RET + +; --------------------------------------------------- +; Find non space character until end +; Inp: DE -> current character +; Out: DE -> non space +; A - character +; ZF set on EOL +; --------------------------------------------------- +ccp_find_no_space: + LD A, (DE) + OR A + RET Z + CP ASCII_SP + RET NZ + INC DE + JP ccp_find_no_space + +; --------------------------------------------------- +; HL=HL+A +; --------------------------------------------------- +sum_hl_a: + ADD A, L + LD L, A + RET NC + INC H ; inc H if CF is set + RET + +; --------------------------------------------------- +; Convert first name to fcb +; --------------------------------------------------- +ccp_cv_first_to_fcb: + LD A, 0x0 + +; Convert filename from cmd to fcb +; replace '*' to '?' +; Inp: A - offset in fcb filename +; Out: A - count of '?' in file name +; ZF is set for fegular file name +ccp_cv_fcb_filename: + LD HL, ccp_current_fcb + CALL sum_hl_a + PUSH HL + PUSH HL + XOR A + LD (ccp_chg_drive), A + LD HL, (ccp_inp_line_addr) ; HL -> input line + EX DE, HL + CALL ccp_find_no_space ; get next non blank char + EX DE, HL + LD (cur_name_ptr), HL ; save name ptr + EX DE, HL + POP HL + LD A, (DE) ; load first name char + OR A + JP Z, cur_cvf_n_end + SBC A, 'A'-1 ; 0x40 for drive letter + LD B, A + INC DE + LD A, (DE) + CP ':' ; is ':' after drive letter? + JP Z, cur_cvf_drv_ltr + DEC DE ; no, step back + +cur_cvf_n_end: + LD A, (ccp_cur_drive) + LD (HL), A + JP cur_cvf_basic_fn + +cur_cvf_drv_ltr: + LD A, B + LD (ccp_chg_drive), A ; set change drive flag + LD (HL), B + INC DE + +cur_cvf_basic_fn: + LD B, 8 ; file name length + +cur_cvf_chr_nxt: + CALL cpp_valid_inp + JP Z, cur_cvf_sp_remains + INC HL + CP '*' + JP NZ, cur_cvf_no_star + LD (HL), '?' + JP cur_cvf_nxt1 + +cur_cvf_no_star: + LD (HL), A + INC DE + +cur_cvf_nxt1: + DEC B + JP NZ, cur_cvf_chr_nxt + +cur_cvf_nxt_delim: + CALL cpp_valid_inp + JP Z, cur_cvf_ext + INC DE + JP cur_cvf_nxt_delim + + ; fill remains with spaces +cur_cvf_sp_remains: + INC HL + LD (HL), ASCII_SP + DEC B + JP NZ, cur_cvf_sp_remains + + ; file name extension +cur_cvf_ext: + LD B, 3 + CP '.' + JP NZ, cur_cvf_ext_fill_sp + INC DE + ; handle current ext char +cur_cvf_ext_nxt: + CALL cpp_valid_inp + JP Z, cur_cvf_ext_fill_sp + INC HL + ; change * to ? + CP '*' + JP NZ, cur_cvf_no_star2 + LD (HL), '?' + JP cur_cvf_nxt2 +cur_cvf_no_star2: + LD (HL), A + INC DE +cur_cvf_nxt2: + DEC B + JP NZ, cur_cvf_ext_nxt + +cur_cvf_ext_skip: + CALL cpp_valid_inp + JP Z, cur_cvf_rst_attrs + INC DE + JP cur_cvf_ext_skip + + ; skip remains ext pos with dpaces +cur_cvf_ext_fill_sp: + INC HL + LD (HL), ASCII_SP + DEC B + JP NZ, cur_cvf_ext_fill_sp + + ; set fcb extent, res1, res2 to 0 +cur_cvf_rst_attrs: + LD B, 3 + +cur_cvf_attrs_nxt: + INC HL + LD (HL), 0x0 + DEC B + JP NZ, cur_cvf_attrs_nxt + + EX DE, HL + LD (ccp_inp_line_addr), HL ; save input line pointer + + ; Check for ambigeous file name + POP HL ; -> file name in fcb + LD BC, 0x000b ; b=0, c=11 +cur_cvf_ambig: + INC HL + LD A, (HL) + CP '?' + JP NZ, cur_cvf_ambig_nxt1 + INC B +cur_cvf_ambig_nxt1: + DEC C + JP NZ, cur_cvf_ambig + LD A, B ; a = count of '?' + OR A ; set ZF if regular filename + RET + +; --------------------------------------------------- +; CP/M command table +; --------------------------------------------------- +cpm_cmd_tab: + DB "DIR " + DB "ERA " + DB "TYPE" + DB "SAVE" + DB "REN " + DB "USER" + +cpm_pattern: + DB 0xF9, 0x16, 0, 0, 0, 0x6B ; CP/M serial number? + ;LD SP, HL + ;LD D, 0x00 + ;NOP + ;NOP + ;LD L,E + +; --------------------------------------------------- +; Search for CP/M command +; Out: A - command number +; --------------------------------------------------- +ccp_search_cmd: + LD HL, cpm_cmd_tab + LD C, 0x0 + +ccp_sc_cmd_nxt: + LD A, C + CP CCP_COMMAND_CNT ; 6 + RET NC + LD DE, ccp_current_fcb_fn ; fcb filename + LD B, CCP_COMMAND_LEN ; max cmd len +ccp_sc_chr_nxt: + LD A, (DE) + CP (HL) ; compate fcb fn and command table + JP NZ, ccp_sc_no_match ; cmd not match + INC DE + INC HL + DEC B + JP NZ, ccp_sc_chr_nxt + ; last can be space for 3-letter commands + LD A, (DE) + CP ASCII_SP + JP NZ, ccp_sc_skip_cmd + LD A, C ; return command number in A + RET + + ; skip remains +ccp_sc_no_match: + INC HL + DEC B + JP NZ, ccp_sc_no_match + ; go to next cmd +ccp_sc_skip_cmd: + INC C + JP ccp_sc_cmd_nxt + +; -------------------------------------------------- +; Clear command buffer and go to command processor +; -------------------------------------------------- +ccp_clear_buff: + XOR A + LD (ccp_inbuff+1), A ; actual buffer len = 0 + +; -------------------------------------------------- +; Entrance to CCP +; Inp: C - current user * 16 +; -------------------------------------------------- +@ccp_ent: + LD SP, ccp_stack + PUSH BC ; + LD A, C + ; / 16 + RRA + RRA + RRA + RRA + ; cur user no in low nibble + AND 0x0f ; user 0..15 + LD E, A + CALL ccp_bdos_set_user + CALL ccp_bdos_drv_allreset + LD (ccp_batch), A + POP BC + LD A, C ; a = user*16 + AND 0xf ; low nibble - drive + LD (ccp_cur_drive), A + CALL ccp_bdos_drv_set + LD A, (ccp_inbuff+1) + OR A + JP NZ, ccp_process_cmd + +; -------------------------------------------------- +; Out prompt and get user command from console +; -------------------------------------------------- +ccp_get_command: + LD SP, ccp_stack ; reset stack pointer + CALL ccp_out_crlf ; from new line + CALL ccp_bdos_drv_get + ADD A, 65 ; convert drive no to character + CALL ccp_print ; print current drive letter + LD A, '>' ; and prompt + CALL ccp_print + CALL ccp_get_inp ; and wait string + +; -------------------------------------------------- +; Process command +; -------------------------------------------------- +ccp_process_cmd: + LD DE, dma_buffer + CALL ccp_bdos_dma_set ; setup buffer + CALL ccp_bdos_drv_get + LD (ccp_cur_drive), A ; store cur drive + CALL ccp_cv_first_to_fcb ; convert first command parameter to fcb + CALL NZ, print_syn_err ; if wildcard, out error message + LD A, (ccp_chg_drive) ; check drive change flag + OR A + JP NZ, ccp_unk_cmd ; if drive changed, handle as unknown command + CALL ccp_search_cmd ; ret A = command number + LD HL, ccp_cmd_addr + ; DE = command number + LD E, A + LD D, 0x0 + ADD HL, DE + ADD HL, DE ; HL = HL + 2*command_number + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + JP (HL) ; jump to command handler + +ccp_cmd_addr: + DW cmd_dir + DW cmd_erase + DW cmd_type + DW cmd_save + DW cmd_ren + DW cmd_user + DW ccp_entry + +; --------------------------------------------------- +; di+halt if serial number validation failed +; --------------------------------------------------- +cpp_halt: + LD HL, 0x76f3 + LD (ccp_ram_ent), HL + LD HL, ccp_ram_ent + JP (HL) + +ccp_type_rd_err: + LD BC, .msg_read_error ; BC -> "READ ERROR" + JP ccp_out_crlf_msg + +.msg_read_error: + DB "READ ERROR", 0 + +; --------------------------------------------------- +; Out message 'NO FILE' +; --------------------------------------------------- +ccp_out_no_file: + LD BC, .msg_no_file ; BC -> "NO FILE" + JP ccp_out_crlf_msg +.msg_no_file: + DB "NO FILE", 0 + +; --------------------------------------------------- +; Decode a command in form: "A>filename number" +; Out: A - number +; --------------------------------------------------- +ccp_decode_num: + CALL ccp_cv_first_to_fcb + LD A, (ccp_chg_drive) + OR A + JP NZ, print_syn_err ; error if drive letter specified + LD HL, ccp_current_fcb_fn + LD BC, 11 ; b=0, c=11 (filename len) + + ; decode number +ccp_dff_nxt_num: + LD A, (HL) + CP ASCII_SP + JP Z, ccp_dff_num_fin ; space - end of number + ; check for digit + INC HL + SUB '0' + CP 10 + JP NC, print_syn_err ; not a digit + LD D, A ; d = number + LD A, B + AND 0xe0 ; check B (sum) overflow + JP NZ, print_syn_err + ; A=B*10 + LD A, B + RLCA + RLCA + RLCA ; *8 + ADD A, B ; *9 + JP C, print_syn_err ; error if overflow + ADD A, B ; * 10 + JP C, print_syn_err ; error if overflow + ; B = B + B*10 + ADD A, D + JP C, print_syn_err ; error if overflow + LD B, A + ; to next number + DEC C + JP NZ, ccp_dff_nxt_num + RET + +ccp_dff_num_fin: + LD A, (HL) + CP ASCII_SP + JP NZ, print_syn_err ; will be space after number + INC HL + DEC C + JP NZ, ccp_dff_num_fin + LD A, B + RET + +; -------------------------------------------------- +; Move 3 bytes from [HL] to [DE] +; (Used only to move file extension) +; -------------------------------------------------- +ccp_mv_hlde_3: + LD B, 3 + +; -------------------------------------------------- +; Move B bytes from [HL] to [DE] +; -------------------------------------------------- +ccp_mv_hlde_b: + LD A, (HL) + LD (DE), A + INC HL + INC DE + DEC B + JP NZ, ccp_mv_hlde_b + RET + +; -------------------------------------------------- +; Return byte from address dma_buffer[A+C] +; Out: A - byte at dma_buffer[A+C] +; HL - dma_buffer+A+C +; -------------------------------------------------- +get_std_buff_ac: + LD HL, dma_buffer + ADD A, C + CALL sum_hl_a + LD A, (HL) + RET + +; -------------------------------------------------- +; Check drive change and select if needed +; Reset drive byte in fcb to 0 (use default) +; -------------------------------------------------- +ccp_drive_sel: + XOR A + LD (ccp_current_fcb), A + LD A, (ccp_chg_drive) + OR A + RET Z ; no need to change cur drive + DEC A + LD HL, ccp_cur_drive + CP (HL) + RET Z ; current and new drive is same + JP ccp_bdos_drv_set ; change + +; -------------------------------------------------- +; Restore previous drive if changed during operation +; -------------------------------------------------- +ccp_restor_drv: + LD A, (ccp_chg_drive) + OR A + RET Z ; not changed + DEC A + LD HL, ccp_cur_drive ; chk cur drive + CP (HL) + RET Z ; new and previous drive is same + LD A, (ccp_cur_drive) + JP ccp_bdos_drv_set ; restore to previous drive + +; -------------------------------------------------- +; Handle user DIR command +; -------------------------------------------------- +cmd_dir: + CALL ccp_cv_first_to_fcb + CALL ccp_drive_sel + ; check filemask specified + LD HL, ccp_current_fcb_fn + LD A, (HL) + CP ASCII_SP + JP NZ, .dir_fmask ; yes specified + + ; fill with wildcard symbol + LD B, 11 +.fill_wc: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_wc + +.dir_fmask: + LD E, 0x0 ; cursor at 0 + PUSH DE + ; find first file + CALL ccp_find_first + CALL Z, ccp_out_no_file ; no file found + +.dir_f_next: + JP Z, .dir_f_no_more + LD A, (ccp_bdos_result_code) + ; find filename pos in direntry + ; a = a * 32 + RRCA + RRCA + RRCA + AND 0x60 + + LD C, A + LD A, 0x0a + + ; get std_buff[10+pos*8] + CALL get_std_buff_ac + RLA ; CF<-[7:0]<-CF + JP C, .dir_dont_lst ; don't display sys files + POP DE + LD A, E ; a = cursor + INC E + PUSH DE ; cursor++ + AND 0x1 ; DIFF + PUSH AF + JP NZ, .dir_no_eol + ; eol, print new line + CALL ccp_out_crlf + ; print A: + PUSH BC + CALL ccp_bdos_drv_get + POP BC + ADD A, 'A' + CALL ccp_putc + LD A, ':' + CALL ccp_putc + JP .dir_out_sp +.dir_no_eol: + ; add space between filenames + CALL ccp_out_space + LD A, ':' + CALL ccp_putc +.dir_out_sp: + CALL ccp_out_space + LD B, 0x1 +.dir_get_one: + LD A, B + CALL get_std_buff_ac + AND 0x7f ; mask status bit + CP ASCII_SP ; name end? + JP NZ, .no_name_end + ; at end of file name + POP AF + PUSH AF + CP 0x3 + JP NZ, .dir_end_ext + LD A, 0x9 + CALL get_std_buff_ac ; chk ext + AND 0x7f ; 7bit + CP ASCII_SP + JP Z, .dir_skp_sp ; do not print space +.dir_end_ext: + LD A, ASCII_SP +.no_name_end: + CALL ccp_putc + INC B + LD A, B + CP 12 + JP NC, .dir_skp_sp ; print until end of file ext + CP 9 + JP NZ, .dir_get_one ; start of file ext? + CALL ccp_out_space ; print sep space + JP .dir_get_one +.dir_skp_sp: + POP AF +.dir_dont_lst: + ; stop if key pressed + CALL ccp_getkey_no_wait + JP NZ, .dir_f_no_more + + ; find next directory entry + CALL ccp_bdos_find_next + JP .dir_f_next + + ; no more to print +.dir_f_no_more: + POP DE + JP ccp_cmdline_back + +; -------------------------------------------------- +; Handle user ERA command +; -------------------------------------------------- +cmd_erase: + CALL ccp_cv_first_to_fcb + ; check for *.* + CP 11 + JP NZ, .era_no_wc + ; confirm erase all + LD BC, msg_all_yn ;= "ALL (Y/N)?" + CALL ccp_out_crlf_msg + CALL ccp_get_inp + LD HL, ccp_inbuff+1 + ; check user input + DEC (HL) + JP NZ, ccp_get_command + INC HL + LD A, (HL) ; user input, first letter + CP 'Y' + JP NZ, ccp_get_command ; return in not exactly 'Y' + INC HL + LD (ccp_inp_line_addr), HL + +.era_no_wc: + CALL ccp_drive_sel ; select drive + LD DE, ccp_current_fcb ; specify current fcb + CALL ccp_bdos_era_file ; and delete file + INC A + CALL Z, ccp_out_no_file + JP ccp_cmdline_back ; go back to command line + +msg_all_yn: + DB "ALL (Y/N)?", 0 + +; -------------------------------------------------- +; Handle user TYPE command +; -------------------------------------------------- +cmd_type: + CALL ccp_cv_first_to_fcb + JP NZ, print_syn_err ; error if wildcard + ; select drive and open + CALL ccp_drive_sel + CALL ccp_open_cur_fcb + JP Z, .not_found ; cant open file + CALL ccp_out_crlf + LD HL, ccp_bytes_ctr + LD (HL), 0xff ; 255>128 for read first sector + +.cont_or_read: + LD HL, ccp_bytes_ctr + LD A, (HL) + CP 128 + JP C, .out_next_char + + ; read 128 bytes + PUSH HL + CALL ccp_read_f_fcb + POP HL + JP NZ, .read_no_ok + ; clear counter + XOR A + LD (HL), A + +.out_next_char: + ; get next byte from buffer + INC (HL) + LD HL, dma_buffer + ; calc offset + CALL sum_hl_a + LD A, (HL) + CP ASCII_SUB ; Ctrl+Z end of text file + JP Z, ccp_cmdline_back ; yes, back to cmd line + CALL ccp_print ; print char to output device + ; interrupt if key pressed + CALL ccp_getkey_no_wait + JP NZ, ccp_cmdline_back + ; + JP .cont_or_read + + ; non zero result from f_read +.read_no_ok: + DEC A + JP Z, ccp_cmdline_back ; A=1 - EOF, return to cmd line + CALL ccp_type_rd_err ; else read error + +.not_found: + CALL ccp_restor_drv + JP print_syn_err + +; -------------------------------------------------- +; Handle user SAVE command +; -------------------------------------------------- +cmd_save: + CALL ccp_decode_num ; get num of pages + PUSH AF ; and store + CALL ccp_cv_first_to_fcb ; conv filename to fcb + JP NZ, print_syn_err ; error if wildcard + ; delete specified file + CALL ccp_drive_sel + LD DE, ccp_current_fcb + PUSH DE + CALL ccp_bdos_era_file + POP DE + ; create specified file + CALL ccp_bdos_make_f + JP Z, ccp_no_space ; 0xff+1 if error + XOR A + LD (ccp_current_fcb_cr), A ; curr record = 0 + POP AF ; a = num pages + LD L, A + LD H, 0 + ADD HL, HL ; HL = A * 2 - number of sectors + LD DE, tpa_start + +.write_next: + ; all sectors written? + LD A, H + OR L + JP Z, ccp_close_f_cur ; no more sectors to write + DEC HL + PUSH HL + ; set buffer address to memory to write + LD HL, 128 + ADD HL, DE + PUSH HL + CALL ccp_bdos_dma_set + ; and write sector + LD DE, ccp_current_fcb + CALL ccp_bdos_f_write + POP DE + POP HL + JP NZ, ccp_no_space ; check for no space left + JP .write_next + +; Close current file +ccp_close_f_cur: + LD DE, ccp_current_fcb + CALL ccp_bdos_close_f + INC A + JP NZ, rest_buf_ret_cmd + +; -------------------------------------------------- +; Out error message about no space left +; -------------------------------------------------- +ccp_no_space: + LD BC, msg_no_space ; BC -> "NO SPACE" + CALL ccp_out_crlf_msg + +rest_buf_ret_cmd: + CALL cpp_set_disk_buff_addr + JP ccp_cmdline_back + +msg_no_space: + DB "NO SPACE", 0 + +; -------------------------------------------------- +; Handle user REN command +; -------------------------------------------------- +cmd_ren: + CALL ccp_cv_first_to_fcb ; get first file name + JP NZ, print_syn_err ; error if wildcard + LD A, (ccp_chg_drive) ; remember drive change flag + PUSH AF + ; check file already exists + CALL ccp_drive_sel + CALL ccp_find_first + JP NZ, .file_exists + ; move filename to "second slot" + LD HL, ccp_current_fcb + LD DE, ccp_current_fcb_fn+15 + LD B, 16 + CALL ccp_mv_hlde_b + ; + LD HL, (ccp_inp_line_addr) ; restore cmd line pointer + EX DE, HL + CALL ccp_find_no_space ; skip spaces between parameters + CP '=' + JP Z, .do_rename + CP '_' + JP NZ, .rename_err + +.do_rename: + EX DE, HL + INC HL ; skip sep + LD (ccp_inp_line_addr), HL ; -> second param + CALL ccp_cv_first_to_fcb ; get second name + JP NZ, .rename_err ; error if wildcard + ; if drive specified it will be same as previous + POP AF + LD B, A + LD HL, ccp_chg_drive + LD A, (HL) + OR A + JP Z, .same_drive ; ok, it is same + CP B + LD (HL), B ; restore first drive + JP NZ, .rename_err + +.same_drive: + LD (HL), B + ; check for seacond file not exists + XOR A + LD (ccp_current_fcb), A + CALL ccp_find_first + JP Z, .second_exists + ; calll bdos to rename + LD DE, ccp_current_fcb + CALL ccp_bdos_rename_f + JP ccp_cmdline_back + +.second_exists: + CALL ccp_out_no_file + JP ccp_cmdline_back + +.rename_err: + CALL ccp_restor_drv + JP print_syn_err + +.file_exists: + LD BC, .msg_file_exists ; BC -> "FILE EXISTS" + CALL ccp_out_crlf_msg + JP ccp_cmdline_back + +.msg_file_exists: + DB "FILE EXISTS", 0 + +; -------------------------------------------------- +; Handle user USER command +; -------------------------------------------------- +cmd_user: + CALL ccp_decode_num ; get user number + ; user will be 0..15 + CP 16 + JP NC, print_syn_err ; >15 - error + LD E, A ; save in E + ; check for other parameters + LD A, (ccp_current_fcb_fn) + CP ASCII_SP + JP Z, print_syn_err ; error if other parameters specified + ; call bdos to set current user + CALL ccp_bdos_set_user + JP ccp_cmdline_back1 + +ccp_unk_cmd: + CALL ccp_verify_pattern ; check if system valid + ; check for file to execute specified + LD A, (ccp_current_fcb_fn) + CP ASCII_SP + JP NZ, .exec_file + ; drive change? + LD A, (ccp_chg_drive) + OR A + JP Z, ccp_cmdline_back1 ; no, return to cmd line + ; change drive + DEC A + LD (ccp_cur_drive), A + CALL ccp_reset_cur_drv + CALL ccp_bdos_drv_set + JP ccp_cmdline_back1 + +.exec_file: + ; check file extension + LD DE, ccp_current_fcb_ft + LD A, (DE) + CP ASCII_SP + JP NZ, print_syn_err + PUSH DE + ; select specified drive + CALL ccp_drive_sel + POP DE + ; set file ext to 'COM' + LD HL, msg_com ; HL -> 'COM' + CALL ccp_mv_hlde_3 + CALL ccp_open_cur_fcb + JP Z, cant_open_exe + LD HL, tpa_start ; load to start of TPA 0x100 + +.read_next_sec: + PUSH HL ; store start pointer + EX DE, HL + CALL ccp_bdos_dma_set + + LD DE, ccp_current_fcb + CALL ccp_bdos_read_f + JP NZ, .read_no_ok ; check for read error + + ; shift start pointer for sector size + POP HL + LD DE, 128 + ADD HL, DE + ; check for enough space in RAM + LD DE, ccp_ram_ent + LD A, L + SUB E + LD A, H + SBC A, D + JP NC, ccp_bad_load + JP .read_next_sec + +.read_no_ok: + POP HL + DEC A + JP NZ, ccp_bad_load ; it is not EOF, is error + ; ok, EOF + CALL ccp_restor_drv ; get first filename + CALL ccp_cv_first_to_fcb + LD HL, ccp_chg_drive ; hl -> buff[16] + PUSH HL + LD A, (HL) + LD (ccp_current_fcb), A ; set drive letter in current fcb + LD A, 16 + CALL ccp_cv_fcb_filename ; replace wildcards + POP HL + ; set drive for second file in reserved fcb area + LD A, (HL) + LD (ccp_current_fcb_al), A + ; clear record count + XOR A + LD (ccp_current_fcb_cr), A + ; Move current to default FCB + LD DE, fcb1 + LD HL, ccp_current_fcb + LD B, 33 + CALL ccp_mv_hlde_b + + ; move remainder of cmd line to 0x0080 + LD HL, ccp_inp_line +.skip_nosp: + LD A, (HL) + OR A + JP Z, .z_or_sp + CP ASCII_SP + JP Z, .z_or_sp + INC HL + JP .skip_nosp + +.z_or_sp: + LD B, 0 ; len of cmd line for program = 0 + LD DE, p_cmd_line ; destination address for cmd line + +.copy_cmd_line: + LD A, (HL) + LD (DE), A + OR A + JP Z, .stor_len + INC B + INC HL + INC DE + JP .copy_cmd_line + +.stor_len: + LD A, B + LD (p_cmd_line_len), A + ; next line + CALL ccp_out_crlf + ; set buffer to cmd line + CALL cpp_set_disk_buff_addr + ; set drive + CALL ccp_set_cur_drv + ; and call loaded program + CALL tpa_start + ; restore stack first + LD SP, ccp_stack + ; restore current drive + CALL ccp_reset_cur_drv + CALL ccp_bdos_drv_set + ; return back to command line mode + JP ccp_get_command + +cant_open_exe: + CALL ccp_restor_drv + JP print_syn_err + +ccp_bad_load: + LD BC, .msg_bad_load ; BC -> "BAD LOAD" + CALL ccp_out_crlf_msg + JP ccp_cmdline_back + +.msg_bad_load: + DB "BAD LOAD", 0 + +msg_com: + DB "COM" + +; -------------------------------------------------- +; Return back to command line +; -------------------------------------------------- +ccp_cmdline_back: + CALL ccp_restor_drv + +ccp_cmdline_back1: + CALL ccp_cv_first_to_fcb + LD A, (ccp_current_fcb_fn) + SUB 0x20 + LD HL, ccp_chg_drive + OR (HL) + JP NZ, print_syn_err + JP ccp_get_command + + DW 0h, 0h, 0h, 0h, 0h, 0h, 0h, 0h + +ccp_stack EQU $ + +ccp_batch: + DB 0h + +ccp_batch_fcb: + DB 0h ; drive code, 0 - default + DB "$$$ SUB" ; filename + DB 0h ; extent + DB 0h ; S1 +ccp_batch_fcb_s2: + DB 0h ; S2 Extent [6:0] bits and [7] write flag +ccp_batch_fcb_rc: + DB 0h ; sectors count +ccp_batch_fcb_al: + DS 16, 0 ; reserved by CPM use only +ccp_batch_fcb_cr: + DB 0h ; current sector to read/write + +ccp_current_fcb: + DB 0h +ccp_current_fcb_fn: + DS 8, 0 +ccp_current_fcb_ft: + DS 3, 0 +ccp_current_fcb_ex: + DB 0h ; extent + DB 0h ; s1 + DB 0h ; s2 + DB 0h ; sectors count +ccp_current_fcb_al: + DS 16, 0 ; reserved by CPM use only +ccp_current_fcb_cr: + DB 0h ; current sector to read/write + +ccp_bdos_result_code: + DB 0h + +ccp_cur_drive: + DB 0h + +ccp_chg_drive: ; change drive flag, 0 - no change + DB 0 +ccp_bytes_ctr: + DW 0 + ; reserved + DS 13, 0 + + +bdos_enter_jump EQU $+6 + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xB200 +;FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| CCP_RAM\t| ",/H,ccp_ram_ent," | ",/H,CODE_SIZE," | \t |" + + ; Check integrity + ASSERT ccp_cmdline_back = 0xb986 + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/ccp_rom.asm b/CPM_v2.2_r8_ddd05ed7/ccp_rom.asm new file mode 100644 index 0000000..ba34e8d --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/ccp_rom.asm @@ -0,0 +1,901 @@ +; ======================================================= +; Ocean-240.2 +; CPM CPP, ROM PART +; AT 0xDB00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + + IFNDEF BUILD_ROM + OUTPUT ccp_rom.bin + ENDIF + + MODULE CCP_ROM + + ORG 0xDB00 + +@ccp_entry: + LD HL, 0x0 ; prevent stack overflow + ADD HL, SP + LD (CPM_VARS.saved_stack_ptr), HL + LD SP, CPM_VARS.ccp_safe_stack + + CALL get_cmd_index + LD HL, ccp_commands ;= DB6Ch + LD E, A + LD D, 0x0 + ADD HL, DE + ADD HL, DE + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + JP (HL) ; jump to command + +ccp_commands_str: + DB "SDIR READ WRITE" + +; ------------------------------------------------------- +; Search user command position in available commands list +; ------------------------------------------------------- +get_cmd_index: + LD HL, ccp_commands_str ; -> 'DIR' + LD C, 0x0 +.cmd_next: + LD A, C + CP CCP_COMMAND_COUNT + RET NC + LD DE, CCP_RAM.ccp_current_fcb_fn + LD B, CCP_COMMAND_SIZE +.cmp_nxt: + LD A, (DE) + CP (HL) ; -> 'DIR' + JP NZ, .no_eq + INC DE + INC HL + DEC B + JP NZ, .cmp_nxt + LD A, (DE) + CP ASCII_SP + JP NZ, .inc_next + LD A, C + RET +.no_eq: + INC HL + DEC B + JP NZ, .no_eq +.inc_next: + INC C + JP .cmd_next + +; -------------------------------------------------- +; Command handlers ref table +; -------------------------------------------------- +ccp_commands: + DW ccp_dir + DW ccp_read + DW ccp_write + DW ccp_ret ; r8 +; DW ccp_exit1 ; r8 + +ccp_ret: + LD HL, (CPM_VARS.saved_stack_ptr) + LD SP, HL + JP CCP_RAM.ccp_unk_cmd + +;ccp_exit1: +; JP MON.mon_cold_start + +; -------------------------------------------------- +; DIR [filemask] command handler +; -------------------------------------------------- +ccp_dir: + CALL CCP_RAM.ccp_cv_first_to_fcb + CALL CCP_RAM.ccp_drive_sel + ; chech some filemask specified in command line + LD HL, CCP_RAM.ccp_current_fcb_fn + LD A, (HL) + CP ' ' + JP NZ, .has_par + + ; no filemask, fill with wildcard '?' + LD B, 11 +.fill_wildcard: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_wildcard + + ; find file by specified mask +.has_par: + CALL CCP_RAM.ccp_find_first + JP NZ, .f_found + ; no files found, print and exit + CALL CCP_RAM.ccp_out_no_file + JP CCP_RAM.ccp_cmdline_back + +.f_found: + CALL CCP_RAM.ccp_out_crlf + LD HL, 0x0 + LD (CPM_VARS.tmp_dir_total), HL + LD E, 0 + +.do_next_direntry: + PUSH DE + CALL CCP_RAM.ccp_find_first + POP DE + PUSH DE + + ; Find file with e number +.find_file_e: + DEC E + JP M, .file_out_next + PUSH DE + CALL CCP_RAM.ccp_bdos_find_next + POP DE + JP Z, .file_e_found + JP .find_file_e + +.file_out_next: + LD A, (CCP_RAM.ccp_bdos_result_code) + ; calc address of DIR entry in DMA buffer + ; A[6:5] = A[1:0] = 32*A + RRCA ; [C] -> [7:0] -> [C] + RRCA ; + RRCA ; + AND 01100000b ; mask + LD C, A + PUSH BC + CALL CCP_RAM.ccp_out_crlf ; start new line + CALL CCP_RAM.ccp_bdos_drv_get + INC A + LD (dma_buffer), A ; disk + POP BC + LD B, 0x0 + LD HL, dma_buffer+FCB_FN ; filename + LD E, L + LD D, H + ADD HL, BC + + ; copy filename to tmp FCB and out to screen + LD B, 0x1 +.copy_next: + LD A, (HL) + LD (DE), A + LD C, A + CALL BIOS.conout_f + INC HL + INC DE + INC B + LD A, B + CP FN_LEN ; >12 end of name + JP C, .copy_next + +.zero_up_36: + XOR A + LD (DE), A ; zero at end + INC B + LD A, B + CP 36 + JP C, .zero_up_36 + + ; calc file size for current entry + LD DE, dma_buffer + CALL cpp_bdos_f_size + LD HL, (fcb_ra_record_num) ; file size in blocks + + ; get disk blk size + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_dsk_a0 + LD B, 3 ; for A - blk=3 + JP .dsk_a0 + +.no_dsk_a0: + LD B, 4 ; for other disks - blk=4 +.dsk_a0: + LD C, L + + ; convert 128b OS block to disk blocks +.mul_to_dsk_blk: + XOR A + LD A, H + RRA + LD H, A + LD A, L + RRA + LD L, A + DEC B + JP NZ, .mul_to_dsk_blk + ; round up + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_dsk_a1 + LD A, 00000111b ; for A - ~(~0 << 3) + JP .ds_skip1 +.no_dsk_a1: + LD A, 00001111b ; for other dsk - ~(~0 << 4) +.ds_skip1: + AND C + JP Z, .cvt_blk_kb + INC HL + + ; Convert blocks to kilobytes (A-1k B-2k) +.cvt_blk_kb: + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP Z, .ds_skip2 + ADD HL, HL ; 2k + + ; add file size to total dir size +.ds_skip2: + EX DE, HL + LD HL, (CPM_VARS.tmp_dir_total) + ADD HL, DE + LD (CPM_VARS.tmp_dir_total), HL + + ; display size in K + LD C, ' ' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL ccp_cout_num + LD C, 'K' + CALL BIOS.conout_f + CALL CCP_RAM.ccp_getkey_no_wait + JP NZ, CCP_RAM.ccp_cmdline_back + POP DE + INC E + JP .do_next_direntry + +.file_e_found: + POP DE + LD HL, msg_free_space ;= "\r\nFREE SPACE " + + ; Out: FREE SPACE + CALL ccp_out_str_z + LD A, (CPM_VARS.bdos_curdsk) + OR A + JP NZ, .no_ram_dsk + LD HL, (BIOS.disk_a_size) + JP .calc_remanis_ds + +.no_ram_dsk: + DEC A + LD HL, (BIOS.disk_b_size) + JP Z, .calc_remanis_ds + + LD HL, (BIOS.disk_c_size) + LD A, (bios_var3) + OR A + JP NZ, .calc_remanis_ds + + LD A, H + CP 1 + JP Z, .d720 + LD HL, 360 + JP .calc_remanis_ds +.d720: + LD HL, 720 + + ; Disk size - Dir size = Free +.calc_remanis_ds: + EX DE, HL + LD HL, (CPM_VARS.tmp_dir_total) + LD A, E + SUB L + LD E, A + LD A, D + SBC A, H + LD D, A + CALL ccp_cout_num + LD C, 'K' + CALL BIOS.conout_f + CALL CCP_RAM.ccp_out_crlf + JP CCP_RAM.ccp_cmdline_back + +msg_free_space: + DB "\r\nFREE SPACE ",0 + +ccp_cout_num: + LD A, D + AND 11100000b + JP Z, .less_224 + LD C, '*' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + RET + +.less_224: + LD HL, 0x0 + ; copy number to BC + LD B, D + LD C, E + LD DE, 0x1 + LD A, 13 + +.bc_rra: + PUSH AF + PUSH HL + ; BC >> 1 + LD A, B + RRA + LD B, A + LD A, C + RRA + LD C, A + JP NC, .bc_rra_ex + POP HL + CALL cpp_daa16 + PUSH HL + +.bc_rra_ex: + LD L, E + LD H, D + CALL cpp_daa16 + EX DE, HL + POP HL + POP AF + DEC A + JP NZ, .bc_rra + LD D, 0x4 + LD B, 0x0 + +.next_d: + LD E, 0x4 + +.next_e: + LD A, L + RLA + LD L, A + LD A, H + RLA + LD H, A + LD A, C + RLA + LD C, A + DEC E + JP NZ, .next_e + LD A, C + AND 0xf + ADD A, '0' + CP '0' + JP NZ, .no_zero + DEC B + INC B + JP NZ, .b_no_one + LD A, D + DEC A + JP Z, .d_one + LD A, ' ' + JP .b_no_one + +.d_one: + LD A, '0' +.no_zero: + LD B, 0x1 +.b_no_one: + LD C, A + CALL BIOS.conout_f + DEC D + JP NZ, .next_d + RET + +; ------------------------------------------------------- +; ADD with correction HL=HL+DE +; ------------------------------------------------------- +cpp_daa16: + LD A, L + ADD A, E + DAA + LD L, A + LD A, H + ADC A, D + DAA + LD H, A + RET + +; ------------------------------------------------------- +; Call BDOS function 35 (F_SIZE) - Compute file size +; ------------------------------------------------------- +cpp_bdos_f_size: + LD C, 35 + JP jp_bdos_enter + +; ------------------------------------------------------- +; Read data from Tape +; ------------------------------------------------------- +ccp_read: + CALL BIOS.const_f + OR A + JP Z, .no_key ; chk key pressed + CALL BIOS.conin_f + JP ccp_read +.no_key: + CALL CCP_RAM.ccp_bdos_drv_allreset + CALL CCP_RAM.ccp_out_crlf + + ; wait tape block header + LD A, 100 + CALL BIOS.tape_wait_f + OR A + JP NZ, .key_pressed1 + + ; try to load header 8 times + LD E, 8 +.load8_cont: + LD HL, dma_buffer + CALL BIOS.tape_read_f + CP 0x4 + JP Z, .key_pressed1 + OR A + JP Z, .hdr_id_chk + DEC E + JP NZ, .load8_cont + JP .cp_load_err_what + + ; Check blk ID, will be 0x8000 +.hdr_id_chk: + LD A, B + CP 0x80 + JP NZ, .load8_cont + LD A, C + OR A + JP NZ, .load8_cont + ; set end of file name strZ + XOR A + LD (dma_buffer+12), A + LD DE, dma_buffer + LD HL, CCP_RAM.ccp_inp_line ;= 2020h + LD (CCP_RAM.ccp_inp_line_addr), HL ;= B208h + +.cp_name_char: + LD A, (DE) + OR A + JP Z, .name_copied + LD (HL), A ;= 2020h + INC HL + INC DE + JP .cp_name_char +.name_copied: + LD (HL), 0x0 ; mark end of strZ + CALL CCP_RAM.ccp_cv_first_to_fcb + JP NZ, CCP_RAM.print_syn_err + + ; Output file name to screen + LD HL, CCP_RAM.ccp_current_fcb_fn + LD B, 11 + +.name_output: + LD A, (HL) + AND 01111111b ; 7bit ASCII + LD C, A + CALL BIOS.conout_f + INC HL + DEC B + JP NZ, .name_output + ; Out ' ' + LD C, ' ' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL CCP_RAM.ccp_open_cur_fcb + JP Z, .recreate_f + LD HL,msg_ex ;= "*EX* " + CALL ccp_out_str_z + LD HL, CCP_RAM.ccp_current_fcb_fn+8 + ; '$$$' at the end of buffer + LD A, '$' + LD (HL), A + INC HL + LD (HL), A + INC HL + LD (HL), A + + ; delete file if exists and create new +.recreate_f: + LD DE, CCP_RAM.ccp_current_fcb + PUSH DE + CALL CCP_RAM.ccp_bdos_era_file + POP DE + CALL CCP_RAM.ccp_bdos_make_f + JP Z, CCP_RAM.ccp_no_space ; out NO SPACE if err + XOR A + LD (CCP_RAM.ccp_current_fcb_fn+31), A + + ; Load block with ID=1 + LD DE, 1 +.fin_load: + LD HL, dma_buffer + PUSH DE + +.blk_ld_rep: + CALL BIOS.tape_read_f + CP 4 + JP Z, .key_pressed2 + OR A + JP Z, .blk_id_chk + LD A, (CCP_RAM.ccp_current_fcb_fn+31) + AND 0x7f + JP NZ, .cp_read_cs + +.blk_id_chk: + LD A, C + OR B + JP Z, .blk_ld_rep + + ; Check id for 0xFFFF - last blk + LD A, C + AND B + CP 0xff + JP Z, .blk_id_last + ; Check ID for DE + LD A, C + CP E + JP NZ, .blk_ne_id + LD A, B + CP D + JP NZ, .blk_ne_id + ; Ok, write blk to disk + LD DE, dma_buffer + CALL CCP_RAM.ccp_bdos_dma_set + LD DE, CCP_RAM.ccp_current_fcb + CALL CCP_RAM.ccp_bdos_f_write + POP DE + JP NZ,ccp_del_f_no_space + INC DE + JP .fin_load + +.cp_load_err_what: + LD HL,msg_what ;= "WHAT?\a" + CALL ccp_out_str_z + JP .load_restart + +.cp_read_cs: + POP BC + LD HL, msg_cs ;= "\a", 0 + +; ------------------------------------------------------- +; Out zerro ended string +; In: HL -> strZ +; ------------------------------------------------------- +ccp_out_str_z: + LD A, (HL) + OR A + RET Z + LD C, A + CALL BIOS.conout_f + INC HL + JP ccp_out_str_z + +; ------------------------------------------------------- +; Delete file and out No Space message +; ------------------------------------------------------- +ccp_del_f_no_space: + LD DE, CCP_RAM.ccp_current_fcb + CALL CCP_RAM.ccp_bdos_era_file + JP CCP_RAM.ccp_no_space + +; ------------------------------------------------------- +; Read current file next block +; Out: A=0 - Ok, 0xFF - HW Error; +; ------------------------------------------------------- +cpp_read_f_blk: + LD DE, CPM_VARS.ccp_fcb ; FCB here + JP CCP_RAM.ccp_bdos_read_f + +ccp_write: + CALL CCP_RAM.ccp_cv_first_to_fcb + CALL CCP_RAM.ccp_drive_sel + LD HL, CCP_RAM.ccp_current_fcb_fn + LD A, (HL) + CP ' ' + JP NZ, .find_f + LD B, 11 + +.fill_with_wc: + LD (HL), '?' + INC HL + DEC B + JP NZ, .fill_with_wc + +.find_f: + CALL CCP_RAM.ccp_find_first + JP NZ, .found_f + CALL CCP_RAM.ccp_out_no_file + JP CCP_RAM.ccp_cmdline_back + +.found_f: + LD E, 0 ; file counter + +.do_next_f1: + PUSH DE + CALL CCP_RAM.ccp_find_first + POP DE + PUSH DE + +.do_next_f2: + DEC E + JP M, .do_file + PUSH DE + CALL CCP_RAM.ccp_bdos_find_next + POP DE + JP Z, .no_more_f + JP .do_next_f2 + +.do_file: + POP BC + PUSH BC + LD A, C + OR A + JP Z, .calc_addr + LD DE, 1200 + + ; Delay with key interrupt check +.delay_1: + XOR A +.delay_2: + DEC A + JP NZ, .delay_2 + PUSH DE + CALL CCP_RAM.ccp_getkey_no_wait + POP DE + JP NZ, CCP_RAM.ccp_cmdline_back + DEC DE + LD A, D + OR E + JP NZ, .delay_1 + +.calc_addr: + LD A, (CCP_RAM.ccp_bdos_result_code) + ; A=0-3 - for Ok, calc address of DIR entry in DMA buffer + RRCA + RRCA + RRCA + AND 01100000b + LD C, A + PUSH BC + CALL CCP_RAM.ccp_out_crlf + CALL CCP_RAM.ccp_bdos_drv_get + INC A + LD (CPM_VARS.ccp_fcb_dr), A ; set drive number + POP BC + LD B, 0x0 + LD HL, dma_buffer+1 + ADD HL, BC + LD DE, CPM_VARS.ccp_fcb_fn + LD B, 0x1 + +.copy_fn: + LD A, (HL) + LD (DE), A + INC HL + INC DE + INC B + LD A, B + CP 12 + JP C, .copy_fn + +.fillz_fn: + XOR A + LD (DE), A + INC DE + INC B + LD A, B + CP 36 + JP C, .fillz_fn + LD HL, CPM_VARS.ccp_fcb_fn + CALL ccp_out_str_z + LD HL, dma_buffer + + ; Empty first 128 bytes of DMA buffer + LD B, 128 +.clear_buf: + LD (HL), 0x0 + INC HL + DEC B + JP NZ, .clear_buf + + ; Copy file name at buffer start + LD HL, dma_buffer + LD DE, CPM_VARS.ccp_fcb_fn + LD B, 8 + +.find_sp: + LD A, (DE) + CP ' ' + JP Z, .sp_rep_dot ; ' ' -> '.' + LD (HL), A + INC HL + INC DE + JP .find_sp +.sp_rep_dot: + LD (HL), '.' + INC HL + CALL CCP_RAM.ccp_find_no_space + +.cont_copy_fn: + LD A, (DE) + LD (HL), A + OR A + JP Z, .end_copy + INC HL + INC DE + JP .cont_copy_fn + +.end_copy: + LD DE, CPM_VARS.ccp_fcb + CALL CCP_RAM.ccp_bdos_open_f + LD DE, 0x8000 ; Block ID + LD HL, dma_buffer + CALL cpp_pause_tape_wr_blk + CALL cpp_pause_tape_wr_blk + LD DE, 0x1 + + ; Read file block and write to Tape +.read_f_write_t: + PUSH DE + CALL cpp_read_f_blk + ; a=0xff if error; a=1 - EOF + DEC A + JP Z, .eof + LD A, (CPM_VARS.ccp_fcb_cr) + AND 0x7f + CP 0x1 + JP NZ, .write_once + ; Write block to Tape with ID=0 twice + LD DE, 0x0 ; Block ID=0 + LD HL, dma_buffer + CALL cpp_pause_tape_wr_blk + +.write_once: + CALL CCP_RAM.ccp_getkey_no_wait + LD HL, dma_buffer + POP DE + JP NZ, CCP_RAM.ccp_cmdline_back + CALL cpp_pause_tape_wr_blk + ; Inc Block ID and continue + INC DE + JP .read_f_write_t + +.eof: + POP DE + EX DE, HL + LD (dma_buffer), HL + EX DE, HL + + ; Final block ID=0xFFFF + LD DE, 0xffff + ; Write twice + CALL cpp_pause_tape_wr_blk + CALL cpp_pause_tape_wr_blk + POP DE + INC E + JP .do_next_f1 + +.no_more_f: + POP DE + CALL CCP_RAM.ccp_out_crlf + JP CCP_RAM.ccp_cmdline_back + +; ------------------------------------------------------- +; Write block to tape after pause +; ------------------------------------------------------- +cpp_pause_tape_wr_blk: + LD BC, 1500 ; DIFF +.delay: + DEC BC + LD A, B + OR C + JP NZ, .delay + JP BIOS.tape_write_f + + DB 0x4c + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xDB00 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| CCP_ROM\t| ",/H,ccp_entry," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT ccp_dir = 0xdb62 + ASSERT ccp_dir.find_file_e = 0xdb97 + ASSERT ccp_dir.file_out_next = 0xdba6 + ASSERT ccp_dir.file_e_found = 0xdc3e + ASSERT ccp_dir.calc_remanis_ds = 0xdc72 + ASSERT ccp_dir.no_ram_dsk = 0xdc52 + ASSERT msg_free_space = 0xdc8a + ASSERT ccp_cout_num = 0xdc9a + +FILLER + DS FILL_SIZE, 0xff + ;DB 0xaa + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_ddd05ed7/cpm.asm b/CPM_v2.2_r8_ddd05ed7/cpm.asm new file mode 100644 index 0000000..7f3d1c9 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/cpm.asm @@ -0,0 +1,44 @@ +; ====================================================== +; Ocean-240.2 +; CP/M Combine file. Includes all sources to build +; ROM 0xC000 +; +; Disassembled by Romych 2025-02-12 +; ====================================================== + + DEFINE BUILD_ROM + + DEVICE NOSLOT64K + +; +; |-----------|---------------|-----------|--------------------------------------| +; | OFFSET | SIZE | Module | Memory Address | +; |-----------|---------------|-----------|--------------------------------------| +; | 0x0000 | 2048 (0x800) | CCP_RAM | 0xC000..0xC7FF -> RAM 0xB200..0xB5FF | +; | 0x0800 | 3584 (0xE00) | BDOS | 0xC800.. | +; | 0x1600 | 1024 (0x400) | BIOS | 0xD600..D9FF | +; | 0x1B00 | 1280 (0x500) | CCP_ROM | 0xDB00..DFFF | +; |-----------|---------------|-----------|--------------------------------------| +; + + DISPLAY "| Module | Offset | Size | Free |" + DISPLAY "|-------------|---------|--------|--------|" + + + OUTPUT cpm-C000.bin + ;LABELSLIST "cpm.labels" + ;CSPECTMAP "cpm.map" + + INCLUDE "ccp_ram.asm" + INCLUDE "bdos.asm" + INCLUDE "bios.asm" + INCLUDE "ccp_rom.asm" + + OUTEND + + OUTPUT variables.bin + INCLUDE "cpm_vars.inc" + OUTEND + + +END \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/cpm_vars.inc b/CPM_v2.2_r8_ddd05ed7/cpm_vars.inc new file mode 100644 index 0000000..ea33312 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/cpm_vars.inc @@ -0,0 +1,219 @@ +; ======================================================= +; Ocean-240.2 +; Module CPM_VARS - BIOS + BDOS + CPP variables +; RAM Range: 0xBA09-0xBF00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + IFNDEF _CPM_VARS + DEFINE _CPM_VARS + + MODULE CPM_VARS + ORG 0xBA09 + +cpm_vars_start EQU $ +; Filled by zeroes by BIOS at cold boot +; until ccp_vars_end + +; Output disabel if non zero +bdos_no_outflag DB 0 +bdos_strtcol DB 0 +bdos_column DB 0 + +; Copy output to printer in non zero +bdos_prnflag DB 0 +bdos_kbchar DB 0 + +; Stored user stack pointer on enter bdos fn +bdos_usersp DW 0 ; 0xba0e + +; BDOS Stack + DS 48, 0 +bdos_stack EQU $ + +; ------------------------------------------------------- +; Common values shared between bdosi and bdos +; ------------------------------------------------------- + +; current user number 0..15 +bdos_userno DB 0x00 ; 0xba40 + +; current disk number +bdos_curdsk DB 0x00 + +; reg DE (pointer) value to call BDOS fn +bdos_info DW 0x0000 ; 0xba42 + +; Address value to return +bdos_aret DW 0x0000 ; 0xba44 +; Empty FCB +bdos_efcb DB 0x00 ; 0xba46 = 0xE5 at init +; Read-only disks flag +bdos_rodsk DW 0x0000 ; 0xba47 +; Drive login vector +bdos_dlog DW 0x0000 ; 0xba49 +; Address of DMA buffer +bdos_dmaad DW 0x0000 ; 0xba4b = 0x80 at init +bdos_cdrmax DW 0x0000 ; 0xba4d +; Current track +bdos_curtrk DW 0x0000 ; 0xba4f +; Current record (sector) +bdos_currec DW 0x0000 ; 0xba51 +; Address of user buffer +bdos_buffa DW 0x0000 ; 0xba53 + +; Address of cur disk DPB +bdos_dpbaddr DW 0x0000 ; 0xba55 +; Calculated checksum +bdos_cksum DW 0x0000 ; 0xba57 +; Address of cur disk allocation map +bdos_alloca DW 0x0000 ; 0xba59 +; Sectors per track +dbos_sectpt DW 0x0000 ; 0xba5b +bdos_blkshf DB 0x00 ; 0xba5d +bdos_blmsk DB 0x00 ; 0xba5e +bdos_extmsk DB 0x00 ; 0xba5f +; Max allocation number (disk size) +bdos_maxall DW 0x0000 ; 0xba60 +bdos_dirmax DW 0x0000 ; 0xba62 +; Size of directory +bdos_dirblk DW 0x0000 ; 0xba64 +bdos_alloc1 DW 0x0000 ; 0xba66 +; First track offset +bdos_offset DW 0x0000 ; 0xba68 +; Disk traslation vector address +bdos_tranv DW 0x0000 ; 0xba6a +; Open and closed flag 0xff - closed +bdos_fcb_closed DB 0x00 ; 0xba6c +; Read file modified flag. 0xff - not allow read unwritten +bdos_rfm DB 0x00 ; 0xba6d +bdos_dirloc DB 0x00 ; 0xba6e +; File access mode (0=random, 1=sequential, 2=special) +bdos_seqio DB 0x00 ; 0xba6f +; Reg E value to call BDOS fn +bdos_linfo DB 0x00 ; 0xba70 +bdos_dminx DB 0x00 ; 0xba71 +; Length in bytes to match when search +bdos_searchl DB 0x00 ; 0xba72 +; Address of buffer where to search +bdos_searcha DW 0x0000 ; 0xba73 + DW 0x0000 +; 0 - big disk (>255 blk), 0xff - small disk +bdos_small_disk DB 0x00 ; 0xba77 +; Drive selected (0xff) by user specified FCB flag +bdos_resel DB 0x00 ; 0xba78 +bdos_olddsk DB 0x00 ; 0xba79 +; Saved disk of user FCB +bdos_fcbdsk DB 0x00 ; 0xba7a +; Number of records in extent +bdos_rcount DB 0x00 ; 0xba7b +bdos_extval DB 0x00 ; 0xba7c +; Record number to read +bdos_vrecord DW 0x0000 ; 0xba7d +; Record to access +bdos_arecord DW 0x0000 ; 0xba7f +bdos_arecord1 DW 0x0000 ; 0xba81 +bdos_dptr DB 0x00 ; 0xba83 +bdos_dcnt DW 0x00 ; 0xba84 +bdos_drec DS 20, 0x00 ; 0xba86 +tmp_dir_total DW 0x0000 ; 0xba9a +; Save pointer at ccp entry, restore on exit +saved_stack_ptr DW 0x0000 ; 0xba9c + DS 22, 0 +ccp_safe_stack + DW 0x0000 ; 0xbab4 + +; -------------------------------------------------- +; FCB +; -------------------------------------------------- +ccp_fcb +ccp_fcb_dr DB 0 ; 0xbab6 drive letter to write file +ccp_fcb_fn DS 8,0 ; 0xbab7 file name +ccp_fcb_ft DS 3,0 ; file type +ccp_fcb_ext DB 0 ; extent +ccp_fcb_s1 DB 0 +ccp_fcb_s2 DB 0 ; extent hi bits +ccp_fcb_rc DB 0 +ccp_fcb_al DS 16, 0 ; Second half of FCB +ccp_fcb_cr DB 0x00 ; Current record +ccp_fcb_rn DB 0x00 ; Random access record number + DW 0x00 + DW 0x00 + DW 0x00 + +ccp_vars_end EQU $ + +; -------------------------------------------------- +; Disk parameters headers/blocks in RAM +; -------------------------------------------------- +DPH_RAM DS 16, 0 ; 0xbade +DPH1_RAM DS 16, 0 ; 0xbaee +DPH2_RAM DS 16, 0 ; 0xbafe +tmp_buff9 DS 9, 0 +DPB_A_RAM DS 15, 0 ; 0xbb17 +DPB_B_RAM DS 15, 0 ; 0xbb26 +DPB_C_RAM DS 15, 0 ; 0xbb35 + +; -------------------------------------------------- +cur_disk DB 0x00 ; 0xbb44 +dma_addr DW 0x0000 ; 0xbb46 +curr_track DB 0x00 ; 0xbb47 +curr_sec DB 0x00 ; 0xbb48 +slicer_disk DB 0x00 ; 0xbb49 +slicer_track DB 0x00 ; 0xbb4a +slicer_real_sector DB 0x00 ; 0xbb4b +tmp_slicer_real_sector DB 0x00 ; 0xbb4c +slicer_has_data DB 0x00 ; 0xbb4d +slicer_need_save DB 0x00 ; 0xbb4e +slicer_uninited_count DB 0x00 ; 0xbb4f +slicer_uninited_disk DB 0x00 ; 0xbb42 +slicer_uninited_track DB 0x00 ; 0xbb43 +slicer_uninited_sector_128 DB 0x00 ; 0xbb44 +tmp_slicer_result DB 0x00 ; 0xbb45 +tmp_slicer_can_read DB 0x00 ; 0xbb46 +tmp_slicer_operation DB 0x00 ; 0xbb47 +tmp_slicer_flush DB 0x00 ; 0xbb48 + +; Directory buffer used by BDOS for directory operations +dir_buffer DS DIR_BUFF_SIZE, 0 ; 0xbb49 + +; Allocation map for drive A (Mark used allocation blocks) +AL_MAP_A DS 31, 0 ; 248 bit +; Check vector for drive A (Checksumm of dir entries, used to detect disk change) +CHK_VEC_A DS 16, 0 ; 0xbbf6 + +; Allocation map for drive B +AL_MAP_B DS 45, 0 ; 184 bit +; Check vector for drive B +CHK_VEC_B DS 32, 0 ; 0xbc33 + +; Allocation map for drive C +AL_MAP_C DS 45, 0 ; 184 bit +; Check vector for drive C +CHK_VEC_C DS 32, 0 ; 0xbc80 + DS 96, 0 + +slicer_buffer DS 512, 0 ; 0xbd00 + +cpm_vars_end EQU $ + +ccp_vars_size EQU ccp_vars_end-cpm_vars_start +cpm_vars_size EQU cpm_vars_end-cpm_vars_start + + ; check integrity of original ROM + ASSERT tmp_dir_total = 0xba9a + ASSERT saved_stack_ptr = 0xba9c + ASSERT ccp_fcb_fn = 0xbab7 + ASSERT DPH_RAM = 0xbade + ASSERT cur_disk = 0xbb44 + ASSERT slicer_uninited_count = 0xbb4f + ASSERT slicer_buffer = 0xbd00 + + DISPLAY "| CCP_VARS\t| ",/H,cpm_vars_start," | ",/H,cpm_vars_size," |\t |" + + DISPLAY "cur_disk:",/H,cur_disk + + ENDMODULE + + ENDIF diff --git a/CPM_v2.2_r8_ddd05ed7/equates.inc b/CPM_v2.2_r8_ddd05ed7/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/io.inc b/CPM_v2.2_r8_ddd05ed7/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/mon_entries.inc b/CPM_v2.2_r8_ddd05ed7/mon_entries.inc new file mode 100644 index 0000000..b607096 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/mon_entries.inc @@ -0,0 +1,35 @@ +; ====================================================== +; Ocean-240.2 +; +; Monitor entries to compile CP/M modules +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _MON + DEFINE _MON + + MODULE MON + +mon_init EQU 0xe000 +mon_cold_start EQU 0xe003 +non_con_status EQU 0xe006 +mon_con_in EQU 0xe009 +mon_con_out EQU 0xe00c +mon_serial_in EQU 0xe00f +mon_serial_out EQU 0xe012 +mon_char_print EQU 0xe015 +mon_tape_read EQU 0xe018 +mon_tape_write EQU 0xe01b +ram_disk_read EQU 0xe01e +ram_disk_write EQU 0xe021 +mon_res_f1 EQU 0xe024 +mon_res_f2 EQU 0xe027 +mon_tape_wait EQU 0xe02a +mon_tape_detect EQU 0xe02d +read_floppy EQU 0xe030 +write_floppy EQU 0xe033 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/ram.inc b/CPM_v2.2_r8_ddd05ed7/ram.inc new file mode 100644 index 0000000..8c6c08e --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +@RST1 EQU 0x0008 +@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/MON_Turbo/.gitignore b/MON_Turbo/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/MON_Turbo/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/MON_Turbo/.vscode/extensions.json b/MON_Turbo/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/MON_Turbo/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/MON_Turbo/.vscode/tasks.json b/MON_Turbo/.vscode/tasks.json new file mode 100644 index 0000000..d38fefc --- /dev/null +++ b/MON_Turbo/.vscode/tasks.json @@ -0,0 +1,65 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make MONITOR (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=turbo_mon.sld", + "--sym=turbo_mon.labels", + "--raw=turbo_mon.obj", + "--fullpath", + "turbo_mon.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "Optimize MONITOR (MDL)", + "type": "shell", + "command": "/opt/java21/bin/java -jar ~/Soft/MDL/mdl.jar monitor.asm -cpu z80 -so -dialect sjasmplus", + "group": "build", + "problemMatcher": { + "applyTo": "allDocuments", + "fileLocation": [ + "autoDetect", + "${workspaceFolder}" + ], + "pattern": [ + { + "regexp": "^(\\w+): (.+) in (.+)#([0-9]+): (.+)$", + "file": 3, + "line": 4, + "severity": 1, + "message": 5, + "code": 2 + } + ] + }, + "presentation": { + "echo": false, + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": true, + "revealProblems": "onProblem" + } + } + + ] +} \ No newline at end of file diff --git a/MON_Turbo/BIN/MON_Turbo_bcac5ca0.BIN b/MON_Turbo/BIN/MON_Turbo_bcac5ca0.BIN new file mode 100644 index 0000000000000000000000000000000000000000..9fc1a6fbb3ca055a87429a56cd230c574412e8bf GIT binary patch literal 8192 zcmds6i&s?Fxj!?^Tjp_q0eQ&GIAD~Az$8JP=n+I6O`=28icOP%v5JXlP+|hZV}z(R zUA9Z3xmnHXZDM+hBxsuovs6zmRbx6OH+<8Mbqh3$bz25+aMzJn z+g;ucpj+wdH?*29#XB~Z74O(&-sarCb(eETwt07fR)fvY&lBf;C4|AZ!2@4gh-B;` z(#tOCBj%ajCSKanTjZq*`W6_NHb?IUUhnKJHu3rYaQ?Y1`KzDb_(aa`XV*Nex8mgkM?}4r zeJ(fByY%&%*N|T8p}juMpUZr;`Y6)7tg@<>$XfSE{VOk?Z#bC0yz%?l zYu~6_@vvS@qrfA0W0KQFyvC$>5$__27U7i})DgV8xP+gcG#xKg>K7xtsj<@YX{!9y zOJq;vB&MwKceQbMZ?<0RWAHD@ly%-vf}c7Vh)?m}(e}G|eO$aJjuNU;}s(xXSPR_3B7i4!T`gJnPb#mA9ZowYf+~2L_^{3!*SW$hq zSifwIi`P;_Kd5x``todVp|m3rt4_6C=ub|5!MZyDYJS*n=Sf~ko$kj^_je6@Y+sU$ zOr?@Zq!RRK;8g_!KN-0;x-0PVt>uB61I)nZ{r~8n7`@VuK4d?GUnf&4<5Q|&3dKhX zw@Kz2(gdm*QGE+OpU)`T0X!m5zFo$;6vZwHH;oc*%)Mw5Yt0 zk+B<=5_#zN19hgddQ&<5>w)@2%+&d(fy8`MXWKyCWJ_6H>ovxHo#fZU(&7-ECNS!& z0ix)#uTN})1v9dZ7Xcc#9+%KR%YsxcFm& zIaD&t1qK3HgCszNnFgSA%E)-FXrXt`ZShmqPfb`n}A}BOGtC; z^$=Yt{O!wesMz8#rtqfAaR~^t{{c2=l>phar?eSRG^E7_7-=2q(0GYi|zTb1fo7KRb9Y;slu^^zk4c z<7n`jye8+e7|#hUb!o6kj6}vhL_!#M3__5>j>tST6juiy#00EkgG3RSjznbs(88{b zV?_Z7ZUu(6ZOu@?e%}<$9p#ahe0p+=mRVWJF4uF&2@&oHHBdkbagHQ?# z(HMXbxH=?fd3aUBbk?v$Wpcv+yT@Q5xm*K+z9E_z)~Pbxya}i+5FKD1#??Khgn>mI zL-#n8K)nr7f;i@&EP*jb9Gc+;9TK26@C_n0cQ_*gKb3lB*zzS=D6tmKqbc&(t>`2O z!pMmI&7K|}7u0OKUr@0YURca_`UMl4KO|__UcaDiu@++4%s7z72ZdNRV<;Huz;;pU zQzZ4Devqp43(4$dKdiTh{U8cSR20~u6ScR%xb_xI&3RGqzjzRQ2btT$gWv}|2=3)U z5R!W~AFQd_6N4aha1ex?gCJBl2tqFmf)FH8p{(0vAZv^y3Vr(;eH9J93j1BsQTK62 zYVDZH;kyZ^CIhwe*0&Y5;ugL2me>(3(H>D!@85#G%Q6Jp7OeO+so9VTc+dG;1nxN1 z1fLod)e*nK$geQ+>+^d;-DX%JB2tMx%q=3(aSDlvQVWW^%@FqfTM+gOjCcMOBTP+a z$Pj>exxl(4@PJMWPl_|+I_cz9F7}B4q2l#5yb?AVM?jn;pLO z)~?HglW$}zhMgYv7AI^jr~Bdj;4W`h^_=dnu@X~PAm7d13^aBttNbq5c&_P`el!7I zU53?q*-L>H>!^!?KpX5fc@Fo*k6{kMB>D)Z5o|mUQ8F`g0&X8|;0&#kM9 zZm&E66N|BYP2&F`Q#OCnDI2M&jRTiAf6}8z#Q+>l0EXYQDoK84@rbF+@)ew4ln$}J zd?c^jot^!z9u`KE)9SWb-=#`M);jv?Z8yj+e;dN<@=|+7>OL;p5Xc%P1Mc5m_$^GP z-(2`jO@_2z_`2fVTEs%aK!CM;Pc6un&q4RDIU-^O3;P z>ty}ERn@&y8SK0?lDD=pkae95u zmVx7c8No5E%Z~)MfI~!+QhsN6B+qfJzajaZytVyZZB7v(!VH+alx6fju$cgM)u=X? zUi43>IkaUIp{`w()U$CkDI>$7rEZV(^)XK`7}_zK=Mc4zBlSHE4=Xu`R3G>!)Ev42 zsbOqsr0;&52t!8{!^fg1@_0e4XEXvsl!`37NW1+j(&6nOj(sxi zDci&C93v!^XPvc52O&9IwLYQ#Q+DSVP#fB%m5uGvD%S~TCqA}5VM*O*&~RRmbkv^{ zB=_^ksyd$>TkkrF_~_+haMJGLs>T2acYW*{cSbBN`UZ~P9wzH+veElw33Y6&!Are0 z#x(5zF6SA0%G*)72lA48?H{1_i>O_sL%YbwBw{bG==6>yuju?EN?K9Uj*`Ek>>yAg}-f~pErCH9q)&RM~rO-es`fq1`nhO31Us7F~LV8OPA{s^s<!61sSLPI6{}I3R4S81Z-82?RHDamoe6PBr8+3W7v6M(N`dsjSIlHmq!@r; zmkZOfQB07jb%8*_?ATwf^8gU{mfLhG- z5;03RV3;-@EMZtWSkp%gEXFWkOin}@_y-yC4>IH+BBmQ+0XQ0W1H_I>Wr)}Vi!g91 z874I%myFR5rZy%S;d(%!(n0V^Bnq_}241aJo4~pbB2`3YlBB~}qk#t0Y7x?cJrr-y z%_sxwY8Z43ON)z(L$y>GKm-Yf`ksGi*rAq=rzgZ=m{P4o{)znws1ekN56}}}4s}nA z9GEanA`vqb4)I7?Eb1)Ofe@2Ql}-gLr~;_fY8XoJNFJLIt2TmrMs+kkO*o2q6L^RG zi`Bqmg0U0h6S)We0SkKW#S8Kaml;L>qVb8m1OM*DXC#u5TQHr(OpNdJI5ZhhxJKv0 zeenqqf~>)dLRe%%$Pjz_XU zt~y>aDRp@2O=SdTDzEjmrZ{0$I%18lKShn*g(6&tJj?~u?ZD}Z6S)#57Pz`3lWKGW zhYo%&Uq!920)ksS?G;bd<&}8}Ikj_wi1FS0mSG81JRx%In;&|^#{;maI2+UXEtI)-8SAuKVH-fe8vOBRsEfpL5mJnJJ zgjp@j+A+zV`6<3&{tB*Jc+uDO`b%ppHbfg zX>3wU1%sYz3_?oCt|3b*0_(_QQVR=GSLb7~T8-Ik-t~I*i&YCZs!-p(zuH`|vcP@n#pP>X%?=BCD2yzDTxn(C6#ubL$oFtsn2-kQR<)3WqHdFx@_tH!w)Nf95o5S4P zg38TdeCw2@g;d0(IE8uk`6&ToH%@`VcsZ3krCTSe25s3$rPT_0=%guYJe@pMoU?gD zPRYicOffih@HBmL$|b0&KTp|Ck@SHn?x}G4G7tsHD`BoQEH-%}ZNh2xq#(S{ zmI*>L`qw_{T4#paYwI+4$~mSBH99ktXvcJ;w}aPHYo~R|P{$V=pu2N*D*ElPZZQyY z*tOGut_4@22Au}zLMpX?nq$NCPbLK|`{AVU9@{qu7a*f^LN>cq5OUd4L2yuSP2(R< zb`1+pvTw}@Ti9RC2;0~%XM`PWN=PVU%^_hI`)pV!g&<(p%nOHEnIITgf0%2XMlo{| z`O}O1f#d{x=RSX!T0zQ=B2*o@3aJQHA)$w+MZC3wL?P|SVg0CGAz=%{3=B{QiVa;d zfNzBRwA&qo;bc|B7sqWOf4Vzg(1aeJ=~f20gfP;+Z3gak9pg%PnrakM2Hh)+H`RC? z4EMd6Y#7(laAZWEpJCii)WLU-&WIfvjmDQ|m?w+3mBOruL^9qy13_;25y_R!&{rm7 zi#KjGe-FcdE>e#msxBLP+wz)NMA4Xa7=DXalgZrN97GY!q1bKC-J8sf7=Gi?Wd4{BK*=EZ4Kcc&)H6p+{G~9LSa|jlM@m) zcjkm_CrMDMvqNG(Np=@QojMW18$%IB=qp+0cS5#fXkJSyDhQSDrfVJbgOHdV4aup4 zAtoRGhcK_l@K5KE(3x3V6Nys` zXD~zTO=Ow9nJl*-Ck5x<-kfa^{<|}KR&oNfpCE-YfJucyx?gehY%U!RaUadX{Lh|^ z3au41J&Cgkb%248*gy&QM3{SV*8V2B&h{oru^}dipFtSLVdhKb4|4}Y5!@ip9hi;a z2IHvw^EvJn=qd<$`Y$1_a<;xq2ZyUVWtrazL?(fiCaP4}CBzXzqM48w7X98Se90ZhH0$?$Nm~xYy>sfd7o{ zBkV(jHiZ&?pxvESJOVKaSq!W#kXZ#8%CK5rCTuR5%AT0HUNhg+(I&w{+#_O{O`C&; zPR$%)rZeZXRN9;zUJqjDx0`&KlPX^(sF&9HtVEj6jA6YQpduL-$>%_1l^kWN2%RpR z3)cIVj<+%{pSd?f@Ag@(JqPCK<8x4_>*s2$K|DiceCf4A=1tYr zuz%#5u^s!5R=aY{#V=M>A2sK$hx*>{=V4{lFCE>Vhvn_6-n;KmE|#}*|DmIhZO_Hb z_0FdnvApG5^PDSkvDBi}9Wg1}iOWaeW^-T;=F|2gIn)>`1Yo)Zm&rKz+OPZim*AXFr;`pV3qw*SPn0YF%XcYc)BL+;+xp_!VrQZ zB>GSD8B#E`!2hwjSfu=7xX#O7iI_9eABF3^9pZAfguW8SF(Yg$xcL|?z)+?yg`WbI z#t!-)VZvZ5CBbcbDbi+phQxd4=tY7Ds8C@7+#&&5VaH49;F5%RXoB;AicJz=*Edk< zf-BE=gHZeWVQl-E-e2%i)+Y2`;E$oQge3%*?*aq6Tn2qWfGiWTWfGJWq2w|oo+`9h zUzu!ZH_$agL${$4h_O3274In70t*~8;`IV_l?5ZkRTI|pw*=TVS_L7Cy%gEPsNV}m zz#;lif-AD9o)>cHw}nl+Jmp~2+QeQJYz8vr3gs8%?1Ye(LtPaz;DwG)0WDpE-9S3h zF}ep8*0J+KHa#UESRYP`Lq%g4997V>yyW2i>Q}2Ot6r&k> [CF] -> 7 + JP NC,mc_calc_column + LD A, C + RLA ; 0 <- [CF] << + RLA + RLA + + ; IE10 shifted + B + AND 01111000b + OR B + INC A + CP KEY_ALF + JP NZ, mc_not_alf + LD A, 0x0 + LD (TM_VARS.mc_stored_key), A + JP mc_fin + +mc_not_alf: + CP 0xc + JP NZ,mc_chk_fix + LD A, 0xff + LD (TM_VARS.mc_stored_key), A + JP mc_fin + +mc_chk_fix: + CP KEY_FIX + JP NZ,mc_plain_key + ; invert fix state + LD A, (TM_VARS.mc_fix_state) + CPL + LD (TM_VARS.mc_fix_state), A + JP mc_fin +mc_plain_key: + LD (TM_VARS.m_last_key), A + LD A, C + AND 0x30 + LD (TM_VARS.m_last_shifts), A + POP BC + LD A, 0xff + RET +mc_fin: + POP BC + XOR A + RET + +; ------------------------------------------------------ +; Read key +; Inp: A +; ------------------------------------------------------ +tm_con_in: + CALL m_con_status + OR A + JP Z, tm_con_in + LD A, (TM_VARS.m_last_key) + PUSH HL + PUSH BC + LD B, A + LD A, (TM_VARS.m_last_shifts) + LD C, A + LD HL, mci_ctrl_tab + AND 0x10 ; Ctrl + JP NZ, mci_is_crtl + LD A, C + LD HL, mci_alt_tab + AND 0x20 ; Shift + JP NZ, mci_is_shift + LD A, (TM_VARS.mc_fix_state) + OR A + JP NZ, mci_is_shift + LD HL, mci_base_tab + ; Calc offset for key decode +mci_is_shift: + LD A, B ; last + ADD A, L + LD L, A + LD A, H + ADC A, 0x0 + LD H, A + LD C, (HL) ; C - decoded + LD A, (TM_VARS.mc_stored_key) + OR A + LD A, C ; A = decoded key + JP Z, mci_key_zero + OR 0x80 +mci_key_zero: + LD C, A + XOR A + LD (TM_VARS.m_last_key), A + ; Return A=C=key + LD A, C + POP BC + POP HL + RET +mci_is_crtl: + LD A,B + ADD A,L + LD L, A + LD A,H + ADC A, 0x0 + LD H, A + LD A, (HL) + JP mci_key_zero + +mci_base_tab equ $-1 + db ',', '-', 0x00, 0x00, 0x00, '7', '8', '9' + db 0x1B, 0x09, 0x00, 0x00, 0x00, '0', '.', "\r" + db '@', 'J', 'F', 'Q', 0x00, '1', '2', '3' + db 0x9E, '1', 'C', 'Y', '^', '4', '5', '6' + db 0x81, '2', 'U', 'W', 'S', '+', 0x7F, 0x03 + db 0x86, '3', 'K', 'A', 'M', "\b", 0x99, 0x8B + db '4', 'E', 'P', 'I', ' ', 0x84, "\r", '/' + db 0x92, '5', 'N', 'R', 'T', 0x98, 0x85, '_' + db 0x83, '6', 'G', 'O', 'X', '.', ':', '-' + db '7', '[', 'L', 'B', 0x93, 0x5C, 'H', '0' + db '8', ']', 'D', ';', ',', 'V', 'Z', '9' + +mci_alt_tab equ $-1 + db ',', '-', 0x00, 0x00, 0x00, '7', '8', '9' + db 0x1B, "\t", 0x00, 0x00, 0x00, '0', '.', "\r" + db '`', 'j', 'f', 'q', 0x00, '1', '2', '3' + db 0x9E, '!', 'c', 'y', '~', '4', '5', '6' + db 0x81, '"', 'u', 'w', 's', '+', 0x7F, 0x03 + db 0x86, '#', 'k', 'a', 'm', "\b", 0x99, 0x8B + db '$', 'e', 'p', 'i', ' ', 0x84, "\r", '?' + db 0x92, '%', 'n', 'r', 't', 0x98, 0x85, '_' + db 0x83, '&', 'g', 'o', 'x', '>', '*', '=' + db 0x27, '{', 'l', 'b', 0x93, '|', 'h', '0' + db '(', '}', 'd', '+', '<', 'v', 'z', ')' + +mci_ctrl_tab equ $-1 + db ',', '-', 0x00, 0x00, 0x00, '7', '8', '9' + db 0x1B, "\t", 0x00, 0x00, 0x00, '0', '.', "\r" + db 0x00, "\n", 0x06, 0x11, 0x00, '1', '2', '3' + db 0x9E, '1', 0x03, 0x19, 0x1E, '4', '5', '6' + db 0x81, '2', 0x15, 0x17, 0x13, '+', 0x7F, 0x03 + db 0x86, '3', "\v", 0x01, "\r", "\b", 0x99, 0x8B + db '4', 0x05, 0x10, "\t", ' ', 0x84, "\r", '/' + db 0x92, '5', 0x0E, 0x12, 0x14, 0x98, 0x85, 0x1F + db 0x83, '6', "\a", 0x0F, 0x18, '.', ':', '-' + db '7', 0x1B, "\f", 0x02, 0x93, 0x1C, "\b", '0' + db '8', 0x1D, 0x04, ';', ',', 0x16, 0x1A, '9' + +; ------------------------------------------------------ +; Out char to console +; Inp: C - char +; ------------------------------------------------------ +m_con_out: + PUSH HL + PUSH DE + PUSH BC + CALL m_con_out_int + POP BC + POP DE + POP HL + RET + +; ------------------------------------------------------ +; Out char C to console +; ------------------------------------------------------ +m_con_out_int: + LD DE, TM_VARS.m_esc_mode + LD A, (DE) + DEC A + OR A + JP M, co_print_no_esc ; standart print no ESC mode + JP NZ, co_exit_esc + + ; handle ESC param + INC DE + LD A, (DE) + OR A + JP P, co_get_esc_param + LD A, C + AND 0xf ; convert char to command code + LD (DE), A + INC DE + XOR A + LD (DE), A + RET + +co_get_esc_param: + LD HL, TM_VARS.m_esc_cmd + LD B, (HL) + ; inc param count + INC HL + LD A, (HL) + INC A + LD (HL), A + ; store new param + LD E, A + LD D, 0x0 + ADD HL, DE + LD (HL), C + ; get params count for esc command + LD HL, m_esc_params_tab + LD E, B ; d=0, b = cmd + ADD HL, DE ; DE - command offset + CP (HL) + ; return if enough + RET M + + ; Entry point for user programs + ; to use graphics with parameters in ASCII form + LD A, (TM_VARS.m_esc_cmd) + AND 0xf ; ??? already applied + CP 15 + JP Z, ge_lbl_1 + CP 11 + LD C, 0x5 + JP Z, ge_lbl_2 + CP 4 + JP P, esc_handler1 +ge_lbl_1: + LD C, 0x4 +ge_lbl_2: + LD HL, TM_VARS.m_esc_param_1 + LD D, H + LD E, L +ge_lbl_3: + + LD A, (HL) + CP 0x3a ; ':' + JP M, ge_is_digit_1 + SUB 0x7 +ge_is_digit_1: + AND 0xf + ADD A, A + ADD A, A + ADD A, A + ADD A, A + LD B, A ; B=A*16 + INC HL + LD A, (HL) + CP ':' + JP M, ge_is_digit_2 + SUB 0x7 +ge_is_digit_2: + AND 0xf + OR B + INC HL + LD (DE), A + INC DE + DEC C + JP NZ, ge_lbl_3 + +esc_handler1: + LD HL, TM_VARS.m_esc_cmd + LD A, (HL) + AND 0xf + LD E, A + DEC HL + OR A + LD (HL), 0x2 + RET Z + LD D, 0x0 + LD (HL), D + DEC DE + ; Calc ESC command handler offset + LD HL, m_esc_handler_tab + ADD HL, DE + ADD HL, DE + LD E, (HL) + INC HL + ; HL = addr of handler func + LD D, (HL) + EX DE, HL + ; It is 1..4 func DRAW_* func? + CP 0x4 + JP P, esc_no_draw_fn + LD A, (TM_VARS.m_screen_mode) + AND 00000111b + ; If not in graphics mode - exit + JP NZ, esc_exit + +esc_no_draw_fn: + LD DE, esc_exit + PUSH DE + + ; Jump to ESC func handler + JP (HL) + +esc_exit: + XOR A + LD (TM_VARS.m_esc_mode), A + RET + + ; Count of parameters for ESC commands +m_esc_params_tab: + db 4, 8, 8, 4, 1, 2, 1, 1 + db 1, 1, 1, 10, 1, 1, 1, 8 + +m_esc_handler_tab: + dw esc_draw_fill_rect ; 1 + dw esc_draw_line ; 2 + dw esc_draw_dot ; 3 + dw esc_set_color ; 4 + dw esc_set_cursor ; 5 + dw esc_set_vmode ; 6 + dw esc_set_charset ; 7 + dw esc_set_palette ; 8 + dw esc_reset_esc ; 9 + dw esc_print_screen ; : + dw esc_fn_b ; ; + dw esc_fn_none ; < + dw esc_fn_none ; = + dw esc_fn_none ; > + dw esc_set_beep ; ? +esc_fn_none: + RET + +esc_set_beep: + LD DE, TM_VARS.m_esc_param_1 + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (TM_VARS.m_beep_period), HL + INC DE + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (TM_VARS.m_beep_duration), HL + RET + +esc_reset_esc: + POP DE + XOR A + LD (TM_VARS.m_esc_mode), A + LD A, (TM_VARS.m_screen_mode) + RET + +esc_print_screen: + LD A, (TM_VARS.m_screen_mode) + AND 00000111b + RET NZ ; ret if not 0 mode + LD DE, 0x30ff + CALL m_print_hor_line + DEC E + LD D, 0xf0 + +fna_chk_keys: + CALL m_con_status + OR A + JP Z, fna_no_keys + CALL tm_con_in + CP ASCII_ESC + RET Z + +fna_no_keys: + CALL m_print_hor_line + DEC E + JP NZ, fna_chk_keys + LD D, 0xe0 + CALL m_print_hor_line + RET + +; ------------------------------------------------------ +; Print line to printer +; D - width +; ------------------------------------------------------ +m_print_hor_line: + LD HL, cmd_esc_set_X0 + + ; Set printer X coordinate = 0 + CALL m_print_cmd + LD HL, 4 + LD (TM_VARS.m_print_pos_x), HL ; Set start coord X = 4 + LD B, 0x0 + +phl_print_next_col: + LD C, 0x0 + ; 1 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (TM_VARS.m_print_pos_x) + INC HL + + ; inc X + LD (TM_VARS.m_print_pos_x), HL + LD C, 0x1 + ; 2 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (TM_VARS.m_print_pos_x) + INC HL + ; inc X + LD (TM_VARS.m_print_pos_x), HL + INC B + LD A, B + CP 236 + JP C, phl_print_next_col + LD HL, cmd_esc_inc_Y2 + CALL m_print_cmd + RET + +; ------------------------------------------------------ +; Send command to printer +; Inp: HL -> to command bytes array +; ------------------------------------------------------ +m_print_cmd: + PUSH BC +ps_print_nxt: + LD A, (HL) + CP ESC_CMD_END + JP Z, ps_cmd_end + LD C, A + CALL m_print_write + INC HL + JP ps_print_nxt +ps_cmd_end: + POP BC + RET + +; ------------------------------------------------------ +; Print 7 vertical pixels to printer +; Inp: A - value to print +; ------------------------------------------------------ +m_print_vert_7pix: + PUSH AF + ; Set coordinate X to 0 + LD HL, cmd_esc_set_X + CALL m_print_cmd + LD HL, (TM_VARS.m_print_pos_x) + LD C,H + CALL m_print_write + LD C,L + CALL m_print_write + ; Set column print mode + LD HL, cmd_esc_print_col + CALL m_print_cmd + POP AF + ; Print 7 vertical pixels + LD C, A + CALL m_print_write + RET + +; ------------------------------------------------------ +; Control codes for printer УВВПЧ-30-004 +; ------------------------------------------------------ +; Zn - Increment Y coordinate +cmd_esc_inc_Y2: + db ASCII_ESC + db 'Z' + db 2h + db ESC_CMD_END + +; Xnn - Set X coordinate +cmd_esc_set_X0: + db ASCII_ESC + db 'X' + db 0h ; 0..479 + db 0h + db ESC_CMD_END +; ------------------------------------------------------ +; X - Start on "Set X coordinate" command +; ------------------------------------------------------ +cmd_esc_set_X: + db ASCII_ESC + db 'X' + db ESC_CMD_END + +; O - Column print (vertical 7 bit) +cmd_esc_print_col: + db ASCII_ESC + db 'O' + db ESC_CMD_END + +; ------------------------------------------------------ +; Get 7 vertical pixels from screen +; Inp: C - sheet +; Out: A - byte +; ------------------------------------------------------ +m_get_7vpix: + LD A, (TM_VARS.m_row_shift) + ADD A, B + ADD A, 19 ; skip first 20pix + LD L, A + PUSH DE + PUSH BC + LD A, E +g7v_calc_pix_no: + AND 0x7 + LD B, A + LD A, E + ; calc hi addr + RRA ; /8 + RRA + RRA + AND 0x1f + ADD A, A ; *2 + ADD A, 64 ; bytes per row + LD H, A + ; select sheet 0|1 + LD A, C + AND 0x1 + ADD A, H + LD H, A + ; HL = pix addr, turn on VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + LD E, (HL) ; read pixel + INC H ; HL += 512 + INC H + LD D, (HL) ; read pixel row+1 + + ; turn off VRAM access + XOR A + OUT (SYS_DD17PB), A +g7v_for_all_pix: + DEC B + JP M,g7v_all_shifted + ; shift pixels D >> [CF] >> E + LD A, D + RRA + LD D, A + LD A, E + RRA + LD E, A + JP g7v_for_all_pix +g7v_all_shifted: + LD A, E + LD D,00000000b + RRA + JP NC,g7v_not_1_1 + LD D,00110000b +g7v_not_1_1: + RRA + JP NC,g7v_not_1_2 + LD A, D + OR 11000000b + LD D, A +g7v_not_1_2: + LD A, D + POP BC + POP DE + RET + +esc_set_palette: + LD A, (TM_VARS.m_esc_param_1) + AND 00111111b ; bgcol[2,1,0],pal[2,1,0] + LD (TM_VARS.m_cur_palette), A + LD B, A + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + LD A, 0x0 + JP NZ,esp_no_colr + LD A, 0x40 + +esp_no_colr: + OR B + OUT (VID_DD67PB), A + RET + +esc_set_charset: + LD A, (TM_VARS.m_esc_param_1) + AND 0x3 ; charset 0..3 + LD (TM_VARS.m_codepage), A + RET + +; ------------------------------------------------------ +; Get address for draw symbol glyph +; ------------------------------------------------------ +m_get_glyph: + LD L, A + LD E, A + XOR A + LD D, A + LD H, A + ; HL = DE = A + ADD HL, HL + ADD HL, DE + ADD HL, HL + ADD HL, DE + ; HL = A * 7 + LD A, E ; A = A at proc entry + CP '@' + ; First 64 symbols is same for all codepages + JP M, m_cp_common + LD A, (TM_VARS.m_codepage) + OR A + ; cp=0 - Latin letters + JP Z, m_cp_common + DEC A + ; cp=1 - Russian letters + JP Z, m_cp_russ + ; cp=2 - 0x40..0x5F - displayed as Lat + ; 0x60 - 0x7F - displayed as Rus + LD A, E + CP 0x60 + JP M, m_cp_common +m_cp_russ: + LD DE, 448 ; +448=64*7 Offset for cp1 + ADD HL, DE + +m_cp_common: + LD DE, m_font_cp0-224 ; m_font_cp0-224 + ADD HL, DE ; add symbol glyph offset + RET + +; ------------------------------------------------------ +; Console output +; ------------------------------------------------------ +co_print_no_esc: + LD A, C + AND 0x7f + CP ' ' + JP M, m_print_control_char + CALL m_get_glyph + ; Calc screen address to DE + EX DE, HL + LD HL, (TM_VARS.m_cursor_row) + LD A, (TM_VARS.m_row_shift) + ADD A, L + LD L, A + LD A, H + ADD A, 64 + LD H, A + LD C, 7 + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + JP NZ, co_m_no_color + ; Access to video RAM + LD A, 0x1 + OUT (SYS_DD17PB), A + EX DE, HL + ; draw to both planes + XOR A + LD (DE), A + INC D + LD (DE), A + DEC D + INC E +co_m_colorify: + LD A, (TM_VARS.m_curr_color) + AND (HL) + ADD A, A + LD (DE), A + INC D + LD A, (TM_VARS.m_curr_color+1) + AND (HL) + ADD A, A + LD (DE), A + + ; next font byte + DEC D + INC HL + INC E + DEC C + JP NZ, co_m_colorify + XOR A + ; Remove access to VRAM + OUT (SYS_DD17PB), A + ; Address to draw cursor proc on stack + LD HL, m_draw_cursor + PUSH HL + LD HL, TM_VARS.m_cursor_row + LD A, (TM_VARS.m_screen_mode) + AND 0x8 + JP NZ, m_cursor_rt_2 + +m_psc_fwd_cmn: + INC HL + LD A, (HL) + ADD A, 0x2 + AND 0x3f + LD (HL), A + DEC HL + RET NZ +m_psc_lf_cmn: + LD A, (HL) + ADD A, 0xe + CP 0xfa + JP NC, LAB_ram_e57a + LD (HL), A + RET +LAB_ram_e57a: + LD A, (TM_VARS.m_row_shift) + ADD A, 0xe + OUT (SYS_DD17PA), A + LD (TM_VARS.m_row_shift), A + LD HL, 0x40f0 + ADD A, L + LD L, A + DEC L + DEC L + LD C, H + LD A, 0x1 + OUT (SYS_DD17PB), A + XOR A + LD DE, 0x1240 + +LAB_ram_e594: + LD H, C + LD B, E +LAB_ram_e596: + LD (HL), A + INC H + DEC B + JP NZ, LAB_ram_e596 + INC L + DEC D + JP NZ, LAB_ram_e594 + XOR A + OUT (SYS_DD17PB), A + RET + +m_psc_bksp_cmn: + INC HL + LD A, (HL) + SUB 0x2 + AND 0x3f + LD (HL), A + CP 0x3e + DEC HL + RET NZ + +m_psc_up_cmn: + LD A, (HL) + SUB 14 + JP NC, up_no_minus + LD A, 238 +up_no_minus: + LD (HL), A + RET + +m_psc_tab_cmn: + INC HL + LD A, (HL) + ADD A,16 + AND 0x30 + LD (HL), A + DEC HL + RET NZ + JP m_psc_lf_cmn + +m_psc_tab: + INC HL + LD A, (HL) + ADD A, 16 + AND 0x30 + LD (HL), A + DEC HL + RET NZ + JP m_psc_lf + + ; Move cursor 2 sym right, move to next line if wrap +m_cursor_rt_2: + INC HL + LD A, (HL) + ADD A,2 + AND 0x3f ; screen column 0..63 + LD (HL), A + DEC HL + RET NZ ; Return if no wrap + +m_psc_lf: + LD A, (HL) + ADD A, 12 + CP 16 + JP NC, mp_next_nowr + LD (HL), A + RET + +mp_next_nowr: + LD A, (TM_VARS.m_row_shift) + LD L, A + ADD A, 12 + LD E, A + LD C, 8 + ; Acces VRAM + LD A,1 + OUT (SYS_DD17PB), A +LAB_ram_e5f2: + LD B, 0x40 + LD H, 0x40 + LD D, H +LAB_ram_e5f7: + LD A, (DE) + LD (HL), A + INC H + INC D + DEC B + JP NZ, LAB_ram_e5f7 + INC L + INC E + DEC C + JP NZ, LAB_ram_e5f2 + LD C, 0xc + LD A, (TM_VARS.m_row_shift) + ADD A, 0x8 + LD E, A +LAB_ram_e60d: + LD B, 0x40 + LD D, 0x40 + XOR A +LAB_ram_e612: + LD (DE), A + INC D + DEC B + JP NZ, LAB_ram_e612 + INC E + DEC C + JP NZ, LAB_ram_e60d + XOR A + OUT (SYS_DD17PB), A + RET + +m_psc_bksp: + INC HL + LD A, (HL) + OR A + DEC HL + RET Z + INC HL + SUB 0x2 + AND 0x3f + LD (HL), A + DEC HL + RET + +co_m_no_color: + CP 7 + JP Z, LAB_ram_e6b5 + CP 3 + JP Z, LAB_ram_e6b5 + AND 0x2 + JP NZ, LAB_ram_e7b9 + LD A, 0x1 + OUT (SYS_DD17PB), A + EX DE, HL + XOR A + LD (DE), A + INC E + +LAB_ram_e645: + LD A, (HL) + ADD A, A + LD (DE), A + INC HL + INC E + DEC C + JP NZ, LAB_ram_e645 + XOR A + OUT (SYS_DD17PB), A + LD HL, m_draw_cursor + PUSH HL + LD HL, TM_VARS.m_cursor_row +LAB_ram_e658: + INC HL + LD A, (HL) + ADD A, 0x1 + AND 0x3f + LD (HL), A + DEC HL + RET NZ +LAB_ram_e661: + LD A, (HL) + ADD A, 0xb + CP 0xfa + JP NC, LAB_ram_e66b + LD (HL), A + RET + +LAB_ram_e66b: + LD A, (TM_VARS.m_row_shift) + ADD A, 0xb + OUT (SYS_DD17PA), A + LD (TM_VARS.m_row_shift), A + LD HL, 0x40f0 + ADD A,L + LD L, A + LD C,H + LD A, 0x1 + OUT (SYS_DD17PB), A + XOR A + LD DE, 0x1040 +LAB_ram_e683: + LD H, C + LD B, E +LAB_ram_e685: + LD (HL), A + INC H + DEC B + JP NZ, LAB_ram_e685 + INC L + DEC D + JP NZ, LAB_ram_e683 + XOR A + OUT (SYS_DD17PB), A + RET +LAB_ram_e694: + INC HL + LD A, (HL) + SUB 0x1 + AND 0x3f + LD (HL), A + CP 0x3f + DEC HL + RET NZ +LAB_ram_e69f: + LD A, (HL) + SUB 0xb + JP NC, LAB_ram_e6a7 + LD A, 0xf2 +LAB_ram_e6a7: + LD (HL), A + RET +LAB_ram_e6a9: + INC HL + LD A, (HL) + ADD A, 0x8 + AND 0x38 + LD (HL), A + DEC HL + RET NZ + JP LAB_ram_e661 +LAB_ram_e6b5: + CALL m_calc_addr_40 + LD A, 0x1 + OUT (SYS_DD17PB), A + EX DE, HL + LD A,B + OR B + JP Z, LAB_ram_e6cd + DEC B + JP Z, LAB_ram_e6df + DEC B + JP Z, LAB_ram_e706 + JP LAB_ram_e731 +LAB_ram_e6cd: + XOR A + LD (DE), A + INC E +LAB_ram_e6d0: + LD B, (HL) + LD A, (DE) + AND 0xc0 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, LAB_ram_e6d0 + JP LAB_ram_e745 +LAB_ram_e6df: + XOR A + LD (DE), A + DEC D + LD (DE), A + INC D + INC E +LAB_ram_e6e5: + LD A, (HL) + RRCA + RRCA + AND 0x7 + LD B, A + LD A, (DE) + AND 0xf0 + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + AND 0xc0 + LD B, A + DEC D + LD A, (DE) + AND 0x1f + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, LAB_ram_e6e5 + JP LAB_ram_e745 +LAB_ram_e706: + XOR A + LD (DE), A + DEC D + LD (DE), A + INC D + INC E +LAB_ram_e70c: + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0x1 + LD B, A + LD A, (DE) + AND 0xfc + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0xf0 + LD B, A + DEC D + LD A, (DE) + AND 0x7 + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, LAB_ram_e70c + JP LAB_ram_e745 +LAB_ram_e731: + DEC D + XOR A + LD (DE), A + INC E +LAB_ram_e735: + LD A, (HL) + RLCA + RLCA + LD B, A + LD A, (DE) + AND 0x1 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, LAB_ram_e735 + INC D +LAB_ram_e745: + XOR A + OUT (SYS_DD17PB), A + LD HL, m_draw_cursor + PUSH HL + LD HL, TM_VARS.m_cursor_row +LAB_ram_e74f: + INC HL + LD A, (HL) + ADD A, 0x1 + AND 0x7f + LD (HL), A + CP 0x50 + DEC HL + RET M +LAB_ram_e75a: + INC HL + XOR A + LD (HL), A + DEC HL +LAB_ram_e75e: + LD A, (HL) + ADD A, 0xb + CP 0xfa + JP NC, LAB_ram_e66b + LD (HL), A + RET +LAB_ram_e768: + INC HL + LD A, (HL) + SUB 0x1 + AND 0x7f + CP 0x7f + JP Z, LAB_ram_e776 + LD (HL), A + DEC HL + RET +LAB_ram_e776: + LD A, 0x4f + LD (HL), A + DEC HL +LAB_ram_e77a: + LD A, (HL) + SUB 0xb + JP NC, LAB_ram_e782 + LD A, 0xf2 +LAB_ram_e782: + LD (HL), A + RET +LAB_ram_e784: + INC HL + LD A, (HL) + ADD A, 0x8 + AND 0x7f + LD (HL), A + CP 0x50 + DEC HL + RET M + JP LAB_ram_e75a + +; ------------------------------------------------------ +; Calculate text position in 40 column text mode +; Out: HL - addr +; B - bit no +; C = 7 +; ------------------------------------------------------ +m_calc_addr_40: + LD HL, (TM_VARS.m_cursor_row) + LD A, (TM_VARS.m_row_shift) + ADD A,L + LD L, A ; HL = row+shift + LD A,H + CP 4 + LD B, A + JP M, ca_bef_scrn_top + AND 0x3 + LD B, A + LD A,H + OR A + RRA + OR A + RRA + LD C, A + LD H, 0x3 + XOR A + +ca_lbl1: + ADD A,H + DEC C + JP NZ, ca_lbl1 + ADD A,B +ca_bef_scrn_top: + ADD A, 0x40 ; next row + LD H, A + LD C, 0x7 + RET + +LAB_ram_e7b9: + LD A, (TM_VARS.m_cursor_col) + CP 0x40 + JP M, LAB_ram_e7c8 + LD HL, TM_VARS.m_cursor_row + CALL m_draw_cursor + RET +LAB_ram_e7c8: + LD A, 0x1 + OUT (SYS_DD17PB), A + EX DE, HL + XOR A + LD (DE), A + INC E +LAB_ram_e7d0: + LD A, (HL) + ADD A, A + LD (DE), A + INC HL + INC E + DEC C + JP NZ, LAB_ram_e7d0 + XOR A + OUT (SYS_DD17PB), A + LD HL, m_draw_cursor + PUSH HL + LD HL, TM_VARS.m_cursor_row + INC HL + LD A, (HL) + ADD A, 0x1 + CP 0x40 + JP M, LAB_ram_e7ee + LD A, 0x40 +LAB_ram_e7ee: + LD (HL), A + DEC HL + RET + +m_psc_clrscr_cmn: + LD A, (TM_VARS.m_screen_mode) + AND 0x8 + JP NZ,m_clr_color + LD A,01111111b + OUT (VID_DD67PB), A ; C/M=1 FL=111 CL=111 All black + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD DE, video_ram + EX DE, HL + LD A,H + ADD A,64 ; row + 1 + LD B, 0x0 + +clr_fill_scrn1: + LD (HL),B + INC HL + CP H + JP NZ, clr_fill_scrn1 + EX DE, HL + LD A, (TM_VARS.m_cur_palette) + LD B, A + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + LD A, 0x0 + JP NZ, clr_rest_no_color + LD A, 01000000b + +clr_rest_no_color: + OR B + ; Restore mode and palette + OUT (VID_DD67PB), A + +m_psc_home_cmn: + XOR A + NOP + NOP + LD (HL), A + INC HL + XOR A + LD (HL), A + DEC HL + XOR A + ; Disable VRAM access + OUT (SYS_DD17PB), A + RET + + ; Clear scr in color mode +m_clr_color: + LD A, (TM_VARS.m_row_shift) + LD L, A + LD C,20 + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A +m_clr_fill_c1: + LD H, 0x40 ; HL = 0x4000 + shift_row + LD B,64 ; 64 bytes at row + XOR A +m_clr_fill_c2: + LD (HL), A + INC H + DEC B + JP NZ, m_clr_fill_c2 + INC L + DEC C + JP NZ, m_clr_fill_c1 + XOR A + ; Disabe VRAM access + OUT (SYS_DD17PB), A + JP m_psc_home_cmn +m_draw_cursor: + LD A, (TM_VARS.m_screen_mode) + AND 0x4 + RET NZ + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + JP NZ, LAB_ram_e884 + EX DE, HL + LD HL, (TM_VARS.m_cursor_row) + LD A, (TM_VARS.m_row_shift) + ADD A, L + LD L, A + LD A, H + ADD A, 0x40 + LD H, A + LD A, 0x1 + OUT (SYS_DD17PB), A + LD BC, 0x7f08 +LAB_ram_e872: + LD A, (HL) + XOR B + LD (HL), A + INC H + LD A, (HL) + XOR B + LD (HL), A + DEC H + INC L + DEC C + JP NZ, LAB_ram_e872 + EX DE, HL + XOR A + OUT (SYS_DD17PB), A + RET +LAB_ram_e884: + CP 0x3 + JP Z, LAB_ram_e8af + EX DE, HL + LD HL, (TM_VARS.m_cursor_row) + LD A, (TM_VARS.m_row_shift) + ADD A, L + LD L, A + LD A, H + CP 0x40 + EX DE, HL + RET P + EX DE, HL + ADD A, 0x40 + LD H, A + LD A, 0x1 + OUT (SYS_DD17PB), A + LD BC, 0x7f08 +LAB_ram_e8a2: + LD A, (HL) + XOR B + LD (HL), A + INC L + DEC C + JP NZ, LAB_ram_e8a2 + EX DE, HL + XOR A + OUT (SYS_DD17PB), A + RET +LAB_ram_e8af: + EX DE, HL + LD HL, (TM_VARS.m_cursor_row) + LD A, H + CP 0x50 + EX DE, HL + RET P + EX DE, HL + CALL m_calc_addr_40 + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, B + OR B + JP Z, LAB_ram_e8d0 + DEC B + JP Z, LAB_ram_e8e6 + DEC B + JP Z, LAB_ram_e908 + JP LAB_ram_e92a +LAB_ram_e8d0: + LD BC, 0x1f08 +LAB_ram_e8d3: + LD A, (HL) + AND 0xc0 + LD B, A + LD A, (HL) + XOR 0x1f + AND 0x1f + OR B + LD (HL), A + INC L + DEC C + JP NZ, LAB_ram_e8d3 + JP LAB_ram_e93e +LAB_ram_e8e6: + LD C, 0x8 +LAB_ram_e8e8: + DEC H + LD A, (HL) + AND 0x1f + LD B, A + LD A, (HL) + XOR 0xc0 + AND 0xc0 + OR B + LD (HL), A + INC H + LD A, (HL) + AND 0xf0 + LD B, A + LD A, (HL) + XOR 0x7 + AND 0x7 + OR B + LD (HL), A + INC L + DEC C + JP NZ, LAB_ram_e8e8 + JP LAB_ram_e93e +LAB_ram_e908: + LD C, 0x8 +LAB_ram_e90a: + DEC H + LD A, (HL) + AND 0x7 + LD B, A + LD A, (HL) + XOR 0xf0 + AND 0xf0 + OR B + LD (HL), A + INC H + LD A, (HL) + AND 0xfc + LD B, A + LD A, (HL) + XOR 0x1 + AND 0x1 + OR B + LD (HL), A + INC L + DEC C + JP NZ, LAB_ram_e90a + JP LAB_ram_e93e +LAB_ram_e92a: + LD C, 0x8 + DEC H +LAB_ram_e92d: + LD A, (HL) + AND 0x1 + LD B, A + LD A, (HL) + XOR 0x7c + AND 0x7c + OR B + LD (HL), A + INC L + DEC C + JP NZ, LAB_ram_e92d + INC H +LAB_ram_e93e: + EX DE, HL + XOR A + OUT (SYS_DD17PB), A ; reset screen shifts + RET + +m_print_control_char: + CP ASCII_ESC + JP NZ,m_psc_std_char + ; turn on ESC mode for next chars + LD HL, TM_VARS.m_esc_mode + LD (HL), 0x1 + INC HL + LD (HL), 0xff + RET + +m_psc_std_char: + CP ASCII_BELL + JP Z, m_beep + LD HL, m_draw_cursor + PUSH HL + LD HL, TM_VARS.m_cursor_row + PUSH AF + CALL m_draw_cursor + LD A, (TM_VARS.m_screen_mode) + AND 0x8 ; mode 40x20? + JP Z, m_psc_no_40x30 ; jump if no + POP AF + CP ASCII_TAB ; TAB + JP Z,m_psc_tab + CP ASCII_BS ; BKSP + JP Z,m_psc_bksp + CP ASCII_CAN ; Cancel + JP Z,m_cursor_rt_2 + CP ASCII_US ; ASCII Unit separator + JP Z,m_clr_color + CP ASCII_LF ; LF + JP Z,m_psc_lf + CP ASCII_CR ; CR + RET NZ ; ret on unknown + INC HL + LD (HL), 0x0 + DEC HL + RET + + ; common for 40x25, 64x25, 80x25 modes +m_psc_no_40x30: + POP AF + CP ASCII_US ; Unit separator + JP Z, m_psc_clrscr_cmn + CP ASCII_FF ; Form feed + JP Z, m_psc_home_cmn + PUSH AF + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + JP NZ, LAB_ram_e9c6 + POP AF + CP ASCII_TAB + JP Z, m_psc_tab_cmn + CP ASCII_BS + JP Z, m_psc_bksp_cmn + CP ASCII_CAN + JP Z, m_psc_fwd_cmn + CP ASCII_EM ; ASCII End of medium + JP Z, m_psc_up_cmn + CP ASCII_SUB + JP Z, m_psc_lf_cmn ; cursor down + CP ASCII_LF + JP Z, m_psc_lf_cmn + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +LAB_ram_e9c6: + LD A, (TM_VARS.m_screen_mode) + CP 0x3 + JP Z, m_psc_no_40x25 + CP 0x7 + JP Z, m_psc_no_40x25 + AND 0x2 + JP NZ, LAB_ram_e9ff +; For 40x25? + POP AF + CP 0x9 + JP Z, LAB_ram_e6a9 + CP 0x8 + JP Z, LAB_ram_e694 + CP 0x18 + JP Z, LAB_ram_e658 + CP 0x19 + JP Z, LAB_ram_e69f + CP 0x1a + JP Z, LAB_ram_e661 + CP 0xa + JP Z, LAB_ram_e661 + CP 0xd + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +LAB_ram_e9ff: + POP AF + CP 0xa + JP Z, LAB_ram_e661 + CP 0xd + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +m_psc_no_40x25: + POP AF + CP 0x9 + JP Z, LAB_ram_e784 + CP 0x8 + JP Z, LAB_ram_e768 + CP 0x18 + JP Z, LAB_ram_e74f + CP 0x19 + JP Z, LAB_ram_e77a + CP 0x1a + JP Z, LAB_ram_e75e + CP 0xa + JP Z, LAB_ram_e75e + CP 0xd + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +m_beep: + LD HL, (TM_VARS.m_beep_duration) + EX DE, HL + LD HL, (TM_VARS.m_beep_period) + LD A,00110110b ; TMR#0 LSB+MSB Square Wave Generator + OUT (TMR_DD70CTR), A + LD A,L ; LSB + OUT (TMR_DD70C1), A + LD A,H ; MSB + OUT (TMR_DD70C1), A + LD A, (TM_VARS.m_strobe_state) + LD B, A +m_bell_cont: + LD A, D ; DE=duration + OR E + RET Z ; ret if enough + DEC DE + LD A,B + XOR BELL_PIN + LD B, A + OUT (DD67PC), A ; Invert bell pin +m_bell_wait_tmr1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; 0x10 + JP NZ,m_bell_wait_tmr1 + LD A,B + XOR BELL_PIN ; Flip pin again + LD B, A + OUT (DD67PC), A +m_bell_wait_tmr2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP Z,m_bell_wait_tmr2 + JP m_bell_cont + +; ------------------------------------------------------ +; 5 Set cursor position +; ------------------------------------------------------ +esc_set_cursor: + LD A, (TM_VARS.m_screen_mode) + AND 0x8 + RET NZ ; ret if graphics mode + CALL m_draw_cursor ; hide cursor + LD A, (TM_VARS.m_screen_mode) + AND 0x7 ; mode 0-7 + JP NZ,sc_set_64_or_80 + ; Set cursor for 32x18 mode + LD DE, TM_VARS.m_esc_param_1 + LD HL, TM_VARS.m_cursor_col + INC DE + LD A, (DE) ; column + AND 0x1f ; limit column to 0..31 + ADD A, A ; *2 + LD (HL), A + DEC DE + DEC HL + LD A, (DE) ; row + AND 0x1f ; 0..31 + CP 17 + JP C,sc_no_row_limit1 + LD A,17 + +sc_no_row_limit1: + LD B, A + ADD A, A + ADD A,B + ADD A, A + ADD A,B + ADD A, A ; a = a * 14 (font height) + LD (HL), A + CALL m_draw_cursor + RET + +sc_set_64_or_80: + LD A, (TM_VARS.m_screen_mode) + CP 0x3 + JP Z, sc_set_for_80x32 + CP 0x7 + JP Z, sc_set_for_80x32 + AND 0x2 + JP NZ, sc_set_for_65x23 + ; Set for 64 col modes + LD DE, TM_VARS.m_esc_param_1 ; row + LD HL, TM_VARS.m_cursor_col + INC DE + LD A, (DE) ; column + SUB 0x20 + AND 0x3f ; 0..63 + LD (HL), A + DEC DE + DEC HL + LD A, (DE) ; row + ; limit row to 0..22 + AND 0x1f + CP 22 + JP C,sc_no_row_limit2 + LD A,22 +sc_no_row_limit2: + LD B, A + ADD A, A + ADD A, A + ADD A,B + ADD A, A + ADD A,B ; A = A * 11 (font height) + LD (HL), A + CALL m_draw_cursor ; show cursor + RET + +sc_set_for_65x23: + LD DE, TM_VARS.m_esc_param_1 + LD HL, TM_VARS.m_cursor_col + INC DE + LD A, (DE) ; column + SUB 0x20 + CP 64 + JP M,sc_no_col_limit3 + LD A,64 + +sc_no_col_limit3: + LD (HL), A + DEC DE + DEC HL + LD A, (DE) ; row + AND 0x1f + CP 22 + JP C,sc_no_row_limit3 + LD A,22 +sc_no_row_limit3: + LD B, A + ADD A, A + ADD A, A + ADD A,B + ADD A, A + ADD A,B ; A = A * 11 + LD (HL), A + CALL m_draw_cursor ; show cursor + RET + +sc_set_for_80x32: + LD DE, TM_VARS.m_esc_param_1 + LD HL, TM_VARS.m_cursor_col + INC DE + LD A, (DE) + SUB 0x20 + AND 0x7f + CP 79 + JP M, sc_no_row_limit4 + LD A,79 + +sc_no_row_limit4: + LD (HL), A + DEC DE + DEC HL + LD A, (DE) + AND 0x1f + CP 22 + JP C,sc_no_col_limit4 + LD A,22 + +sc_no_col_limit4: + LD B, A + ADD A, A + ADD A, A + ADD A,B + ADD A, A + ADD A,B ; A = A * 11 + LD (HL), A + CALL m_draw_cursor ; show cursor + RET + +; ------------------------------------------------------ +; 6n +; where n is +; 0 - 32x18 with cursor; +; 1,2 - 64x23 with cursor; +; 3 - 80x23 with cursor; +; 4 - 32x18 no cursor; +; 5,6 - 64x23 no cursor; +; 7 - 80x23 no cursor; +; 8 - graphics mode. +; ------------------------------------------------------ +esc_set_vmode: + LD HL, TM_VARS.m_screen_mode + LD A, (TM_VARS.m_cur_palette) + LD B, A ; b = palette + LD A, (TM_VARS.m_esc_param_1) + LD C, A ; c = mode + AND 0x8 ; graph mode? + LD A, C + JP Z,svm_set_text + LD A, 0x8 +svm_set_text: + AND 0xf + LD (HL), A ; save new mode + AND 0x7 ; with cursor? + LD A,00000000b + JP NZ, swm_no_color + LD A,01000000b +swm_no_color: + OR B + OUT (VID_DD67PB), A ; Set C/M and palette + LD HL, TM_VARS.m_cursor_row + CALL m_psc_clrscr_cmn + CALL m_draw_cursor + RET + +; ------------------------------------------------------ +; 4n n=1..4 Set drawing color +; ------------------------------------------------------ +esc_set_color: + LD A, (TM_VARS.m_esc_param_1) + AND 0x3 + RRA + LD B, A + LD A, 0x0 + SBC A, A + LD (TM_VARS.m_curr_color), A + LD A, B + DEC A + CPL + LD (TM_VARS.m_curr_color+1), A + RET + +co_exit_esc: + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + JP NZ, esc_exit + LD A, C + AND 0x7f + LD C, A + CP 0x20 + JP M, esc_exit + LD HL, TM_VARS.m_esc_param_1 + LD A, (HL) + LD E, A + ADD A, 0x8 + JP C, esc_exit + LD (HL), A + INC HL + LD A, 0xf7 + CP (HL) + JP C, esc_exit + LD D, (HL) + CALL SUB_ram_ebe2 + LD A, L + SUB 0x7 + LD L, A + PUSH HL + LD A, C + CALL m_get_glyph + POP DE + LD C, 0x7 + +LAB_ram_eb9b: + PUSH HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD L, (HL) + LD H, 0x0 + LD A, B + OR A + JP Z, LAB_ram_ebad +LAB_ram_eba8: + ADD HL, HL + DEC A + JP NZ, LAB_ram_eba8 +LAB_ram_ebad: + EX DE, HL + PUSH BC + LD A, (TM_VARS.m_curr_color) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + LD A, (TM_VARS.m_curr_color+1) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + DEC H + DEC H + INC L + EX DE, HL + POP BC + XOR A + OUT (SYS_DD17PB), A + POP HL + INC HL + DEC C + JP NZ, LAB_ram_eb9b + RET +SUB_ram_ebe2: + LD A, (TM_VARS.m_row_shift) + SUB D + DEC A + LD L, A + LD A, E + AND 0x7 + LD B, A + LD A, E + RRA + RRA + AND 0x3e + ADD A, 0x40 + LD H, A + RET + +esc_draw_fill_rect: + LD HL, TM_VARS.m_esc_param_4 + LD DE, TM_VARS.m_esc_param_2 + LD A, (DE) + LD B, (HL) + CP B + JP NC, LAB_ram_ec04 + LD (HL), A + LD A,B + LD (DE), A +LAB_ram_ec04: + DEC DE + DEC HL + LD A, (DE) + LD B, (HL) + CP B + JP C, LAB_ram_ec0f + LD (HL), A + LD A,B + LD (DE), A +LAB_ram_ec0f: + EX DE, HL + LD E, (HL) + INC HL + LD D, (HL) + CALL SUB_ram_ebe2 + PUSH HL + XOR A +LAB_ram_ec18: + SCF + RLA + DEC B + JP P, LAB_ram_ec18 + RRA + LD D, A + LD HL, TM_VARS.m_esc_param_3 + LD A, (HL) + AND 0x7 + LD B, A + XOR A +LAB_ram_ec28: + SCF + RLA + DEC B + JP P, LAB_ram_ec28 + CPL + LD E, A + LD A, (HL) + DEC HL + DEC HL + SUB (HL) + RRCA + RRCA + RRCA + AND 0x1f + LD C, A + INC HL + LD A, (HL) + INC HL + INC HL + SUB (HL) + JP NZ, LAB_ram_ec43 + INC A +LAB_ram_ec43: + LD B, A + POP HL + LD A, E + LD (TM_VARS.m_esc_hex_cmd), A +LAB_ram_ec49: + PUSH DE + PUSH HL + PUSH BC + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, C + OR A + JP NZ, LAB_ram_ec58 + LD A, D + OR E +dr_no_cmd: + LD D, A +LAB_ram_ec58: + LD B, D + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + INC H + LD A, C + OR A + JP Z, LAB_ram_ec81 + DEC C +LAB_ram_ec70: + LD A, (TM_VARS.m_esc_hex_cmd) + JP Z, dr_no_cmd +LAB_ram_ec76: + LD (HL), E + INC H + LD (HL), D + INC H + DEC C + JP NZ, LAB_ram_ec76 + JP LAB_ram_ec70 +LAB_ram_ec81: + XOR A + OUT (SYS_DD17PB), A + POP BC + POP HL + POP DE + INC L + DEC B + JP NZ, LAB_ram_ec49 + RET + +esc_draw_line: + LD HL, TM_VARS.m_esc_param_1 + LD E, (HL) + INC HL + LD D, (HL) + INC HL + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + CP E + JP C, LAB_ram_ec9d + EX DE, HL +LAB_ram_ec9d: + LD (TM_VARS.m_esc_param_1), HL + LD A, E + SUB L + LD L, A + LD A, D + SUB H + LD H, A + PUSH AF + JP NC, LAB_ram_ecad + CPL + INC A + LD H, A +LAB_ram_ecad: + EX DE, HL + LD HL, (TM_VARS.m_esc_param_1) + EX DE, HL + JP Z, LAB_ram_ed96 + LD A, L + OR A + JP Z, LAB_ram_ed4c + LD B, A + POP AF + LD A, 0x0 + ADC A, A + LD (TM_VARS.m_esc_hex_cmd), A + LD E, H + LD C, 0x10 + LD D, 0x0 +LAB_ram_ecc7: + ADD HL, HL + EX DE, HL + ADD HL, HL + EX DE, HL + LD A, D + JP C, LAB_ram_ecd3 + CP B + JP C, LAB_ram_ecd6 +LAB_ram_ecd3: + SUB B + LD D, A + INC HL +LAB_ram_ecd6: + DEC C + JP NZ, LAB_ram_ecc7 + LD DE, 0x0 + PUSH DE + PUSH HL + LD HL, (TM_VARS.m_esc_param_1) + EX DE, HL + LD C,B + CALL SUB_ram_ebe2 + LD A, 0x80 +LAB_ram_ece9: + RLCA + DEC B + JP P, LAB_ram_ece9 + CPL + LD B, A +LAB_ram_ecf0: + POP DE + EX (SP), HL + LD A,H + ADD HL, DE + SUB H + CPL + INC A + EX (SP), HL + PUSH DE + PUSH BC + LD C, A + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (TM_VARS.m_esc_hex_cmd) + OR A + JP NZ, LAB_ram_ed21 +LAB_ram_ed0b: + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, LAB_ram_ed37 + DEC C + DEC L + JP LAB_ram_ed0b +LAB_ram_ed21: + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, LAB_ram_ed37 + DEC C + INC L + JP LAB_ram_ed21 +LAB_ram_ed37: + XOR A + OUT (SYS_DD17PB), A + POP BC + LD A,B + SCF + RLA + JP C, LAB_ram_ed44 + RLA + INC H + INC H +LAB_ram_ed44: + LD B, A + DEC C + JP NZ, LAB_ram_ecf0 + POP HL + POP HL + RET +LAB_ram_ed4c: + LD C,H + CALL SUB_ram_ebe2 + LD A, 0x80 +LAB_ram_ed52: + RLCA + DEC B + JP P, LAB_ram_ed52 + CPL + LD B, A + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + POP AF + LD A, 0x1 + OUT (SYS_DD17PB), A + JP C, LAB_ram_ed7c +LAB_ram_ed66: + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, LAB_ram_ed92 + DEC C + DEC L + JP LAB_ram_ed66 +LAB_ram_ed7c: + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, LAB_ram_ed92 + DEC C + INC L + JP LAB_ram_ed7c +LAB_ram_ed92: + XOR A + OUT (SYS_DD17PB), A + RET +LAB_ram_ed96: + POP AF + LD C,L + LD A,L + OR A + JP NZ, LAB_ram_ed9e + INC C +LAB_ram_ed9e: + CALL SUB_ram_ebe2 + LD A, 0x80 +LAB_ram_eda3: + RLCA + DEC B + JP P, LAB_ram_eda3 + CPL + LD B, A + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A +LAB_ram_edb3: + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A,B + SCF + RLA + JP C, LAB_ram_edc8 + RLA + INC H + INC H +LAB_ram_edc8: + LD B, A + DEC C + JP NZ, LAB_ram_edb3 + XOR A + OUT (SYS_DD17PB), A + RET +esc_draw_dot: + LD HL, (TM_VARS.m_esc_param_1) + EX DE, HL + CALL SUB_ram_ebe2 + LD A, 0x80 +LAB_ram_edda: + RLCA + DEC B + JP P, LAB_ram_edda + LD B, A + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + XOR B + LD (HL), A + INC H + LD A, (HL) + XOR B + LD (HL), A + XOR A + OUT (SYS_DD17PB), A + RET +esc_fn_b: + LD A, (TM_VARS.m_esc_param_3) + LD B, A + OR A + RET Z + LD A, 0x7f + CP B + RET M + XOR A + LD D, A + LD E,B + CALL SUB_ram_ee53 + LD A, 0x1 + LD H, A + SUB B + LD C, A + LD A,B + RLCA + LD B, A + LD A, 0x1 + SUB B + LD L, A + CCF +LAB_ram_ee11: + INC D + LD A, E + CP D + JP Z,SUB_ram_ee53 + CALL SUB_ram_ee53 + LD A,H + ADD A, 0x2 + LD H, A + LD A,L + ADD A, 0x2 + LD L, A + LD A, C + ADD A,H + LD C, A + JP NC, LAB_ram_ee11 +LAB_ram_ee28: + CCF + INC D + DEC E + LD A, D + CP E + JP Z,SUB_ram_ee53 + SUB E + CP 0x1 + RET Z + LD A, E + SUB D + CP 0x1 + JP Z,SUB_ram_ee53 + CALL SUB_ram_ee53 + LD A,H + ADD A, 0x2 + LD H, A + LD A,L + ADD A, 0x4 + LD L, A + JP NC, LAB_ram_ee4a + CCF +LAB_ram_ee4a: + LD A, C + ADD A,L + LD C, A + JP NC, LAB_ram_ee11 + JP LAB_ram_ee28 +SUB_ram_ee53: + PUSH HL + PUSH DE + PUSH BC + PUSH DE + CALL SUB_ram_ee6f + LD HL, (TM_VARS.m_esc_param_1) + CALL SUB_ram_eedc + POP DE + CALL SUB_ram_ee8f + LD HL, (TM_VARS.m_esc_param_1) + CALL SUB_ram_ef0b + POP BC + POP DE + POP HL + XOR A + RET +SUB_ram_ee6f: + LD HL, (TM_VARS.m_esc_param_4) + LD A,L + OR A + LD C, D + LD B, E + JP NZ, LAB_ram_ee7f + LD A,H + OR A + JP NZ, LAB_ram_ee88 + RET +LAB_ram_ee7f: + LD A,H + LD H,L + LD E, C +OFF_ram_ee82: + CALL SUB_ram_eeaf + LD C, E + OR A + RET Z +LAB_ram_ee88: + LD H, A + LD E,B + CALL SUB_ram_eeaf + LD B, E + RET +SUB_ram_ee8f: + LD HL, (TM_VARS.m_esc_param_4) + LD A,L + OR A + LD C, D + LD B, E + JP NZ, LAB_ram_ee9f + LD A,H + OR A + JP NZ, LAB_ram_eea8 + RET +LAB_ram_ee9f: + LD A,H + LD H,L + LD E,B + CALL SUB_ram_eeaf + LD B, E + OR A + RET Z +LAB_ram_eea8: + LD H, A + LD E, C + CALL SUB_ram_eeaf + LD C, E + RET +SUB_ram_eeaf: + LD D, 0x0 + LD L, D + ADD HL, HL + JP NC, LAB_ram_eeb7 + ADD HL, DE +LAB_ram_eeb7: + ADD HL, HL + JP NC, LAB_ram_eebc + ADD HL, DE +LAB_ram_eebc: + ADD HL, HL + JP NC, LAB_ram_eec1 + ADD HL, DE +LAB_ram_eec1: + ADD HL, HL + JP NC, LAB_ram_eec6 + ADD HL, DE +LAB_ram_eec6: + ADD HL, HL + JP NC, LAB_ram_eecb + ADD HL, DE +LAB_ram_eecb: + ADD HL, HL + JP NC, LAB_ram_eed0 + ADD HL, DE +LAB_ram_eed0: + ADD HL, HL + JP NC, LAB_ram_eed5 + ADD HL, DE +LAB_ram_eed5: + ADD HL, HL + JP NC, LAB_ram_eeda + ADD HL, DE +LAB_ram_eeda: + LD E,H + RET +SUB_ram_eedc: + LD A,H + ADD A,B + JP C, LAB_ram_eee8 + LD D, A + LD A,L + ADD A, C + LD E, A + CALL SUB_ram_ef3a +LAB_ram_eee8: + LD A,H + ADD A,B + JP C, LAB_ram_eef4 + LD D, A + LD A,L + SUB C + LD E, A + CALL SUB_ram_ef3a +LAB_ram_eef4: + LD A,H + SUB B + JP C, LAB_ram_ef00 + LD D, A + LD A,L + SUB C + LD E, A + CALL SUB_ram_ef3a +LAB_ram_ef00: + LD A,H + SUB B + RET C + LD D, A + LD A,L + ADD A, C + LD E, A + CALL SUB_ram_ef3a + RET +SUB_ram_ef0b: + LD A,H + ADD A, C + JP C, LAB_ram_ef17 + LD D, A + LD A,L + ADD A,B + LD E, A + CALL SUB_ram_ef3a +LAB_ram_ef17: + LD A,H + ADD A, C + JP C, LAB_ram_ef23 + LD D, A + LD A,L + SUB B + LD E, A + CALL SUB_ram_ef3a +LAB_ram_ef23: + LD A,H + SUB C + JP C, LAB_ram_ef2f + LD D, A + LD A,L + SUB B + LD E, A + CALL SUB_ram_ef3a +LAB_ram_ef2f: + LD A,H + SUB C + RET C + LD D, A + LD A,L + ADD A,B + LD E, A + CALL SUB_ram_ef3a + RET +SUB_ram_ef3a: + RET C + PUSH HL + PUSH BC + CALL SUB_ram_ebe2 + LD A, 0x80 +LAB_ram_ef42: + RLCA + DEC B + JP P, LAB_ram_ef42 + CPL + LD B, A + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + XOR A + OUT (SYS_DD17PB), A + POP BC + POP HL + RET + + ; Full charset, Common + Latin letters +m_font_cp0: + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 + db 0x04, 0x04, 0x04, 0x00, 0x00, 0x04, 0x14, 0x14 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x1F + db 0x0A, 0x1F, 0x0A, 0x0A, 0x04, 0x1E, 0x05, 0x0E + db 0x14, 0x0F, 0x04, 0x03, 0x13, 0x08, 0x04, 0x02 + db 0x19, 0x18, 0x06, 0x09, 0x05, 0x02, 0x15, 0x09 + db 0x16, 0x06, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00 + db 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x02 + db 0x04, 0x08, 0x08, 0x08, 0x04, 0x02, 0x00, 0x0A + db 0x04, 0x1F, 0x04, 0x0A, 0x00, 0x00, 0x04, 0x04 + db 0x1F, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x06, 0x04, 0x02, 0x00, 0x00, 0x00, 0x1F, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06 + db 0x06, 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00 + db 0x0E, 0x11, 0x19, 0x15, 0x13, 0x11, 0x0E, 0x04 + db 0x06, 0x04, 0x04, 0x04, 0x04, 0x0E, 0x0E, 0x11 + db 0x10, 0x08, 0x04, 0x02, 0x1F, 0x1F, 0x08, 0x04 + db 0x08, 0x10, 0x11, 0x0E, 0x08, 0x0C, 0x0A, 0x09 + db 0x1F, 0x08, 0x08, 0x1F, 0x01, 0x0F, 0x10, 0x10 + db 0x11, 0x0E, 0x0C, 0x02, 0x01, 0x0F, 0x11, 0x11 + db 0x0E, 0x1F, 0x10, 0x08, 0x04, 0x02, 0x02, 0x02 + db 0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E, 0x0E + db 0x11, 0x11, 0x1E, 0x10, 0x08, 0x06, 0x00, 0x06 + db 0x06, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06 + db 0x00, 0x06, 0x04, 0x02, 0x08, 0x04, 0x02, 0x01 + db 0x02, 0x04, 0x08, 0x00, 0x00, 0x1F, 0x00, 0x1F + db 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x08, 0x04 + db 0x02, 0x0E, 0x11, 0x10, 0x08, 0x04, 0x00, 0x04 + db 0x0E, 0x11, 0x10, 0x16, 0x15, 0x15, 0x0E, 0x04 + db 0x0A, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x0F, 0x11 + db 0x11, 0x0F, 0x11, 0x11, 0x0F, 0x0E, 0x11, 0x01 + db 0x01, 0x01, 0x11, 0x0E, 0x07, 0x09, 0x11, 0x11 + db 0x11, 0x09, 0x07, 0x1F, 0x01, 0x01, 0x0F, 0x01 + db 0x01, 0x1F, 0x1F, 0x01, 0x01, 0x0F, 0x01, 0x01 + db 0x01, 0x0E, 0x11, 0x01, 0x1D, 0x11, 0x11, 0x1E + db 0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11, 0x0E + db 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E, 0x1C, 0x08 + db 0x08, 0x08, 0x08, 0x09, 0x06, 0x11, 0x09, 0x05 + db 0x03, 0x05, 0x09, 0x11, 0x01, 0x01, 0x01, 0x01 + db 0x01, 0x01, 0x1F, 0x11, 0x1B, 0x15, 0x15, 0x11 + db 0x11, 0x11, 0x11, 0x11, 0x13, 0x15, 0x19, 0x11 + db 0x11, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E + db 0x0F, 0x11, 0x11, 0x0F, 0x01, 0x01, 0x01, 0x0E + db 0x11, 0x11, 0x11, 0x15, 0x09, 0x16, 0x0F, 0x11 + db 0x11, 0x0F, 0x05, 0x09, 0x11, 0x1E, 0x01, 0x01 + db 0x0E, 0x10, 0x10, 0x0F, 0x1F, 0x04, 0x04, 0x04 + db 0x04, 0x04, 0x04, 0x11, 0x11, 0x11, 0x11, 0x11 + db 0x11, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x0A + db 0x04, 0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A + db 0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11, 0x11 + db 0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x1F, 0x10 + db 0x08, 0x04, 0x02, 0x01, 0x1F, 0x0E, 0x02, 0x02 + db 0x02, 0x02, 0x02, 0x0E, 0x00, 0x01, 0x02, 0x04 + db 0x08, 0x10, 0x00, 0x0E, 0x08, 0x08, 0x08, 0x08 + db 0x08, 0x0E, 0x0E, 0x11, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F + db 0x1C, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x0E, 0x10, 0x1E, 0x13, 0x1E, 0x01, 0x01 + db 0x0D, 0x13, 0x11, 0x11, 0x0F, 0x00, 0x00, 0x0E + db 0x01, 0x01, 0x01, 0x0E, 0x10, 0x10, 0x16, 0x19 + db 0x11, 0x11, 0x1E, 0x00, 0x00, 0x0E, 0x11, 0x1F + db 0x01, 0x0E, 0x18, 0x04, 0x04, 0x0E, 0x04, 0x04 + db 0x04, 0x00, 0x0E, 0x11, 0x11, 0x1E, 0x10, 0x0E + db 0x01, 0x01, 0x0D, 0x13, 0x11, 0x11, 0x11, 0x04 + db 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x00 + db 0x08, 0x08, 0x08, 0x08, 0x06, 0x01, 0x01, 0x09 + db 0x05, 0x03, 0x05, 0x09, 0x04, 0x04, 0x04, 0x04 + db 0x04, 0x04, 0x08, 0x00, 0x00, 0x0F, 0x15, 0x15 + db 0x15, 0x15, 0x00, 0x00, 0x09, 0x13, 0x11, 0x11 + db 0x11, 0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E + db 0x00, 0x00, 0x0E, 0x11, 0x11, 0x0F, 0x01, 0x00 + db 0x00, 0x0E, 0x11, 0x11, 0x1E, 0x10, 0x00, 0x00 + db 0x0D, 0x13, 0x01, 0x01, 0x01, 0x00, 0x00, 0x1E + db 0x01, 0x0E, 0x10, 0x0F, 0x04, 0x04, 0x0E, 0x04 + db 0x04, 0x04, 0x18, 0x00, 0x00, 0x11, 0x11, 0x11 + db 0x19, 0x16, 0x00, 0x00, 0x11, 0x11, 0x0A, 0x0A + db 0x04, 0x00, 0x00, 0x11, 0x15, 0x15, 0x15, 0x0A + db 0x00, 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x00 + db 0x00, 0x11, 0x11, 0x1E, 0x10, 0x0C, 0x00, 0x00 + db 0x1F, 0x08, 0x04, 0x02, 0x1F, 0x10, 0x08, 0x08 + db 0x04, 0x08, 0x08, 0x10, 0x04, 0x04, 0x04, 0x04 + db 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x10, 0x08 + db 0x08, 0x04, 0x00, 0x02, 0x15, 0x08, 0x00, 0x00 + db 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15 + db 0x00, 0x00, 0x09, 0x15, 0x17, 0x15, 0x09, 0x00 + db 0x00, 0x06, 0x08, 0x0E, 0x09, 0x16, 0x06, 0x01 + db 0x01, 0x06, 0x09, 0x09, 0x06, 0x00, 0x00, 0x09 + db 0x09, 0x09, 0x1F, 0x10, 0x00, 0x00, 0x0E, 0x0A + db 0x0A, 0x1F, 0x11, 0x00, 0x00, 0x0E, 0x11, 0x1F + db 0x01, 0x1E, 0x00, 0x04, 0x0E, 0x15, 0x15, 0x0E + db 0x04, 0x00, 0x00, 0x0F, 0x09, 0x01, 0x01, 0x01 + db 0x00, 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x00 + db 0x00, 0x11, 0x19, 0x15, 0x13, 0x11, 0x0A, 0x04 + db 0x11, 0x19, 0x15, 0x13, 0x11, 0x00, 0x00, 0x11 + db 0x09, 0x07, 0x09, 0x11, 0x00, 0x00, 0x1C, 0x12 + db 0x12, 0x12, 0x11, 0x00, 0x00, 0x11, 0x1B, 0x15 + db 0x11, 0x11, 0x00, 0x00, 0x11, 0x11, 0x1F, 0x11 + db 0x11, 0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E + db 0x00, 0x00, 0x1F, 0x11, 0x11, 0x11, 0x11, 0x00 + db 0x00, 0x1E, 0x11, 0x1E, 0x14, 0x12, 0x00, 0x00 + db 0x07, 0x09, 0x07, 0x01, 0x01, 0x00, 0x00, 0x0E + db 0x01, 0x01, 0x01, 0x0E, 0x00, 0x00, 0x1F, 0x04 + db 0x04, 0x04, 0x04, 0x00, 0x00, 0x11, 0x11, 0x1E + db 0x10, 0x0E, 0x00, 0x00, 0x15, 0x15, 0x0E, 0x15 + db 0x15, 0x00, 0x00, 0x03, 0x05, 0x07, 0x09, 0x07 + db 0x00, 0x00, 0x01, 0x01, 0x07, 0x09, 0x07, 0x00 + db 0x00, 0x11, 0x11, 0x13, 0x15, 0x13, 0x00, 0x00 + db 0x0E, 0x11, 0x0C, 0x11, 0x0E, 0x00, 0x00, 0x15 + db 0x15, 0x15, 0x15, 0x1F, 0x00, 0x00, 0x07, 0x08 + db 0x0E, 0x08, 0x07, 0x00, 0x00, 0x15, 0x15, 0x15 + db 0x1F, 0x10, 0x00, 0x00, 0x09, 0x09, 0x0E, 0x08 + db 0x08, 0x00, 0x00, 0x06, 0x05, 0x0C, 0x14, 0x0C + +; 32 chars cp=1 (Russian letters) +m_font_matrix_cp2: + db 0x09, 0x15, 0x15, 0x17, 0x15, 0x15, 0x09, 0x04 + db 0x0A, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x1F, 0x11 + db 0x01, 0x0F, 0x11, 0x11, 0x1F, 0x09, 0x09, 0x09 + db 0x09, 0x09, 0x1F, 0x10, 0x0C, 0x0A, 0x0A, 0x0A + db 0x0A, 0x1F, 0x11, 0x1F, 0x01, 0x01, 0x0F, 0x01 + db 0x01, 0x1F, 0x04, 0x0E, 0x15, 0x15, 0x15, 0x0E + db 0x04, 0x1F, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01 + db 0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11, 0x11 + db 0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x04, 0x15 + db 0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x09, 0x05 + db 0x03, 0x05, 0x09, 0x11, 0x1C, 0x12, 0x12, 0x12 + db 0x12, 0x12, 0x11, 0x11, 0x1B, 0x15, 0x15, 0x11 + db 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F, 0x11, 0x11 + db 0x11, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E + db 0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E + db 0x11, 0x11, 0x11, 0x1E, 0x12, 0x11, 0x0F, 0x11 + db 0x11, 0x11, 0x0F, 0x01, 0x01, 0x0E, 0x11, 0x01 + db 0x01, 0x01, 0x11, 0x0E, 0x1F, 0x04, 0x04, 0x04 + db 0x04, 0x04, 0x04, 0x11, 0x11, 0x11, 0x11, 0x1E + db 0x10, 0x0E, 0x15, 0x15, 0x15, 0x0E, 0x15, 0x15 + db 0x15, 0x0F, 0x11, 0x11, 0x0F, 0x11, 0x11, 0x0F + db 0x01, 0x01, 0x01, 0x0F, 0x11, 0x11, 0x0F, 0x11 + db 0x11, 0x11, 0x13, 0x15, 0x15, 0x13, 0x0E, 0x11 + db 0x10, 0x0C, 0x10, 0x11, 0x0E, 0x15, 0x15, 0x15 + db 0x15, 0x15, 0x15, 0x1F, 0x0E, 0x11, 0x10, 0x1C + db 0x10, 0x11, 0x0E, 0x15, 0x15, 0x15, 0x15, 0x15 + db 0x1F, 0x10, 0x11, 0x11, 0x11, 0x1E, 0x10, 0x10 + db 0x10, 0x1F, 0x15, 0x1F, 0x15, 0x1F, 0x15, 0x1C + +m_print_write: + LD SP, m_font_cp0+28 + ds 24, 0xFF + +m_ramdisk_read: + PUSH HL + PUSH DE + LD A, D + AND 0x1 + OR 0x2 + LD B, A + XOR A + LD A, E + RRA + LD D, A + LD A, 0x0 + RRA + LD E, A +LAB_ram_f3ee: + LD A,B + OUT (SYS_DD17PB), A + LD A, (DE) + LD C, A + XOR A + OUT (SYS_DD17PB), A + LD (HL), C + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, LAB_ram_f3ee + XOR A + OUT (SYS_DD17PB), A + POP DE + POP HL + RET +m_ramdisk_write: + PUSH HL + PUSH DE + LD A, D + AND 0x1 + OR 0x2 + LD B, A + XOR A + LD A, E + RRA + LD D, A + LD A, 0x0 + RRA + LD E, A +LAB_ram_f414: + XOR A + OUT (SYS_DD17PB), A + LD C, (HL) + LD A,B + OUT (SYS_DD17PB), A + LD A, C + LD (DE), A + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, LAB_ram_f414 + XOR A + OUT (SYS_DD17PB), A + POP DE + POP HL + RET + +; ------------------------------------------------------ +; Write block to Tape +; In: DE - block ID, +; HL -> block of data. +; ------------------------------------------------------ +m_tape_write: + PUSH HL + PUSH DE + PUSH DE + LD BC,2550 + LD A,PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A,TMR0_SQWAVE ; tmr0, load lsb+msb, sq wave, bin + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A,B + OUT (TMR_DD70C1), A + ; Write Hi+Lo, Hi+Lo + LD DE, 0x4 ; repeat next 4 times +tw_wait_t0_l1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; check rst4 from timer#0 + JP NZ,tw_wait_t0_l1 + LD A, D + CPL + LD D, A + OR A + LD A,TL_HIGH ; tape level hi + JP NZ,tw_set_tape_lvl + LD A,TL_LOW ; tape level low +tw_set_tape_lvl: + OUT (DD67PC), A ; set tape level + LD A,TMR0_SQWAVE ; tmr0, load lsb+msb, swq, bin + ; timer on + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A,B + OUT (TMR_DD70C1), A + DEC E + JP NZ,tw_wait_t0_l1 +tw_wait_t0_l2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ,tw_wait_t0_l2 + ; Write 00 at start + LD A, 0x0 + CALL m_tape_wr_byte + ; Write 0xF5 marker + LD A, 0xf5 + CALL m_tape_wr_byte + LD E, 0x0 ; checksum=0 + ; Write block ID + POP BC + LD A, C + CALL m_tape_wr_byte + LD A,B + CALL m_tape_wr_byte + ; Write 128 data bytes + LD B,128 +tw_wr_next_byte: + LD A, (HL) + CALL m_tape_wr_byte + INC HL + DEC B + JP NZ,tw_wr_next_byte + ; Write checksum + LD A, E + CALL m_tape_wr_byte + ; Write final zero byte + LD A, 0x0 + CALL m_tape_wr_byte +tw_wait_t0_l3: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ,tw_wait_t0_l3 + LD A,TL_MID ; tape level middle + OUT (DD67PC), A + POP DE + POP HL + RET +; ------------------------------------------------------ +; Write byte to tape +; Inp: A - byte top write +; D - current level +; E - current checksum +; ------------------------------------------------------ +m_tape_wr_byte: + PUSH BC +; calc checksum + LD B, A + LD A, E + SUB B + LD E, A + LD C,8 ; 8 bit in byte +twb_get_bit: + LD A,B + RRA + LD B, A + JP C,twb_bit_hi +twb_wait_t0_l1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ,twb_wait_t0_l1 + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; program for 360 cycles + LD A, 0x68 + OUT (TMR_DD70C1), A + LD A, 0x1 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A,TL_HIGH + JP NZ,twb_out_bit_l1 + LD A,TL_LOW +twb_out_bit_l1: + OUT (DD67PC), A + DEC C + JP NZ,twb_get_bit + POP BC + RET +twb_bit_hi: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ,twb_bit_hi + ; program for 660 cycles + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + LD A, 0x94 + OUT (TMR_DD70C1), A + LD A, 0x2 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A,TL_HIGH + JP NZ,twb_out_bit_l2 + LD A,TL_LOW +twb_out_bit_l2: + OUT (DD67PC), A + DEC C + JP NZ,twb_get_bit + POP BC + RET + +; ------------------------------------------------------ +; Load block from Tape +; In: HL -> buffer to receive bytes from Tape +; Out: A = 0 - ok, +; 1 - CRC error, +; 2 - unexpected block Id +; 4 - key pressed +; ------------------------------------------------------ +m_tape_read: + PUSH HL + PUSH DE + LD A,PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A ; tmr0, load lsb+msb, sq wave + LD A, 0x0 + ; tmr0 load 0x0000 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + LD C,3 +tr_wait_3_changes: + CALL ccp_read_tape_bit_kbd + INC A + JP Z,tr_key_pressed_l0 + LD A,B + ADD A,4 + JP P,tr_wait_3_changes + DEC C + JP NZ,tr_wait_3_changes +tr_wait_4th_change: + CALL ccp_read_tape_bit_kbd + INC A + JP Z,tr_key_pressed_l0 + LD A,B + ADD A,4 + JP M,tr_wait_4th_change + LD C, 0x0 +tr_wait_f5_marker: + CALL ccp_read_tape_bit_kbd + INC A + JP Z,tr_key_pressed_l0 + DEC A + RRA + LD A, C + RRA + LD C, A + CP 0xf5 + JP NZ,tr_wait_f5_marker + LD E, 0x0 ; checksum = 0 + ; Read blk ID + CALL m_tape_read_byte + JP NC,tr_err_read_id + LD C, D + CALL m_tape_read_byte + JP NC,tr_err_read_id + LD B, D + PUSH BC + ; Read block, 128 bytes + LD C,128 +tr_read_next_b: + CALL m_tape_read_byte + JP NC,tr_err_read_blk + LD (HL), D + INC HL + DEC C + JP NZ,tr_read_next_b + + ; Read checksum + CALL m_tape_read_byte + JP NC,tr_err_read_blk + LD A, E + OR A + JP Z,tr_checksum_ok + LD A, 0x1 ; bad checksum +tr_checksum_ok: + POP BC +tr_return: + POP DE + POP HL + RET +tr_err_read_blk: + POP BC + LD BC, 0x0 +tr_err_read_id: + LD A, 0x2 ; read error + JP tr_return +tr_key_pressed_l0: + CALL tm_con_in + LD C, A ; store key code in C + LD B, 0x0 + LD A, 0x4 + JP tr_return +m_tape_read_byte: + PUSH BC + +; ------------------------------------------------------ +; Read byte from Tape +; Out: D - byte +; CF is set if ok, cleared if error +; ------------------------------------------------------ + LD C,8 +trb_next_bit: + CALL ccp_read_tape_bit + ; push bit from lo to hi in D + RRA + LD A, D + RRA + LD D, A + LD A,4 + ADD A,B + JP NC,trb_ret_err + DEC C + JP NZ,trb_next_bit + ; calc checksum + LD A, D + ADD A, E + LD E, A + SCF +trb_ret_err: + POP BC + RET + +; ------------------------------------------------------ +; Read bit from tape +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +ccp_read_tape_bit: + IN A, (KBD_DD78PB) ; Read Tape bit 5 (data) + AND TAPE_P + LD B, A +rtb_wait_change: + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z,rtb_wait_change + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; [360...480...660] 0x220=544d + IN A, (TMR_DD70C1) ; get tmer#0 lsb + ADD A, 0x20 + IN A, (TMR_DD70C1) ; get tmer#0 msb + LD B, A + ADC A, 0x2 + ; reset timer to 0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; For 0 - 65535-360+544 -> overflow P/V=1 + ; For 1 - 65535-660+544 -> no overflow P/V=0 + RET P + INC A + RET + +; ------------------------------------------------------ +; Read bit from tape with keyboard interruption +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +ccp_read_tape_bit_kbd: + IN A, (KBD_DD78PB) + AND TAPE_P + LD B, A ; save tape bit state + ; wait change with keyboard check +twc_wait_change: + IN A, (PIC_DD75RS) + AND KBD_IRQ + JP NZ,twc_key_pressed + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z,twc_wait_change + ; measure time + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; read lsb+msb + IN A, (TMR_DD70C1) + ADD A, 0x20 + IN A, (TMR_DD70C1) + LD B, A + ADC A, 0x2 + ; reset timer#0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; flag P/V is set for 0 + RET P + INC A + RET +twc_key_pressed: + LD A, 0xff + RET + +; ------------------------------------------------------ +; Wait tape block +; Inp: A - periods to wait +; Out: A=4 - interrupded by keyboard, C=key +; ------------------------------------------------------ +m_tape_wait: + OR A + RET Z + PUSH DE + LD B, A +m_wait_t4: + LD C,B + IN A, (KBD_DD78PB) + AND TAPE_P ; Get TAPE4 (Wait det) and save + LD E, A ; store T4 state to E +m_wait_next_2ms: + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A +; load 3072 = 2ms + XOR A + OUT (TMR_DD70C1), A + LD A, 0xc + OUT (TMR_DD70C1), A +m_wait_tmr_key: + IN A, (PIC_DD75RS) + AND KBD_IRQ ; RST1 flag (keyboard) + JP NZ,mt_key_pressed + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; RST4 flag (timer out) + JP Z,m_wait_no_rst4 + IN A, (KBD_DD78PB) + AND TAPE_P ; TAPE4 not changed? + CP E + JP NZ,m_wait_t4 ; continue wait + JP m_wait_tmr_key +m_wait_no_rst4: + DEC C + JP NZ,m_wait_next_2ms + XOR A + POP DE + RET +mt_key_pressed: + CALL tm_con_in + LD C, A ; C = key pressed + LD A, 0x4 ; a=4 interrupted by key + POP DE + RET + +; ------------------------------------------------------ +; Check block marker from Tape +; Out: A=0 - not detected, 0xff - detected +; ------------------------------------------------------ +m_tape_blk_detect: + IN A, (KBD_DD78PB) + AND TAPE_D ; TAPE5 - Pause detector + LD A, 0x0 + RET Z + CPL + RET + +esc_64x23_cursor: + db ASCII_ESC, '6', '1', ASCII_ESC, '8', '0', 0 + +msg_turbo_mon: + db "\f\r\n Turbo MONITOR Ver 1.1 for 13.01.92 (C)Alex Z.\r\n",0 + +m_rst1_handler: + DI + LD (TM_VARS.rst_hl_save), HL + LD HL, 0x2 + ADD HL,SP + LD (TM_VARS.rst_sp_save), HL + POP HL + LD SP, TM_VARS.rst_hl_save + PUSH DE + PUSH BC + PUSH AF + LD (TM_VARS.rst_ret_addr), HL + LD (TM_VARS.tm_hrg), HL + JP tm_main +tm_rst_ret: + LD A, 0xc3 + LD (TM_VARS.rst_ret_JP), A + LD SP, TM_VARS.rst_af_save + POP AF + POP BC + POP DE + POP HL + LD SP, HL + LD HL, (TM_VARS.rst_hl_save) + JP TM_VARS.rst_ret_JP + +m_cold_start: + LD HL, tpa_start + LD (TM_VARS.tm_hrg), HL +tm_main: + LD SP, TM_VARS.tm_stsp + LD DE,esc_64x23_cursor ; FORM61 + CALL tm_print + LD DE, msg_turbo_mon ;= '\f' + CALL tm_out_strz + LD DE, 0x363d ; 54 x '=' + CALL tm_rpt_print + CALL tm_beeep + +tm_mon: + LD DE, 0x300 + CALL tm_screen + CALL tm_lps + LD DE, 0x520 + CALL tm_rpt_print + LD HL, msg_digits ;= "0123456789ABCDEF" + + ; Out '012345789ABCDEF' string +tm_tit: + CALL tm_print_SP + LD C, (HL) + CALL tm_sym_out + INC HL + LD A, (HL) + OR A + JP NZ, tm_tit + LD HL, (TM_VARS.tm_hrg) + LD L, 0x0 +tm_adr: + CALL tm_print_LFCR_hexw + +tm_prh: + LD A, (HL) + CALL tm_hex_b + INC L + LD A,L + AND 0xf + CP 0x0 + JP NZ, tm_prh + LD A, L + SUB 0x10 + LD L, A + CALL tm_print_SP + CALL tm_buk + LD A, L + CP 0x0 + JP NZ, tm_adr + CALL tm_print_LFCR + CALL tm_lpr + LD DE, 0x363d ; 54 x '=' + CALL tm_rpt_print + CALL tm_print_LFCR + LD DE, tm_msg_regs ;= "A,FB,CD,EH,L SP" + LD HL, TM_VARS.rst_af_save + +LAB_ram_f717: + LD B, 0x3 + +LAB_ram_f719: + LD A, (DE) ;= "A,FB,CD,EH,L SP" + LD C, A + CP 0xff + JP Z,tm_out_contacts + CP 0x0 + JP Z, LAB_ram_f744 + CALL tm_sym_out + INC DE + DEC B + JP NZ, LAB_ram_f719 + LD C,'=' + CALL tm_sym_out + INC HL + LD A, (HL) + CALL tm_hex_b + DEC HL + LD A, (HL) + CALL tm_hex_b + INC HL + INC HL + CALL tm_print_SP + JP LAB_ram_f717 +LAB_ram_f744: + INC HL + INC DE + JP LAB_ram_f719 + +tm_msg_regs: + db "A,FB,CD,EH,L SP",0 + db " PC", 0xFF + +tm_out_contacts: + LD DE, m_msg_contacts ;= " (Chernogolovka Mosk.reg. ... + CALL tm_out_strz + LD HL, (TM_VARS.tm_hrg) +tm_kur: + LD DE, 0x405 + LD A, L + AND 0xf0 + RRCA + RRCA + RRCA + RRCA + ADD A, D + LD D, A + LD A, L + AND 0xf + ADD A, A + ADD A, E + LD E, A + CALL tm_screen + LD A, (TM_VARS.tm_tbu) + CP 0x9 + JP Z, tm_tabu + LD B, 0x1 +tm_m2: + CALL tm_con_in + LD C, A + CP 0x3 + JP Z, m_print_log_sep + CALL tm_poke + LD A, B + OR A + JP NZ, tm_m6 + CALL tm_re1 + JP tm_m2 +tm_m6: + LD A, C + CP 0x9 + JP NZ, tm_m7 + LD (TM_VARS.tm_tbu), A + JP tm_kur +tm_tabu: + CALL tm_print_CR + LD E, '&' + LD A, L + AND 0xf + ADD A, E + LD E, A + CALL tm_scr1 +tm_tab: + CALL tm_get_key_ctrl_c + CP ASCII_TAB + JP NZ, tm_m7 + XOR A + LD (TM_VARS.tm_tbu), A + JP tm_kur +tm_m7: + CP 0x8b + JP Z, tm_search + CP 0x9e + JP Z, tm_f1 + CP 0x81 + JP Z, tm_f2 + CP 0x86 + JP Z, tm_f3 + CP 0x92 + JP Z, tm_f4 + CP 0x83 + JP Z, tm_f5 + LD A, (TM_VARS.tm_tbu) + CP 0x9 + LD A, C + JP NZ, tm_m8_monitor + CALL tm_m9 + JP tm_tab +tm_m9: + CP 0x93 + JP Z, tm_leta + CP 0x8 + JP Z, tm_leta + CP 0x84 + JP Z, tm_rgta + CP 0x85 + JP Z, tm_up + CP 0x98 + JP Z, tm_down + AND 0x7f + CP 0x20 + LD (HL), C + JP NC, tm_met1 + LD C, 0x20 + CP ASCII_CR + JP NZ, tm_met1 + CALL tm_sym_out + CALL tm_rigth1 + LD (HL), ASCII_LF + LD C, 0x20 +tm_met1: + CALL tm_sym_out + JP tm_rigth1 +tm_leta: + LD A, L + OR A + RET Z + AND 0xf + JP NZ, tm_left1 + LD C, 0x19 ; EM (End of medium) + CALL tm_sym_out + LD DE, 0x1018 ; 16 x CAN (Cancel) + CALL tm_rpt_print +tm_left1: + DEC L + LD C, 0x8 ; BS (Backspace) + JP tm_sym_out +tm_rgta: + LD C, 0x18 ; CAN + CALL tm_sym_out +tm_rigth1: + INC L + JP Z, tm_left1 +tm_rigth2: + LD A, L + AND 0xf + RET NZ + LD DE, 0x1008 ; 16 x BS + CALL tm_rpt_print + LD C, ASCII_LF + JP tm_sym_out +tm_m8_monitor: + CP ASCII_ESC + JP NZ, tm_m3 + CALL tm_niz + JP tm_rst_ret +tm_m3: + CP ASCII_CR + JP Z, tm_main + CP 0x99 + JP Z, tm_fill + CP 0x7f + JP Z, tm_fill_nxt2 + CP '+' + JP Z, tm_plus + CP '-' + JP Z, tm_minus + CP 'G' + JP NZ, tm_rstr + LD DE, tm_main + PUSH DE + JP (HL) +tm_rstr: + CP 'J' + JP Z, tm_jump + CP 'H' + JP Z, tm_add + CP 'M' + JP Z, tm_move + CP 'R' + JP Z, tm_dsk_read + CP 'W' + JP Z, tm_dsk_write + CP 'T' + JP Z, tm_print_t + CP 'P' + LD (TM_VARS.tm_hrg), HL + JP Z, LAST ; ??? + CP 'p' + JP Z, tm_lprn + CP 0x10 + ; if key 0x10 - turn on duplicate output to printer + JP Z, tm_ltab + CALL tm_met2 + LD (TM_VARS.tm_hrg), HL + JP tm_m2 +tm_met2: + CP 0x93 + JP Z, tm_left + CP 0x8 + JP Z, tm_left + CP 0x84 + JP Z, tm_rght + CP 0x85 + JP Z, tm_up + CP 0x98 + JP Z, tm_down + RET +tm_left: + LD A,B + CP 0x2 + JP Z, tm_le2 + LD A,L + AND 0xf + RET Z +tm_le3: + DEC L + LD B, 0x2 +tm_le1: + LD C, ASCII_BS + JP tm_sym_out +tm_le2: + LD B, 0x1 + JP tm_le1 +tm_rght: + LD C, 0x18 + CALL tm_sym_out + LD A, B + CP 0x1 + LD B, 0x2 + RET Z +tm_re1: + INC L + JP Z, tm_le3 + LD A, L + AND 0xf + LD B, 0x1 + JP Z, tm_print_LFCR_hexw + RET +tm_up: + LD A, L + AND 0xf0 + RET Z + LD A, L + SUB 0x10 + LD L, A + LD C, 0x19 + JP tm_sym_out +tm_down: + LD A, L + ADD A, 0x10 + AND 0xf0 + RET Z + LD A, 0x10 + ADD A, L + LD L, A + LD C, 0xa + JP tm_sym_out + +; ------------------------------------------------------ +; Get nibbles from A and (HL), conver to byte +; put back to (HL), print nibble +; ------------------------------------------------------ +tm_poke: + CP 'G' + RET P + CP '0' + RET M + SUB '0' + CP 10 + JP M, p_is_alpha + SUB 0x7 +p_is_alpha: + PUSH AF + CALL tm_print_hex_nibble + LD A, B + CP 0x2 + JP Z, tm_m5 + LD A, (HL) + AND 0xf + LD B, A + POP AF + RLCA + RLCA + RLCA + RLCA + ADD A, B + LD (HL), A + LD B, 0x2 + RET + +tm_m5: + LD A, (HL) + AND 0xf0 + LD B, A + POP AF + ADD A, B + LD (HL), A + XOR A + LD B, A + RET + +tm_pokh: + CALL tm_pok + DEC HL +tm_pok: + LD B, 0x1 +tm_pk1: + CALL tm_get_key_ctrl_c + CALL tm_poke + LD A, B + OR A + JP NZ, tm_pk1 + RET +tm_buk: + LD A, (HL) + LD C, A + AND 0x7f + CP 0x20 + JP P, tm_m24 + LD C, 0x20 +tm_m24: + CALL tm_sym_out + INC L + LD A, L + AND 0xf + CP 0x0 + JP NZ, tm_buk + RET +m_msg_jump: + db "JAMP\r\n", 0 + +tm_jump: + LD DE, 0x300 + CALL tm_screen + LD DE, m_msg_jump ;= "JAMP\r\n" + CALL tm_print + LD HL, TM_VARS.tm_hrg+1 + CALL tm_pokh + JP tm_mon +m_msg_add: + db "\fADD [", 0 + +tm_add: + LD DE,m_msg_add ;= "\fADD [" + CALL tm_print + LD HL, TM_VARS.tm_strt+1 + CALL tm_pokh + LD C,'+' + CALL tm_sym_out + LD HL, TM_VARS.tm_strt+3 + CALL tm_pokh + LD C,'=' + CALL tm_sym_out + LD HL, (TM_VARS.tm_strt) + EX DE, HL + LD HL, (TM_VARS.tm_strt+2) + ADD HL, DE + CALL tm_hex_hl + CALL tm_con_in + JP tm_main + +m_msg_move: + db "\fMOVE }", 0 + +tm_move: + LD DE,m_msg_move ;= "\fMOVE }" + CALL tm_print + LD HL, TM_VARS.tm_strt+1 + CALL tm_pokh + LD C,'-' + CALL tm_sym_out + LD HL, TM_VARS.tm_strt+3 + CALL tm_pokh + LD C,'=' + CALL tm_sym_out + LD HL, TM_VARS.tm_strt+5 + CALL tm_pokh + LD HL, (TM_VARS.tm_strt) + LD B,H + LD C,L + LD HL, (TM_VARS.tm_strt+2) + LD D,H + LD E,L + INC DE + LD HL, (TM_VARS.tm_strt+4) + +tm_mov_nxt: + LD A, (BC) + LD (HL), A + INC BC + INC HL + LD A, C + CP E + JP NZ, tm_mov_nxt + LD A, B + CP D + JP NZ, tm_mov_nxt + JP tm_main +tm_dsk_read: + LD DE, m_msg_read ;= "Read/" + JP tm_ra4 +tm_dsk_write: + LD DE, m_msg_write ;= "Write/" +tm_ra4: + LD (TM_VARS.tm_rw_disk), A + CALL tm_print_verh + CALL tm_print +tm_ra5: + CALL tm_get_key_ctrl_c + CP 'A' + JP C, tm_ra5 + CP 'F' + JP NC, tm_ra5 + LD (TM_VARS.tm_drive), A + CALL tm_sym_out + LD C, ':' + CALL tm_sym_out + LD A, (TM_VARS.tm_drive) + SUB 65 + LD (TM_VARS.tm_drive), A + JP NZ, tm_disk + LD DE, tm_msg_0123 ;= "0123 ?" + CALL tm_print +tm_rrr: + CALL tm_get_key_ctrl_c + SUB 48 + CP 4 + JP NC, tm_rrr + LD (TM_VARS.tm_strt+15), A + JP tm_main +tm_beeep: + LD BC, 0x6060 +tm_bell_next_p: + LD A,BELL_PIN + OUT (DD67PC), A ; BELL=1 + LD A, C +tm_bell_w1: + DEC A + JP NZ, tm_bell_w1 + OUT (DD67PC), A ; BELL 0 + LD A, C +tm_bell_w2: + DEC A + JP NZ, tm_bell_w2 + DEC B + JP NZ, tm_bell_next_p + RET +tm_msg_0123: + db "0123 ?", 0 + +tm_disk: + LD DE, m_msg_track ;= "/Track-" + CALL tm_print + CALL tm_des_dv + LD (TM_VARS.tm_track_no), A + LD DE, tm_sector ;= "/Sekt-" + CALL tm_print + CALL tm_des_dv + LD (TM_VARS.tm_sector_no), A + LD DE, m_msg_num ;= "/N-" + CALL tm_print + CALL tm_des_dv + LD (TM_VARS.tm_num_sect), A + LD DE, m_msg_io ;= "/(I/O)-" + CALL tm_print +tm_disk_op_unk: + CALL tm_get_key_ctrl_c + CP 'I' + JP Z, tm_disk_inp + CP 'O' + JP NZ, tm_disk_op_unk +tm_disk_inp: + LD (TM_VARS.tm_disk_op), A + CALL tm_sym_out + CALL tm_rd_wr_disk + JP tm_cont +tm_rd_wr_disk: + CALL tm_niz +tm_rd: + CALL tm_print_CR + LD A, (TM_VARS.tm_track_no) + LD D, A + LD A, (TM_VARS.tm_sector_no) + LD E, A + LD A, (TM_VARS.tm_disk_op) + CP 'O' + CALL Z, tm_sect_map + LD A, H + CP 0xac + JP NC, tm_error + LD BC, 0x84 + LD A, (TM_VARS.tm_rw_disk) + CP 'W' + LD A, (TM_VARS.tm_drive) + JP NZ, tm_d3 + LD C, 0xa4 + CALL m_write_floppy + DEC L + JP tm_d4 +tm_d3: + CALL m_read_floppy +tm_d4: + CP 0x0 + JP NZ, tm_error + LD A, (TM_VARS.tm_track_no) + LD D, A + LD A, (TM_VARS.tm_sector_no) + INC A + CP 10 + JP C, tm_wf_nx_trk + INC D + LD A, D + LD (TM_VARS.tm_track_no), A + LD A, 0x1 +tm_wf_nx_trk: + LD (TM_VARS.tm_sector_no), A + LD E, A + CALL tm_print_CR_hexw + CALL tm_dv_des + LD A, (TM_VARS.tm_num_sect) + DEC A + LD (TM_VARS.tm_num_sect), A + JP NZ, tm_rd + RET +tm_error: + PUSH AF + LD DE, m_msg_disk_error ;= "Disk Error - " + CALL tm_print + POP AF + CALL tm_hex_b + LD BC, 0x70ff + CALL tm_bell_next_p + LD DE, m_msg_track ;= "/Track-" + CALL tm_print + LD A, (TM_VARS.tm_track_no) + CALL tm_dva + LD DE, tm_sector ;= "/Sekt-" + CALL tm_print + LD A, (TM_VARS.tm_sector_no) + CALL tm_dva + LD DE, m_msg_retr_abrt ;= "Retry, Abort -?" + CALL tm_print_w_key + CP 'A' + JP Z, tm_main + JP tm_rd_wr_disk +tm_dv_des: + CALL tm_dv1 + LD A, E + +; ------------------------------------------------------ +; Convert to BCD and print? +; ------------------------------------------------------ +tm_dva: + LD D, A +tm_dv1: + LD C, 0 + LD B, 8 +tm_dv2: + LD A, D + RLCA + LD D, A + LD A, C + ADC A, C + DAA + LD C, A + DEC B + JP NZ, tm_dv2 + +OFF_ram_fb76: + JP tm_hex_b + +tm_sec_map_t: + db 1, 8, 6, 4, 2, 9, 7, 5, 3 + +; ------------------------------------------------------ +; Map sectors +; Inp: E - sector +; Out: E - sector +; ------------------------------------------------------ +tm_sect_map: + LD BC, tm_sec_map_t-1 + LD A, E + ADD A, C + LD C, A + JP NC, tm_tbl_noc + INC B +tm_tbl_noc: + LD A, (BC) + LD E, A + RET +tm_des_dv: + CALL SUB_ram_fb9f + RLCA + RLCA + RLCA + LD B, A + RRCA + RRCA + ADD A,B + LD B, A + CALL SUB_ram_fb9f + ADD A,B + RET +SUB_ram_fb9f: + CALL tm_get_key_ctrl_c + SUB 0x30 + JP M,SUB_ram_fb9f + CP 0xa + JP P,SUB_ram_fb9f + PUSH AF + CALL tm_sym_out + POP AF + RET + +; ------------------------------------------------------ +; Read data from floppy +; Inp: A = 0..4 - select drives +; HL -> buffer +; C - command (0x84) READ_SEC, single, 15ms delay +; Out: A=0 if ok +; CF is set if error +; ------------------------------------------------------ +m_read_floppy: + CALL m_select_drive + CALL m_start_floppy + RET C + CALL m_fdc_seek_trk + RET C + CALL m_fdc_read_c_bytes + RET C + XOR A + RET + +; ------------------------------------------------------ +; Write data to floppy +; Inp: A = 0..4 - select drives +; HL -> buffer +; C - command (0xA4) WRITE_SEC, single, 15ms delay +; Out: A=0 if ok +; CF is set if error +; ------------------------------------------------------ +m_write_floppy: + CALL m_select_drive + CALL m_start_floppy + RET C + CALL m_fdc_seek_trk + RET C + CALL m_fdc_write_bytes + RET C + XOR A + RET + +; ------------------------------------------------------ +; Select or disable drive +; Inp: A = 0 - disable drives +; A = 1 - select + drsel + motor0 en +; A = 2 - select + motor0 enanle +; A = 3 - select +; A = 4 - select + drsel +; ------------------------------------------------------ +m_select_drive: + PUSH BC + LD B, 00000111b ; DRSEL, DSEL1, DSEL0 + CP 0x1 + JP Z, mal_out_to_flop + LD B, 00100111b ; MOT0_EN, DRSEL, DSEL1, DSEL0 + CP 0x2 + JP Z, mal_out_to_flop + LD B, 00000011b ; DSEL1, DSEL0 + CP 0x3 + JP Z, mal_out_to_flop + LD B, 00100011b ; MOT0_EN, DSEL1, DSEL0 + CP 0x4 + JP Z, mal_out_to_flop + LD B, 00000000b ; Turn off and deselect drives +mal_out_to_flop: + LD A, B + OUT (FLOPPY), A + POP BC + RET + +; ------------------------------------------------------ +; Start floppy +; Out: +; CF is set, A=0x20 if INTRQ +; CF not set, A=0x00 if READY +; ------------------------------------------------------ +m_start_floppy: + IN A, (FLOPPY) ; Get floppy ctrl state + RLCA + JP C, .start_motor + IN A, (FDC_CMD) + AND FDC_NOT_READY + RET Z + +.start_motor: + PUSH BC + LD BC, 64000 ; timeout + CALL m_fdc_set_init + +.wait_mot_start: + IN A, (FDC_CMD) + AND FDC_NOT_READY + JP Z, .delay_for_spin_up + IN A, (FLOPPY) + ; Check motor start bit + RLCA + JP NC, .wait_mot_start + ; Time is out or not started + LD A, 0x20 + JP .ok_exit + ; Delay for spindle spin-up +.delay_for_spin_up: + DEC C + JP NZ, .delay_for_spin_up + DEC B + JP NZ, .delay_for_spin_up + XOR A +.ok_exit: + POP BC + RET + +; ------------------------------------------------------ +; Send INIT to FDC +; ------------------------------------------------------ +m_fdc_set_init: + IN A, (FLOPPY) + AND 01001110b ; Read SSEL, DDEN, MOT1, MOT0 + RRA + OUT (FLOPPY), A ; SSEL DRSEL MOT1 MOT0 + OR 00001000b ; INIT + OUT (FLOPPY), A + RET + +; ------------------------------------------------------ +; Seek track on floppy +; Inp: DE - track/sector +; ------------------------------------------------------ +m_fdc_seek_trk: + PUSH BC + LD B, 0x2 ; try 2 times if error +fs_seek_again: + LD A, D + OUT (FDC_DATA), A ; Set track to search + LD A,FDC_SEEK_LV ; Seek, Load Head, Verify on dst track + OUT (FDC_CMD), A + NOP + NOP + IN A, (FDC_WAIT) + IN A, (FDC_CMD) + AND 00011001b ; SEEK error, CRC error, BUSY flags + CP 0x0 + JP Z, fs_seek_ok + LD A, 0x20 + SCF ; set erro flag + DEC B + JP Z, fs_seek_ok + LD A, FDC_RESTORE_L ; restore with load head + OUT (FDC_CMD), A + NOP + NOP + IN A, (FDC_WAIT) + JP fs_seek_again +fs_seek_ok: + PUSH AF + LD A, E + OUT (FDC_SECT), A + POP AF + POP BC + RET + +; ------------------------------------------------------ +; Write bytes to FDC +; Inp: C - count of bytes +; HL -> buffer +; Out: CF set if error, +; C - error code if CF is set +; ------------------------------------------------------ +m_fdc_write_bytes: + LD A, C + OUT (FDC_CMD), A +fw_next_byte: + IN A, (FDC_WAIT) + RRCA + LD A, (HL) + OUT (FDC_DATA), A + INC HL + JP C,fw_next_byte + JP m_floppy_chk_error + +; ------------------------------------------------------ +; Read bytes from FDC +; Inp: C - command code for VG93 +; HL -> buffer +; Out: CF set if error, +; C - error code if CF is set +; ------------------------------------------------------ +m_fdc_read_c_bytes: + LD A, C + OUT (FDC_CMD), A + JP .skip_first_inc +.read_next_byte: + LD (HL), A + INC HL +.skip_first_inc: + IN A, (FDC_WAIT) + RRCA + IN A, (FDC_DATA) + JP C, .read_next_byte + +m_floppy_chk_error: + IN A, (FDC_CMD) + AND 11011111b ; mask 5th bit Write fault... ??? + CP 0x0 + RET Z ; retutn in no errors + IN A, (FDC_CMD) + LD C, A + SCF + RET + +m_msg_disk_error: + db "Disk Error - ", 0 + +m_msg_retr_abrt: + db "Retry, Abort -?", 0 + +m_msg_read: + db "Read/", 0 + +m_msg_write: + db "Write/", 0 + +m_msg_track: + db "/Track-", 0 + +tm_sector: + db "/Sekt-", 0 + +m_msg_num: + db "/N-", 0 + +m_msg_b_ok: + db " {OK}", 0 + +m_msg_io: + db "/(I/O)-", 0 + +esc_cmd_kod: + db ASCII_ESC, '@', ASCII_ESC, 'R', 3, ESC_CMD_END + +tm_fill: + LD C, (HL) + LD A, 0xb0 +.fill_nxt1: + LD (HL), C + INC HL + CP H + JP NZ, .fill_nxt1 + JP tm_mon +tm_fill_nxt2: + LD C, (HL) +.fill_nxt3: + LD (HL), C + INC L + JP NZ, .fill_nxt3 + JP tm_mon + EX DE, HL + CALL tm_hex_hl + EX DE, HL + RET + +; ------------------------------------------------------ +; Print word as hex +; Inp: HL - value to print +; ------------------------------------------------------ +tm_hex_hl: + LD A, H + CALL tm_hex_b + LD A, L + +; ------------------------------------------------------ +; Print byte in HEX +; Inp: A - Byte to print +; ------------------------------------------------------ +tm_hex_b: + PUSH AF + AND 0xf0 + RRCA + RRCA + RRCA + RRCA + CALL tm_print_hex_nibble + POP AF + PUSH AF + AND 0xf + CALL tm_print_hex_nibble + POP AF + RET + +tm_print_hex_nibble: + PUSH DE + LD DE,msg_digits ;= "0123456789ABCDEF" + ADD A, E + JP NC, .no_cf + INC D +.no_cf: + LD E, A + LD A, (DE) + LD C, A + CALL tm_sym_out + POP DE + RET + +msg_digits: + db "0123456789ABCDEF", 0 + +tm_print_LFCR_hexw: + LD C, ASCII_LF + CALL tm_sym_out + +tm_print_CR_hexw: + CALL tm_print_CR + CALL tm_hex_hl + +tm_print_SP: + LD C, ' ' + JP tm_sym_out + +tm_print_LFCR: + LD C, ASCII_LF + CALL tm_sym_out + +tm_print_CR: + LD C, ASCII_CR + JP tm_sym_out + +; ------------------------------------------------------ +; Get key from keyboard. Return to monitor +; if Ctrl+C pressed +; Out: C=A - key +; ------------------------------------------------------ +tm_get_key_ctrl_c: + CALL tm_con_in + LD C, A + CP 0x3 + JP Z, tm_main + RET + +; TYPE +tm_print_t: + LD C, ASCII_US + CALL tm_sym_out + EX DE, HL + CALL tm_print +tm_cont: + LD DE, m_msg_b_ok ;= " {OK}" + CALL tm_print_w_key + JP tm_main + +; ------------------------------------------------------ +; Print message and wait key +; Ctrl+C to cansel current op and +; return to monitor +; Inp: DE -> strz +; Out: A - key +; ------------------------------------------------------ +tm_print_w_key: + CALL tm_out_strz + JP tm_get_key_ctrl_c + +tm_out_strz: + CALL tm_print_LFCR +tm_print: + LD B,16 +p11_print_next: + LD A, (DE) + OR A + RET Z + CP ASCII_CR + JP NZ, p11_no_CR + DEC B + JP NZ, p11_no_CR + CALL tm_get_key_ctrl_c + LD B, 16 + LD A, (DE) +p11_no_CR: + LD C, A + CALL tm_sym_out + INC DE + JP p11_print_next + +; ------------------------------------------------------ +; Out symbol +; Inp: C - symbol +; ------------------------------------------------------ +tm_sym_out: + LD A, C + AND 0x80 + JP Z, tm_rus + LD A, 0x1 + +tm_rus: + LD (TM_VARS.m_codepage), A + CALL m_con_out + LD A, (TM_VARS.tm_ltb+1) ; if 0x10, return, no out to printer; + CP 0x10 + RET NZ + JP tm_char_print + +; ------------------------------------------------------ +; Print FS (FileSeparator) +; ------------------------------------------------------ +tm_print_verh: + LD C, 0xc + JP tm_sym_out +tm_screen: + CALL tm_print_verh + LD A, D + OR A +tm_scr0: + JP Z,tm_scr1 + LD C, ASCII_LF + CALL tm_sym_out + DEC D + JP tm_scr0 + +; ------------------------------------------------------ +; Print CAN symbol E times +; ------------------------------------------------------ +tm_scr1: + LD A, E + OR A + RET Z + LD C, ASCII_CAN ; CAN (Cancel) + CALL tm_sym_out + DEC E + JP tm_scr1 + +tm_niz: + LD DE, 0x1500 ; 21 x 0 + CALL tm_screen + LD DE, 0x7f20 ; 126 x ' ' + CALL tm_rpt_print + LD DE, 0x1500 ; 21 x 0 + JP tm_screen + +; ------------------------------------------------------ +; Print the character the specified number of times +; Inp: E - character +; D - count +; ------------------------------------------------------ +tm_rpt_print: + LD C, E ; DE=363D +rpt_next: + CALL tm_sym_out + DEC D + RET Z + JP rpt_next + +; Turn on duplicate output to printer +tm_ltab: + LD A,10h + LD (TM_VARS.tm_ltb), A + JP tm_mon + +tm_lps: + LD A, (TM_VARS.tm_ltb) + CP 0x10 + RET NZ + LD (TM_VARS.tm_ltb+1), A + LD DE,esc_cmd_kod ;= 1Bh + JP tm_lprint + +tm_lpr: + XOR A + LD (TM_VARS.tm_ltb), A + LD (TM_VARS.tm_ltb+1), A + RET + +tm_lprn: + EX DE, HL + CALL tm_lprint + JP tm_mon + +; ------------------------------------------------------ +; Print string until SUB symbol +; Inp: DE -> string ended with SUB (0x1A) +; ------------------------------------------------------ +tm_lprint: + LD A, (DE) + LD C, A + CP ASCII_SUB + RET Z + CALL tm_char_print + INC DE + JP tm_lprint + +; ------------------------------------------------------ +; Send character to printer +; Inp: C - character +; ------------------------------------------------------ +tm_char_print: + IN A, (PIC_DD75RS) + AND PRINTER_IRQ + JP Z, tm_char_print ; Wait printer ready +cp_wait_ack: + IN A, (KBD_DD78PB) + AND PRINTER_ACK + JP NZ, cp_wait_ack + LD A, C + CPL + OUT (LPT_DD67PA), A + LD A, 00001001b ; set Printer Strobe (port C 4th bit) + OUT (DD67CTR), A + LD A, 00001000b ; reset Printer Strobe + OUT (DD67CTR), A + RET + +; ------------------------------------------------------ +; Wait and read data from UART +; Out: A - 7 bit data +; ------------------------------------------------------ +m_serial_in: + IN A, (UART_DD72RR) + AND RX_READY + JP Z, m_serial_in ; wait for rx data ready + IN A, (UART_DD72RD) + AND 0x7f ; leave 7 bits + RET + +; ------------------------------------------------------ +; Send data by UART +; Inp: C - data to transmitt +; ------------------------------------------------------ +m_serial_out: + IN A, (UART_DD72RR) + AND TX_READY + JP Z, m_serial_out ; Wait for TX ready + LD A, C + OUT (UART_DD72RD), A + RET +tm_plus: + LD HL, (TM_VARS.tm_hrg) + INC H + JP tm_ff +tm_minus: + LD HL, (TM_VARS.tm_hrg) + DEC H + JP tm_ff +tm_f1: + LD H, 0x1 + JP tm_ff +tm_f2: + LD H, 0x40 + JP tm_ff +tm_f3: + LD H, 0xb0 + JP tm_ff +tm_ff: + XOR A + LD L, A + LD (TM_VARS.tm_hrg), HL + JP tm_mon +tm_f4: + LD A,H + ADD A, 0x10 + JP tm_fa +tm_f5: + LD A,H + SUB 0x10 +tm_fa: + LD H, A + JP tm_ff + +tm_msg_search: + db "\fSEARCH [", 0 + +tm_search: + LD (TM_VARS.tm_hrg), HL +tm_search0: + LD DE, tm_msg_search ;= "\fSEARCH [" + CALL tm_print + LD HL, TM_VARS.tm_stack +tm_s0: + LD B, 0x1 +tm_s1: + CALL tm_get_key_ctrl_c + CP ASCII_BS + JP Z, tm_search0 + CP ASCII_CR + JP Z, tm_do_search + CP '*' + JP Z, tm_s4 + LD A, (TM_VARS.tm_tbu) + CP ASCII_TAB + JP NZ, tm_s2 +tm_s4: + LD (HL), C + CALL tm_sym_out + JP tm_s5 +tm_s2: + LD A, C + CALL tm_poke + LD A, B + OR A + JP NZ, tm_s1 +tm_s5: + INC L + JP tm_s0 + +tm_do_search: + LD B,L + LD D,H + XOR A + LD E, A + LD HL, (TM_VARS.tm_hrg) + INC HL + ADD A,B + JP Z, tm_sea1 + LD (TM_VARS.tm_stack+16), A +tm_sea1: + LD A, (DE) + CP (HL) + JP NZ, tm_s3 +tm_s6: + DEC B + JP Z, tm_aga + INC E +tm_sea2: + INC L + JP NZ, tm_sea1 + INC H + JP NZ, tm_sea1 + JP tm_main +tm_s3: + LD A, (DE) + CP '*' + JP Z, tm_s6 + LD A, (TM_VARS.tm_stack+16) + LD B, A + LD E, 0x0 + JP tm_sea2 + +tm_aga: + LD A, (TM_VARS.tm_stack+16) + +tm_ogo: + DEC A + JP Z, tm_rets + DEC HL + JP tm_ogo + +tm_rets: + LD (TM_VARS.tm_hrg), HL + JP tm_main + +m_print_log_sep: + LD C, ASCII_US + CALL tm_sym_out + JP warm_boot + +m_msg_contacts: + db " (Chernogolovka Mosk.reg. Tel 51-24)", 0 + +LAST EQU $ +CODE_SIZE EQU LAST-0xE000 +FILL_SIZE EQU 8192-CODE_SIZE + + DISPLAY "Code size is: ",/A,CODE_SIZE + +FILLER + DS FILL_SIZE, 0xFF + DISPLAY "Filler size is: ",/A,FILL_SIZE + + ENDMODULE + + OUTEND + + INCLUDE "tm_vars.inc" diff --git a/MON_v8_3edeb015/.gitignore b/MON_v8_3edeb015/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/MON_v8_3edeb015/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/MON_v8_3edeb015/BIN/MON_V8_3edeb015.BIN b/MON_v8_3edeb015/BIN/MON_V8_3edeb015.BIN new file mode 100644 index 0000000000000000000000000000000000000000..4a573b17e9794dfa10275bbac2c63f5d8fd730c2 GIT binary patch literal 8192 zcmdTp3s)Q0nIjannIFk;+UwjX?``MIIKQ0U{ofKqL_`+V8u< zkEY!pup`aA-~I0QzTbV!40q2}ZcCEO4{=*V+=C&mEW|a1xPu|CV~zXaD%ZTqJ+{i7 zOme%D+_81;`|I2enQNB0*i|k!#yMl$&!b%PEcZ}^E1KgrEpm4*FVX!0E-*=dFVIZ) z1wynU;MGtTYEQ3!@8i}#vR}PQQvolX8DJacn1P0?OhcGym}2T1CK)=TVVa?vfkzdX zXqaa#l&W%uvDg)rA29S^0zajz;XV;~kS+*(aUk$TIzP}q5I9vII7L?k`spmV`vdzZ zY9O%hpWbA|7bJbA_+ZFJjmexqjak;NieR)Mbg0WS1b8dBkdt-a=kGgw;I4{8Er%;^ zYdLage@+&4V!WS{lr%L|&yW99QVM0E@v~ANFXo3l!q(8ZUFu7q9?%j)0)@~N>I$6Y z$NMF%a4^(G1+Id96WC;1i6?w`)F(a`@(zc1Lx3r1@+3bu$u`d#OFZ$@p)M&m-#C!Z zTmFZ+&FXG+wiQyvU$?v!I#&Frj{xVEa@%S>yOcwP)Dxwa@lbK`aqH2DtcrU>wyTVk z!TLi6e`s`epdrBU8ERFALa9)|tyX7hR1ZZqE#4cgpFJ}(J9EpVJfWX3Ox(F#H1}HU z=h2TthB2n3!&uS@AvUIg4;Y?1GehJK8d5t%+r;L=DDivygt#>%mkuF{t(Yjh3IK2U zuL<$QghfSFE-)HOPl>;uc=65C6WbOTD`51Bziq2r@?7xXgyk2=#QWTGiSW!si+B(Q z!a3pP6P8!71DNjw++A>6ev610lJKSXD@#g`Q4|yabq)bPT*=&}~ zr~UShE=lop2j<)Hs;Z-yW$NVYM7O!M$K1xfGtpB>nMdLig*(h6$%*cj!Pai?FmD@U zq&<*z4&pUUvBpW-{%RmZIx-q0hVN_}Xzi#et|{)3?aBRK+3Ed5^Gp%2~_PpK{i^$FfFhrsyd4`L!F&+{csKLJW+d(nss4Wdz79GQOyE&`?#S0bJaBaSv)c4pvMkuQ*bkRDs|wU? z|5ROB4%;)O&jJd+woPL%v=8J%$B(A~|AQ%X0rY!nhpcEHm?mck2g28FfYH}yqRhxZ zewq7pz7@CvgzIk|uN}PMDr(9K48r<(X##%G+hgIsh<%$JDrGM7g<9&QpIgZtmZ+_~^=CDP!l#G2z_?#-|}Hy1Lt zM2fzekKmxgR3e#Q%`ZT6AKM@RyaGtp2njTzNR+uj1VY~oo7|ZwM3EN!`*6W02e{8E z-<=-d8{U$E?UVb2l`sx$OL}0H(sftTOkD6rP;p>)N;d{J0|$1ygyX8u$httT;=bTBDR}m)guy_RRa{|#Iz$p#Z=H2VA!_UHTD6F`Lj}84*QgZ-P~6t zRG=D6Po<3M2#j^FyfqRHM_^VVTXl7vJ`?8E+mc$i4EMVdHz`$B8O#(XNru5pETal6 zdnxzJFhTJCHMwz$Wtol;tJ7xO4Q9ZY z>odvhc6l^r;rMiuA#+f81S&-oDot-ScYHd{e0-WaF@e01IzU$7G#QJ0)v{m#5?+VPR4>3tf_&B|J1G=L}kRQMG&w zP`65Qw(wOck&ajD23PI|SFvE3f<;$71*}kPumU-_zjFqZ@0>wRw}wGeRTwl)PsznX zeHeJ6$OFZ7bvi*M0oe?ay&VR!B$B-s1~Mo%$hLjJM7GT^^sc~zCj;%hfp*(G-*0$9B!!Xb9h!;W}r)aoCwG6R;_bRp7D=<_Tz?Fyj%J8CeB|3zp)M8QRW` z0Js&wtpvOq!MmNMwZJMD1>c{ciyWs<_n&rxP~LKtAt?zfey+Uf61ZAE3-)fEb%M;? zSt$cnf5R-vQ&=d)TP0+2uc(~R;!0-S(y9|G+%PiC4x0K299UdU^>B0{GXZyE*a2E( zg)lJ-`+ziS8DrdSer-^ibz12U{{;0>@t(-ZF>Pnq_95e8zcJedTZKMzxQzSpY!?`8 z9hsl)9O$U2sWII8L9t#tY`elV39;EQCL(MEu(teu7IuPx*$8d}uQ;o!aOr==S&e%E zX~OysO_&%YSwq$w_QhGg;X{TWfP(XfhB@hGaTyTh{pS!w)wWxr>U%8>gom$BU+?DYu)No_3!)l}epD<)(@np)-BC8;d9^ zZ%1F>jy#IeX3KK6mU?!7r2%h4<^E^!&V?(waUq=AxX?@l5r@|`8pL7W#fv+4;{CYx z1Yq1n-1s=yG|sJET+7bZ=Vr=&+3%M+GKw0}+Ny^YN|iRR!rtF+ugKG?@VJ$}P!?P% zSVX?t)VXmok!V;*cu0lg)3kW#Y0Qd8`Eh<}(X=42m+j~I!Hq&Z*pJ^Kn`^4b|Z)mtq&3Ym{bdjESnKsN8 z=YYio{dn`9zAXCXhjvFP#$o91$CZ!^OCk!ver4!aiNRw8L>qv?gtLQ%I? zij3vw%*P<0@)DZZBsR?2{tCy6iww+zPTOUsqd{QMx`r_Z4lO(r0oN)oGjxBZI^H_x zX!t8*yU4WMCl)PiyUYNqLtLK+$>A{YdxU?Rv++z*$D?7Jz;t{i42|nB*Rg<{NXA~C z1KDpeHa{aPh1ce|H|A&s=;FeNhq`Ir_7+G34npz99QWEBfY1fAF+7L;e}<^}n6?cg zY(p@y>gK`rI?*`~(6%6Bdxvq+?Usw&-qScHBx4*|nQ&+9{*UAc~tbMw{J z=?=mOgAO*fj>33eV8`Y=F_2*{Igb&)9~z{m^@5O`C)}|O?iqBBtDEnUQ$uB_IctGv z7S;*frRr+F+cRWBg;fhgp-{AdCSIzl;xWP8XW4@bRqpDuZ0iE934h>8qm49Z#UFS? z(#nlG;}3kO^3O~p)aw!a3za@5oh1iJnz$D*OJHzdR9dkk$lOgjez?%^4(1HZik$e* z7R+q`nBu=#sBZe(*xEJQOU1BOHnQbETF)%*NFf~AX*$}y(r`<>m-cO*t?7wa24BHPB8 zh)@_k=@H&qbi`=tI}JhR3;Mi5swPqs=?>nrcs_V|@k}eMj|UbF6g3(~hJ)Y}@uy?4 z-Z4DcTl{=|L_v1=A7JaaRU(^@-F4E#mPfO%Nwh{cB$C0u z5zlHg;$Spw#Lg(%#iP^L`2Hp8fOP}5@c)Rq>ItY6o{pw7h&-|oj_aYgDF&niGQHs= zhTryA#x2_ht?=w?b;AP%6f2B1e8_s6)fyQ^Y2p~sJskSF^>I;H!pAo_m3(;#&LC41jm&(;&4t{it>-N!?RHfJYG3OMLYz7gJs1T10!=|{I)Ah-Cg@15Hk}sINZKb zMHcnIVO``&>l0Qltx81FV62J#bSwfZnNC-utAQ2M@wm100(&s_I@|+bq8FGASyXWZ zmX?T5w8p)l)31acIctb(ivfC<%^D`^di^R#`{#+u5b6ORtf;&u;tN9{hfn4kd-IL1 ze504R8jA6-Sjox}Rg7#bM`3HZ4d_r+uiSq{K7T2Kgh1qB74fjoonK{y8~WIIY>VBP z!%oJ$A=o%VF|Hi?;StPO;xrNhV|ykEe~N`{n-NH{)Y=2MkC8(e8XJm}@mcjGv2VwIJhEQ;Vxf-ow5dr~4e&)ZCC=Tk6iQEd^QgAiK6yjfE-) z+OuidYPCzI2Q_Zq2fU|GKh^mql-c5gvZ<*BaE@#Cso9359QM;o-cKt5-QfdM!BQjI z#@@fAu~)OVFL|@Kvj?EePKyIy1=mv0wKQgLa^(1miHf$9-)lQuT8q#7*EOWARI|^6 zhBG&5KzmEU!aeLCmo$CV>~EI5IosJ^L7BskmX^TVBUl*m9Z|>^4#Tv;)ClQdsKn1M z!+Plv;!CJ!OhqDHVjNABw8dJ_)hB-WJ7tD7_RDin4rAn3m!SA?iSTS$jsW{`1gs^q zhGd~4&X1J?fCx*po0zH_37i$B!M4a^8-ppQJ?uCQTGT-~JIHB(F40RlXy7_wlhuBXgpr7EO zpV0Az)K_XoaudZhBL!F#V^NNU6^pG{Y{#M@QTzqIw?Al4w4OZGCVn>o@SOlZN8mHT zV>jR{8x(x+sbD9d`w7XZU=P4&2>e{|^bHuu2?c)zj&>Vc#tVsmyzJt%gy+ZfiIY;n zMNz7P0@BXPGQ%%RRj$?-(i@6RMuT3ZL5+I70kLMY7S6*4y*4{bXVz-XirhSCWh)iA27}&= z6iTHYK={L5o~PBIJouAJr33;zQB#x|eiWXq0YgMnDyRxgCcP@lU@#jDAQ1n01Dv-F z`b?d{V9;fn6^dMi!kn%Y(6 ztV4Pf51nAA0fL1sU?>MfUS2N51}|jUpobU(Xfm0=yeza4t}GxSPAklMa2#Bx$nyfF zMHt3v(x%i8K6_8%Ak~{ER&WpZTu*m=@yd?hFYuF6=o_`dYuji zUZ>NU^D&ASzDL)}YP#fHs!h{GdLwcjhWQ19j4O0}l;W9(3 z>{`p52PEZrd3k0N`ANC{GoSj4{ZlP`0-v14`eaNjOTxz!-a;|@{#_KDbU&K}*pNzz zKU@``OhW9_fPzv2?1MhBdDWQ4c{jO!%RZ3g6-f%A6G@(aNIbUgVONsnn$3a_C5att z@H%EC>0zHu0?o6@4MBkSuosh?T(7gCWY+~K#K%^RMB*=#yhXbz?pnoj+z8fb1O}m z+gt%}NeeF0U$+{2PP0c=5r*$Se@}@Whk(2bjyOQscY|_Nv7#d%wc!0(SD@V$Xom?8 zpOy01S5~|0TRpUz9bBa|0u%UxElsc)x(LN35F_f>=mZqy6ga3~bHT0YcS8v$vW3+i z00<4Cr{L2=U;+&EIeN~(+j*DS5!|~*x=PnjKKH851t~~*1_{Lfq?(qm?YqSD`?S-jNE4j;S@a?KaL&3+D z%WFQpR@APW;iC|(>L1ap!<($N?x)&zgByD#l>>|OLe4t6Jl^iF7;ks{1wI9$!O_MI zl730a9$8OQ?)X0_=@jo78g(2*sx}lh0D;L4v_q_v?6=o3WWqOyj0@}+*15;PjxVhb z4D)Cytr|v6TJi-FbXy_R6%;jIA*ms?Utb@u&K!AZovz?rO|(Mb*Bch$GisdS8=_Dz zF?>R-mEjom{yO*`TBp^H@oLF#el<|-h&2f->$ZIq_wl;zjxnF3`+U$OpK0}Wt!P_Z zQiW&GwNeqN7OQ05i_IaIl$ma7^3?1Wnb!<(TjYc|ydD#e%E2Qkad!&J0nm5@wIz)b z+vF?a&*Uq-!xm%C3y;d;37H4WgC}Hgq<_e#z!ihQ!gpkJ>7-0oaNn0VxjgK5WEhTW z>5w^4y`l|NgQLP9W!oa-x%i*!S8Q=6VCe>jIZ?JnH*iY?x5S__S3`_pHK}pS>w+c~ zOvtuncns2{G%0b54DXGW8BUWz9Q3Af6yUlc%w`hiDzL9urr6CXt|GOYwkLW5l`+06 uP%&PW>ke40!J3qKM+y<#o|MxH*l}yHiQs_qeVG!Dq;~Ta_;&IC#r40rWGoN> literal 0 HcmV?d00001 diff --git a/MON_v8_3edeb015/README.md b/MON_v8_3edeb015/README.md new file mode 100644 index 0000000..924270f --- /dev/null +++ b/MON_v8_3edeb015/README.md @@ -0,0 +1,17 @@ +# Ocean-240.2 ROM Monitor V8 checksum 3edeb015 + +Source codes of Monitor v8 for Ocean-240.2 with Floppy controller. +In Z80 mnemonics, but limited for i8080 instruction set. + +## Differences: + +1) Font. Russian letters б and д; +2) Calculate values for extended ram access in procedures m_ramdisk_read, m_ramdisk_write. + +## Compile: + +Code is located in memory at address: 0xE000..0xFFFF + + sjasmplus --sld=monitor.sld --sym=monitor.labels --raw=monitor.obj --fullpath monitor.asm + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). diff --git a/MON_v8_3edeb015/bios_entries.inc b/MON_v8_3edeb015/bios_entries.inc new file mode 100644 index 0000000..117a2c8 --- /dev/null +++ b/MON_v8_3edeb015/bios_entries.inc @@ -0,0 +1,41 @@ + +; ======================================================= +; Ocean-240.2 +; Definition of CPM BIOS entries to compile depended +; modules +; +; By Romych 2025-09-09 +; ====================================================== + IFNDEF _BIOS + DEFINE _BIOS + + MODULE BIOS + +boot_f EQU 0xD600 +wboot_f EQU 0xD603 +const_f EQU 0xD606 +conin_f EQU 0xD609 +conout_f EQU 0xD60C +list_f EQU 0xD60F +punch_f EQU 0xD612 +reader_f EQU 0xD615 +home_f EQU 0xD618 +seldsk_f EQU 0xD61B +settrk_f EQU 0xD61E +setsec_f EQU 0xD621 +setdma_f EQU 0xD624 +read_f EQU 0xD627 +write_f EQU 0xD62A +sectran_f EQU 0xD630 +reserved_f1 EQU 0xD633 +reserved_f2 EQU 0xD636 +tape_read_f EQU 0xD639 +tape_write_f EQU 0xD63C +tape_wait_f EQU 0xD63F + +bios_var04 EQU 0xD64B + + ENDMODULE + + + ENDIF diff --git a/MON_v8_3edeb015/equates.inc b/MON_v8_3edeb015/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/MON_v8_3edeb015/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/MON_v8_3edeb015/font-6x7.inc b/MON_v8_3edeb015/font-6x7.inc new file mode 100644 index 0000000..221a101 --- /dev/null +++ b/MON_v8_3edeb015/font-6x7.inc @@ -0,0 +1,165 @@ + ; 96 symbols 6x7, with codes 0x20..0x7f KOI-7 H0 +m_font_cp0: + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; ' ' - 0x20 + DB 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x04 ; '!' - 0x21 + DB 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00 ; '"' - 0x22 + DB 0x0a, 0x0a, 0x1f, 0x0a, 0x1f, 0x0a, 0x0a ; '#' - 0x23 + DB 0x04, 0x1e, 0x05, 0x0e, 0x14, 0x0f, 0x04 ; '$' - 0x24 + DB 0x03, 0x13, 0x08, 0x04, 0x02, 0x19, 0x18 ; '%' - 0x25 + DB 0x06, 0x09, 0x05, 0x02, 0x15, 0x09, 0x16 ; '&' - 0x26 + DB 0x06, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00 ; ' - 0x27 + DB 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08 ; '(' - 0x28 + DB 0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02 ; ')' - 0x29 + DB 0x00, 0x0a, 0x04, 0x1f, 0x04, 0x0a, 0x00 ; '*' - 0x2a + DB 0x00, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00 ; '+' - 0x2b + DB 0x00, 0x00, 0x00, 0x00, 0x06, 0x04, 0x02 ; ',' - 0x2c + DB 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00 ; '-' - 0x2d + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06 ; '.' - 0x2e + DB 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00 ; '/' - 0x2f + DB 0x0e, 0x11, 0x19, 0x15, 0x13, 0x11, 0x0e ; '0' - 0x30 + DB 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x0e ; '1' - 0x31 + DB 0x0e, 0x11, 0x10, 0x08, 0x04, 0x02, 0x1f ; '2' - 0x32 + DB 0x1f, 0x08, 0x04, 0x08, 0x10, 0x11, 0x0e ; '3' - 0x33 + DB 0x08, 0x0c, 0x0a, 0x09, 0x1f, 0x08, 0x08 ; '4' - 0x34 + DB 0x1f, 0x01, 0x0f, 0x10, 0x10, 0x11, 0x0e ; '5' - 0x35 + DB 0x0c, 0x02, 0x01, 0x0f, 0x11, 0x11, 0x0e ; '6' - 0x36 + DB 0x1f, 0x10, 0x08, 0x04, 0x02, 0x02, 0x02 ; '7' - 0x37 + DB 0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e ; '8' - 0x38 + DB 0x0e, 0x11, 0x11, 0x1e, 0x10, 0x08, 0x06 ; '9' - 0x39 + DB 0x00, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00 ; ':' - 0x3a + DB 0x00, 0x06, 0x06, 0x00, 0x06, 0x04, 0x02 ; ';' - 0x3b + DB 0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08 ; '<' - 0x3c + DB 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00 ; '=' - 0x3d + DB 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02 ; '>' - 0x3e + DB 0x0e, 0x11, 0x10, 0x08, 0x04, 0x00, 0x04 ; '?' - 0x3f + DB 0x0e, 0x11, 0x10, 0x16, 0x15, 0x15, 0x0e ; '@' - 0x40 + DB 0x04, 0x0a, 0x11, 0x11, 0x1f, 0x11, 0x11 ; 'A' - 0x41 + DB 0x0f, 0x11, 0x11, 0x0f, 0x11, 0x11, 0x0f ; 'B' - 0x42 + DB 0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e ; 'C' - 0x43 + DB 0x07, 0x09, 0x11, 0x11, 0x11, 0x09, 0x07 ; 'D' - 0x44 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x1f ; 'E' - 0x45 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x01 ; 'F' - 0x46 + DB 0x0e, 0x11, 0x01, 0x1d, 0x11, 0x11, 0x1e ; 'G' - 0x47 + DB 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11 ; 'H' - 0x48 + DB 0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e ; 'I' - 0x49 + DB 0x1c, 0x08, 0x08, 0x08, 0x08, 0x09, 0x06 ; 'J' - 0x4a + DB 0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11 ; 'K' - 0x4b + DB 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1f ; 'L' - 0x4c + DB 0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11 ; 'M' - 0x4d + DB 0x11, 0x11, 0x13, 0x15, 0x19, 0x11, 0x11 ; 'N' - 0x4e + DB 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; 'O' - 0x4f + DB 0x0f, 0x11, 0x11, 0x0f, 0x01, 0x01, 0x01 ; 'P' - 0x50 + DB 0x0e, 0x11, 0x11, 0x11, 0x15, 0x09, 0x16 ; 'Q' - 0x51 + DB 0x0f, 0x11, 0x11, 0x0f, 0x05, 0x09, 0x11 ; 'R' - 0x52 + DB 0x1e, 0x01, 0x01, 0x0e, 0x10, 0x10, 0x0f ; 'S' - 0x53 + DB 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 'T' - 0x54 + DB 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; 'U' - 0x55 + DB 0x11, 0x11, 0x11, 0x11, 0x0a, 0x0a, 0x04 ; 'V' - 0x56 + DB 0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0a ; 'W' - 0x57 + DB 0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11 ; 'X' - 0x58 + DB 0x11, 0x11, 0x11, 0x0a, 0x04, 0x04, 0x04 ; 'Y' - 0x59 + DB 0x1f, 0x10, 0x08, 0x04, 0x02, 0x01, 0x1f ; 'Z' - 0x5a + DB 0x0e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0e ; '[' - 0x5b + DB 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00 ; '\' - 0x5c + DB 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0e ; ']' - 0x5d + DB 0x0e, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00 ; '^' - 0x5r + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f ; '_' - 0x5f + DB 0x1c, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00 ; '`' - 0x60 + DB 0x00, 0x00, 0x0e, 0x10, 0x1e, 0x13, 0x1e ; 'a' - 0x61 + DB 0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x0f ; 'b' - 0x62 + DB 0x00, 0x00, 0x0e, 0x01, 0x01, 0x01, 0x0e ; 'c' - 0x63 + DB 0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1e ; 'd' - 0x64 + DB 0x00, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x0e ; 'e' - 0x65 + DB 0x18, 0x04, 0x04, 0x0e, 0x04, 0x04, 0x04 ; 'f' - 0x66 + DB 0x00, 0x0e, 0x11, 0x11, 0x1e, 0x10, 0x0e ; 'g' - 0x67 + DB 0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x11 ; 'h' - 0x68 + DB 0x04, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04 ; 'i' - 0x69 + DB 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x06 ; 'j' - 0x6a + DB 0x01, 0x01, 0x09, 0x05, 0x03, 0x05, 0x09 ; 'k' - 0x6b + DB 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08 ; 'l' - 0x6c + DB 0x00, 0x00, 0x0f, 0x15, 0x15, 0x15, 0x15 ; 'm' - 0x6d + DB 0x00, 0x00, 0x09, 0x13, 0x11, 0x11, 0x11 ; 'n' - 0x6e + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e ; 'o' - 0x6f + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x0f, 0x01 ; 'p' - 0x70 + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x1e, 0x10 ; 'q' - 0x71 + DB 0x00, 0x00, 0x0d, 0x13, 0x01, 0x01, 0x01 ; 'r' - 0x72 + DB 0x00, 0x00, 0x1e, 0x01, 0x0e, 0x10, 0x0f ; 's' - 0x73 + DB 0x04, 0x04, 0x0e, 0x04, 0x04, 0x04, 0x18 ; 't' - 0x74 + DB 0x00, 0x00, 0x11, 0x11, 0x11, 0x19, 0x16 ; 'u' - 0x75 + DB 0x00, 0x00, 0x11, 0x11, 0x0a, 0x0a, 0x04 ; 'v' - 0x76 + DB 0x00, 0x00, 0x11, 0x15, 0x15, 0x15, 0x0a ; 'w' - 0x77 + DB 0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11 ; 'x' - 0x78 + DB 0x00, 0x00, 0x11, 0x11, 0x1e, 0x10, 0x0c ; 'y' - 0x79 + DB 0x00, 0x00, 0x1f, 0x08, 0x04, 0x02, 0x1f ; 'z' - 0x7a + DB 0x0c, 0x02, 0x02, 0x01, 0x02, 0x02, 0x0c ; '{' - 0x7b + DB 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; '|' - 0x7c + DB 0x03, 0x04, 0x04, 0x08, 0x04, 0x04, 0x03 ; '}' - 0x7d + DB 0x00, 0x02, 0x15, 0x0a, 0x15, 0x08, 0x00 ; '~' - 0x7e + DB 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15 ; [DEL] - 0x7f + +; 64 symbols 6x7, with codes 0x40..0x7f KOI-7 H1 +m_font_cp1: + DB 0x00, 0x00, 0x09, 0x15, 0x17, 0x15, 0x09 ; ю - 0x40 + DB 0x00, 0x00, 0x06, 0x08, 0x0e, 0x09, 0x16 ; а - 0x41 + DB 0x07, 0x02, 0x04, 0x0e, 0x09, 0x09, 0x06 ; б - 0x42 DIFF + DB 0x00, 0x00, 0x09, 0x09, 0x09, 0x1f, 0x10 ; ц - 0x43 + DB 0x03, 0x04, 0x08, 0x0e, 0x09, 0x09, 0x06 ; д - 0x44 DIFF + DB 0x00, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x1e ; е - 0x45 + DB 0x00, 0x04, 0x0e, 0x15, 0x15, 0x0e, 0x04 ; ф - 0x46 + DB 0x00, 0x00, 0x0f, 0x09, 0x01, 0x01, 0x01 ; г - 0x47 + DB 0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11 ; х - 0x48 + DB 0x00, 0x00, 0x11, 0x19, 0x15, 0x13, 0x11 ; и - 0x49 + DB 0x0a, 0x04, 0x11, 0x19, 0x15, 0x13, 0x11 ; й - 0x4a + DB 0x00, 0x00, 0x11, 0x09, 0x07, 0x09, 0x11 ; к - 0x4b + DB 0x00, 0x00, 0x1c, 0x12, 0x12, 0x12, 0x11 ; л - 0x4c + DB 0x00, 0x00, 0x11, 0x1b, 0x15, 0x11, 0x11 ; м - 0x4d + DB 0x00, 0x00, 0x11, 0x11, 0x1f, 0x11, 0x11 ; н - 0x4e + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e ; о - 0x4f + DB 0x00, 0x00, 0x1f, 0x11, 0x11, 0x11, 0x11 ; п - 0x50 + DB 0x00, 0x00, 0x1e, 0x11, 0x1e, 0x14, 0x12 ; я - 0x51 + DB 0x00, 0x00, 0x07, 0x09, 0x07, 0x01, 0x01 ; р - 0x52 + DB 0x00, 0x00, 0x0e, 0x01, 0x01, 0x01, 0x0e ; с - 0x53 + DB 0x00, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04 ; т - 0x54 + DB 0x00, 0x00, 0x11, 0x11, 0x1e, 0x10, 0x0e ; у - 0x55 + DB 0x00, 0x00, 0x15, 0x15, 0x0e, 0x15, 0x15 ; ж - 0x56 + DB 0x00, 0x00, 0x03, 0x05, 0x07, 0x09, 0x07 ; в - 0x57 + DB 0x00, 0x00, 0x01, 0x01, 0x07, 0x09, 0x07 ; ь - 0x58 + DB 0x00, 0x00, 0x11, 0x11, 0x13, 0x15, 0x13 ; ы - 0x59 + DB 0x00, 0x00, 0x0e, 0x11, 0x0c, 0x11, 0x0e ; з - 0x5a + DB 0x00, 0x00, 0x15, 0x15, 0x15, 0x15, 0x1f ; ш - 0x5b + DB 0x00, 0x00, 0x07, 0x08, 0x0e, 0x08, 0x07 ; э - 0x5c + DB 0x00, 0x00, 0x15, 0x15, 0x15, 0x1f, 0x10 ; щ - 0x5d + DB 0x00, 0x00, 0x09, 0x09, 0x0e, 0x08, 0x08 ; ч - 0x5e + DB 0x00, 0x00, 0x06, 0x05, 0x0c, 0x14, 0x0c ; ъ - 0x5f + DB 0x09, 0x15, 0x15, 0x17, 0x15, 0x15, 0x09 ; Ю - 0x60 + DB 0x04, 0x0a, 0x11, 0x11, 0x1f, 0x11, 0x11 ; А - 0x61 + DB 0x1f, 0x11, 0x01, 0x0f, 0x11, 0x11, 0x1f ; Б - 0x62 + DB 0x09, 0x09, 0x09, 0x09, 0x09, 0x1f, 0x10 ; С - 0x63 + DB 0x0c, 0x0a, 0x0a, 0x0a, 0x0a, 0x1f, 0x11 ; Д - 0x64 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x1f ; Е - 0x65 + DB 0x04, 0x0e, 0x15, 0x15, 0x15, 0x0e, 0x04 ; Ф - 0x66 + DB 0x1f, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01 ; Г - 0x67 + DB 0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11 ; Х - 0x68 + DB 0x11, 0x11, 0x19, 0x15, 0x13, 0x11, 0x11 ; И - 0x69 + DB 0x04, 0x15, 0x11, 0x19, 0x15, 0x13, 0x11 ; Й - 0x6a + DB 0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11 ; К - 0x6b + DB 0x1c, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11 ; Л - 0x6c + DB 0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11 ; М - 0x6d + DB 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11 ; Н - 0x6e + DB 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; О - 0x6f + DB 0x1f, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 ; П - 0x70 + DB 0x1e, 0x11, 0x11, 0x11, 0x1e, 0x12, 0x11 ; Я - 0x71 + DB 0x0f, 0x11, 0x11, 0x11, 0x0f, 0x01, 0x01 ; Р - 0x72 + DB 0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e ; С - 0x73 + DB 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; Т - 0x74 + DB 0x11, 0x11, 0x11, 0x11, 0x1e, 0x10, 0x0e ; У - 0x75 + DB 0x15, 0x15, 0x15, 0x0e, 0x15, 0x15, 0x15 ; Ж - 0x76 + DB 0x0f, 0x11, 0x11, 0x0f, 0x11, 0x11, 0x0f ; В - 0x77 + DB 0x01, 0x01, 0x01, 0x0f, 0x11, 0x11, 0x0f ; Ь - 0x78 + DB 0x11, 0x11, 0x11, 0x13, 0x15, 0x15, 0x13 ; Ы - 0x79 + DB 0x0e, 0x11, 0x10, 0x0c, 0x10, 0x11, 0x0e ; З - 0x7a + DB 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x1f ; Ш - 0x7b + DB 0x0e, 0x11, 0x10, 0x1c, 0x10, 0x11, 0x0e ; Э - 0x7c + DB 0x15, 0x15, 0x15, 0x15, 0x15, 0x1f, 0x10 ; Щ - 0x7d + DB 0x11, 0x11, 0x11, 0x1e, 0x10, 0x10, 0x10 ; Ч - 0x7e + DB 0x1f, 0x15, 0x1f, 0x15, 0x1f, 0x15, 0x1f ; [DEL] - 0x7f diff --git a/MON_v8_3edeb015/io.inc b/MON_v8_3edeb015/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/MON_v8_3edeb015/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/MON_v8_3edeb015/m_vars.inc b/MON_v8_3edeb015/m_vars.inc new file mode 100644 index 0000000..578bddd --- /dev/null +++ b/MON_v8_3edeb015/m_vars.inc @@ -0,0 +1,101 @@ +; ======================================================= +; Ocean-240.2 +; Module M_VARS - Monitor variables +; RAM Range: 0xBA09-0xBFFF +; +; Disassembled by Romych 2025-02-05 +; ======================================================= + + IFNDEF _M_VARS + DEFINE _M_VARS + + MODULE M_VARS + ORG 0xbf00 + +buffer DS 128 ; 0xbf00 search text or buffer to search? + DS 36 +paint_stack EQU $ ; 0xbfa4 + DS 32 +stack1: EQU $ ; 0xbfc4 + DS 1 +paint_var1 DS 1 ; 0xbfc5 +paint_var2 DS 1 ; 0xbfc6 +paint_var3 DS 1 ; 0xbfc7 +paint_var4 DS 1 ; 0xbfc8 +paint_var5 DS 1 ; 0xbfc9 +paint_y DS 1 ; 0xbfca +paint_var7 DS 1 ; 0xbfcb +cmp_color DS 1 ; 0xbfcc +paint_sp_save DS 2 + +; Right pixel mask ex: 11111000 +pixel_mask_r DS 1 ; 0xbfcf +tmp_color DS 1 ; 0xbfd0 +rect_var2 DS 1 ; 0xbfd1 +stack_0 EQU $ +rect_var3 DS 1 ; 0xbfd2 +esc_mode DS 1 ; 0xbfd3 +esc_cmd DS 1 ; 0xbfd4 + +esc_param_cnt DS 1 ; 0xbfd5 +esc_param DS 7 ; 0xbfd6 + +; Left inverse pixel mask ex: 00011111 +pixel_mask_l_i DS 1 +; Right inverse pixel mask ex: 00011111 +pixel_mask_r_i DS 1 +; Left pixel mask ex: 11100000 +pixel_mask_l DS 1 + +; Current screen mode, bit 3 - hide/show cursor, bit 4 - only 20 rows +screen_mode DS 1 ; 0xbfe0 +cursor_row DS 1 ; 0xbfe1 Cursor Y position +cursor_col DS 1 ; 0xbfe2 Cursor X position +curr_color DS 2 ; 0xbfe3 Current color low and hi bytes +esc_hex_cmd: +row_shift DS 1 ; 0xbfe5 +codepage DS 1 ; 0xbfe6 + + +cur_palette DS 1 ; 0xbfe7 00bbbfff - background and foreground colors +beep_period DS 2 ; 0xbfe8 +beep_duration DS 2 ; 0xbfea +pix_shift DS 1 ; 0xbfec +strobe_state DS 1 ; 0xbfed +ul_var0 DS 1 ; 0xbfee +ul_A_var1 DS 1 ; 0xbfef +ul_B_var2 DS 1 ; 0xbff0 +ul_var3 DS 1 ; 0xbff1 +ul_A_var4 DS 1 ; 0xbff2 +ul_B_var5 DS 1 ; 0xbff3 +ul_var6 DS 1 ; 0xbff4 +esc_var0 DS 1 ; 0xbff5 +esc_var1 DS 1 ; 0xbff6 +esc_var2 DS 1 ; 0xbff7 +esc_var3 DS 1 ; 0xbff8 + DS 1 ; 0xbff9 + DS 1 ; 0xbffa + DS 1 ; 0xbffb + DS 1 ; 0xbffc + DS 1 ; 0xbffd + DS 1 ; 0xbffe + DS 1 ; 0xbfff + + ASSERT stack1 = 0xbfc4 + ASSERT buffer = 0xbf00 + ASSERT paint_var1 = 0xbfc5 + ASSERT pixel_mask_r = 0xbfcf + ASSERT stack_0 = 0xbfd2 + ASSERT esc_mode = 0xbfd3 + ASSERT screen_mode = 0xbfe0 + ASSERT cur_palette = 0xbfe7 + ASSERT ul_var0 = 0xbfee + ASSERT paint_stack = 0xbfa4 + ASSERT esc_var3 = 0xbff8 + + ;DISPLAY "screen_mode: ", /H, screen_mode + ;DISPLAY "fn48_var1: ", /H, fn48_var1 + + ENDMODULE + + ENDIF diff --git a/MON_v8_3edeb015/monitor.asm b/MON_v8_3edeb015/monitor.asm new file mode 100644 index 0000000..262dbb5 --- /dev/null +++ b/MON_v8_3edeb015/monitor.asm @@ -0,0 +1,5594 @@ +; ====================================================== +; Ocean-240.2 +; Monitor V8 checksum 3edeb015 +; +; Disassembled by Romych 2026-02-15 +; ====================================================== + + DEVICE NOSLOT64K + + INCLUDE "io.inc" + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "bios_entries.inc" + + OUTPUT monitor_E000.bin + + + MODULE MONITOR + + ORG 0xE000 + +; ------------------------------------------------------ +; Monitor Entry points +; ------------------------------------------------------ + +start: JP m_hot_start ; E000 +mon_hexb: JP m_hexb ; E003 +non_con_status: JP m_con_status ; E006 +mon_con_in: JP m_con_in ; E009 +mon_con_out: JP m_con_out ; E00C +mon_serial_in: JP m_serial_in ; E00F +mon_serial_out: JP m_serial_out ; E012 +mon_char_print: JP m_char_print ; E015 +mon_tape_read: JP m_tape_read ; E018 +mon_tape_write: JP m_tape_write ; E01B +ram_disk_read: JP m_ramdisk_read ; E01E +ram_disk_write: JP m_ramdisk_write ; E021 +mon_tape_read_ram: JP m_tape_read_ram2 ; E024 +mon_tape_write_ram: JP m_tape_write_ram2 ; E027 +mon_tape_wait: JP m_tape_wait ; E02A +mon_tape_detect: JP m_tape_blk_detect ; E02D +read_floppy: JP m_read_floppy ; E030 +write_floppy: JP m_write_floppy ; E033 +mon_out_str_z: JP m_out_strz ; E036 + JP m_fn_39 ; E039 + JP get_image_hdr ; E03C + JP esc_picture ; E03F + JP m_print_at_xy ; E042 C-char esc_param[0]=X, [1]=Y + JP esc_draw_fill_rect ; E045 + JP esc_paint ; E048 + JP esc_draw_line ; E04B + JP esc_draw_circle ; E04E + + +; ------------------------------------------------------ +; Init system devices +; ------------------------------------------------------ +m_hot_start: + DI + ;LD SP, M_VARS.rst_ret_jp + LD A, 10000000b ; DD17 all ports to out + OUT (SYS_DD17CTR), A ; VV55 Sys CTR + OUT (DD67CTR), A ; VV55 Video CTR + + ; init_kbd_tape + LD A, 0x93 + OUT (KBD_DD78CTR), A + + + LD A, 01111111b ; VSU=0, C/M=1, FL=111, COL=111 + OUT (VID_DD67PB), A ; color mode + LD A, 00000001b + OUT (SYS_DD17PB), A ; Access to VRAM + LD B, 0x0 ; TODO: replace to LD HL, 0x3f00 LD B,L + LD HL, 0x3f00 + LD A, H + ADD A, 0x41 ; A=128 0x80 + + ; Clear memory from 0x3F00 to 0x7FFF +.fill_video: + LD (HL), B + INC HL + CP H + JP NZ, .fill_video + + ;XOR A + LD A, 0 + OUT (SYS_DD17PB), A ; Disable VRAM + LD A, 00000111b + OUT (SYS_DD17PC), A ; pix shift to 7 + LD (M_VARS.pix_shift), A + + XOR A + LD (M_VARS.screen_mode), A + LD (M_VARS.row_shift), A + + ; Set color mode and palette + LD (M_VARS.curr_color+1), A + CPL + LD (M_VARS.curr_color), A + LD A, 00000011b + LD (M_VARS.cur_palette), A + ; VSU=0, C/M=1, FL=000, COL=011 + ; color mode, black border + ; 00-black, 01-red, 10-purple, 11-white + LD A, 01000011b + OUT (VID_DD67PB), A + + ; config LPT + LD A, 0x4 + OUT (DD67PC), A ; bell=1, strobe=0 + LD (M_VARS.strobe_state), A ; store strobe + LD HL, 1024 ; 683us + LD (M_VARS.beep_period), HL + LD HL, 320 ; 213us + LD (M_VARS.beep_duration), HL + +.conf_uart: + ; Config UART + LD A, 11001110b + OUT (UART_DD72RR), A + LD A, 00100101b + OUT (UART_DD72RR), A + + ; Config Timer#1 for UART clock + LD A, 01110110b ; tmr#1, load l+m bin, sq wave + OUT (TMR_DD70CTR), A + + ; 1.5M/20 = 75kHz + LD A, 20 + OUT (TMR_DD70C2), A + XOR A + OUT (TMR_DD70C2), A +.conf_pic: + ; Config PIC + LD A,00010010b ; ICW1 edge trigger, interval 8, sin... + OUT (PIC_DD75RS), A + XOR A + OUT (PIC_DD75RM), A ; ICW2 + CPL + OUT (PIC_DD75RM), A ; ICW3 no slave + LD A,00100000b + OUT (PIC_DD75RS), A ; Non-specific EOI command, End of I... + LD A, PIC_POLL_MODE + OUT (PIC_DD75RS), A ; Poll mode, poll on next RD + + LD A, 0x80 + OUT (KBD_DD78PC), A ; TODO: - Check using this 7th bit + NOP + NOP + XOR A + OUT (KBD_DD78PC), A + + ; Init cursor + LD SP, M_VARS.stack1 + CALL m_draw_cursor + + ; Beep + LD C, ASCII_BELL + CALL m_con_out + + LD A, (BIOS.boot_f) + CP JP_OPCODE + JP Z, BIOS.boot_f + LD HL, mgs_system_nf + CALL m_out_strz + JP m_sys_halt + +; -------------------------------------------------- +; Output ASCIIZ string +; Inp: HL -> string +; -------------------------------------------------- +m_out_strz: + LD C, (HL) + LD A, C + OR A + RET Z + CALL m_con_out + INC HL + JP m_out_strz + +mgs_system_nf: + DB "\r\nSYSTEM NOT FOUND\r\n", 0 + +m_sys_halt: + HALT + +; ------------------------------------------------------ +; Console status +; Out: A = 0 - not ready +; A = 0xFF - ready (key pressed) +; ------------------------------------------------------ +m_con_status: + IN A, (PIC_DD75RS) ; Read PIC status + NOP + AND KBD_IRQ ; Check keyboard request RST1 + LD A, 0 + RET Z ; no key pressed + CPL + RET ; key pressed + +; ------------------------------------------------------ +; Wait and read data from UART +; Out: A - 7 bit data +; ------------------------------------------------------ +m_serial_in: + IN A, (UART_DD72RR) + AND RX_READY + JP Z, m_serial_in ; wait for rx data ready + IN A, (UART_DD72RD) + AND 0x7f ; leave 7 bits + RET + +; ------------------------------------------------------ +; Read key +; Out: A +; ------------------------------------------------------ +m_con_in: + CALL m_con_status + OR A + JP Z, m_con_in ; wait key + IN A, (KBD_DD78PA) ; get key + AND 0x7f ; reset hi bit, leave 0..127 code + PUSH AF + ; TODO: Check if it is keyboard ACK + ; PC7 Set Hi (ACK?) + LD A, 0x80 + OUT (KBD_DD78PC), A + ; PC7 Set Lo + XOR A + OUT (KBD_DD78PC), A + POP AF + RET + +; ------------------------------------------------------ +; Send data by UART +; Inp: C - data to transmitt +; ------------------------------------------------------ +m_serial_out: + IN A, (UART_DD72RR) + AND TX_READY + JP Z, m_serial_out ; Wait for TX ready + LD A, C + OUT (UART_DD72RD), A + RET + +; ------------------------------------------------------ +; Send character to printer +; Inp: C - character +; ------------------------------------------------------ +m_char_print: + ; wait printer ready + IN A, (PIC_DD75RS) + AND PRINTER_IRQ + JP Z, m_char_print + + LD A, C + NOP + OUT (LPT_DD67PA), A + ; set LP strobe + LD A, 00010100b + OUT (DD67PC),A + +.wait_lp: + ; wait printer ack + IN A, (PIC_DD75RS) + AND PRINTER_IRQ + JP NZ, .wait_lp + ; remove LP strobe + LD A, 00000100b + OUT (DD67PC), A + RET + +; ------------------------------------------------------ +; Out char to console +; Inp: C - char +; ------------------------------------------------------ +m_con_out: + PUSH HL + PUSH DE + PUSH BC + CALL m_con_out_int + POP BC + POP DE + POP HL + RET + +; ------------------------------------------------------ +; Out char C to console +; ------------------------------------------------------ +m_con_out_int: + LD DE, M_VARS.esc_mode + LD A, (DE) + DEC A + OR A ; TODO: unused (save 1b 4t) + JP M, m_print_no_esc ; esc_mode=0 - standart print no ESC mode + JP NZ, m_print_at_xy ; esc_mode=2 (graphics) + + ; handle ESC param (esc_mode=1) + INC DE ; TODO: replace to INC E E=0xd3 save 2t + LD A, (DE) + OR A + JP P, get_esc_param + LD A, C + AND 0xf ; convert char to command code + LD (DE), A + INC DE ; TODO: replace to INC E E=0xd3 save 2t + XOR A + LD (DE), A + RET + +get_esc_param: + LD HL, M_VARS.esc_cmd + LD B, (HL) ; TODO: replace to INC L L=0xd4 save 2t + INC HL ; HL -> param count + LD A, (HL) + INC A + LD (HL), A + ; store new param + LD E, A + LD D, 0x0 + ADD HL, DE ; HL -> parameter[param_count] + LD (HL), C ; store letter as esc parameter + ; get params count for esc command + LD HL, esc_params_tab + LD E, B ; d=0, b = cmd + ADD HL, DE ; DE - command offset + CP (HL) + ; return if enough + RET M + +;esc_set_mode: + LD HL, M_VARS.esc_cmd + LD A, (HL) + AND 0x0f ; mask (cmd=0..15) + LD E, A + DEC HL ; HL -> esc_mode + OR A + LD (HL), 2 ; mode=2 for cmd=0 + RET Z ; just return, no handler there + + LD D, 0 ; TODO: remove, D already 0 + LD (HL), D ; reset mode to 0 for other + DEC DE ; DE = cmd-1 + +;co_get_hdlr: + ; Calc ESC command handler offset + LD HL, esc_handler_tab + ADD HL, DE + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) + ; HL = addr of handler func + EX DE, HL + ; It is 1..4 func DRAW_* func? + CP 0x4 + JP P, esc_no_draw_fn + LD A, (M_VARS.screen_mode) + AND 00000011b + ; If not in graphics mode - exit + JP NZ, esc_exit + +esc_no_draw_fn: + LD DE, esc_exit + PUSH DE + + ; Jump to ESC func handler + JP (HL) + +esc_exit: + XOR A + LD (M_VARS.esc_mode), A + RET + + ; Count of parameters for ESC commands + ; 0xe1cb +esc_params_tab: + DB 3, 5, 4, 3, 1, 2, 1, 1 + DB 1, 2, 1, 5, 5, 7, 6, 4 + +esc_handler_tab: + DW esc_draw_fill_rect ;5 1x1y1x2y2m + DW esc_draw_line ;4 2x1y1x2y2 + DW esc_draw_dot ;3 3xxyy + DW esc_set_color ;1 4N N=1..4 + DW esc_set_cursor ;2 5rc r-Row, c-Col + DW esc_set_vmode ;1 6m m-mode: + ; C 0 - 40x25 cursor on + ; M 1,2 - 64x25 cursor on + ; M 3 - 80x25 cursor on + ; C 4 - 40x25 cursor off + ; M 5,6 - 64x25 cursor off + ; M 7 - 80x25 cursor off + ; M 8 - 20rows mode + ; 9 - cursor off + ; 10 - cursor on + DW esc_set_charset ;1 7n where n is: + ; 0 - LAT Both cases + ; 1 - RUS Both cases + ; 2 - LAT+RUS Upper case + DW esc_set_palette ;1 8c c - Foreground+Backgound + DW esc_set_cursor2 ;2 9xy + DW esc_print_screen ;1 : + DW esc_draw_circle ;5 ;xyraxay X,Y, Radius, aspect ratio X, aspect ratio Y + DW esc_paint ;5 = + DW esc_picture ;6 > + DW esc_set_beep ;4 ?ppdd pp-period (word), dd - duration (word) + +esc_set_beep: + ; param byte 1+2 -> period + LD DE, M_VARS.esc_param + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (M_VARS.beep_period), HL + ; param byte 3+4 -> duration + INC DE + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (M_VARS.beep_duration), HL + RET + +esc_set_cursor2: + JP esc_set_cursor + +esc_print_screen: + LD A, (M_VARS.screen_mode) + AND 00000011b + RET NZ ; ret if not 0-3 mode + LD DE, 0x30ff + CALL m_print_hor_line + DEC E + LD D, 0xf0 + +.chk_keys: + CALL m_con_status + OR A + JP Z, .no_keys + CALL m_con_in + CP ASCII_ESC + RET Z + +.no_keys: + CALL m_print_hor_line + DEC E + JP NZ, .chk_keys + LD D, 0xe0 ; 224d + CALL m_print_hor_line + RET + +; ------------------------------------------------------ +; Print line to printer +; D - width +; ------------------------------------------------------ +m_print_hor_line: + LD HL, cmd_esc_set_X0 + + ; Set printer X coordinate = 0 + CALL m_print_cmd + LD HL, 4 + LD (M_VARS.ul_var0), HL ; Set start coord X = 4 + LD B, 0x0 ; TODO: LD B, H (save 1b 3t) + +.print_next_col: + LD C, 0x0 + ; 1 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (M_VARS.ul_var0) + INC HL + + ; inc X + LD (M_VARS.ul_var0), HL + LD C, 0x1 + ; 2 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (M_VARS.ul_var0) + INC HL + ; inc X + LD (M_VARS.ul_var0), HL + INC B + LD A, B + CP 236 + JP C, .print_next_col + LD HL, cmd_esc_inc_Y2 + CALL m_print_cmd + RET + +; ------------------------------------------------------ +; Send command to printer +; Inp: HL -> command bytes array +; ------------------------------------------------------ +m_print_cmd: + PUSH BC +.print_nxt: + LD A, (HL) + CP ESC_CMD_END + JP Z, .cmd_end + LD C, A + CALL m_char_print + INC HL + JP .print_nxt +.cmd_end: + POP BC + RET + +; ------------------------------------------------------ +; Print 7 vertical pixels to printer +; Inp: A - value to print +; ------------------------------------------------------ +m_print_vert_7pix: + PUSH AF + ; Set coordinate X to 0 + LD HL, cmd_esc_set_X + CALL m_print_cmd + LD HL, (M_VARS.ul_var0) + LD C,H + CALL m_char_print + LD C,L + CALL m_char_print + ; Set column print mode + LD HL, cmd_esc_print_col + CALL m_print_cmd + POP AF + ; Print 7 vertical pixels + LD C, A + CALL m_char_print + RET + +; ------------------------------------------------------ +; Control codes for printer УВВПЧ-30-004 +; ------------------------------------------------------ +; Zn - Increment Y coordinate +; 0xe2a5 +cmd_esc_inc_Y2: + DB ASCII_ESC + DB 'Z' + DB 2h + DB ESC_CMD_END + +; Xnn - Set X coordinate +cmd_esc_set_X0: + DB ASCII_ESC + DB 'X' + DB 0h ; 0..479 + DB 0h + DB ESC_CMD_END + +; ------------------------------------------------------ +; X - Start on "Set X coordinate" command +; ------------------------------------------------------ +cmd_esc_set_X: + DB ASCII_ESC + DB 'X' + DB ESC_CMD_END + +; O - Column print (vertical 7 bit) +cmd_esc_print_col: + DB ASCII_ESC + DB 'O' + DB ESC_CMD_END + +; ------------------------------------------------------ +; Get 7 vertical pixels from screen +; Inp: C - sheet +; Out: A - byte +; ------------------------------------------------------ +m_get_7vpix: + LD A, (M_VARS.row_shift) + ADD A, B + ADD A, 19 ; skip first 20pix + LD L, A + PUSH DE + PUSH BC + LD A, E + +.calc_pix_no: + AND 0x7 + LD B, A + LD A, E + ; calc hi addr + RRA ; /8 + RRA + RRA + AND 0x1f + ADD A, A ; *2 + ADD A, 64 ; bytes per row + LD H, A + ; select sheet 0|1 + LD A, C + AND 0x1 + ADD A, H + LD H, A + ; HL = pix addr, turn on VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + LD E, (HL) ; read pixel + INC H ; HL += 512 + INC H + LD D, (HL) ; read pixel row+1 + + ; turn off VRAM access + ;v8 XOR A + LD A, 0 + OUT (SYS_DD17PB), A +.for_all_pix: + DEC B + JP M, .all_shifted + ; shift pixels D >> [CF] >> E + LD A, D + RRA + LD D, A + LD A, E + RRA + LD E, A + JP .for_all_pix +.all_shifted: + LD A, E + LD D, 0 + RRA + JP NC,.not_1_1 + LD D,00110000b +.not_1_1: + RRA + JP NC, .not_1_2 + LD A, D + OR 11000000b + LD D, A +.not_1_2: + LD A, D + POP BC + POP DE + RET + +esc_set_palette: + LD A, (M_VARS.esc_param) + AND 00111111b ; bgcol[2,1,0],pal[2,1,0] + LD (M_VARS.cur_palette), A + LD B, A + LD A, (M_VARS.screen_mode) + AND 00000011b + LD A, 0x0 + JP NZ, esp_no_colr + LD A, 0x40 + +esp_no_colr: + OR B + OUT (VID_DD67PB), A + RET + +esc_set_charset: + LD A, (M_VARS.esc_param) + AND 0x3 ; charset 0..3 + LD (M_VARS.codepage), A + RET + +; ------------------------------------------------------ +; Get address for draw symbol glyph +; Inp: A - ascii code +; Out: HL -> glyph offset +; ------------------------------------------------------ +m_get_glyph: + LD L, A ; L = ascii code + LD E, A ; E = ascii code + XOR A + LD D, A + LD H, A + ; HL = DE = ascii code + ADD HL, HL + ADD HL, DE + ADD HL, HL + ADD HL, DE + ; HL = A * 7 + LD A, E ; A = A at proc entry + CP '@' + ; First 64 symbols is same for all codepages + JP M, .cp_common + LD A, (M_VARS.codepage) + OR A + ; cp=0 - Latin letters + JP Z, .cp_common + DEC A + ; cp=1 - Russian letters + JP Z, .cp_rus + ; cp=2 - 0x40..0x5F - displayed as Lat + ; 0x60 - 0x7F - displayed as Rus + LD A, E + CP 0x60 + JP M, .cp_common +.cp_rus: + LD DE, 448 ; +448=64*7 Offset for cp1 + ADD HL, DE + +.cp_common: + LD DE, m_font_cp0-224 ; m_font_cp0-32*7 + ADD HL, DE ; add symbol glyph offset + RET + + +; -------------------------------------------------- +; Console output +; Inp: C - char +; -------------------------------------------------- +m_print_no_esc: + LD A, C + AND 0x7f ; C = 0..127 ASCII code + CP ASCII_SP ; C < ' '? + JP M, m_handle_esc_code ; jump if less + CALL m_get_glyph + EX DE, HL + LD A, (M_VARS.screen_mode) + AND 0x3 + JP NZ, mp_mode_64 ; jump to non color modes + + CALL calc_addr_40 + INC L + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + DEC H + DEC H + ; one or two bytes + LD A, B + OR B + JP Z, .l1 + DEC B + JP Z, .l2 + DEC B + JP Z, .l3 + JP .l4 +.l1: + INC H + INC H + LD BC, 0xffc0 + LD A, 0x0 + JP .l5 +.l2: + LD BC, 0xf03f + LD A, 0x6 + JP .l5 +.l3: + LD BC, 0xfc0f + LD A, 0x4 + JP .l5 +.l4: + LD BC, 0xff03 + LD A, 0x2 +.l5: + LD (M_VARS.esc_var1), A + EX DE, HL + +.sym_draw: + LD A, (M_VARS.esc_var1) + PUSH HL + LD L, (HL) + LD H, 0x0 + OR A + JP Z, .pne_l8 + +.pne_l7: + ADD HL, HL + DEC A + JP NZ, .pne_l7 + +.pne_l8: + EX DE, HL + LD A, (HL) + AND C + LD (HL), A + LD A, (M_VARS.curr_color) + AND E + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND C + LD (HL), A + LD A, (M_VARS.curr_color+1) + AND E + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, (M_VARS.curr_color) + AND D + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, (M_VARS.curr_color+1) + AND D + OR (HL) + LD (HL), A + INC L + DEC H + DEC H + DEC H + EX DE, HL + POP HL + INC HL + LD A, (M_VARS.esc_var0) + DEC A + LD (M_VARS.esc_var0), A + JP NZ, .sym_draw + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + ; draw cursor on return + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_rt: + INC HL + LD A, (HL) ; a = col + ADD A, 1 ; col+1 + AND 0x3f ; screen column 0..63 + LD (HL), A ; save new col + CP 40 + DEC HL + RET M ; Return if no wrap + +m40_wrap_rt: + INC HL + XOR A + LD (HL), A + DEC HL + LD A, (M_VARS.screen_mode) + AND 0x08 ; screen_mode=8? + JP NZ, m2_lf + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_bksp: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A + AND 0x3f ; A=0..63 + CP 0x3f + JP Z, .wrap + LD (HL), A + DEC HL + RET + +.wrap: + LD A, 39 + LD (HL), A + DEC HL + ; and cursor up + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_up: + LD A, (HL) + SUB 10 ; 10 rows per symbol + JP NC, .up_no_minus + LD A, 240 ; wrap to bottom +.up_no_minus: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor right 8 pos) 20rows mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m20_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x3f ; wrap A=0..63 + LD (HL), A + CP 40 + DEC HL + RET M ; ret if column <40 + JP m40_wrap_rt ; or wrap to next line + +; -------------------------------------------------- +; Calculate VRAM address in 40 column mode +; -------------------------------------------------- +calc_addr_40: + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + CP 4 + LD B, A + JP M, .l2 + AND 0x3 + LD B, A + LD A, H + OR A + RRA + OR A + RRA + LD C, A + LD H, 0x6 + XOR A + +.l1: + ADD A, H + DEC C + JP NZ, .l1 + ADD A, B + +.l2: + ADD A, B + ADD A, 66 + LD H, A + LD A, 0x7 + LD (M_VARS.esc_var0),A + RET + +m2_lf: + LD A, (HL) + ADD A, 10 + CP 15 + JP NC, .lf_nowr + LD (HL), A + RET + +.lf_nowr: + LD A, (M_VARS.row_shift) + LD L, A + ADD A, ASCII_LF + LD E, A + LD C, ASCII_BS + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.cas_l5: + LD B, 0x40 + LD H, 0x40 ; TODO: LD H, B save 1b 3t + LD D, H + +.cas_l6: + LD A, (DE) + LD (HL), A + INC H + INC D + DEC B + JP NZ, .cas_l6 + INC L + INC E + DEC C + JP NZ, .cas_l5 + LD C, 10 + LD A, (M_VARS.row_shift) + ADD A, 8 + LD E, A + +.cas_l7: + LD B, 0x40 + LD D, 0x40 ; TODO: LD D, B save 1b 3t + XOR A + +.cas_l8: + LD (DE),A + INC D + DEC B + JP NZ,.cas_l8 + INC E + DEC C + JP NZ,.cas_l7 + LD A,0x0 + OUT (SYS_DD17PB),A + RET + + +; --------------------------------------------------- +; Handle ASCII_BS (cursor left) in 20row mode +; --------------------------------------------------- +m20_bksp: + INC HL + LD A, (HL) + OR A + DEC HL + RET Z + + INC HL + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x3f + LD (HL), A + DEC HL + RET + +; --------------------------------------------------- +; Print symbol in 64x25 mode +; --------------------------------------------------- +mp_mode_64: + CP 3 ; + JP Z, mp_mode_80 ; jump for screen_mode=3 + ; calc symbol address in VRAM + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + ADD A, 0x40 + LD H, A + ; + LD C, 7 ; symbol height + + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + EX DE, HL + XOR A + LD (DE), A + INC E + +.next_row: + LD A, (HL) + ADD A, A + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .next_row + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + ; draw cursor at end + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_rt: + INC HL + LD A, (HL) + ADD A, 1 + AND 0x3f ; wrap + LD (HL), A + DEC HL + RET NZ ; ret if no wrap + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Scroll Up for 10 rows +; -------------------------------------------------- +scroll_up: + LD A, (M_VARS.row_shift) + ADD A, 10 + OUT (SYS_DD17PA), A ; Scroll via VShift register + LD (M_VARS.row_shift), A ; store new VShift value + ; calc bottom 16 rows address in VRAM + LD HL, 0x40f0 ; 240th VRAM byte + ADD A, L + LD L, A + LD C, H + + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + XOR A + LD DE, 0x1040 ; D=16 E=64 (512/8 bytes in row) + +.next_row: + LD H, C + LD B, E + + ; clear 64 bytes (512px in mono or 256px in color mode) +.next_col: + LD (HL), A + INC H ; next column + DEC B + JP NZ, .next_col + INC L ; next row address + DEC D ; row counter - 1 + JP NZ, .next_row + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_bs: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x3f ; wrap column (0..63) + LD (HL), A + CP 63 + DEC HL + RET NZ + ; cursor up if wrapped + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_up: + LD A, (HL) + SUB 10 + JP NC, .no_wrap + LD A, 240 + +.no_wrap: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor column + 8) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x38 + LD (HL), A + DEC HL + RET NZ ; return if no wrap + ; cursor down if wrap + JP m64_lf + +; -------------------------------------------------- +; Print symbols in 80x25 mode +; -------------------------------------------------- +mp_mode_80: + CALL calc_addr_80 + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + ; fix address + EX DE, HL + INC E + ; make bitmask + LD A, B + OR A + JP Z, .l1 + DEC A + JP Z, .l2 + DEC A + JP Z, .l3 + JP .l4 + +.l1: + LD B, (HL) + LD A, (DE) + AND 0xc0 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .l1 + JP .l6 +.l2: + LD A, (HL) + RRCA + RRCA + AND 0x7 + LD B, A + LD A, (DE) + AND 0xf0 + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + AND 0xc0 + LD B, A + DEC D + LD A, (DE) + AND 0x1f + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, .l2 + JP .l6 +.l3: + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0x1 + LD B, A + LD A, (DE) + AND 0xfc + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0xf0 + LD B, A + DEC D + LD A, (DE) + AND 0x7 + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, .l3 + JP .l6 +.l4: + DEC D +.l5: + LD A, (HL) + RLCA + RLCA + LD B, A + LD A, (DE) + AND 0x1 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .l5 + INC D + +.l6: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + ; Draw cursor after symbol + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_rt: + INC HL + LD A, (HL) + ADD A, 1 ; inc column + AND 0x7f + LD (HL), A + CP 80 + DEC HL + RET M ; return if no wrap + +m80_col_wrap: + INC HL + XOR A + LD (HL), A + DEC HL + ; and move cursor to next row + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_bs: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x7f ; mask [0..127] + CP 127 + JP Z, .wrap + LD (HL), A + DEC HL + RET + +.wrap: + LD A, 79 + LD (HL), A + DEC HL + ; and move cursor to previous line + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_up: + LD A, (HL) + SUB 10 + JP NC, .no_wrap + LD A, 240 + +.no_wrap: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor column + 8) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x7f + LD (HL), A + CP 80 + DEC HL + RET M ; return if no cursor wrap + JP m80_col_wrap + +; -------------------------------------------------- +; Calculate address for cursor pos for 80x25 mode +; Out: HL -> VRAM +; B -> pixel pos in byte +; -------------------------------------------------- +calc_addr_80: + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + CP 4 + LD B, A + JP M, mns_ep_fm_0 + AND 3 + LD B, A + LD A, H + OR A + RRA + OR A + RRA + LD C, A + LD H, 3 + XOR A +mns_l1: + ADD A, H + DEC C + JP NZ, mns_l1 + ADD A, B +mns_ep_fm_0: + ADD A, 0x42 + LD H, A + LD C, 0x7 + RET + +; -------------------------------------------------- +; Clear screen and set cursor to 0,0 +; Inp: HL -> cursor position +; -------------------------------------------------- +m_clear_screen: + LD A, (M_VARS.screen_mode) + AND 0x8 + JP NZ, m_clear_20_rows ; for bit 4 is set, clear only 20 rows + ; all in black + LD A, 01111111b + OUT (VID_DD67PB), A ; C/M=1 FL=111 CL=111 All black + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD DE, video_ram + EX DE, HL + LD A, H + ADD A, 0x40 ; A=0x80 + LD B, 0 + +.fill_scrn: + LD (HL), B + INC HL + CP H + JP NZ, .fill_scrn ; fill while HL<0x8000 + + EX DE, HL + LD A, (M_VARS.cur_palette) + LD B, A ; B = current palette + LD A, (M_VARS.screen_mode) + AND 0x3 ; color? + LD A, 0x0 + JP NZ, .mono_mode + LD A, 01000000b +.mono_mode: + OR B + ; Restore mode and palette + OUT (VID_DD67PB), A + + ; And set cursor to home position + +; -------------------------------------------------- +; Set cursor to 0,0 and close VRAM access +; Inp: HL -> cursor_row +; -------------------------------------------------- +m_cursor_home: + XOR A + NOP + NOP + LD (HL), A + INC HL + XOR A + LD (HL), A + DEC HL + ;XOR A + LD A, 0 + ; Disable VRAM access + OUT (SYS_DD17PB), A + RET + +; Clear only 20 rows +m_clear_20_rows: + ; take row shift in account + LD A, (M_VARS.row_shift) + LD L, A + LD C, 20 + + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.next_row: + LD H, 0x40 ; HL = 0x4000 + shift_row + LD B, 64 ; 64 bytes at row + XOR A +.next_col: + LD (HL), A + INC H ; next column + DEC B + JP NZ, .next_col + INC L ; next row + DEC C + JP NZ, .next_row + ; Disabe VRAM access + LD A, 0 + OUT (SYS_DD17PB), A + JP m_cursor_home + +; -------------------------------------------------- +; Draw cursor at current cursor position +; if not hidden +; -------------------------------------------------- +m_draw_cursor: + LD A, (M_VARS.screen_mode) + AND 0x4 ; check hidden cursor bit + RET NZ ; return if hidden + LD A, (M_VARS.screen_mode) + AND 0x3 ; check color mode (40 column mode 6x7 font) + JP NZ, .dc_mode_64 + + EX DE, HL + LD HL, (M_VARS.cursor_row) + LD A, H ; cursor column + CP 40 ; > 40? + EX DE, HL + RET P ; ret if column out of screen + + PUSH HL + EX DE, HL + CALL calc_addr_40 + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; previous address + DEC H + DEC H + INC L + LD C, 7 ; cursor size + ; build masks + LD A, B + OR B + JP Z, .dc_rt2 + DEC B + JP Z, .dc_mid + DEC B + JP Z, .dc_lt + JP .dc_rt1 +.dc_rt2: + INC H + INC H + LD DE, 0x001f + JP .dc_put +.dc_mid: + LD DE, 0x07c0 + JP .dc_put +.dc_lt: + LD DE, 0x01f0 + JP .dc_put +.dc_rt1: + LD DE, 0x007c + +.dc_put: + ; xor cursor mask with VRAM[HL] value + ; left bytes + LD A, (HL) + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR E + LD (HL), A + ; right bytes + INC H + LD A, (HL) + XOR D + LD (HL), A + INC H + LD A, (HL) + XOR D + LD (HL), A + ; next cursor row address + INC L + DEC H + DEC H + DEC H + DEC C + JP NZ, .dc_put ; draw next cursor row if c>0 + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + RET + + ; draw cursor in 64 column mode +.dc_mode_64: + CP 3 ; screen_mode = 3 - 80 rows + JP Z, .dc_mode_80 + EX DE, HL + LD HL, (M_VARS.cursor_row) ; H - col, L - row + ; take into account the vertical shift + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + ; + LD A, H + CP 64 ; check column + EX DE, HL + RET P ; return if column out of screen + EX DE, HL + ; calc VRAM address + ADD A, 0x40 + LD H, A + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD BC, 0x7f08 ; B=01111111b - mask, C=8 - cursor size +.cur_64_next: + ; xor with VRAM content + LD A, (HL) + XOR B + LD (HL), A + ; next row address + INC L + DEC C + JP NZ, .cur_64_next + EX DE, HL + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + + ; draw cursor in 80 column mode +.dc_mode_80 + EX DE, HL + LD HL, (M_VARS.cursor_row) + + LD A, H + CP 80 + EX DE, HL + RET P ; return if column > 80 + + PUSH HL + CALL calc_addr_80 + LD C, 7 ; cursor size + INC L + + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; mask + LD A, B + OR A + LD B, 0x1f + JP Z, .dc_1_byte + DEC A + LD DE, 0xc007 + JP Z, .dc_2_byte + DEC A + LD DE, 0xf001 + JP Z, .dc_2_byte + LD B, 0x7c + DEC H + JP .dc_1_byte ; TODO: unused + +.dc_1_byte: + ; xor with VRAM byte + LD A, (HL) + XOR B + LD (HL), A + INC L + DEC C + JP NZ, .dc_1_byte + JP .dc_80_end + +.dc_2_byte: + ; xor with previous byte + DEC H + LD A, (HL) + XOR D + LD (HL), A + ; xor with current byte + INC H + LD A, (HL) + XOR E + LD (HL), A + ; next cursor address + INC L + DEC C + JP NZ, .dc_2_byte + +.dc_80_end: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + RET + +; -------------------------------------------------- +; If ESC character, turn esc_mode ON +; Inp: A - ASCII symbol +; -------------------------------------------------- +m_handle_esc_code: + CP ASCII_ESC + JP NZ, m_handle_control_code + ; turn on ESC mode for next chars + LD HL, M_VARS.esc_mode + LD (HL), 0x1 ; turn on ESC mode + INC HL + LD (HL), 0xff ; esc_cmd = 0xff + RET + +; -------------------------------------------------- +; Handle one byte ASCII control code +; Inp: A - ASCII symbol +; -------------------------------------------------- +m_handle_control_code: + CP ASCII_BELL + JP Z, m_beep + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + PUSH AF + CALL m_draw_cursor + LD A, (M_VARS.screen_mode) + AND 0x08 ; 20-rows mode? + JP Z, handle_cc_common ; jump for normal screen modes + + ; for hidden cursor modes + POP AF + CP ASCII_TAB ; TAB + JP Z, m20_tab + CP ASCII_BS ; BKSP + JP Z, m20_bksp + CP ASCII_CAN ; Cancel + JP Z, m40_rt + CP ASCII_US ; ASCII Unit separator + JP Z, m_clear_20_rows + CP ASCII_LF ; LF + JP Z, m2_lf + CP ASCII_CR ; CR + RET NZ ; ret on unknown + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; Handle cursor for 40x25, 64x25, 80x25 modes +; -------------------------------------------------- +handle_cc_common: + POP AF + CP ASCII_US + JP Z, m_clear_screen + CP ASCII_FF + JP Z, m_cursor_home + PUSH AF + LD A, (M_VARS.screen_mode) + AND 3 ; check for color modes + JP NZ, .handle_cc_mono + ; 32x25 text mode + POP AF + CP ASCII_TAB ; cursor right +8 + JP Z, m20_tab + CP ASCII_BS ; cursor left + JP Z, m40_bksp + CP ASCII_CAN ; cursor right + JP Z, m40_rt + CP ASCII_EM ; cursor up + JP Z, m40_up + CP ASCII_SUB + JP Z, m40_lf ; cursor down + CP ASCII_LF + JP Z, m40_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 ; move cursor to first column for CR + DEC HL + RET + +; -------------------------------------------------- +; Handle control chars for 64x25 or 80x25 modes +; -------------------------------------------------- +.handle_cc_mono: + LD A, (M_VARS.screen_mode) + CP 3 + JP Z, handle_cc_80x25 + CP 7 + JP Z, handle_cc_80x25 + ; 64x25 screen mode + POP AF + CP ASCII_TAB + JP Z, m64_tab + CP ASCII_BS + JP Z, m64_bs + CP ASCII_CAN + JP Z, m64_rt + CP ASCII_EM + JP Z, m64_up + CP ASCII_SUB + JP Z, m64_lf + CP ASCII_LF + JP Z, m64_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; Handle control chars for 80x25 modes +; -------------------------------------------------- +handle_cc_80x25: + POP AF + CP ASCII_TAB + JP Z, m80_tab + CP ASCII_BS + JP Z, m80_bs + CP ASCII_CAN + JP Z, m80_rt + CP ASCII_EM + JP Z, m80_up + CP ASCII_SUB + JP Z, m80_lf + CP ASCII_LF + JP Z, m80_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; +; -------------------------------------------------- +m_beep: + LD HL, (M_VARS.beep_duration) + EX DE, HL + LD HL, (M_VARS.beep_period) + LD A, 00110110b ; TMR#0 LSB+MSB Square Wave Generator + OUT (TMR_DD70CTR), A + LD A, L ; LSB + OUT (TMR_DD70C1), A + LD A, H ; MSB + OUT (TMR_DD70C1), A + LD A, (M_VARS.strobe_state) + LD B, A +m_bell_cont: + LD A, D ; DE=duration + OR E + RET Z ; ret if enough + DEC DE + LD A, B + XOR BELL_PIN + LD B, A + OUT (DD67PC), A ; Invert bell pin +m_bell_wait_tmr1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; 0x10 + JP NZ, m_bell_wait_tmr1 + LD A, B + XOR BELL_PIN ; Flip pin again + LD B, A + OUT (DD67PC), A +m_bell_wait_tmr2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP Z,m_bell_wait_tmr2 + JP m_bell_cont + + +; ------------------------------------------------------ +; 5 Set cursor position +; ------------------------------------------------------ +esc_set_cursor: + CALL m_draw_cursor + LD DE, M_VARS.esc_param + LD HL, M_VARS.cursor_col + INC DE + LD A, (DE) ; column + SUB 32 + LD B, A + LD A, (M_VARS.screen_mode) + CP 3 + JP Z, .mode_80 + CP 7 + JP Z, .mode_80 + OR A + JP Z, .mode_40 + CP 4 + JP Z, .mode_40 + ; mode 64x25 + LD A, B + CP 64 + JP M, .common + LD A, 64 + JP .common + ; mode 40x25 +.mode_40: + LD A, B + CP 40 + JP M, .common + LD A, 40 + JP .common + ; mode 80x25 +.mode_80: + LD A, B + CP 80 + JP M, .common + LD A, 80 +.common: + LD (HL), A + DEC DE + DEC HL + LD A, (DE) + SUB 32 + CP 24 + JP C, esc_le_24 + LD A, 24 +esc_le_24: + LD B, A + ADD A, A + ADD A, A + ADD A, B + ADD A, A + LD (HL), A + CALL m_draw_cursor ; TODO change call+ret to jp + RET ; + +; ------------------------------------------------------ +; 6n Set video mode or cursor visibility +; Inp: n is +; 0 - C 32x25 with cursor; 0000 +; 1 - M 64x25 with cursor; 0001 +; 2 - M 64x25 with cursor; 0010 +; 3 - M 80x25 with cursor; 0011 +; 4 - C 32x25 no cursor; 0100 +; 5 - M 64x25 no cursor; 0101 +; 6 - M 64x25 no cursor; 0110 +; 7 - M 80x25 no cursor; 0111 +; 8 - M 20rows mode 1000 +; 9 - hide cursor 1001 +; 10 - show cursor 1010 +; ------------------------------------------------------ +esc_set_vmode: + LD HL, M_VARS.screen_mode + LD A, (M_VARS.cur_palette) + LD B, A + LD A, (M_VARS.esc_param) ; first parameter - video mode + AND 0xf + CP 11 + RET NC ; return if not valid input parameter + CP 9 + JP Z, .cursor_hide + CP 10 + JP Z, .cursor_show + LD (HL), A ; store new mode + CP 4 + JP Z, .set_color_mode + AND 0x3 ; monochrome (80x25, 64x25) mode? + LD A, 0 ; mode 512x254 mono + JP NZ, .skip_for_mono_mode + ; mode=0 or 4 -> 256x256px color +.set_color_mode: + LD A, 0x40 ; color mode +.skip_for_mono_mode: + OR B ; color mode with palette + OUT (VID_DD67PB), A ; configure screen mode + + LD HL, M_VARS.cursor_row + CALL m_clear_screen + +.draw_cursor: + CALL m_draw_cursor ; TODO change call+ret to jp + RET + +.cursor_hide: + LD A, (HL) ; screen_mode + OR 00000100b ; cursor hide + LD (HL), A + LD HL, M_VARS.cursor_row + JP .draw_cursor + +.cursor_show: + LD A, (HL) ; screen_mode + AND 11111011b ; cursor show + LD (HL), A + JP .draw_cursor + + +; ------------------------------------------------------ +; 4n n=1..4 Set drawing color +; ------------------------------------------------------ +esc_set_color: + LD A, (M_VARS.esc_param) +m_set_color: + AND 0x3 + RRA + LD B, A + LD A, 0x0 ; TODO: unused + SBC A, A + LD (M_VARS.curr_color), A + LD A, B + DEC A + CPL + LD (M_VARS.curr_color+1), A + RET + +;--------------------------------------------------- +; Print symbol or print sprite at X,Y coordinates +; Inp: param x,y +; C - character or sprite_no to draw +;--------------------------------------------------- +m_print_at_xy: + ; check video mode + LD A, (M_VARS.screen_mode) + AND 0x3 ; color? + JP NZ, esc_exit ; exit for mono modes + + LD A, C + AND 0x7f + LD C, A ; C = C with 7th bit reset + CP 0x1 + JP Z, .sprites_en ; enable sprite mode + + CP ASCII_SP + JP M, mode2_exit ; codes 0..31 - turm off game_mode + + ; check X, Y range to prevent drawing symbols out of screen + LD HL, M_VARS.esc_param + LD A, (HL) + LD E, A + ADD A, 8 + JP C, mode2_exit ; exit if esc_param[0]>247 + LD (HL), A + INC HL ; HL -> esc_param[1] + LD A, 247 + CP (HL) + JP C, mode2_exit ; exit if esc_param[1]>247 + ; calculate X,Y pixel address in VRAN + LD D, (HL) + CALL calc_px_addr + ; HL - address, B - pixel pos in byte + LD A, L + SUB 8 + LD L, A + PUSH HL ; save address + + LD A, (M_VARS.esc_var2) + OR A + JP NZ, .mode_sp + + ; font + LD A, C + CALL m_get_glyph + LD C, 7 + POP DE + INC E + JP .out_sp + + ; sprite mode +.mode_sp: + LD A, C + SUB 32 + CP 35 + JP NC, co_ex_l08 + + ; Calc sprite address + LD L, A ; HL=A - sprite_no + XOR A + LD H, A + ADD HL, HL + ADD HL, HL + ADD HL, HL ; HL=HL*8 + LD DE, game_sprite_tab + ADD HL, DE ; HL -> sprite + LD C, 8 ; bytes count + POP DE + + ; Out sprite + ; DE -> VRAM address + ; C - height +.out_sp: + LD A, (M_VARS.esc_param+2) + DEC A + JP Z, out_no_xor + +.next_line: + PUSH HL + ; Access Video RAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD L, (HL) ; load from table + LD H, 0x0 + LD A, B + OR A + JP Z, .l05 +.l04: + ADD HL, HL + DEC A + JP NZ, .l04 +.l05: + EX DE, HL + LD A, (M_VARS.curr_color) + AND E + XOR (HL) + LD (HL), A + INC H + INC H + LD A, (M_VARS.curr_color) + AND D + XOR (HL) + LD (HL), A + DEC H + LD A, (M_VARS.curr_color+1) + AND E + XOR (HL) + LD (HL), A + INC H + INC H + LD A, (M_VARS.curr_color+1) + AND D + XOR (HL) + LD (HL), A + DEC H + DEC H + DEC H + INC L + EX DE, HL + ; Disable VRAM + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + INC HL + DEC C + JP NZ, .next_line + RET + +.sprites_en: + LD (M_VARS.esc_var2), A + RET + +mode2_exit: + XOR A + LD (M_VARS.esc_var2), A + JP esc_exit + +co_ex_l08: + POP DE + JP mode2_exit + +out_no_xor: + PUSH HL + ; Acess to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD L, (HL) + LD H, 0x0 + LD A, B + OR A + JP Z, .l11 +.l10: + ADD HL, HL + DEC A + JP NZ, .l10 +.l11: + EX DE, HL + PUSH BC + LD A, (M_VARS.curr_color) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + LD A, (M_VARS.curr_color+1) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + DEC H + DEC H + INC L + EX DE, HL + POP BC + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP HL + INC HL + DEC C + JP NZ, out_no_xor + RET + +game_sprite_tab: + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; 0x00 + DB 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E ; 0x01 + DB 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E ; 0x02 + DB 0x00, 0x08, 0x08, 0x14, 0x63, 0x14, 0x08, 0x08 ; 0x03 + DB 0x36, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00 ; 0x04 + DB 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00 ; 0x05 + DB 0x1C, 0x3E, 0x1C, 0x7F, 0x7F, 0x6B, 0x08, 0x1C ; 0x06 + DB 0x08, 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x08, 0x1C ; 0x07 + DB 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18 ; 0x08 + DB 0x18, 0xDB, 0x3C, 0xE7, 0xE7, 0x3C, 0xDB, 0x18 ; 0x09 + DB 0xE7, 0xE7, 0x00, 0x7E, 0x7E, 0x00, 0xE7, 0xE7 ; 0x0a + DB 0x7E, 0x81, 0x81, 0xFF, 0xFF, 0x81, 0x81, 0x7E ; 0x0b + DB 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18 ; 0x0c + DB 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00 ; 0x0d + DB 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00 ; 0x0e + DB 0x00, 0x08, 0x0C, 0xFE, 0xFE, 0x0C, 0x08, 0x00 ; 0x0f + DB 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 ; 0x10 + DB 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 ; 0x11 + DB 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0 ; 0x12 + DB 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F ; 0x13 + DB 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ; 0x14 + DB 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF ; 0x15 + DB 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F ; 0x16 + DB 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0 ; 0x17 + DB 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 ; 0x18 + DB 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33 ; 0x19 + DB 0x70, 0x08, 0x76, 0xFF, 0xFF, 0xFF, 0x7E, 0x18 ; 0x1a + DB 0xC3, 0xDB, 0xDB, 0x18, 0x18, 0xDB, 0xDB, 0xC3 ; 0x1b + DB 0xFC, 0xCC, 0xFC, 0x0C, 0x0C, 0x0E, 0x0F, 0x07 ; 0x1c + DB 0xFE, 0xC6, 0xFE, 0xC6, 0xC6, 0xE6, 0x67, 0x03 ; 0x1d + DB 0x18, 0x3C, 0x3C, 0x18, 0x7E, 0x18, 0x24, 0x66 ; 0x1e + DB 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 ; 0x1f + DB 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 ; 0x20 + DB 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 0x21 + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF ; 0x22 + +; -------------------------------------------------- +; Calculate address of pixel in Video RAM +; Inp: DE - Y, X +; Out: HL - address +; B - offset in byte +; -------------------------------------------------- +calc_px_addr: + ; take into account the vertical displacement + LD A, (M_VARS.row_shift) + SUB D + DEC A + LD L, A + + LD A, E + AND 0x07 ; X mod 8 - offset in byte + LD B, A + + LD A, E + RRA + RRA + AND 00111110b + ADD A, 0x40 ; VRAM at 0x4000 + LD H, A + RET + + +;--------------------------------------------------- +; Draw filled rectanger +; Inp: esc param X1,Y2,X2,Y2 +; -------------------------------------------------- +esc_draw_fill_rect: + LD HL, M_VARS.esc_param + LD E, (HL) ; E=X1 + INC HL + LD C, (HL) ; C=Y1 + INC HL + INC HL + LD D, (HL) ; D=Y2 + LD A, D + SUB C ; delta Y + JP NZ, .non_zero_h + INC A ; 1 as minimum +.non_zero_h: + LD C, A ; C = height + ; DE = Y2, X1 + CALL calc_px_addr + ; HL -> videomem offset, b - pixel offset + + ; build pixel mask + XOR A +.shift_mask_l: + SCF + RLA + DEC B + JP P, .shift_mask_l + RRA + LD (M_VARS.pixel_mask_l), A + CPL ; invert + LD (M_VARS.pixel_mask_l_i), A + LD A, (M_VARS.esc_param+2) ; X2 + AND 0x7 ; 0..7 + LD B, A + XOR A +.shift_mask_r: + SCF + RLA + DEC B + JP P, .shift_mask_r + LD (M_VARS.pixel_mask_r_i), A + LD B, C + ; calc end address + LD A, (M_VARS.esc_param+2) ; X2 + RRA + RRA + AND 00111110b + ADD A, 0x40 + SUB H + RRCA + LD C, A ; C - width + INC B + LD A, (M_VARS.esc_param+4) + DEC A + JP NZ, .rectangle_xor + LD A, (M_VARS.pixel_mask_r_i) + CPL + LD (M_VARS.pixel_mask_r), A + LD D, A + LD A, (M_VARS.pixel_mask_l) + LD E, A + ; draw B horisontal lines +.next_line: + PUSH DE + PUSH HL + PUSH BC + CALL draw_line_h + POP BC + POP HL + POP DE + INC L + DEC B + JP NZ, .next_line + RET + + ; draw B horisontal lines (xor) +.rectangle_xor: + LD A, (M_VARS.pixel_mask_r_i) + LD (M_VARS.pixel_mask_r), A + LD D, A + LD A, (M_VARS.pixel_mask_l_i) + LD E, A + ; Access to VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.edf_l6: + PUSH DE + PUSH HL + PUSH BC + LD A, C + OR A + JP NZ, .w_ne_0 ; jump if width != 0 + LD A, E ; merge masks E=E or D + OR D +.next_8px: + LD E, A +.w_ne_0: + LD B, E ; B - mask + EX DE, HL + LD HL, (M_VARS.curr_color) ; color + EX DE, HL + ; Set pixels - VideoRAM[HL] = color xor VideoRAM[HL] + LD A, E + AND B + XOR (HL) + LD (HL), A + ; And next byte + INC H + LD A, D + AND B + XOR (HL) + LD (HL), A + ; ---------- + INC H + LD A, C + OR A + JP Z, .complete + DEC C + ; right tail of line, use right mask +.r_mask: + LD A, (M_VARS.pixel_mask_r) + JP Z, .next_8px + ; full 8 bits without mask at middle of line +.next_full: + LD A, (HL) + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + LD (HL), A + INC H + DEC C + JP NZ, .next_full + JP .r_mask +.complete: + POP BC + POP HL + POP DE + INC L + DEC B + JP NZ, .edf_l6 + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +;--------------------------------------------------- +; Paint screen +; Inp: params X,Y,Color,repColor +;--------------------------------------------------- +esc_paint: + ; Save stack + LD HL, 0x0 + ADD HL, SP + LD (M_VARS.paint_sp_save), HL + + ; Set our own stack + LD HL, M_VARS.paint_stack ; TODO: Z80 LD SP,var i800 - LXI SP,nn + LD SP, HL + + ; save current color + LD HL, (M_VARS.curr_color) + LD (M_VARS.tmp_color), HL + + ; set color from param 3 + LD A, (M_VARS.esc_param+2) + DEC A + CALL m_set_color + + ; color to replace, from param 4 + LD A, (M_VARS.esc_param+3) + DEC A + LD (M_VARS.cmp_color), A + + ; HL - Y,X + LD A, (M_VARS.esc_param) + LD L, A + LD A, (M_VARS.esc_param+1) + LD H, A + LD (M_VARS.paint_y), A + + LD A, (M_VARS.esc_param+4) ; 0 - full fill, 1 - fast fill + DEC A + LD (M_VARS.esc_param), A + + LD A, 0x2 + LD (M_VARS.paint_var5), A ; task_no=2 + + EX DE, HL + CALL calc_px_addr + LD (M_VARS.esc_param+1), HL ; temporary ctore address of start fill point + ; make mask + LD A, 10000000b +.l1: + RLCA + DEC B + JP P, .l1 + + LD B, A + LD (M_VARS.esc_param+3), A ; store mask + + ; find left border + LD A, (M_VARS.cmp_color) + LD C, A + LD D, E ; D = X + CALL paint_find_left + ; find right border + LD HL, (M_VARS.esc_param+1) ; restore HL + LD A, (M_VARS.esc_param+3) ; restore mask + LD B, A + CALL paint_find_right + ; + LD HL, 0x0 + PUSH HL + PUSH HL + ; + LD A, (M_VARS.esc_param) ; A = fill mode + OR A + JP Z, ep_fm_0 + ; push fill task parameters + LD A, (M_VARS.paint_var5) + DEC A ; task_no-1 + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + +ep_fm_0: + ; push fill task parameters + LD A, (M_VARS.paint_var5) ; task_no + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + JP paint_task ; exec task + +ep_task_end: + LD A, (M_VARS.cmp_color) + LD C, A ; color to compare + + LD A, (M_VARS.esc_param) ; fill mode 0 - full, 1 - fast + OR A + JP NZ, ep_f_fast + + LD A, 0x2 + LD (M_VARS.paint_var7), A + LD A, (M_VARS.paint_var2) + CP 2 + JP Z, ep_l4 + JP ep_l5 ; TODO: change to one JP NZ + +ep_l4: + LD A, 1 + LD (M_VARS.paint_var5), A ; task_no? + JP ep_l8 + +ep_l5: + LD A, 2 + LD (M_VARS.paint_var5), A + JP ep_l11 + +ep_l6: + LD A, (M_VARS.paint_var7) + OR A + JP Z, paint_task + LD A, (M_VARS.paint_var2) + CP 2 + JP Z, ep_l5 ; TODO: change to one JP NZ + JP ep_l4 + +ep_f_fast: + LD A, (M_VARS.paint_var2) + LD (M_VARS.paint_var5), A + CP 1 ; TODO: DEC A - save 1b 3t + JP Z, ep_l8 ; TODO: change to one JP NZ + JP ep_l11 + +ep_l8: + LD A, (M_VARS.paint_var3) + LD D, A + LD A, (M_VARS.paint_var1) + LD E, A + LD HL, (M_VARS.esc_param+1) + LD A, (M_VARS.esc_param+3) + LD B, A + LD A, (M_VARS.paint_var4) + DEC A + JP Z, ep_l10 + LD (M_VARS.paint_y), A + INC L + CALL paint_find_next_right + JP Z, ep_l10 + LD HL, (M_VARS.esc_param+4) + LD A, (M_VARS.esc_param+6) + LD B, A + INC L + CALL paint_find_next_left + JP Z, ep_l10 + LD A, (M_VARS.esc_param) + OR A + JP NZ, ep_l9 + JP ep_l12 + +ep_l9: + LD A, (M_VARS.paint_var5) + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + JP paint_task + +ep_l10: + LD A, (M_VARS.esc_param) + OR A + JP NZ, paint_task + LD A, (M_VARS.paint_var7) + DEC A + LD (M_VARS.paint_var7), A + JP ep_l6 + +ep_l11: + LD A, (M_VARS.paint_var3) + LD D, A + LD A, (M_VARS.paint_var1) + LD E, A + LD HL, (M_VARS.esc_param+1) + LD A, (M_VARS.esc_param+3) + LD B, A + LD A, (M_VARS.paint_var4) + INC A + CP 0xff + JP Z, ep_l10 + LD (M_VARS.paint_y), A + DEC L + CALL paint_find_next_right + JP Z, ep_l10 + LD HL, (M_VARS.esc_param+4) + LD A, (M_VARS.esc_param+6) + LD B, A + DEC L + CALL paint_find_next_left + JP Z, ep_l10 + LD A, (M_VARS.esc_param) + OR A + JP NZ, ep_l9 + JP ep_l12 + +; --------------------------------------------------- +; +; --------------------------------------------------- +paint_find_next_right: + CALL get_pixel + JP NZ, .l1 + CALL paint_find_left + LD A, 0xff + OR A + RET + +.l1: + LD A, D + CP E + RET Z + INC D + LD A, B + RLCA + LD B, A + JP NC, .l2 + INC H + INC H +.l2: + CALL get_pixel + JP NZ, .l1 + LD A, 0xff + OR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +paint_find_next_left: + CALL get_pixel + JP NZ, .l1 + CALL paint_find_right + LD A, 0xff + OR A + RET +.l1: + LD A, E + CP D + RET Z + DEC E + LD A, B + RRCA + LD B, A + JP NC, .l2 + DEC H + DEC H +.l2: + CALL get_pixel + JP NZ, .l1 + LD A, 0xff + OR A + RET + +ep_l12: + LD A, D + LD (M_VARS.pixel_mask_r), A + LD A, (M_VARS.paint_var5) + LD D, A + PUSH DE + LD A, (M_VARS.pixel_mask_r) + CP E + JP NZ, ep_l13 + LD L, A + LD A, (M_VARS.paint_y) + LD H, A + PUSH HL + JP ep_l16 +ep_l13: + LD D, E + CALL paint_find_left + LD E, D + LD A, (M_VARS.paint_y) + LD D, A + PUSH DE + LD A, (M_VARS.pixel_mask_r) + LD D, A + CP E + JP Z, ep_l16 +ep_l14: + DEC E + LD A, B + RRCA + LD B, A + JP NC, ep_l15 + DEC H + DEC H +ep_l15: + CALL get_pixel + JP NZ, ep_l14 + JP ep_l12 +ep_l16: + JP ep_l10 + +; --------------------------------------------------- +; Find rightmost pixel to fill +; In/Out: E = x_right +; HL - current pixel address +; B - pixel mask +; --------------------------------------------------- +paint_find_right: + LD A, E + CP 0xff + RET Z ; return if X=right border + INC E ; x=x+1 + ; rotate pixel mask right + LD A, B + RLCA + LD B, A + JP NC, .in_byte + ; inc addr+2 (2 byte per 8 pixels) + INC H + INC H + +.in_byte: + CALL get_pixel + JP Z, paint_find_right ; find until same color + ; border found, x-1 + DEC E + ; rotate mask back 1 px + LD A, B + RRCA + LD B, A + RET NC + ; addr-2 if previous byte + DEC H + DEC H + RET + +; --------------------------------------------------- +; Find leftmost pixel to fill +; In/Out: D = x_left +; HL - current pixel address +; B - pixel mask +; --------------------------------------------------- +paint_find_left: + LD A, D + OR A + RET Z ; return if x=0 + + DEC D ; x-1 + LD A, B + RRCA ; rotate mask to right + LD B, A + JP NC, .in_byte + DEC H ; addr-2 (2 byte for 8px) + DEC H + +.in_byte: + CALL get_pixel + JP Z, paint_find_left ; repeat until same color + + INC D ; border found, x+1 + ; mask rotate right + LD A, B + RLCA + LD B, A + RET NC + ; if CF, inc address+2 + INC H + INC H + RET + +; --------------------------------------------------- +; Inp: HL - address +; B - pixel mask +; C - color to compare +; Out: A - 0,1,2 +; ZF - set if color match +; --------------------------------------------------- +get_pixel: + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; get pixel and mask + LD A, (HL) + AND B + JP NZ, .bit1_set + INC H + LD A, (HL) + DEC H + AND B + JP NZ, .bit2_set + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + CP C + RET + +.bit1_set: + INC H + LD A, (HL) + DEC H + AND B + JP NZ, .bit12_set + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 0x1 + CP C + RET + +.bit2_set: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 0x2 + CP C + RET + +.bit12_set: + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 3 + CP C + RET + +paint_task: + POP HL ; L=x0, H=Y + LD (M_VARS.paint_var3), HL + EX DE, HL + + POP HL ; L=x1, H=mode + LD A, H + OR A + JP Z, paint_exit ; jump for mode=0 + + ; calc leftmost pixel address, mask for draw horisontal line + LD (M_VARS.paint_var1), HL + CALL calc_px_addr + LD (M_VARS.esc_param+1), HL + LD C, B + LD A, 0x80 +.lmp_mask: + RLCA + DEC B + JP P, .lmp_mask + LD (M_VARS.esc_param+3), A + ; calc rightmos pixel address and mask + LD A, (M_VARS.paint_var1) + LD E, A + LD A, (M_VARS.paint_var4) + LD D, A + CALL calc_px_addr + LD (M_VARS.esc_param+4), HL + LD D, B + LD A, 0x80 +.rmp_mask: + RLCA + DEC B + JP P, .rmp_mask + LD (M_VARS.esc_param+6), A + LD A, (M_VARS.esc_param+3) ; TODO: unused code + + XOR A +.lmi_mask: + SCF + RLA + DEC C + JP P, .lmi_mask + RRA + LD E, A ; E - left inv mask + + XOR A +.rmi_mask: + SCF + RLA + DEC D + JP P, .rmi_mask + CPL + LD D, A ; D - right inv mask + + LD (M_VARS.pixel_mask_r), A + LD HL, (M_VARS.esc_param+1) ; HL -> lext pix address + LD A, (M_VARS.esc_param+5) ; right pix address (low byte) + SUB H ; delta x + RRCA ; 2 byte for 8 pix + LD C, A ; C - line width + CALL draw_line_h + JP ep_task_end + +paint_exit: + LD HL, (M_VARS.tmp_color) ; restore previous current color + LD (M_VARS.curr_color), HL + LD HL, (M_VARS.paint_sp_save) ; restore previous stack + LD SP, HL + RET + +;--------------------------------------------------- +; Draw horizontal line +; Inp: C - width +; DE - left & right pixel mask +; HL - address of first byte of line +;--------------------------------------------------- +draw_line_h: + ; Access to VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, C + OR A + JP NZ, .width_ne0 + LD A, E ; join left and right masks + OR D +.next_byte: + LD E, A +.width_ne0: + LD B, E + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Get pixels, apply colors + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A ; store first + ; Same for second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; move to next byte + INC H + LD A, C + OR A + JP Z, .complete + DEC C +.r_mask: + ; use right mask for last right byte + LD A, (M_VARS.pixel_mask_r) + JP Z, .next_byte +.full_8: + LD (HL), E + INC H + LD (HL), D + INC H + DEC C + JP NZ, .full_8 + JP .r_mask +.complete: ; TODO: duplicate close_vram_ret + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +;--------------------------------------------------- +; 2x1y1x2y2 Draw Line +;--------------------------------------------------- +esc_draw_line: + LD HL, M_VARS.esc_param + LD E, (HL) ; E=X1 + INC HL + LD D, (HL) ; D=Y1 + INC HL + LD A, (HL) + INC HL + LD H, (HL) ; H=Y2 + LD L, A ; L=X2 + CP E + JP C, .x1_le_x2 + EX DE, HL ; exchange if X1>X2 +.x1_le_x2: + LD (M_VARS.esc_param), HL ; store x1,y1 back + LD A, E + SUB L + LD L, A ; L - width + LD A, D + SUB H + LD H, A ; H - height + PUSH AF + JP NC, .pos_height + ; change sign + CPL + INC A + LD H, A +.pos_height: + EX DE, HL + LD HL, (M_VARS.esc_param) + EX DE, HL + JP Z, height0 + LD A, L + OR A + JP Z, .width0 + LD B, A + POP AF + LD A, 0x0 + ADC A, A + LD (M_VARS.esc_param+4), A + ; HL = E/B height/width + LD E, H + LD C, 16 + LD D, 0 +.next_16: + ADD HL, HL + EX DE, HL + ADD HL, HL + EX DE, HL + LD A, D + JP C, .edl_l4 + CP B + JP C, .edl_l5 +.edl_l4: + SUB B + LD D, A + INC HL +.edl_l5: + DEC C + JP NZ, .next_16 + LD DE, 0x0 + PUSH DE + ; save result at stack + PUSH HL + + LD HL, (M_VARS.esc_param) ; x1,y1 + EX DE, HL + LD C, B + CALL calc_px_addr + ; HL - address, B - offset in byte + ; make mask + LD A, 10000000b +.roll_l: + RLCA + DEC B + JP P, .roll_l + CPL + LD B, A ; b - inv mask + +.edl_l7 + POP DE + EX (SP), HL ; save HL on top of stack + LD A, H + ADD HL, DE + SUB H + CPL + INC A + EX (SP), HL + PUSH DE + PUSH BC + LD C, A + EX DE, HL + + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Access VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD A, (M_VARS.esc_param+4) ; sign of delta Y + OR A + JP NZ, .next_down +.next_up: + ; firs byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, .is_last + DEC C + ; draw up + DEC L + JP .next_up +.next_down: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; + DEC H + LD A, C + OR A + JP Z, .is_last + DEC C + ; draw down + INC L + JP .next_down +.is_last: + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP BC + LD A, B + ; <<1px + SCF + RLA + JP C, .edl_l11 + RLA + INC H + INC H +.edl_l11 + LD B, A + DEC C + JP NZ, .edl_l7 + POP HL + POP HL + RET + +; -------------------------------------------------- +; draw vertical line +; Inp: DE - YX +; L - length +; -------------------------------------------------- +.width0 + LD C, H + CALL calc_px_addr + + ; make pixel mask + LD A, 10000000b +.edl_l13: + RLCA + DEC B + JP P, .edl_l13 + CPL + LD B, A + + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + POP AF + + ; Enable VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + JP C, .next_row_down + +.next_row_up: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next Y + DEC H + LD A, C + OR A + JP Z, close_vram_ret + DEC C + ; dec row + DEC L + JP .next_row_up + +.next_row_down: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next address + DEC H + LD A, C + OR A + JP Z, close_vram_ret + DEC C + ; inc row + INC L + JP .next_row_down + +close_vram_ret + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; draw horizontal line +; Inp: DE - YX +; L - length +; -------------------------------------------------- +height0 + POP AF + LD C, L + LD A, L + OR A + JP NZ, .len_ne0 + INC C ; length 1 at least +.len_ne0: + CALL calc_px_addr + ; make pixel mask + LD A, 10000000b +.edl_l19 + RLCA + DEC B + JP P, .edl_l19 + CPL + LD B, A + + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + +.next_col: + ; set 1st byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; set 2nd byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next byte + DEC H + ; next (right) horizontal pixel + LD A, B + SCF + RLA + JP C, .edl_l21 + RLA + INC H + INC H +.edl_l21 + LD B, A + DEC C + JP NZ, .next_col + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; ESC Draw Dot +; -------------------------------------------------- +esc_draw_dot: + LD HL, (M_VARS.esc_param) + EX DE, HL + CALL calc_px_addr + LD A, 0x80 +edd_l1 + RLCA + DEC B + JP P, edd_l1 + LD B, A + LD A, (M_VARS.esc_param+2) + CP 0x3 + JP Z, edd_ep_task_end + LD A, B + CPL + LD B, A + LD A, (M_VARS.esc_param+2) + CP 0x2 + JP Z, edd_ep_fm_0 + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + AND B + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, 0x0 + OUT (SYS_DD17PB), A + RET +edd_ep_fm_0 + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + LD A, 0x0 + OUT (SYS_DD17PB), A + RET +edd_ep_task_end + CALL get_pixel + LD (M_VARS.esc_var3), A + RET + +; -------------------------------------------------- +; +; -------------------------------------------------- +esc_picture: + LD HL, (M_VARS.esc_param+3) + LD A, (HL) + CP ':' + RET NZ + INC HL + LD E, (HL) + INC HL + LD D, (HL) + INC HL + LD A, (HL) + LD (M_VARS.esc_var0), A + INC HL + LD A, (HL) + LD (M_VARS.esc_var1), A + INC HL + PUSH HL + LD C, (HL) + INC HL + LD B, (HL) + INC HL + INC HL + EX DE, HL + PUSH DE + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL pict_sub1 + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP DE + POP HL + LD (HL), C + INC HL + LD (HL), B + CALL pict_sub2 ; TODO: replace call+ret to jp; + RET + +pict_sub1: + LD A, (M_VARS.esc_param) + CP ASCII_EM + JP Z, gih_up + CP ASCII_CAN + JP Z, gih_rt + CP ASCII_SUB + JP Z, gih_ctrl_z + CP ASCII_BS + JP Z, gih_bs + CP ASCII_US + JP Z, pict_clr + RET + +pict_clr: + LD L, C + LD H, B + LD A, (M_VARS.esc_var0) + LD C, A + LD A, (M_VARS.esc_var1) + LD B, A + CALL put_image + POP HL + POP HL + POP HL + RET + +ehd_l1: + CP 0x2 + JP Z, get_image_hdr + CP 0x3 + JP Z, m_fn_39 + RET + +; -------------------------------------------------- +; Function 39 +; -------------------------------------------------- +m_fn_39: + LD HL, (M_VARS.esc_param+2) ; pr 3,4 + INC L + LD C, L + LD B, H + LD E, L + CALL dc_mul_e_h + ADD HL, HL + EX DE, HL + LD HL, (M_VARS.esc_param) ; par 1,2 + PUSH BC + PUSH DE + LD BC, 10 + LD E, L + LD D, H + ADD HL, BC + EX DE, HL + LD (HL), E + INC HL + LD (HL), D + INC HL + POP BC + LD A, 4 +.l1: + PUSH AF ; TODO: remove AF not changed + EX DE, HL + ADD HL, BC + EX DE, HL + LD (HL), E + INC HL + LD (HL), D + INC HL + POP AF ; TODO: remove AF not changed + DEC A + JP NZ, .l1 + EX DE, HL + LD HL, 0x0 + ADD HL, BC ; HL=BC + ADD HL, BC ; HL=2*BC + ADD HL, HL ; HL=4*BC + ADD HL, BC ; HL=5*BC + ADD HL, HL ; HL=10*BC + EX DE, HL ; DE=10*BC + LD A, 0x0 + LD B, A ; B=A=0 + ; fill DE bytes at [HL] with 0 +.l2: + LD (HL), B + INC HL + DEC DE + CP E + JP NZ, .l2 + CP D + JP NZ, .l2 + + XOR A + LD (M_VARS.esc_var0), A + POP BC + LD HL, (M_VARS.esc_param) + LD DE, 10 + ADD HL, DE +.l3: + EX DE, HL + LD HL, (M_VARS.esc_param+4) + EX DE, HL + PUSH BC + CALL fn39_sub2 + POP BC + LD A, (M_VARS.esc_var0) + ADD A, 0x2 + LD (M_VARS.esc_var0), A + CP 10 + RET Z + JP .l3 + +; --------------------------------------------------- +; Function 3C +; --------------------------------------------------- +get_image_hdr: + LD HL, (M_VARS.esc_param+4) + LD (HL), 58 ; ':' + INC HL + PUSH HL + LD HL, (M_VARS.esc_param+2) + INC L + LD C, L + LD A, H + ADD A, 0x4 + LD B, A + LD H, B + LD E, C + CALL dc_mul_e_h + ADD HL, HL + EX DE, HL + POP HL + LD (HL), E + INC HL + LD (HL), D + INC HL + LD (HL), C + INC HL + LD (HL), B + INC HL + EX DE, HL + LD HL, (M_VARS.esc_param) + ADD HL, HL + EX DE, HL + PUSH BC + PUSH HL + CALL calc_px_addr + POP DE + LD A, L + LD (DE), A + INC DE + LD A, H + LD (DE), A + INC DE + LD A, B + LD (DE), A + INC DE + POP BC + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL get_image ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; = +; --------------------------------------------------- +esc_get_put_image: + LD A, (M_VARS.esc_param+6) + CP 0x2 + JP NC, ehd_l1 + LD HL, M_VARS.esc_param + LD E, (HL) + INC HL + LD D, (HL) + INC HL + LD C, (HL) + INC HL ; TODO: next call to calc_px_addr + LD B, (HL) ; destroy value of B and HL + CALL calc_px_addr + EX DE, HL + LD HL, (M_VARS.esc_param+4) + LD A, H + CP 128 + RET C + + CP 184 + RET NC + + EX DE, HL + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (M_VARS.esc_param+6) + OR A + JP NZ, put_image + +; --------------------------------------------------- +; Get image from VRAM to user buffer +; Inp: HL -> VRAM[x,y] +; DE -> buffer +; BC - width, height +; --------------------------------------------------- +get_image: + PUSH HL + PUSH BC +.next_row: + ; byte 1 + LD A, (HL) + LD (DE), A + INC H ; next Y (row) + INC DE + ; byte 2 ; next dst addr + LD A, (HL) + LD (DE), A + INC H + LD A, H + CP 128 ; last row? + JP NZ, .l2 + LD H, 0x40 ; reset Y +.l2: + INC DE + DEC C + JP NZ, .next_row + POP BC + POP HL + INC L + DEC B ; dec width + JP NZ, get_image + JP img_task_end + +; --------------------------------------------------- +; Put image from buffer to VRAM +; Inp: HL -> VRAM[x,y] +; DE -> buffer +; BC - width, height +; --------------------------------------------------- +put_image: + PUSH HL + PUSH BC +.next_row: + ; two bytes for 8 pixels + ; byte 1 + LD A, (DE) ; get from buffer + LD (HL), A ; put to screen + INC H ; next Y (row) + INC DE ; next src addr + ; byte 2 + LD A, (DE) + LD (HL), A + INC DE + INC H + LD A, H + CP 128 ; last row? + JP NZ, .l2 + LD H, 0x40 ; reset +.l2: + DEC C + JP NZ, .next_row + POP BC + POP HL + ; next column + INC L + DEC B + JP NZ, put_image + +img_task_end: + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +fn39_sub2: + DEC C +.l1: + PUSH BC +.l2: + EX DE, HL + PUSH BC + PUSH HL + LD L, (HL) + LD H, 0x0 + LD A, (M_VARS.esc_var0) + LD B, A + OR A + JP Z, .l4 +.l3: + ADD HL, HL + DEC A + JP NZ, .l3 +.l4: + LD A, B + LD C, L + LD B, H + POP HL + INC HL + PUSH HL + LD L, (HL) + LD H, 0x0 + OR A + JP Z, .l6 +.l5: + ADD HL, HL + DEC A + JP NZ, .l5 +.l6: + EX DE, HL + LD A, (HL) + OR C + LD (HL), A + INC HL + LD A, (HL) + OR E + LD (HL), A + INC HL + LD (HL), B + INC HL + LD (HL), D + DEC HL + POP DE + INC DE + POP BC + DEC C + JP NZ, .l2 + POP BC + INC HL + INC HL + DEC B + JP NZ, .l1 + RET + +; -------------------------------------------------- +; Handle ASCII_CAN symbol (cursor right) +; -------------------------------------------------- +gih_rt: + DEC DE + LD A, (DE) + ADD A, 0x2 + LD (DE), A + CP 9 + RET C + LD A, 0x2 + LD (DE), A + INC DE + PUSH BC + LD H, D + LD L, E + INC HL + INC HL + LD A, (M_VARS.esc_var0) + PUSH AF + DEC A + LD (M_VARS.esc_var0), A + INC A + ADD A, A + ADD A, B + CP 128 + JP C, .l1 + SUB 0x40 +.l1: + LD B, A + LD A, (M_VARS.esc_var1) +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (HL) + LD (DE), A + INC HL + INC DE + LD A, (HL) + LD (DE), A + INC HL + INC DE + POP AF + DEC A + JP NZ, .l3 + LD A, (BC) + LD (DE), A + INC DE + INC HL + INC B + LD A, (BC) + LD (DE), A + INC HL + INC DE + DEC B + INC C + POP AF + DEC A + JP NZ, .l2 + POP AF + LD (M_VARS.esc_var0), A + POP BC + INC B + INC B + LD A, B + CP 0x80 + RET NZ + LD B, 0x40 + RET + +; -------------------------------------------------- +; Handle ASCII_BS (BackSpace) symbol +; -------------------------------------------------- +gih_bs: + DEC DE + LD A, (DE) + SUB 0x2 + LD (DE), A + RET NC + + LD A, 6 + LD (DE), A + INC DE + PUSH BC + ADD HL, DE + DEC HL + LD D, H + LD E, L + DEC HL + DEC HL + LD A, (M_VARS.esc_var1) + ADD A, C + DEC A + LD C, A + LD A, B + DEC A + CP 0x3f + JP NZ, .l1 + LD A, 0x7f ; [DEL]? +.l1: + LD B, A + LD A, (M_VARS.esc_var0) + PUSH AF + DEC A + LD (M_VARS.esc_var0), A + LD A, (M_VARS.esc_var1) +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + POP AF + DEC A + JP NZ, .l3 + LD A, (BC) + LD (DE), A + DEC HL + DEC DE + DEC B + LD A, (BC) + LD (DE), A + DEC HL + DEC DE + INC B + DEC C + POP AF + DEC A + JP NZ, .l2 + POP AF + LD (M_VARS.esc_var0), A + POP BC + DEC B + DEC B + LD A, B + CP 0x3e + RET NZ + LD B, 0x7e + RET + +; -------------------------------------------------- +; Handle ASCII_SUB symbol +; -------------------------------------------------- +gih_ctrl_z: + PUSH BC + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + ADD A, E + LD L, A + LD H, D + LD A, (M_VARS.esc_var1) + ADD A, C + LD C, A + PUSH BC + LD A, (M_VARS.esc_var1) + DEC A + DEC A + LD C, A +.l1: + LD A, (M_VARS.esc_var0) + LD B, A +.l2: + LD A, (HL) + LD (DE), A + INC HL + INC DE + LD A, (HL) + LD (DE), A + INC HL + INC DE + DEC B + JP NZ, .l2 + DEC C + JP NZ, .l1 + POP BC + LD L, 0x2 +.l3: + LD A, (M_VARS.esc_var0) + LD H, A + PUSH BC +.l4: + LD A, (BC) + LD (DE), A + INC DE + INC B + LD A, (BC) + LD (DE), A + INC DE + INC B + LD A, B + CP 0x80 + JP NZ, .l5 + LD B, 0x40 +.l5: + DEC H + JP NZ, .l4 + POP BC + INC C + DEC L + JP NZ, .l3 + POP BC + INC C + INC C + RET + +; -------------------------------------------------- +; Handle ASCII_EM symbol +; -------------------------------------------------- +gih_up: + PUSH BC + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, B + CP 128 + JP Z, .l1 + JP C, .l1 + SUB 64 +.l1: + DEC A + LD B, A + DEC C + PUSH BC + ADD HL, DE + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + LD E, A + LD A, L + SUB E + LD E, A + LD D, H + DEC DE + DEC HL + EX DE, HL + LD A, (M_VARS.esc_var1) + DEC A + DEC A + LD C, A +.l2: + LD A, (M_VARS.esc_var0) + LD B, A +.l3: + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + DEC B + JP NZ, .l3 + DEC C + JP NZ, .l2 + POP BC + LD L, 0x2 + +.l4: + LD A, (M_VARS.esc_var0) + LD H, A + PUSH BC + +.l5: + LD A, (BC) + LD (DE), A + DEC DE + DEC B + LD A, (BC) + LD (DE), A + DEC DE + DEC B + LD A, B + CP 0x3f + JP NZ, .l6 + LD B, 0x7f + +.l6: + DEC H + JP NZ, .l5 + POP BC + DEC C + DEC L + JP NZ, .l4 + POP BC + DEC C + DEC C + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +pict_sub2: + PUSH DE + DEC DE + LD A, (DE) + LD E, A + LD D, 0x0 + LD HL, (M_VARS.esc_param+1) + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) + POP HL + PUSH BC + PUSH HL + PUSH HL + LD HL, (M_VARS.esc_param+3) + INC HL + LD C, (HL) + INC HL + LD B, (HL) + POP HL + ADD HL, BC + LD C, L + LD B, H + POP HL + CALL mov_hl_bc + LD A, (M_VARS.esc_param+5) + OR A + JP Z, .l1 + EX DE, HL +.l1: + LD A, (M_VARS.esc_var1) + SUB 0x4 +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (DE) + INC DE + PUSH DE + PUSH AF + LD A, (DE) + LD E, A + POP AF + LD D, A + OR E + CPL + PUSH AF + AND (HL) + OR D + LD (BC), A + INC BC + INC HL + POP AF + AND (HL) + OR E + LD (BC), A + INC BC + INC HL + POP DE + INC DE + POP AF + DEC A + JP NZ, .l3 + POP AF + DEC A + JP NZ, .l2 + LD A, (M_VARS.esc_param+5) + OR A + JP Z, .l4 + EX DE, HL +.l4: + CALL mov_hl_bc + POP DE + EX DE, HL + LD A, (M_VARS.esc_var0) + LD C, A + LD A, (M_VARS.esc_var1) + LD B, A + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL put_image ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Move form [HL] to [BC] count of bytes +; Inp: HL -> src +; BC -> dst +; esc_var0*4 - count +; --------------------------------------------------- +mov_hl_bc: + PUSH DE + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + LD E, A ; E = param * 4 + ; move [HL] -> [BC] E bytes +.next: + LD A, (HL) + LD (BC), A + INC HL + INC BC + DEC E + JP NZ, .next + POP DE + RET + +; --------------------------------------------------- +; Draw circle +; Inp: param x,y,radius, aspect_x, aspect_y +; --------------------------------------------------- +esc_draw_circle: + LD A, (M_VARS.esc_param+2) ; radius + LD B, A + OR A + RET Z ; exit ir radius 0 + LD A, 0x7f + CP B + RET M ; exit if radius>127 + + XOR A + LD D, A ; 0 + LD E, B ; r + CALL dc_draw_8px + + LD A, 1 + LD H, A + SUB B + LD C, A + LD A, B + RLCA + LD B, A + LD A, 0x1 + SUB B + LD L, A + CCF ; TODO: unused +.l1: + INC D + LD A, E + CP D + JP Z, dc_draw_8px + CALL dc_draw_8px + LD A, H + ADD A, 0x2 + LD H, A + LD A, L + ADD A, 0x2 + LD L, A + LD A, C + ADD A, H + LD C, A + JP NC, .l1 +.l2: + CCF ; TODO: unused + INC D + DEC E + LD A, D + CP E + JP Z, dc_draw_8px + SUB E + CP 0x1 + RET Z + LD A, E + SUB D + CP 0x1 + JP Z, dc_draw_8px + CALL dc_draw_8px + LD A, H + ADD A, 0x2 + LD H, A + LD A, L + ADD A, 0x4 + LD L, A + JP NC, .l3 + CCF ; TODO: unused +.l3: + LD A, C + ADD A, L + LD C, A + JP NC, .l1 + JP .l2 + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_8px: + PUSH HL + PUSH DE + PUSH BC + PUSH DE + CALL dc_aspect_ratio_1 + LD HL, (M_VARS.esc_param) ; HL=Y,X + CALL dc_draw_4px_bc + POP DE + CALL dc_aspect_ratio2 + LD HL, (M_VARS.esc_param) ; HL=Y,X + CALL dc_draw_4px_cb + POP BC + POP DE + POP HL + XOR A + RET + +; --------------------------------------------------- +; Scale circle axis dy specified aspect ratio +; if aspect_x = 0 C = D else C = D * aspect_x / 256 +; if aspect_y = 0 B = E else B = E * aspect_y / 256 +; --------------------------------------------------- +dc_aspect_ratio_1: + LD HL, (M_VARS.esc_param+3) ; aspect_x -> L, aspect_y -> H + LD A, L + OR A + LD C, D + LD B, E + JP NZ, .dc_ax_ne0 + LD A, H + OR A + JP NZ, .dc_ay_ne0 + RET +.dc_ax_ne0: + LD A, H + LD H, L + LD E, C + CALL dc_mul_e_h + LD C, E + OR A + RET Z +.dc_ay_ne0: + LD H, A + LD E, B + CALL dc_mul_e_h + LD B, E + RET + +; --------------------------------------------------- +; if aspect_x = 0 B = E else B = E * aspect_x / 256 +; if aspect_y = 0 C = D else C = D * aspect_y / 256 +; --------------------------------------------------- +dc_aspect_ratio2: + LD HL, (M_VARS.esc_param+3) ; aspect_x -> L, aspect_y -> H + LD A, L + OR A + LD C, D + LD B, E + JP NZ, .dc_ax_ne0 + LD A, H + OR A + JP NZ, .dc_ay_ne0 + RET +.dc_ax_ne0: + LD A, H + LD H, L + LD E, B + CALL dc_mul_e_h + LD B, E + OR A + RET Z + +.dc_ay_ne0: + LD H, A + LD E, C + CALL dc_mul_e_h + LD C, E + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_mul_e_h: + LD D, 0x0 + LD L, D + ADD HL, HL + JP NC, .l1 + ADD HL, DE +.l1: + ADD HL, HL + JP NC, .l2 + ADD HL, DE +.l2: + ADD HL, HL + JP NC, .l3 + ADD HL, DE +.l3: + ADD HL, HL + JP NC, .l4 + ADD HL, DE +.l4: + ADD HL, HL + JP NC, .l5 + ADD HL, DE +.l5: + ADD HL, HL + JP NC, .l6 + ADD HL, DE +.l6: + ADD HL, HL + JP NC, .l7 + ADD HL, DE +.l7: + ADD HL, HL + JP NC, .l8 + ADD HL, DE +.l8: + LD E, H + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_4px_bc: + ; draw pixel(H+B, L+C) if in screen + LD A, H + ADD A, B + JP C, .l1 + LD D, A + LD A, L + ADD A, C + LD E, A + CALL dc_put_pixel +.l1: + ; draw pixel(H+B, L-C) if in screen + LD A, H + ADD A, B + JP C, .l2 + LD D, A + LD A, L + SUB C + LD E, A + CALL dc_put_pixel +.l2: + ; draw pixel(H-B, L-C) if in screen + LD A, H + SUB B + JP C, .l3 + LD D, A + LD A, L + SUB C + LD E, A + CALL dc_put_pixel +.l3: + ; draw pixel(H-B, L+C) if in screen + LD A, H + SUB B + RET C + LD D, A + LD A, L + ADD A, C + LD E, A + CALL dc_put_pixel ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_4px_cb: + ; draw pixel(H+C, L+B) if in screen + LD A, H + ADD A, C + JP C, .l1 + LD D, A + LD A, L + ADD A, B + LD E, A + CALL dc_put_pixel +.l1: + ; draw pixel(H+C, L-B) if in screen + LD A, H + ADD A, C + JP C, .l2 + LD D, A + LD A, L + SUB B + LD E, A + CALL dc_put_pixel +.l2: + ; draw pixel(H-C, L-B) if in screen + LD A, H + SUB C + JP C, l3 + LD D, A + LD A, L + SUB B + LD E, A + CALL dc_put_pixel +l3: + ; draw pixel(H-C, L+B) if in screen + LD A, H + SUB C + RET C + LD D, A + LD A, L + ADD A, B + LD E, A + CALL dc_put_pixel ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Draw pixel on screen +; Inp: DE - X, Y +; --------------------------------------------------- +dc_put_pixel: + RET C ; return if CF set (out of screen) + PUSH HL + PUSH BC + CALL calc_px_addr + ; calculate B = pixel mask + LD A, 10000000b +.roll: + RLCA ; [07654321] <- [76547210], [7] -> CF + DEC B + JP P, .roll + CPL + LD B, A + ; DE = foreground color low and hi bytes + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Turn on Video RAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; Load VRAM[HL] byte (low byte), mask and set + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; Load VRAM[HL+1] byte (low byte), mask and set + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; Turn off Video RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + POP BC + POP HL + RET + + ; Full charset, Common + Latin letters (112*8=890) + INCLUDE "font-6x7.inc" + +; --------------------------------------------------- +; Convert 0h..Fh decimal value to symbol '0'..'F' +; --------------------------------------------------- +conv_nibble: + AND 0xf + ADD A, 0x90 + DAA + ADC A, 0x40 + DAA + LD C, A + RET + +; --------------------------------------------------- +; Print byte in HEX +; Inp: A - byte to print +; --------------------------------------------------- +m_hexb: + PUSH AF + RRCA + RRCA + RRCA + RRCA + CALL out_hex + POP AF + +out_hex: + CALL conv_nibble + CALL m_con_out ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Wtite RAM-Disk 64K to TAPE +; --------------------------------------------------- +m_tape_write_ram2: + LD HL, M_VARS.buffer + LD C, 128 +.cl_stack: + LD (HL), 0x0 + INC HL + DEC C + JP NZ, .cl_stack + LD HL, M_VARS.buffer + LD DE, 0xffff + ; write empty block + ; DE - block ID + ; HL -> block + CALL m_tape_write + CALL twr2_delay + LD DE, 0x0 + CALL m_tape_write + CALL twr2_delay + LD BC, 512 + LD DE, 0x0 +.nxt_blk: + PUSH BC + LD HL, M_VARS.buffer + CALL m_ramdisk_read + INC DE + CALL m_tape_write + CALL twr2_delay + POP BC + DEC BC + LD A, B + OR C + JP NZ, .nxt_blk + RET + +; --------------------------------------------------- +; Pause between blocks on tape +; --------------------------------------------------- +twr2_delay: + LD BC, 250 +.delay: + DEC BC + LD A, B + OR C + JP NZ, .delay + RET + +; --------------------------------------------------- +; Read RAM-Disk 64K from TAPE +; --------------------------------------------------- +m_tape_read_ram2: + LD A, 100 + CALL m_tape_wait + OR A + JP NZ, .end + LD E, 6 + +.srch_first: + DEC E + JP Z, .not_found + ; read block + LD HL, M_VARS.buffer + CALL m_tape_read + CP 4 + JP Z, .end + OR A + JP NZ, .srch_first + LD A, B + OR C + JP NZ, .srch_first + + LD BC, 512 + LD DE, 0x0 + +.rd_next: + PUSH BC + ; Read block from tape + CALL m_tape_read + OR A + JP NZ, .rd_error + DEC BC + LD A, B + CP D + JP NZ, .inv_id + LD A, C + CP E + JP NZ, .inv_id + ; Ok, write block to RAM disk + CALL m_ramdisk_write + INC DE + POP BC + DEC BC + LD A, B + OR C + JP NZ, .rd_next + RET +.not_found: + LD HL, msg_no_start_rec + CALL me_out_strz ; TODO: replace call+ret to jp + RET +.rd_error: + CP 2 + JP Z, .err_ubi + CP 4 + JP Z, .err_ibu + LD HL, msg_checksum + CALL me_out_strz + CALL out_hexw + POP BC + RET + + ; Illegal sequence of blocks +.inv_id: + LD HL, msg_sequence + CALL me_out_strz + INC BC + CALL out_hexw + POP BC + RET + +.err_ubi: + LD HL, msg_ibg + CALL me_out_strz + POP BC + RET + + ; Interrupted by user +.err_ibu: + POP BC +.end: + LD HL, msg_break + CALL me_out_strz ; TODO: replace call+ret to jp + RET + +; -------------------------------------------------- +; Output hex word +; Inp: BC - word to output +; -------------------------------------------------- +out_hexw: + PUSH BC + LD A, B + CALL m_hexb + POP BC + LD A, C + CALL m_hexb ; TODO: replace call+ret to jp + RET + +msg_no_start_rec: + DB "NO START RECORD", 0 +msg_checksum: + DB "CHECKSUM ", 0 +msg_sequence: + DB "SEQUENCE ", 0 +msg_ibg: + DB "IBG", 0 +msg_break: + DB "BREAK", 0 + +; --------------------------------------------------- +; Out ASCIIZ message +; Inp: HL -> zero ended string +; --------------------------------------------------- +me_out_strz: + LD A, (HL) + OR A + RET Z + PUSH BC + LD C, A + CALL m_con_out + INC HL + POP BC + JP me_out_strz + + + +; --------------------------------------------------- +; Read from RAM-disk to RAM +; Inp: DE - source sector +; HL -> destination buffer +; --------------------------------------------------- +m_ramdisk_read: + PUSH HL + PUSH DE + LD A, D + ; Build value to access ext RAM (A17, A16, 32k bits) + AND 00000111b ; A17, A16, Low 32K bits of memory mapper + ADD 0x2 ; Calc A16, A17 address lines + OR 0x0 ; TODO: nothing, remove + LD B, A ; B - value to turn on access to Ext RAM + ; Calculate DE = address from sector number + XOR A + LD A, E ; E - low address + RRA ; [CF] -> [7:0] -> [CF] + LD D, A ; D = E/2 + LD A, 0x0 + RRA ; [CF] -> E + LD E, A +.read: + ; Access to ExtRAM + LD A, B + OUT (SYS_DD17PB), A + ; Get Byte + LD A, (DE) + LD C, A + ; Access to RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + ; Set Byte + LD (HL), C + ; HL++, DE++ + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, .read ; jump if has more bytes + + ; Access to RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP DE + POP HL + RET + +; --------------------------------------------------- +; Write sector to RAM disk +; Inp: HL -> source buffer +; DE - destination sector +; --------------------------------------------------- +m_ramdisk_write: + PUSH HL + PUSH DE + LD A, D + AND 0x7 + ADD 0x2 ; build value to access ext RAM (A17, A16, 32k bits) + OR 0x0 ; TODO: remove unused + LD B, A + XOR A + LD A, E + RRA + LD D, A + LD A, 0x0 + RRA + LD E, A +.wr_byte: + LD A, 0x0 + OUT (SYS_DD17PB), A + LD C, (HL) + LD A, B + OUT (SYS_DD17PB), A + LD A, C + LD (DE), A + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, .wr_byte + LD A, 0x0 + OUT (SYS_DD17PB), A + POP DE + POP HL + RET + +; -------------------------------------------------- +; Write block to Tape +; In: DE - block ID, +; HL -> block of data. +; -------------------------------------------------- +m_tape_write: + PUSH HL + PUSH DE + PUSH DE + LD BC, 2550 + LD A, PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A,TMR0_SQWAVE ; tmr0, load lsb+msb, sq wave, bin + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A, B + OUT (TMR_DD70C1), A + ; Write Hi+Lo, Hi+Lo + LD DE, 4 ; repeat next 4 times +.l1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; check rst4 from timer#0 + JP NZ, .l1 + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH ; tape level hi + JP NZ, .set_lvl + LD A, TL_LOW ; tape level low +.set_lvl: + OUT (DD67PC), A ; set tape level + LD A, TMR0_SQWAVE ; tmr0, load lsb+msb, swq, bin + ; timer on + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A, B + OUT (TMR_DD70C1), A + DEC E + JP NZ, .l1 + +.l2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .l2 + + ; Write 00 at start + LD A, 0x0 + CALL m_tape_wr_byte + ; Write 0xF5 marker + LD A, 0xf5 + CALL m_tape_wr_byte + LD E, 0x0 ; checksum=0 + ; Write block ID + POP BC + LD A, C + CALL m_tape_wr_byte + LD A, B + CALL m_tape_wr_byte + ; Write 128 data bytes + LD B, 128 +.next_byte: + LD A, (HL) + CALL m_tape_wr_byte + INC HL + DEC B + JP NZ, .next_byte + ; Write checksum + LD A, E + CALL m_tape_wr_byte + ; Write final zero byte + LD A, 0x0 + CALL m_tape_wr_byte +.wait_end: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .wait_end + LD A, TL_MID ; tape level middle + OUT (DD67PC), A + POP DE + POP HL + RET + + +; ------------------------------------------------------ +; Write byte to tape +; Inp: A - byte top write +; D - current level +; E - current checksum +; ------------------------------------------------------ +m_tape_wr_byte: + PUSH BC + ; calc checksum + LD B, A + LD A, E + SUB B + LD E, A + LD C, 8 ; 8 bit in byte +.get_bit: + LD A, B + RRA + LD B, A + JP C, .bit_hi +.wait_t: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .wait_t + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; program for 360 cycles + LD A, 0x68 + OUT (TMR_DD70C1), A + LD A, 0x1 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH + JP NZ, .out_bit + LD A, TL_LOW +.out_bit: + OUT (DD67PC), A + DEC C + JP NZ,.get_bit + POP BC + RET +.bit_hi: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .bit_hi + ; program for 660 cycles + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + LD A, 0x94 + OUT (TMR_DD70C1), A + LD A, 0x2 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH + JP NZ, .out_bit_hi + LD A, TL_LOW +.out_bit_hi: + OUT (DD67PC), A + DEC C + JP NZ, .get_bit + POP BC + RET + +; ------------------------------------------------------ +; Load block from Tape +; In: HL -> buffer to receive bytes from Tape +; Out: A = 0 - ok, +; 1 - CRC error, +; 2 - unexpected block Id +; 4 - key pressed +; ------------------------------------------------------ +m_tape_read: + PUSH HL + PUSH DE + LD A, PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A ; tmr0, load lsb+msb, sq wave + LD A, 0x0 + ; tmr0 load 0x0000 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + LD C, 3 +.wait_3_changes: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + LD A, B + ADD A, 4 + JP P, .wait_3_changes + DEC C + JP NZ, .wait_3_changes +.wait_4th_change: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + LD A, B + ADD A, 4 + JP M, .wait_4th_change + LD C, 0x0 +.wait_f5_marker: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + DEC A + RRA + LD A, C + RRA + LD C, A + CP 0xf5 + JP NZ, .wait_f5_marker + LD E, 0x0 ; checksum = 0 + ; Read blk ID + CALL m_tape_read_byte + JP NC, .err_read_id + LD C, D + CALL m_tape_read_byte + JP NC, .err_read_id + LD B, D + PUSH BC + ; Read block, 128 bytes + LD C, 128 +.read_next_b: + CALL m_tape_read_byte + JP NC, .err_read_blk + LD (HL), D + INC HL + DEC C + JP NZ, .read_next_b + + ; Read checksum + CALL m_tape_read_byte + JP NC, .err_read_blk + LD A, E + OR A + JP Z, .checksum_ok + LD A, 0x1 ; bad checksum +.checksum_ok: + POP BC +.return: + POP DE + POP HL + RET + +.err_read_blk: + POP BC + LD BC, 0x0 +.err_read_id: + LD A, 0x2 ; read error + JP .return +.key_pressed: + CALL m_con_in + LD C, A ; store key code in C + LD B, 0x0 + LD A, 0x4 + JP .return + +; ------------------------------------------------------ +; Read byte from Tape +; Out: D - byte +; CF is set if ok, cleared if error +; ------------------------------------------------------ +m_tape_read_byte: + PUSH BC + LD C, 8 +.next_bit: + CALL m_read_tape_bit + ; push bit from lo to hi in D + RRA + LD A, D + RRA + LD D, A + LD A, 4 + ADD A, B + JP NC, .ret_err + DEC C + JP NZ, .next_bit + ; calc checksum + LD A, D + ADD A, E + LD E, A + SCF +.ret_err: + POP BC + RET + +; ------------------------------------------------------ +; Read bit from tape +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +m_read_tape_bit: + IN A, (KBD_DD78PB) ; Read Tape bit 5 (data) + AND TAPE_P + LD B, A +.wait_change: + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z, .wait_change + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; [360...480...660] 0x220=544d + IN A, (TMR_DD70C1) ; get tmer#0 lsb + ADD A, 0x20 + IN A, (TMR_DD70C1) ; get tmer#0 msb + LD B, A + ADC A, 0x2 + ; reset timer to 0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; For 0 - 65535-360+544 -> overflow P/V=1 + ; For 1 - 65535-660+544 -> no overflow P/V=0 + RET P + INC A + RET + +; ------------------------------------------------------ +; Read bit from tape with keyboard interruption +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +read_tape_bit_kbd: + IN A, (KBD_DD78PB) + AND TAPE_P + LD B, A ; save tape bit state + ; wait change with keyboard check +.wait_change: + IN A, (PIC_DD75RS) + AND KBD_IRQ + JP NZ, .key_pressed + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z, .wait_change + ; measure time + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; read lsb+msb + IN A, (TMR_DD70C1) + ADD A, 0x20 + IN A, (TMR_DD70C1) + LD B, A + ADC A, 0x2 + ; reset timer#0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; flag P/V is set for 0 + RET P + INC A + RET +.key_pressed: + LD A, 0xff + RET + +; ------------------------------------------------------ +; Wait tape block +; Inp: A - periods to wait +; Out: A=4 - interrupded by keyboard, C=key +; ------------------------------------------------------ +m_tape_wait: + OR A + RET Z + PUSH DE + LD B, A +.wait_t4: + LD C,B + IN A, (KBD_DD78PB) + AND TAPE_P ; Get TAPE4 (Wait det) and save + LD E, A ; store T4 state to E +.wait_next_2ms: + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; load 3072 = 2ms + XOR A + OUT (TMR_DD70C1), A + LD A, 0xc + OUT (TMR_DD70C1), A +.wait_tmr_key: + IN A, (PIC_DD75RS) + AND KBD_IRQ ; RST1 flag (keyboard) + JP NZ, .key_pressed + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; RST4 flag (timer out) + JP Z, .wait_no_rst4 + IN A, (KBD_DD78PB) + AND TAPE_P ; TAPE4 not changed? + CP E + JP NZ, .wait_t4 ; continue wait + JP .wait_tmr_key +.wait_no_rst4: + DEC C + JP NZ, .wait_next_2ms + XOR A + POP DE + RET + +.key_pressed: + CALL m_con_in + LD C, A ; C = key pressed + LD A, 0x4 ; a=4 interrupted by key + POP DE + RET + +; ------------------------------------------------------ +; Check block marker from Tape +; Out: A=0 - not detected, 0xff - detected +; ------------------------------------------------------ +m_tape_blk_detect: + IN A, (KBD_DD78PB) + AND TAPE_D ; TAPE5 - Pause detector + LD A, 0x0 + RET Z + CPL + RET + +; ====================================================== +; FDC DRIVER +; ====================================================== + + +fdc_unload_head: + LD A, 0x0 + OUT (FDC_DATA), A + LD A, FDC_RESTORE_UH_NV + OUT (FDC_CMD), A + NOP + NOP + +.wait_no_busy: + IN A, (FDC_CMD) + AND 00000101b ; Track0 , Busy + CP 00000100b + JP Z, .tr0_ok + + IN A, (FLOPPY) + RLCA ; MOTST -> CF + JP NC, .wait_no_busy + LD A, 0x20 + RET +.tr0_ok: + LD A, B + DEC A + LD A, 0x1 + JP Z, .b1 + LD (M_VARS.ul_A_var1), A + XOR A + LD (M_VARS.ul_A_var4), A + RET + +.b1: + LD (M_VARS.ul_B_var2), A + XOR A + LD (M_VARS.ul_B_var5), A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_select_drive: + PUSH AF + CALL delay_1.4mS + CP 0x1 ; TODO: DEC A to save 1b 3t + JP Z, .sel_A + LD A, 0x2 + JP .sel_B +.sel_A + LD A, 0x5 +.sel_B + LD B, A ; 010b or 101b + IN A, (FLOPPY) ; read Floppy controller reg + AND 0x40 ; SSEL + RRA ; SSEL for out + OR B ; 0x22 or 0x25 if WP + OUT (FLOPPY), A ; Select drive A or B + LD B, A + POP AF + DEC A + JP Z, .dpar_a + LD A, (bios_var2) + JP .dpar_b +.dpar_a + LD A, (BIOS.bios_var04) +.dpar_b + PUSH BC + LD B, A ; B = dpar_A or B + LD A, D ; compare with D? + CP B + JP C, .l_le ; D < B + SUB B + LD D, A + POP BC + LD A, C + OR 0x8 + LD C, A + LD A, B + AND 0x20 + OR A + RET NZ + LD A, B + OR 0x20 ; set SSEL to 1 + OUT (FLOPPY), A + CALL delay_136uS + RET +.l_le + POP BC + LD A, B + AND 0x20 + OR A + RET Z + LD A, B + AND 0x7 + OUT (FLOPPY), A + CALL delay_136uS + RET + +; --------------------------------------------------- +; Delay for 136uS +; --------------------------------------------------- +delay_136uS: + LD B, 16 ; 7 + +; --------------------------------------------------- +; Delay for B*8uS +; --------------------------------------------------- +delay_b: + DEC B ; 4 + JP NZ, delay_b ; 10 + RET ; 10 + +; --------------------------------------------------- +; Delay for 1.4mS +; --------------------------------------------------- +delay_1.4mS: + LD B, 175 ; 7 + JP delay_b ; 10 + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_read_floppy: + PUSH AF + CALL m_select_drive + POP AF + CALL m_start_seek_track + JP C, fdc_ret + CALL m_fdc_read_c_bytes + JP C, fdc_ret + XOR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_write_floppy: + PUSH AF + CALL m_select_drive + POP AF + CALL m_start_seek_track + JP C, fdc_ret + CALL m_fdc_write_bytes + JP C, fdc_ret + XOR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_start_seek_track: + CALL m_start_floppy + RET C + CALL m_fdc_seek_trk + RET C + RET ; TODO: remove + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_start_floppy: + LD B, A + LD A, (M_VARS.ul_var3) + CP B + JP Z, .need_m_start + CALL .wait_motor ; TODO: replace call+ret to jp + RET +.need_m_start: + IN A, (FLOPPY) + RLCA ; check MOTST + JP C, .wait_motor + IN A, (FDC_CMD) + AND FDC_NOT_READY ; not ready flag + RET Z + +; --------------------------------------------------- +; +; --------------------------------------------------- +.wait_motor: + PUSH BC + LD BC, 65535 + CALL fdc_init +.wait_rdy1: + IN A, (FDC_CMD) + AND FDC_NOT_READY + JP Z, .long_delay + IN A, (FLOPPY) + RLCA ; CF<-A[7] MOTST flag + JP NC, .wait_rdy1 + LD A, 0x20 + JP .mst_exi +.long_delay: + DEC BC + LD A, B + OR A + JP NZ, .long_delay +.mst_exi: + POP BC + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +fdc_init: + IN A, (FLOPPY) + AND 01001110b ; Get SSEL, DRSEL, MOT1, MOT0 + RRA + OUT (FLOPPY), A + OR 0x08 ; Set INIT bit + OUT (FLOPPY), A + RET + +; --------------------------------------------------- +; Seek track on floppy +; Inp: DE - track/sector +; --------------------------------------------------- +m_fdc_seek_trk: + LD A, B + DEC A + JP Z, .drv_b + LD A, (M_VARS.ul_A_var1) + OR A + CALL Z, fdc_unload_head + RET C + LD A, (M_VARS.ul_A_var4) + OUT (FDC_TRACK), A + LD A, D + LD (M_VARS.ul_A_var4), A + JP .cmn +.drv_b: + LD A, (M_VARS.ul_B_var2) + OR A + CALL Z, fdc_unload_head + RET C + LD A, (M_VARS.ul_B_var5) + OUT (FDC_TRACK), A + LD A, D + LD (M_VARS.ul_B_var5), A +.cmn: + LD A, (M_VARS.ul_var3) + CP B + LD A, B + LD (M_VARS.ul_var3), A + JP NZ, .l2 + IN A, (FDC_TRACK) + CP D + JP Z, .l2 + JP C, .l1 + LD A, (M_VARS.ul_var6) + OR A + JP NZ, .l2 + LD B, 0xff + CALL delay_b + LD A, 0x1 + LD (M_VARS.ul_var6), A + JP .l2 +.l1: + LD A, (M_VARS.ul_var6) + OR A + JP Z, .l2 + LD B, 0xff + CALL delay_b + LD A, 0x0 + LD (M_VARS.ul_var6), A +.l2: + LD A, D + OUT (FDC_DATA), A + LD A, 0x1f + OUT (FDC_CMD), A + NOP + NOP + IN A, (FDC_WAIT) + IN A, (FDC_CMD) + AND 0x19 + CP 0x0 + JP NZ, .l3 + JP .l4 +.l3: + SCF + LD A, 0x40 +.l4: + PUSH AF + LD A, E + OUT (FDC_SECT), A + POP AF + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_fdc_write_bytes: + LD A, C + OUT (FDC_CMD), A +.w_next: + IN A, (FDC_WAIT) + RRCA + LD A, (HL) + OUT (FDC_DATA), A + INC HL + JP C, .w_next + CALL fdc_check_status ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_fdc_read_c_bytes: + LD A, C + OUT (FDC_CMD), A + JP .l2 +.l1: + LD (HL), A + INC HL +.l2: + IN A, (FDC_WAIT) + RRCA + IN A, (FDC_DATA) + JP C, .l1 + CALL fdc_check_status ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Check fdc status for errors +; Out: CF set if errors +; --------------------------------------------------- +fdc_check_status: + IN A, (FDC_CMD) + AND 11011111b + CP 0x0 + JP Z, fdc_ret + SCF +fdc_ret: + RET + +filler1: + DB 20h + +; filler: +; ds 169, 0xff + +; ------------------------------------------------------ + +LAST EQU $ +CODE_SIZE EQU LAST-0xE000 +FILL_SIZE EQU ROM_CHIP_SIZE-CODE_SIZE + + + ASSERT m_hot_start = 0xe051 + ASSERT m_out_strz = 0xe0f1 + ASSERT m_con_out_int = 0xe16d + ASSERT get_esc_param = 0xe187 + ASSERT esc_params_tab = 0xe1cb + ASSERT esc_handler_tab = 0xe1db + ASSERT esc_set_beep = 0xe1f9 + ASSERT m_print_hor_line = 0xe23a + ASSERT m_get_7vpix = 0xe2b4 + ASSERT esc_set_palette = 0xe2fe + ASSERT m_get_glyph = 0xe320 + ASSERT m_print_no_esc = 0xe349 + ASSERT calc_addr_40 = 0xe439 + ASSERT mp_mode_64 = 0xe4b8 + ASSERT calc_addr_80 = 0xe612 + ASSERT m_clear_screen = 0xe639 + ASSERT m_cursor_home = 0xe66c + ASSERT m_draw_cursor = 0xe69a + ASSERT m_handle_esc_code = 0xe77c + ASSERT handle_cc_common = 0xe7c4 + ASSERT handle_cc_80x25 = 0xe833 + ASSERT m_beep = 0xe85a + ASSERT esc_set_cursor = 0xe890 + ASSERT esc_set_vmode = 0xe8e9 + ASSERT esc_set_color = 0xe92f + ASSERT m_print_at_xy = 0xe943 + ASSERT game_sprite_tab = 0xea39 + ASSERT esc_draw_fill_rect = 0xeb64 + ASSERT draw_line_h = 0xeed1 + ASSERT esc_draw_line = 0xef0b + ASSERT esc_draw_dot = 0xf052 + ASSERT esc_picture = 0xf0a4 + ASSERT m_fn_39 = 0xf10f + ASSERT get_image_hdr = 0xf177 + ASSERT esc_get_put_image = 0xf1b5 + ASSERT pict_sub2 = 0xf3ca + ASSERT m_font_cp0 = 0xf5bc + ASSERT me_out_strz = 0xfb36 + ASSERT m_ramdisk_write = 0xfb6d + ASSERT m_tape_write = 0xfb97 + ASSERT m_tape_wr_byte = 0xfc0e + ASSERT m_tape_read_byte = 0xfcee + ASSERT m_read_tape_bit = 0xfd08 + ASSERT m_tape_wait = 0xfd58 + + + ; DISPLAY "Code size is: ", /A, CODE_SIZE + + +FILLER + DS FILL_SIZE, 0xFF + ; DISPLAY "Free size is: ", /A, FILL_SIZE + + ENDMODULE + + OUTEND + + OUTPUT m_vars.bin + ; put in separate waste file + INCLUDE "m_vars.inc" + OUTEND diff --git a/MON_v8_3edeb015/ram.inc b/MON_v8_3edeb015/ram.inc new file mode 100644 index 0000000..8c6c08e --- /dev/null +++ b/MON_v8_3edeb015/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +@RST1 EQU 0x0008 +@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/MON_v8_c4eec374/.gitignore b/MON_v8_c4eec374/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/MON_v8_c4eec374/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/MON_v8_c4eec374/.vscode/extensions.json b/MON_v8_c4eec374/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/MON_v8_c4eec374/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/MON_v8_c4eec374/.vscode/tasks.json b/MON_v8_c4eec374/.vscode/tasks.json new file mode 100644 index 0000000..9c09341 --- /dev/null +++ b/MON_v8_c4eec374/.vscode/tasks.json @@ -0,0 +1,34 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make MONITOR (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=monitor.sld", + "--sym=monitor.labels", + "--raw=monitor.obj", + "--fullpath", + "monitor.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + } + + ] +} \ No newline at end of file diff --git a/MON_v8_c4eec374/BIN/MON_V8_c4eec374.BIN b/MON_v8_c4eec374/BIN/MON_V8_c4eec374.BIN new file mode 100644 index 0000000000000000000000000000000000000000..203b0645e6906fac44de7f77d7c2881b8ecc5f2a GIT binary patch literal 8192 zcmdTp3s)Q0nIjLLL^N0U{ofKqL_`+V8u< zkEY!pup`aA-~I0QzTbV!40q2}ZcCEO3vpXR+=C&mG{iNAxPu|CeU1C!D%Z5iJ+{i7 zOme%D+_81;`|I2enQM}{*i|kk#<^nL&!b$^EcZ}^E1cstEpm4*FVTHLE;vblFW5x) z21B$W=+jVEYEMsK@8h;VvR}PQQ$Zh{8D#6{nEv{!OnsQCpJM9jCmA}Uewv}1fJYUa zsGn!7l&WHeu{snLA29S^f<5E_~7uMaYOhFX=OP%0E~tJRqr)kBd@i}yzBX3xyb&fGF7PZ%bQ6L&5b&b=1< zdGzCuag-@;Hx+k4h)rqW1BU0$%n-Q)#?%hcKC!tVO8nkFA#M%HC4-1!%O?u10>E4U zYeGCRVO3ET3yg*`P~z_=UVQWP#I^;-1{j0lZ`&%jJU4c5!uktj;{6`ESa@clSv&{> z;hOOA3F|A^0nB#-?k>2kzePk0N%)fcmBl5;C<=<=lICK2nCT0cI!656Ql_)pVzEe; z(*Z|&r=)ng9rNvYW#v)KGIerxqRZ0KZE5A+ndmN{EW`1Mf*qFO=PU;F?qmr{MnPB=_88Wu;;uMN`}l zCKY?4vE;S%6v83 zH&t7zXS=5iSwIocwrU&&j{ZF8`0*6re=vnEfPQbypbhN<)8q`{K=`^1F#3i}lo{#I zEA^btvjJC-aQ&_0H3K(XMNL`30a$;mQO7eKcOrYg(UJSbCRe4J%@5a_*}U+EVxm;u z1MCou-Qnxe_>8M&fQwFvc@j}x>h=o9!;QjYaQ}OlI~TsKSQ>bhSaUqgy&3l8hRx62^gTP7kb7y6#Gvi5t5SR24dO4k;7 zBjt$n1RNR)SNVITi2bHn^@v1fRX;_!FzrZCF%=B?7`81AjiVo9{;X81XP=U=oBOJS z3RDB>sgyAtfwAt9w??Aj2+S&EtE#FsWWv09TT%;`;eJ=*CZ) z59N6oCJ5fYCO1wIJu4ASAxxz@A|Fh`e0D^l5lAT1q0shvUUGp|*69ebI&H??U6@gmotU?XExF`0oxY|u<+(|Ox6iCQ?gcgc}gx67A9qj&?(7T!b4NCe!#Yis^MdR zx>b_1g|AA9bi7hGxN(+MgG$Yzl2?J$rfk?g%NkU_CQw(SEZvTcT;cLg6j8Eoqbw%O+yXVwCE(o%-t8)>0am$i?EM+K(0TfF-)R>J<*ip4l9I6E=g6BbfvaV+VDHvh7s$++ zl`>%UH_nngg@sbQRYE4uipm8ou6WiXt-7GX4I{(iq^Y04fyLce2S*n&6L2Sn9iUlO z2otlg4@k4tQO466(2hy7E*t&fpP)V}-V-@Fs_h8dKV-b@H)cCwt1x5^m2y9x?F55u z!}GHp{q5D&)y7*tC^Be=>{pmZAvPPvM1+k1)|TJT!cNdX8^LYh6<1{?F8!~#s&FqL zO<4b-2@``PYsjW&U!3LZKVm+=9JlV>A-kW=UMymam*km-5pcP|l< zpTMA0AIVz~8=K{+jmvZD7pLC%(J7B7HJ;+8QyxmI&3`aotKHSx+e?>fDQziD>QcJ2 zxA#$Psg@wLr40{1{BT2|r?7CmVS2h@yl{G&@_4A}Y0s%ssnn@c9;&bbI@6b@p^&0- zcl7q|$fYQ4wk&6Bsb}|B81Xh%?0**T9Jrzz7s9EH3(Z6jad=&$K^*p7yts2G-j8cf z0LD|ujgNy(Gf z{a+dTMW*>av2bDAWd>NC;`%&D4u^r?EBxD>oo5=`9}U|Dru{2nXk3T6_66ibGWPNu z$bO5l2N+o?yf(+ZF-I#v7Z*l6)Q$7@w?G8U!)wk+V9@CTkWT1kUe{DDU# zt=y(%|JzgQOP~m6NS#pr1iDv<`1O^92r3E|2n7c{G4;Sj+!JL6vp^yJ; z!O{wVIsTi4x)$&nM{=Bj$pN!~dtq+dC8lDM*(Nd{6Z0VX-(9$#{GON3lCA$NZ2pk` znnjZS?<_o7`9!6jU0vW3#O(AmJ!Rd-pA4+~O^j zl}L;ZbqaZu{53se|Dv`~{?mFpZ%c9xWB-)}I63O7- zh-VcVaWI-TVrLZX;?e1AeE$-4!ny%l_^fP$MsO$6a&%$nO^@9 z!*Ba5eO1``V=aYxy za5O1eg5$~?aX2R}MfpeC;Mu4d9Szqp-Ey26U*ZNA9~KpT87ALLl<8ig?)X$*VNM4Sj4pw#8x6 zvy(Aj2sVyTj4OkFcmy*QyG(?@6yM^|vl;My0s0kjG75S>7y<_n@WAO6J`;yS!_K(- zuKgif`uX&B#2ko6Jim3>Jm*|s{S!4qCZC?Iiu-IfHCq|yONs-^JXv>zR_2-fUwYJr zPp1idJZ(7LC-;qgGd}8Y!hj?Y?a8x&xi%BT__=sip2?=lf|fcP+TV`HFt#dqnb0?& zae&xSW!@!P144^x4nI&+12RrEHoF_;J?y)2y4QJ4%?;T#C7!&R5|A|yvTI7zSg7>S zo=wYEtKBj^pz-j2;5~i%sg5t9%w|87jg8HKb6&Gg&DJmJ*-tO|KCJ|FyB|yiOHF7S zd;gNgQN`ZAx3j;3QqPZ+6vNviSQzmgQOF+-!?eNF2g5MIv2n8c7tl##+wRC4TukWri*G%X3f;VdPhrp!jf!@N8L*0Q*n`tR=IC zWT7I?kEG4_C~X;=*?r6KHsuN=$ROQ&{-geuW8%xpw4(h?Gwd3);!MY${O;F3aW%*iR*(pWvaN z(D4P-SE`3|5=GU+`B)TTQHF&Li>+8}$D%$_^aZ}BZ_JTsIeDs8{B8o^I{|)qRmtCBe@cfuQaZ)O{ zC`y%|Puf{o7WieU%G8GZ994!1&ZtF&8l75c))i<}N~8gl5>B!3*8)gosVu525C9d( zfh$dBAuu*I6glZq24j)gWHhKWsL^0BBGzKj!g<(e&}L`pELyEak&_FpY^5T{Xf#-m zLa8(W2!FWCa;Dsz3^blhJ&1N&0mxVUMl?5ckX@$iAj)UtId0wCl zXoMC*a4o1%TT0PyybQTzCeWidfl6qh4Y|1mU|1Qnj24BV5aJD@p#dR;exx+2$jE~X z6s=Jxh=XXP3gza2WoFo*kP@YU)uhT%kg5!xr1U1xL{Vj+Ehnu29)LMhCK!K$W241Jr zSwOo1GL=|nQIx@0<3NMeI%3i_eVA_$W~@NF4hEf~O7rsauvG#Bh$g{MU-u7(9a?3^ zviv-X%G70|ef9eCKQ&9!NLGSPG)KiS|Gs&Sgq5+ zP=ZJ5?EGw<8Qe4LHuBSA#GJQ)cj#Ys7F-q>JCdL19{2|&cwNsI^vkF+6aO~y6TJif zuIFbulhG}hPNX9FU6zNF0n>G3KHQX_kRhlIZcJf9gq9(_$!s>ktjdNd3f*v7pjCRU zWyuASvfSKUi<$hSoWPk+{l)RAWGzs1BdZ9*_n*I~#EwHi-U&w>pzOUtIigt6k&jyN{;V_D<_@;Ogon>c zx$G;eU3D#9TFnlu(iy=Ce8H9`*bH5S;u44v4Qq4)iZTiuG_1Md*7SLxgcI4qYBvCc zhR{>+=^;1)2Kt@dXW;F;+u|JCyGFW7)=)nE_w7t-HaEy^_j_#d$%$jxLY41-|GK zT1u;iP?MH?fdt()2z5C{jh9PmNbT3x$Ez}jUs|Wjd3PhN5cu`_Mfi*wXZZRk)JqJX z5Nl*OM!mldzK7OnwR5~ma#&ssRykvh!pgdRAH{vVZogyH@9a81W|q&i_&Qg#&2FjO zJK$a^4_1klGVjCYpj*mJH#NCxc8knw`nfG~LL6F;iAUwJBPnrr3d(-acmuU1jS^es zE8@@OE4t+3(0O997aG zOR#E18>|9Hg+I#nMaFyaKi99=<4n-n1rBqfY>#f>mI!W%L1pgx7{h8(J5 z+n3=nNR!f}#4R$sH(F*mO$u?)o5oRq>w+*_NSG_Zz9N}oH>bGr)Nb05=nhuI_|9PY tcx8?!XtRw~r^Gu_h~W05TsFXt+r}CR4mjVJDd9+JH(!o#7yn;e{|hJCEQ0_5 literal 0 HcmV?d00001 diff --git a/MON_v8_c4eec374/README.md b/MON_v8_c4eec374/README.md new file mode 100644 index 0000000..5beee09 --- /dev/null +++ b/MON_v8_c4eec374/README.md @@ -0,0 +1,16 @@ +# Ocean-240.2 ROM Monitor V8 checksum c4eec374 + +Source codes of Monitor v8 for Ocean-240.2 with Floppy controller. +In Z80 mnemonics, but limited for i8080 instruction set. + +## Differences: + +Other versions of the code are compared with this one. + +## Compile: + +Code is located in memory at address: 0xE000..0xFFFF + + sjasmplus --sld=monitor.sld --sym=monitor.labels --raw=monitor.obj --fullpath monitor.asm + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). diff --git a/MON_v8_c4eec374/bios_entries.inc b/MON_v8_c4eec374/bios_entries.inc new file mode 100644 index 0000000..117a2c8 --- /dev/null +++ b/MON_v8_c4eec374/bios_entries.inc @@ -0,0 +1,41 @@ + +; ======================================================= +; Ocean-240.2 +; Definition of CPM BIOS entries to compile depended +; modules +; +; By Romych 2025-09-09 +; ====================================================== + IFNDEF _BIOS + DEFINE _BIOS + + MODULE BIOS + +boot_f EQU 0xD600 +wboot_f EQU 0xD603 +const_f EQU 0xD606 +conin_f EQU 0xD609 +conout_f EQU 0xD60C +list_f EQU 0xD60F +punch_f EQU 0xD612 +reader_f EQU 0xD615 +home_f EQU 0xD618 +seldsk_f EQU 0xD61B +settrk_f EQU 0xD61E +setsec_f EQU 0xD621 +setdma_f EQU 0xD624 +read_f EQU 0xD627 +write_f EQU 0xD62A +sectran_f EQU 0xD630 +reserved_f1 EQU 0xD633 +reserved_f2 EQU 0xD636 +tape_read_f EQU 0xD639 +tape_write_f EQU 0xD63C +tape_wait_f EQU 0xD63F + +bios_var04 EQU 0xD64B + + ENDMODULE + + + ENDIF diff --git a/MON_v8_c4eec374/equates.inc b/MON_v8_c4eec374/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/MON_v8_c4eec374/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/MON_v8_c4eec374/font-6x7.inc b/MON_v8_c4eec374/font-6x7.inc new file mode 100644 index 0000000..221a101 --- /dev/null +++ b/MON_v8_c4eec374/font-6x7.inc @@ -0,0 +1,165 @@ + ; 96 symbols 6x7, with codes 0x20..0x7f KOI-7 H0 +m_font_cp0: + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; ' ' - 0x20 + DB 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x04 ; '!' - 0x21 + DB 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00 ; '"' - 0x22 + DB 0x0a, 0x0a, 0x1f, 0x0a, 0x1f, 0x0a, 0x0a ; '#' - 0x23 + DB 0x04, 0x1e, 0x05, 0x0e, 0x14, 0x0f, 0x04 ; '$' - 0x24 + DB 0x03, 0x13, 0x08, 0x04, 0x02, 0x19, 0x18 ; '%' - 0x25 + DB 0x06, 0x09, 0x05, 0x02, 0x15, 0x09, 0x16 ; '&' - 0x26 + DB 0x06, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00 ; ' - 0x27 + DB 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08 ; '(' - 0x28 + DB 0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02 ; ')' - 0x29 + DB 0x00, 0x0a, 0x04, 0x1f, 0x04, 0x0a, 0x00 ; '*' - 0x2a + DB 0x00, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00 ; '+' - 0x2b + DB 0x00, 0x00, 0x00, 0x00, 0x06, 0x04, 0x02 ; ',' - 0x2c + DB 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00 ; '-' - 0x2d + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06 ; '.' - 0x2e + DB 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00 ; '/' - 0x2f + DB 0x0e, 0x11, 0x19, 0x15, 0x13, 0x11, 0x0e ; '0' - 0x30 + DB 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x0e ; '1' - 0x31 + DB 0x0e, 0x11, 0x10, 0x08, 0x04, 0x02, 0x1f ; '2' - 0x32 + DB 0x1f, 0x08, 0x04, 0x08, 0x10, 0x11, 0x0e ; '3' - 0x33 + DB 0x08, 0x0c, 0x0a, 0x09, 0x1f, 0x08, 0x08 ; '4' - 0x34 + DB 0x1f, 0x01, 0x0f, 0x10, 0x10, 0x11, 0x0e ; '5' - 0x35 + DB 0x0c, 0x02, 0x01, 0x0f, 0x11, 0x11, 0x0e ; '6' - 0x36 + DB 0x1f, 0x10, 0x08, 0x04, 0x02, 0x02, 0x02 ; '7' - 0x37 + DB 0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e ; '8' - 0x38 + DB 0x0e, 0x11, 0x11, 0x1e, 0x10, 0x08, 0x06 ; '9' - 0x39 + DB 0x00, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00 ; ':' - 0x3a + DB 0x00, 0x06, 0x06, 0x00, 0x06, 0x04, 0x02 ; ';' - 0x3b + DB 0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08 ; '<' - 0x3c + DB 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00 ; '=' - 0x3d + DB 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02 ; '>' - 0x3e + DB 0x0e, 0x11, 0x10, 0x08, 0x04, 0x00, 0x04 ; '?' - 0x3f + DB 0x0e, 0x11, 0x10, 0x16, 0x15, 0x15, 0x0e ; '@' - 0x40 + DB 0x04, 0x0a, 0x11, 0x11, 0x1f, 0x11, 0x11 ; 'A' - 0x41 + DB 0x0f, 0x11, 0x11, 0x0f, 0x11, 0x11, 0x0f ; 'B' - 0x42 + DB 0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e ; 'C' - 0x43 + DB 0x07, 0x09, 0x11, 0x11, 0x11, 0x09, 0x07 ; 'D' - 0x44 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x1f ; 'E' - 0x45 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x01 ; 'F' - 0x46 + DB 0x0e, 0x11, 0x01, 0x1d, 0x11, 0x11, 0x1e ; 'G' - 0x47 + DB 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11 ; 'H' - 0x48 + DB 0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e ; 'I' - 0x49 + DB 0x1c, 0x08, 0x08, 0x08, 0x08, 0x09, 0x06 ; 'J' - 0x4a + DB 0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11 ; 'K' - 0x4b + DB 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1f ; 'L' - 0x4c + DB 0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11 ; 'M' - 0x4d + DB 0x11, 0x11, 0x13, 0x15, 0x19, 0x11, 0x11 ; 'N' - 0x4e + DB 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; 'O' - 0x4f + DB 0x0f, 0x11, 0x11, 0x0f, 0x01, 0x01, 0x01 ; 'P' - 0x50 + DB 0x0e, 0x11, 0x11, 0x11, 0x15, 0x09, 0x16 ; 'Q' - 0x51 + DB 0x0f, 0x11, 0x11, 0x0f, 0x05, 0x09, 0x11 ; 'R' - 0x52 + DB 0x1e, 0x01, 0x01, 0x0e, 0x10, 0x10, 0x0f ; 'S' - 0x53 + DB 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 'T' - 0x54 + DB 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; 'U' - 0x55 + DB 0x11, 0x11, 0x11, 0x11, 0x0a, 0x0a, 0x04 ; 'V' - 0x56 + DB 0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0a ; 'W' - 0x57 + DB 0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11 ; 'X' - 0x58 + DB 0x11, 0x11, 0x11, 0x0a, 0x04, 0x04, 0x04 ; 'Y' - 0x59 + DB 0x1f, 0x10, 0x08, 0x04, 0x02, 0x01, 0x1f ; 'Z' - 0x5a + DB 0x0e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0e ; '[' - 0x5b + DB 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00 ; '\' - 0x5c + DB 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0e ; ']' - 0x5d + DB 0x0e, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00 ; '^' - 0x5r + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f ; '_' - 0x5f + DB 0x1c, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00 ; '`' - 0x60 + DB 0x00, 0x00, 0x0e, 0x10, 0x1e, 0x13, 0x1e ; 'a' - 0x61 + DB 0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x0f ; 'b' - 0x62 + DB 0x00, 0x00, 0x0e, 0x01, 0x01, 0x01, 0x0e ; 'c' - 0x63 + DB 0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1e ; 'd' - 0x64 + DB 0x00, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x0e ; 'e' - 0x65 + DB 0x18, 0x04, 0x04, 0x0e, 0x04, 0x04, 0x04 ; 'f' - 0x66 + DB 0x00, 0x0e, 0x11, 0x11, 0x1e, 0x10, 0x0e ; 'g' - 0x67 + DB 0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x11 ; 'h' - 0x68 + DB 0x04, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04 ; 'i' - 0x69 + DB 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x06 ; 'j' - 0x6a + DB 0x01, 0x01, 0x09, 0x05, 0x03, 0x05, 0x09 ; 'k' - 0x6b + DB 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08 ; 'l' - 0x6c + DB 0x00, 0x00, 0x0f, 0x15, 0x15, 0x15, 0x15 ; 'm' - 0x6d + DB 0x00, 0x00, 0x09, 0x13, 0x11, 0x11, 0x11 ; 'n' - 0x6e + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e ; 'o' - 0x6f + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x0f, 0x01 ; 'p' - 0x70 + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x1e, 0x10 ; 'q' - 0x71 + DB 0x00, 0x00, 0x0d, 0x13, 0x01, 0x01, 0x01 ; 'r' - 0x72 + DB 0x00, 0x00, 0x1e, 0x01, 0x0e, 0x10, 0x0f ; 's' - 0x73 + DB 0x04, 0x04, 0x0e, 0x04, 0x04, 0x04, 0x18 ; 't' - 0x74 + DB 0x00, 0x00, 0x11, 0x11, 0x11, 0x19, 0x16 ; 'u' - 0x75 + DB 0x00, 0x00, 0x11, 0x11, 0x0a, 0x0a, 0x04 ; 'v' - 0x76 + DB 0x00, 0x00, 0x11, 0x15, 0x15, 0x15, 0x0a ; 'w' - 0x77 + DB 0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11 ; 'x' - 0x78 + DB 0x00, 0x00, 0x11, 0x11, 0x1e, 0x10, 0x0c ; 'y' - 0x79 + DB 0x00, 0x00, 0x1f, 0x08, 0x04, 0x02, 0x1f ; 'z' - 0x7a + DB 0x0c, 0x02, 0x02, 0x01, 0x02, 0x02, 0x0c ; '{' - 0x7b + DB 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; '|' - 0x7c + DB 0x03, 0x04, 0x04, 0x08, 0x04, 0x04, 0x03 ; '}' - 0x7d + DB 0x00, 0x02, 0x15, 0x0a, 0x15, 0x08, 0x00 ; '~' - 0x7e + DB 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15 ; [DEL] - 0x7f + +; 64 symbols 6x7, with codes 0x40..0x7f KOI-7 H1 +m_font_cp1: + DB 0x00, 0x00, 0x09, 0x15, 0x17, 0x15, 0x09 ; ю - 0x40 + DB 0x00, 0x00, 0x06, 0x08, 0x0e, 0x09, 0x16 ; а - 0x41 + DB 0x07, 0x02, 0x04, 0x0e, 0x09, 0x09, 0x06 ; б - 0x42 DIFF + DB 0x00, 0x00, 0x09, 0x09, 0x09, 0x1f, 0x10 ; ц - 0x43 + DB 0x03, 0x04, 0x08, 0x0e, 0x09, 0x09, 0x06 ; д - 0x44 DIFF + DB 0x00, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x1e ; е - 0x45 + DB 0x00, 0x04, 0x0e, 0x15, 0x15, 0x0e, 0x04 ; ф - 0x46 + DB 0x00, 0x00, 0x0f, 0x09, 0x01, 0x01, 0x01 ; г - 0x47 + DB 0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11 ; х - 0x48 + DB 0x00, 0x00, 0x11, 0x19, 0x15, 0x13, 0x11 ; и - 0x49 + DB 0x0a, 0x04, 0x11, 0x19, 0x15, 0x13, 0x11 ; й - 0x4a + DB 0x00, 0x00, 0x11, 0x09, 0x07, 0x09, 0x11 ; к - 0x4b + DB 0x00, 0x00, 0x1c, 0x12, 0x12, 0x12, 0x11 ; л - 0x4c + DB 0x00, 0x00, 0x11, 0x1b, 0x15, 0x11, 0x11 ; м - 0x4d + DB 0x00, 0x00, 0x11, 0x11, 0x1f, 0x11, 0x11 ; н - 0x4e + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e ; о - 0x4f + DB 0x00, 0x00, 0x1f, 0x11, 0x11, 0x11, 0x11 ; п - 0x50 + DB 0x00, 0x00, 0x1e, 0x11, 0x1e, 0x14, 0x12 ; я - 0x51 + DB 0x00, 0x00, 0x07, 0x09, 0x07, 0x01, 0x01 ; р - 0x52 + DB 0x00, 0x00, 0x0e, 0x01, 0x01, 0x01, 0x0e ; с - 0x53 + DB 0x00, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04 ; т - 0x54 + DB 0x00, 0x00, 0x11, 0x11, 0x1e, 0x10, 0x0e ; у - 0x55 + DB 0x00, 0x00, 0x15, 0x15, 0x0e, 0x15, 0x15 ; ж - 0x56 + DB 0x00, 0x00, 0x03, 0x05, 0x07, 0x09, 0x07 ; в - 0x57 + DB 0x00, 0x00, 0x01, 0x01, 0x07, 0x09, 0x07 ; ь - 0x58 + DB 0x00, 0x00, 0x11, 0x11, 0x13, 0x15, 0x13 ; ы - 0x59 + DB 0x00, 0x00, 0x0e, 0x11, 0x0c, 0x11, 0x0e ; з - 0x5a + DB 0x00, 0x00, 0x15, 0x15, 0x15, 0x15, 0x1f ; ш - 0x5b + DB 0x00, 0x00, 0x07, 0x08, 0x0e, 0x08, 0x07 ; э - 0x5c + DB 0x00, 0x00, 0x15, 0x15, 0x15, 0x1f, 0x10 ; щ - 0x5d + DB 0x00, 0x00, 0x09, 0x09, 0x0e, 0x08, 0x08 ; ч - 0x5e + DB 0x00, 0x00, 0x06, 0x05, 0x0c, 0x14, 0x0c ; ъ - 0x5f + DB 0x09, 0x15, 0x15, 0x17, 0x15, 0x15, 0x09 ; Ю - 0x60 + DB 0x04, 0x0a, 0x11, 0x11, 0x1f, 0x11, 0x11 ; А - 0x61 + DB 0x1f, 0x11, 0x01, 0x0f, 0x11, 0x11, 0x1f ; Б - 0x62 + DB 0x09, 0x09, 0x09, 0x09, 0x09, 0x1f, 0x10 ; С - 0x63 + DB 0x0c, 0x0a, 0x0a, 0x0a, 0x0a, 0x1f, 0x11 ; Д - 0x64 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x1f ; Е - 0x65 + DB 0x04, 0x0e, 0x15, 0x15, 0x15, 0x0e, 0x04 ; Ф - 0x66 + DB 0x1f, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01 ; Г - 0x67 + DB 0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11 ; Х - 0x68 + DB 0x11, 0x11, 0x19, 0x15, 0x13, 0x11, 0x11 ; И - 0x69 + DB 0x04, 0x15, 0x11, 0x19, 0x15, 0x13, 0x11 ; Й - 0x6a + DB 0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11 ; К - 0x6b + DB 0x1c, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11 ; Л - 0x6c + DB 0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11 ; М - 0x6d + DB 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11 ; Н - 0x6e + DB 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; О - 0x6f + DB 0x1f, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 ; П - 0x70 + DB 0x1e, 0x11, 0x11, 0x11, 0x1e, 0x12, 0x11 ; Я - 0x71 + DB 0x0f, 0x11, 0x11, 0x11, 0x0f, 0x01, 0x01 ; Р - 0x72 + DB 0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e ; С - 0x73 + DB 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; Т - 0x74 + DB 0x11, 0x11, 0x11, 0x11, 0x1e, 0x10, 0x0e ; У - 0x75 + DB 0x15, 0x15, 0x15, 0x0e, 0x15, 0x15, 0x15 ; Ж - 0x76 + DB 0x0f, 0x11, 0x11, 0x0f, 0x11, 0x11, 0x0f ; В - 0x77 + DB 0x01, 0x01, 0x01, 0x0f, 0x11, 0x11, 0x0f ; Ь - 0x78 + DB 0x11, 0x11, 0x11, 0x13, 0x15, 0x15, 0x13 ; Ы - 0x79 + DB 0x0e, 0x11, 0x10, 0x0c, 0x10, 0x11, 0x0e ; З - 0x7a + DB 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x1f ; Ш - 0x7b + DB 0x0e, 0x11, 0x10, 0x1c, 0x10, 0x11, 0x0e ; Э - 0x7c + DB 0x15, 0x15, 0x15, 0x15, 0x15, 0x1f, 0x10 ; Щ - 0x7d + DB 0x11, 0x11, 0x11, 0x1e, 0x10, 0x10, 0x10 ; Ч - 0x7e + DB 0x1f, 0x15, 0x1f, 0x15, 0x1f, 0x15, 0x1f ; [DEL] - 0x7f diff --git a/MON_v8_c4eec374/io.inc b/MON_v8_c4eec374/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/MON_v8_c4eec374/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/MON_v8_c4eec374/m_vars.inc b/MON_v8_c4eec374/m_vars.inc new file mode 100644 index 0000000..578bddd --- /dev/null +++ b/MON_v8_c4eec374/m_vars.inc @@ -0,0 +1,101 @@ +; ======================================================= +; Ocean-240.2 +; Module M_VARS - Monitor variables +; RAM Range: 0xBA09-0xBFFF +; +; Disassembled by Romych 2025-02-05 +; ======================================================= + + IFNDEF _M_VARS + DEFINE _M_VARS + + MODULE M_VARS + ORG 0xbf00 + +buffer DS 128 ; 0xbf00 search text or buffer to search? + DS 36 +paint_stack EQU $ ; 0xbfa4 + DS 32 +stack1: EQU $ ; 0xbfc4 + DS 1 +paint_var1 DS 1 ; 0xbfc5 +paint_var2 DS 1 ; 0xbfc6 +paint_var3 DS 1 ; 0xbfc7 +paint_var4 DS 1 ; 0xbfc8 +paint_var5 DS 1 ; 0xbfc9 +paint_y DS 1 ; 0xbfca +paint_var7 DS 1 ; 0xbfcb +cmp_color DS 1 ; 0xbfcc +paint_sp_save DS 2 + +; Right pixel mask ex: 11111000 +pixel_mask_r DS 1 ; 0xbfcf +tmp_color DS 1 ; 0xbfd0 +rect_var2 DS 1 ; 0xbfd1 +stack_0 EQU $ +rect_var3 DS 1 ; 0xbfd2 +esc_mode DS 1 ; 0xbfd3 +esc_cmd DS 1 ; 0xbfd4 + +esc_param_cnt DS 1 ; 0xbfd5 +esc_param DS 7 ; 0xbfd6 + +; Left inverse pixel mask ex: 00011111 +pixel_mask_l_i DS 1 +; Right inverse pixel mask ex: 00011111 +pixel_mask_r_i DS 1 +; Left pixel mask ex: 11100000 +pixel_mask_l DS 1 + +; Current screen mode, bit 3 - hide/show cursor, bit 4 - only 20 rows +screen_mode DS 1 ; 0xbfe0 +cursor_row DS 1 ; 0xbfe1 Cursor Y position +cursor_col DS 1 ; 0xbfe2 Cursor X position +curr_color DS 2 ; 0xbfe3 Current color low and hi bytes +esc_hex_cmd: +row_shift DS 1 ; 0xbfe5 +codepage DS 1 ; 0xbfe6 + + +cur_palette DS 1 ; 0xbfe7 00bbbfff - background and foreground colors +beep_period DS 2 ; 0xbfe8 +beep_duration DS 2 ; 0xbfea +pix_shift DS 1 ; 0xbfec +strobe_state DS 1 ; 0xbfed +ul_var0 DS 1 ; 0xbfee +ul_A_var1 DS 1 ; 0xbfef +ul_B_var2 DS 1 ; 0xbff0 +ul_var3 DS 1 ; 0xbff1 +ul_A_var4 DS 1 ; 0xbff2 +ul_B_var5 DS 1 ; 0xbff3 +ul_var6 DS 1 ; 0xbff4 +esc_var0 DS 1 ; 0xbff5 +esc_var1 DS 1 ; 0xbff6 +esc_var2 DS 1 ; 0xbff7 +esc_var3 DS 1 ; 0xbff8 + DS 1 ; 0xbff9 + DS 1 ; 0xbffa + DS 1 ; 0xbffb + DS 1 ; 0xbffc + DS 1 ; 0xbffd + DS 1 ; 0xbffe + DS 1 ; 0xbfff + + ASSERT stack1 = 0xbfc4 + ASSERT buffer = 0xbf00 + ASSERT paint_var1 = 0xbfc5 + ASSERT pixel_mask_r = 0xbfcf + ASSERT stack_0 = 0xbfd2 + ASSERT esc_mode = 0xbfd3 + ASSERT screen_mode = 0xbfe0 + ASSERT cur_palette = 0xbfe7 + ASSERT ul_var0 = 0xbfee + ASSERT paint_stack = 0xbfa4 + ASSERT esc_var3 = 0xbff8 + + ;DISPLAY "screen_mode: ", /H, screen_mode + ;DISPLAY "fn48_var1: ", /H, fn48_var1 + + ENDMODULE + + ENDIF diff --git a/MON_v8_c4eec374/monitor.asm b/MON_v8_c4eec374/monitor.asm new file mode 100644 index 0000000..8960185 --- /dev/null +++ b/MON_v8_c4eec374/monitor.asm @@ -0,0 +1,5593 @@ +; ====================================================== +; Ocean-240.2 +; Monitor V8.8 +; +; Disassembled by Romych 2026-02-05 +; ====================================================== + + DEVICE NOSLOT64K + + INCLUDE "io.inc" + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "bios_entries.inc" + + OUTPUT monitor_E000.bin + + + MODULE MONITOR + + ORG 0xE000 + +; ------------------------------------------------------ +; Monitor Entry points +; ------------------------------------------------------ + +start: JP m_hot_start ; E000 +mon_hexb: JP m_hexb ; E003 +non_con_status: JP m_con_status ; E006 +mon_con_in: JP m_con_in ; E009 +mon_con_out: JP m_con_out ; E00C +mon_serial_in: JP m_serial_in ; E00F +mon_serial_out: JP m_serial_out ; E012 +mon_char_print: JP m_char_print ; E015 +mon_tape_read: JP m_tape_read ; E018 +mon_tape_write: JP m_tape_write ; E01B +ram_disk_read: JP m_ramdisk_read ; E01E +ram_disk_write: JP m_ramdisk_write ; E021 +mon_tape_read_ram: JP m_tape_read_ram2 ; E024 +mon_tape_write_ram: JP m_tape_write_ram2 ; E027 +mon_tape_wait: JP m_tape_wait ; E02A +mon_tape_detect: JP m_tape_blk_detect ; E02D +read_floppy: JP m_read_floppy ; E030 +write_floppy: JP m_write_floppy ; E033 +mon_out_str_z: JP m_out_strz ; E036 + JP m_fn_39 ; E039 + JP get_image_hdr ; E03C + JP esc_picture ; E03F + JP m_print_at_xy ; E042 C-char esc_param[0]=X, [1]=Y + JP esc_draw_fill_rect ; E045 + JP esc_paint ; E048 + JP esc_draw_line ; E04B + JP esc_draw_circle ; E04E + + +; ------------------------------------------------------ +; Init system devices +; ------------------------------------------------------ +m_hot_start: + DI + ;LD SP, M_VARS.rst_ret_jp + LD A, 10000000b ; DD17 all ports to out + OUT (SYS_DD17CTR), A ; VV55 Sys CTR + OUT (DD67CTR), A ; VV55 Video CTR + + ; init_kbd_tape + LD A, 0x93 + OUT (KBD_DD78CTR), A + + + LD A, 01111111b ; VSU=0, C/M=1, FL=111, COL=111 + OUT (VID_DD67PB), A ; color mode + LD A, 00000001b + OUT (SYS_DD17PB), A ; Access to VRAM + LD B, 0x0 ; TODO: replace to LD HL, 0x3f00 LD B,L + LD HL, 0x3f00 + LD A, H + ADD A, 0x41 ; A=128 0x80 + + ; Clear memory from 0x3F00 to 0x7FFF +.fill_video: + LD (HL), B + INC HL + CP H + JP NZ, .fill_video + + ;XOR A + LD A, 0 + OUT (SYS_DD17PB), A ; Disable VRAM + LD A, 00000111b + OUT (SYS_DD17PC), A ; pix shift to 7 + LD (M_VARS.pix_shift), A + + XOR A + LD (M_VARS.screen_mode), A + LD (M_VARS.row_shift), A + + ; Set color mode and palette + LD (M_VARS.curr_color+1), A + CPL + LD (M_VARS.curr_color), A + LD A, 00000011b + LD (M_VARS.cur_palette), A + ; VSU=0, C/M=1, FL=000, COL=011 + ; color mode, black border + ; 00-black, 01-red, 10-purple, 11-white + LD A, 01000011b + OUT (VID_DD67PB), A + + ; config LPT + LD A, 0x4 + OUT (DD67PC), A ; bell=1, strobe=0 + LD (M_VARS.strobe_state), A ; store strobe + LD HL, 1024 ; 683us + LD (M_VARS.beep_period), HL + LD HL, 320 ; 213us + LD (M_VARS.beep_duration), HL + +.conf_uart: + ; Config UART + LD A, 11001110b + OUT (UART_DD72RR), A + LD A, 00100101b + OUT (UART_DD72RR), A + + ; Config Timer#1 for UART clock + LD A, 01110110b ; tmr#1, load l+m bin, sq wave + OUT (TMR_DD70CTR), A + + ; 1.5M/20 = 75kHz + LD A, 20 + OUT (TMR_DD70C2), A + XOR A + OUT (TMR_DD70C2), A +.conf_pic: + ; Config PIC + LD A,00010010b ; ICW1 edge trigger, interval 8, sin... + OUT (PIC_DD75RS), A + XOR A + OUT (PIC_DD75RM), A ; ICW2 + CPL + OUT (PIC_DD75RM), A ; ICW3 no slave + LD A,00100000b + OUT (PIC_DD75RS), A ; Non-specific EOI command, End of I... + LD A, PIC_POLL_MODE + OUT (PIC_DD75RS), A ; Poll mode, poll on next RD + + LD A, 0x80 + OUT (KBD_DD78PC), A ; TODO: - Check using this 7th bit + NOP + NOP + XOR A + OUT (KBD_DD78PC), A + + ; Init cursor + LD SP, M_VARS.stack1 + CALL m_draw_cursor + + ; Beep + LD C, ASCII_BELL + CALL m_con_out + + LD A, (BIOS.boot_f) + CP JP_OPCODE + JP Z, BIOS.boot_f + LD HL, mgs_system_nf + CALL m_out_strz + JP m_sys_halt + +; -------------------------------------------------- +; Output ASCIIZ string +; Inp: HL -> string +; -------------------------------------------------- +m_out_strz: + LD C, (HL) + LD A, C + OR A + RET Z + CALL m_con_out + INC HL + JP m_out_strz + +mgs_system_nf: + DB "\r\nSYSTEM NOT FOUND\r\n", 0 + +m_sys_halt: + HALT + +; ------------------------------------------------------ +; Console status +; Out: A = 0 - not ready +; A = 0xFF - ready (key pressed) +; ------------------------------------------------------ +m_con_status: + IN A, (PIC_DD75RS) ; Read PIC status + NOP + AND KBD_IRQ ; Check keyboard request RST1 + LD A, 0 + RET Z ; no key pressed + CPL + RET ; key pressed + +; ------------------------------------------------------ +; Wait and read data from UART +; Out: A - 7 bit data +; ------------------------------------------------------ +m_serial_in: + IN A, (UART_DD72RR) + AND RX_READY + JP Z, m_serial_in ; wait for rx data ready + IN A, (UART_DD72RD) + AND 0x7f ; leave 7 bits + RET + +; ------------------------------------------------------ +; Read key +; Out: A +; ------------------------------------------------------ +m_con_in: + CALL m_con_status + OR A + JP Z, m_con_in ; wait key + IN A, (KBD_DD78PA) ; get key + AND 0x7f ; reset hi bit, leave 0..127 code + PUSH AF + ; TODO: Check if it is keyboard ACK + ; PC7 Set Hi (ACK?) + LD A, 0x80 + OUT (KBD_DD78PC), A + ; PC7 Set Lo + XOR A + OUT (KBD_DD78PC), A + POP AF + RET + +; ------------------------------------------------------ +; Send data by UART +; Inp: C - data to transmitt +; ------------------------------------------------------ +m_serial_out: + IN A, (UART_DD72RR) + AND TX_READY + JP Z, m_serial_out ; Wait for TX ready + LD A, C + OUT (UART_DD72RD), A + RET + +; ------------------------------------------------------ +; Send character to printer +; Inp: C - character +; ------------------------------------------------------ +m_char_print: + ; wait printer ready + IN A, (PIC_DD75RS) + AND PRINTER_IRQ + JP Z, m_char_print + + LD A, C + NOP + OUT (LPT_DD67PA), A + ; set LP strobe + LD A, 00010100b + OUT (DD67PC),A + +.wait_lp: + ; wait printer ack + IN A, (PIC_DD75RS) + AND PRINTER_IRQ + JP NZ, .wait_lp + ; remove LP strobe + LD A, 00000100b + OUT (DD67PC), A + RET + +; ------------------------------------------------------ +; Out char to console +; Inp: C - char +; ------------------------------------------------------ +m_con_out: + PUSH HL + PUSH DE + PUSH BC + CALL m_con_out_int + POP BC + POP DE + POP HL + RET + +; ------------------------------------------------------ +; Out char C to console +; ------------------------------------------------------ +m_con_out_int: + LD DE, M_VARS.esc_mode + LD A, (DE) + DEC A + OR A ; TODO: unused (save 1b 4t) + JP M, m_print_no_esc ; esc_mode=0 - standart print no ESC mode + JP NZ, m_print_at_xy ; esc_mode=2 (graphics) + + ; handle ESC param (esc_mode=1) + INC DE ; TODO: replace to INC E E=0xd3 save 2t + LD A, (DE) + OR A + JP P, get_esc_param + LD A, C + AND 0xf ; convert char to command code + LD (DE), A + INC DE ; TODO: replace to INC E E=0xd3 save 2t + XOR A + LD (DE), A + RET + +get_esc_param: + LD HL, M_VARS.esc_cmd + LD B, (HL) ; TODO: replace to INC L L=0xd4 save 2t + INC HL ; HL -> param count + LD A, (HL) + INC A + LD (HL), A + ; store new param + LD E, A + LD D, 0x0 + ADD HL, DE ; HL -> parameter[param_count] + LD (HL), C ; store letter as esc parameter + ; get params count for esc command + LD HL, esc_params_tab + LD E, B ; d=0, b = cmd + ADD HL, DE ; DE - command offset + CP (HL) + ; return if enough + RET M + +;esc_set_mode: + LD HL, M_VARS.esc_cmd + LD A, (HL) + AND 0x0f ; mask (cmd=0..15) + LD E, A + DEC HL ; HL -> esc_mode + OR A + LD (HL), 2 ; mode=2 for cmd=0 + RET Z ; just return, no handler there + + LD D, 0 ; TODO: remove, D already 0 + LD (HL), D ; reset mode to 0 for other + DEC DE ; DE = cmd-1 + +;co_get_hdlr: + ; Calc ESC command handler offset + LD HL, esc_handler_tab + ADD HL, DE + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) + ; HL = addr of handler func + EX DE, HL + ; It is 1..4 func DRAW_* func? + CP 0x4 + JP P, esc_no_draw_fn + LD A, (M_VARS.screen_mode) + AND 00000011b + ; If not in graphics mode - exit + JP NZ, esc_exit + +esc_no_draw_fn: + LD DE, esc_exit + PUSH DE + + ; Jump to ESC func handler + JP (HL) + +esc_exit: + XOR A + LD (M_VARS.esc_mode), A + RET + + ; Count of parameters for ESC commands + ; 0xe1cb +esc_params_tab: + DB 3, 5, 4, 3, 1, 2, 1, 1 + DB 1, 2, 1, 5, 5, 7, 6, 4 + +esc_handler_tab: + DW esc_draw_fill_rect ;5 1x1y1x2y2m + DW esc_draw_line ;4 2x1y1x2y2 + DW esc_draw_dot ;3 3xxyy + DW esc_set_color ;1 4N N=1..4 + DW esc_set_cursor ;2 5rc r-Row, c-Col + DW esc_set_vmode ;1 6m m-mode: + ; C 0 - 40x25 cursor on + ; M 1,2 - 64x25 cursor on + ; M 3 - 80x25 cursor on + ; C 4 - 40x25 cursor off + ; M 5,6 - 64x25 cursor off + ; M 7 - 80x25 cursor off + ; M 8 - 20rows mode + ; 9 - cursor off + ; 10 - cursor on + DW esc_set_charset ;1 7n where n is: + ; 0 - LAT Both cases + ; 1 - RUS Both cases + ; 2 - LAT+RUS Upper case + DW esc_set_palette ;1 8c c - Foreground+Backgound + DW esc_set_cursor2 ;2 9xy + DW esc_print_screen ;1 : + DW esc_draw_circle ;5 ;xyraxay X,Y, Radius, aspect ratio X, aspect ratio Y + DW esc_paint ;5 = + DW esc_picture ;6 > + DW esc_set_beep ;4 ?ppdd pp-period (word), dd - duration (word) + +esc_set_beep: + ; param byte 1+2 -> period + LD DE, M_VARS.esc_param + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (M_VARS.beep_period), HL + ; param byte 3+4 -> duration + INC DE + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (M_VARS.beep_duration), HL + RET + +esc_set_cursor2: + JP esc_set_cursor + +esc_print_screen: + LD A, (M_VARS.screen_mode) + AND 00000011b + RET NZ ; ret if not 0-3 mode + LD DE, 0x30ff + CALL m_print_hor_line + DEC E + LD D, 0xf0 + +.chk_keys: + CALL m_con_status + OR A + JP Z, .no_keys + CALL m_con_in + CP ASCII_ESC + RET Z + +.no_keys: + CALL m_print_hor_line + DEC E + JP NZ, .chk_keys + LD D, 0xe0 ; 224d + CALL m_print_hor_line + RET + +; ------------------------------------------------------ +; Print line to printer +; D - width +; ------------------------------------------------------ +m_print_hor_line: + LD HL, cmd_esc_set_X0 + + ; Set printer X coordinate = 0 + CALL m_print_cmd + LD HL, 4 + LD (M_VARS.ul_var0), HL ; Set start coord X = 4 + LD B, 0x0 ; TODO: LD B, H (save 1b 3t) + +.print_next_col: + LD C, 0x0 + ; 1 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (M_VARS.ul_var0) + INC HL + + ; inc X + LD (M_VARS.ul_var0), HL + LD C, 0x1 + ; 2 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (M_VARS.ul_var0) + INC HL + ; inc X + LD (M_VARS.ul_var0), HL + INC B + LD A, B + CP 236 + JP C, .print_next_col + LD HL, cmd_esc_inc_Y2 + CALL m_print_cmd + RET + +; ------------------------------------------------------ +; Send command to printer +; Inp: HL -> command bytes array +; ------------------------------------------------------ +m_print_cmd: + PUSH BC +.print_nxt: + LD A, (HL) + CP ESC_CMD_END + JP Z, .cmd_end + LD C, A + CALL m_char_print + INC HL + JP .print_nxt +.cmd_end: + POP BC + RET + +; ------------------------------------------------------ +; Print 7 vertical pixels to printer +; Inp: A - value to print +; ------------------------------------------------------ +m_print_vert_7pix: + PUSH AF + ; Set coordinate X to 0 + LD HL, cmd_esc_set_X + CALL m_print_cmd + LD HL, (M_VARS.ul_var0) + LD C,H + CALL m_char_print + LD C,L + CALL m_char_print + ; Set column print mode + LD HL, cmd_esc_print_col + CALL m_print_cmd + POP AF + ; Print 7 vertical pixels + LD C, A + CALL m_char_print + RET + +; ------------------------------------------------------ +; Control codes for printer УВВПЧ-30-004 +; ------------------------------------------------------ +; Zn - Increment Y coordinate +; 0xe2a5 +cmd_esc_inc_Y2: + DB ASCII_ESC + DB 'Z' + DB 2h + DB ESC_CMD_END + +; Xnn - Set X coordinate +cmd_esc_set_X0: + DB ASCII_ESC + DB 'X' + DB 0h ; 0..479 + DB 0h + DB ESC_CMD_END + +; ------------------------------------------------------ +; X - Start on "Set X coordinate" command +; ------------------------------------------------------ +cmd_esc_set_X: + DB ASCII_ESC + DB 'X' + DB ESC_CMD_END + +; O - Column print (vertical 7 bit) +cmd_esc_print_col: + DB ASCII_ESC + DB 'O' + DB ESC_CMD_END + +; ------------------------------------------------------ +; Get 7 vertical pixels from screen +; Inp: C - sheet +; Out: A - byte +; ------------------------------------------------------ +m_get_7vpix: + LD A, (M_VARS.row_shift) + ADD A, B + ADD A, 19 ; skip first 20pix + LD L, A + PUSH DE + PUSH BC + LD A, E + +.calc_pix_no: + AND 0x7 + LD B, A + LD A, E + ; calc hi addr + RRA ; /8 + RRA + RRA + AND 0x1f + ADD A, A ; *2 + ADD A, 64 ; bytes per row + LD H, A + ; select sheet 0|1 + LD A, C + AND 0x1 + ADD A, H + LD H, A + ; HL = pix addr, turn on VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + LD E, (HL) ; read pixel + INC H ; HL += 512 + INC H + LD D, (HL) ; read pixel row+1 + + ; turn off VRAM access + ;v8 XOR A + LD A, 0 + OUT (SYS_DD17PB), A +.for_all_pix: + DEC B + JP M, .all_shifted + ; shift pixels D >> [CF] >> E + LD A, D + RRA + LD D, A + LD A, E + RRA + LD E, A + JP .for_all_pix +.all_shifted: + LD A, E + LD D, 0 + RRA + JP NC,.not_1_1 + LD D,00110000b +.not_1_1: + RRA + JP NC, .not_1_2 + LD A, D + OR 11000000b + LD D, A +.not_1_2: + LD A, D + POP BC + POP DE + RET + +esc_set_palette: + LD A, (M_VARS.esc_param) + AND 00111111b ; bgcol[2,1,0],pal[2,1,0] + LD (M_VARS.cur_palette), A + LD B, A + LD A, (M_VARS.screen_mode) + AND 00000011b + LD A, 0x0 + JP NZ, esp_no_colr + LD A, 0x40 + +esp_no_colr: + OR B + OUT (VID_DD67PB), A + RET + +esc_set_charset: + LD A, (M_VARS.esc_param) + AND 0x3 ; charset 0..3 + LD (M_VARS.codepage), A + RET + +; ------------------------------------------------------ +; Get address for draw symbol glyph +; Inp: A - ascii code +; Out: HL -> glyph offset +; ------------------------------------------------------ +m_get_glyph: + LD L, A ; L = ascii code + LD E, A ; E = ascii code + XOR A + LD D, A + LD H, A + ; HL = DE = ascii code + ADD HL, HL + ADD HL, DE + ADD HL, HL + ADD HL, DE + ; HL = A * 7 + LD A, E ; A = A at proc entry + CP '@' + ; First 64 symbols is same for all codepages + JP M, .cp_common + LD A, (M_VARS.codepage) + OR A + ; cp=0 - Latin letters + JP Z, .cp_common + DEC A + ; cp=1 - Russian letters + JP Z, .cp_rus + ; cp=2 - 0x40..0x5F - displayed as Lat + ; 0x60 - 0x7F - displayed as Rus + LD A, E + CP 0x60 + JP M, .cp_common +.cp_rus: + LD DE, 448 ; +448=64*7 Offset for cp1 + ADD HL, DE + +.cp_common: + LD DE, m_font_cp0-224 ; m_font_cp0-32*7 + ADD HL, DE ; add symbol glyph offset + RET + + +; -------------------------------------------------- +; Console output +; Inp: C - char +; -------------------------------------------------- +m_print_no_esc: + LD A, C + AND 0x7f ; C = 0..127 ASCII code + CP ASCII_SP ; C < ' '? + JP M, m_handle_esc_code ; jump if less + CALL m_get_glyph + EX DE, HL + LD A, (M_VARS.screen_mode) + AND 0x3 + JP NZ, mp_mode_64 ; jump to non color modes + + CALL calc_addr_40 + INC L + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + DEC H + DEC H + ; one or two bytes + LD A, B + OR B + JP Z, .l1 + DEC B + JP Z, .l2 + DEC B + JP Z, .l3 + JP .l4 +.l1: + INC H + INC H + LD BC, 0xffc0 + LD A, 0x0 + JP .l5 +.l2: + LD BC, 0xf03f + LD A, 0x6 + JP .l5 +.l3: + LD BC, 0xfc0f + LD A, 0x4 + JP .l5 +.l4: + LD BC, 0xff03 + LD A, 0x2 +.l5: + LD (M_VARS.esc_var1), A + EX DE, HL + +.sym_draw: + LD A, (M_VARS.esc_var1) + PUSH HL + LD L, (HL) + LD H, 0x0 + OR A + JP Z, .pne_l8 + +.pne_l7: + ADD HL, HL + DEC A + JP NZ, .pne_l7 + +.pne_l8: + EX DE, HL + LD A, (HL) + AND C + LD (HL), A + LD A, (M_VARS.curr_color) + AND E + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND C + LD (HL), A + LD A, (M_VARS.curr_color+1) + AND E + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, (M_VARS.curr_color) + AND D + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, (M_VARS.curr_color+1) + AND D + OR (HL) + LD (HL), A + INC L + DEC H + DEC H + DEC H + EX DE, HL + POP HL + INC HL + LD A, (M_VARS.esc_var0) + DEC A + LD (M_VARS.esc_var0), A + JP NZ, .sym_draw + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + ; draw cursor on return + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_rt: + INC HL + LD A, (HL) ; a = col + ADD A, 1 ; col+1 + AND 0x3f ; screen column 0..63 + LD (HL), A ; save new col + CP 40 + DEC HL + RET M ; Return if no wrap + +m40_wrap_rt: + INC HL + XOR A + LD (HL), A + DEC HL + LD A, (M_VARS.screen_mode) + AND 0x08 ; screen_mode=8? + JP NZ, m2_lf + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_bksp: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A + AND 0x3f ; A=0..63 + CP 0x3f + JP Z, .wrap + LD (HL), A + DEC HL + RET + +.wrap: + LD A, 39 + LD (HL), A + DEC HL + ; and cursor up + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_up: + LD A, (HL) + SUB 10 ; 10 rows per symbol + JP NC, .up_no_minus + LD A, 240 ; wrap to bottom +.up_no_minus: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor right 8 pos) 20rows mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m20_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x3f ; wrap A=0..63 + LD (HL), A + CP 40 + DEC HL + RET M ; ret if column <40 + JP m40_wrap_rt ; or wrap to next line + +; -------------------------------------------------- +; Calculate VRAM address in 40 column mode +; -------------------------------------------------- +calc_addr_40: + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + CP 4 + LD B, A + JP M, .l2 + AND 0x3 + LD B, A + LD A, H + OR A + RRA + OR A + RRA + LD C, A + LD H, 0x6 + XOR A + +.l1: + ADD A, H + DEC C + JP NZ, .l1 + ADD A, B + +.l2: + ADD A, B + ADD A, 66 + LD H, A + LD A, 0x7 + LD (M_VARS.esc_var0),A + RET + +m2_lf: + LD A, (HL) + ADD A, 10 + CP 15 + JP NC, .lf_nowr + LD (HL), A + RET + +.lf_nowr: + LD A, (M_VARS.row_shift) + LD L, A + ADD A, ASCII_LF + LD E, A + LD C, ASCII_BS + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.cas_l5: + LD B, 0x40 + LD H, 0x40 ; TODO: LD H, B save 1b 3t + LD D, H + +.cas_l6: + LD A, (DE) + LD (HL), A + INC H + INC D + DEC B + JP NZ, .cas_l6 + INC L + INC E + DEC C + JP NZ, .cas_l5 + LD C, 10 + LD A, (M_VARS.row_shift) + ADD A, 8 + LD E, A + +.cas_l7: + LD B, 0x40 + LD D, 0x40 ; TODO: LD D, B save 1b 3t + XOR A + +.cas_l8: + LD (DE),A + INC D + DEC B + JP NZ,.cas_l8 + INC E + DEC C + JP NZ,.cas_l7 + LD A,0x0 + OUT (SYS_DD17PB),A + RET + + +; --------------------------------------------------- +; Handle ASCII_BS (cursor left) in 20row mode +; --------------------------------------------------- +m20_bksp: + INC HL + LD A, (HL) + OR A + DEC HL + RET Z + + INC HL + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x3f + LD (HL), A + DEC HL + RET + +; --------------------------------------------------- +; Print symbol in 64x25 mode +; --------------------------------------------------- +mp_mode_64: + CP 3 ; + JP Z, mp_mode_80 ; jump for screen_mode=3 + ; calc symbol address in VRAM + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + ADD A, 0x40 + LD H, A + ; + LD C, 7 ; symbol height + + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + EX DE, HL + XOR A + LD (DE), A + INC E + +.next_row: + LD A, (HL) + ADD A, A + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .next_row + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + ; draw cursor at end + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_rt: + INC HL + LD A, (HL) + ADD A, 1 + AND 0x3f ; wrap + LD (HL), A + DEC HL + RET NZ ; ret if no wrap + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Scroll Up for 10 rows +; -------------------------------------------------- +scroll_up: + LD A, (M_VARS.row_shift) + ADD A, 10 + OUT (SYS_DD17PA), A ; Scroll via VShift register + LD (M_VARS.row_shift), A ; store new VShift value + ; calc bottom 16 rows address in VRAM + LD HL, 0x40f0 ; 240th VRAM byte + ADD A, L + LD L, A + LD C, H + + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + XOR A + LD DE, 0x1040 ; D=16 E=64 (512/8 bytes in row) + +.next_row: + LD H, C + LD B, E + + ; clear 64 bytes (512px in mono or 256px in color mode) +.next_col: + LD (HL), A + INC H ; next column + DEC B + JP NZ, .next_col + INC L ; next row address + DEC D ; row counter - 1 + JP NZ, .next_row + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_bs: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x3f ; wrap column (0..63) + LD (HL), A + CP 63 + DEC HL + RET NZ + ; cursor up if wrapped + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_up: + LD A, (HL) + SUB 10 + JP NC, .no_wrap + LD A, 240 + +.no_wrap: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor column + 8) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x38 + LD (HL), A + DEC HL + RET NZ ; return if no wrap + ; cursor down if wrap + JP m64_lf + +; -------------------------------------------------- +; Print symbols in 80x25 mode +; -------------------------------------------------- +mp_mode_80: + CALL calc_addr_80 + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + ; fix address + EX DE, HL + INC E + ; make bitmask + LD A, B + OR A + JP Z, .l1 + DEC A + JP Z, .l2 + DEC A + JP Z, .l3 + JP .l4 + +.l1: + LD B, (HL) + LD A, (DE) + AND 0xc0 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .l1 + JP .l6 +.l2: + LD A, (HL) + RRCA + RRCA + AND 0x7 + LD B, A + LD A, (DE) + AND 0xf0 + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + AND 0xc0 + LD B, A + DEC D + LD A, (DE) + AND 0x1f + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, .l2 + JP .l6 +.l3: + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0x1 + LD B, A + LD A, (DE) + AND 0xfc + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0xf0 + LD B, A + DEC D + LD A, (DE) + AND 0x7 + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, .l3 + JP .l6 +.l4: + DEC D +.l5: + LD A, (HL) + RLCA + RLCA + LD B, A + LD A, (DE) + AND 0x1 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .l5 + INC D + +.l6: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + ; Draw cursor after symbol + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_rt: + INC HL + LD A, (HL) + ADD A, 1 ; inc column + AND 0x7f + LD (HL), A + CP 80 + DEC HL + RET M ; return if no wrap + +m80_col_wrap: + INC HL + XOR A + LD (HL), A + DEC HL + ; and move cursor to next row + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_bs: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x7f ; mask [0..127] + CP 127 + JP Z, .wrap + LD (HL), A + DEC HL + RET + +.wrap: + LD A, 79 + LD (HL), A + DEC HL + ; and move cursor to previous line + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_up: + LD A, (HL) + SUB 10 + JP NC, .no_wrap + LD A, 240 + +.no_wrap: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor column + 8) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x7f + LD (HL), A + CP 80 + DEC HL + RET M ; return if no cursor wrap + JP m80_col_wrap + +; -------------------------------------------------- +; Calculate address for cursor pos for 80x25 mode +; Out: HL -> VRAM +; B -> pixel pos in byte +; -------------------------------------------------- +calc_addr_80: + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + CP 4 + LD B, A + JP M, mns_ep_fm_0 + AND 3 + LD B, A + LD A, H + OR A + RRA + OR A + RRA + LD C, A + LD H, 3 + XOR A +mns_l1: + ADD A, H + DEC C + JP NZ, mns_l1 + ADD A, B +mns_ep_fm_0: + ADD A, 0x42 + LD H, A + LD C, 0x7 + RET + +; -------------------------------------------------- +; Clear screen and set cursor to 0,0 +; Inp: HL -> cursor position +; -------------------------------------------------- +m_clear_screen: + LD A, (M_VARS.screen_mode) + AND 0x8 + JP NZ, m_clear_20_rows ; for bit 4 is set, clear only 20 rows + ; all in black + LD A, 01111111b + OUT (VID_DD67PB), A ; C/M=1 FL=111 CL=111 All black + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD DE, video_ram + EX DE, HL + LD A, H + ADD A, 0x40 ; A=0x80 + LD B, 0 + +.fill_scrn: + LD (HL), B + INC HL + CP H + JP NZ, .fill_scrn ; fill while HL<0x8000 + + EX DE, HL + LD A, (M_VARS.cur_palette) + LD B, A ; B = current palette + LD A, (M_VARS.screen_mode) + AND 0x3 ; color? + LD A, 0x0 + JP NZ, .mono_mode + LD A, 01000000b +.mono_mode: + OR B + ; Restore mode and palette + OUT (VID_DD67PB), A + + ; And set cursor to home position + +; -------------------------------------------------- +; Set cursor to 0,0 and close VRAM access +; Inp: HL -> cursor_row +; -------------------------------------------------- +m_cursor_home: + XOR A + NOP + NOP + LD (HL), A + INC HL + XOR A + LD (HL), A + DEC HL + ;XOR A + LD A, 0 + ; Disable VRAM access + OUT (SYS_DD17PB), A + RET + +; Clear only 20 rows +m_clear_20_rows: + ; take row shift in account + LD A, (M_VARS.row_shift) + LD L, A + LD C, 20 + + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.next_row: + LD H, 0x40 ; HL = 0x4000 + shift_row + LD B, 64 ; 64 bytes at row + XOR A +.next_col: + LD (HL), A + INC H ; next column + DEC B + JP NZ, .next_col + INC L ; next row + DEC C + JP NZ, .next_row + ; Disabe VRAM access + LD A, 0 + OUT (SYS_DD17PB), A + JP m_cursor_home + +; -------------------------------------------------- +; Draw cursor at current cursor position +; if not hidden +; -------------------------------------------------- +m_draw_cursor: + LD A, (M_VARS.screen_mode) + AND 0x4 ; check hidden cursor bit + RET NZ ; return if hidden + LD A, (M_VARS.screen_mode) + AND 0x3 ; check color mode (40 column mode 6x7 font) + JP NZ, .dc_mode_64 + + EX DE, HL + LD HL, (M_VARS.cursor_row) + LD A, H ; cursor column + CP 40 ; > 40? + EX DE, HL + RET P ; ret if column out of screen + + PUSH HL + EX DE, HL + CALL calc_addr_40 + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; previous address + DEC H + DEC H + INC L + LD C, 7 ; cursor size + ; build masks + LD A, B + OR B + JP Z, .dc_rt2 + DEC B + JP Z, .dc_mid + DEC B + JP Z, .dc_lt + JP .dc_rt1 +.dc_rt2: + INC H + INC H + LD DE, 0x001f + JP .dc_put +.dc_mid: + LD DE, 0x07c0 + JP .dc_put +.dc_lt: + LD DE, 0x01f0 + JP .dc_put +.dc_rt1: + LD DE, 0x007c + +.dc_put: + ; xor cursor mask with VRAM[HL] value + ; left bytes + LD A, (HL) + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR E + LD (HL), A + ; right bytes + INC H + LD A, (HL) + XOR D + LD (HL), A + INC H + LD A, (HL) + XOR D + LD (HL), A + ; next cursor row address + INC L + DEC H + DEC H + DEC H + DEC C + JP NZ, .dc_put ; draw next cursor row if c>0 + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + RET + + ; draw cursor in 64 column mode +.dc_mode_64: + CP 3 ; screen_mode = 3 - 80 rows + JP Z, .dc_mode_80 + EX DE, HL + LD HL, (M_VARS.cursor_row) ; H - col, L - row + ; take into account the vertical shift + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + ; + LD A, H + CP 64 ; check column + EX DE, HL + RET P ; return if column out of screen + EX DE, HL + ; calc VRAM address + ADD A, 0x40 + LD H, A + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD BC, 0x7f08 ; B=01111111b - mask, C=8 - cursor size +.cur_64_next: + ; xor with VRAM content + LD A, (HL) + XOR B + LD (HL), A + ; next row address + INC L + DEC C + JP NZ, .cur_64_next + EX DE, HL + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + + ; draw cursor in 80 column mode +.dc_mode_80 + EX DE, HL + LD HL, (M_VARS.cursor_row) + + LD A, H + CP 80 + EX DE, HL + RET P ; return if column > 80 + + PUSH HL + CALL calc_addr_80 + LD C, 7 ; cursor size + INC L + + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; mask + LD A, B + OR A + LD B, 0x1f + JP Z, .dc_1_byte + DEC A + LD DE, 0xc007 + JP Z, .dc_2_byte + DEC A + LD DE, 0xf001 + JP Z, .dc_2_byte + LD B, 0x7c + DEC H + JP .dc_1_byte ; TODO: unused + +.dc_1_byte: + ; xor with VRAM byte + LD A, (HL) + XOR B + LD (HL), A + INC L + DEC C + JP NZ, .dc_1_byte + JP .dc_80_end + +.dc_2_byte: + ; xor with previous byte + DEC H + LD A, (HL) + XOR D + LD (HL), A + ; xor with current byte + INC H + LD A, (HL) + XOR E + LD (HL), A + ; next cursor address + INC L + DEC C + JP NZ, .dc_2_byte + +.dc_80_end: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + RET + +; -------------------------------------------------- +; If ESC character, turn esc_mode ON +; Inp: A - ASCII symbol +; -------------------------------------------------- +m_handle_esc_code: + CP ASCII_ESC + JP NZ, m_handle_control_code + ; turn on ESC mode for next chars + LD HL, M_VARS.esc_mode + LD (HL), 0x1 ; turn on ESC mode + INC HL + LD (HL), 0xff ; esc_cmd = 0xff + RET + +; -------------------------------------------------- +; Handle one byte ASCII control code +; Inp: A - ASCII symbol +; -------------------------------------------------- +m_handle_control_code: + CP ASCII_BELL + JP Z, m_beep + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + PUSH AF + CALL m_draw_cursor + LD A, (M_VARS.screen_mode) + AND 0x08 ; 20-rows mode? + JP Z, handle_cc_common ; jump for normal screen modes + + ; for hidden cursor modes + POP AF + CP ASCII_TAB ; TAB + JP Z, m20_tab + CP ASCII_BS ; BKSP + JP Z, m20_bksp + CP ASCII_CAN ; Cancel + JP Z, m40_rt + CP ASCII_US ; ASCII Unit separator + JP Z, m_clear_20_rows + CP ASCII_LF ; LF + JP Z, m2_lf + CP ASCII_CR ; CR + RET NZ ; ret on unknown + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; Handle cursor for 40x25, 64x25, 80x25 modes +; -------------------------------------------------- +handle_cc_common: + POP AF + CP ASCII_US + JP Z, m_clear_screen + CP ASCII_FF + JP Z, m_cursor_home + PUSH AF + LD A, (M_VARS.screen_mode) + AND 3 ; check for color modes + JP NZ, .handle_cc_mono + ; 32x25 text mode + POP AF + CP ASCII_TAB ; cursor right +8 + JP Z, m20_tab + CP ASCII_BS ; cursor left + JP Z, m40_bksp + CP ASCII_CAN ; cursor right + JP Z, m40_rt + CP ASCII_EM ; cursor up + JP Z, m40_up + CP ASCII_SUB + JP Z, m40_lf ; cursor down + CP ASCII_LF + JP Z, m40_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 ; move cursor to first column for CR + DEC HL + RET + +; -------------------------------------------------- +; Handle control chars for 64x25 or 80x25 modes +; -------------------------------------------------- +.handle_cc_mono: + LD A, (M_VARS.screen_mode) + CP 3 + JP Z, handle_cc_80x25 + CP 7 + JP Z, handle_cc_80x25 + ; 64x25 screen mode + POP AF + CP ASCII_TAB + JP Z, m64_tab + CP ASCII_BS + JP Z, m64_bs + CP ASCII_CAN + JP Z, m64_rt + CP ASCII_EM + JP Z, m64_up + CP ASCII_SUB + JP Z, m64_lf + CP ASCII_LF + JP Z, m64_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; Handle control chars for 80x25 modes +; -------------------------------------------------- +handle_cc_80x25: + POP AF + CP ASCII_TAB + JP Z, m80_tab + CP ASCII_BS + JP Z, m80_bs + CP ASCII_CAN + JP Z, m80_rt + CP ASCII_EM + JP Z, m80_up + CP ASCII_SUB + JP Z, m80_lf + CP ASCII_LF + JP Z, m80_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; +; -------------------------------------------------- +m_beep: + LD HL, (M_VARS.beep_duration) + EX DE, HL + LD HL, (M_VARS.beep_period) + LD A, 00110110b ; TMR#0 LSB+MSB Square Wave Generator + OUT (TMR_DD70CTR), A + LD A, L ; LSB + OUT (TMR_DD70C1), A + LD A, H ; MSB + OUT (TMR_DD70C1), A + LD A, (M_VARS.strobe_state) + LD B, A +m_bell_cont: + LD A, D ; DE=duration + OR E + RET Z ; ret if enough + DEC DE + LD A, B + XOR BELL_PIN + LD B, A + OUT (DD67PC), A ; Invert bell pin +m_bell_wait_tmr1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; 0x10 + JP NZ, m_bell_wait_tmr1 + LD A, B + XOR BELL_PIN ; Flip pin again + LD B, A + OUT (DD67PC), A +m_bell_wait_tmr2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP Z,m_bell_wait_tmr2 + JP m_bell_cont + + +; ------------------------------------------------------ +; 5 Set cursor position +; ------------------------------------------------------ +esc_set_cursor: + CALL m_draw_cursor + LD DE, M_VARS.esc_param + LD HL, M_VARS.cursor_col + INC DE + LD A, (DE) ; column + SUB 32 + LD B, A + LD A, (M_VARS.screen_mode) + CP 3 + JP Z, .mode_80 + CP 7 + JP Z, .mode_80 + OR A + JP Z, .mode_40 + CP 4 + JP Z, .mode_40 + ; mode 64x25 + LD A, B + CP 64 + JP M, .common + LD A, 64 + JP .common + ; mode 40x25 +.mode_40: + LD A, B + CP 40 + JP M, .common + LD A, 40 + JP .common + ; mode 80x25 +.mode_80: + LD A, B + CP 80 + JP M, .common + LD A, 80 +.common: + LD (HL), A + DEC DE + DEC HL + LD A, (DE) + SUB 32 + CP 24 + JP C, esc_le_24 + LD A, 24 +esc_le_24: + LD B, A + ADD A, A + ADD A, A + ADD A, B + ADD A, A + LD (HL), A + CALL m_draw_cursor ; TODO change call+ret to jp + RET ; + +; ------------------------------------------------------ +; 6n Set video mode or cursor visibility +; Inp: n is +; 0 - C 32x25 with cursor; 0000 +; 1 - M 64x25 with cursor; 0001 +; 2 - M 64x25 with cursor; 0010 +; 3 - M 80x25 with cursor; 0011 +; 4 - C 32x25 no cursor; 0100 +; 5 - M 64x25 no cursor; 0101 +; 6 - M 64x25 no cursor; 0110 +; 7 - M 80x25 no cursor; 0111 +; 8 - M 20rows mode 1000 +; 9 - hide cursor 1001 +; 10 - show cursor 1010 +; ------------------------------------------------------ +esc_set_vmode: + LD HL, M_VARS.screen_mode + LD A, (M_VARS.cur_palette) + LD B, A + LD A, (M_VARS.esc_param) ; first parameter - video mode + AND 0xf + CP 11 + RET NC ; return if not valid input parameter + CP 9 + JP Z, .cursor_hide + CP 10 + JP Z, .cursor_show + LD (HL), A ; store new mode + CP 4 + JP Z, .set_color_mode + AND 0x3 ; monochrome (80x25, 64x25) mode? + LD A, 0 ; mode 512x254 mono + JP NZ, .skip_for_mono_mode + ; mode=0 or 4 -> 256x256px color +.set_color_mode: + LD A, 0x40 ; color mode +.skip_for_mono_mode: + OR B ; color mode with palette + OUT (VID_DD67PB), A ; configure screen mode + + LD HL, M_VARS.cursor_row + CALL m_clear_screen + +.draw_cursor: + CALL m_draw_cursor ; TODO change call+ret to jp + RET + +.cursor_hide: + LD A, (HL) ; screen_mode + OR 00000100b ; cursor hide + LD (HL), A + LD HL, M_VARS.cursor_row + JP .draw_cursor + +.cursor_show: + LD A, (HL) ; screen_mode + AND 11111011b ; cursor show + LD (HL), A + JP .draw_cursor + + +; ------------------------------------------------------ +; 4n n=1..4 Set drawing color +; ------------------------------------------------------ +esc_set_color: + LD A, (M_VARS.esc_param) +m_set_color: + AND 0x3 + RRA + LD B, A + LD A, 0x0 ; TODO: unused + SBC A, A + LD (M_VARS.curr_color), A + LD A, B + DEC A + CPL + LD (M_VARS.curr_color+1), A + RET + +;--------------------------------------------------- +; Print symbol or print sprite at X,Y coordinates +; Inp: param x,y +; C - character or sprite_no to draw +;--------------------------------------------------- +m_print_at_xy: + ; check video mode + LD A, (M_VARS.screen_mode) + AND 0x3 ; color? + JP NZ, esc_exit ; exit for mono modes + + LD A, C + AND 0x7f + LD C, A ; C = C with 7th bit reset + CP 0x1 + JP Z, .sprites_en ; enable sprite mode + + CP ASCII_SP + JP M, mode2_exit ; codes 0..31 - turm off game_mode + + ; check X, Y range to prevent drawing symbols out of screen + LD HL, M_VARS.esc_param + LD A, (HL) + LD E, A + ADD A, 8 + JP C, mode2_exit ; exit if esc_param[0]>247 + LD (HL), A + INC HL ; HL -> esc_param[1] + LD A, 247 + CP (HL) + JP C, mode2_exit ; exit if esc_param[1]>247 + ; calculate X,Y pixel address in VRAN + LD D, (HL) + CALL calc_px_addr + ; HL - address, B - pixel pos in byte + LD A, L + SUB 8 + LD L, A + PUSH HL ; save address + + LD A, (M_VARS.esc_var2) + OR A + JP NZ, .mode_sp + + ; font + LD A, C + CALL m_get_glyph + LD C, 7 + POP DE + INC E + JP .out_sp + + ; sprite mode +.mode_sp: + LD A, C + SUB 32 + CP 35 + JP NC, co_ex_l08 + + ; Calc sprite address + LD L, A ; HL=A - sprite_no + XOR A + LD H, A + ADD HL, HL + ADD HL, HL + ADD HL, HL ; HL=HL*8 + LD DE, game_sprite_tab + ADD HL, DE ; HL -> sprite + LD C, 8 ; bytes count + POP DE + + ; Out sprite + ; DE -> VRAM address + ; C - height +.out_sp: + LD A, (M_VARS.esc_param+2) + DEC A + JP Z, out_no_xor + +.next_line: + PUSH HL + ; Access Video RAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD L, (HL) ; load from table + LD H, 0x0 + LD A, B + OR A + JP Z, .l05 +.l04: + ADD HL, HL + DEC A + JP NZ, .l04 +.l05: + EX DE, HL + LD A, (M_VARS.curr_color) + AND E + XOR (HL) + LD (HL), A + INC H + INC H + LD A, (M_VARS.curr_color) + AND D + XOR (HL) + LD (HL), A + DEC H + LD A, (M_VARS.curr_color+1) + AND E + XOR (HL) + LD (HL), A + INC H + INC H + LD A, (M_VARS.curr_color+1) + AND D + XOR (HL) + LD (HL), A + DEC H + DEC H + DEC H + INC L + EX DE, HL + ; Disable VRAM + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + INC HL + DEC C + JP NZ, .next_line + RET + +.sprites_en: + LD (M_VARS.esc_var2), A + RET + +mode2_exit: + XOR A + LD (M_VARS.esc_var2), A + JP esc_exit + +co_ex_l08: + POP DE + JP mode2_exit + +out_no_xor: + PUSH HL + ; Acess to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD L, (HL) + LD H, 0x0 + LD A, B + OR A + JP Z, .l11 +.l10: + ADD HL, HL + DEC A + JP NZ, .l10 +.l11: + EX DE, HL + PUSH BC + LD A, (M_VARS.curr_color) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + LD A, (M_VARS.curr_color+1) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + DEC H + DEC H + INC L + EX DE, HL + POP BC + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP HL + INC HL + DEC C + JP NZ, out_no_xor + RET + +game_sprite_tab: + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; 0x00 + DB 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E ; 0x01 + DB 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E ; 0x02 + DB 0x00, 0x08, 0x08, 0x14, 0x63, 0x14, 0x08, 0x08 ; 0x03 + DB 0x36, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00 ; 0x04 + DB 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00 ; 0x05 + DB 0x1C, 0x3E, 0x1C, 0x7F, 0x7F, 0x6B, 0x08, 0x1C ; 0x06 + DB 0x08, 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x08, 0x1C ; 0x07 + DB 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18 ; 0x08 + DB 0x18, 0xDB, 0x3C, 0xE7, 0xE7, 0x3C, 0xDB, 0x18 ; 0x09 + DB 0xE7, 0xE7, 0x00, 0x7E, 0x7E, 0x00, 0xE7, 0xE7 ; 0x0a + DB 0x7E, 0x81, 0x81, 0xFF, 0xFF, 0x81, 0x81, 0x7E ; 0x0b + DB 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18 ; 0x0c + DB 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00 ; 0x0d + DB 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00 ; 0x0e + DB 0x00, 0x08, 0x0C, 0xFE, 0xFE, 0x0C, 0x08, 0x00 ; 0x0f + DB 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 ; 0x10 + DB 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 ; 0x11 + DB 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0 ; 0x12 + DB 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F ; 0x13 + DB 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ; 0x14 + DB 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF ; 0x15 + DB 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F ; 0x16 + DB 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0 ; 0x17 + DB 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 ; 0x18 + DB 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33 ; 0x19 + DB 0x70, 0x08, 0x76, 0xFF, 0xFF, 0xFF, 0x7E, 0x18 ; 0x1a + DB 0xC3, 0xDB, 0xDB, 0x18, 0x18, 0xDB, 0xDB, 0xC3 ; 0x1b + DB 0xFC, 0xCC, 0xFC, 0x0C, 0x0C, 0x0E, 0x0F, 0x07 ; 0x1c + DB 0xFE, 0xC6, 0xFE, 0xC6, 0xC6, 0xE6, 0x67, 0x03 ; 0x1d + DB 0x18, 0x3C, 0x3C, 0x18, 0x7E, 0x18, 0x24, 0x66 ; 0x1e + DB 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 ; 0x1f + DB 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 ; 0x20 + DB 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 0x21 + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF ; 0x22 + +; -------------------------------------------------- +; Calculate address of pixel in Video RAM +; Inp: DE - Y, X +; Out: HL - address +; B - offset in byte +; -------------------------------------------------- +calc_px_addr: + ; take into account the vertical displacement + LD A, (M_VARS.row_shift) + SUB D + DEC A + LD L, A + + LD A, E + AND 0x07 ; X mod 8 - offset in byte + LD B, A + + LD A, E + RRA + RRA + AND 00111110b + ADD A, 0x40 ; VRAM at 0x4000 + LD H, A + RET + + +;--------------------------------------------------- +; Draw filled rectanger +; Inp: esc param X1,Y2,X2,Y2 +; -------------------------------------------------- +esc_draw_fill_rect: + LD HL, M_VARS.esc_param + LD E, (HL) ; E=X1 + INC HL + LD C, (HL) ; C=Y1 + INC HL + INC HL + LD D, (HL) ; D=Y2 + LD A, D + SUB C ; delta Y + JP NZ, .non_zero_h + INC A ; 1 as minimum +.non_zero_h: + LD C, A ; C = height + ; DE = Y2, X1 + CALL calc_px_addr + ; HL -> videomem offset, b - pixel offset + + ; build pixel mask + XOR A +.shift_mask_l: + SCF + RLA + DEC B + JP P, .shift_mask_l + RRA + LD (M_VARS.pixel_mask_l), A + CPL ; invert + LD (M_VARS.pixel_mask_l_i), A + LD A, (M_VARS.esc_param+2) ; X2 + AND 0x7 ; 0..7 + LD B, A + XOR A +.shift_mask_r: + SCF + RLA + DEC B + JP P, .shift_mask_r + LD (M_VARS.pixel_mask_r_i), A + LD B, C + ; calc end address + LD A, (M_VARS.esc_param+2) ; X2 + RRA + RRA + AND 00111110b + ADD A, 0x40 + SUB H + RRCA + LD C, A ; C - width + INC B + LD A, (M_VARS.esc_param+4) + DEC A + JP NZ, .rectangle_xor + LD A, (M_VARS.pixel_mask_r_i) + CPL + LD (M_VARS.pixel_mask_r), A + LD D, A + LD A, (M_VARS.pixel_mask_l) + LD E, A + ; draw B horisontal lines +.next_line: + PUSH DE + PUSH HL + PUSH BC + CALL draw_line_h + POP BC + POP HL + POP DE + INC L + DEC B + JP NZ, .next_line + RET + + ; draw B horisontal lines (xor) +.rectangle_xor: + LD A, (M_VARS.pixel_mask_r_i) + LD (M_VARS.pixel_mask_r), A + LD D, A + LD A, (M_VARS.pixel_mask_l_i) + LD E, A + ; Access to VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.edf_l6: + PUSH DE + PUSH HL + PUSH BC + LD A, C + OR A + JP NZ, .w_ne_0 ; jump if width != 0 + LD A, E ; merge masks E=E or D + OR D +.next_8px: + LD E, A +.w_ne_0: + LD B, E ; B - mask + EX DE, HL + LD HL, (M_VARS.curr_color) ; color + EX DE, HL + ; Set pixels - VideoRAM[HL] = color xor VideoRAM[HL] + LD A, E + AND B + XOR (HL) + LD (HL), A + ; And next byte + INC H + LD A, D + AND B + XOR (HL) + LD (HL), A + ; ---------- + INC H + LD A, C + OR A + JP Z, .complete + DEC C + ; right tail of line, use right mask +.r_mask: + LD A, (M_VARS.pixel_mask_r) + JP Z, .next_8px + ; full 8 bits without mask at middle of line +.next_full: + LD A, (HL) + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + LD (HL), A + INC H + DEC C + JP NZ, .next_full + JP .r_mask +.complete: + POP BC + POP HL + POP DE + INC L + DEC B + JP NZ, .edf_l6 + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +;--------------------------------------------------- +; Paint screen +; Inp: params X,Y,Color,repColor +;--------------------------------------------------- +esc_paint: + ; Save stack + LD HL, 0x0 + ADD HL, SP + LD (M_VARS.paint_sp_save), HL + + ; Set our own stack + LD HL, M_VARS.paint_stack ; TODO: Z80 LD SP,var i800 - LXI SP,nn + LD SP, HL + + ; save current color + LD HL, (M_VARS.curr_color) + LD (M_VARS.tmp_color), HL + + ; set color from param 3 + LD A, (M_VARS.esc_param+2) + DEC A + CALL m_set_color + + ; color to replace, from param 4 + LD A, (M_VARS.esc_param+3) + DEC A + LD (M_VARS.cmp_color), A + + ; HL - Y,X + LD A, (M_VARS.esc_param) + LD L, A + LD A, (M_VARS.esc_param+1) + LD H, A + LD (M_VARS.paint_y), A + + LD A, (M_VARS.esc_param+4) ; 0 - full fill, 1 - fast fill + DEC A + LD (M_VARS.esc_param), A + + LD A, 0x2 + LD (M_VARS.paint_var5), A ; task_no=2 + + EX DE, HL + CALL calc_px_addr + LD (M_VARS.esc_param+1), HL ; temporary ctore address of start fill point + ; make mask + LD A, 10000000b +.l1: + RLCA + DEC B + JP P, .l1 + + LD B, A + LD (M_VARS.esc_param+3), A ; store mask + + ; find left border + LD A, (M_VARS.cmp_color) + LD C, A + LD D, E ; D = X + CALL paint_find_left + ; find right border + LD HL, (M_VARS.esc_param+1) ; restore HL + LD A, (M_VARS.esc_param+3) ; restore mask + LD B, A + CALL paint_find_right + ; + LD HL, 0x0 + PUSH HL + PUSH HL + ; + LD A, (M_VARS.esc_param) ; A = fill mode + OR A + JP Z, ep_fm_0 + ; push fill task parameters + LD A, (M_VARS.paint_var5) + DEC A ; task_no-1 + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + +ep_fm_0: + ; push fill task parameters + LD A, (M_VARS.paint_var5) ; task_no + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + JP paint_task ; exec task + +ep_task_end: + LD A, (M_VARS.cmp_color) + LD C, A ; color to compare + + LD A, (M_VARS.esc_param) ; fill mode 0 - full, 1 - fast + OR A + JP NZ, ep_f_fast + + LD A, 0x2 + LD (M_VARS.paint_var7), A + LD A, (M_VARS.paint_var2) + CP 2 + JP Z, ep_l4 + JP ep_l5 ; TODO: change to one JP NZ + +ep_l4: + LD A, 1 + LD (M_VARS.paint_var5), A ; task_no? + JP ep_l8 + +ep_l5: + LD A, 2 + LD (M_VARS.paint_var5), A + JP ep_l11 + +ep_l6: + LD A, (M_VARS.paint_var7) + OR A + JP Z, paint_task + LD A, (M_VARS.paint_var2) + CP 2 + JP Z, ep_l5 ; TODO: change to one JP NZ + JP ep_l4 + +ep_f_fast: + LD A, (M_VARS.paint_var2) + LD (M_VARS.paint_var5), A + CP 1 ; TODO: DEC A - save 1b 3t + JP Z, ep_l8 ; TODO: change to one JP NZ + JP ep_l11 + +ep_l8: + LD A, (M_VARS.paint_var3) + LD D, A + LD A, (M_VARS.paint_var1) + LD E, A + LD HL, (M_VARS.esc_param+1) + LD A, (M_VARS.esc_param+3) + LD B, A + LD A, (M_VARS.paint_var4) + DEC A + JP Z, ep_l10 + LD (M_VARS.paint_y), A + INC L + CALL paint_find_next_right + JP Z, ep_l10 + LD HL, (M_VARS.esc_param+4) + LD A, (M_VARS.esc_param+6) + LD B, A + INC L + CALL paint_find_next_left + JP Z, ep_l10 + LD A, (M_VARS.esc_param) + OR A + JP NZ, ep_l9 + JP ep_l12 + +ep_l9: + LD A, (M_VARS.paint_var5) + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + JP paint_task + +ep_l10: + LD A, (M_VARS.esc_param) + OR A + JP NZ, paint_task + LD A, (M_VARS.paint_var7) + DEC A + LD (M_VARS.paint_var7), A + JP ep_l6 + +ep_l11: + LD A, (M_VARS.paint_var3) + LD D, A + LD A, (M_VARS.paint_var1) + LD E, A + LD HL, (M_VARS.esc_param+1) + LD A, (M_VARS.esc_param+3) + LD B, A + LD A, (M_VARS.paint_var4) + INC A + CP 0xff + JP Z, ep_l10 + LD (M_VARS.paint_y), A + DEC L + CALL paint_find_next_right + JP Z, ep_l10 + LD HL, (M_VARS.esc_param+4) + LD A, (M_VARS.esc_param+6) + LD B, A + DEC L + CALL paint_find_next_left + JP Z, ep_l10 + LD A, (M_VARS.esc_param) + OR A + JP NZ, ep_l9 + JP ep_l12 + +; --------------------------------------------------- +; +; --------------------------------------------------- +paint_find_next_right: + CALL get_pixel + JP NZ, .l1 + CALL paint_find_left + LD A, 0xff + OR A + RET + +.l1: + LD A, D + CP E + RET Z + INC D + LD A, B + RLCA + LD B, A + JP NC, .l2 + INC H + INC H +.l2: + CALL get_pixel + JP NZ, .l1 + LD A, 0xff + OR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +paint_find_next_left: + CALL get_pixel + JP NZ, .l1 + CALL paint_find_right + LD A, 0xff + OR A + RET +.l1: + LD A, E + CP D + RET Z + DEC E + LD A, B + RRCA + LD B, A + JP NC, .l2 + DEC H + DEC H +.l2: + CALL get_pixel + JP NZ, .l1 + LD A, 0xff + OR A + RET + +ep_l12: + LD A, D + LD (M_VARS.pixel_mask_r), A + LD A, (M_VARS.paint_var5) + LD D, A + PUSH DE + LD A, (M_VARS.pixel_mask_r) + CP E + JP NZ, ep_l13 + LD L, A + LD A, (M_VARS.paint_y) + LD H, A + PUSH HL + JP ep_l16 +ep_l13: + LD D, E + CALL paint_find_left + LD E, D + LD A, (M_VARS.paint_y) + LD D, A + PUSH DE + LD A, (M_VARS.pixel_mask_r) + LD D, A + CP E + JP Z, ep_l16 +ep_l14: + DEC E + LD A, B + RRCA + LD B, A + JP NC, ep_l15 + DEC H + DEC H +ep_l15: + CALL get_pixel + JP NZ, ep_l14 + JP ep_l12 +ep_l16: + JP ep_l10 + +; --------------------------------------------------- +; Find rightmost pixel to fill +; In/Out: E = x_right +; HL - current pixel address +; B - pixel mask +; --------------------------------------------------- +paint_find_right: + LD A, E + CP 0xff + RET Z ; return if X=right border + INC E ; x=x+1 + ; rotate pixel mask right + LD A, B + RLCA + LD B, A + JP NC, .in_byte + ; inc addr+2 (2 byte per 8 pixels) + INC H + INC H + +.in_byte: + CALL get_pixel + JP Z, paint_find_right ; find until same color + ; border found, x-1 + DEC E + ; rotate mask back 1 px + LD A, B + RRCA + LD B, A + RET NC + ; addr-2 if previous byte + DEC H + DEC H + RET + +; --------------------------------------------------- +; Find leftmost pixel to fill +; In/Out: D = x_left +; HL - current pixel address +; B - pixel mask +; --------------------------------------------------- +paint_find_left: + LD A, D + OR A + RET Z ; return if x=0 + + DEC D ; x-1 + LD A, B + RRCA ; rotate mask to right + LD B, A + JP NC, .in_byte + DEC H ; addr-2 (2 byte for 8px) + DEC H + +.in_byte: + CALL get_pixel + JP Z, paint_find_left ; repeat until same color + + INC D ; border found, x+1 + ; mask rotate right + LD A, B + RLCA + LD B, A + RET NC + ; if CF, inc address+2 + INC H + INC H + RET + +; --------------------------------------------------- +; Inp: HL - address +; B - pixel mask +; C - color to compare +; Out: A - 0,1,2 +; ZF - set if color match +; --------------------------------------------------- +get_pixel: + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; get pixel and mask + LD A, (HL) + AND B + JP NZ, .bit1_set + INC H + LD A, (HL) + DEC H + AND B + JP NZ, .bit2_set + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + CP C + RET + +.bit1_set: + INC H + LD A, (HL) + DEC H + AND B + JP NZ, .bit12_set + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 0x1 + CP C + RET + +.bit2_set: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 0x2 + CP C + RET + +.bit12_set: + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 3 + CP C + RET + +paint_task: + POP HL ; L=x0, H=Y + LD (M_VARS.paint_var3), HL + EX DE, HL + + POP HL ; L=x1, H=mode + LD A, H + OR A + JP Z, paint_exit ; jump for mode=0 + + ; calc leftmost pixel address, mask for draw horisontal line + LD (M_VARS.paint_var1), HL + CALL calc_px_addr + LD (M_VARS.esc_param+1), HL + LD C, B + LD A, 0x80 +.lmp_mask: + RLCA + DEC B + JP P, .lmp_mask + LD (M_VARS.esc_param+3), A + ; calc rightmos pixel address and mask + LD A, (M_VARS.paint_var1) + LD E, A + LD A, (M_VARS.paint_var4) + LD D, A + CALL calc_px_addr + LD (M_VARS.esc_param+4), HL + LD D, B + LD A, 0x80 +.rmp_mask: + RLCA + DEC B + JP P, .rmp_mask + LD (M_VARS.esc_param+6), A + LD A, (M_VARS.esc_param+3) ; TODO: unused code + + XOR A +.lmi_mask: + SCF + RLA + DEC C + JP P, .lmi_mask + RRA + LD E, A ; E - left inv mask + + XOR A +.rmi_mask: + SCF + RLA + DEC D + JP P, .rmi_mask + CPL + LD D, A ; D - right inv mask + + LD (M_VARS.pixel_mask_r), A + LD HL, (M_VARS.esc_param+1) ; HL -> lext pix address + LD A, (M_VARS.esc_param+5) ; right pix address (low byte) + SUB H ; delta x + RRCA ; 2 byte for 8 pix + LD C, A ; C - line width + CALL draw_line_h + JP ep_task_end + +paint_exit: + LD HL, (M_VARS.tmp_color) ; restore previous current color + LD (M_VARS.curr_color), HL + LD HL, (M_VARS.paint_sp_save) ; restore previous stack + LD SP, HL + RET + +;--------------------------------------------------- +; Draw horizontal line +; Inp: C - width +; DE - left & right pixel mask +; HL - address of first byte of line +;--------------------------------------------------- +draw_line_h: + ; Access to VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, C + OR A + JP NZ, .width_ne0 + LD A, E ; join left and right masks + OR D +.next_byte: + LD E, A +.width_ne0: + LD B, E + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Get pixels, apply colors + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A ; store first + ; Same for second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; move to next byte + INC H + LD A, C + OR A + JP Z, .complete + DEC C +.r_mask: + ; use right mask for last right byte + LD A, (M_VARS.pixel_mask_r) + JP Z, .next_byte +.full_8: + LD (HL), E + INC H + LD (HL), D + INC H + DEC C + JP NZ, .full_8 + JP .r_mask +.complete: ; TODO: duplicate close_vram_ret + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +;--------------------------------------------------- +; 2x1y1x2y2 Draw Line +;--------------------------------------------------- +esc_draw_line: + LD HL, M_VARS.esc_param + LD E, (HL) ; E=X1 + INC HL + LD D, (HL) ; D=Y1 + INC HL + LD A, (HL) + INC HL + LD H, (HL) ; H=Y2 + LD L, A ; L=X2 + CP E + JP C, .x1_le_x2 + EX DE, HL ; exchange if X1>X2 +.x1_le_x2: + LD (M_VARS.esc_param), HL ; store x1,y1 back + LD A, E + SUB L + LD L, A ; L - width + LD A, D + SUB H + LD H, A ; H - height + PUSH AF + JP NC, .pos_height + ; change sign + CPL + INC A + LD H, A +.pos_height: + EX DE, HL + LD HL, (M_VARS.esc_param) + EX DE, HL + JP Z, height0 + LD A, L + OR A + JP Z, .width0 + LD B, A + POP AF + LD A, 0x0 + ADC A, A + LD (M_VARS.esc_param+4), A + ; HL = E/B height/width + LD E, H + LD C, 16 + LD D, 0 +.next_16: + ADD HL, HL + EX DE, HL + ADD HL, HL + EX DE, HL + LD A, D + JP C, .edl_l4 + CP B + JP C, .edl_l5 +.edl_l4: + SUB B + LD D, A + INC HL +.edl_l5: + DEC C + JP NZ, .next_16 + LD DE, 0x0 + PUSH DE + ; save result at stack + PUSH HL + + LD HL, (M_VARS.esc_param) ; x1,y1 + EX DE, HL + LD C, B + CALL calc_px_addr + ; HL - address, B - offset in byte + ; make mask + LD A, 10000000b +.roll_l: + RLCA + DEC B + JP P, .roll_l + CPL + LD B, A ; b - inv mask + +.edl_l7 + POP DE + EX (SP), HL ; save HL on top of stack + LD A, H + ADD HL, DE + SUB H + CPL + INC A + EX (SP), HL + PUSH DE + PUSH BC + LD C, A + EX DE, HL + + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Access VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD A, (M_VARS.esc_param+4) ; sign of delta Y + OR A + JP NZ, .next_down +.next_up: + ; firs byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, .is_last + DEC C + ; draw up + DEC L + JP .next_up +.next_down: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; + DEC H + LD A, C + OR A + JP Z, .is_last + DEC C + ; draw down + INC L + JP .next_down +.is_last: + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP BC + LD A, B + ; <<1px + SCF + RLA + JP C, .edl_l11 + RLA + INC H + INC H +.edl_l11 + LD B, A + DEC C + JP NZ, .edl_l7 + POP HL + POP HL + RET + +; -------------------------------------------------- +; draw vertical line +; Inp: DE - YX +; L - length +; -------------------------------------------------- +.width0 + LD C, H + CALL calc_px_addr + + ; make pixel mask + LD A, 10000000b +.edl_l13: + RLCA + DEC B + JP P, .edl_l13 + CPL + LD B, A + + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + POP AF + + ; Enable VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + JP C, .next_row_down + +.next_row_up: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next Y + DEC H + LD A, C + OR A + JP Z, close_vram_ret + DEC C + ; dec row + DEC L + JP .next_row_up + +.next_row_down: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next address + DEC H + LD A, C + OR A + JP Z, close_vram_ret + DEC C + ; inc row + INC L + JP .next_row_down + +close_vram_ret + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; draw horizontal line +; Inp: DE - YX +; L - length +; -------------------------------------------------- +height0 + POP AF + LD C, L + LD A, L + OR A + JP NZ, .len_ne0 + INC C ; length 1 at least +.len_ne0: + CALL calc_px_addr + ; make pixel mask + LD A, 10000000b +.edl_l19 + RLCA + DEC B + JP P, .edl_l19 + CPL + LD B, A + + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + +.next_col: + ; set 1st byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; set 2nd byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next byte + DEC H + ; next (right) horizontal pixel + LD A, B + SCF + RLA + JP C, .edl_l21 + RLA + INC H + INC H +.edl_l21 + LD B, A + DEC C + JP NZ, .next_col + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; ESC Draw Dot +; -------------------------------------------------- +esc_draw_dot: + LD HL, (M_VARS.esc_param) + EX DE, HL + CALL calc_px_addr + LD A, 0x80 +edd_l1 + RLCA + DEC B + JP P, edd_l1 + LD B, A + LD A, (M_VARS.esc_param+2) + CP 0x3 + JP Z, edd_ep_task_end + LD A, B + CPL + LD B, A + LD A, (M_VARS.esc_param+2) + CP 0x2 + JP Z, edd_ep_fm_0 + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + AND B + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, 0x0 + OUT (SYS_DD17PB), A + RET +edd_ep_fm_0 + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + LD A, 0x0 + OUT (SYS_DD17PB), A + RET +edd_ep_task_end + CALL get_pixel + LD (M_VARS.esc_var3), A + RET + +; -------------------------------------------------- +; +; -------------------------------------------------- +esc_picture: + LD HL, (M_VARS.esc_param+3) + LD A, (HL) + CP ':' + RET NZ + INC HL + LD E, (HL) + INC HL + LD D, (HL) + INC HL + LD A, (HL) + LD (M_VARS.esc_var0), A + INC HL + LD A, (HL) + LD (M_VARS.esc_var1), A + INC HL + PUSH HL + LD C, (HL) + INC HL + LD B, (HL) + INC HL + INC HL + EX DE, HL + PUSH DE + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL pict_sub1 + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP DE + POP HL + LD (HL), C + INC HL + LD (HL), B + CALL pict_sub2 ; TODO: replace call+ret to jp; + RET + +pict_sub1: + LD A, (M_VARS.esc_param) + CP ASCII_EM + JP Z, gih_up + CP ASCII_CAN + JP Z, gih_rt + CP ASCII_SUB + JP Z, gih_ctrl_z + CP ASCII_BS + JP Z, gih_bs + CP ASCII_US + JP Z, pict_clr + RET + +pict_clr: + LD L, C + LD H, B + LD A, (M_VARS.esc_var0) + LD C, A + LD A, (M_VARS.esc_var1) + LD B, A + CALL put_image + POP HL + POP HL + POP HL + RET + +ehd_l1: + CP 0x2 + JP Z, get_image_hdr + CP 0x3 + JP Z, m_fn_39 + RET + +; -------------------------------------------------- +; Function 39 +; -------------------------------------------------- +m_fn_39: + LD HL, (M_VARS.esc_param+2) ; pr 3,4 + INC L + LD C, L + LD B, H + LD E, L + CALL dc_mul_e_h + ADD HL, HL + EX DE, HL + LD HL, (M_VARS.esc_param) ; par 1,2 + PUSH BC + PUSH DE + LD BC, 10 + LD E, L + LD D, H + ADD HL, BC + EX DE, HL + LD (HL), E + INC HL + LD (HL), D + INC HL + POP BC + LD A, 4 +.l1: + PUSH AF ; TODO: remove AF not changed + EX DE, HL + ADD HL, BC + EX DE, HL + LD (HL), E + INC HL + LD (HL), D + INC HL + POP AF ; TODO: remove AF not changed + DEC A + JP NZ, .l1 + EX DE, HL + LD HL, 0x0 + ADD HL, BC ; HL=BC + ADD HL, BC ; HL=2*BC + ADD HL, HL ; HL=4*BC + ADD HL, BC ; HL=5*BC + ADD HL, HL ; HL=10*BC + EX DE, HL ; DE=10*BC + LD A, 0x0 + LD B, A ; B=A=0 + ; fill DE bytes at [HL] with 0 +.l2: + LD (HL), B + INC HL + DEC DE + CP E + JP NZ, .l2 + CP D + JP NZ, .l2 + + XOR A + LD (M_VARS.esc_var0), A + POP BC + LD HL, (M_VARS.esc_param) + LD DE, 10 + ADD HL, DE +.l3: + EX DE, HL + LD HL, (M_VARS.esc_param+4) + EX DE, HL + PUSH BC + CALL fn39_sub2 + POP BC + LD A, (M_VARS.esc_var0) + ADD A, 0x2 + LD (M_VARS.esc_var0), A + CP 10 + RET Z + JP .l3 + +; --------------------------------------------------- +; Function 3C +; --------------------------------------------------- +get_image_hdr: + LD HL, (M_VARS.esc_param+4) + LD (HL), 58 ; ':' + INC HL + PUSH HL + LD HL, (M_VARS.esc_param+2) + INC L + LD C, L + LD A, H + ADD A, 0x4 + LD B, A + LD H, B + LD E, C + CALL dc_mul_e_h + ADD HL, HL + EX DE, HL + POP HL + LD (HL), E + INC HL + LD (HL), D + INC HL + LD (HL), C + INC HL + LD (HL), B + INC HL + EX DE, HL + LD HL, (M_VARS.esc_param) + ADD HL, HL + EX DE, HL + PUSH BC + PUSH HL + CALL calc_px_addr + POP DE + LD A, L + LD (DE), A + INC DE + LD A, H + LD (DE), A + INC DE + LD A, B + LD (DE), A + INC DE + POP BC + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL get_image ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; = +; --------------------------------------------------- +esc_get_put_image: + LD A, (M_VARS.esc_param+6) + CP 0x2 + JP NC, ehd_l1 + LD HL, M_VARS.esc_param + LD E, (HL) + INC HL + LD D, (HL) + INC HL + LD C, (HL) + INC HL ; TODO: next call to calc_px_addr + LD B, (HL) ; destroy value of B and HL + CALL calc_px_addr + EX DE, HL + LD HL, (M_VARS.esc_param+4) + LD A, H + CP 128 + RET C + + CP 184 + RET NC + + EX DE, HL + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (M_VARS.esc_param+6) + OR A + JP NZ, put_image + +; --------------------------------------------------- +; Get image from VRAM to user buffer +; Inp: HL -> VRAM[x,y] +; DE -> buffer +; BC - width, height +; --------------------------------------------------- +get_image: + PUSH HL + PUSH BC +.next_row: + ; byte 1 + LD A, (HL) + LD (DE), A + INC H ; next Y (row) + INC DE + ; byte 2 ; next dst addr + LD A, (HL) + LD (DE), A + INC H + LD A, H + CP 128 ; last row? + JP NZ, .l2 + LD H, 0x40 ; reset Y +.l2: + INC DE + DEC C + JP NZ, .next_row + POP BC + POP HL + INC L + DEC B ; dec width + JP NZ, get_image + JP img_task_end + +; --------------------------------------------------- +; Put image from buffer to VRAM +; Inp: HL -> VRAM[x,y] +; DE -> buffer +; BC - width, height +; --------------------------------------------------- +put_image: + PUSH HL + PUSH BC +.next_row: + ; two bytes for 8 pixels + ; byte 1 + LD A, (DE) ; get from buffer + LD (HL), A ; put to screen + INC H ; next Y (row) + INC DE ; next src addr + ; byte 2 + LD A, (DE) + LD (HL), A + INC DE + INC H + LD A, H + CP 128 ; last row? + JP NZ, .l2 + LD H, 0x40 ; reset +.l2: + DEC C + JP NZ, .next_row + POP BC + POP HL + ; next column + INC L + DEC B + JP NZ, put_image + +img_task_end: + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +fn39_sub2: + DEC C +.l1: + PUSH BC +.l2: + EX DE, HL + PUSH BC + PUSH HL + LD L, (HL) + LD H, 0x0 + LD A, (M_VARS.esc_var0) + LD B, A + OR A + JP Z, .l4 +.l3: + ADD HL, HL + DEC A + JP NZ, .l3 +.l4: + LD A, B + LD C, L + LD B, H + POP HL + INC HL + PUSH HL + LD L, (HL) + LD H, 0x0 + OR A + JP Z, .l6 +.l5: + ADD HL, HL + DEC A + JP NZ, .l5 +.l6: + EX DE, HL + LD A, (HL) + OR C + LD (HL), A + INC HL + LD A, (HL) + OR E + LD (HL), A + INC HL + LD (HL), B + INC HL + LD (HL), D + DEC HL + POP DE + INC DE + POP BC + DEC C + JP NZ, .l2 + POP BC + INC HL + INC HL + DEC B + JP NZ, .l1 + RET + +; -------------------------------------------------- +; Handle ASCII_CAN symbol (cursor right) +; -------------------------------------------------- +gih_rt: + DEC DE + LD A, (DE) + ADD A, 0x2 + LD (DE), A + CP 9 + RET C + LD A, 0x2 + LD (DE), A + INC DE + PUSH BC + LD H, D + LD L, E + INC HL + INC HL + LD A, (M_VARS.esc_var0) + PUSH AF + DEC A + LD (M_VARS.esc_var0), A + INC A + ADD A, A + ADD A, B + CP 128 + JP C, .l1 + SUB 0x40 +.l1: + LD B, A + LD A, (M_VARS.esc_var1) +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (HL) + LD (DE), A + INC HL + INC DE + LD A, (HL) + LD (DE), A + INC HL + INC DE + POP AF + DEC A + JP NZ, .l3 + LD A, (BC) + LD (DE), A + INC DE + INC HL + INC B + LD A, (BC) + LD (DE), A + INC HL + INC DE + DEC B + INC C + POP AF + DEC A + JP NZ, .l2 + POP AF + LD (M_VARS.esc_var0), A + POP BC + INC B + INC B + LD A, B + CP 0x80 + RET NZ + LD B, 0x40 + RET + +; -------------------------------------------------- +; Handle ASCII_BS (BackSpace) symbol +; -------------------------------------------------- +gih_bs: + DEC DE + LD A, (DE) + SUB 0x2 + LD (DE), A + RET NC + + LD A, 6 + LD (DE), A + INC DE + PUSH BC + ADD HL, DE + DEC HL + LD D, H + LD E, L + DEC HL + DEC HL + LD A, (M_VARS.esc_var1) + ADD A, C + DEC A + LD C, A + LD A, B + DEC A + CP 0x3f + JP NZ, .l1 + LD A, 0x7f ; [DEL]? +.l1: + LD B, A + LD A, (M_VARS.esc_var0) + PUSH AF + DEC A + LD (M_VARS.esc_var0), A + LD A, (M_VARS.esc_var1) +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + POP AF + DEC A + JP NZ, .l3 + LD A, (BC) + LD (DE), A + DEC HL + DEC DE + DEC B + LD A, (BC) + LD (DE), A + DEC HL + DEC DE + INC B + DEC C + POP AF + DEC A + JP NZ, .l2 + POP AF + LD (M_VARS.esc_var0), A + POP BC + DEC B + DEC B + LD A, B + CP 0x3e + RET NZ + LD B, 0x7e + RET + +; -------------------------------------------------- +; Handle ASCII_SUB symbol +; -------------------------------------------------- +gih_ctrl_z: + PUSH BC + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + ADD A, E + LD L, A + LD H, D + LD A, (M_VARS.esc_var1) + ADD A, C + LD C, A + PUSH BC + LD A, (M_VARS.esc_var1) + DEC A + DEC A + LD C, A +.l1: + LD A, (M_VARS.esc_var0) + LD B, A +.l2: + LD A, (HL) + LD (DE), A + INC HL + INC DE + LD A, (HL) + LD (DE), A + INC HL + INC DE + DEC B + JP NZ, .l2 + DEC C + JP NZ, .l1 + POP BC + LD L, 0x2 +.l3: + LD A, (M_VARS.esc_var0) + LD H, A + PUSH BC +.l4: + LD A, (BC) + LD (DE), A + INC DE + INC B + LD A, (BC) + LD (DE), A + INC DE + INC B + LD A, B + CP 0x80 + JP NZ, .l5 + LD B, 0x40 +.l5: + DEC H + JP NZ, .l4 + POP BC + INC C + DEC L + JP NZ, .l3 + POP BC + INC C + INC C + RET + +; -------------------------------------------------- +; Handle ASCII_EM symbol +; -------------------------------------------------- +gih_up: + PUSH BC + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, B + CP 128 + JP Z, .l1 + JP C, .l1 + SUB 64 +.l1: + DEC A + LD B, A + DEC C + PUSH BC + ADD HL, DE + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + LD E, A + LD A, L + SUB E + LD E, A + LD D, H + DEC DE + DEC HL + EX DE, HL + LD A, (M_VARS.esc_var1) + DEC A + DEC A + LD C, A +.l2: + LD A, (M_VARS.esc_var0) + LD B, A +.l3: + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + DEC B + JP NZ, .l3 + DEC C + JP NZ, .l2 + POP BC + LD L, 0x2 + +.l4: + LD A, (M_VARS.esc_var0) + LD H, A + PUSH BC + +.l5: + LD A, (BC) + LD (DE), A + DEC DE + DEC B + LD A, (BC) + LD (DE), A + DEC DE + DEC B + LD A, B + CP 0x3f + JP NZ, .l6 + LD B, 0x7f + +.l6: + DEC H + JP NZ, .l5 + POP BC + DEC C + DEC L + JP NZ, .l4 + POP BC + DEC C + DEC C + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +pict_sub2: + PUSH DE + DEC DE + LD A, (DE) + LD E, A + LD D, 0x0 + LD HL, (M_VARS.esc_param+1) + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) + POP HL + PUSH BC + PUSH HL + PUSH HL + LD HL, (M_VARS.esc_param+3) + INC HL + LD C, (HL) + INC HL + LD B, (HL) + POP HL + ADD HL, BC + LD C, L + LD B, H + POP HL + CALL mov_hl_bc + LD A, (M_VARS.esc_param+5) + OR A + JP Z, .l1 + EX DE, HL +.l1: + LD A, (M_VARS.esc_var1) + SUB 0x4 +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (DE) + INC DE + PUSH DE + PUSH AF + LD A, (DE) + LD E, A + POP AF + LD D, A + OR E + CPL + PUSH AF + AND (HL) + OR D + LD (BC), A + INC BC + INC HL + POP AF + AND (HL) + OR E + LD (BC), A + INC BC + INC HL + POP DE + INC DE + POP AF + DEC A + JP NZ, .l3 + POP AF + DEC A + JP NZ, .l2 + LD A, (M_VARS.esc_param+5) + OR A + JP Z, .l4 + EX DE, HL +.l4: + CALL mov_hl_bc + POP DE + EX DE, HL + LD A, (M_VARS.esc_var0) + LD C, A + LD A, (M_VARS.esc_var1) + LD B, A + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL put_image ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Move form [HL] to [BC] count of bytes +; Inp: HL -> src +; BC -> dst +; esc_var0*4 - count +; --------------------------------------------------- +mov_hl_bc: + PUSH DE + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + LD E, A ; E = param * 4 + ; move [HL] -> [BC] E bytes +.next: + LD A, (HL) + LD (BC), A + INC HL + INC BC + DEC E + JP NZ, .next + POP DE + RET + +; --------------------------------------------------- +; Draw circle +; Inp: param x,y,radius, aspect_x, aspect_y +; --------------------------------------------------- +esc_draw_circle: + LD A, (M_VARS.esc_param+2) ; radius + LD B, A + OR A + RET Z ; exit ir radius 0 + LD A, 0x7f + CP B + RET M ; exit if radius>127 + + XOR A + LD D, A ; 0 + LD E, B ; r + CALL dc_draw_8px + + LD A, 1 + LD H, A + SUB B + LD C, A + LD A, B + RLCA + LD B, A + LD A, 0x1 + SUB B + LD L, A + CCF ; TODO: unused +.l1: + INC D + LD A, E + CP D + JP Z, dc_draw_8px + CALL dc_draw_8px + LD A, H + ADD A, 0x2 + LD H, A + LD A, L + ADD A, 0x2 + LD L, A + LD A, C + ADD A, H + LD C, A + JP NC, .l1 +.l2: + CCF ; TODO: unused + INC D + DEC E + LD A, D + CP E + JP Z, dc_draw_8px + SUB E + CP 0x1 + RET Z + LD A, E + SUB D + CP 0x1 + JP Z, dc_draw_8px + CALL dc_draw_8px + LD A, H + ADD A, 0x2 + LD H, A + LD A, L + ADD A, 0x4 + LD L, A + JP NC, .l3 + CCF ; TODO: unused +.l3: + LD A, C + ADD A, L + LD C, A + JP NC, .l1 + JP .l2 + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_8px: + PUSH HL + PUSH DE + PUSH BC + PUSH DE + CALL dc_aspect_ratio_1 + LD HL, (M_VARS.esc_param) ; HL=Y,X + CALL dc_draw_4px_bc + POP DE + CALL dc_aspect_ratio2 + LD HL, (M_VARS.esc_param) ; HL=Y,X + CALL dc_draw_4px_cb + POP BC + POP DE + POP HL + XOR A + RET + +; --------------------------------------------------- +; Scale circle axis dy specified aspect ratio +; if aspect_x = 0 C = D else C = D * aspect_x / 256 +; if aspect_y = 0 B = E else B = E * aspect_y / 256 +; --------------------------------------------------- +dc_aspect_ratio_1: + LD HL, (M_VARS.esc_param+3) ; aspect_x -> L, aspect_y -> H + LD A, L + OR A + LD C, D + LD B, E + JP NZ, .dc_ax_ne0 + LD A, H + OR A + JP NZ, .dc_ay_ne0 + RET +.dc_ax_ne0: + LD A, H + LD H, L + LD E, C + CALL dc_mul_e_h + LD C, E + OR A + RET Z +.dc_ay_ne0: + LD H, A + LD E, B + CALL dc_mul_e_h + LD B, E + RET + +; --------------------------------------------------- +; if aspect_x = 0 B = E else B = E * aspect_x / 256 +; if aspect_y = 0 C = D else C = D * aspect_y / 256 +; --------------------------------------------------- +dc_aspect_ratio2: + LD HL, (M_VARS.esc_param+3) ; aspect_x -> L, aspect_y -> H + LD A, L + OR A + LD C, D + LD B, E + JP NZ, .dc_ax_ne0 + LD A, H + OR A + JP NZ, .dc_ay_ne0 + RET +.dc_ax_ne0: + LD A, H + LD H, L + LD E, B + CALL dc_mul_e_h + LD B, E + OR A + RET Z + +.dc_ay_ne0: + LD H, A + LD E, C + CALL dc_mul_e_h + LD C, E + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_mul_e_h: + LD D, 0x0 + LD L, D + ADD HL, HL + JP NC, .l1 + ADD HL, DE +.l1: + ADD HL, HL + JP NC, .l2 + ADD HL, DE +.l2: + ADD HL, HL + JP NC, .l3 + ADD HL, DE +.l3: + ADD HL, HL + JP NC, .l4 + ADD HL, DE +.l4: + ADD HL, HL + JP NC, .l5 + ADD HL, DE +.l5: + ADD HL, HL + JP NC, .l6 + ADD HL, DE +.l6: + ADD HL, HL + JP NC, .l7 + ADD HL, DE +.l7: + ADD HL, HL + JP NC, .l8 + ADD HL, DE +.l8: + LD E, H + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_4px_bc: + ; draw pixel(H+B, L+C) if in screen + LD A, H + ADD A, B + JP C, .l1 + LD D, A + LD A, L + ADD A, C + LD E, A + CALL dc_put_pixel +.l1: + ; draw pixel(H+B, L-C) if in screen + LD A, H + ADD A, B + JP C, .l2 + LD D, A + LD A, L + SUB C + LD E, A + CALL dc_put_pixel +.l2: + ; draw pixel(H-B, L-C) if in screen + LD A, H + SUB B + JP C, .l3 + LD D, A + LD A, L + SUB C + LD E, A + CALL dc_put_pixel +.l3: + ; draw pixel(H-B, L+C) if in screen + LD A, H + SUB B + RET C + LD D, A + LD A, L + ADD A, C + LD E, A + CALL dc_put_pixel ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_4px_cb: + ; draw pixel(H+C, L+B) if in screen + LD A, H + ADD A, C + JP C, .l1 + LD D, A + LD A, L + ADD A, B + LD E, A + CALL dc_put_pixel +.l1: + ; draw pixel(H+C, L-B) if in screen + LD A, H + ADD A, C + JP C, .l2 + LD D, A + LD A, L + SUB B + LD E, A + CALL dc_put_pixel +.l2: + ; draw pixel(H-C, L-B) if in screen + LD A, H + SUB C + JP C, l3 + LD D, A + LD A, L + SUB B + LD E, A + CALL dc_put_pixel +l3: + ; draw pixel(H-C, L+B) if in screen + LD A, H + SUB C + RET C + LD D, A + LD A, L + ADD A, B + LD E, A + CALL dc_put_pixel ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Draw pixel on screen +; Inp: DE - X, Y +; --------------------------------------------------- +dc_put_pixel: + RET C ; return if CF set (out of screen) + PUSH HL + PUSH BC + CALL calc_px_addr + ; calculate B = pixel mask + LD A, 10000000b +.roll: + RLCA ; [07654321] <- [76547210], [7] -> CF + DEC B + JP P, .roll + CPL + LD B, A + ; DE = foreground color low and hi bytes + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Turn on Video RAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; Load VRAM[HL] byte (low byte), mask and set + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; Load VRAM[HL+1] byte (low byte), mask and set + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; Turn off Video RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + POP BC + POP HL + RET + + ; Full charset, Common + Latin letters (112*8=890) + INCLUDE "font-6x7.inc" + +; --------------------------------------------------- +; Convert 0h..Fh decimal value to symbol '0'..'F' +; --------------------------------------------------- +conv_nibble: + AND 0xf + ADD A, 0x90 + DAA + ADC A, 0x40 + DAA + LD C, A + RET + +; --------------------------------------------------- +; Print byte in HEX +; Inp: A - byte to print +; --------------------------------------------------- +m_hexb: + PUSH AF + RRCA + RRCA + RRCA + RRCA + CALL out_hex + POP AF + +out_hex: + CALL conv_nibble + CALL m_con_out ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Wtite RAM-Disk 64K to TAPE +; --------------------------------------------------- +m_tape_write_ram2: + LD HL, M_VARS.buffer + LD C, 128 +.cl_stack: + LD (HL), 0x0 + INC HL + DEC C + JP NZ, .cl_stack + LD HL, M_VARS.buffer + LD DE, 0xffff + ; write empty block + ; DE - block ID + ; HL -> block + CALL m_tape_write + CALL twr2_delay + LD DE, 0x0 + CALL m_tape_write + CALL twr2_delay + LD BC, 512 + LD DE, 0x0 +.nxt_blk: + PUSH BC + LD HL, M_VARS.buffer + CALL m_ramdisk_read + INC DE + CALL m_tape_write + CALL twr2_delay + POP BC + DEC BC + LD A, B + OR C + JP NZ, .nxt_blk + RET + +; --------------------------------------------------- +; Pause between blocks on tape +; --------------------------------------------------- +twr2_delay: + LD BC, 250 +.delay: + DEC BC + LD A, B + OR C + JP NZ, .delay + RET + +; --------------------------------------------------- +; Read RAM-Disk 64K from TAPE +; --------------------------------------------------- +m_tape_read_ram2: + LD A, 100 + CALL m_tape_wait + OR A + JP NZ, .end + LD E, 6 + +.srch_first: + DEC E + JP Z, .not_found + ; read block + LD HL, M_VARS.buffer + CALL m_tape_read + CP 4 + JP Z, .end + OR A + JP NZ, .srch_first + LD A, B + OR C + JP NZ, .srch_first + + LD BC, 512 + LD DE, 0x0 + +.rd_next: + PUSH BC + ; Read block from tape + CALL m_tape_read + OR A + JP NZ, .rd_error + DEC BC + LD A, B + CP D + JP NZ, .inv_id + LD A, C + CP E + JP NZ, .inv_id + ; Ok, write block to RAM disk + CALL m_ramdisk_write + INC DE + POP BC + DEC BC + LD A, B + OR C + JP NZ, .rd_next + RET +.not_found: + LD HL, msg_no_start_rec + CALL me_out_strz ; TODO: replace call+ret to jp + RET +.rd_error: + CP 2 + JP Z, .err_ubi + CP 4 + JP Z, .err_ibu + LD HL, msg_checksum + CALL me_out_strz + CALL out_hexw + POP BC + RET + + ; Illegal sequence of blocks +.inv_id: + LD HL, msg_sequence + CALL me_out_strz + INC BC + CALL out_hexw + POP BC + RET + +.err_ubi: + LD HL, msg_ibg + CALL me_out_strz + POP BC + RET + + ; Interrupted by user +.err_ibu: + POP BC +.end: + LD HL, msg_break + CALL me_out_strz ; TODO: replace call+ret to jp + RET + +; -------------------------------------------------- +; Output hex word +; Inp: BC - word to output +; -------------------------------------------------- +out_hexw: + PUSH BC + LD A, B + CALL m_hexb + POP BC + LD A, C + CALL m_hexb ; TODO: replace call+ret to jp + RET + +msg_no_start_rec: + DB "NO START RECORD", 0 +msg_checksum: + DB "CHECKSUM ", 0 +msg_sequence: + DB "SEQUENCE ", 0 +msg_ibg: + DB "IBG", 0 +msg_break: + DB "BREAK", 0 + +; --------------------------------------------------- +; Out ASCIIZ message +; Inp: HL -> zero ended string +; --------------------------------------------------- +me_out_strz: + LD A, (HL) + OR A + RET Z + PUSH BC + LD C, A + CALL m_con_out + INC HL + POP BC + JP me_out_strz + + + +; --------------------------------------------------- +; Read from RAM-disk to RAM +; Inp: DE - source sector +; HL -> destination buffer +; --------------------------------------------------- +m_ramdisk_read: + PUSH HL + PUSH DE + LD A, D + AND 0x1 ; Low 32K or Hi page or Ext RAM + OR 0x2 ; Set A16 address line + OR 0x0 ; TODO: nothing, remove + LD B, A ; B - value to turn on access to Ext RAM + ; Calculate DE = address from sector number + XOR A + LD A, E ; E - low address + RRA ; [CF] -> [7:0] -> [CF] + LD D, A ; D = E/2 + LD A, 0x0 + RRA ; [CF] -> E + LD E, A +.read: + ; Access to ExtRAM + LD A, B + OUT (SYS_DD17PB), A + ; Get Byte + LD A, (DE) + LD C, A + ; Access to RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + ; Set Byte + LD (HL), C + ; HL++, DE++ + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, .read ; jump if has more bytes + + ; Access to RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP DE + POP HL + RET + +; --------------------------------------------------- +; Write sector to RAM disk +; Inp: HL -> source buffer +; DE - destination sector +; --------------------------------------------------- +m_ramdisk_write: + PUSH HL + PUSH DE + LD A, D + AND 0x1 + OR 0x2 + OR 0x0 ; TODO: remove unused + LD B, A + XOR A + LD A, E + RRA + LD D, A + LD A, 0x0 + RRA + LD E, A +.wr_byte: + LD A, 0x0 + OUT (SYS_DD17PB), A + LD C, (HL) + LD A, B + OUT (SYS_DD17PB), A + LD A, C + LD (DE), A + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, .wr_byte + LD A, 0x0 + OUT (SYS_DD17PB), A + POP DE + POP HL + RET + +; -------------------------------------------------- +; Write block to Tape +; In: DE - block ID, +; HL -> block of data. +; -------------------------------------------------- +m_tape_write: + PUSH HL + PUSH DE + PUSH DE + LD BC, 2550 + LD A, PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A,TMR0_SQWAVE ; tmr0, load lsb+msb, sq wave, bin + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A, B + OUT (TMR_DD70C1), A + ; Write Hi+Lo, Hi+Lo + LD DE, 4 ; repeat next 4 times +.l1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; check rst4 from timer#0 + JP NZ, .l1 + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH ; tape level hi + JP NZ, .set_lvl + LD A, TL_LOW ; tape level low +.set_lvl: + OUT (DD67PC), A ; set tape level + LD A, TMR0_SQWAVE ; tmr0, load lsb+msb, swq, bin + ; timer on + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A, B + OUT (TMR_DD70C1), A + DEC E + JP NZ, .l1 + +.l2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .l2 + + ; Write 00 at start + LD A, 0x0 + CALL m_tape_wr_byte + ; Write 0xF5 marker + LD A, 0xf5 + CALL m_tape_wr_byte + LD E, 0x0 ; checksum=0 + ; Write block ID + POP BC + LD A, C + CALL m_tape_wr_byte + LD A, B + CALL m_tape_wr_byte + ; Write 128 data bytes + LD B, 128 +.next_byte: + LD A, (HL) + CALL m_tape_wr_byte + INC HL + DEC B + JP NZ, .next_byte + ; Write checksum + LD A, E + CALL m_tape_wr_byte + ; Write final zero byte + LD A, 0x0 + CALL m_tape_wr_byte +.wait_end: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .wait_end + LD A, TL_MID ; tape level middle + OUT (DD67PC), A + POP DE + POP HL + RET + + +; ------------------------------------------------------ +; Write byte to tape +; Inp: A - byte top write +; D - current level +; E - current checksum +; ------------------------------------------------------ +m_tape_wr_byte: + PUSH BC + ; calc checksum + LD B, A + LD A, E + SUB B + LD E, A + LD C, 8 ; 8 bit in byte +.get_bit: + LD A, B + RRA + LD B, A + JP C, .bit_hi +.wait_t: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .wait_t + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; program for 360 cycles + LD A, 0x68 + OUT (TMR_DD70C1), A + LD A, 0x1 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH + JP NZ, .out_bit + LD A, TL_LOW +.out_bit: + OUT (DD67PC), A + DEC C + JP NZ,.get_bit + POP BC + RET +.bit_hi: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .bit_hi + ; program for 660 cycles + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + LD A, 0x94 + OUT (TMR_DD70C1), A + LD A, 0x2 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH + JP NZ, .out_bit_hi + LD A, TL_LOW +.out_bit_hi: + OUT (DD67PC), A + DEC C + JP NZ, .get_bit + POP BC + RET + +; ------------------------------------------------------ +; Load block from Tape +; In: HL -> buffer to receive bytes from Tape +; Out: A = 0 - ok, +; 1 - CRC error, +; 2 - unexpected block Id +; 4 - key pressed +; ------------------------------------------------------ +m_tape_read: + PUSH HL + PUSH DE + LD A, PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A ; tmr0, load lsb+msb, sq wave + LD A, 0x0 + ; tmr0 load 0x0000 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + LD C, 3 +.wait_3_changes: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + LD A, B + ADD A, 4 + JP P, .wait_3_changes + DEC C + JP NZ, .wait_3_changes +.wait_4th_change: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + LD A, B + ADD A, 4 + JP M, .wait_4th_change + LD C, 0x0 +.wait_f5_marker: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + DEC A + RRA + LD A, C + RRA + LD C, A + CP 0xf5 + JP NZ, .wait_f5_marker + LD E, 0x0 ; checksum = 0 + ; Read blk ID + CALL m_tape_read_byte + JP NC, .err_read_id + LD C, D + CALL m_tape_read_byte + JP NC, .err_read_id + LD B, D + PUSH BC + ; Read block, 128 bytes + LD C, 128 +.read_next_b: + CALL m_tape_read_byte + JP NC, .err_read_blk + LD (HL), D + INC HL + DEC C + JP NZ, .read_next_b + + ; Read checksum + CALL m_tape_read_byte + JP NC, .err_read_blk + LD A, E + OR A + JP Z, .checksum_ok + LD A, 0x1 ; bad checksum +.checksum_ok: + POP BC +.return: + POP DE + POP HL + RET + +.err_read_blk: + POP BC + LD BC, 0x0 +.err_read_id: + LD A, 0x2 ; read error + JP .return +.key_pressed: + CALL m_con_in + LD C, A ; store key code in C + LD B, 0x0 + LD A, 0x4 + JP .return + +; ------------------------------------------------------ +; Read byte from Tape +; Out: D - byte +; CF is set if ok, cleared if error +; ------------------------------------------------------ +m_tape_read_byte: + PUSH BC + LD C, 8 +.next_bit: + CALL m_read_tape_bit + ; push bit from lo to hi in D + RRA + LD A, D + RRA + LD D, A + LD A, 4 + ADD A, B + JP NC, .ret_err + DEC C + JP NZ, .next_bit + ; calc checksum + LD A, D + ADD A, E + LD E, A + SCF +.ret_err: + POP BC + RET + +; ------------------------------------------------------ +; Read bit from tape +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +m_read_tape_bit: + IN A, (KBD_DD78PB) ; Read Tape bit 5 (data) + AND TAPE_P + LD B, A +.wait_change: + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z, .wait_change + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; [360...480...660] 0x220=544d + IN A, (TMR_DD70C1) ; get tmer#0 lsb + ADD A, 0x20 + IN A, (TMR_DD70C1) ; get tmer#0 msb + LD B, A + ADC A, 0x2 + ; reset timer to 0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; For 0 - 65535-360+544 -> overflow P/V=1 + ; For 1 - 65535-660+544 -> no overflow P/V=0 + RET P + INC A + RET + +; ------------------------------------------------------ +; Read bit from tape with keyboard interruption +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +read_tape_bit_kbd: + IN A, (KBD_DD78PB) + AND TAPE_P + LD B, A ; save tape bit state + ; wait change with keyboard check +.wait_change: + IN A, (PIC_DD75RS) + AND KBD_IRQ + JP NZ, .key_pressed + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z, .wait_change + ; measure time + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; read lsb+msb + IN A, (TMR_DD70C1) + ADD A, 0x20 + IN A, (TMR_DD70C1) + LD B, A + ADC A, 0x2 + ; reset timer#0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; flag P/V is set for 0 + RET P + INC A + RET +.key_pressed: + LD A, 0xff + RET + +; ------------------------------------------------------ +; Wait tape block +; Inp: A - periods to wait +; Out: A=4 - interrupded by keyboard, C=key +; ------------------------------------------------------ +m_tape_wait: + OR A + RET Z + PUSH DE + LD B, A +.wait_t4: + LD C,B + IN A, (KBD_DD78PB) + AND TAPE_P ; Get TAPE4 (Wait det) and save + LD E, A ; store T4 state to E +.wait_next_2ms: + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; load 3072 = 2ms + XOR A + OUT (TMR_DD70C1), A + LD A, 0xc + OUT (TMR_DD70C1), A +.wait_tmr_key: + IN A, (PIC_DD75RS) + AND KBD_IRQ ; RST1 flag (keyboard) + JP NZ, .key_pressed + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; RST4 flag (timer out) + JP Z, .wait_no_rst4 + IN A, (KBD_DD78PB) + AND TAPE_P ; TAPE4 not changed? + CP E + JP NZ, .wait_t4 ; continue wait + JP .wait_tmr_key +.wait_no_rst4: + DEC C + JP NZ, .wait_next_2ms + XOR A + POP DE + RET + +.key_pressed: + CALL m_con_in + LD C, A ; C = key pressed + LD A, 0x4 ; a=4 interrupted by key + POP DE + RET + +; ------------------------------------------------------ +; Check block marker from Tape +; Out: A=0 - not detected, 0xff - detected +; ------------------------------------------------------ +m_tape_blk_detect: + IN A, (KBD_DD78PB) + AND TAPE_D ; TAPE5 - Pause detector + LD A, 0x0 + RET Z + CPL + RET + +; ====================================================== +; FDC DRIVER +; ====================================================== + + +fdc_unload_head: + LD A, 0x0 + OUT (FDC_DATA), A + LD A, FDC_RESTORE_UH_NV + OUT (FDC_CMD), A + NOP + NOP + +.wait_no_busy: + IN A, (FDC_CMD) + AND 00000101b ; Track0 , Busy + CP 00000100b + JP Z, .tr0_ok + + IN A, (FLOPPY) + RLCA ; MOTST -> CF + JP NC, .wait_no_busy + LD A, 0x20 + RET +.tr0_ok: + LD A, B + DEC A + LD A, 0x1 + JP Z, .b1 + LD (M_VARS.ul_A_var1), A + XOR A + LD (M_VARS.ul_A_var4), A + RET + +.b1: + LD (M_VARS.ul_B_var2), A + XOR A + LD (M_VARS.ul_B_var5), A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_select_drive: + PUSH AF + CALL delay_1.4mS + CP 0x1 ; TODO: DEC A to save 1b 3t + JP Z, .sel_A + LD A, 0x2 + JP .sel_B +.sel_A + LD A, 0x5 +.sel_B + LD B, A ; 010b or 101b + IN A, (FLOPPY) ; read Floppy controller reg + AND 0x40 ; SSEL + RRA ; SSEL for out + OR B ; 0x22 or 0x25 if WP + OUT (FLOPPY), A ; Select drive A or B + LD B, A + POP AF + DEC A + JP Z, .dpar_a + LD A, (bios_var2) + JP .dpar_b +.dpar_a + LD A, (BIOS.bios_var04) +.dpar_b + PUSH BC + LD B, A ; B = dpar_A or B + LD A, D ; compare with D? + CP B + JP C, .l_le ; D < B + SUB B + LD D, A + POP BC + LD A, C + OR 0x8 + LD C, A + LD A, B + AND 0x20 + OR A + RET NZ + LD A, B + OR 0x20 ; set SSEL to 1 + OUT (FLOPPY), A + CALL delay_136uS + RET +.l_le + POP BC + LD A, B + AND 0x20 + OR A + RET Z + LD A, B + AND 0x7 + OUT (FLOPPY), A + CALL delay_136uS + RET + +; --------------------------------------------------- +; Delay for 136uS +; --------------------------------------------------- +delay_136uS: + LD B, 16 ; 7 + +; --------------------------------------------------- +; Delay for B*8uS +; --------------------------------------------------- +delay_b: + DEC B ; 4 + JP NZ, delay_b ; 10 + RET ; 10 + +; --------------------------------------------------- +; Delay for 1.4mS +; --------------------------------------------------- +delay_1.4mS: + LD B, 175 ; 7 + JP delay_b ; 10 + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_read_floppy: + PUSH AF + CALL m_select_drive + POP AF + CALL m_start_seek_track + JP C, fdc_ret + CALL m_fdc_read_c_bytes + JP C, fdc_ret + XOR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_write_floppy: + PUSH AF + CALL m_select_drive + POP AF + CALL m_start_seek_track + JP C, fdc_ret + CALL m_fdc_write_bytes + JP C, fdc_ret + XOR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_start_seek_track: + CALL m_start_floppy + RET C + CALL m_fdc_seek_trk + RET C + RET ; TODO: remove + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_start_floppy: + LD B, A + LD A, (M_VARS.ul_var3) + CP B + JP Z, .need_m_start + CALL .wait_motor ; TODO: replace call+ret to jp + RET +.need_m_start: + IN A, (FLOPPY) + RLCA ; check MOTST + JP C, .wait_motor + IN A, (FDC_CMD) + AND FDC_NOT_READY ; not ready flag + RET Z + +; --------------------------------------------------- +; +; --------------------------------------------------- +.wait_motor: + PUSH BC + LD BC, 65535 + CALL fdc_init +.wait_rdy1: + IN A, (FDC_CMD) + AND FDC_NOT_READY + JP Z, .long_delay + IN A, (FLOPPY) + RLCA ; CF<-A[7] MOTST flag + JP NC, .wait_rdy1 + LD A, 0x20 + JP .mst_exi +.long_delay: + DEC BC + LD A, B + OR A + JP NZ, .long_delay +.mst_exi: + POP BC + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +fdc_init: + IN A, (FLOPPY) + AND 01001110b ; Get SSEL, DRSEL, MOT1, MOT0 + RRA + OUT (FLOPPY), A + OR 0x08 ; Set INIT bit + OUT (FLOPPY), A + RET + +; --------------------------------------------------- +; Seek track on floppy +; Inp: DE - track/sector +; --------------------------------------------------- +m_fdc_seek_trk: + LD A, B + DEC A + JP Z, .drv_b + LD A, (M_VARS.ul_A_var1) + OR A + CALL Z, fdc_unload_head + RET C + LD A, (M_VARS.ul_A_var4) + OUT (FDC_TRACK), A + LD A, D + LD (M_VARS.ul_A_var4), A + JP .cmn +.drv_b: + LD A, (M_VARS.ul_B_var2) + OR A + CALL Z, fdc_unload_head + RET C + LD A, (M_VARS.ul_B_var5) + OUT (FDC_TRACK), A + LD A, D + LD (M_VARS.ul_B_var5), A +.cmn: + LD A, (M_VARS.ul_var3) + CP B + LD A, B + LD (M_VARS.ul_var3), A + JP NZ, .l2 + IN A, (FDC_TRACK) + CP D + JP Z, .l2 + JP C, .l1 + LD A, (M_VARS.ul_var6) + OR A + JP NZ, .l2 + LD B, 0xff + CALL delay_b + LD A, 0x1 + LD (M_VARS.ul_var6), A + JP .l2 +.l1: + LD A, (M_VARS.ul_var6) + OR A + JP Z, .l2 + LD B, 0xff + CALL delay_b + LD A, 0x0 + LD (M_VARS.ul_var6), A +.l2: + LD A, D + OUT (FDC_DATA), A + LD A, 0x1f + OUT (FDC_CMD), A + NOP + NOP + IN A, (FDC_WAIT) + IN A, (FDC_CMD) + AND 0x19 + CP 0x0 + JP NZ, .l3 + JP .l4 +.l3: + SCF + LD A, 0x40 +.l4: + PUSH AF + LD A, E + OUT (FDC_SECT), A + POP AF + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_fdc_write_bytes: + LD A, C + OUT (FDC_CMD), A +.w_next: + IN A, (FDC_WAIT) + RRCA + LD A, (HL) + OUT (FDC_DATA), A + INC HL + JP C, .w_next + CALL fdc_check_status ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_fdc_read_c_bytes: + LD A, C + OUT (FDC_CMD), A + JP .l2 +.l1: + LD (HL), A + INC HL +.l2: + IN A, (FDC_WAIT) + RRCA + IN A, (FDC_DATA) + JP C, .l1 + CALL fdc_check_status ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Check fdc status for errors +; Out: CF set if errors +; --------------------------------------------------- +fdc_check_status: + IN A, (FDC_CMD) + AND 11011111b + CP 0x0 + JP Z, fdc_ret + SCF +fdc_ret: + RET + +filler1: + DB 20h + +; filler: +; ds 169, 0xff + +; ------------------------------------------------------ + +LAST EQU $ +CODE_SIZE EQU LAST-0xE000 +FILL_SIZE EQU ROM_CHIP_SIZE-CODE_SIZE + + + ASSERT m_hot_start = 0xe051 + ASSERT m_out_strz = 0xe0f1 + ASSERT m_con_out_int = 0xe16d + ASSERT get_esc_param = 0xe187 + ASSERT esc_params_tab = 0xe1cb + ASSERT esc_handler_tab = 0xe1db + ASSERT esc_set_beep = 0xe1f9 + ASSERT m_print_hor_line = 0xe23a + ASSERT m_get_7vpix = 0xe2b4 + ASSERT esc_set_palette = 0xe2fe + ASSERT m_get_glyph = 0xe320 + ASSERT m_print_no_esc = 0xe349 + ASSERT calc_addr_40 = 0xe439 + ASSERT mp_mode_64 = 0xe4b8 + ASSERT calc_addr_80 = 0xe612 + ASSERT m_clear_screen = 0xe639 + ASSERT m_cursor_home = 0xe66c + ASSERT m_draw_cursor = 0xe69a + ASSERT m_handle_esc_code = 0xe77c + ASSERT handle_cc_common = 0xe7c4 + ASSERT handle_cc_80x25 = 0xe833 + ASSERT m_beep = 0xe85a + ASSERT esc_set_cursor = 0xe890 + ASSERT esc_set_vmode = 0xe8e9 + ASSERT esc_set_color = 0xe92f + ASSERT m_print_at_xy = 0xe943 + ASSERT game_sprite_tab = 0xea39 + ASSERT esc_draw_fill_rect = 0xeb64 + ASSERT draw_line_h = 0xeed1 + ASSERT esc_draw_line = 0xef0b + ASSERT esc_draw_dot = 0xf052 + ASSERT esc_picture = 0xf0a4 + ASSERT m_fn_39 = 0xf10f + ASSERT get_image_hdr = 0xf177 + ASSERT esc_get_put_image = 0xf1b5 + ASSERT pict_sub2 = 0xf3ca + ASSERT m_font_cp0 = 0xf5bc + ASSERT me_out_strz = 0xfb36 + ASSERT m_ramdisk_write = 0xfb6d + ASSERT m_tape_write = 0xfb97 + ASSERT m_tape_wr_byte = 0xfc0e + ASSERT m_tape_read_byte = 0xfcee + ASSERT m_read_tape_bit = 0xfd08 + ASSERT m_tape_wait = 0xfd58 + + + ; DISPLAY "Code size is: ", /A, CODE_SIZE + + +FILLER + DS FILL_SIZE, 0xFF + ; DISPLAY "Free size is: ", /A, FILL_SIZE + + ENDMODULE + + OUTEND + + OUTPUT m_vars.bin + ; put in separate waste file + INCLUDE "m_vars.inc" + OUTEND diff --git a/MON_v8_c4eec374/ram.inc b/MON_v8_c4eec374/ram.inc new file mode 100644 index 0000000..8c6c08e --- /dev/null +++ b/MON_v8_c4eec374/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +@RST1 EQU 0x0008 +@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/Test/.vscode/extensions.json b/Test/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/Test/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/Test/.vscode/launch.json b/Test/.vscode/launch.json new file mode 100644 index 0000000..c6e8ff2 --- /dev/null +++ b/Test/.vscode/launch.json @@ -0,0 +1,94 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "dezog", + "request": "launch", + "name": "Internal Simulator", + "remoteType": "zsim", + "zsim": { + "visualMemory": true, + "memoryModel": "CUSTOM", + "customMemory": { + "slots": [ + { + "name": "PAGE0", + "range": ["0x0000","0x3FFF"], + "banks": [{"index": [0, 255]}], + "initialBank": 0 + }, + { + "name": "PAGE1", + "range": ["0x4000","0x7FFF"], + "banks": [{"index": [0, 255]}], + "initialBank": 1 + }, + { + "name": "PAGE2", + "range": ["0x8000","0xBFFF"], + "banks": [{"index": [0, 255]}], + "initialBank": 2 + }, + { + "name": "PAGE3", + "range": ["0xC000","0xFFFF"], + "banks": [{"index": [0, 255]}], + "initialBank": 3 + } + ], + "ioMmu": [ + "if (portAddress == 0x82) {", + " bank = portValue;", + " PAGE0 = bank;", + "}", + "if (portAddress == 0xA2) {", + " bank = portValue;", + " PAGE1 = bank;", + "}", + "if (portAddress == 0xC2) {", + " bank = portValue;", + " PAGE2 = bank;", + "}", + "if (portAddress == 0xE2) {", + " bank = portValue;", + " PAGE3 = bank;", + "}" + ] + }, + "customCode": { + "debug": false, + "jsPath": "sim/ports.js" + //"uiPath": "simulation/ui.html" + }, + //"ulaScreen": true, + //"zxBorderWidth": 20, + //"vsyncInterrupt": true, + //"zxKeyboard": true, + //"zxBeeper": true + }, + "sjasmplus": [ + { + "path": "test_O240.sld" + } + ], + "history": { + "reverseDebugInstructionCount": 1000000, + "spotCount": 10, + "codeCoverageEnabled": true + }, + "startAutomatically": false, + "commandsAfterLaunch": [], + "rootFolder": "${workspaceFolder}", + "topOfStack": "STACK_TOP", + "loadObjs": [ + { + "path": "test_O240.obj", + "start": "0x0000" + } + ], + "execAddress": "0x8100", + "smallValuesMaximum": 513, + "tmpDir": ".tmp" + } + ] +} \ No newline at end of file diff --git a/Test/.vscode/tasks.json b/Test/.vscode/tasks.json new file mode 100644 index 0000000..7741c0c --- /dev/null +++ b/Test/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "make (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=test_O240.sld", + "--sym=test_O240.labels", + "--raw=test_O240.obj", + "--fullpath", + "test_O240.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "start mame", + "type": "shell", + "command": "while true; do ./mame spectrum -window -debugger gdbstub -debug -debugger_port 12000 -verbose -resolution 512x384 ; sleep 2 ; done", + "options": { + "cwd": "${config:mame_dir}" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/Test/README.md b/Test/README.md new file mode 100644 index 0000000..604b430 --- /dev/null +++ b/Test/README.md @@ -0,0 +1,7 @@ +# Ocean-240.2 Test ROM Sources + +Source codes of TEST ROM for Ocean-240.2 computer. + +Source codes in Z80 mnemonics, but limited for i8080 instruction set. + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). diff --git a/Test/test_O240.asm b/Test/test_O240.asm new file mode 100644 index 0000000..111ac6b --- /dev/null +++ b/Test/test_O240.asm @@ -0,0 +1,1249 @@ +; ======================================================= +; Исходный текст тестового ПЗУ ПК "Океан 240.2" +; Дизасемблировал Romych, 2025-08-20 +; в мнемонике ассемблера Z80 в фомате sjasmplus +; ======================================================= + + DEVICE NOSLOT64K + +; КР580ВВ55 System +DD17RA EQU 0xC0 ; VShift[8..1] +DD17RB EQU 0xC1 ; [A14,A13,REST,ENROM,A18,A17,A16,32K] +DD17RC EQU 0xC2 ; HShift[HS5..1,SB3..1] +DD17RR EQU 0xC3 ; VV55 Sys CTR + +; КР580ВВ55 LPT/Video/Tape +DD67RA EQU 0xE0 ; [LP7..0] +DD67RB EQU 0xE1 ; [VSU,C/M,FL3..1,COL3..1] +DD67RC EQU 0xE2 ; [USER3..1,STB-LP,BELL,TAPE3..1] +DD67RR EQU 0xE3 ; VV55 Video CTR + +; КР580ВИ53 +DD70C1 EQU 0x60 ; VI53 TIM/CTR1 +DD70C2 EQU 0x61 ; VI53 TIM/CTR2 +DD70C3 EQU 0x62 ; VI53 TIM/CTR3 +DD70RR EQU 0x63 ; VI53 TIM CTR + +; КР580ВВ51 +DD72RD EQU 0xA0 ; VV51 +DD72RR EQU 0xA1 ; VV51 [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] + +; КР580ВН59 +DD75RS EQU 0x80 ; VN59 +DD75RM EQU 0x81 ; VN59 + +; КР580ВВ55 Порты A и С, B5-7 - Клавиатура +DD78RA EQU 0x40 ; VV55 [Keyboard] +DD78RB EQU 0x41 ; [JST3..1,ACK,TAPE5,TAPE4,GK,GC] +DD78RC EQU 0x42 ; VV55 [PC7..0] +DD78RR EQU 0x43 ; VV55 KBD CTL + +; КР580ВВ55 Для внешних устройств пользователя +DD79RA EQU 0x00 ; VV55 User PA +DD79RB EQU 0x01 ; VV55 User PB +DD79RC EQU 0x02 ; VV55 User PC +DD79RR EQU 0x03 ; VV55 User CTR + +; КР580ВВ55 Для устройств пользователя или FDC +DD80RA EQU 0x20 ; VV55 FDC PA +DD80RB EQU 0x21 ; VV55 FDC PB +DD80RC EQU 0x22 ; VV55 FDC PC +DD80RR EQU 0x23 ; VV55 FDC CTR + +; Паттерны тестирования памяти +PATTERN1 EQU 0x55FF +PATTERN2 EQU 0x00FF +PATTERN3 EQU 0xFF00 + +; Размер блока +B32K EQU 0x8000 +B48K EQU 0xC000 + +; Задержки +DELAY1 EQU 0xFFFF ; задержка +W_BUSY EQU 0x9C40 ; таймаут ожидания готовности принтера +W_BUTN EQU 0xC350 ; таймаут отпускания клавиши S1 + +; Переменные для прерывания +T_STACK EQU 0x100 ; временный стек для прерывания +INT_CALL EQU 0x20 ; код инструкции перехода +INT_ADDR EQU 0x21 ; адрес обработчика прерывания + +; Флаги статуса ВВ51 +TX_RDY EQU 0x01 ; передатчик готов +RX_RDY EQU 0x02 ; приемник готов +; TX_EMPTY EQU 0x04 ; буфер передатчика пуст +; TX_PE EQU 0x08 ; Parity error +; TX_OE EQU 0x01 ; Overrun error +; TX_FE EQU 0x02 ; Framing error +; TX_DSR EQU 0x08 + +; Биты порта чтения с магнитофона +TAPE_4 EQU 0x04 +TAPE_5 EQU 0x08 + +; Флаги прерывания ВН59 +RST_1 EQU 0x02 +RST_2 EQU 0x04 +RST_3 EQU 0x08 + +; Бит порта строба клавиатуры +KBD_ACK EQU 0x80 + +; Флаг гашения кадра +BIT_GK EQU 0x02 ; бит гашения кадра + + +; ======================================================= +; Код теста в памяти располагается с адреса 0xE000 +; ======================================================= + + ORG 0xE000 + + JP TEST1 +; ------------------------------------------------------- +; TEST 1 Программирование портов ВВ55, Таймера ВИ53, +; Последовательного порта ВВ51 +; ------------------------------------------------------- +TEST1 + DI + ; Программирование ВВ55 + LD A, 0x80 ; 1000 0000 -> PA mode 0 out, PC out, PB mode 0 out + OUT (DD17RR), A ; mem + OUT (DD67RR), A ; video + OUT (DD80RR), A ; user + + LD A, 0x93 ; 1001 0011 -> PA mode 0 inp, PB mode 0 inp, PC hi out, PC lo inp + OUT (DD78RR), A + LD A, 0x47 ; 0100 0111 -> VSU=0, C/M=1, FL=000, COL=111 + OUT (DD67RB), A + LD A, 0x07 ; 0000 0111 -> H Shift HS=00000, SB=111 + OUT (DD17RC), A + LD A, 0x04 ; 0000 0100 -> TAPE3 = 1 + OUT (DD67RC), A + + ; Программирование порта ВВ51 + LD A, 0xCE ; 1100 1110 -> Mode: 11 - 2S, 00-N, 11-8b, 76.8КГц/16=4800 бод + OUT (DD72RR), A + LD A, 0x25 ; 0010 0101 -> 0-Norm, 0-No Rst, 1-RTS-=0, 0-No Err, 0-No brk, 1-RxEn, 0-DTR-=1, 1-TxEn + OUT (DD72RR), A + + ; Программирование таймера ВИ53 + LD A, 0x76 ; 0111 0110 -> 01-CTR2, 11-LSB+MSB, 011-square wave gen, 0-binary + OUT (DD70RR), A + LD A, 20 ; 1.536MHz/20=76.8КГц -> /16=4800 бод + OUT (DD70C2), A + LD A, 0x00 + OUT (DD70C2), A + + ; Программирование rонтроллера прерываний ВН59 + LD A, 0x12 ; ICW1 (D4=1) 0001 0010 -> D1=1 - Single mode + OUT (DD75RS), A + LD A, 0x00 ; ICW2 Interrup vector address = 0000 + OUT (DD75RM), A + LD A, 0xFF ; OCW1 1111 1111 -> Маскируем все прерывания + OUT (DD75RM), A + LD A, 0x20 ; OCW2 0010 0000 -> 001 - Non-specific EOI command, 000 - Active IR Level 0 + OUT (DD75RS), A + LD A, 0x0A ; OCW3 0000 1010 -> D3=1 Pool command, 01-No OP, RIS=0 + OUT (DD75RS), A + + LD SP, TEST1_1 ; возврат на TEST2 + LD D, 0x00 ; 0 - passed ok + LD E, 0x01 ; номер теста + JP MSG + +TEST1_1 + ; инкремент данных на пользовательском порту + LD A, D + OUT (DD80RA), A + OUT (DD80RB), A + OUT (DD80RC), A + INC D + ; проверка нажатия SA1 + IN A, (DD72RR) + AND RX_RDY + JP Z, TEST1_1 + + XOR A + OUT (DD80RA), A + OUT (DD80RB), A + OUT (DD80RC), A + + LD SP, TEST2 + JP WAIT_BTN + +; ------------------------------------------------------- +; Тест 2 Проверка работы устройства отображения +; ------------------------------------------------------- +TEST2 + ; вкл доступа к видео-ЗУ + LD A, 0x01 + OUT (DD17RB), A + ; вывод шахматки + LD C, 0x00 +T4C + LD A, C + AND 0x07 + LD B, A + LD A, C + RRA + RRA + RRA + AND 0x01 + ADD A, B + LD DE, 0x00 + RRA + JP NC, T46 + LD DE, PATTERN1 + OR A + JP Z, T46 + LD DE, PATTERN2 + CP 0x01 + JP Z, T46 + LD DE, PATTERN3 + CP 0x02 + JP Z, T46 + LD E, 0xff +T46 + LD A,C + AND 0x7 + ADD A,A + ADD A,A + ADD A,A + ADD A,A + ADD A,A + LD L,A + LD B,0x20 +T4B + LD A,C + AND 0xf8 + ADD A,0x40 + LD H,A + LD A,0x4 +T4A + LD (HL),E + INC H + LD (HL),D + INC H + DEC A + JP NZ,T4A + INC L + DEC B + JP NZ,T4B + INC C + LD A,C + CP 0x40 + JP NZ, T4C + ; нормальный маппинг ОЗУ/ПЗУ + LD A, 0x0 + OUT (DD17RB), A + + LD A, 0x40 ; 0100 000 - Color mode ON + OUT (DD67RB),A + LD E, 0x0 + LD D, 0x0 +T57 + LD BC, B48K +T4E + DEC BC + LD A,B + OR C + JP NZ,T4E + LD C, 0x80 + + ; ждем GK - гашение кадра +T2_WAIT_GK + IN A, (DD78RB) + AND BIT_GK + JP NZ, T2_WAIT_GK + +T2_WAIT_N_GK + IN A, (DD78RB) + AND BIT_GK + JP Z, T2_WAIT_N_GK + + LD A, E + RRA + JP C, T51 + INC D + JP T52 +T51 + DEC D +T52 + RRA + JP C, T53 + LD A,D + ; горизонтальный битовый сдвиг + ADD A, 0x7 + OUT (DD17RC), A + JP T54 +T53 + LD A,D + ; вертикальный сдвиг + OUT (DD17RA), A +T54 + DEC C + JP NZ, T2_WAIT_GK + LD BC, B48K +T55 + DEC BC + LD A,B + OR C + JP NZ, T55 + INC E + LD A, E + CP 0x4 + JP P, T56 + OR 0x40 ; 0100 000 + OUT (DD67RB), A + JP T57 +T56 + ; Доступ к старшим 32К доп. ОЗУ + LD A, 0x3 + OUT (DD17RB), A + LD C, 0x00 + ; шахматка в mono режиме +T5B + LD A, C + AND 0x07 + LD B, A + LD A, C + RRA + RRA + RRA + AND 0x01 + ADD A, B + LD D, 0x00 + RRA + JP NC, T58 + LD D, 0xff +T58 + LD A, C + AND 0x07 + ADD A, A + ADD A, A + ADD A, A + ADD A, A + ADD A, A + LD L, A + LD B, 0x20 +T5A + LD A, C + AND 0xF8 + ADD A, 0x40 + LD H, A + LD A, 0x04 +T59 + LD (HL), D + INC H + LD (HL), D + INC H + DEC A + JP NZ, T59 + INC L + DEC B + JP NZ, T5A + INC C + LD A,C + CP 0x40 + JP NZ, T5B + + ; Нормальный режим работы памяти + LD A, 0x00 + OUT (DD17RB),A + + LD A, 0x80 ; 1000 0000 - VSU=1 Mono + OUT (DD67RB), A + LD E, 0x00 +T5E + LD BC, DELAY1 +T2_DLY2 + DEC BC + LD A, B + OR C + JP NZ, T2_DLY2 + + LD A, E + ADD A, A + ADD A, A + ADD A, A + OR 0x80 + OUT (DD67RB), A ; VSU=1 + INC E + LD A, E + CP 0x07 + JP C, T5E + LD SP, T2_W_S2 + LD D, 0x00 ; ok + LD E, 0x02 ; test #2 + JP MSG + +T2_W_S2 + IN A, (DD72RR) + AND RX_RDY + JP Z, T2_W_S2 + + LD SP, TEST3 + JP WAIT_BTN + +;------------------------------------------------------- +; Тест3 Проверка ШД ОЗУ +; проверка шины данных ОЗУ) выполняет последовательную +; проверку ячеек ОЗУ на соответствие записываемых и +; считываемых 8-разрядных слов (для значений 00H и FFH) +;------------------------------------------------------- +TEST3 + LD A, 0xC0 ; 1100 0000 VSU=1 Color mode + OUT (DD67RB),A + LD E, 0x00 +T3_TEST_PAGE + LD A, E + ; выбор режима доступа к озу + AND 0x03 + OUT (DD17RB), A + LD HL, 0x0000 + LD BC, B32K + ; проверка записи и чтения ячейки ОЗУ +T3_TEST_CELL + LD (HL), 0x00 + LD A, (HL) + CP 0x00 + JP NZ, T3_TEST_ERR + + LD (HL), 0xFF + LD A, (HL) + CP 0xFF + JP NZ, T3_TEST_ERR + + INC HL ; addr++ + DEC BC ; counter-- + LD A, B + OR C + JP NZ, T3_TEST_CELL + ; переход к следующему банку ОЗУ + INC E + LD A, E + ; если E=4, закончим + CP 0x04 + JP M, T3_TEST_PAGE + ; тест успешен, вывод сообщения + LD D, 0x00 + LD E, 0x03 + LD SP, TEST4 + JP MSG + + ; тест ОЗУ завершен ошибкой +T3_TEST_ERR + LD A, E + AND 0x01 + RRA + RRA + ADD A, H + LD H, A + LD A, E + LD SP, T3_ERR_CONT + ; 0-осн. 1-доп ОЗУ + AND 0x02 + LD D, 0x01 + JP Z, T3_EPN3 + LD D, 0x05 +T3_EPN3 + LD E, 0x03 + JP MSG + +T3_ERR_CONT + EX DE, HL + LD A, H + AND 0x7F + LD H, A + + ; запись/чтение в ошибочную ячеку памяти и ожидание кнопки S1 +T4_WR_WS1 + LD (HL), 0x00 + LD A, (HL) + LD (HL), 0xFF + LD A, (HL) + IN A, (DD72RR) + AND RX_RDY + JP Z, T4_WR_WS1 + + ; обычный режим памяти + LD A, 0x00 + OUT (DD17RB), A + JP T4_L1 + +;------------------------------------------------------- +; Тест 4 Проверка адресов и регенерации ОЗУ +;------------------------------------------------------- +TEST4 + + ; обычный режим памяти + LD A, 0x00 + OUT (DD17RB), A +T4_WS1 + IN A, (DD72RR) + AND RX_RDY + JP Z, T4_WS1 + +T4_L1 + LD SP, T4_L2 + JP WAIT_BTN + +T4_L2 + LD E, 0x0 + +T4_W_PG + LD A, E + ; выбор страницы ОЗУ + AND 0x03 + OUT (DD17RB), A + LD HL, 0x0000 + LD BC, B32K + +T4_W_FN + ; value = (Addr AND 0FFH) + (Addr/8) + LD A, E + AND 0x01 + RRA + RRA + ADD A, H + ADD A, L + LD (HL), A + INC HL ; addr++ + DEC BC ; ctr++ + LD A,B + OR C + JP NZ,T4_W_FN + + INC E + LD A,E + CP 0x4 + JP M, T4_W_PG + + ; ожидание 0,5 сек + LD BC,DELAY1 +T4_WS2 + DEC BC + LD A, B + OR C + JP NZ, T4_WS2 + + ; чтение записанного, после паузы, проверка refresh + LD E, 0x00 +T4_R_PG + LD A, E + AND 0x03 + ; выбор страниц ОЗУ + OUT (DD17RB), A + LD HL, 0x0000 + LD BC, B32K +T4_R_FN + LD A, E + AND 0x01 + RRA + RRA + ADD A,H + ADD A,L + ; прочитали то, что писали? + CP (HL) + JP NZ, T4_R_ERR + INC HL ; addr++ + DEC BC ; ctr-- + LD A,B + OR C + JP NZ, T4_R_FN + + INC E + LD A,E + CP 0x04 + JP M,T4_R_PG + + ; тест завершен, нормальноая адресация ОЗУ + LD A, 0x00 + OUT (DD17RB),A + ; вывод результата теста 4 ok + LD D, 0x00 + LD E, 0x04 + LD SP, TEST5 + JP MSG + + ; вывод ошибки теста 4 +T4_R_ERR + ; установка флага осн/доп ОЗУ + LD A, E + AND 0x01 + RRA + RRA + ADD A, H + LD H, A + LD A, E + LD SP, TEST5 + AND 0x02 + LD D, 0x01 + JP Z, T4_EPN3 + LD D, 0x05 +T4_EPN3 + LD E, 0x04 + JP MSG + +;------------------------------------------------------- +; Тест 5 Запись на кассетный магнитофон тестового +; сигнала +;------------------------------------------------------- +TEST5 + ; обычный режим памяти + LD A, 0x0 + OUT (DD17RB), A + +T5_W_S1 + IN A, (DD72RR) + AND RX_RDY + JP Z, T5_W_S1 + + LD SP, T5_L1 + JP WAIT_BTN + +T5_L1 + LD B, 0x04 +T5_L2 + LD C, 0x06 + LD DE, T5_L5 +T5_L3 + LD A, (DE) +T5_L4 + ADD HL, HL + ADD HL, HL + DEC H + NOP + NOP + DEC A + JP NZ, T5_L4 + + LD A, B + XOR 0x02 + LD B, A + ; вывод данных на ленту + OUT (DD67RC), A + INC DE + DEC C + JP NZ, T5_L3 + IN A, (DD72RR) + AND 0x2 + JP Z, T5_L2 + LD D, 0x0 + LD E, 0x5 + LD SP, TEST6 + JP MSG + +T5_L5 + DB 0x0F, 0x07, 0x0F, 0x0B, 0x07, 0x44 + +;------------------------------------------------------- +; Тест 6 проверка правильности настройки усилителя- +; формирователя (УФ) считывания +;------------------------------------------------------- +TEST6 + LD SP, T6_L1 + JP WAIT_BTN + + ; ожидание паузы при чтении с ленты +T6_L1 + IN A, (DD78RB) + AND TAPE_4 + LD B, A +T6_L2 + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L2 + LD B, A +T6_INT_NXT + LD C, 0x00 +T6_L4 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END ; лишнее + + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L4 + LD B, A + LD A, C + CP 0x19 + JP C, T6_INT_NXT + LD C, 0x00 + +T6_L5 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END + + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L5 + + LD B,A + LD A,C + CP 0x09 + JP C, T6_INT_ERR + CP 0x09 + JP C, T6_INT_ERR + LD C, 0x00 + +T6_L6 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END + + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L6 + + LD B, A + LD A, C + CP 0x07 + JP NC, T6_INT_ERR + CP 0x07 + JP NC, T6_INT_ERR + LD C, 0x00 + +T6_L7 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L7 + + LD B, A + LD A, C + CP 0x09 + JP C, T6_INT_ERR + CP 0x09 + JP C, T6_INT_ERR + LD C, 0x00 + +T6_L9 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END + + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L9 + LD B, A + LD A, C + CP 0x09 + JP NC, T6_INT_ERR + CP 0x07 + JP C, T6_INT_ERR + LD C, 0x00 + +T6_L10 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L10 + LD B, A + LD A, C + CP 0x07 + JP NC, T6_INT_ERR + LD E, '+' + JP T6_W_TX + + ; вывод '-' при несоответствии интервала +T6_INT_ERR + LD E, '-' + + ; ждем готовности передатчика и выводим +T6_W_TX + IN A, (DD72RR) + AND TX_RDY + JP Z, T6_W_TX + LD A, E + OUT (DD72RD), A + + CP 0x2B + JP Z, T6_L12 + ; импульс 5мс на РА0 DD80 + LD A, 0x01 + OUT (DD80RA), A + XOR A + OUT (DD80RA), A + +T6_L12 + IN A, (DD72RR) + AND RX_RDY + JP Z, T6_INT_NXT + +T6_END + ; тест 6 ок + LD D, 0x00 + LD E, 0x06 + LD SP, TEST7 + JP MSG + +;------------------------------------------------------- +; Тест 7 ввод с клавиатуры 7-разрядных кодов символов +; и передача их на терминал +;------------------------------------------------------- +TEST7 + LD SP, T7_W_STB + JP WAIT_BTN + ; ждем сигнала запроса прерывания от клавиатуры (STB-) +T7_W_STB + IN A, (DD75RS) + AND RST_1 + JP NZ, T7_KBD_RQ + + IN A, (DD72RR) + AND RX_RDY + JP Z, T7_W_STB + ; Выход из теста по кнопке S1 с сообшением об успехе + LD D, 0x0 + LD E, 0x7 + LD SP, TEST8 + JP MSG + + ; обработка клавиши +T7_KBD_RQ + ; чтение кода клавиши + IN A, (DD78RA) + ; код клавиши в порт PA DD80 + OUT (DD80RA), A + LD B, A + LD A, KBD_ACK + ; подтверждение чтения клавиатуры сигналом ACK + OUT (DD78RC), A + ; ожидаем реакции клавиатуры +T7_W_STB1 + IN A, (DD75RS) + AND RST_1 + JP NZ, T7_W_STB1 + ; убираем сигнал ACK + XOR A + OUT (DD78RC), A + ; ждем готовности передатчика +T7_W_TXR + IN A, (DD72RR) + AND TX_RDY + JP Z, T7_W_TXR + ; вывод кода клавиши в последовательный порт + LD A, B + OUT (DD72RD), A + JP T7_W_STB + +;------------------------------------------------------- +; Тест 8 Проверки контроллера прерываний +; и системного таймера +;------------------------------------------------------- +TEST8 + LD E, 4 + ; ждем 2с +T8_W2 + LD BC, DELAY1 +T8_W05 + DEC BC + LD A, B + OR C + JP NZ, T8_W05 + DEC E + JP NZ, T8_W2 + + ; программируем таймер + LD A, 0x36 ; 0011 0110 -> 00 - канал , 11 - запись слова, 011 - режим 3, 0 - двоичный счет + OUT (DD70RR), A + ; сброс счетчика + XOR A + OUT (DD70C1), A + OUT (DD70C1), A + + LD SP, T_STACK + LD A, 0xC3 ; CALL xxxx + LD (INT_CALL), A + LD HL, T9_INT_HNDL + LD (INT_ADDR), HL + + ; программируем контроллер на прерывание от таймера + LD A, 0xEF ; 1110 1111 -> вкл RST4 + OUT (DD75RM), A + LD A,0x20 + OUT (DD75RS), A + + ; пауза на ловлю прерывания + LD DE, DELAY1 + EI +T9_W_IRQ + DEC DE + LD A, D + OR E + JP NZ, T9_W_IRQ + + ; не поймали, ошибка и переход на тест 9 + DI + LD E, 0x08 + LD D, 0x02 + LD SP, TEST9 + JP MSG + +T9_INT_HNDL + DI + ; поймали прерывание без ошибок + LD E, 0x08 + LD D, 0x00 + LD SP,TEST9 + JP MSG + +;------------------------------------------------------- +; Тест 9 Проверка печати на принтер +;------------------------------------------------------- +TEST9 + ; ждем кнопку S1 + LD SP, T9_W_S1 + JP WAIT_BTN +T9_W_S1 + IN A,(DD72RR) + AND RX_RDY + JP Z,T9_W_S1 + + LD SP, T9_L1 + JP WAIT_BTN +T9_L1 + + LD C, 0x20 ; ' ' +T9_LE_127 + LD DE, W_BUSY ; 9C40 + + ; статус сигнала BUSY от принтера +T9_W_BUSY + IN A, (DD75RS) + AND RST_3 + JP NZ, T9_STROBE_ON + + DEC DE + LD A,D + OR E + JP NZ, T9_W_BUSY + ; принтер не освободился + JP T9_T_OUT + + ; выдаем строб +T9_STROBE_ON + LD A, C + OUT (DD67RA), A ; вывод символа + LD A, 0x14 ; 0001 0100 STROBE + TAPE3 + OUT (DD67RC), A + ; ждем реакции принтера + LD DE, W_BUSY +T9_W_BUSY1 + IN A,(DD75RS) + AND RST_3 + + JP Z, T9_STROBE_OFF + DEC DE + LD A,D + OR E + JP NZ,T9_W_BUSY1 + JP T9_T_OUT + ; убираем строб +T9_STROBE_OFF + LD A, 0x04 ; 0000 0100 + OUT (DD67RC), A + +T9_T_OUT + IN A, (DD72RR) + AND RX_RDY + JP NZ, T9_S1 + INC C ; следующий код символа + LD A, C + CP 127 ; печатные символы с кодом <127 + JP C, T9_LE_127 + LD C, ' ' + JP T9_LE_127 + + ; выход из теста по клавише S1 +T9_S1 + LD E, 0x09 + LD D, 0x00 + LD SP, TEST_END + JP MSG + +;------------------------------------------------------- +; Окончание тестов +;------------------------------------------------------- +TEST_END + ; вывод сообщения об окончании в последовательный порт + LD HL, MS4 +TE_OUT_CHAR + ; проверка конца строки + LD A, (HL) + OR A + JP Z, TE_STOP + + LD B, A + ; ждем готовности передатчика +TE_W_S1 + IN A, (DD72RR) + AND TX_RDY + JP Z, TE_W_S1 + ; передаем байт в последовательный порт + LD A, B + OUT (DD72RD),A + INC HL ; addr++ + JP TE_OUT_CHAR +TE_STOP + JP TE_STOP + +;------------------------------------------------------- +; Ожидание отпускания кнопки S1 +;------------------------------------------------------- +WAIT_BTN + ; очистить буфер приема + IN A, (DD72RD) + IN A, (DD72RD) + ; прочитать статус + IN A, (DD72RR) + ; пока нажата S1 ждем + AND RX_RDY + JP NZ, WAIT_BTN + LD BC, W_BUTN +WB_DELAY + DEC BC + LD A,B + OR C + JP NZ,WB_DELAY + ; очистить буфер приема + IN A, (DD72RD) + IN A, (DD72RD) + LD HL, 0x0000 + ; возврат к точке перехода + ADD HL, SP + JP (HL) + +;------------------------------------------------------- +; Вывод сообщения +; Inp: D - результат теста (0-PASSED, 1-ERROR AT, 2-ERROR) +; E - номер теста +; HL - адрес +;------------------------------------------------------- +MSG + LD C, E ; число гудков + ; ------ beep +MSG_RPT0 + LD B, 125 +MSG_RPT1 + LD A, D + OR A + LD A, 0x3C + JP Z, MSG_DLY1 + LD A, 120 +MSG_DLY1 + DEC A + JP NZ, MSG_DLY1 + LD A, 0x0C ; 0000 1100 [STB,BELL]=00 [TAPE3..2]=11 + OUT (DD67RC), A + LD A, D + OR A + LD A, 0x3C ; 0011 1100 [STB,BELL]=11 [TAPE3..2]=11 + JP Z, MSG_DLY2 + LD A, 120 +MSG_DLY2 + DEC A + JP NZ, MSG_DLY2 + LD A, 0x04 ; 0000 0100 [TAPE3]=1 + OUT (DD67RC), A + DEC B + JP NZ, MSG_RPT1 + ; пауза после гудка 50*200 раз + LD B, 50 +MSG_DLY3 + LD A, 200 +MSG_DLY4 + DEC A + JP NZ, MSG_DLY4 + DEC B + JP NZ, MSG_DLY3 + DEC C + JP NZ, MSG_RPT0 + ; вывод строки в последовательный порт + LD BC, MSG_TEST +MSG_SEND_CHAR + LD A, (BC) + OR A + JP Z, MSG_TEST_END + ; ждем готовности ВВ51 +MSG_W_TX_EN1 + IN A,(DD72RR) + AND TX_RDY + JP Z,MSG_W_TX_EN1 + ; передача символа + LD A,(BC) + OUT (DD72RD),A + INC BC + JP MSG_SEND_CHAR + ; Ожидание конца передачи +MSG_TEST_END + IN A, (DD72RR) + AND TX_RDY + JP Z, MSG_TEST_END + + ; номер теста в строку и передача + LD A, E + AND 0x0F + ADD A, 0x30 + OUT (DD72RD), A + + ; вывод результата теста + LD BC, MSG_PASS + LD A, D + OR A + JP Z, MSG_OUT_RES + LD BC, MSG_ERR_AT + RRA + JP C, MSG_OUT_RES + LD BC, MSG_ERR +MSG_OUT_RES + LD A, (BC) + OR A + JP Z, MSG_ERR_END + ; посимвольная отпр сообщения об ошибке +MSG_NXT_ERR + IN A,(DD72RR) + AND TX_RDY + JP Z, MSG_NXT_ERR + LD A, (BC) + OUT (DD72RD), A + INC BC + JP MSG_OUT_RES +MSG_ERR_END + ; отправка нулей в порты пользователя/FDC + XOR A + OUT (DD80RA),A + OUT (DD80RB),A + OUT (DD80RC),A + LD A, D + ; надо выводить адрес? + AND 0x01 + JP Z, MSG_EXIT + LD A, D + RRA + RRA + AND 0x01 + ; PC0 <- 0/1 - осн/доп ОЗУ + OUT (DD80RC),A + ADD A, 0x30 + LD B, A +MSG_W_TX_EN2 + IN A,(DD72RR) + AND TX_RDY + JP Z,MSG_W_TX_EN2 + + LD A, B + OUT (DD72RD), A + LD B, 4 ; addr len 4 bytes + ; вывод адреса ошибки на порт пользователя A и B + LD A, L + OUT (DD80RA),A + LD A, H + OUT (DD80RB),A + LD D, H + LD E, L + + ; вывод адреса в HEX +MSG_TO_HEX + LD A, H + RRA + RRA + RRA + RRA + AND 0x0F + ADD A, 0x90 + DAA + ADC A, 0x40 + DAA + LD C,A + ; вывод HEX символа адреса +MSG_W_TX_EN3 + IN A,(DD72RR) + AND 0x1 + JP Z, MSG_W_TX_EN3 + LD A, C + OUT (DD72RD),A + + LD C, 0x4 +MSG_A_SR4 + LD A, L + RLA + LD L, A + LD A, H + RLA + LD H, A + DEC C + JP NZ, MSG_A_SR4 + ; вывод следующего символа адреса, если не закончили + DEC B + JP NZ, MSG_TO_HEX + ; возврат назад к точке указанной в SP +MSG_EXIT + LD HL, 0x0000 + ADD HL, SP + JP (HL) + +; ------------------------------------------------------- +; Сообщения +; ------------------------------------------------------- +MSG_TEST + DB "\r\nTEST ", 0 + +MSG_PASS + DB " PASSED", 0 + +MSG_ERR + DB " ERROR!", 0 + +MSG_ERR_AT + DB " ERROR AT ADDR ", 0 + +MS4 + DB "\r\nEND", 0 + DB 0x22 + +; ------------------------------------------------------- +; Заполнение остатака ПЗУ байтами FF до 2КБ +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xE000 +FILL_SIZE EQU 2048-CODE_SIZE + + DISPLAY "Code size is: ",/A,CODE_SIZE + +FILLER + DS 2048-CODE_SIZE, 0xFF + DISPLAY "Filler size is: ",/A,FILL_SIZE + + END \ No newline at end of file