Title Program Loader for the MD-1/2/3 using CP/M 2 (12_Sept_84) ; ; Copyright 1984 ; Morrow Designs, Inc ; San Leandro, Ca. .z80 aseg org 0100h ; This program allows you to batch command strings to the ; Ims facility. Additionally, you have the option to check for the ; existance of a file before the ims string is submitted which allows ; you to do validity checks. ; There are two major data structures that are used to drive this ; program. The first is a parameter block kept in page zero while the ; program is running. It consists of an ID word followed by the current ; iteration (0 to n) followed by the maximum number of iterations. ; The second data structure is a two dimensional array. The first ; dimension is set by the iteration counter. ; Index: (12_Sept_84) ;-------------------- ; ; Main_Line Main Line ; Check_Ccl Check the Cbios Compatibility Level ; Clear_Screen Clear the Console Screen ; Look_for_File Check for the existance of the key file ; Get_Base Return a Pointer to the Desired Entry ; Get_Position Return a Drive Position Code ; Get_Bios_Pntr Get One of the Bios Pointers ; Get_Response Get the User's Response ; Install_Ims Put the Ims string into the IMS buffer ; Report_Errors Report an Error on the Console ; Print_Message Print a Message on the Console page 64 ;---------------------------------------------------------------------- ; External Definitions (10_Sept_84) ;---------------------------------- ; 1) This variables are defined in the 'List' files ; External LodLst External LodLen ;---------------------------------------------------------------------- ; Equates (10_Sept_84) ;--------------------- ; ; IDs and Rev Numbers ;-------------------- ; CpmVer equ 20h ;Cpm Version Number Ccl equ 30h ;Cbios Compatibility Level Prev equ 11h ;Program revision number PID equ 'Ok' ;Program ID ; Address Definitions ;-------------------- ; Bios_Entry equ 0 ;Address of the Start of the BIOS + 3 warmbt equ 01h ;Address of the Bios Warm boot entry Number_Drives equ 04h ;Number of Physical Drives Bdos_Entry equ 05h ;Address of the start of the BDOS IBuf equ 80h ;tail buffer location PIDadd equ 40h ;Address of the ID word LodOff equ 42h ;Current Program Load offset LodMax equ 43h ;Maximum number of load files ErrFlag equ 44h ;Error Flag ; Bdos Function Numbers ;---------------------- ; Bdos_ConOut equ 2 ;Console Output a Character Bdos_DirIO equ 6 ;Direct console control Bdos_PString equ 9 ;Print a string Bdos_GetRev equ 0Ch ;Get revision number of cpm system Bdos_SFirst equ 11h ;Search for first occurance of file x Bdos_SetDMA equ 1Ah ;Set the DMA Address InWait equ 0FFh ;Direct console input ; Offsets ;-------- ; Ccl_Offset equ 3Ch ;Offset to Clevel from start of Bios+3 Inject_Offset equ 52h ;Offset to Inject IMS from start of Bios+3 Key_Offset equ 0 ;Key File Offset Ims_Offset equ 2 ;Ims Sting Offset RamDatY_Offset equ 42h ;RamDatY Pointer from Start of Jump Table Mtab_Offset equ 44h ;Mtab Pointer from Start of Jump Table ;Error String Codes Error_Opening equ 10 ;Error Intro Err_Revision equ 11 ;Wrong Revision of CP/M Bios Err_Wrong_Disk equ 12 ;Wrong Disk Err_Ims_Filled equ 13 ;Ims buffer has overflowed ;Message Strings Msg_Left equ 21 ;Left Hand Drive Msg_Lower equ 22 ;Lower Drive Msg_Right equ 23 ;Right Hand Drive Msg_Upper equ 24 ;Upper Drive Msg_Prompt equ 25 ;Initial Prompt Message Msg_Prompt_1 equ 26 ;Please Insert your... Msg_Prompt_2 equ 27 ;Diskette into your... Msg_Prompt_3 equ 28 ;disk drive Msg_Response equ 29 ;Response Prompt Message ; Non Printing Ascii Character Equates ;------------------------------------- ; CntrlC equ 3 ;Control C Code (Warm Boot) Bell equ 7 ;Bell Code Tab equ 9 ;Tab Lf equ 0Ah ;Line Feed Cr equ 0Dh ;Carriage Return PCr equ 8Dh ;Pause carriage return (see Install_Ims) ClrScr equ 1Ah ;Clear Screen Esc equ 1Bh ;Escape Bright equ '(' ;Start High Lighting Dim equ ')' ;Start Low Lighting ; Other Definitions ;------------------ ; Null equ 0FFh ;Null Key filespec prompt @Double_Sided equ 5 ;Double Sided Bit in Mtab's DskDef0 page ;---------------------------------------------------------------------- ; Main Line of the Floppy Disk Loader for Cpm 2.2 (10_Sept_84) ;------------------------------------------------------------- ; Main_Line: ld hl,(PIDadd) ;HL:= Contents of ID word location ld de,PID ;DE:= Program ID word or a ;(Clear carry) sbc hl,de ;If (ID hasn't been written) jr z,MSkp1 call Check_Ccl ; Check CP/M rev and compatibility or a ; If (Rev levels aren't high enough) jr nz,Mdone ; Quit call Clear_Screen ; Clear the Console Screen ld a,Msg_Prompt ; A:= Initial Prompt Message call Print_Message ; Print the initial prompt call Get_Response ; Get the user response or a ; If (Response eq Stop) jr nz,MDone ; Quit ld de,PID ld (PIDadd),de ; Write the ID ld a,0 ld (LodOff),a ; Reset the Load Offset ld (ErrFlag),a ; Reset the Error flag ld a,(LodLen) ld (LodMax),a ; Set Maximum number of load files MSkp1: ld a,(ErrFlag) ;see if previous copy O.K. or a jr z,CopyOk ld a,(LodOff) ;decrement Load Offset if Error dec a ld (LodOff),a CopyOk: call Look_for_File ;Get the user to load the next disk or a ;If (the user wants to stop) jr nz,MDone ; Quit call Install_Ims ;Install the Ims String or a ;If (There wasn't room for the Ims String) jp nz,MDone ; Quit ld a,(LodOff) inc a ld (LodOff),a ;Load Offset:= Load Offset + 1 ld b,a ld a,(LodMax) ;(A:= Last Load File Number) cp b ;If (this is the last file) jr nz,MSkp4 MDone: ld de,0 ld (PIDadd),de ; Reset the ID word MSkp4: jp Bios_Entry ;Goto the Bios Warm Boot Entry page ;====================================================================== ; Check the Cbios Compatibility Level (18_Jun_84) ;================================================ ; ;Check the Cpm Version Number Check_Ccl: ld c,Bdos_GetRev ;C:= Bdos get cpm version number call Bdos_Entry ;Get version number in L ld a,l ;A:= Cpm Version 14, 22 or 31 and 0F0h ;Mask off the minor revision number cp CpmVer ;If (version ne expected version) jr nz,CclErr ; Goto Error return ;Check the Cbios Compatibility Level ld hl,(Bios_Entry+1) ;HL:= Bios warm boot entry address ld de,Ccl_Offset ;DE:= offset to Cbios compatiblity level add hl,de ;HL:= Pointer to Cbios compatibility ld a,(hl) ;If (crnt compatibility ne Cbios Ccl) and 0F0h ; (Mask off minor revision number) cp Ccl jp nz,CclErr ; Goto Error return Cclok: ld a,0 ;A:= Successful Completion status ret ;Return CclErr: ld a,Err_Revision ;A:= revision error message call Report_Errors ;Print the Error ld a,0FFh ;A:= error return status ret ;Return page ;====================================================================== ; Clear the Console Screen (20_Jul_84) ;===================================== ; 1) This Routine Simply outputs a Carriage Return followed by 50 Line ; Feeds to the Console. ; 2) NONE of the Register Pairs are altered. ; Clear_Screen: push bc push de push hl ld c,Bdos_ConOut ;C:= Bdos Console Output Function Number ld e,Cr ;E:= Carriage Return call Bdos_Entry ;Print a Carriage Return ld b,50 ;B:= Character Counter ld c,Bdos_ConOut ;C:= Bdos Console Output Function Number CsSk1: push bc ;Repeat ld e,Lf ; E:= Line Feed call Bdos_Entry ; Print the Line Feed pop bc djnz CsSk1 ;Until (Character Counter - 1 eq 0) pop hl pop de pop bc ret ;Return page ;====================================================================== ; Prompt for Key file then Check search for it (10_Sept_84) ;========================================================== ; 1) This routine checks for the existance of a particular file on ; a particular disk. ; 2) If the File Name String is Null (0) then the file check is ; NOT preformed. ; Look_for_File: ld bc,Key_Offset ;Loop BC:= Offset to Prompt string call Get_Base ; HL:= Pointer Prompt string table base ld a,(hl) ; If (File Name string is Null) cp Null ld a,0 ret z ; Return (No Error) inc hl ; HL:= Source (Start of FCB) ld de,Local_Fcb ; DE:= Destination (Local FCB) ld bc,13 ; BC:= Length of Transfer ldir ; Transfer FCB for the Key File push hl ; (save pointer to prompt string) ld a,Msg_Prompt_1 ; A:= 'Please insert your... call Print_Message ; Print the Message pop de ; DE:= Pointer to prompt string ld c,Bdos_PString call Bdos_Entry ; Print title of diskette ld a,Msg_Prompt_2 ; A:= 'diskette into your... call Print_Message ; Print the Message ld c,Msg_Left ; C:= Left Hand Drive Message call Get_Position ; Get the Drive Position or a ; If (NOT a Singe Drive System) call nz,Print_Message ; Print the Message ld a,Msg_Prompt_3 ; A:= disk drive call Print_Message ; Print the Message call Get_Response ; Get response from console or a ; If (response eq Stop) ret nz ; Return ld c,Bdos_SetDMA ld de,Local_Buffer call Bdos_Entry ; Set the DMA Address ld c,Bdos_SFirst ld de,Local_Fcb call Bdos_Entry ; Find Key File On Selected Drive cp 0FFh ; If (key file was found) ld a,0 ret nz ; Return (Ok) ld a,Err_Wrong_Disk ; A:= Wrong Disk Error Message call Report_Errors ; Print the Error Message jr Look_for_File page ;---------------------------------------------------------------------- ; Return a Pointer to the Desired Entry (5_Feb_84) ;------------------------------------------------- ; 1) This routine uses the LodOff Value along with the Entry offset ; (passed in the BC pair) to form a pointer into the Load List. ; 2) The Load List is a two dimensional (n by 2 word) array. The First ; dimension of the array is the set by the current iteration number ; of the Loader Program (LodOff). The second dimension of the array ; allows access to the current Key file (and it prompt string) and ; the Ims Stings pointers. ; 3) Pointer offsets (passed in the BC pair): ; 0 = Key File Name ; 2 = Ims String Text ; 4) Register Usage: ; A -> General Purpose ; BC -> Pointer Offset or second dimension (0=Prompt,2=Key,4=Ims) ; DE -> used as Offset in calculating the first dimension ; HL -> Pointer to Desired Entry's Pointer ; Get_Base: ld a,(LodOff) sla a sla a ld e,a ld d,0 ;DE:= Offset - LodOff * 4 ld hl,LodLst ;HL:= Base of Load List add hl,de ;HL:= Pointer to Base of First Dimenstion add hl,bc ;HL:= Pointer to Second Dimension ld e,(hl) inc hl ld d,(hl) ;(retrieve the pointer from the table) ex de,hl ;HL:= Pointer to base of desired string ret ;Return page ;====================================================================== ; Get the Drive Position Code (11_Sept_84) ;========================================= ; 1) This Routine returns the accm equal to... ; 0 for a Single Drive System ; C_Reg + 0 for a Single Sided Double Drive System ; C_Reg + 1 for a Double Sided Double Drive System ; Get_Position: ld de,RamDatY_Offset call Get_Bios_Pntr ;DE:= Pointer to Start of RamDatY dec de dec de ;DE:= Pointer to Vnum ld a,(de) ;Update Vnum or a ;If (there's only one drive) ret z ; Return (A:= 0) ld de,Mtab_Offset call Get_Bios_Pntr ;DE:= Pointer to DskDef0 ld a,(de) bit @Double_Sided,a ;If (it a Single Sided System) ld a,c ; ret z ; Return (A:= C_Reg) inc a ;Else ret ; Return (A:= C_Reg + 1) page ;---------------------------------------------------------------------- ; Get One of the Bios Pointers (10_Sept_84) ;------------------------------------------ ; 1) This routine picks up one of the Vectors from the Bios Jump Table. ; 2) On Entry the DE register pair holds the offset to the desired entry ; 3) On Return the DE register pair holds the vector. ; 4) Register Usage: ; DE -> Enter:= Offset to Vector; Exit:= Vector ; HL -> Pointer to base of Bios (Cold Start Entry) ; Get_Bios_Pntr: ld hl,(Bios_Entry+1) ;HL:= Pointer to Warm Boot Entry ld l,0 ;HL:= Pointer to Base of Bios add hl,de ;HL:= Pointer to RamDatY Pointer ld e,(hl) inc hl ld d,(hl) ;DE:= Contents of Addr Pointed to by HL ret ;Return page ;---------------------------------------------------------------------- ; Get the User's Response (10_Sept_84) ;------------------------------------- ; 1) This routine prints a prompt string and then waits for a valid ; response from the console device. ; 2) A valid response is a Carriage Return to continue or a Control_C ; to stop. ; 3) Register Usage: ; AF -> Returned 0 for continue or 0FFh for Stop ; BC -> Bdos function number in the C register ; DE -> On Entry Points to the string to print ; Get_Response: ld a,Msg_Response ;A:= Response Prompt Message call Print_Message ;Issue the response prompt AskLp1º lä c,Bdos_DirIO ;Loop ld e,InWait ; (Set funct to wait for input) call Bdos_Entry ; Get a character from console and 5Fh ; (force to upper case) cp CntrlC ; If (Response eq Stop) jr nz,AskSk1 ld a,0FFh ; (Stop code) jr AskSk2 ; Break AskSk1: cp Cr ; Else If (Response eq Continue) jr nz,AskLp1 ld a,0 ; (Continue code) AskSk2: push af ;(Save the Continue/Stop Flag) ld de,CrLf ld c,Bdos_PString call Bdos_Entry ;Print Carriage Return Line Feed pop af ;A:= Response (0-Continue or 0FFh-Stop) ret ;Return page ;====================================================================== ; Put the Ims string into the IMS buffer (12_Sept_84) ;==================================================== ; 1) This routine puts the current iteration's ims string into the ; ims buffer. ; Install_Ims: ld bc,Ims_Offset ;BC:= Offset to IMS sting call Get_Base ;HL:= Pointer to the ims string push hl ;(save pointer on stack) ;Translate special characters in the Ims String ImsLp2: inc hl ;Repeat (move HL past the length byte) ld a,';' ; If (char eq Standard_Cr) cp (hl) jr nz,ImsSk2 ld (hl),Cr ; substitute Cr jr ImsSk3 ImsSk2: ld a,'|' ; Else If (char eq Pause_Cr) cp (hl) jr nz,ImsSk3 ld (hl),PCr ; substitute pause Cr ImsSk3: djnz ImsLp2 ;Until (all of buffer is processed) pop de ;DE:= pointer to start of processed buffer ;Inject the Ims string into the Ims buffer ld hl,(Bios_Entry+1) ;Get warm boot address ld bc,Inject_Offset ;offset to inject IMS routine add hl,bc ;add offset to HL call jphl ;call (HL) ld a,0 ;If (the ims string was accepted) ret nc ; Return (no error) ld a,Err_Ims_Filled ;Else A:= Ims Filled Error call Report_Errors ; Print the Error Message ld a,0FFh ret ; Return (ERROR) jphl: jp (hl) page ;====================================================================== ; Report Errors (5_Jun_84) ;========================= ; 1) Report Errors on the console. ; 2) Register Usage: ; A -> Has Error Code on entry ; Report_Errors: push af ld a,Error_Opening call Print_Message ;Print the Error Opening Message pop af call Print_Message ;Print the Error Message ret ;====================================================================== ; Print a Message on the Console (11_Sept_84) ;============================================ ; 1) This routine prints a message string on the console. ; 2) Register Usage: ; A -> Has Error Code on entry ; B -> Counter ; C -> Holds copy of Error Code ; DE -> Pointer to Message String ; HL -> Pointer into Message Code Table ; Print_Message: ld c,a ;C:= Error Code to Find ld b,Max_Table ld hl,Message_Table ;HL:= Start of the Message table PMLp1: ld a,(hl) ;Repeat A:= Table's Message Code inc hl ld e,(hl) inc hl ld d,(hl) ; DE:= Pointer to String inc hl ; (HL equal to start next entry) cp c ; If (Code eq table) jr z,PMprn ; Goto Print djnz PMLp1 ;Until (the whole tables been checked) ld de,NoCode ;(set pointer to 'Unrecognized Message') PMprn: ld c,Bdos_PString ; call Bdos_Entry ; Print the Message ret page ;---------------------------------------------------------------------- ; Message String Lookup Table (12_Sept_84) ;----------------------------------------- ; Message_Table: db Error_Opening ;Error Intro dw ErrOpn db Err_Revision ;Wrong Revision of CP/M Bios dw RVmsg db Err_Wrong_Disk ;Wrong Disk dw WDmsg db Err_Ims_Filled ;Ims buffer has overflowed dw IFmsg db Msg_Left ;Left Hand Drive dw LHmsg db Msg_Lower ;Lower Drive dw LOmsg db Msg_Right ;Right Hand Drive dw RHmsg db Msg_Upper ;Upper Drive dw UPmsg db Msg_Prompt ;Initial Prompt Message dw PRmsg db Msg_Prompt_1 ;Please Insert your... dw P1msg db Msg_Prompt_2 ;Diskette into your... dw P2msg db Msg_Prompt_3 ;disk drive dw P3msg db Msg_Response ;Response Prompt Message dw RSmsg End_Table: Max_Table equ ( (End_Table - Message_Table)/3 ) + 1 page ;---------------------------------------------------------------------- ; Text Strings (12_Sept_84) ;-------------------------- ; ErrOpn: db Cr,Lf,'*=> Error: $' RVmsg: db Cr,Lf,'Wrong Version of CP/M' db Cr,Lf,'This version of FDL Requires CP/M vers 2.2 Cbios rev ' db Ccl shr 4 + 30h,'.x$' WDmsg: db Cr,Lf,'This is NOT the proper disk$' IFmsg: db Cr,Lf,'There is insufficient space in the ims buffer$' LHmsg: db 'left hand $' LOmsg: db 'lower $' RHmsg: db 'right hand $' UPmsg: db 'upper $' PRmsg: db Esc,Dim,Lf,Lf,Lf,Tab,Tab db ' Micro Decision Distribution Diskette' db Cr,Lf,Lf,Tab db 'This is your ' db Esc,Bright db 'DISTRIBUTION ' db Esc,Dim db 'diskette. It is used to create a CP/M' db Cr,Lf,Tab db 'working diskette for your Micro Decision. You will need another' db Cr,Lf,Tab db 'diskette to become your ' db Esc,Bright db 'WORKING ' db Esc,Dim db 'diskette. This diskette need not' db Cr,Lf,Tab db 'be blank, but any information currently on it will be erased.' db Cr,Lf,Lf,Tab db 'It is very important that you keep these two diskettes straight.' db Cr,Lf,Lf,'$' P1msg: db Esc, Dim, Cr,Lf,Lf,Tab db 'Please insert your ' db Esc, Bright, Cr,Lf,Tab,'$' P2msg: db Esc, Dim, Cr,Lf,Tab db 'diskette into your $' P3msg: db 'disk drive. $' RSmsg: db Cr,Lf,Lf,Lf,Tab db 'Press ' db Esc,Bright db '[RETURN] ' db Esc,Dim db 'when you are ready to proceed ' db Esc,Bright,'$' CrLf: db Cr,Lf,'$' NoCode: db 'Unrecognized Message $' page ;---------------------------------------------------------------------- ; General Parameters (5_Feb_84) ;------------------------------ ; Local_Fcb: ds 32,0 Local_Buffer: ds 128,0 end ;---------------------------------------------------------------------- ; Text Message ;------------- ; Micro Decision Distribution Diskette This is your DISTRIBUTION diskette. It is used to create a CP/M working diskette for your Micro Decision. You will need another diskette to become your WORKING diskette. This diskette need not be blank, but any information currently on it will be erased. It is very important that you keep these two diskettes straight.'