From 1f7e4e3ac9bf15278e74887e8a6957ce6ea5cbbe Mon Sep 17 00:00:00 2001 From: Roman Boykov Date: Fri, 20 Feb 2026 13:25:57 +0300 Subject: [PATCH] CP/M R7 crc32:b89a7e16 added --- CPM_v2.2_r7_b89a7e16/.vscode/extensions.json | 9 + CPM_v2.2_r7_b89a7e16/.vscode/tasks.json | 35 + .../BIN/CPM_v2.2_r7_b89a7e16.BIN | Bin 0 -> 8192 bytes CPM_v2.2_r7_b89a7e16/README.md | 12 + CPM_v2.2_r7_b89a7e16/bdos.asm | 3038 +++++++++++++++++ CPM_v2.2_r7_b89a7e16/bdos_entries.inc | 24 + CPM_v2.2_r7_b89a7e16/bios.asm | 668 ++++ CPM_v2.2_r7_b89a7e16/bios_entries.inc | 39 + CPM_v2.2_r7_b89a7e16/ccp_ram.asm | 1527 +++++++++ CPM_v2.2_r7_b89a7e16/ccp_rom.asm | 875 +++++ CPM_v2.2_r7_b89a7e16/cpm.asm | 40 + CPM_v2.2_r7_b89a7e16/cpm_fill_1.asm | 24 + CPM_v2.2_r7_b89a7e16/cpm_vars.inc | 228 ++ CPM_v2.2_r7_b89a7e16/equates.inc | 142 + CPM_v2.2_r7_b89a7e16/io.inc | 132 + CPM_v2.2_r7_b89a7e16/mon_entries.inc | 35 + CPM_v2.2_r7_b89a7e16/ram.inc | 38 + 17 files changed, 6866 insertions(+) create mode 100644 CPM_v2.2_r7_b89a7e16/.vscode/extensions.json create mode 100644 CPM_v2.2_r7_b89a7e16/.vscode/tasks.json create mode 100644 CPM_v2.2_r7_b89a7e16/BIN/CPM_v2.2_r7_b89a7e16.BIN create mode 100644 CPM_v2.2_r7_b89a7e16/README.md create mode 100644 CPM_v2.2_r7_b89a7e16/bdos.asm create mode 100644 CPM_v2.2_r7_b89a7e16/bdos_entries.inc create mode 100644 CPM_v2.2_r7_b89a7e16/bios.asm create mode 100644 CPM_v2.2_r7_b89a7e16/bios_entries.inc create mode 100644 CPM_v2.2_r7_b89a7e16/ccp_ram.asm create mode 100644 CPM_v2.2_r7_b89a7e16/ccp_rom.asm create mode 100644 CPM_v2.2_r7_b89a7e16/cpm.asm create mode 100644 CPM_v2.2_r7_b89a7e16/cpm_fill_1.asm create mode 100644 CPM_v2.2_r7_b89a7e16/cpm_vars.inc create mode 100644 CPM_v2.2_r7_b89a7e16/equates.inc create mode 100644 CPM_v2.2_r7_b89a7e16/io.inc create mode 100644 CPM_v2.2_r7_b89a7e16/mon_entries.inc create mode 100644 CPM_v2.2_r7_b89a7e16/ram.inc diff --git a/CPM_v2.2_r7_b89a7e16/.vscode/extensions.json b/CPM_v2.2_r7_b89a7e16/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/.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_r7_b89a7e16/.vscode/tasks.json b/CPM_v2.2_r7_b89a7e16/.vscode/tasks.json new file mode 100644 index 0000000..b4df061 --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/.vscode/tasks.json @@ -0,0 +1,35 @@ +{ + "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_r7_b89a7e16/BIN/CPM_v2.2_r7_b89a7e16.BIN b/CPM_v2.2_r7_b89a7e16/BIN/CPM_v2.2_r7_b89a7e16.BIN new file mode 100644 index 0000000000000000000000000000000000000000..ba16f6380be667ac88e3cb8fabbad93fee430360 GIT binary patch literal 8192 zcmeHMdvsIBnZLreY#A(DI6!O~tZTVR5GfA@Oi*k&Ha3PsK#5I4IBjr}&9g~k9^J|U zEKEYkqdnd90h^@RoV>F+Bvm3mu65OTtve{yz1G;4AF|`O+RD3BO~CEaE$#00{^rWi z?dGrDvw!rD6?pDEzWL^R&&)UE8|~zt_T!xSzx}p4@7Pzjqjviq^Nm|qn>TFPyyYfy z^^V#ddn#`?*VWY5RMu_XZZ>oOXTK~T$2AxikqjpFd9+P49@n2zf3mf65{jPKKGlbpY-^Qd1x9iFv7s>y6q7NP z&+^d^d{k;x-fN|kt>me8l(ewbm~GPgz#U!P%4hnvS(*ezZ1vFft*#7mw$+7c#n1U< z<6?jaVWWpnhXJ&vANvVHfn5Z^@AUqg950 ztuOdIs#z&+6Y!@A+Cp;M92F2bMpKj(3SreeW@rnm7_Q--QAW?yls|JbB5$^)2FFxrSon1 zY~Z{G{~3cH*Wkk%{6)Y|r{K>6-crhCNWTRY&-voI#hk^XR!D~S^c+do-XzE+)jR5H z>MG59_T5oaUwL;;UCnOuUG+6}mzP6SkMnu$M$WD3`%S%meR^S+xr^|d&26B|T4|@xcGe$s&*s`L_@NPdrcaPwY6ldPZhz~i zl@PSU!KA&V6)L2ot>|U2``gYL+Uytog7i)&UE9tF+Z&d11qB|PZYS^g>S`*h%{6s(&N@zak#+2Lnz!w^y@mr$4WXfp@AX3r zOz}5&SYqqd>;XgD-lo|Z$87Wp&5^#h4{Wy1AoC)~9)vTA-S$ z4jEQ{0=o9dUK{l@hGxBs@VldhQ2N_vd{Y@cPCufH0jspA%HD-uv)HEbgB)~ z;m)Kc0kC+C#$<^g5}K%ji9lPzgvt58iqDPD9>vDMMZ%+XwP&yM;% z^&A*f^I(4s_Fg{>5*#mUCC~fmrB@^6cxSv^I_L*UkSpj%iE%{`tzsUwY!kdR#{HC0a zId_Uo1a(b^x+DG+-&OF;+ntrw+*aqV)cW8tW(E-*UW{Trtr(*cfx1GABU60Rbuj}?SP0KqPSYzQfw4A+$MT$M?_nb$ZsA_xYgDDJNeqM z7J~L$?zRH{d|E85yeRi7OJljQA?0)Bcgi1>xU4FbL)(V3h8l(*9eQ%;yF;%Gy*2dy zkawtaC^+=^$)Nwe8*E~&czL38~>fAj6eLnZYOJ%LKew#S}$$XvJ1 zS(2c#Y_AkQt+ag_)`?*{C`O%9Ty8Ar z<`;|V@$g2~+`kFCv0KfO-0~(_&F(JcWH*)_cu~G&+)ua5bHBgM=|(i#E*s5g+82wn znbH|qrXR45B{5?bL1DoV<7ctRXNz6^8YuD0#vJq;v&nWjTP+Evg%~SAGRWX;*&>v* z+mg_8p#)f+JsuCLIZ9AbvssZPEmv@~xFBaRSc6CoDC%+P`c$7MV-0yW!_d$tH}y9VO&Ci=Eab}Q;K>2ZaAEXN6~VX5@4 z5<(*wPlT}G3yNiT29O??<3ZA-B$x=5IT;AffC|j#4dN#0Wd+MwT_3s?}!|GH?*W%c{SW4uc&ofnk*dP+IRydcwSV_vIMbXi>1J6S2y8)C%ML#;6( z%p1Zar^k9yG%lhaMv9M%#ivC6IFz*wu*|!hBHQNOKWchq;FF$&_3Pr%(+x*n-5Xy+ z-qV@|V*@?z(@n49lZ;!)Cq41da3X1aR4l08MOVe5KycV{C<<)&1l>P!Hoa17{ic|t z>thfBALH*8ciWpqp$L0>q}Gvj#%Vt$<|suGVFg(pX^~+$raZ2|V0S?~8zWC%&&dWR zCzV4geZS}g<~Q0KH3;BY!WC%va=C&eZ$g6} zjCgF#V$x$fD$>Uy_6G4?+cA-K+Z#o7k@QBy%TSzfCGAH=mkiY$6N(;bE@~t{jTl?3 z^`iF_&LQ0qHnN(8BO#_TUdPd&M}qG5#-_GMWNWRc>G8uz^Q=zhYnfe@Ah<+%mUe{!-%l?@ zWY|OlrEgK3;*a)_e_&Pmw>|t$k-SS`WuTdq#K3RTx>Gc^kflBRN!UeKcbUx3&ljJB z0v^EFNzsBjmKvC#BuMQi#o07{#XuuNO|x{DWgJ4HfmqEkSY%Ya7#G-z@P(yTJ zZy%Pk9};2JZb8}BSEyDf6@5*JlofsIl0v1l@67CR>0&Rb=nL(pN^cPLd`o%`S>C5f z8bt9=iZCHB1Qn!?c@RZcP~#Fh-m5W_NwGj_0wjR)=P3oMNBZvA2Z{Z-Z}D&lqAnWn z_64Jm*3r{PUL6qn@%ObB0nL@(w0^{~1&SYwcrGP?HVG_7^Dskx zD~*=4bgf*2O`}@0ofK`)i14d! z_|0rf_tiyoc?crZz?Lu=^E8_}+wTzyrPumhg7kL3XK9!8i+v9UmW#t zk}+a5d0jwci1Im1Qcg;{`^c@-3k>w3ekM80)-B--p0e2@m$EHd3tM}ge?oj$xid-{ zqq;?$VWVd@S8+$L@Fg`y?cWm1&_n;3VL*i(iqh-)sVCxPmXXne6&yNSqX%TqB1CDX1n5Hx_V$CKa>D?< zfgUF72H+D01N_vuh>?RpvWrFN@^Z;D5DdAsZvx?Iugh}HZ=C~(Ue*)X(SR`oT7!sb z*cXf(c7)n@IxW(@191#dggFd`FdDqV`so4JM&qKK9O=!0OUzD=yXJ5fAp>kX(BVr@ zM<5oc)FVj2fpUnal!F|+kYWh5CwU(6dG`1v&kBoY1tOa#b^WfLvpjjz*VgK#-GjUt z8fF&kJ|krh@`&e6aBHP%Ffcoj+Dx!5p*tRIhNVNT2wy=FQ3+L%0ec+G(| z2bn{^HyGFUS2KfQqBv+dfa8XB$CWJ*m!Yrp#3R`~%|)Z=-BrzY!W|-cxYv#Yf2G(J zc9CBV!XBI0iP1A#RE~>)x@zE-A?%?xx9Gt&V0OkuvxX3m0ny=-9mQau-4$v@VDj23 zMcg}!cQWk)72QPXHACc@epWuzZ{(6lwXC4$XNM+&7%!{ z&90}Vt(a~btDe_aIDd#iYZm!T}$!D-1$8@-Am&6>z#&LXFtXRj{knz-u z#d^+b+Z3>E4%pKhgfCEC7>m1jxUPc+qsDii7U!BaS>q@l65SEdl$wfNe{_zCK{H2ke_c z+RR9KYLLGr&?U``0D#|;s;v+3cCEJ2qn3<)HUe|p5-7uTFBa0)*opXa_$rZvTe}FvIXoL1Gbw3_A(6o zaMWunWmsp^Smr79GjX42oEuy0-kNUeU7uGi|kZ`6Ks;F-q#c3WUC&wXOw5Ri3Q`t(JH%#1W#rkx2~i*f4- zjIJzQzw(wvD>vFU-m=rY^^SGB%s1X`TWed5$BNt6ZeC}r&dDy_xYJx$xeL!2^*hZs zR@ztRWN)}-{Z8|?YV(a%taUYmQV}o)rUL&IxYAJ(xGs?KvoHRlebdjD{Vb15%j7D! zBCdtY0&EGFo~vs`M+=vSX8M)s|Nqq_aP+S$@};Q3&b6iZRdGX`NY{?@_l*BJv5toT zGpq+$y1jKf_S7IlxyQ^GjXGU3tT>;CxB_%IUE zQDyrCd1L%)Qk=brAp>59gkumVixb?vdklJ=XQ>k&VaaEy?XkXb?^0As#-_&Eb3)pr z^$iXir6B$LxIT@j7>2JK0yuQb)f+eli~AzcWF9;_(MiCgXGE z(mNB&)Aih{^yU?f$5uSzUg5k>`uPN2SUz2^u9q%MWTZ=T6GzIG&nNJTI5*)~uh%j8 zER#YLa`V$SG#-252Heptcq-HKM9U^wo+xV)FEx;^<3U;x6Qnz*R%Kx?qg!HXx^m}~ zpp;EsCtW{DE>1S#n121FTR`EpeUH7mrJm#p1*KsU+Z5R|iR-NP1^}h&7vo^6X(vN^ zV)Cje!7d&LUY_Kio`U@NnBP%?6p_XC<@7#80Xl|LgxEmHr<3;2{kW1|p2QW>H|c&| zomYyd+yO~H)g+=|O23-^mtLOACqJF!zsZ&%%cp|;K~{raNFEyBS!nRzWfhjOzo0!0 zWKAwS7$`4Kv8RHp$!I7wiifP@NP#{$rLIK5AJylI)+_yS;Kx zMJ89awcgB?)!%8xofY1-)5=+E?y)i>aAa~Uq(eUza)ku)C&APqlFPoo@SX0d1>f;c zG2e+yrF`elF7wN&l*EW`oj>(fm4&gs&%7QA*Ax&-Bk zX>{B_4NmYxY~os&IfbY3A1WQDZ%&mfA5Pn2BIK!)UY*9NX#qfLMTOA{(0p=YI+`qQ k;gY6#q(|6nI9~5mlu7rLUpJA#PDMS_q%79|KM1t{3A*&@Qvd(} literal 0 HcmV?d00001 diff --git a/CPM_v2.2_r7_b89a7e16/README.md b/CPM_v2.2_r7_b89a7e16/README.md new file mode 100644 index 0000000..9b91a8c --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/README.md @@ -0,0 +1,12 @@ +# Ocean-240.2 ROM Sources CP/M (V2.2) REL.7 checksum b89a7e16 + +Source codes of CP/M (V2.2) REL.7 version for Ocean-240.2 with Floppy controller. +In Z80 mnemonics, but limited for i8080 instruction set. + +* 64k 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_r7_b89a7e16/bdos.asm b/CPM_v2.2_r7_b89a7e16/bdos.asm new file mode 100644 index 0000000..ed7bb17 --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/bdos.asm @@ -0,0 +1,3038 @@ +; ======================================================= +; 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 BDOS_CTLC + 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 ASCII_SP + 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 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, ASCII_SP + 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 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," |" + +FILLER + DS FILL_SIZE, 0xFF + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r7_b89a7e16/bdos_entries.inc b/CPM_v2.2_r7_b89a7e16/bdos_entries.inc new file mode 100644 index 0000000..8ce3f92 --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/bdos_entries.inc @@ -0,0 +1,24 @@ +; ======================================================= +; Ocean-240.2 +; CP/M BDOS Entries to build other depended modules +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _BDOS + DEFINE _BDOS + + MODULE BDOS + +bdos_enter EQU 0xba06 +bdos_no_outflag EQU 0xba09 +bdos_strtcol EQU 0xba0a +bdos_column EQU 0xba0b +bdos_prnflag EQU 0xba0c +bdos_kbchar EQU 0xba0d + +bdos_entrance EQU 0xc811 + + ENDMODULE + + ENDIF diff --git a/CPM_v2.2_r7_b89a7e16/bios.asm b/CPM_v2.2_r7_b89a7e16/bios.asm new file mode 100644 index 0000000..bcc655d --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/bios.asm @@ -0,0 +1,668 @@ +; ======================================================= +; 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.mon_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 + +; ------------------------------------------------------- +; 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 + + ; 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.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 + +bios_signon: + LD SP, bios_stack + ; Print CP/M hello message + LD HL, msg_hello + CALL print_strz + +; ------------------------------------------------------- +; BIOS Warm start entry +; ------------------------------------------------------- +bios_wboot: + 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_A + LD BC, DPB_END-DPH_A ; 78 +.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 + + 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 + +list_st: + XOR A + RET + +; ------------------------------------------------------- +; Select disk +; Inp: C - disk, +; E - active drive flag +; Out: HL -> DPH +; ------------------------------------------------------- +sel_disk: + LD A, C + 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.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.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, 0xA4 ; VG93 CMD + CALL MON.mon_write_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +slicer_read_buffer: + CALL slicer_get_floppy_args + LD C, 0x84 ; VG93 CMD + CALL MON.mon_read_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +slicer_get_floppy_args: + LD HL, sector_128_interleave_b ; = 1h + 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 + +sector_128_interleave_b: + DB 1, 8, 6, 4, 2, 9, 7, 5, 3 + +; ------------------------------------------------------- +; 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, "8", 2 ; Выбор палтитры 82 + DB ASCII_ESC, "42" ; Выбор цвета 42 + DB "48K CP/M (V2.2) REL.7/2D\r\n64K RAM DISK (A:)\r\n180K FD (B:)\r\n", 0 + +; -------------------------------------------------- +; Disk parameters headers in ROM +; -------------------------------------------------- +; Disk A RAM +DPH_A: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.DIRBUFF ; Directory buffer pointer + DW CPM_VARS.DPB64K_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_B: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.DIRBUFF ; Directory buffer pointer + DW CPM_VARS.DPB360K_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_C: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.DIRBUFF ; Directory buffer pointer + DW CPM_VARS.DPB360K_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_C ; Check Vector pointer + DW CPM_VARS.AL_MAP_C ; Allocation map pointer + +; -------------------------------------------------- +; 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_64K: + 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) + +; For FLOPPY 360k +DPB_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 00B3h ; DSM Disk size blocks - 1 (179d) + DW 003Fh ; DRM Directory entries - 1 (63d) + DB 10000000b ; AL0 Dir map byte 1 + DB 00000000b ; AL1 Dir map byte 2 + DW 0010h ; CKS checksum vector size (16 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +DPB_END: + DB 4h ; reserved + + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xD600 +FILL_SIZE EQU 0x400-CODE_SIZE + + DISPLAY "| BIOS\t| ",/H,boot_f," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + +FILLER + DS FILL_SIZE, 0x00 + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r7_b89a7e16/bios_entries.inc b/CPM_v2.2_r7_b89a7e16/bios_entries.inc new file mode 100644 index 0000000..44de99a --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/bios_entries.inc @@ -0,0 +1,39 @@ + +; ======================================================= +; 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 + + ENDMODULE + + + ENDIF diff --git a/CPM_v2.2_r7_b89a7e16/ccp_ram.asm b/CPM_v2.2_r7_b89a7e16/ccp_ram.asm new file mode 100644 index 0000000..53e987d --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/ccp_ram.asm @@ -0,0 +1,1527 @@ +; ====================================================== +; Ocean-240.2 +; CP/M 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 +; change drive flag, 0 - no change +ccp_chg_drive: + DB 0 +ccp_bytes_ctr: + DW 0 + ; reserved + DS 13, 0 + +; head of BDOS, copyed at start form ROM to RAM +; LD SP,HL +; LD D,0 +; NOP +; NOP +; LD L,E +; JP BDOS.bdos_entrance + +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 |" + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r7_b89a7e16/ccp_rom.asm b/CPM_v2.2_r7_b89a7e16/ccp_rom.asm new file mode 100644 index 0000000..ae420c3 --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/ccp_rom.asm @@ -0,0 +1,875 @@ +; ======================================================= +; 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 "DIR " + DB "READ " + DB "WRITE" + DB "EXIT " + +; ------------------------------------------------------- +; 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_COMMANDS_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_exit1 + DW ccp_ret + +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_dsk_a3 + LD DE, DEF_DISK_A_SIZE ; 63 blk + JP .calc_remanis_ds +.no_dsk_a3: + LD DE, DEF_DISK_B_SIZE ; 360 blk * 2k = 720k + + ; Disk size - Dir size = Free +.calc_remanis_ds: + 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 +.delay: + DEC BC + LD A, B + OR C + JP NZ, .delay + JP BIOS.tape_write_f + RET NC + +; ------------------------------------------------------- +; 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," |" + +FILLER + DS FILL_SIZE, 0xFF + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r7_b89a7e16/cpm.asm b/CPM_v2.2_r7_b89a7e16/cpm.asm new file mode 100644 index 0000000..ad9837a --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/cpm.asm @@ -0,0 +1,40 @@ +; ====================================================== +; 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 + + INCLUDE "ccp_ram.asm" + INCLUDE "bdos.asm" + INCLUDE "bios.asm" + INCLUDE "cpm_fill_1.asm" + INCLUDE "ccp_rom.asm" + + OUTEND + + INCLUDE "cpm_vars.inc" + +END diff --git a/CPM_v2.2_r7_b89a7e16/cpm_fill_1.asm b/CPM_v2.2_r7_b89a7e16/cpm_fill_1.asm new file mode 100644 index 0000000..5c8fa1a --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/cpm_fill_1.asm @@ -0,0 +1,24 @@ +; ======================================================= +; Ocean-240.2 +; +; CPM filler, between BIOS and CCP_ROM +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF BUILD_ROM + OUTPUT ccp_fill_1.bin + ENDIF + + MODULE CPM_FILL_1 + ORG 0xda00 + +FILL1 DS 107, 0 +FILL2 DB 0x7b +FILL3 DS 148, 0xff + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r7_b89a7e16/cpm_vars.inc b/CPM_v2.2_r7_b89a7e16/cpm_vars.inc new file mode 100644 index 0000000..e942007 --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/cpm_vars.inc @@ -0,0 +1,228 @@ +; ======================================================= +; Ocean-240.2 +; Module CPM - 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 DS 1 +bdos_strtcol DS 1 +bdos_column DS 1 + +; Copy output to printer in non zero +bdos_prnflag DS 1 +bdos_kbchar DS 1 + +; Stored user stack pointer on enter bdos fn +bdos_usersp DS 2 ; 0xba0e + +; BDOS Stack + DS 48 +bdos_stack EQU $ + +; ------------------------------------------------------- +; Common values shared between bdosi and bdos +; ------------------------------------------------------- + +; current user number 0..15 +bdos_userno DS 1 ; 0xba40 + +; current disk number +bdos_curdsk DS 1 + +; reg DE (pointer) value to call BDOS fn +bdos_info DS 2 ; 0xba42 + +; Address value to return +bdos_aret DS 2 ; 0xba44 +; Empty FCB +bdos_efcb DS 1 ; 0xba46 = 0xE5 at init +; Read-only disks flag +bdos_rodsk DS 2 ; 0xba47 +; Drive login vector +bdos_dlog DS 2 ; 0xba49 +; Address of DMA buffer +bdos_dmaad DS 2 ; 0xba4b = 0x80 at init +bdos_cdrmax DS 2 ; 0xba4d +; Current track +bdos_curtrk DS 2 ; 0xba4f +; Current record (sector) +bdos_currec DS 2 ; 0xba51 +; Address of user buffer +bdos_buffa DS 2 ; 0xba53 + +; Address of cur disk DPB +bdos_dpbaddr DS 2 ; 0xba55 +; Calculated checksum +bdos_cksum DS 2 ; 0xba57 +; Address of cur disk allocation map +bdos_alloca DS 2 ; 0xba59 +; Sectors per track +dbos_sectpt DS 2 ; 0xba5b +bdos_blkshf DS 1 ; 0xba5d +bdos_blmsk DS 1 ; 0xba5e +bdos_extmsk DS 1 ; 0xba5f +; Max allocation number (disk size) +bdos_maxall DS 2 ; 0xba60 +bdos_dirmax DS 2 ; 0xba62 +; Size of directory +bdos_dirblk DS 2 ; 0xba64 +bdos_alloc1 DS 2 ; 0xba66 +; First track offset +bdos_offset DS 2 ; 0xba68 +; Disk traslation vector address +bdos_tranv DS 2 ; 0xba6a +; Open and closed flag 0xff - closed +bdos_fcb_closed DS 1 ; 0xba6c +; Read file modified flag. 0xff - not allow read unwritten +bdos_rfm DS 1 ; 0xba6d +bdos_dirloc DS 1 ; 0xba6e +; File access mode (0=random, 1=sequential, 2=special) +bdos_seqio DS 1 ; 0xba6f +; Reg E value to call BDOS fn +bdos_linfo DS 1 ; 0xba70 +bdos_dminx DS 1 ; 0xba71 +; Length in bytes to match when search +bdos_searchl DS 1 ; 0xba72 +; Address of buffer where to search +bdos_searcha DS 2 ; 0xba73 + DS 2 +; 0 - big disk (>255 blk), 0xff - small disk +bdos_small_disk DS 1 ; 0xba77 +; Drive selected (0xff) by user specified FCB flag +bdos_resel DS 1 ; 0xba78 +bdos_olddsk DS 1 ; 0xba79 +; Saved disk of user FCB +bdos_fcbdsk DS 1 ; 0xba7a +; Number of records in extent +bdos_rcount DS 1 ; 0xba7b +bdos_extval DS 1 ; 0xba7c +; Record number to read +bdos_vrecord DS 2 ; 0xba7d +; Record to access +bdos_arecord DS 2 ; 0xba7f +bdos_arecord1 DS 2 ; 0xba81 +bdos_dptr DS 1 ; 0xba83 +bdos_dcnt DS 2 ; 0xba84 +;bdos_dcnt_hi DS 1 ; 0xba85 +bdos_drec DS 20 ; 0xba86 +tmp_dir_total DS 2 ; 0xba9a +; Save pointer at ccp entry, restore on exit +saved_stack_ptr DS 2 ; 0xba9c + DS 22 +ccp_safe_stack + DS 2 ; 0xbab4 + +; -------------------------------------------------- +; FCB +; -------------------------------------------------- +ccp_FCB +ccp_FCB_DR DS 1 ; 0xbab6 drive letter to write file +ccp_FCB_FN DS 8 ; 0xbab7 file name +ccp_FCB_FT DS 3 ; file type +cpp_FCB_EXT DS 1 ; extent +cpp_FCB_S1 DS 1 +cpp_FCB_S2 DS 1 ; extent hi bits +cpp_FCB_RC DS 1 +cpp_FCB_AL DS 16 ; Second half of FCB +ccp_FCB_CR DS 1 ; Current record +ccp_FCB_Rn DS 1 ; Random access record number + DS 2 + DS 2 + DS 2 + +ccp_vars_end EQU $ + +; -------------------------------------------------- +; Disk parameters headers/blocks in RAM +; -------------------------------------------------- +DPH_RAM DS 16, 0 ; 0xbade +DPH1_RAM DS 16, 0 +DPH2_RAM DS 16, 0 +DPB64K_RAM DS 15, 0 ; 0xbb0e +DPB360K_RAM DS 15, 0 ; 0xbb1d + +; -------------------------------------------------- +cur_disk DS 1 ; 0xbb2c +dma_addr DS 2 ; 0xbb2d +curr_track DS 1 ; 0xbb2f +curr_sec DS 1 ; 0xbb30 +slicer_disk DS 1 ; 0xbb31 +slicer_track DS 1 ; 0xbb32 +slicer_real_sector + DS 1 ; 0xbb33 +tmp_slicer_real_sector + DS 1 ; 0xbb34 +slicer_has_data DS 1 ; 0xbb35 +slicer_need_save + DS 1 ; 0xbb36 +slicer_uninited_count + DS 1 ; 0xbb37 +slicer_uninited_disk + DS 1 ; 0xbb38 +slicer_uninited_track + DS 1 ; 0xbb39 +slicer_uninited_sector_128 + DS 1 ; 0xbb3a +tmp_slicer_result + DS 1 ; 0xbb3b +tmp_slicer_can_read + DS 1 ; 0xbb3c +tmp_slicer_operation + DS 1 ; 0xbb3d +tmp_slicer_flush + DS 1 ; 0xbb3e + +; The directory buffer is an area used by the BDOS for directory operations. +; There is only one of these for the entire system. +DIRBUFF DS DIR_BUFF_SIZE ; 0xbb3f + +; Allocation map for drive A (Mark used allocation blocks) +AL_MAP_A DS 31 ; 248 bit +; Check vector for drive A (Checksumm of dir entries, used to detect disk change) +CHK_VEC_A DS 16 ; 0xbbde + +; Allocation map for drive B +AL_MAP_B DS 23 ; 184 bit +; Check vector for drive B +CHK_VEC_B DS 16 ; 0xbc05 + +; Allocation map for drive C +AL_MAP_C DS 23 ; 184 bit +; Check vector for drive C +CHK_VEC_C DS 16 ; 0xbc2c + + DS 196 + +slicer_buffer DS 512 ; 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 slicer_uninited_count = 0xbb37 + ASSERT slicer_buffer = 0xbd00 + + DISPLAY "| CCP_VARS\t| ",/H,cpm_vars_start," | ",/H,cpm_vars_size," |\t |" + + ENDMODULE + + ENDIF diff --git a/CPM_v2.2_r7_b89a7e16/equates.inc b/CPM_v2.2_r7_b89a7e16/equates.inc new file mode 100644 index 0000000..d592f86 --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/equates.inc @@ -0,0 +1,142 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 + +ASCII_BS EQU 0x08 +ASCII_TAB EQU 0x09 +ASCII_LF EQU 0x0A +ASCII_FF EQU 0x0C +ASCII_CR EQU 0x0D +ASCII_CAN EQU 0x18 +ASCII_EM EQU 0x19 +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B +ASCII_US EQU 0x1F +ASCII_SP EQU 0x20 +; ------------------------------------------------------ +BDOS_CTLC EQU 0x03 +BDOS_NFUNCS EQU 0x29 +BELL_CHAR EQU 0x07 +BELL_PIN EQU 0x08 +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 0x05 +CCP_COMMANDS_COUNT EQU 0x04 +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 +CCP_DST_ADDR EQU 0xb200 +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 +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 +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 +; ------------------------------------------------------ +KBD_ACK EQU 0x10 +KBD_IRQ EQU 0x02 +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 +DIR_BUFF_SIZE EQU 128 +RO_FILE EQU 0x09 +DEL_KEY EQU 0x7F +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 +; ------------------------------------------------------ +;U_BYTES EQU 0x0D + + ENDIF diff --git a/CPM_v2.2_r7_b89a7e16/io.inc b/CPM_v2.2_r7_b89a7e16/io.inc new file mode 100644 index 0000000..2a401a7 --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/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 diff --git a/CPM_v2.2_r7_b89a7e16/mon_entries.inc b/CPM_v2.2_r7_b89a7e16/mon_entries.inc new file mode 100644 index 0000000..9f4522d --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/mon_entries.inc @@ -0,0 +1,35 @@ +; ====================================================== +; Ocean-240.2 +; +; Monitor entries to compile CP/M modules +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _MON_ENTRY + DEFINE _MON_ENTRY + + MODULE MON + +mon_init EQU 0xe000 +mon_cold_start EQU 0xe003 +mon_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 +mon_ram_disk_read EQU 0xe01e +mon_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 +mon_read_floppy EQU 0xe030 +mon_write_floppy EQU 0xe033 + + ENDMODULE + + ENDIF diff --git a/CPM_v2.2_r7_b89a7e16/ram.inc b/CPM_v2.2_r7_b89a7e16/ram.inc new file mode 100644 index 0000000..54b1e32 --- /dev/null +++ b/CPM_v2.2_r7_b89a7e16/ram.inc @@ -0,0 +1,38 @@ +; ======================================================= +; 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 + +@bios_var1 EQU 0x0041 +@bios_var2 EQU 0x0042 +@bios_var3 EQU 0x0043 + +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c + +@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