IMD 1.16: 31/05/2007 20:37:11 FOGCPM.145 --FOGCPM145ALERT COMALLOC COMBMAP7-11COMBMAP7-11ASMM BMAP COMCLS-ANY ASM CLS DOCCLS-^Z COMCPMADR COM+CPMCALC COM* !"#$GKEY2 COM %&GKEY2 DOC#'()*+GK2SET COM,-.GSUB14 COM/0GSUB DOCP123456789:ELSEG COM;ENDIFG COM<GOTOG COM=IFG COM>IFG DOC?@ABIF COMCIF ASMHDEFGHIJKLIFSKIP DOCMNOPQRSTUVWXYZ[\IFSKIP DOC]-09-00 86 PRLMOV ASM ^_`aRAMDSK COMbcdRAMDSK DOCefghRAMDSK ASMijklmnopqrstuvwxRAMDSK ASM>yz{|}~-CPM145 DOCSAFRAM2 COMSAFRAM ASMSAFRAM ASM SAFRAM DOC8SAFRAM DSCSKIP COMSKIP ASMTPA COMWAIT COMWAIT DOCThis is the disk name. !9"1 U!x> #P=!+|E*ALERT (C) E.Gans 06/30/85 << Type any character >>X$Yd ALLOC 4/10/82 by Ward C. ALLOC show bit map. ALLOC B: show bit map for drive B. ALLOC FOO.ASM show bit map, with FOO.ASM as "*". ALLOC 22 zap (temporarily write-protect) 22K of map. ALLOC - 22 zap "all but" 22K. ALLOC - FOO.ASM zap "all but" nnK, where nn = FOO.ASM size. Notes: 1) Typing ^C restores the original map. 2) If interrupted, ALLOC skips bit map and prints just totals. 3) Drive name prefix is ok: "ALLOC - B:FOO.ASM" 4) "- " may precede or follow the operand: "ALLOC 22 -" $!9"c 1c :m-ʅ:^ ‡:]H-‡x2X !""! """"! 0"""">22&:\=_ }*6~##^#F##~###^#V###F#N2 #":] 6ͽ7:6>2Go>2*"Gd *&"*z"*:/_B>0*#"ʙ ~™>1:<22zʹͻÀɷ#*yoxg!":PY)*)":'2="!z!~6 &0w0#w*!**{_zW:/o&#!`iʹ#͹ʹʹʹ͹ʹʹy/Ox/G {_{Z!6 #6 ! ~#j ¦ʗG(® 5×){_   K/bit; 0K dir. + 0K files + 0K free = 0K total. ??% full `i) y2O:G5G +x2!5:O:>D5:<2xͻG Hx *c :)=r:ʉ=}:: ++ K to "zap" rounded down to whole blocks ++ $|g}o:2:*DM>x wz# \!l~# !e p#z.*+|&.H&."\>?2h>2Ô*DM=o&)))))^#x=}V#zʔ!e 6**#" s:O>2<[:ʳ!"*o"!]  "0:?T])))0o$::> 5> 52z~0D5:<2xͻG Hx *c :)=r:ʉ=}:: ++ K to "zap" rounded down to whole blocks ++ $|g}o:2:*DM>x wz# \!l~# !e p#z.*+|&.H&."\>?2h>2Ô*DM=o&)))))^#x=}V#zʔ!e 6**#" s:O>2<[:ʳ!"*o"!]  "0:?T])))0o$::> 5> 52z~01O>0O*#"&o ڊ |ā{0O? Nonstandard disk parameter block error $*=!:\2?BITMAP 2.2 AS OF 7/11/80 $2:=2:_"^#V#"*##~ңڣ?Allocated disk block size is $2!)=ú? bytes per block $*:_+0^#~emz ʧʬð^^:AO?: R/W, Space: $:*=)́?k $; ; BITMAP for CP/M 2.0+ as of 7/11/80 ; ; ; Lauren Guimont ; 14211 8th Avenue South ; Seattle, Washington 98168 ; ; ; ;The bitmap idea is based upon Ward Christensen's original ; bitmap program, which refused to run on 2.0+ systems. ; After giving his program a quick going over with SID, I ; decided it would be easier to rewrite it than to try and ; patch it for 2.0, 2.1, 2.2. ; ; ; ***** EQUATES ***** ; base equ 0 ; 'normal' CP/M bdos equ base+5 ; jump to bdos ochar equ 2 ; bdos console output sdsk equ 14 ; select disk curdsk equ 25 ; current disk gtaloc equ 27 ; get allocation address dskpar equ 31 ; get disk parameters fcb equ base+5ch ; file control block ; ; ; org base+100h ; start of TPA ; lxi h,0 ; clear HL dad sp  ; load HL with CCP sp shld oldsp ; save it for later lxi sp,stack ; initialize our own sp jmp start ; bypass some subroutines ds 48 ; stack space stack equ $ ; our own stack oldsp ds 2 ; old stack from ccp ; inlprt: ; in line print xthl ; HL to stack...pointer to HL inlprt1 mov a,m ; get a character inx h ; increment the pointer cpi '$' ; endmark? jz inlprt2 ; if so, prepare to exit call conout ; output to console jmp inlprt1 ; go get another inlprt2 xthl ; orig HL...sp at end of msg ret ; return to end of msg ; conout push h ; single character console push d ; ...output; 1st save all  push b ; ...the registers push psw mvi c,ochar ; tell bdos mov e,a ; bdos wants it in E call bdos ; let bdos do it pop psw ; reinstate all registers pop b pop d pop h ret ; return to caller ; crlf call inlprt ; use in line print db 0dh,0ah,'$' ; ...for cr & lf ret ; return to caller ; one push psw ; save Acc mvi a,'1' ; print a '1' to console call conout ; do it pop psw ; restore Acc ret ; return to caller ; zero push psw ; save Acc mvi a,'0' ; print a '0' to console call conout ; do it push h ; save lhld free ; get nb of free blocks inx h ; add one free shld free ; store total free count pop h pop psw ; restore Acc ret ; return to caller ; ;Binary to decimal output routine. Enter with 8 bit binary ;number in . Second entry at BNDEC2 assumes 16 bit nb. in ; bndec1 mvi h,0 mov l,a ; now has number ; bndec2 push b push d push h lxi b,-10 lxi d,-1 bndc dad b inx d jc bndc lxi b,10 dad b xchg mov a,h ora l cnz bndec2 mov a,e adi '0' call conout pop h pop d pop b ret ; err1 call inlprt ; in line print db 0dh,0ah,'Nonstandard disk ' db 'parameter block error' db 0dh,0ah,'$' ; finis lhld oldsp ; get CCP sp sphl ; retore it ret ; direct return to CCP ; ;We need a little internal storage ; drive ds 1 ; current drive aldrv ds 1 ; alternate specified drv dpb ds 2 ; disk parameter block add tbtr ds 2 ; total bits to read alloc ds 2 ; allocation address blksiz ds 1 ; block size code free dw 0 ; count of free blocks ; ;The actual start of it all ; start lda fcb ; get any alternate drv sta aldrv ; save it for later call inlprt ; in line print db 'BITMAP 2.2 AS OF ' db '7/11/80',0dh,0ah,0dh,0ah,'$' mvi c,curdsk ; get current disk in call bdos ; ...use from bdos sta drive ; save it lda  aldrv ; get any alternate drv ora a ; any specified? jz dpblk ; if not, skip next dcr a ; less one sta drive ; save as drive to use ; dpblk lda drive ; get drive to bitmap mvi c,sdsk ; set call for disk select mov e,a ; bdos wants it in E call bdos ; let bdos do it mvi c,dskpar ; we want dsk parameter blk call bdos ; get it, and..... shld dpb ; ...save it lxi d,5 ; offset for total blks used dad d ; add it to HL mov e,m ; lsb into E inx h ; point to msb mov d,m ; get it xchg ; put it in HL... inx h ; alloc size = (dsm/8)+1 shld tbtr ; ...and save it lhld dpb ; get dsk parameter blk add inx h ; ...and increment HL to inx h ; ...the 3rd byte mov a,m ; it has the block size sui 2 ; it will be 3-7 (make it 1-5) cpi 5+1 ; check for over 5 jnc err1 ; nonstandard size cpi 1 ; check for less than 1 jc err1 ; nonstandard size push psw ; save it call inlprt ; in line print db 'Allocated disk block size is $' pop psw ; get block size back sta blksiz ; save it for end lxi h,512 ; set 1/2k counter lp dad h ; multiply * 2=1024 dcr a ; less block size code count jnz lp ; loop till = 0 call bndec2 ; print size in K ; dpbend call inlprt ; finish message db ' bytes per block',0dh,0ah,'$' lhld tbtr ; total bits to read push h ; save it in the stack lda drive ; again to be safe mov e,a ; into E for bdos mvi c,sdsk ; reselect disk call bdos ; let bdos do it mvi c,gtaloc ; get the allocation address call bdos ; ...from bdos pop d ; tbtr from stack dcx h ; back allocation up one ; ; ;We now have the total number of bits to read in DE, and ; the address to start reading them at in HL for the ; proper drive. So now let's print the bitmap. ; ; bmap mvi c,48 ; 1's and 0's per line call crlf ; followed by a cr,lf bmap1 inx h ; kick the pointer mov a,m ; get the byte  mvi b,8 ; it has 8 bits bmap2 rlc ; runn'em through carry cc one ; carry set = print '1' cnc zero ; carry not set = print '0' dcx d ; decrement bit count push psw ; save the bit pattern mov a,d ; check to see if... ora e ; ...DE = 0 jz bmapend ; if so, we're finished pop psw ; restore bit pattern dcr c ; decrement line count jz bmap ; new line if zero dcr b ; decrment bit count jz bmap1 ; new byte if zero jmp bmap2 ; finish this byte ; bmapend pop psw ; not neccessary, but keeps the call crlf ; ...stack straight..send cr,lf call crlf lda drive ;get drive used  adi 'A' call conout call inlprt db ': R/W, Space: $' lda blksiz ; get block size code lhld free ; get nb of free blocks lp1 dcr a jz don ; multiplied by size of block dad h ; times 2 jmp lp1 ; don call bndec2 ; print size of free space call inlprt db 'k',0dh,0ah,'$' jmp finis ; restore things and GET OUT ; end  line if zero dcr b ; decrment bit count jz bmap1 ; new byte if zero jmp bmap2 ; finish this byte ; bmapend pop psw ; not neccessary, but keeps the call crlf ; ...stack straight..send cr,lf call crlf lda drive ;get drive used !9"=1=~#$MO@_? $>1O>0O*#"&o ڌ |ă{0O? Nonstandard disk parameter block error $*=!ͼ:\2?Bitmap 2.2 as of 12/17/81 $2:=2:_"^#V#"*##~ҥڥ?Allocated disk block size is $2!)=w̓? bytes per block $*:_+0`#~goz ʩʮò``:O{_zW {oz>O2 :AO?: R/W Space $:*=#)̓?k $!ͷ’P͌Q!ͷªP}QxQ!ͷ͓G@Q! ͷ y͓Gþ!ͷ ͓Q!ͷ;CP/M PROGRAM TO CLEAR SCREEN AND HOME CURSOR (SETUP FOR ^Z) ; ;COPYRIGHT (C) 1985 BY ERNEST SPENCER OF JACKSON, MS. ; ;*** THIS PROGRAM MAY BE USED FREELY BY THE CP/M USER COMMUNITY FOR ;NON-COMMERCIAL PURPOSES - ERNEST SPENCER - 06/06/85 *** ; ;ASSEMBLE WITH ASM.COM ; ;USE WITH ANY CP/M COMPUTER BY CHANGING THE CLEAR EQUATE TO ;THAT COMPUTER'S CLEAR SCREEN & HOME CURSOR ASCII CHARACTER CODE. ; ;RETURNS TO CP/M PROMPT WITHOUT DOING A WARM BOOT. ; ;WORKS WITH ANY SIZE CP/M SYSTEM SINCE IT INITIALLY DETERMINES ;PROMPT AREA OF CCP BY SAVING ITS ADDRESS ON STACK AND ;THEN RETURNS TO IT WHEN THROUGH INSTEAD OF DOING WARM BOOT. ; ORG 100H ;BEGINNING OF TPA FOR STANDARD CP/M ; CONOUT EQU 02H ;SYSTEM CALL TO OUTPUT A CHAR. TO CONSOLE CLEAR EQU 026 ;CLEAR SCREEN CHAR. (^Z FOR KAYPRO 4) ENTRY EQU 05H ;CP/M ENTRY POINT ; START: LXI H,0 ;ZERO HL REGISTER DAD SP ;GET OLD CP/M CCP PROMPT STACK POINTER SHLD STACK ;SAVE IT LXI SP,STACK;SET NEW STACK POINTER ; MVI E,CLEAR ;LOAD CLEAR SCREEN CHAR. INTO E REG. MVI C,CONOUT;LOAD SYSTEM CALL TO OUTPUT A CHAR. CALL ENTRY ;CALL CP/M TO OUTPUT CLEAR CODE & CLEAR SCREEN ; EXIT: LHLD STACK ;GET OLD CP/M CCP PROMPT STACK SPHL ;RESTORE OLD STACK POINTER RET ;RETURN TO CCP & DO PROMPT W/O WARM BOOT ; DS 2 ;ROOM FOR 1 LEVEL STACK PLUS OLD STACK STACK DS 2 ;OLD CP/M STACK SAVED HERE ; END CP & DO PROMPT W/O WAR ASCII CHARACTER CODE. ; ;RETURNS TO CP/M PROMPT WITHOUT DOING A WARM BOOT. ; ;WORKS WITH ANY SIZE CP/M SYSTEM SINCE IT INITIALLY DETERMINES ;PROMPT AREA OF CCP BY SAVING ITS ADDRESS ON STACK AND ;THEN RETURNS TO IT WHEN THROUGH INSTEAD OF DOING WARM BOOT. ; ORG 100H ;BEGINNING OF TPA FOR STANDARD CP/M ; CONOUT EQU 02H ;SYSTEM CALL TO OUTPUT A CHAR. TO CONSOLE CLEAR EQU 026 ;CLEAR SCREEN CHAR. (^Z FOR KAYPRO 4) ENTRY EQU 05H ;CP/M ENTRY POINT ; START: LXI H,0 ;ZERO HL REGISTER DAD SP ;GET OLD CP/M CCP PROMPT STACK POINTER SHLD STACK ;SAVE IT LXI SP,STACK;SET NEW STACK POINTER ; MVI E,CLEAR ;LOAD CLEAR SCREEN CLS.COM - A "SCREEN" DOCUMENTARY. Dear Friends: I suspect that most of us have wished that the earlier versions of CP/M had a clear screen - home cursor command. I know I have. For that matter, I also always wanted the command in early versions of Microsoft (TM) Basic. Remember though, when Gary Kildall wrote CP/M and Bill Gates wrote the first versions of Altair Basic, (for you young folks - this was in the Dark Ages - 1975 and 1976), most of the micro-computer community used as their terminals the terrible, noisy, 'leaks oil like some cars', MONSTER called a Teletype (TM) Model 33. I built an Altair (TM) 8800B, (like the one Steve Jobs and Steve Wozniak could not afford) which started this whole thing, from the kit in early 1977 and had such a terminal. If anyone ever knew how to 'home the cursor and clear the screen' on a Teletype (TM), I never heard of it here in Mississippi and I doubt somehow that anyone else did either. Therefore, please "forgive" Gary Kildall and Bill Gates for not including these commands in their early programming 'works of art'. I certainly have. Still, I have always wanted this command and it is not even present on my version of CP/M vers. 2.2G (for my Kaypro 4 84). Therefore, in an uncharacteristic 'fit' of energy recently, I came up with the enclosed 'life work epic' which must run at least 15 or 20 bytes of 'dreaded' machine language code. If you study it carefully, it is a bit more elegant than you might, at first, suspect. Not surprisingly, I call it CLS.COM. I have used this little program with great pleasure for the past few months and now I would like to share it with you. I know, because of that 'feature' of CP/M, that any program, no matter how small, takes up 2K of disk space; but I think it is worth the space - even if it's on every disk. I find that it is my third most used program, right behind SD.COM and NSWEEP.COM. I like it so much that my 'pride of authorship' and ego want me to stamp it with my name - or initials, at the very least - especially since I am sure that it is the only program I have ever written that amounts to a 'hill of beans'. Alas! If I did that, it would defeat the whole purpose of it, wouldn't it? Sometimes, "life is not always fair" as President Kennedy once reminded us. Oh, well, for what it's worth, here it is and I really do hope you enjoy it but, please... If you ever see my name again, just mumble, "I've seen that name somewhere before, but I don't remember where!" Regards, ERNEST SPENCER of Jackson, Ms., W5PQY. 06/06/85ve seen that name somewhere before, but I don't remember where!" Regards, are it with you. I know, because of that 'feature' of CP/M, that any program, no matter how small, takes up 2K of disk space; but I think it is worth the space - even if it's on every disk. I find that it is my third most used program, right behind SD.COM and NSWEEP.COM. I like it so much that my 'pride of authorship' and ego want me to stamp it with my name - or initials, !9"1*BMAP7/11ASMM BMAP COMCLS-ANY ASM CLS DOCCLS-^Z $$$!9"y4K͞+'+FÎr*y*w#w#w^#V#*~#fo^#*~#fo^#V#*n^#*n^#V# ~#fo^#& ~#fo!+!#!+!#!+!+}|z{|}|z7||7zZZ)|/g}/o#|͉k|/g}/o#ɯ2qZZk:q|/g}/o#|/g}/o#:q<2qqDM!xxGyO҃)v|͔`i|)Öxڷz/W{/_ѯzW{_=yOxGæ2qZZ͉M|}ȯ|g}o)|/g}/o#z/W{/_!9~#fo! ! ! ! ! ! !9~#A"s!`*"!"!Y">2>2>22!"!"!@"!" ʞ!F#x±~#±!b2r~# "2r+}|~#G:rx"2r+w# +6#!6#2w2x*s!>r<o&F=-/` r'~h6!+`W?_!~7z?` :>ª@w#G.¶ww#?*>?w#> w#.7:77!a{   `OE!y6$ -7rBo&))T])))!y## !2 s#r!C ͐*### !4 s#r!f ͐*#### !6 s#r! ͐*#!8 s#r! ͐* #!< s#r! ͐*  !> s#r! !A9 CP/M Data Program Version 1.0 by C. B. Mueller 12/15/82 This program will print CP/M information based on your system size and attributes of the drive that it is run on. %x = CCP Start Address %x = BDOS Start Address %x = BIOS start Address (Cold Boot) BIOS ENTRY ADDRESS %x = Warm Boot %x = Select Disk %x = Console Stat %x = Set Tra!9DM!͛`is#r͐###! s#r͐###! s#r͐###! s#r͐###! s#r͐###! s#r͐ ###! s#r͐ ###! s#r͐###! s#r͐###! s#r͐###! s#r͐###! s#r͐###! s#r͐###! s#r͐###! s#r͐###! s#r!W ! ! ͐! ͐! ͐+++!9 !_ ͐͐!s ͐͐! ͐͐! ͐͐! ͐͐! ͐͐ !8 ͐͐ !\ ͐͐! !/!$ s#r͐$|¬ !@ 6Aò !@ 6B!͐$!!͐6! s#r!@ n&! ͐ ! ͐ ͛!& s#r! ͐ ͛!( s#r!͐  ͛!* s#r!B͐ ͛!, s#r!e!͐*/!0 s#r!͐*##/!2 s#r!͐*###/!4 s#r!͐*####/!6 s#r!͐*͛#!8 s#r!3͐*/#!< s#r!V͐* /!> s#r!yT!A9 CP/M Data Program Version 1.0 by C. B. Mueller 12/15/82 This program will print CP/M information based on your system size and attributes of the drive that it is run on. %x = CCP Start Address %x = BDOS Start Address %x = BIOS start Address (Cold Boot) BIOS ENTRY ADDRESS %x = Warm Boot %x = Select Disk %x = Console Stat %x = Set Track Number %x = Console Input %x = Set Sector number %x = Console Output %x = Set DMA Address %x = List Output %x = Read Disk %x = Punch Output %x = Write Disk %x = Reader Output %x = Return List Status %x = Home Drive %x = Sector Translate DRIVE %c DISK PARAMETER HEADER TABLES Disk Parmater Header Address = %x Translation Table Address = %x Directory Buffer Address = %x Disk Parameter Block Address = %x Disk Allocation Vector = %x DISK %c PARAMETER BLOCK TABLES Logical Sectors per Track = %dD Block Shift Factor = %dD Block Mask = %x Extent Mask = %x Max Disk Blocks = %dD Max Directory Entries = %dD Directory Block Alloc = %x !9DM͐ /`is#r͐ #/! s#r͐?͐! s#r͐!9!y9DM! `i`ir!9!h9DM! ^#Vr+s~#fo! s#r͐! s#r! ^#Vr+sn`is{c`in}%M! ! s#r! 6#6! s! s! s͐n}-¢! ^#Vr+s! 4͐n}0²! 4͐n&@}! o!! s#r! ^#Vr+sn`is{.! o! s#r! 4! ^#Vr+sn`is`in&}DFUʂXʋOʔCS 7͐~#fo|҂! ^#Vr+s6-͐͐~#fos#r! ^#Vr+s! 6 Ú! 6Ú! 6! ~#fo! n&! ^#Vr+s~#fo! ͞ѯgs#rÃ! ^#Vr+s! ^#Vr+s~#fos! ^#Vr+sÃ! n}! 6#6! ^#Vr+s~#fo! s#r͐n}ʃ͐|ʃ! ^#Vr+s! ^#Vr+sns! ^#Vr+s! ^#Vr+s9͐6! ! s#r! n}! ^#Vr+s!|! ^#Vr+s! n}!0! sß͐! ^#Vr+sns{! ^#Vr+s! n}4! ^#Vr+s!|4! ^#Vr+s6 J! ^#Vr+s`ins`! ^#Vr+s`insA͐6!9!9DM͐n}ʜ! ^#Vr+sn&Wz!9DM͐͐ ͐^#Vr+s͐͐0͐7s!&7͐ ͐͐ ͉͐͞`is͐ ͐͐ )͐͞`in&#&7!9!9DM! n&|ͯk! n&|ͩ!9DM`iw#w͐~#fon&@}ʼ͐ ?͐^#Vr+snѯg`is#r}͐!9!9DM! n&|! n&! n&& !9DM! n&|ͯ+! n&|ͩ n&7**DM:!R**   k > _ zx = List Output %x = Read Disk #%x = Punch Output %x = Write Disk ,%x = Reader Output %x = Return List Status (%xx = List Output %x = Read Disk #%x = Punch Output %x = Write Disk ,%x = Reader Output %x = Return List Status (%x9͐6! ! s#r! n}! ^#Vr+s!|! ^#Vr+s! n}!0! sß͐! ^#Vr+sns{! ^#Vr+s! n}4! ^#Vr+s!|4! ^#Vr+s6 J! ^#Vr+s`ins`! ^#Vr+s`insA͐6!9!9DM͐n}ʜ! ^#Vr+sn&Wz!9DM͐͐ ͐^#Vr+s͐͐0͐7s!&7͐ ͐͐ ͉͐͞`is͐ ͐͐ )͐͞`in&#&7!9!9DM! n&|ͯk! n&|ͩ!9DM`iw#w͐~#fon&@}ʼ͐ ?͐^#Vr+snѯg`is#r}͐!9!9DM! n&|! n&! n&&*K͞+;;;'+FÎr ͖ 8{ Ăw#w#w^#V#*~#fo^#*~#fo^#V#*n^#*n^#V# ~#fo^#& ~#fo!+!#!+!#!+!+}|z{|}|z7||7zZZ)|/g}/o#|͉k|/g}/o#ɯ2qZZk:q|/g}/o#|/g}/o#:q<2qqDM!xxGyO҃)v|͔`i|)Öxڷz/W{/_ѯzW{_=yOxGæ2qZZ͉M|}ȯ|g}o)|/g}/o#z/W{/_!9~#fo! ! ! ! ! ! !9~#A"s!`*"!"!Y">2>2>22!"!"!@"!" ʞ!F#x±~#±!b2r~# "2r+}|~#G:rx"2r+w# +6#!6#2w2x*s!>r<o&F=-` r'~h6!+`W?_!~7z?` :>ª@w#G.¶ww#?*>?w#> w#.7:77!a{   `OE!y6$ -7rBo&))T])))!y8\LH]PL]MT]MB]HM\FM\PC\PO\SR\UJ\BP\CW\PA\CP\PN\OP\HE\FO\IG\PF ]OJ ]IJ ]LM ]LS ]RM ]CS ]DM ]SV ]AV ]RP ]DF ]RV ]FI ]!vL;LT%ʼ];Lw\]b`o ] ]ͣR!6]bP0]bPL͉V *v1_^h__@_1c^ `y`!6p_L`1^p__y`C_!6`^_c^W:c5}^:{…^`͛^͛^T"!{j&~!x":6W0# *7[*7[!!W_:c5_:t_>^`:+^:6!6^:6^26ͳ)1)ͥ"> ͜ `'_'_!tÏ^:6W0#w 7_||g}o&!9DM! ! !/ !? !s ! `i! " ! ! ! " |! w#w͐͐?4! s#r͐͐! ͐!! ͐!6 ͐!K ͐ !` !z ! ! ! ! !9CP/M System generation calculator Ver 1.0 C B Mueller 12-9-82 This program calculates CP/M addresses based on system memory sizes. If no additional memory is required enter 0 or CR. Exit with control C. Enter desired system memory size in K->%dEnter additional memory in K (CCS requires 2) ->%d For system size of %d K with %d K additional memory: CCP starts at %x H BDOS starts at %x H BIOS starts at %x H BIOS OFFSET LOAD = %x H When CP/M has been loaded with DDT: BOOT starts at 900 H CCP starts at 980 H BDOS starts at 1180 H BIOS starts at 1F80 H !y9DM! `iJ `iͪ!9!y9DM`i! `iA !9!h9DM! ^#Vr+s~#fo! s#r͐! s#r! ^#Vr+sn`is{ʛ`in}%…! ! s#r! 6#6! s! s! s͐n}- ! ^#Vr+s! 4͐n}0 ! 4͐n&̈́} ! ͳ !! s#r! ^#Vr+sn`is{.R ! ͳ! s#r! 4! ^#Vr+sn`is`in&}D~ Uʺ X O C SC o͐~#fo|Һ ! ^#Vr+s6-͐͐~#fos#r! ^#Vr+s! 6 ! 6 ! 6! ~#fo! n&! ^#Vr+s~#fo! ѯgs#rû ! ^#Vr+s! ^#Vr+s~#fos! ^#Vr+sû ! n}V ! 6#6! ^#Vr+s~#fo! s#r͐n}ʻ ͐|ʻ ! ^#Vr+s! ^#Vr+sns! ^#Vr+s! ^#Vr+sq ͐6! ! s#r! n}! ^#Vr+s!|! ^#Vr+s! n} !0 ! s ͐! ^#Vr+sns{;! ^#Vr+s! n}l! ^#Vr+s!|l! ^#Vr+s6 EÂ! ^#Vr+s`insØ! ^#Vr+s`insy ͐6!9!9DM͐n}! ^#Vr+sn&ò!9DM! ^#Vr+s~#fo! s#r͐! s#r! 6! ^#Vr+sn! s{! n&D|8 ! n}%v! n! wh! n&s! ^#Vr+sø! 6#6! 6 `i6! ^#Vr+sn! s{*º`i4! ^#Vr+sn! s! n&}XODU SʬC?{! 6 ! 6 ! w| ! 6#6! ^#Vr+s! w#w! n&! wͷ#|O! n&! n&! ^#Vr+sn&ͷ! s{ʞ͐ ! nѯg?! nѯg! s#rO! ^#Vr+sÅ! w͐ ~#fo! s#r! ^#Vr+sn! s{! n͐n}! ^#Vr+s`in }! ^#Vr+s! ns`in}<! 4͐6! ^#Vr+s `in}m͐n&! ^#Vr+s~#fo0! 4! ^#Vr+s ! n&`in}¸! ^#Vr+s~#fo͐ ͐?s#r! 4͐n}! n& ! n&!9!9DM͐͐ 0͐^#Vr+s͐͐0%͐7s!&{͐ ͐͐ ͉͐`is͐ ͐͐ )͐`in&#&{!9!9DM! n&|ͯگ! n&|ͩ!9DM`iw#w͐~#fon&̈́}͐ ?͐^#Vr+snѯg`is#r͐!9!9DM! n&A|7! n&>! n&&!9DM! n} ͝s! n} ͝s! n} ͝!9DM͐~#fon`isD|ʦ͐^#Vr+s`in&î!9!9DM! n&! sp|! n&s! n&̈́}! n&s!! n! n&+6!?! n&!9DM! n&|ͯl! n&|ͩ!9DM! n&͡|ͣ! n&A|ͣ!9DM! n&|ͯ! n&|ͩ !j96  #F#x~#   > _ $7*:wemory: CCP starts at %x H BDOS starts at %x H BIOS starts    > _ $7*:wemory: CCP starts at %x H BDOS starts at %x H BIOS starts Vr+s͐͐0%͐7s!&{͐ ͐͐ ͉͐`is͐ ͐͐ )͐`in&#&{!9!9DM! n&|ͯگ! n&|ͩ!9DM`iw#w͐~#fon&̈́}͐ ?͐^#Vr+snѯg`is#r͐!9!9DM! n&A|7! n&>! n&&!9DM! n} ͝s! n} ͝s! n} ͝!9DM͐~#fon`isD|ʦ͐^#Vr+s`in&î!9!9DM! n&! sp|! n&s! n&̈́}! n&s!! n! n&+6!?! n&!9DM! n&|ͯl! n&|ͩ!9DM! n&͡|ͣ! n&A|ͣ!9DM! n&|ͯ! n&|ͩ !j96  #F#x~#|\s1{ * V+"^R8h:]- +*#^#VS#^#V*s#r ^#V*.s#r1 (*# z:m$qK!/ ! :]#(- 7" (q0:|( 22F2|:\( 22B2!6#we\\<ʏ*&\*0"?R0"\* R0\mÏ!GK"e>22ge\< L\(`  7!  #~2!"\(*R|8} **|. "a""""5. ^#V"""*"2s0g.0E(+RR!""%!"*".!"""!" "V!"?"N"y!q""Q!"""d!"g"!"9"<"!DM*q#p!*.Ns#FrC""|<2!0GKEY2 v1.0 (c) Eric Gans 05/06/86 $GKEY2 already in memory.$GKEY2 removed from memory.$GKEY2 not in memory.$GK2 file not found.$File error.$GK2 file saved.$GK2 file loaded. $GKEY2 ready.$:8!": QG:x(2x\ 2|(6 G!q!0$o~(!_~(=2#~"*s1ʹ\(B|(> ʹ!q!0$o=!:_B#|0ʹ|( w\ x(B#}2B}w> { 0 ^@OGKEY2 v1.*&\*0"?R0"\* R0\mÏ!GK"e>22ge\< L\(`  7!  #~2!"\(*R|8} **|. "a""""5. ^#V"""*"2s0g.0E(+RR!""%!"*".!"""!" "V!"?"N"y!q""Q!"""d!"g"!"9"<"!DM*q#p!*.Ns#FrC""|<2!0GKEY2 v1.0 (c) Eric Gans 05/06/86 $GKEY2 already in memory.$GKEY2 removed from memory.$GKEY2 not in memory.$GK2 file not found.$File error.$GK2 file saved.$GK2 file loaded. $GKEY2 ready.$:8!": QG:x(2x\ 2|(6  Eric Gans French Dep't UCLA Los Angeles, CA 90024 GKEY2.DOC v1.0 05/06/86 After writing GKEY for the Kaypro 10, it seemed natural to adapt it for general use. Unfortunately, this involves not only some programming tricks but the sacrifice of some space in the TPA (=Transient Program Area, where programs are loaded). GKEY2 is a keyboard enhancement program that has a few decided advantages over SMARTKEY and other commercial programs. - takes less memory space, leaving more for programs - loads and responds faster - can save key files, clear current settings, or take itself out of memory without the need for an auxiliary program. - allows redefinition of escape sequences, thereby doubling the number of redefinable keys. This last feature was the deciding factor in writing the program. After some recent experience with MS-DOS, where keyboard enhancers have  so much more to work with, I was struck by the poverty of programs that are limited to the standard ASCII set, most of which cannot be redefined without great inconvenience. GKEY2 allows the redefinition of + any key, thereby adding 127 configurable combinations few of which are ever used by application software. The general structure of the program is similar to SMARTKEY, although it is much simpler (one might say, less sophisticated). To redefine a key, you use the "setup" key, originally defined as "|"; a "shift" key, originally defined as "\", allows you to use the original value of a redefined key. Unlike SMARTKEY, GKEY2 lets you abort a redefinition by typing the shift key; you don't have to re-redefine. You can also reset a key by redefining it as nothing. GKEY2 can load and save key files, clear current settings, and remove itself from memory. (You need only enter the filename; the program will supply the .GK2.) A companion file, GK2SET, can read key files and current settings, and change the setup and switch keys in GKEY2; it also includes some help information. (Unlike FIXKEY, GK2SET does not require GKEY2 to be in memory at time of use.) GKEY2 Commands: To redefine a key: [you will be prompted by "=" here] Typing the shift key at any point will abort the redefinition. To produce the original value of a redefined key: To load a key file: GKEY2 fn(.GK2) To save current settings in a file: GKEY2 fn(.GK2) $ To remove GKEY2 from memory: GKEY2 - To clear current settings (leave GKEY2 in memory): GKEY2 # + is treated as a single key in these procedures. For example, the sequence: |B(=)blurk| will define B as blurk. (The "=" is a prompt added by the program, not typed by you.) To type a straight , either use the shift key or just hit the key twice. There are 512 bytes available for definitions, enough for nearly any conceivable use. If you get past the maximum you will get a beep while trying to redefine. Any single redefinition can contain as many as 256 characters. GKEY2's redefinitions are not cumulative: if you redefine B as blurk then if you redefine 1 as BLURK you really get BLURK, not blurkLURK. Another simple but (I think) nice feature of GKEY2 is that it doesn't kill high bits used by some modified systems; it merely ignores them. (SMARTKEY won't work with the French Wordstar program my wife uses, for example.) GKEY2 doesn't tamper with the console ready (or CONSTAT) BIOS function, nor does it care about Warm Boots. It changes only the BIOS CONIN address to "translate" keys as they are typed and fixes a few addresses to protect the program in the TPA. (SMARTKEY uses the CONOUT function to check for the BDOS address after Warm Boots; GKEY2 uses the -- less often used -- SELDSK function.) Technical notes: As opposed to GKEY, GKEY2 uses a 1-byte pointer to key definitions (which must therefore start at even bytes). This permits separate redefinition addresses for all 256 possible redefinitions in only 256 bytes.s BLURK you really get BLURK, not blurkLURK. Another simple but (I think) nice feature of GKEY2 is that it doesn't kill high bits used by some modified systems; it merely ignores them. (SMARTKEY won't work with the French Wordstar program my wife uses, for example.) GKEY2 doesn't tamper with the console ready (or CONSTAT) BIOS function, nor does it care about Warm Boots. It changes only the BIOS CONIN address to "translate" keys as they are typed and fixes a few addresses to protect the program in the TPA. (SMARTKEY uses the CONOUT function to check for the BDOS address aftersU 1u   :]  3080! 0$o^#V{U *&   #  T   B !C ~͇!+ e2|\<|\! w#~2 *S \ *S "S ! ~(+>r  ^! F#~# ( S!"! ": o&)!R  ^ > (   G2\:E : #~2\#x ڰDO#]~a8{0˯#y>   k  0 ^@_͊:   (2  2|\\͊:   (2   $! ^  B !C ~ !2 \͇!; e2|\<|\qd }0[ɯR0[ !80S   GKEY2 v1.0 is a key enhancer for CP/M 2.2. It is similar to GKEY, which was written specially for the Kaypro-10, but it uses no specific addresses and loads in the TPA. Like its predecessor, GKEY2 offers several advantages ov er SMARTKEY. It occupies less space in memory, loads faster, and above all, allows ESCAPE sequences that double the number of potential key redefines. This version allows 512 bytes for redefinition sequences. To redefine a key: Type setup key (originally <|>), key to be redefined, redefinition sequence, setup key. To reset a defined key, define it as nothing. To use a redefined key (or other special key): Type shift key (originally <\>), key to display. To load a key file: GKEY2 fn.GK2 To save current settings: GKEY2 fn.GK2 $ To remove GKEY2 from memory: GKEY2 - To clear current settings: GKEY2 # Escape sequences: + any key can be redefined just as if it were a single key: entering |Bblurk| will redefine B as blurk.$ ... Type any key$ GK2SET v1.0 (c) Eric Gans 05/06/86$ 0 = Quit GK2SET 1 = Display GK2 file 2 = Display current settings 3 = Change setup key 4 = Change shift key 5 = GKEY2 information Your choice: $GKEY2 v1. File name (.GK2) : $ File name (.COM - <CR> for GKEY2.COM) : $ File not found.$ Incorrect GK2 file.$ Bad GKEY2.COM file.$ GKEY2 not in memory.$ $ redefined as: $ is current setup key. Change to: $ is current shift key. Change to: $ bytes free for redefinitions. $ Setup key changed to: $ Shift key changed to: $>mC<mGK2GKEY2 COM defined key (or other special key): Type shift key (originally <\>), key to display. To load a key file: GKEY2 fn.GK2 To save current settings: GKEY2 fn.GK2 $ To remove GKEY2 from memory: GKEY2 - To clear current settings: GKEY2 # Escape sequences: + any key can be redefined just as if it were a single key: entering |Bblurk| will redefine B as blurk.$ ... Type any key$ GK2SET v1.0 (c) Eric Gans 05/06/86$ 0 = Quit GK2SET 1 = Display GK2 file 2 = Display current settings 3 = Change setup key 4 = Change shift key 5 = GKEY2 information Your choice: $GKEY2 v1. File name (.GK2) : $ File name (.COM - <:"s%1G=O!~=# .~;(4^(M& y= ~ x >*{(7$ # (~# x {_0x ## (~x># ~{^ $ # ~}(| > ~ >>G> # (9 # (0(x~& y= !"@ # ( ~ (# > !e2|\<.ͻ~;P(a (@< b.~ x #>$(?{^ ,̻>,̻x( {_0x=,̻~ Cͬ8,̻~1:?0O!G~(# ~ (#ͬ8 {%>,̻~;P({(< b$( (^ ,̻~(}(| > ~ >>,̻~ ʔ,̻~ ,̻~>,̻~>(B| > > $ ͬ8\ ,̻~^ ,̻~>\!áa{˯ɯ!w#w#w!R"#*|22I2222,^,VS. ^#V"x""+*"22822!z0R[#RR* " "Y"d"i""$*V"V*"*"*"*""#"*""{"*"*"*"*""]""(*"*"""*2"2"q!-""!K#!#s#r{% GSUB v1.4 (c) Eric Gans 07/10/85 Form 1: gsub =cmd1;{..}cmd2;{..} [&] Form 2: gsub subfn [var1] [var2] [&] {file input} ^ch=CtlCh ~=await input |=CR Vars $1-$9 @=NUL ;=cmnt $ch=lit \=killCRLF &=chain Command error ++ ABORTED ++$ SUB file error ++ ABORTED ++$SUB!~_#y {< : <  :8s 1-<(v*~ ;#~(l 2#"{  G#~ <O #~<̪"{ *~#" ; 2*"**#s#r;{  !`" w#w#w=2 #ɯ2 !" "a>2`:O#~#"O ABORTED :$GSUB TERMINATED$""#"*""{"*"*"*"*""]""(*"*"""*2"2"q!-""!K#!#s#r{% GS;;07-02-85 GSUB.DOC v1.4 07/10/85 Eric Gans French Dep't UCLA Los Angeles, CA 90024 Version 1.4 Enhancements: Allows console message display, input request in CCP mode (formerly allowed only in internal file mode). Fix: Removed last bug from console input routine; now guaranteed to handle any program input. Version 1.3 (7/3/85) Avoids problems with CONSTAT routine and with programs (e.g., MBasic) that copy the BIOS CONIN address. Seems (finally) to handle ALL kinds of console input within programs. [Almost!!(EG)] ****************************<< NOTE >>*************************** * * * This LBR includes versions of IFG/ELSEG/ENDIFG/GOTOG that can * * ONLY be used with GSUB 1.4. If you have the versions of these * * files from GSUB13.LBR or GSUB12X.LBR you should remove them * * as they will not work with GSUB14.   * * * * Please see IFG14.DOC for details of operation. * * * ***************************************************************** Version 1.2 6/25/85 Improvements: Protects CCP stack & makes fast CCP return on entry. Adds "&" toggle for slow CCP return, "@" for null variables. Calculates TPA from given BDOS address for effective memory use & compatibility with programs that load below the CCP. Uses no specific addresses other than standard jumps. Better error checking. Unlimited buffer space (uses only what is needed). Fixes: Bug in command-line read routine. Version 1.1 06/16/85 Allows internal use of SUBMIT -- permits chaining of batch files. Prints abort message. Allows null variable entry. ***** GSUB is a SUBMIT substitute that puts its command buffer in memory instead of in a $$$.SUB disk file. It allows: - internal program input separate from CCP input - chaining of batch files (v1.1+) - run from command line or SUB file - unlimited command space (v1.2) - output messages to console from SUB file (v1.4) - variables ($1 - $9) permitting null values - control characters - wait for console input - comments GSUB is smaller & faster than EX (or any other CP/M batch program). It only does a long CCP return (for chaining with SUBMIT or to reset drives) on request. Its command buffer has no size limit; only what is filled is placed in memory. Like SUBMIT, GSUB can be aborted by typing a character before it regains control. NB - I am no longer supporting a special Kaypro version (V1.0x, 1.1x). Why not EX? EX can't distinguish between console input within a program and input to the CCP. Thus it can't run a SUB file that contains a series of programs one of which allows console input; it just reads the rest of the SUB file into the program. For example, I use a little file Z.SUB for writing assembly language programs: ws $1.azm z80mr $1.bbz mload $1 EX can't handle this; as soon as it gets into Wordstar it writes the other two lines into my source file instead of waiting to get back to the console. GSUB solves this problem while allowing full control of the batch process with IF-ELSE-GOTO and counted loop structures. Command syntax: (1) Command line: to run GSUB in immediate mode (without a SUB file) you enter the command line as follows (CP/M will process a maximum of 127 characters): gsub =cmd1[;{file-internal commands}][cmd2;[~]..[;&] CCP commands (except the last in line) must be followed by ";". These follow standard syntax. File-internal commands must be within "{...}". The closing "}" need not be followed by a ";", although it makes things clearer, $ will take as a literal (i.e., to use "{" in the command line, write "${") ~ entered at the beginning of a command will produce an input request; this is better done in SUBfile mode where you can enter a prompt message. ^ will produce a control character "&" entered as a separate command at the end of the line will produce a "long" CCP return that allows chaining with SUBMIT. Use this switch if you want to use GSUB to run a program like SUBMIT that creates $$$.SUB disk files, or if your operations require a disk reset after Warm Boots (the default avoids this). Internal Commands (within {...}) | = carriage return (0Dh = ^m) ~ waits for a character input Examples: gsub =ws;{nblurk.azm^m}era *.bak will enter WS & open the non-document file blurk.azm, then on exit erase the BAK file(s) gsub =ddt;{dd200|lea00~s100|42|4c|55|52|4b|.|^c}save 1 blurk will enter ddt, dump d200-, write out the command "lea00" and wait for you to type a key (e.g., a CR), then display the byte at 100H and enter five characters and a "." to end, exit with ^C and save the result. Note that except for the wait for input, the second example might have been accomplished (several times slower) with SUBMIT and XSUB, but not the first, since "n" in Wordstar does not use CPM function #10 and is thus inaccessible to XSUB. EX could handle the "n" OK, but would then proceed to write "era *.bak" into the first line of BLURK.AZM. (2) File input: Variables: as in normal SUB files, variables should be numbered consecutively from $1 to $9. You may enter "@" alone in the command line to signify a null variable. "&" entered separately at the end of the command line will switch on the long CCP return as above. As with SUBMIT, each CCP command must be followed by a CRLF. Internal commands may include CRLF's also; usually a CR alone (|) suffices. The internal command line, if any, must end with  a CRLF after the closing "}". Messages to be displayed to the console during operation (either in CCP or file-internal mode) are placed between "<...>". GSUB will check for closed brackets of both {} and <>; you are advised not to try structures like "{ ... < ... } ... >" (GSUB would interpret the "}" as display text). NB - Within the display brackets, the character "|" is inter- preted as a CRLF. To kill the CRLF at the end of a line (e.g., if the line you want to display contains control characters that won't actually print but will set video controls), enter "\" just before the CR. The message will not end with a CRLF unless it is within the brackets: zap will run ZAP.COM OK, but will display as "Hi there!ZAP" on screen. Comment lines beginning with ";" will not be copied to the command buffer. You may also add comments after the end of the internal command line. $ will produce a literal of for anything but the variables $1-$9. Otherwise, the $, ^, | and ~ symbols function as above. Examples: (1) ws {nblurk.azm|} era *.bak (2) ddt { .... } (as above) save 1 blurk Another way of entering a CR into the internal input is to go to the next line; the program kills the LF's that are usually misinterpreted by word-processors & other programs (Wordstar thinks you want to set the help level with ^J): (3) ;for the literary ws charlott.let {Liebchen, I can't live without you. Death is my only recourse. ;don't believe a word - The Editor - Werther ^kx} ;pretty hot stuff? To output control characters etc. to the screen in immediate mode, enter them as external commands (NOT within {...}): gsub =^g^z^[B0^[=00BLURK!^[C0^m will sound the bell (^g), then (on an ADM 3A) blank the screen, set inverse video, go sixteen lines down and sixteen across, print BLURK!, then turn off inverse video and do a CR. (You can't use | here.) CHAINING WITH SUBMIT If "&" is entered at the end of the command line in either file or immediate mode, GSUB returns control after Warm Boots to the start of the CCP and thus allows the accessing of $$$.SUB files from within the program. This allows you to include calls to SUBMIT (or other disk-based batch utilities like my program OFFRUN) within a GSUB command line or SUB file. Thus you can enter: gsub =submit blurk urk1 urk2;submit zlonk urk3 ur4 urk5;& (NB the "@"=null feature won't work here) and both blurk.sub and zlonk.sub will be submitted. Or you can put this series in YECCH.SUB and enter: gsub yecch urk1 urk2 @ urk4.. & In this case, remember not to use SUBMIT with YECCH.SUB! Technical notes: GSUB works by trapping BDOS calls and testing for requests for CCP input. The location of the "late" CCP return is determined from the stack. GSUB also checks for changes in the BDOS jump address (at 0006) made by programs that want to keep themselves resident in memory, and locates itself just under the resident program. CP/M function 6 (direct console I/O) is handled by trapping calls to the BDOS. When internal program input is finished, it puts back the BIOS CONIN address not only in the BIOS jump table but in its own console input routine in order to work with programs (like MBasic) that skip past the jump table. Now that a last bug has been corrected, GSUB 1.4 should be usable on any CPM system under any circumstances. (( If it ain't so, please let me know!! )) ***** ALERT.COM (v1.0) is for use in SUB files when something goes wrong and your attention is required. It will sound the bell twice a second for about a minute (on 4MHz Z80 systems; ALERT is actually written in 8080 code) and then continue to wait for a keystroke. s1,**  0###"^#V""25q8 : <(ͽ { *6*[s#r̀ͽ2*" ~(-#o0$~( ((#># #~("">2 *Ⱦ#IFGENDIFGTED$GSUB14 not in memory$ERROR - ABORTED$GKEY2 DOC#'()*+GK2SET COM,-.GSUB14 COM/0GSUB DOCP123456789:ELSEG $$$ɪ!!8080 CPU?${ͅ; DCON Debugger$| }*|(=g.x( ~#bx( { ~#o}o0BMAP7/11ASMM BMAP COMCLS-ANY ASM CLS DOCCLS-^Z COMCPMADR COM+CPMCALC COM* !"#$GKEY2 COM %&GKEY2 DOC#'()*+GK2SET COM,-.GSUB14 COM/0GSUB DOCP123456789:ELSEG COM;ENDIFG $$$se1** a¥###"i^#V"g"m!$"k!m~ ("# y##~ r*m+~08fG0b5A 69!]~ (S08 0 GyOO#~y(8G*k"mx(  :q<(!*i[ms#rK {e *g6*m"o~(-#o0$~( ((#># #~("m"m>2qImproper command$Format: GOTOG n [#c] 1<=n<=255 cmd no|1<=c<=f count$GSUB14 not in memory$TED$GKEY2 DOC#'()*+GK2SET COM,-.GSUB14 COM/0GSUB DOCP123456789:ELSEG COM;ENDIFG COM<GOTOG $$$s$1** 3 0###"^#V"":] N(k:m  J)&:]A(*E(7M(# #~("">2*Ⱦ#> {$:]N( D(U  !m^#!!yo0$^#V IFGELSEGENDIFGTED$ ERROR - ABORTED$No filename/drive/user entered $Not a legal option (A,E,M,N or Z) $ERROR - Skipping both IF and ELSE clauses $Null filename$ ambiguous$ exists$ missing$ zero/missing$= current drive$= current user$: TRUE$: FALSE$GSUB14 not in memory IFG v1.4 (c) Eric Gans 07/10/85 IFG/ELSEG/ENDIFG/GOTOG v1.4 are designed for use with GSUB v1.4 GOTOG n [#c] n=cmdno c=cnt $NAEMZDU;;07-02-85 IFG.DOC v1.4 07/10/85 Eric Gans French Dep't UCLA Los Angeles, CA 90024 Version 1.4: These files are for use ONLY with GSUB 1.4!! Please get rid of earlier versions: they won't work with GSUB14. These files are substantively the same as in v1.3, but the addresses are different and new code has been added to allow for the message-display and input-request functions in GSUB 1.4. IFG, ELSEG, ENDIFG and GOTOG are faster and more versatile than the corresponding IF, ELSE... series written for SUBMIT. (To be fair, a good deal of IFG was taken from IF.ASM.) Out of deference to the latter, I have added a "G" to all the commands; if GSUB becomes the standard CP/M batch utility, I will take the G's off in a future version. IFG offers the same command structure as IF, plus a couple of enhancements. As with IF, only the first letter of the commands count (as long as there are no spaces). The options are: IFG N[ull] = if command line is null (e.g., a null variable in a SUB file) IFG E[xists] filespec IFG A[mbig] filespec = if the filespec contains wildcards IFG M[issing] filespec = if the filespec cannot be found IFG Z[ero] filespec = if filespec either zero-length or missing Additions: IFG D[rive] dr = if current drive is dr IFG U[user] userno = if current user is userno Whether these conditions are true or false, IFG will print the information on the screen (another improvement). The command structure is predictable: if the condition is true, what follows the IFG will be executed; if it is false, an ELSEG will be sought else commands will be executed following the ENDIFG. (Leave this out and you get an error message.) Since GSUB operates in the memory rather than on disk, IFG et al perform far faster than the IF series. ELSEG merely skips to the next ENDIFG since, like ELSE, it is only executed when the original condition was true. ENDIFG is, like ENDIF, a one-byte file consisting of a "ret". GOTOG Format: gotog n [#c] Unlike GOTO, GOTOG can jump backward as well as forward and includes a count option, permitting the creation of loops. The command to go to is numbered according to its position in the series of commands (in the SUB file or in immediate mode). Only CCP commands are counted, not internal file commands in GSUB. You may include the numbers as comments in the SUB file, but this is not necessary. GOTOG n performs an unconditional goto. To create a loop, use the count option GOTOG n #c where "c" is the desired number of executions. The command number "n" must be between 1 and 255; "c" is a single (hex) digit from 1 to F (=15). (If you don't like hex numbers, just use the digits 1 through 9.) Examples: ifg e blurk.com ;tests existence of file zlonk {^nr354|} ;commands between {} don't count for GOTOG ;message to be displayed dir gotog 7 ;goes to 7th command in list elseg ;if blurk.com doesn't exist, then rename rn zlonk.com=blurk.com era *.bak endifg ifg u 10 ;if user 10, edit zap.txt ws zap.txt elseg u 10 ;else change user* gotog 2 ;& go back to edit (the "else" clause will be endifg ;skipped, as always) *unlike SUBMIT, GSUB won't get lost when you change user numbers, but IFG &c must be accessible on disk blurk zap zlonk gotog 2 #a ;will go back to zap and execute the loop 10 times NB that GSUB converts all lower-case to upper case (except within the display-message brackets <...>) when it creates its buffer in high memory. I will continue do my best to implement your suggestions for modifications or enhancements.ts existence of file zlonk {^nr354|} ;commands between {} don't count for GOTOG ;mess4 Ver:1.0 Copyright (c) 1982 Gary P. Novosielski !91!~2 y+~# #~ A}2lCˆ2lD“2lEž2lM©2lP´2lU¿2l02l12l22l2  Option "" not valid.$...CANCELED$::ڐ:?Ґ:ڐ: ڐ:.ڐ:Gڐ:Ĉڐ:9ڐ:?9Ґ:Ĝڐ<ʵ!5+6<ʵÍ Error accessing .SUB file.$!]>? #  is a standard CP/M file reference either entered literally, or supplied by Submit parameter substitution. The syntax is the normal D:FILENAME.EXT form. If either Filename or Extension are not entered Page 1. 82-06-10 The //IF command DOCUMENTATION FOR //IF.COM AND //SKIP.COM they default to blanks. If the Drive is not entered, the current drive is assumed. If the name or extension contain question mark (?) or asterisk (*) characters, the file reference is said to be ambiguous. Otherwise it is unambiguous. is an optional second filespec used by some of the options described below. The defaults are the same as for parm1 is a string of single-character options with no intervening or trailing blanks. The option list must be separated from the preceding parameters by at least one blank, and must always begin with a colon (:) to identify it as the option list, and prevent it from being confused with parm1 or parm2 if one or both are omitted. The entire option list may also be omitted, in which case the identifying colon should not be entered. The ordering of the options within the list is not significant. The option list is really a set of tests to be performed upon the first parameter, parm1. (Parm1 is assumed to be the name of a file, but may in fact be any text at all, as long as it conforms to the syntax for a valid file reference.) If the tests are passed, the //IF command does nothing at all, and the next line in the submit file is processed normally. If however, any of the specified option tests fails, the //IF command modifies the submit control file (A:$$$.SUB) to Page 2. 82-06-10 The //IF command DOCUMENTATION FOR //IF.COM AND //SKIP.COM  skip over the next command in the file. Command processing then continues with the line after the one which was skipped, if any. If two mutually exclusive options are included in the option list, the following line will never be executed. Explanation of option characters: A Ambiguous: True if parm1 is an ambiguous file reference. False otherwise. The A option does not test to see if a file or files exists. For example the parameter *.ASM will pass the A test regardless of whether there are any .ASM files on the disk. U Unambiguous: True if parm1 is an unambiguous file reference. False otherwise. P Present: True if at least one file exists which matches the specification given in parm1. Parm1 may be ambiguous (unless of course the U option appears in the list) M Missing: True if there exists no file which matches the specification given in parm1. Parm1 may be ambiguous. C Contents: True if the file referenced by parm1 exists and contains at least one record (sector). If Parm1 is ambiguous the test fails. E Empty: True if the file referenced by parm1 exists in the directory but contains no data. If Parm1 is ambiguous the test fails. Page 3. 82-06-10 The //IF command DOCUMENTATION FOR //IF.COM AND //SKIP.COM D Drive substitution: This option is not really a test, and cannot fail as such. It causes whatever drive has been entered in parm2 to be moved into parm1 before any testing is done on parm1. For example if parm1 were A:TEST.FIL and parm2 were B: all the remaining options would treat parm1 as if it had been B:TEST.FIL. If parm2 is blank, or does not contain a drive spec, parm1 is modified to remove any explicit drive spec. Parm1 then refers to the default drive. Note again that the order of the options makes no difference to the program. The D option is always performed before any other tests, even if it occurs at the end of the option list. The following three options are matching tests. They match portions of parm1 against the corresponding portions of parm2. 0 Matches the drivespec portions of parm1 and parm2. Tests true if the same drive is referred to by both. Note that if one of the drives is explicit and the other is defaulted, the test will pass or not depending upon whether the explicit drive is in fact the current default drive. If both parm1 and parm2 are missing, this test will pass, since both parameters refer by default to the current drive. 1 Matches the filename portion of parm1 and parm2. Tests true if the filenames match or are both missing. If either or both of the filenames are ambiguous, the Page 4. 82-06-10 The //IF command DOCUMENTATION FOR //IF.COM AND //SKIP.COM ambiguous characters are considered to match the corresponding portion of the other name. For example if parm2 is * this test will always pass. Note that only the first 8 characters of the names are tested. 2 Matches the extension (filetype) portion of parm1 and parm2. Same as option 1 except that only the three characters after the period of each parameter are tested. No option list: If the option list is omitted entirely, the //IF command merely tests to see if anything was entered following the word //IF on the command line. If the remainder of the line (after Submit parameter substitution) is blank, the test fails. If any non-blank character appears on the line, the test  passes. For example the line //IF $1 tests to see if parameter number $1 was defined on the Submit command line. If it was not, the line after substitution becomes just "//IF ", and the test fails. Error Messages: The following error messages indicate fatal errors in processing. The remainder of the submit file is not processed, and control returns to the console. Option "X" not valid....CANCELED Page 5. 82-06-10 The //IF command DOCUMENTATION FOR //IF.COM AND //SKIP.COM A character appears in the option list which is not one of those defined above. Error accessing .SUB file....CANCELED The file A:$$$.SUB could not be found, or could not be closed, by the //IF program. This error can also occur if //IF is run from the console rather than within a submit file, or if //IF is the last line in a submit file;  i.e. there is no next line to skip when a test fails. Page 6. 82-06-10 The //IF command DOCUMENTATION FOR //IF.COM AND //SKIP.COM THE //SKIP COMMAND: The skip command is used to unconditionally skip over any number of lines within a Submit file. Normally it is used as the line immediately following an //IF command. In this way the testing capabilities of //IF can be extended to apply to more than one line of the file. //SKIP can also be used to implement IF...THEN...ELSE structures within Submit files. Syntax: (Square brackets indicate optional parameter.) //SKIP [number] where: is a decimal integer in the range 0-127 which indicates the number of lines in the submit file to be skipped. If omitted, the number 1 is assumed. The number must be separated from the //SKIP command by at least one space, and may not have any trailing spaces. //Skip operates by evaluating the argument and, if it is greater than zero, modifying the file A:$$$.SUB to cause the system to ignore the indicated number of lines. If the argument is 0, //SKIP does nothing at all. Error Messages: The following error messages indicate fatal errors in processing. The remainder of the submit file is not processed, and control returns to the console. Page 7. 82-06-10 The //SKIP command DOCUMENTATION FOR //IF.COM AND //SKIP.COM Error accessing .SUB file....CANCELED The file A:$$$.SUB could not be found, or could not be closed, by the //SKIP program. This error can also occur if //SKIP is typed directly from the console rather than within a submit file. //SKIP argument not numeric....CANCELLED The parameter contains characters other than 0 to 9. //SKIP argument exceeds file size....CANCELLED An attempt was made to skip over more lines than there are remaining in the A:$$$.SUB file. Page 8. 82-06-10 The //SKIP command DOCUMENTATION FOR //IF.COM AND //SKIP.COM USAGE EXAMPLES: Example 1: The following Submit file will assemble and load a source file. The file name is indicated as the first parameter. If the second parameter is EDIT, the file will first be edited. Some error checking is done. Note the use of lines beginning with a semicolon to supply messages to the operator. CP/M treats such lines as comments. The line numbers are for reference purposes, and are not part of the file. [1] //IF $1 :U2 [2] //SKIP 2 [3] ;PARAMETER 1 NOT VALID [4] ERA A:$$$$$$.SUB [5] //IF EDIT $2 :U012 [6] ED $1.ASM [7] //IF $1.ASM :C [8] ASM $1 [9] //IF $1.HEX :C [10] LOAD $1 Notes: Line 1: Tests that parameter 1 is unambiguous and has a filetype of all blanks. Parameter 2 is missing, and is treated as blanks for the comparison. Line 2: If the test is passed, lines 3 and 4 are skipped. Line 3: The operator is informed of an error condition Page 9. 82-06-10 Usage Examples DOCUMENTATION FOR //IF.COM AND //SKIP.COM Line 4: The submit file is aborted by erasing A:$$$.SUB. Since $ is a special character to SUBMIT, each one must be represented by two in the source file. Line 5: The second parameter is tested to see if it is equal to the word "EDIT". The 0 and 2 options are extra insurance that the drive and filetype are blank Line 6: If the test is passed the file is edited with a filetype of .ASM Line 7: The file is tested to see if it contains data. This helps insure that the editor terminated normally. Line 8: If the test is passed, the file is assembled. Line 9: The .HEX file is tested for contents to help insure that the assembler terminated normally Line 10: If the .HEX file is present, the file is LOADed to produce a .COM file. Page 10. 82-06-10 Usage Examples DOCUMENTATION FOR //IF.COM AND //SKIP.COM Example 2: The following is a skeleton example of how //IF and //SKIP can be used to form an IF....THEN....ELSE structure. <--------------------------------------> <--------- prior submit lines ---------> <--------------------------------------> //IF : //SKIP m <--------------------------------------> <---- m-1 lines to be executed---------> <---- if test is FALSE ----------------> <--------------------------------------> //SKIP n <--------------------------------------> <---- n lines to be executed ----------> <---- if test is TRUE------------------> <--------------------------------------> <---- remainder of submit file to -----> <---- be executed unconditionally -----> <--------------------------------------> Notes: The //IF condition tests whatever combination of options is desired. If the conditions are not met, the //SKIP immediately below is not executed. Instead, control passes through to the lines to be executed if the test is false, in effect the ELSE section. The last line of the ELSE section is another //SKIP which skips past the THEN section Page 11. 82-06-10 Usage Examples DOCUMENTATION FOR //IF.COM AND //SKIP.COM (those lines to be executed if the test is true) and executes the remainder of the file. If the //IF test is true, the first //SKIP statement skips over the ELSE section. The value m is chosen so that the last line skipped over is the second //SKIP statement. It takes some time to get used to seeing a structure where the ELSE comes between the IF and the THEN, but the structure is no less valid for being slightly unorthodox. Page 12. 82-06-10 Usage Examples DOCUMENTATION FOR //IF.COM AND //SKIP.COM PROGRAM NOTES: The program is written for the 8080 processor and should execute properly on 8080, 8085, and Z-80 processors. It has been tested under CP/M80 version 2.2, but should operate properly with 2.1, 2.0 and 1.4 also. There is a minor difference in the operation of the C option depending on the version used. If the operating system identifies itself as version 2.0 or above (as determined by a call to Function 12) the C option will use Function 35, "Compute File Size" in order to determine if the file contains any records. If the version test shows that the operating system is pre-version-2, the C option test merely tries to read the first record of the file. The only time this would make a difference is on a Random file which happens to have no records in the first extent. The sequential read would fail, but the Compute File Size function would properly report the existence of records in the file. While sequential files will test correctly on any version, files which have been created randomly should not be tested for contents on CP/M 1.4 or earlier. I would appreciate being informed of any bugs found in either program, or suggestions for expansion or improvement. Gary P. Novosielski 21 W. Pierrepont Avenue Rutherford, NJ 07070 Page 13. 82-06-10 Program Notes nt Avenue Ruherford, NJ 07070 Page 13. 82-06-10 Program Notes nt Avenue RuBMAP7/11ASMM BMAP COMCLS-ANY ASM CLS DOCCLS-^Z COMCPMADR COM+CPMCALC COM* !"#$GKEY2 COM %&GKEY2 DOC#'()*+GK2SET COM,-.GSUB14 COM/0GSUB DOCP123456789:ELSEG COM;ENDIFG COM<GOTOG COM=IFG COM>IFG DOC?@ABIF COMCIF ASMHDEFGHIJKLIFSKIP $$$MNOPQRSTUVWXYZ[\IFSKIP $$$;**************************************************************** ; PRL FILE FORMAT RELOCATER FOR BIT MAPPED FILES ;**************************************************************** ; ; ; This small program block moves a bit map relocatable ; file from address 0200h above this module up to a spot ; below the CCP and then jumps to the base of the moved ; code. The DIGITAL RESEARCH RELOCATING ASSEMBLER and the ; companion LINKER permit the generation of the PRL file ; format. LINK will put the code size word in to address ; 101 and 102. The code spot is intended to be 0200h with ; the bit map immediately above the code. A one in the bit ; map indicates the location of a moved byte that requires ; a relocation address offset. ; ;*************************************************************** ; ; ;start point for the beginning of the mover module ; ORG 0100H ; ; ;GENERAL CP/M BDOS INTERFACE EQUATES. ; BDOS EQU 0005H ;FILE MANAGER ENTRANCE LOCATION CODE$START EQU 0200H ;LINK 80 CODE START POINT FOR ORG 0 FILE ; ; ;START POINT OF MOVER CODE ; DB 01H ;INSERT HEX FILE CODE FOR LXI B,XXXX DS 2 ;INSERT CODE SIZE FROM LINK .PRL FILE ;WITH DDT HERE LXI H,0 ;GET CCP STACK FOR LATER PASSING DAD SP ;TO RELOCATED PROGRAM LXI SP,CODE$START ;LET STACK WORK DOWN FROM 0200H PUSH H ;SAVE CCP POINTER ON OUR STACK ; PUSH B ;SAVE A COPY OF CODE SIZE ON STACK ; ; ;GET BDOS PAGE ADDRESS BOUNDARY ; LXI H,BDOS+2 ;PAGE ADDRESS OF BDOS BASE MOV A,M ;INTO (A) SUI 8 ;DECREASE SIZE FOR CCP SIZE ;OF EIGHT PAGES DCR A ;ONE MORE TIME TO ACCOUNT FOR ;..PARTIAL PAGE CODE SIZE SUB B ;SUBTRACT CODE SIZE IN TRUNC INTETGER SIZE ; MOV D,A ;(DE) = DESTINATION ADDRESS BASE MVI E,0 PUSH D ;SAVE LOAD ADDRESS FOR LATER JUMP TO CODE ; LXI H,CODE$START ;START MOVE POINTER TO (HL) ; ; ;LOOP TO MOVE CODE UP IN RAM UNDER CCP ; MOVLOOP: MOV A,B ;CHECK BYTE COUNT TO SEE IF ALL MOVED YET ORA C JZ MOVDONE ;EXIT LOOP IF DONE ; DCX B ;DECREMENT BYTES TO MOVE COUNT MOV A,M ;GET A BYTE TO MOVE STAX D ;SAVE AT DESTINATION ADDRESS INX D ;BUMP SOURCE DESTINATION POINTERS INX H JMP MOVLOOP ;GO MOVE MORE BYTES ; ; ;CODE MOVED SO SET UP TO SCAN BIT MAP ; MOVDONE: POP D ;GET BACK A COPY OF THE DESTINATION ADDR POP B ;RESET (BC) TO BYTE COUNT FOR BIT MAP SCAN PUSH H ;SAVE ADDRESS OF BIT MAP ON TOP OF STACK ; MOV H,D ;SET (H) TO RELOCATE PAGE OFFSET DCR H ; ; ;LOOP TO SCAN CODE BLOCK JUST MOVED AND TO ADD IN OFFSET OF EXECUTION ;PAGE ADDRESS ON ALL BYTES NEEDING RELOCATION. ; RELOCLOOP: MOV A,B ;CHECK BIT MAP COUNTER TO SEE IF RELOC DONE ORA C JZ RELOCDONE ;EXIT IF ALL BYTES CHECKED DCX B ;DECREASE BYTE COUNT MOV A,E ;IS (DE) ADDRESS MOD EIGHT BYTES? ANI 7 ;IF SO WE NEED NEXT BIT MAP BYTE JNZ SAMEBYTE ;STILL ON SAME BIT MAP BYTE ; ; ;GET NEXT BIT MAP BYTE VIA POINTER ON TOP OF STACK ; XTHL ;SAVE (HL) AND GET CURRENT MAP POINTER MOV A,M ;GET MAP BYTE TO (A) INX H ;INCREASE POINTER FOR NEXT TIME XTHL ;PUT POINTER BACK ONTO STACK MOV L,A ;MAP BYTE TO HOLDING RESISTER ; SAMEBYTE: MOV A,L ;FETCH OUR CURRENT BIT MAP BYTE RAL ;SHIFT BIT MAP BYTE FOR NEXT BIT MOV L,A ;SAVE SHIFTED ONE FOR NEXT PASS JNC NOOFFSET ;SKIP OFFSET ADD IF BIT WAS NOT ONE ; ; ;GET CODE BYTE AND ADD IN OFFSET IF MAP BIT WAS 1 ; LDAX D ;FETCH THE DESTINATION BYTE ADD H ;ADD IN OFFSET STAX D ;STORE BACK AWAY ; NOOFFSET: INX D ;INCREASE THE MOVED CODE BYTE POINTER JMP RELOCLOOP ;GO TO PROCESS MORE BYTES ; ; ;HERE WHEN THE RELOCATION IS DONE READY TO JUMP TO THE MOVED CODE ;REMEMBER THAT (H) HAS PAGE ADDRESS OF MOVED CODE. ; RELOCDONE: POP D ;GET BIT MAP POINTER OFF STACK MOV D,H INR D ;SET UP EXECUTION ADDRESS MVI E,0 ;MAKE (DE) AND EVEN PAGE BOUNDARY ADDRESS POP H ;GET THE SAVED CCP STACK POINTER SPHL ;RESET FOR FOING TO MOVED PROGRAM XCHG ;GET TRANSFER ADDRESS FROM (DE) PCHL ;OFF TO THE MOVED CODE AREA ; END ; ; ;+++...END OF FILE OVED PROGRAM XCHG ;GET TR!91!~=W!x( ~#b%xK {>~#o}oG-Tf< ,,,,,,,,,, Micro Resources Ram Disk Already Active$\*}o|g:g."1*"!%."[6#s#r!76#ŸP!6# x¬!M6!X" Micro Resources RAM Disk Demonstration Add-on Access Module Version 1.0 6/14/82 Copyright (C) 1982 Micro Resources !%."*1:OGy!  ~,+j~÷XýK-]tÀÊåj*+++>32 ~ w#: =2 Ny:2M27>2M!:M:A\2@:M`i"8:M`i:My2:`i"K:M2B>2I2H>2J:M2Iy2J>2B:72C*8"D::2F:B=2B:7!C!DͶ::!F4~  6*D#"D2H2B<2H2G::<2?!@~6U:7!;N!<ͶN:?!>r:A:72;*8"<:?2>:H2A::o&)))))))N*K:I˜>2Aw# ˜:J:G2A:G!8#2G$2G!N"5:<G:>=W!"3N#y *3*5~# x*5*3 HA  $@  I$I$ T"BH"HHII"$$B"DD@$D$DHB!H@ "$ D" & 5 !N 5! 5  _W! :z 0 ?* !9" ͌" * " * 0 83  Ɛ':' '/7?v (08"*2:DEFGMOVW^_goSI@DLI@DPCI@SPI@XTI@DAD@OTDRINDRCCDRLDDROTIRINIRCCIRLDIROUTDIN*3*5~# x*5*3 HA  $@  I$I$ T"BH"HHII"$$B"DD@$D$DHB!H@ "$ D"  A MEMORY RESIDENT FLOPPY DISK PROGRAM by Michail J. Karas This is from the Lifelines/The Software Magazine, September1982 issue, about ADD-ON MODULE Construction. An add-on module is designed for use under single user CP/M-80 2.2. The next several paragraphs, from the previous articles, January, and February 1982, show the procedure to get a version up and running. The source file, RAMDISK.ASM, being very similar to a BIOS for CP/M-80 2.2, requires the extension disk definitions to have disk parameter tables, check vector and allocation vectors. In addition, a directory buffer must be allocated. An ADD-ON MODULE generates the appropriate tables through the use of the macro library DISKDEF.LIB provided by Digital Research on the CP/M-80 2.2 distribution diskette. This macro capability requires a macro assembler to properly process the DISKDEF macros. An additional implementation requirement is the availability of the assembler output as a .REL file. The Digital Research Relocating Macro Assembler RMAC is just the tichet to generate the appropriate .REL file and process the DISKDEF.LIB macro includes. The command structure below shows how to assemble an ADD-ON MODULE program like the RAM disk demo listing on the disk as RAMDISK.ASM. The following is an example on the assembly. A>RMAC RAMDISK $+S PZ The resulting .REL file has to be converted to a page relocatable format. After agonizing over the problem of easily making a "BIT MAP", I found that the Digital Research Link program distributed with RMAC and PL/I-80 can generate page relocatable files (.PRL type) which are compatible with MP/M-80. After reviewing the LINK output format of a .PRL file, I designed the PRLMOVE program to relocate the LINK output files. The command structure below shows how to produce the required .HEX file from PRLMOVE. A>MAC PRLMOVE $+S PZ The command structure below shows how to produce the .PRL file from the RMAC output. A>LINK RAMDISK[OP] where [OP] directs LINK to generate the desired .PRL file. The program PRLMOVE.HEX and the page relocatable RAMDISK.PRL must be combined into a single executable command file as follows: A>DDT DDT Vers. 2.2 -IRAMDISK.PRL -R <-Load of .PRL file to RAM to address 0100H with code image starting at address 0200H. MEXT PC nnmm 0000 <-Convert nn to decimal and remember value. -IPRLMOVE.HEX -R <-Read PRLMOVE program in over the .PRL file at load address of 0100H. -G0 <-Exit DDT to CP/M-80. A>SAVE dd RAMDISK.COM <-Save dd pages of memory to get command  file. dd = converted nn from above! This results in the .COM command file necessary to make an executable module out of the ADD-ON MODULE. The present design of the ADD-ON MODULE with the negative offset pointer calculation reentry to CCP requires that the BDOS and CCP combination be genuine (REAL Digital Research CP/M-80 Ver 2.2 of total length 01600H bytes. If your system uses a modified BDOS or CP/M-80 system look-alike, then the negative offset to the stack pointer will require a different value. See NOT$PRES: in listing. Also, for non-genuine CP/M-80 systems, if the CCP is a different size (not 0800H) bytes, the program PRLMOVE will have to be modified to set down the relocation load address calculation by an amount equal to the difference in the alien CCP size. ied to set down the relocation load address calculation by a <-Exit DDT to CP/M-80. A>SAVE dd RAMDISK.COM <-Save dd pages of memory to get command ;************************************************************** ; MICRO RESOURCES CP/M RAM DISK ADDON MODULE ;************************************************************** ; ; ; THIS PROGRAM IS A SMALL TRANSIENT PROGRAM BASED BIOS SUBSTITURE ; THAT ALLOWS FILE TRANSFER UTILITY BETWEEN THE NORMAL CP/M SYSTEM ; DISKS AND AN MEMORY RESIDENT "RAM DISK". THIS TRANSIENT PROGRAM IS ; SETUP FOR ANY VERSION 2.2 CP/M SYSTEM. ; ; THIS PROGRAM PRESENTS A SMALL DISK DEMONSTRATION VERSION THAT USES ; 20 K BYTES OF THE HOST SYSTEM TPA TO DEMONSTRATE THE "RAM DISK" ; ADDON MODULE TECHNIQUE. THE DEMO VERSION USES 1K BYTE ALLOCATION ; GROUPS (BLOCKS) AND ALLOWS FOR A TOTAL OF 32 DIRECTORY ENTRIES AS ; DEFINED BY THE DISKDEF MACRO CALLS. PLEASE NOTE THAT WHILE THIS MAY ; NOT APPEAR TO BE A VERY PRACTICAL IMPLEMENTATION OF A RAM DISK ; IT DOES DEMONSTRATE THE FRIVER TECHNIQUES. A GREATLY EXPANDED VERSION ; WOULD USE ADDITIONAL BANK SWITCHED RAM BOARDS TO STORE THE DISK DATA ; SUCH THAT THE DISK STORAGE AREA COULD EASILY BE EXPANDED TO AS MUCH ; AS A MEGABYTE OF MORE. ; ; THE SUPPORTED RAM DISK FORMAT IS AS FOLLOWS: ; ; 10 TRACKS MAPPED INTO RAM ; 8 PHYSICAL SECTORS FER TRACK ; 256 BYTES/PER SECTOR ; SECTORS ACCESSED FROM RAM THROUGH THE STANDARD BEBLOCKING ; TECHNIQUES. RAM RESIDENT "HOST SECTORS" MAPPED TO ; 256 BYTES IN SIZE SO EXPANSION OF THIS PROGRAM TO ; BANK SWITCHED MEMORY CAN BE MADE EASILY. ; ; PROGRAM ACCESS TO THE RAM DRIVE IS DONE IN 256 BYTE SECTORS ; THAT ARE DEBLOCKED WITHIN A BUFFER CONTAINED INSIDE OF THIS SOFTWARE ; PACKAGE. THE INITIAL LOADING OF THIS SOFTWARE SWAPS OUT THE NORMAL ; CP/M DISK I/O ENTERY POINTS TO THE BIOS WITH A NEW SET OF ENTRY POINTS ; TO THIS MODULE. THIS MODULE THEN CHECKS ALL SELECT DISK ACCESSES FOR ; LOGICAL UNIT P: AND WILL STEER I/O REQUESTS FOR THIS DRIVE THROUGH ; DRIVERS CONTAINED WITHIN THIS PROGRAM. NOTE THAT THIS PROGRAM STILL ; DEPENDS UPON THE HOST CP/M SYSTEM BDOS AS THE FILE INTERFACE MEDIUM. ; ; OPERATION OF THE PROGRAM IS DONE TO MOVE THE MODULE UP TO A WORKSPACE ; BELOW THE MEMORY RESIDENT CCP. THE WARM BOOT VECTOR AT THE SYSTEM ; WARM BOOT ENTRY POINT IS SWAPPED TO A NEW ENTRY POINT WITHIN THE ; RELOCATED I/O MODULE. THE NEW WARMBOOT FUNCTION SIMPLY RE-ENTERS ; THE ALREADY PRESENT CCP FOR FURTHER OPERATOR COMMAND PROCESSING. ; THE BDOS ENTRY VECTOR IS ALSO MODIFIED TO PERMIT THE DYNAMIC ; MODIFICATION OF THE USER PROGRAM AREA SIZE SUCH THAT THE CCP AND ; THE RELOCATED I/O MODULE DO NOT GET OVERLAYED BY THE TRANSIENT ; PROGRAM AREA'S BUFFER SPACES. THE UTILITY, WHEN LOADED PERFORMS ; A CHECK TO VERIFY WHETHER A RELOCATED MODULE IS ALREADY PRESENT ; IN MEMORY. THE ALREADY PRESENT CHECK IS DONE VIA A SPECIALLY DEFINED ; BDOS CALL THAT REQUESTS THE OPENING OF A FILE WITH THE SPECIALLY ; DEFINED NAME SEQUENCE OF "A,,,,,,,,.,,," AS THE FILE NAME WHARE (A) ; IS A REQUEST NUMBER FOR PRESENCE CHECKING. AS THIS CHARACTER SEQUENCE ; IS AN ILLEGAL FILE NAME SEQUENCE, THE CHECK PROGRAM WILL TRAP THE NAME ; AND RETURN A ZERO BYTE IN THE (A) REGISTER IF THE MODULE IS PRESENT. ; IF THE ADDRESS BYTE IN THE FIRST BYTE OF THE FCB IS NOT RECOGNIZED, ; THEN THE MODULE PASSES THE OPEN FILE REQUEST ON TO THE NEXT HIGHER ; LEVEL BDOS CALL. IN ANY CASE THE NON PRESENCE OF A FILE BY THE NAME ; OF "A,,,,,,,.,,," IS VIRTUALLY ASSURED TO CAUSE THE BDOS TO RETURN ; A NOT FOUND "0FFH" ERROR CODE IN THE (A) REGISTER. THIS WOULD INDICATE ; THE ABSENSE OF THE MODULE BEING CHECKED FOR. ; ; ; THIS RAM DISK DRIVER PACKAGE ; PROGRAM IS COPYRIGHT PROTECTED BY: ; ; COPYRIGHT (C) 1982 ; ; MICRO RESOURCES ; 2468 HANSEN COURT ; SIMI VALLEY, CALIFORNIA 93065 ; (805) 527-7922 ; ;***************************************************************************** ; ; ; ;DEFINE TRUE AND FALSE ASSEMBLY PARAMETERS ; TRUE EQU -1 ;DEFINE TRUE FALSE EQU NOT TRUE ;DEFINE FALSE ; ; ;DEFINE RAMSISK MODULE SELECT ADDRESS AS SPECIAL VALUE ; MODADDR EQU 08AH ;ADDRESS OF THIS MODULE ; ; ;CP/M BDOS INTERFACE EQUATES ; ASEG BOOT EQU 0000H ;FIXED BOOT ADDRESS BDOS EQU 0005H ;FIXED BDOS ADDRESS DEFFCB EQU 005CH ;DEFAULT FCB LOCATION DEFBUF EQU 0080H ;DEFAULT SYSTEM BUFFER RESET EQU 13 ;RESET DISK SYSTEM OPEN EQU 15 ;OPEN FILE STDMA EQU 26 ;SET DMA ADDRESS ; ; ;ASCII CHARACTER DEFINITIONS ; LF EQU 00AH ;ASCII LINE FEED CHARACTER CR EQU 00DH ;ASCII CARRIAGE RETURN CHARAVTER ; ; ;SECTOR DEBLOCKING ALGORITHMS FOR CP/M 2.2 ; MACLIB DISKDEF ; SMASK MACRO HBLK ;UTILITY MACRO TO COMPUTE SECTOR MASK ; ; COMPUTE LOG2(HBLK), RETURN @X AS RESULT ; (2 ** @X = HBLK ON RETURN) ; @Y SET HBLK @X SET 0 ; ; COUNT RIGHT SHIFTS OF @Y UNTIL = 1 ; REPT 8 IF @Y = 1 EXITM ENDIF ; ; @Y IS NOT 1, SHIFT RIGHT ONE POSITION ; @Y SET @Y SHR 1 @X SET @X + 1 ENDM ENDM ; ; ;BDOS CONSTANTS ON ENTRY TO "WRITE" ; WRALL EQU 0 ;WRITE TO ALLOCATED BLOCK WRDIR EQU 1 ;WRITE TO DIRECTORY WRUAL EQU 2 ;WRITE TO UNALLOCATED BLOCK ; ; ; ;CP/M 2.2 TO HOST DISK CONSTANTS ; BLKSIZ EQU 2048 ;CP/M ALLOCATION SIZE HSTSIZ EQU 256 ;HOST DISK SECTOR SIZE ; HDSPT EQU 32 ;HOST HARD DISK 256 BYTE SECTORS/TRACK HSTBLK EQU HSTSIZ/128 ;CP/M SECTS/HOST BUFF ; SECMSK EQU HSTBLK-1 ;SECTOR MASK SMASK HSTBLK ;COMPUTE SECTOR MASK SECSHF EQU @X ;LOG2(HSTBLK) ; ;SECTOR SKEW INTERLACE FACTOR ; SKEW EQU 00 ;SECTOR SKEW FACTOR ; SECSIZ EQU 256 ;NUMBER OF BYTES IN DISK RECORD ; ; ;**************************************************************************** ; ; CSEG ;SET ORIGIN TO ZERO FOR RELOCATABLE ;ASSEMBLY BY RMAC ; ; ;SETUP STORAGE FOR THE RAM DISK DRIVE DATA BUFFER BELOW THE RELOCATED ;ADDON MODULE. ; RAMBUF EQU $-(10*8*SECSIZ) ;SIZE SET AT 10 TRACKS OF EIGHT 256 ;BYTE SECTORS PER TRACK BUFSIZ EQU $-RAMBUF ;SIZE OF FUFFER FOR INIT CLEAR ; ; ;FIRST TIME START UP ENTRY POINT FOR THE RAM DISK AUTO RELOCATING ;I/O MODULE. ENTRY HERE ASSURES PRESENSE OF CP/M 2.2. ; JMP CHKPRES ;GO CHECK IF A MODULE OF ;SAME FUNCTION ADDRESS IS PRESENT ;IN SYSTEM ; ; ;SUBSTITURE BDOS ENTRY POINT. EXECUTION ADDRESS IS PLACED HERE ;FROM LOCATION 6 & 7 BY THE START UP MODULE PROVIDED THIS ;MODULE IS DETERMINED TO NOT ALREADY BE IN MEMORY. ; TO$BDOS: JMP $-$ ;ENTER ADDRESS AT STARTUP ; ; ;START UP CHECK ROUTINE TO SEE IF THIS SOFTWRE WAS ALREADY ;LOADED BY A PREVIOS OPERATOR COMMAND. ; CHKPRES: MVI C,OPEN ;ATTEMPT TO OPEN FILE "A,,,,,,,.,,," LXI D,CHKFCB ;POINT AT THE CHECK FCB CALL BDOS ;CALL NORMAL BDOS ADDRESS ORA A JNZ NOT$PRES ;NON ZERO RETURN MOFULE IS NOT ;..PRESENT LXI D,PRESMSG ;POINT TO PRESENT MESSAGE MVI C,9 ;PRINT FUNCTION CODE CALL BDOS ;PRINT ALREADY PRESENT MESSAGE RET ;SIMPLE RETURN TO THE CCP ; CHKFCB: DB 0,MODADDR,',,,,,,,,,,',0,0,0,0 DS 16 DB 0 ; PRESMSG: DB CR,LF,'Micro Resources Ram Disk Already Active','$' ; ; ;HERE IF THIS RELOCATED MODULE IS NOT PRESENT IN MEMORY ; NOT$PRES: ; POP H ;COMPUTE CCP RE-ENTRY POINT ; PUSH H ; WITH STANDARD CPM 2.2 ; LXI D,075CH ;NEGATIVE OFFSET TO CCP ENTRY ; ; FOR USE WITH ZCPR CPM OR USE 0765H ABOVE ; ; LXI D,0806H-003H ;LOAD OFFSET OF CCP TO BDOS ; LHLD 006H ;GET BDOS ENTRY POINT ; ;END OF ZCPR ; ; MOV A,L ; SUB E ; MOV L,A ; MOV A,H ; SBB D ; MOV H,A LDA BDOS+2 SUI 8 MOV H,A MVI L,3 SHLD CCP$ENT ;SAVE THAT ENTRY ADDRESS ; LHLD BDOS+1 ;GET PREVIOUS BDOS ADDRESS SHLD TO$BDOS+1 ;SET TO LOCAL REFERENCE VECTOR ; ; ;COMPUTE THE NEW RAM TOP OF TPA TO SET IN A JUMP ONE PAGE BELOW ;FOR PLACEMENT OF BASE OF THE RAM DISK DRIVE FOR BDOS REFERENCE ; LXI H,RAMBUF DCR H ;ONE PAGE DOWN MVI L,06H ;AT CP/M'S BDOS LOOK ALIKE SHLD BDOS+1 ;BASE+6 LXI D,BDOS$SCAN MVI M,0C3H ;SET A JUMP AT TPA TOP INX H MOV M,E ;LOW BYTE OF ENTRY POINT INX H MOV M,D ;HIGH BYTE OF ENTRY POINT ; ; ;INITIALIZE ALL ITEMS FOR USE IN THIS I/O HANDLER ; MVI B,ENDZ-STARTZ ;ZERO DATA AREA IN PARAMETER TABLE LXI H,STARTZ ZLP: MVI M,00H ;PUT IN A ZERO PARM BYTE INX H ;POINT TO NEXT BYTE TO BE ZEROED DCR B ;CHECK BYTE COUNT TO SEE IF DONE JNZ ZLP ; ; ;INITIALIZE THE RAM BUFFER TO LOOK LIKE FRESH FORMATTED DRIVE ; LXI B,BUFSIZ ;RAM DRIVE SIZE LXI H,RAMBUF ;DRIVE BASE ADDRESS E5LP: MVI M,0E5H ;STORE AN E5 BYTE INX H ;BUMP POINTER DCX B ;DEC BYTE COUNT MOV A,B ORA C JNZ E5LP ; LXI H,RAMSEL ;DISABLE DRIVE SELECT FOR RAM DISK MVI M,00H ; CALL MOVDN ;MOVE DOWN THE BIOS VECTOR TABLE LXI H,BOOTENT ;SET MOVED DOWN TABLE TO LOCAL BOOT HANDLER SHLD BWBOOT+1 ; CALL PRTMSG ;PRINT SIGNON MESSAGE ; DB CR,LF,'Micro Resources RAM Disk Demonstration' DB CR,LF,'Add-on Access Module Version 1.0 6/14/82' DB CR,LF,'Copyright (C) 1982 Micro Resources' DB CR,LF,0 ; ; ; ;HAVE THIS UTILITY QUEUE BOTH BIOS AND THIS DRIVER TO THE SAME ;CP/M DATA BUFFER ADDRESS ; LXI D,DEFBUF ;USE DEFAULT BUFFER MVI C,STDMA ;SET DMA CODE CALL TO$BDOS ; ; ;RETURN TO CCP VIA THE OLD DEFINED REENTRY POINT ; CCPGO: LXI H,RAMBUF DCR H ;ONE PAGE DOWN MVI L,06H ;AT CP/M'S BDOS LOOK ALIKE SHLD BDOS+1 ;BASE+6 LHLD CCP$ENT ;GET THE CCP ENTRY POINT LDA 004H ;GET CURRENTLY LODDED DRIVE MOV C,A PCHL ; ; ;NEW WARM BOOT ENTRY LOCATION THAT RESETS THE DISK SYSTEM ;AND TRANSFERS CONTROL BACK TO THE ALREADY PRESENT CCP ; BOOTENT: JMP CCPGO ;NO GO BACK TO THE CCP ; ; ;HERE FROM A BDOS ENTRY TO TRAP FILE OPEN I/O TO CHECK FOR ;MODULE PRESENT CHECK. ; BDOS$SCAN: PUSH D ;SAVE CALLERS PARAMETERS PUSH B MOV A,C ;GET FUNCTION CODE TO A CPI OPEN ;SEE IF THIS IS AN OPEN FUNCTION JNZ CHKFAIL INX D ;POINT TO FCB CHECK BYTE LXI H,10 ;SET SCAN COUNTER TO FAKE FILE NAME END DAD D MVI B,10 ;NUMBER OF "," TO CHECK FOR SCAN$LOOP: MOV A,M ;GET FILE NAME CHARACTER CPI ',' JNZ CHKFAIL ;PASS ON IF CHECK FAIL DCX H ;DECREASE BUFFER POINTER DCR B JNZ SCAN$LOOP ;CHECKED ALL PASSIBLE CHARS YET MOV A,M ;CHECK IF ADDRESS BYTE IS OURS CPI MODADDR JNZ CHKFAIL ;BALE OUT IF NOT XRA A ;RETURN ZERO BYTE IF ALL CHECK VALID POP B POP D RET ;BACK TO PRESENT CHECKER ; CHKFAIL: POP B ;PROPER OPEN CHECK FAIL  POP D JMP TO$BDOS ;OFF TO THE NORMAL BDOS ROUTINE ; ; ; XFRTAB: ; ;SUBSTITURE BIOS VECTOR TABLE. THIS JUMP TABLE VECTORS ALL CP/M ;DISK I/O TO THIS TRANSIENT MODULE FIRST. TABLE IS PUT INTO THE ;BIOS VECTOR TABLE POSITION BY A CALL TO THE SUBROUTINE "MOVDN" ; ; JMP BCBOOT ;TO NORMAL BIOS COLD BOOT ROUTINE JMP BOOTENT ;TO LOCAL WARM BOOT HANDLER JMP BCSTAT ;TO NORMAL BIOS CONSOLE STATUS CHECK JMP BCIN ;TO NORMAL BIOS CONSOLE INPUT JMP BCOUT ;TO NORMAL BIOS CONSOLE OUTPUT JMP BLOUT ;TO NORMAL BIOS LPT OUTPUT JMP BPUN ;TO NORMAL BIOS PUNCH OUTPUT JMP BRDR ;TO NORMAL BIOS READER INPUT JMP HOME ;MOVE DISK TO TRACK ZERO JMP SELDSK ;SELECT DISK DRIVE JMP SETTRK ;SEEK TO TRACK IN REG A JMP SETSEC ;SET SECTOR NUMBER JMP SETDMA ;SET DISK STARTING ADR JMP READ ;READ SELECTED SECTOR JMP WRITE ;WRITE SELECTED SECTOR JMP BLSTST ;GO RIGHT TO NORMAL BIOS FOR THIS I/O JMP SECTRAN ;SECTOR TRANSLATE ; ; LOCTAB: ; ;LOCAL COPY OF THE ORIGINAL BIOS DISK I/O VECTOR TABLE ;INITIALIZED BY CALLING THE "MOVDN" SUBROUTINE. ; BCBOOT: JMP $-$ ;TO BIOS COLD BOOT ROUTINE BWBOOT: JMP $-$ ;TO BIOS WARM BOOT ROUTINE BCSTAT: JMP $-$ ;TO BIOS CONSOLE STATUS CHECK BCIN: JMP $-$ ;TO BIOS CONSOLE INPUT BCOUT: JMP $-$ ;TO BIOS CONSOLE OUTPUT BLOUT: JMP $-$ ;TO BIOS LPT OUTPUT BPUN: JMP $-$ ;TO BIOS PUNCH OUTPUT BRDR: JMP $-$ ;TO BIOS READER INPUT BHOME: JMP $-$ ;TO BIOS HOME DISK ROUTINE BSELDSK: JMP $-$ ;TO BIOS SELECT DISK ROUTINE BSETTRK: JMP $-$ ;TO BIOS SET TRACK ROUTINE BSETSEC: JMP $-$ ;TO BIOS SET SECTOR ROUTINE BSETDMA: JMP $-$ ;TO BIOS SET DMA ROUTINE BREAD: JMP $-$ ;TO BIOS SECTOR RAD ROUTINE BWRITE: JMP $-$ ;TO BIOS SECTOR WRITE ROUTINE BLSTST: JMP $-$ ;TO BIOS LIST STATUS ROUTINE BSTRAN: JMP $-$ ;TO BIOS SECTOR TRANSLATE ROUTINE ; ; ;SUBROUTINE TO INTERCHANGE BIOS DISK I/O VECTOR TABLE ENTRIES WITH ;THOSE CONTAINED LOCALLY. ; TABSIZ EQU 17*3 ;TABLE SIZE TO INITIALIZE WITH 17 JMP'S ; ; MOVDN:  LHLD BOOT+1 ;GET ORIGINAL WARM BOOT VECTOR POINTER DCX H ;ADJUST TO BASE OF COLD BOOT VECTOR DCX H DCX H MVI A,TABSIZ ;SET BYTE COUNT TO MOVE STA BYTCNT LXI D,LOCTAB ;POINT TO LOCAL TABLE FILL FROM ABOVE LXI B,XFRTAB ;POINT TO TABLE TO MOVE UP MDLP: MOV A,M ;GET A BIOS TABLE BYTE STAX D ;PUT IN LOCAL COPY TABLE LDAX B ;GET BYTE OF PATCH TABLE MOV M,A ;PUT PATCH BYTE INTO BIOS POSITION INX H ;MOVE UP TO NEXT BYTE INX D INX B LDA BYTCNT ;SEE IF DONE YET DCR A STA BYTCNT JNZ MDLP ;CONTINUE IF NOT DONE YET RET ; BYTCNT: DB 0 ;LOCAL MOVE BYTE COUNTER ; ; ;*************************************************************************** ; ;PARAMETER TABLE FOR TPA REASIDEN RAM DRIVER ; DISKS 1 ;ONE LOGICAL DRIVES SUPPORTED DISKDEF 0,1,16,,1024,20,32,0,0 ;P: RAM DRIVE ; SELDSK: MOV A,C ;GET NEW UNIT NUMBER CPI 'D'-041H ;IS THIS OUR DRIVE? JZ SDSK1 ;IF SO THEN GIVE THEM A PARAMETER POINTER ; XRA A ;IF NOT CLEAR THE ZOBEX DRIVE SELECT FLAG STA RAMSEL JMP BSELDSK ;IF NOT FOR US THEN LET BIOS HAVE SELECT ; ; ;HERE IF DRIVE SELECT WAS FOR THIS PIECE OF SOFTWARE ; SDSK1: SUI 'D'-041H ;SET SEKDSK TO THE HEAD SELECT CODE FOR STA SEKDSK ;..RAM DISK DRIVE ; PUSH PSW MVI A,0FFH ;SET THE RAM DRIVE SELECT FLAG STA RAMSEL POP PSW ; LXI H,DPBASE ;PASS BACK DISK PARAMETER BASE XRA A ;SET A REG. = 00 RET ;RETURN FROM SELDSK ; ; ;DO DIGITAL RESEARCH BUFFER PURGE IF NEED BE AND BALE OUT ;NO RESTORE MEMORY IS RANDOM ACCESS ; HOME: LDA RAMSEL ;SEE IF RESTORE FOR US ORA A JZ BHOME ;NO MUST BE FOR BIOS DRIVE ; LDA HSTWRT ;CHECK HOST ACTIVE WRITE FLAG ORA A JNZ HOMEIT STA HSTACT HOMEIT: RET ; ; ;SET TRACK NUMBER SPECIFIED BY B&C REGS. ; SETTRK: LDA RAMSEL ;SEE IF TRACK FOR US ORA A JZ BSETTRK ;TO PROM IF NOT LOCAL ; MOV H,B MOV L,C SHLD SEKTRK ;TRACK TO EMULATE RET ; ; ; ;TRANSLATE THE SECTOR GIVEN BY B&C REGS. ; ; NO TRANSLATE DONE AT THIS TIME. WE WILL NOT NEED TO TRANSLATE ; RAM DISK SECTOR BECAUSE RAM HAS NO ROTATIONAL LATENCY ; SECTRAN: LDA RAMSEL ;SEE IF SECTRAN FOR US ORA A JZ BSTRAN ;TO BIOS IF NOT LOCAL ; MOV H,B MOV L,C RET ;RETURN FROM SECTRAN ; ; ;SET DISK SECTOR NUMBER ; SETSEC: LDA RAMSEL ;SEE IF SECTOR FOR US ORA A JZ BSETSEC ;TO PROM IF NOT LOCAL ; MOV A,C ;GET SECTOR NUMBER STA SEKSEC ;SECTOR TO EMULATE RET ;RETURN FROM SETSEC ; ; ;SET DISK DMA ADDRESS ; SETDMA: PUSH H MOV H,B ;MOVE B&C TO H&L MOV L,C SHLD DMAADR ;PUT AT DMA ADR ADDRESS POP H JMP BSETDMA ;TELL BIOS DMA ADDRESS ; ; ;READ THE SELECTED CP/M 2.2 SECTOR ; READ: LDA RAMSEL ;SEE IF OPERATION FOR US ORA A JZ BREAD ;GO READ IN BIOS IF NOT FOR US LOCAL ; XRA A ;CLEAR UNALLOCATED COUNT STA UNACNT MVI A,1 STA READOP ;READ OPERATION STA RSFLAG ;MUST READ DATA MVI A,WRUAL STA WRTYPE ;TREAT AS UNALLOCCATED JMP RWOPER ;TO PERFORM THE READ ; ; ;WRITE THE SELECTED CP/M 2.2 SECTOR ; WRITE: LDA RAMSEL ;IS THIS WRITE FOR HERE ORA A JZ BWRITE ;TO BIAS IF NOT SO ; XRA A ;0 TO A REG. STA READOP ;NOT A READ OPERATION MOV A,C ;WRITE TYPE IN C STA WRTYPE CPI WRUAL ;WRITE UNALLACATED? JNZ CHKUNA ;CHECK FOR UNALLOCATED ; ; ;WRITE TO UNALLOCATED, SET PARAMETERS ; MVI A,BLKSIZ/128 ;NEXT UNALLOCATED RECORDS STA UNACNT LDA SEKDSK ;DISK TO SEEK STA UNADSK ;UNADSK = SEKDSK LHLD SEKTRK SHLD UNATRK ;UNATRK = SECTRK LDA SEKSEC STA UNASEC ;UNASEC = SEKSEC ; ; ;CHECK FOR WRITE TO UNALLOCATED SECTOR ; CHKUNA: LDA UNACNT ;ANY UNALLOCATED REMAINING? ORA A JZ ALLOC ;SKIP IF NOT ; ; ;MORE UNALLOCATED RECORDS REMAIN ; DCR A ;UNACNT = UNACNT-1 STA UNACNT LDA SEKDSK ;SAME DISK? LXI H,UNADSK CMP M ;SEKDSK = UNADSK? JNZ ALLOC ;SKIP IF NOT ; ; ;DISKS ARE THE SAME ; LXI H,UNATRK CALL SEKTRKCMP ;SEKTRK = UNATRK? JNZ ALLOC ;SKIP IF NOT ; ; ;TRACKS ARE THE SAME ; LDA SEKSEC ;SAME SECTOR? LXI H,UNASEC CMP M ;SEKSEC = UNASEC? JNZ ALLOC ;SKIP IF NOT ; ; ;MATCH, MOVE TO NEXT SECTOR FOR FUTURE REFERENCE ; INR M ;UNASEC = UNASEC+1 MOV A,M ;END OF TRACK? PUSH B MVI B,HDSPT ;USE HARD DISK SPT CMP H POP B JC NOOVF ;SKIP IF NO OVERFLOW ; ; ;OVERLFOW TO NEXT TRACK ; MVI M,0 ;UNASEC = 0 LHLD UNATRK INX H SHLD UNATRK ;UNATRK = UNATRK+ ; ; ;MATCH FOUND, MARK AS UNNECESSARY READ ; NOOVF: XRA A ;0 TO A REG. STA RSFLAG ;REFLAG = 0 JMP RWOPER ;TO PERFORM THE WRITE ; ; ;NOT AN UNALLOCATED RECORD, REQUIRES PRE-READ ; ALLOC: XRA A ;0 TO A REG. STA UNACNT ;UNACNT = 0 INR A ;1 TO A REG. STA RSFLAG ;RSFLAG = 1 ; ; ;COMMON CODE FOR READ AND WRITE FOLLOWS: ; RWOPER: ;ENTER HERE TO PERFORM THE READ/WRITE XRA A ;ZERO TO A REG. STA ERFLAG ;NO ERRORS (YET) LDA SEKSEC ;COMPUTE HOST SECTOR ; REPT SECSHF ORA A ;CARRY = 0 RAR ;SHIFT RIGHT ENDM ; ; ;LET BIOS PRETEND THAT SECTORS ARE NUMBERED FROM 1 TO AVOID ;OTHER PROBLEMS IN THE "SEKHST" SECTOR NUMBER VALUE. ; INR A STA SEKHST ;HOST SECTOR TO SEEK ; ; ;ACTIVE HOST SECTOR? ; LXI H,HSTACT ;HOST ACTIVE FLAG MOV A,M MVI M,1 ;ALWAYS BECOMES 1 ORA A ;WAS IT ALREADY? JZ FILHST ;FILL HOST IF NOT ; ; ;HOST BUFFER ACTIVE, SAME AS SEEK BUFFER? ; LDA SEKDSK LXI H,HSTDSK ;SAME DISK? CMP M ;SEKDSK = HSTDSK? JNZ NOMATCH ; ; ;SAME DISK, SAME TRACK? ; LXI H,HSTTRK CALL SEKTRKCMP ;SEKTRK = HSTTRK? JNZ NOMATCH ; ; ;SAME DISK, SAME TRACK, SAME BUFFER? ; LDA SEKHST LXI H,HSTSEC ;SEKHST = HSTSEC? CMP M JZ MATCH ;SKIP IF MATCH ; ; ;PROPER DISK, BUT NOT CORRECT SECTOR ; NOMATCH: LDA HSTWRT ;HST WRITTEN? ORA A CNZ WRITEHST ;CLEAR HAST BUFF ; ; ;MAY HAVE TO FILL THE HOST BUFFER ; FILHST: LDA SEKDSK STA HSTDSK LHLD SEKTRK SHLD HSTTRK LDA SEKHST STA HSTSEC LDA RSFLAG ;NEED TO READ? ORA A CNZ READHST ;YES, IF 1 XRA A ;0 TO A REG. STA HSTWRT ;NO PENDING WRITE ; ; ;COPY DATA TO OR FROM BUFFER ; MATCH: LDA SEKSEC ;MASK BUFFER NUMBER ANI SECMSK ;LEAST SIGNIF BITS  MOV L,A ;READY TO SHIFT MVI H,0 ;DOUBLE COUNT ; REPT 7 ;SHIFT LEFT 7 DAD H ENDM ; ; ;HL HAS RELATIVE HOST BUFFER ADDRESS ; LXI D,HSTBUF DAD D ;HL = HOST ADDRESS XCHG ;NOW IN DE LHLD DMAADR ;GET/PUT CP/M DATA MVI C,128 ;LENGTH OF MOVE; CP/M SECTOR SIZE LDA READOP ;WHICH WAY? ORA A JNZ RWMOVE ;SKIP IF READ ; ; ;WRITE OPERATION, MARK AND SWITCH DIRECTION ; MVI A,1 STA HSTWRT ;HSTWRT = 1 XCHG ;SOURCE/DESTINATION SWAP ; ; ;C INITIALLY 128, DE IS SOURCE, HL IS DESTINATION ; RWMOVE: LDAX D ;SOURCE CHARACTER INX D MOV M,A ;TO DESTINATION INX H DCR C ;LOOP 128 TIMES JNZ RWMOVE ; ; ;DATA HAS BEEN MOVED TO/FROM HOST BUFFER ; LDA WRTYPE ;WRITE TYPE CPI WRDIR ;TO DIRECTORY? LDA ERFLAG ;IN CASE OF ERRORS RNZ ;NO FURTHER PROCESSING ; ; ;CLEAR HOST BUFFER FOR DIRECTORY WRITE ; ORA A ;ERRORS? RNZ ;SKIP IF SO XRA A ;0 TO A REG. STA HSTWRT ;BUFFER WRITTEN CALL WRITEHST LDA ERFLAG RET ; ; ;UTILITY SUBROUTINE FOR 16-BIT COMPARE ; SEKTRKCMP: ;HL = .UNATRK OR .HSTTRK, COMPARE ;..WITH SEKTRK XCHG LXI H,SEKTRK LDAX D ;LOW BYTE COMPARE CMP M ;SAME? RNZ ;RETURN IF NOT ; ; ;LOW BYTES EQUAL, TEST HIGH FIRST ; INX D INX H LDAX D CMP M ;SETS FLAGS RET ; ; WRITEHST: ;PERFORMS THE PHYSICAL WRITE ;TO THE HST DISK ; ;HSTDSK = HOST DISK NUMBER, HSTTRK = HOST TRACK NUMBER, ;HSTSEC = HOST SECT NUMBER. WRITE "HSTSIZ" BYTES ;FROM HSTBUF AND RETURN ERROR FLAG IN ERFLAG. ;RETURN ERFLAG NON-ZERO IF ERROR ; WRTSEC: CALL RIOPB ;SETUP RAM DISK IOPB ;..FROM BIOS VARIABLES CALL RWRITE ;GO WRITE RAM DISK SECTOR XRA A STA ERFLAG ;RESET ERROR FLAG RET ;RETURN FROM "WRITEHST", IF O.K. ; ; READHST: ;PERFORMS THE PHYSICAL READ FROM ;.. THE HOST DISK ; ;HSTDSK = HOST DISK NUMBER, HSTTRK = HOST TRACK NUMBER, ;HSTSEC = HOST SECT NUMBER. READ "HSTSIZ" BYTES ;INTO HSTBUF AND RETURN ERROR FLAG IN ERFLAG. ; READSEC: CALL RIOPB ;SET RAM DISK IOPB CALL RREAD ;GO READ SECTOR XRA A STA ERFLAG RET ; ; ;ROUTINE TO SETUP THE RAM DISK SOFTWARE ADDRESS VALUES. VALUES ;BASED UPON THE CP/M LOGICAL VALUES ; RIOPB: LXI H,HSTBUF ;POINT TO HOST BUFFER ADDRESS SHLD XFRPNT LDA HSTTRK ;CONVERT CP/M TRACK AND SECTOR TO ADD A ; 256 BYTE RAM PAGE ADDRESS INDEX ADD A ADD A ;TRACK BOUNDARY INDEX MOV B,A ;SAVE TO GET SECTOR LDA HSTSEC DCR A ;HOST BIOS ABOVE THINKS SECTORS START AT 1 ADD B ;SECTOR 256 BYTE INDEX TO RAM DISK MOV D,A MVI E,00H LXI H,RAMBUF ;POINT TO RAM BUFFER FOR DISK DAD D ;(HL) IS RAM ADDRESS FOR MOVE SHLD RAMADDR RET ; ; ;INLINE PRINT OF MESSAGE TILL A ZERO ; PRTMSG: XTHL ;SAVE HL, GET MSG POINTER ; PRTMLP: MOV C,M ;GET CHARACTER INX H ;INCREMENT POINTER TO HEXT CHAR ;.. OR RETURN ADDRESS MOV A,C ;CHECK IF ZERO END ORA A JZ PMXIT ;EXIT IF ZERO ; CALL CTYPE ;OUTPUT IT JMP PRTMLP ;GO CHECK/DO NEXT CHAR ; CTYPE: ; PUSH PSW PUSH B PUSH D PUSH H CALL BCOUT POP H POP D POP B ; POP PSW RET ; ; ; PMXIT: XTHL ;RESTORE HL, RET ADDR RET ;RET PAST MSG ; ; ;*************************************************************************** ;*************************************************************************** ; ; MICRO RESOURCES TPA RESIDENT RAM DRIVE I/O ROUTINES ; ;*************************************************************************** ;*************************************************************************** ; ; ;WRITE ON 256 BYTE RECORD FROM THE HSTBUF TO THE RAM DRIVE ; RWRITE: LHLD RAMADDR ;POINT AT THE RAM DRIVE ADDRESS XCHG ;TO (DE) AS "TO" ADDRESS LHLD XFRPNT ;GET (HL) AS "FROM" ADDRESS LXI B,SECSIZ ;PHYSICAL HOST SECTOR SIZE ; RWXFR: MOV A,M ;GET A DATA BYTE STAX D ;PUT AT DESTINATION INX H ;BUMP POINTERS INX D DCX B ;DECREMENT BUFFER SIZE COUNTER MOV A,B ORA C ;CHECK IF ALL MOVED YET JNZ RWXFR RET ;DONE WITH READ ALREADY ; ; ;READ ONE 256 BYTE RECORD FROM THE RAM DRIVE TO THE HOST BUFFER ; RREAD:  LHLD XFRPNT ;POINT AT THE HOST BUFFER AS XCHG ;..(DE) AS "TO" ADDRESS LHLD RAMADDR ;GET (HL) READ ADDRESS AS "FROM" ADDRESS LXI B,SECSIZ ;BYTES PER SECTOR COUNTER JMP RWXFR ;GO DO THE TRANSTER ; ; ;*************************************************************************** ; ; STORAGE AREA FOR VARIABLES BEGINS HERE.. ; ; ;RELOCATION POINTER STORAGE AREA ; CCP$ENT DS 2 ;STORE CCP RE-ENTRY POINTER HERE ;TABLE POINTER HERE ; ; ;RAM DISK DRIVE ACCESS PARAMETER BLOCK ; RAMADDR: DS 2 ;RAM DRIVE POINTER ADDRESS XFRPNT: DS 2 ;READ/WRITE ROUTINE DATA BUFFER POINTER ; ; ;THE NEXT SEVERAL BYTES, BETWEEN STARTZ AND ;ENDZ, ARE SET TO ZERO AT MODULE INITIALIZATION ; STARTZ EQU $ ;START OF ZEROED AREA ; ; ;HOST DISK BLOCKING/DE-BLOCKING DATA AREA ; SEKDSK: DS 1 ;SEEK DISK NUMBER SEKTRK: DS 2 ;SEEK TRACK NUMBER SEKSEC: DS 1 ;SEEK SECTOR NUMBER HSTDSK: DS 1 ;HOST DISK NUMBER HSTTRK: DS 2 ;HOST TRACK NUMBER HSTSEC: DS 1 ;HOST SECTOR NUMBER SEKHST:  DS 1 ;SEEK SHR SECSHF HSTACT: DS 1 ;HOST ACTIVE FLAG HSTWRT: DS 1 ;HOST WRITTEN FLAG UNACNT: DS 1 ;UNALLOCATED RECORD COUNT UNADSK: DS 1 ;LAST UNALLOCATED DISK UNATRK: DS 2 ;LAST UNALLOCATED TRACK UNASEC: DS 1 ;LAST UNALLOCATED SECTOR ERFLAG: DS 1 ;ERROR REPORTING RSFLAG: DS 1 ;READ SECTOR FLAG READOP: DS 1 ;1 IF READ OPERATION WRTYPE: DS 1 ;WRITE OPERATION TYPE DMAADR: DS 2 ;DISK DMA TRANSFER ADDRESS RAMSEL: DS 1 ;LOCAL DISK SELECTED FLAG ; ENDZ EQU $ ;END OF ZEROED AREA ; ; ;HOST DATA BUFFER MEMORY AREA ; ; HSTBUF: DS HSTSIZ ;HOST BUFFER ; ; ; ; ;SCRATCH RAM AREA FOR BDOS USE ; ENDEF ;LET DISKDEF FIXUP BDOS BUFFERS ; END ; ; ;+++...END OF FILE R ; ; ; ; ;SCRATCH RAM AREA CKING/DE-BLOCKING DATA AREA ; SEKDSK: DS 1 ;SEEK DISK NUMBER SEKTRK: DS 2 ;SEEK TRACK NUMBER SEKSEC: DS 1 ;SEEK SECTOR NUMBER HSTDSK: DS 1 ;HOST DISK NUMBER HSTTRK: DS 2 ;HOST TRACK NUMBER HSTSEC: DS 1 ;HOST SECTOR NUMBER SEKHST:  Abort: Valid address range at present is ....H through ....H. $Abort: unable to locate CCP $ SAFRAM Copyright 1982 by Roy Lipscomb, Logic Associates Version 1.0 Aug, 1982 Assigns safe partition (removed only by system reset) below BDOS. (Multiple executions of SAFRAM are permitted). [] To secure the CCP or the boot+6 address, type "SAFRAM". [] To secure a lower address, type "SAFRAM xxxx", where "xxxx" is address in hex. $ $͉*̏!\!:^Ą!9|*"S*Y*Y*"Gb!¡ʃq!~ ҡ*# ڡ"!!@*!N  p#q#r#s0 ))))oBKW_|))))0:|/g}/o#."> ~5P#د*#^#V"/=7[#N*>o"*"*6ͥ*>o""ͽ:xʼ*T] }||!# xGO! * }|*!|͙**"6#s#r* s#r*t !zS,0:7w#!" !" !" e! " !" ! " e** *#^#V*  s#r*  s#ryÒW x~# Ùxʼ*T] }||!# xGO! * }|*!|͙**>>2>2!":=$;O + (Safram at ....) $s#r*  s#ryÒW x~# Ùxʼ*T] }||!# xGO! * }|*!|͙**>>2>2!":=$;O + (Safram at ....) $>>2>2!":=$;O + (Safram at ....) $0:|/g}/o#."> ~5P#د*#^#V"/=7[#N*>o"*"*6ͥ*>o""ͽ:xʼ*T] }||!# xGO! * }|*!|͙*** safram.asm Roy Lipscomb * Version 1.0 Aug 1, 1982 * * Creates a "safe" partition below BDOS. Modules residing * in the partition are safe from destruction until the * the next system reset. * * Copyright 1982 by Roy Lipscomb, Logic Associates, Chicago * * Original distributor: HP/RCPM, (312) 955-4493 * * ******************************************************************** * * Description of this program is contained in SAFRAM.DOC and * in SAFRAM.H. * * Before attempting to assemble this source, please read the * ASSEMBLING section in SAFRAM.DOC. * ********************************************************* * service/loader routines * ********************************************************* if copy1 ;service routines org 100h jmp begin no equ 0 yes equ 1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; customizing variables ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; boot equ 0 ;lowest address within CP/M notify equ yes ;at each ^C, notify of secure addr hotboot equ 0 ;set to 0 to deactivate. ;hotboot gives displacement from start ; of bios to first instruction after ; code that loads CCP/BDOS/BIOS. ; See document SAFRAM.H for information. pagebnd equ no ;to force all secure addresses ;to page boundary, change to "yes" ;nothing below this point needs to be customized. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; bell equ 7 coutdsp equ 12-3 ;displacement of charout from wboot bdosjmp equ boot+5 pstr equ 9 deffcb equ boot+5ch tbuff equ boot+80h tab equ 9 cr equ 13 lf equ 10 blank equ ' ' pageopt equ (not pagebnd)/100h ;if pagebnd = no, pageopt = 0ffh secaddr dw 0 ;highest available RAM excluding CCP loadpnt dw 0 modbase dw 0 ;starting point of module ;(different from loadpnt if old module) ;values used in setting traps biosdsp dw 0 biosav dw 0 trapent dw 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;values used in manipulating the ccp doslen1 equ 0d00h ;length of cpm 1.x bdos doslen2 equ 0e00h ;length of cpm 2.x bdos ccplen equ 800h ;length of ccp ccpclen equ 7 ;ccp command-length byte ccpcchr equ 88h ;ccp current-char displacement ccp dw 0 ;ccp entry point curdrv equ 4 ;current drive in pg zero ccpstrt dw 0 ;pointer to first char in ccp buff ;messages eom equ '$' baddr db cr,lf db bell db 'Abort: Valid address range at present is ' badad1 db '....H through ' badad2 db '....H.' db cr,lf db '$' ccpmess db 'Abort: unable to locate CCP' db bell,cr,lf,cr,lf,eom helpmes db cr,lf db ' SAFRAM' db ' Copyright 1982 by Roy Lipscomb, Logic Associates' db cr,lf db ' Version 1.0 Aug, 1982' db cr,lf db cr,lf db ' Assigns safe partition (removed only by system' db ' reset) below BDOS.' db cr,lf db ' (Multiple executions of SAFRAM are permitted).' db cr,lf db cr,lf db ' [] To secure the CCP or the boot+6 address, ' db 'type "SAFRAM".' db cr,lf db ' [] To secure a lower address, type "SAFRAM xxxx",' db ' where' db cr,lf db ' "xxxx" is address in hex.' db cr,lf db cr,lf db ' ',eom crlf db cr,lf db eom ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; mainline ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; begin call signon ;print signon message call getbase ;compute load address: just below CCP cz ccperr ;ccp not found jz exit ;test for explicit address on command line. Abort if bad. call expladr jnz exit ;quit if error ;if already loaded and active, don't load again (just update it) call tstact cz active cnz instal ;if ddt executing, do restart 7; else return to cpm exit lxi h,0 dad sp ;if stack pointer <= 100, mov a,h ; then ddt active cpi 2 rnc ;stack pointer >1ff, so must be cpm rst 7 ;ddt active; do restart 7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; compute load address ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;if ccp not found, return with zero set ;first try to find ccp: standard method getbase lhld boot+6 shld secaddr ;secure-address if ccp already ; secured (default) lxi d,0 call tstccp ;add de and hl for candidate ccpbase jz ccpnosc ;ccp found, not secured ;second try: via cbios entry point of cpm 2.x lhld boot+1 lxi d,-doslen2 call tstccp ;add de and hl for candidate ccpbase jz ccpsec ;ccp found, secured ;last try: via cbios entry point of cpm 1.x lhld boot+1 lxi d,-doslen1 call tstccp ;add de and hl for candidate ccpbase jz ccpsec ;ccp found, secured ;ccp not found xra a ret ;set zero flag ("not found") ;ccp secured/not-secured ccpnosc lhld ccp ;(really contains bdos addr here) shld secaddr ;set to secure ccp ccpsec ori 1 ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;test for explicit address on command line. Abort if error ;if no error, return with z flag = yes ;get parm length and address expladr lxi d,tbuff ;get parm length ldax d mov b,a ;save parm length byte inr b ;skip leading blanks, exit if nothing left expl4 dcr b jz expl9 inx d ldax d call blnktab ;test for blank or tab jz expl4 ;digest the parm lxi h,0 ;initialize hex value expl6 call asc2hex ;build hex value in hl jnz expl8 ;exit if error dcr b jz expl7 inx d ldax d call blnktab ;test for blank or tab jnz expl6 ;value digested: verify it's acceptable expl7 xchg ;save in de lxi h,progend+2 call neghl dad d jnc expl8 ;error if below end of this prog... lhld secaddr inx h call neghl dad d jc expl8 ; ...or if higher than available top ;value ok: substitute it for implicit address xchg shld secaddr xra a ;set z = "no error" jmp expl9 ;error exit: display acceptable addr range, then exit. expl8 lxi h,progend+3 call hex2asc ;put value in de and hl lxi h,badad1 call put2bad lhld secaddr call hex2asc  lxi h,badad2 call put2bad lxi d,baddr mvi c,pstr call bdosjmp ori 1 ;set z = "error" expl9 ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;test for blank or tab in a; if yes, return z = yes blnktab cpi blank rz cpi tab ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;put address into error message put2bad mov m,b inx h mov m,c inx h mov m,d inx h mov m,e ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;convert ascii characters into hex (using hl) asc2hex sui '0' rc ;exit if invalid char cpi 10 jc asc2b sui 7 cpi 16 jnc asc2x asc2b dad h dad h dad h dad h add l mov l,a xra a ;set z = "no error" ret ;error exit asc2x ori 1 ;set z = "error" ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;convert hex value to ascii hex2asc call hex2b mov b,d mov c,e hex2b call hex2c mov d,a call hex2c mov e,a ret hex2c mov a,h dad h dad h dad h dad h rar rar rar rar ani 0fh adi '0' cpi '9'+1 rc adi 7 ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;convert hl to its negative neghl push psw mov a,h cma mov h,a mov a,l cma mov l,a inx h pop psw ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;test if this location has ccp ;compute tentative ccpbase tstccp mvi l,0 dad d ;compute tentative bdos entry point lxi d,-ccplen dad d ;tentative bdos entry minus ccp length shld ccp ; ...gives tentative ccp entry ;test for the two leading jumps in the ccp mvi a,jmp lxi b,3 cmp m rnz dad b cmp m rnz ;test for standard "maximum buffer length" value dad b mov a,m cpi 7fh ;standard ccp maxbuff len jz tstcc4 cpi 50h ;standard zcp maxbuff len rnz ;test for "buffer length" < "maximum buffer length" tstcc4 inx h cmp m rc ;give up if bufflen > maxbufflen xra a ;looks ok ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; test if module already active ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;check address in bios table for wboot tstact lhld boot+1 inx h mov e,m inx h mov d,m xchg shld modbase ;save it ;test if "secure" message present where expected lxi d,wb2mess ;get adjustment for message dad d ;compare to unmoved module message lxi d,message+module1 mvi b,testlen tstmess ldax d cmp m jnz failtst inx h inx d dcr b jnz tstmess ;active already xra a ret failtst ori 1 ;set flag ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; module already active: process only stub ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;determine where to install bypass active lhld secaddr lxi d,-3 dad d mvi a,pageopt ana l mov l,a shld loadpnt ;determine old module's start lhld modbase lxi d,-beforwb dad d shld modbase ;secure, then update message and savearea call doprot ;set for display of current secured address lhld modbase lxi d,bootflg dad d mvi m,on ;set flag to "on" ;set to bypass "not active" module xra a ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; module not active: load from scratch ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;if hotboot option requested, set it now instal call sethot ;compute load point lhld secaddr lxi d,-length dad d mvi a,pageopt ana l mov l,a shld loadpnt shld modbase ;modbase = new module entry ;move it call chgaddr ;convert to true addresses call movemod ;move module to load address call traps ;install traps call doprot ;do secure, and update message ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;overlay warmboot jmptable entry with hotboot, if hotboot > bdos. sethot lxi b,hotboot mov a,b ora c jz noacex ;hotboot option not in effect, so exit. lhld boot+1 ;put warmstart jmptab entry into hl mov d,h mov e,l ; point de to warmboot jmp addr inx d dcx b ;normalize bc for warmstart, not cold dcx b dcx b dad b ;compute hl = hotboot jump address mov a,l ;substitute hot for warm boot address stax d inx d mov a,h stax d noacex ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; convert module1 to true addresses ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; change pseudo addresses to true addresses chgaddr lxi b,length lxi d,module1 lxi h,module2 truloop ldax d cmp m cnz convert inx h inx d dcx b mov a,b ora c jnz truloop ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; convert displacement into address ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; convert push b ;length of module not yet processed push h ;module2 character position ; get displacement from instruction, put into bc. ldax d mov b,a dcx d ldax d mov c,a  lxi h,-module1 ;normalize displacement to zero dad b push h pop b lhld loadpnt ;add load point for true addr dad b ; move true address to instruction mov a,l stax d inx d mov a,h stax d pop h pop b ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; move module into place ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; movemod lhld loadpnt xchg ;de=destination lxi h,module1 ;hl=source lxi b,length call move ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; install security, describe in message ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;move the new secured address to de, then store wherever needed ; secure the module doprot lhld 6 xchg lhld loadpnt shld 6 ;install bypass at base of secured RAM push h ;save new secured address mvi m,jmp inx h mov m,e inx h mov m,d pop d ;restore new secured address ;store new secured address in module lhld modbase lxi b,savbase dad b mov m,e inx h mov m,d ;convert hex address at location 6 to ascii, put in message lhld modbase ;address message lxi b,messadr dad b ;pointer to message into hl ;output hi and lo bytes of address to message call hilo hilo mov a,d mov d,e ;get next byte push psw rar rar rar rar call hexbyte pop psw hexbyte ani 0fh adi '0' cpi '9'+1 jc movhex adi 7 movhex mov m,a inx h ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; set all traps ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;set trap for wboot traps lxi h,toboot+1 shld biosav lxi h,beforwb shld trapent lxi h,0 shld biosdsp call settrap ;set trap for conout lxi h,jmpcout+1 shld biosav lxi h,pentry shld trapent lxi h,coutdsp shld biosdsp call settrap ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; set a trap ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; settrap lhld modbase ;get address of moved module push h pop b ;save in bc lhld biosdsp ;get displace for bios jmptable into de push h pop d ;get contents of bios jumptable entry lhld boot+1 dad d ;address bios jumptable entry inx h push h ;...save pointer to table entry mov e,m ;...and get contents inx h mov d,m ;store bios entry into trap savearea lhld biosav ;save bios contents into trap mod dad b mov m,e inx h mov m,d ;set bios entry to trap entry lhld trapent ;get addr of appropriate trap into de dad b xchg pop h ;store trap addr in bios jmptab mov m,e inx h mov m,d ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; print messages ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; signon lxi d,helpmes jmp domess ccperr lxi d,ccpmess domess mvi c,pstr call bdosjmp xra a ;set zero flag (for ccperr return) ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; move block of  data ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;source in hl, destination in de, length in bc. move mov a,b ora c rz mov a,m stax d inx d inx h dcx b jmp move ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; end of service routines ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; endif ********************************************************* ********************************************************* * beginning of movable module * ********************************************************* ********************************************************* org ($+0ffh)/100h*100h ;must be page boundary adjust set $ if copy1 module1 equ $ endif if not copy1 module2 equ $ endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; address equ 0ffffh ;various address to fill at runtime on equ 0 ;do resecure off equ 0ffh pflag equ on ;harden security as soon as loaded pbase equ $-adjust jmp address ;security base: "address" set at run ;time to jump to previous pbase notiflg equ $-adjust db notify ;yes/no for message at each ^C ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;maintrap: perform setup if wboot (or trap just loaded) pentry equ $-adjust bootflg equ pentry+1 mvi a,pflag ;"on" for wboot cpi on ;test for "on" cz afterwb+adjust jmpcout equ $-adjust jmp address ;from bios conout jmptable entry ;("address" set at runtime) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;pre-wboot action: set flag beforwb equ $-adjust mvi a,on sta bootflg+adjust toboot equ $-adjust jmp address ;on to wboot (address set at runtime) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;post-wboot action: reset flag, restore security, rearm boot trap afterwb equ $-adjust mvi a,off sta bootflg+adjust ;turn off flag savbase equ $-adjust+1 lxi h,address ;restore secured address to 5h shld 6 ;("address" filled at runtime) ;if display option set, notify that security still in effect lda notiflg+adjust cpi yes rnz push b lxi d,message+adjust ;notify of current pbase display equ $-adjust ldax d inx d cpi eom jz dispend+adjust mov c,a push d call jmpcout+adjust pop d jmp display+adjust dispend equ $-adjust pop b ret ;don't remove the message below; used to test if module already present message equ $-adjust ;(number of blanks below is arbitrary) db ' ' db '(Safram at ' messadr equ $-adjust db '....)',cr,lf,eom wb2mess equ message-beforwb ;distance from wboot trap testlen equ messadr-message ********************************************************* * end of relocatable routines * ********************************************************* ;get length of relocatable routines length equ $-adjust ;flip the copy1/copy2 toggle copy1 set not copy1 ;assemble address of end of this program if copy1 progend equ $ endif ;link a second copy, if this was the first copy if not copy1 link safram endif ram if copy1 pe+adjust ;notify of current pbase display equ $-adjust ldax d inx d cpi eom jz dispend+adjust mov c,a push d call jmpcout+adjust pop d jmp display+adjust dispend equ $-adjust pop b ret ;don't remove the message below; used to test if module already present message equ $-adjust ;(number of blanks below is arbitrary) db ' ' db '(Safram at ' messadr equ $-adjust db '....)',cr,lf,eom wb2mess equ message-beforwb ;distance from wboot trap testlen equ messadr-message ********************************************************* * end of relocatable routines * ********************************************************* ;get length of relocatable routines length equ $-adjust ;flip the copy1/copy2 toggle copy1 set not copy1 ;assemble address of end of this program if copy1 p* safram.doc Roy Lipscomb * version 1.0 Aug 1, 1982 * * Secures CCP and relocated modules against destruction until * system reset. Operates as is with any standard CP/M-80. * * Copyright 1982 by Roy Lipscomb, Logic Associates, Chicago * Copying permitted only for non-commercial/non-profit uses. * * Original distributor: HP/RCPM, (312) 955-4493 * * ******************************************************************** * * FUNCTION * * SAFRAM creates a safe zone or partition below BDOS in which * the CCP and trap modules can reside without fear of being * overwritten by transient programs. The protection lasts until * the system is reset. * * SAFRAM makes it possible to load any module below the CCP that * previously had to be loaded above CP/M to be secure against * warm boots. Thus, one-size CP/M can now be used with utilities * like IOCAP or self-installing drivers for hard disks. * * SAFRAM also makes it possible to create BDOS and/or BIOS * extensions without actually inst!alling them in the BDOS and/or * BIOS. This allows them to be RAM-resident only when they are * needed. It also makes possible extensions that are too long * to include in the allotted BIOS space on disk. * * Installed, the SAFRAM module occupies less that 128 bytes of * RAM. Each additional execution of SAFRAM will add another * 3 bytes of overhead. The CCP (2K) will also be resident * whenever SAFRAM has been installed. * ******************************************************************** * * BACKGROUND * * Various utility modules or "traps" install themselves * just below the CCP before they begin operation. This * allows other programs to load and execute normally, * without overlaying and disrupting operation of the traps. * * Essential to this technique is changing location boot+6 * to point to the start of the trap. This is necessary because * boot+6 tells the next program what the upper end of the * available work space is. * * This technique has been hobbled by the fact that warmboot * alters the boot+6 address back to its original coldboot * value. This strips away the module's protection. * * SAFRAM operates by making the address at boot+6 "permanent." * ******************************************************************** * * THEORY OF OPERATION * * In its simplest terms, SAFRAM operates by patching two traps * into the BIOS jump table. The "WARMBOOT" trap sets a flag * indicating that a warmboot has been requested, and then * jumps to the normal warmboot routine. The "CONSOLE OUT" * trap checks the warmboot flag. If the flag is set, this trap * resets the flag, and restores boot+6 to the way it was just * before the last warmboot. Whether the flag was set or not, * this trap finishes by jumping to the normal CONOUT routine. * * Operation of these traps is disrupted only by pressing reset. * * At installation, the SAFRAM trap module relocates itself to * just below the CCP (or the currently protected address), and * creates the "safe ram" partition around itself. * * SAFRAM will not install if the CCP is not resident and intact. * This is to avert any address contention between the CCP and * resident traps. It is recommended that SAFRAM be executed * prior to installing any other traps, thus insuring that * that the CCP is protected and secured. * * Once SAFRAM is executed, any number of self-relocating * utilities may be installed normally. Simply follow up each * installation with an fresh execution of SAFRAM * (without pressing the reset button, of course). SAFRAM * will reuse the resident copy of the SAFRAM trap module, rather * than create a new copy. * * SAFRAM has two modes of execution. Executed without param- * eters, it will secure the current address in location boot+6 * (or secure the CCP if boot+6 points higher than the CCP start). * Executed with a hex address as its parameter, it will secure * that explicit address instead. Examples: * * Implicit mode: A>SAFRAM * Explicit mode: A>SAFRAM A000 * * ******************************************************************** * * ASSEMBLING * * This source uses the LINK pseudo-operation featured in * Ward Christensen's LASM.COM (available on RCPM's) and * its predecessor LINKASM.COM (available from CPM Users * Group as 36.11 and 36.12). This feature is used here to * link SAFRAM.ASM to itself, and thus introduce two * (partial) copies to the assembler. The two copies are used * by the resultant SAFRAM.COM to produce a relocatable module. * * If neither of the above assemblers is available, this * source can be assembled in a more roundabout fashion. * * First, delete these three statements from the end of * SAFRAM.ASM: * * if not copy1 * link safram * endif * * Next, execute the following commands: * * PIP SAFRAM2.ASM=SAFRAM.ASM,SAFRAM.ASM * ASM SAFRAM2 (using any standard assembler) * LOAD SAFRAM2 * * You should now use SAFRAM2 instead of SAFRAM. * ******************************************************************** * * CUSTOMIZING * * Customizing of SAFRAM.ASM is accomplished through several * variables: * * VARIABLE VALUE * -------- ----- * * BOOT 0 for standard CP/M * * NOTIFY "yes" for notification of what is * the currently secured address. * (Displayed at each warmboot.) * * HOTBOOT 0 if not active. Otherwise gives * displacement from start of bios to * the first instruction after the end * of the CCP/BDOS/BIOS-loading code. * * Used to bypass unnecessary reloading * of the CCP and BDOS, which are saved * by SAFRAM from being overlaid (with 99% * assurance: somewhere there may be * some unlikely program that ignores * location boot+6). * * (See document SAFRAM.H for more information on HOTBOOT.) * ******************************************************************** * * APPLICATION NOTES * * [] Self-relocating utility modules that will be protected by * SAFRAM should insure that they are not overlaying the CCP. * * [] When securing "a module that does not protect itself by changing * location boot+6, you should use the explicit-address mode * of SAFRAM. (See THEORY OF OPERATION above.) * * [] There is no option to individually "de-secure" a module. The * only way to nullify security is to press the reset button. * The de-securing option was deemed infeasible, since there is * no simple way to keep track of what patches may have been * intalled by any given trap module, so that they could be * reversed if the module were de-secured. * * [] To inspect the CCP using DDT (which normally overlays the CCP), * execute SAFRAM before invoking DDT. The CCP will be intact * after DDT has been loaded. * * [] It is sometimes desirable to secure DDT (for instance, for * use with BACK2DDT, available on RCPM's). Use this procedure: * * SAFRAM (to secure the CCP) * DDT SAFRAM.COM * -G (to secure DDT itself) * * (DDT may now be used in the normal fashion.) * * * (end)  * DDT SAFRAM.COM * -G (to SAFRAM allows you to protect an area of high memory from being overwritten. Place SAFRAM2.COM in drive A. At the A> prompt, type SAFRAM2 Refer to SAFRAM.DOC for further information. 4 Ver:1.0 Copyright (c) 1982 Gary P. Novosielski !91!~<=6!CV!q!qX0_=ZKڱKf<ʴ:uo&Q}2u2tf<ʴError accessing .SUB file.$//SKIP argument not numeric.$//SKIP argument exceeds file size.$ f1 ...CANCELED$og~ #C|}}o|g0:?DM ) )$$$ SUB TITLE '//SKIP.ASM Transfer control in Submit file' VERSION EQU 1$0 ; @MSG SET 9 @OPN SET 15 @CLS SET 16 @DEL SET 19 ; CPMBASE EQU 0 BOOT SET CPMBASE BDOS SET BOOT+5 TBUFF EQU BOOT+80H TPA EQU BOOT+100H CTRL EQU ' '-1 ;Ctrl char mask CR SET CTRL AND 'M' LF SET CTRL AND 'J' ; CPM MACRO FUNC,OPERAND IF NOT NUL OPERAND LXI D,OPERAND ENDIF ;;of not nul operand IF NOT NUL FUNC MVI C,@&FUNC ENDIF CALL BDOS ENDM ; FCBS2 EQU 14 FCBRC EQU 15 FCBR0 EQU 33 ;Offsets into File Control Blocks FCBR1 EQU 34 FCBR2 EQU 35 ; SKIPROG ORG TPA ; JMP PASTC DB ' Ver:' DB VERSION/10+'0' DB '.' DB VERSION MOD 10+'0' DB ' Copyright (c) 1982 Gary P. Novosielski ' DB CTRL AND 'Z' PASTC: LXI H,0 ;Clear HL DAD SP ;Get Stack Pointer value LXI SP,LCLSTAK ;Set up local stack PUSH H ;Save old SP on new stack ; LXI H,TBUFF ;point to Command Buffer MOV A,M ;get count INR A ;Point past end of string CALL HLXA ;Index the pointer MVI M,0 ;Insist on null terminator ; LXI H,TBUFF+1 ;base of command buffer CALL SCNB ;scan to first non-blank ORA A ;An argument present? JNZ EVALARG ;Yes, evaluate it. LXI H,1 ;Else default to one JMP EVALEXIT ;Don't do the loop EVALARG: XCHG ;Scan pointer to DE LXI H,0 ;initialize value EVALOOP: LDAX D ;Get character ORA A ;Terminator? JZ EVALEXIT ;exit loop ; CALL ISNUM ;Test range 0-9 ASCII JC NOTNUM ;argument not numeric SUI '0' ;Make it binary ; ; Multiply current value in HL by 10 CALL HMULT10 ; ; Add in new value from A CALL HLXA ; INX D ;bump argument pointer JMP EVALOOP ; EVALEXIT: ;Range test. Must be 1-127 LXI D,1 CALL DCMP JC EXIT ;Skip 0 lines=do nothing LXI D,128 CALL DCMP JNC RANGERR ; ; OK so far. Now skip over (L) lines in the .SUB file ; PUSH H ;Save the value CPM OPN,SUBFILE ;Open the $$$.SUB file. POP D ;Restore the value ; INR A ;Test return code. JZ SUBERR ;Not within a .SUB file?? LDA SUBFILE+FCBRC ;Record counter for the exte#nt MOV L,A ;Get the record counter to HL MVI H,0 ; CALL DSUB JC NELERR ;Not enough lines remaining ; MOV A,L ;The new counter value STA SUBFILE+FCBRC ;goes back into the FCB XRA A ;And a zero goes into STA SUBFILE+FCBS2 ;the S2 byte (traditionally) CPM CLS,SUBFILE ;Write change to directory. INR A ;Trouble? JZ SUBERR EXIT: ;Ok, all finished. POP H ;Old SP SPHL ;Restore Stack RET ;to Console Command Processor ; SUBERR: CALL ABEND DB 'Error accessing .SUB file.' DB '$' ; NOTNUM ;Argument is not numeric CALL ABEND DB '//SKIP argument not numeric.' DB '$' ; RANGERR: NELERR: CALL ABEND DB '//SKIP argument exceeds file size.' DB '$' ; ABEND: POP D ;Message address CPM MSG ;Send to console CPM DEL,SUBFILE ;Abort the jobstream CPM MSG,CANCEL JMP BOOT CANCEL: DB '...CANCELED' DB '$' ; ; ; Utility subroutines ; HLXA: ;Index HL by the value of A. Returned flags not defined ADD L MOV L,A ADC H SUB L MOV H,A RET ; SCNB: ;Scan over leading blanks. Return char in A MOV A,M CPI ' ' RNZ INX H JMP SCNB ; DCMP: ;Set borrow as for (HL)-(DE) MOV A,H CMP D RNZ MOV A,L CMP E RET ; DSUB: ;Do HL:=(HL)-(DE). Return meaningful borrow flag MOV A,L SUB E MOV L,A MOV A,H SBB D MOV H,A RET ; ISNUM: ;Return carry set if not ASCII decimal char 0-9. CPI '0' RC CPI '9'+1 CMC RET ; HMULT10:;Multiply HL by 10. Clobbers BC. MOV B,H MOV C,L DAD B ;*2 DAD H ;*4 DAD B ;*5 DAD H ;*10 RET ; SUBFILE: DB 1 ;Drive A: DB '$$$ SUB' DB 0,0,0,0 DS SUBFILE-$+36 ; DS 20 LCLSTAK EQU $ ; END SKIPPROG  ; SUBFILE: DB 1 ;Drive A: DB '$$$ SUB' DB 0,0,0,0 DS SUBFILE-$+36 ; DS 20 LCLSTAK EQU $ ; END SKIPROG  ; SUBFILE: DB 1 ;Drive A: DB '$$$ SUB' DB 0,0,0,0 DS SUBFILE-$+36 ; DS 20 LCLSTAK EQU $ ; END SKItines ; HLXA: ;Index HL by the value of A. Returned flags not defined ADD L MOV L,A ADC H SUB L MOV H,A RET ; SC!91*!|1 bytes total tpa space. $ !|f bytes before overlaying the ccp. $ _ څ ||{0o!9"17 >e  "*Waiting for any keyboard entry to continue: $!! څk=e*. $e*. $WAIT.COM is useful for causing SUBMIT streams to suspend so disks can be changed. It simply waits, beeping once every ten seconds or so, for someone to strike a key. Then it does a warm boot, resetting the drives to read/write. ng once every ten secnds or so, for someone to strike a key. Then it does a warm boot, resetting the drives to read/write. ng once every ten sec$ This is the release date of the disk. ALLOC COM BMAP7-11COM BMAP7-11ASM &BMAP COM CLS-ANY ASM CLS DOC  CLS-^Z COM CPMADR COM CPMCALC COM GKEY2 COM %GKEY2 DOC 'GK2SET COM , GSUB14 COM /GSUB DOC 1(CPMCALC .COM A4 9D 5376 42 GKEY2 .COM 51 47 1280 10 GKEY2 .DOC C3 22 4480 35 GK2SET .COM E4 CF 2432 19 GSUB14 .COM 84 ED 1920 15 GSUB .DOC 8B A1 10240 80 ELSEG .COM 68 B6 384 3 ENDIFG .COM 67 25 128 1 GOTOG .COM A9 38 384 3 IFG .COM 2F 78 1024 8 IFG .DOC DE F4 3968 31 IF .COM AD 74 768 6 IF  Fog Library Disk FOG-CPM.145 Copyright (1986) by Fog International Computer Users Group to the extent not copyrighted by the original author for the exclusive use and enjoyment of its members. Any reproduction or distribution for profit or personal gain is strictly forbidden. For information, contact FOG, P. O. Box 3474, Daly City, CA. 94015-0474. as part of the description of a file indicates that the program is distributed on a "try first, pay if you like it" basis. If you find the program(s) meet your need, please refer to the author's documentation for information on becoming a registered user. Only by registering and paying for the programs you like and use will the authors of such programs continue development. Often, more complete documentation, additional modules, and new releases are available only to registered users. CPM Utility Programs. Filename Description -09-00 .86 This is the release date of the disk. -CPM145 .DOC This is the description of the disk contents. ALERT .COM A5FD 1K Sounds the console bell and waits for keystroke. ALLOC .COM 4401 3K Displays disk bit map. Type ALLOC H for help. BMAP7-11.COM 3798 1K ver. 7.11 [Bit Map 1 of 3] Displays disk bit map of directory information. Assembler source code is included. BMAP7-11.ASM 0C6A 10K ver. 7.11 [Bit Map 2 of 3] BMAP .COM E4F7 1K ver. 7.11 [Bit Map 3 of 3] CLS-ANY .ASM EE94 2K [CLEAR SCREEN 1 of 3] Clear screen program with Assembler Source code and Documentation. CLS .DOC 6840 3K [CLEAR SCREEN 2 of 3] CLS-^Z .COM B3EB 1K [CLEAR SCREEN 3 of 3] CPMADR .COM 177A 6K Shows various CPM addresses. BDOS, CCP, BIOS, etc. CPMCALC .COM A49D 6K Calculates various CPM addresses. GKEY2 .COM 5147 2K ver. 2 [GKEY 1 of 3] A key redefinition program. Also included is the documentation, and a program to change the attention key for GKEY. GKEY2 .DOC C322 5K ver. 2 [GKEY 2 of 3] GK2SET .COM E4CF 3K ver. 2 [GKEY 3 of 3] GSUB14 .COM 84ED 2K ver. 1.4 [GSUB 1 of 7] An improved version of SUBMIT. GSUB .DOC 8BA1 10K ver. 1.4 [GSUB 2 of 7] ELSEG .COM 68B6 1K ver. 1.4 [GSUB 3 of 7] ENDIFG .COM 6725 1K ver. 1.4 [GSUB 4 of 7] GOTOG .COM A938 1K ver. 1.4 [GSUB 5 of 7] IFG .COM 2F78 1K ver. 1.4 [GSUB 6 of 7] IFG .DOC DEF4 4K ver. 1.4 [GSUB 7 of 7] IF .COM AD74 1K [IF 1 of 3] Allows use of IF statements in SUBMIT files. IF .ASM 19A1 9K [IF 2 of 3] IFSKIP .DOC 4F70 17K [IF 3 of 3] PRLMOV .ASM C9C9 4K Routine to re-locate Page Relocatable Files. RAMDSK .COM 5207 3K [Ram Disk 1 of 3] Add on module to create a small CPM 2.2 ramdisk, 20k for demonstration purposes. RAMDSK .DOC E8D5 4K  [Ram Disk 2 of 3] RAMDSK .ASM D72F 24K [Ram Disk 3 of 3] SAFRAM2 .COM DD8F 2K ver. 2 [Safe Ram 1 of 4] Allows you to protect an area of high memory from being overwritten. Documentation and Assembler source code are included in this set. SAFRAM .ASM 8138 18K ver. 2 [Safe Ram 2 of 4] SAFRAM .DOC 9E5E 7K ver. 2 [Safe Ram 3 of 4] SAFRAM .DSC AA67 1K ver. 2 [Safe Ram 4 of 4] SKIP .COM 28ED 1K [SKIP 1 of 2] For use in submit files. SKIP .ASM 6ACA 4K [SKIP 2 of 2] TPA .COM BE56 1K Shows the Transient Program Area available. WAIT .COM 1DC4 1K [WAIT 1 of 2] For use with submit files. Suspends operation and waits for keyboard input. WAIT .DOC 4A4B 1K [WAIT 2 of 2] ET ALTE ON DO WHILE diskno="&mdiskno".AND. .NOT. EOF() IF diskno="000" IF dfile="FOG-DOS" ? " %&'