C P / M 1 . 4 A L T E R A T I O N G U I D E Table of Contents Section Page l. INTRODUCTION 1 2. FIRST LEVEL SYSTEM REGENERATION 1 3. SECOND LEVEL SYSTEM REGENERATION 5 4. SAMPLE GETSYS AND PURSYS PROGRAMS 8 5. DISKETTE ORGANIZATION 9 6. THE BIOS ENTRY POINTS 11 7. A SAMPLE BIOS 17 8. A SAMPLE COLD START LOADER 17 9. RESERVED LOCATIONS IN PAGE ZERO 17 Appendix A. THE MDS LOADER MOVE PROGRAM B. THE MDS COLD START LMDER C. THE MDS BASIC I/0 SYSTEM (BIOS) D. A SKELETAL CBIOS E. A SKELETAL GETSYS/PUTSYS PROGRAM F. A SKELETAL COLD START LOADER CP/M System Alteration Guide 1. INTRODUCTION The standard CP/M system assumes operation on an Intel MDS microcomputer development system, but is designed so that the user can alter a specific set of subroutines which define the hardware operating enviornment. In this way, the user can produce a diskette which operates with a non-standard (but IBM-compatible format) drive controller and/or peripheral devices. In order to achieve device independence, CP/M is separated into three distinct modules: BIOS - basic I/0 system which is environment dependent BDOS - basic disk operating system which is not dependent unon the hardware configuration CCP - the console command processor which uses the BDOS of these mdules, only the BIOS is dependent upon the particular hardware. That is, the user can "patch" the distribution version of CP/M to provide a new BIOS which provides a customized interface between the remaining CP/M modules and the user's own hardware system. The purpose of this document is to provide a step-by-step procedure for patchinq the new BIOS into CP/M. The new BIOS requires some relatively simple software development and testing; the current BIOS, however, is listed in Appendix C, and can be used as a model for the customized packaqe. A skeletal version of the BIOS is given in Appendix D which can form the base for a modified BIOS. In addition to the BIOS, the user must write a simple memory loader, called GETSYS, which brings the operating system into memory. In order to patch the new BIOS into CP/M, the user must write the reverse of GETSYS, called PUTSYS, which places an altered version of CP/M back onto the diskette. PUTSYS is usually derived from GETSYS by chanqinq the disk read commands into disk write commands. Sample skeletal GETSYS and PUTSYS programs are described in Section 3, and listed in Appendix E. In order to make the CP/M system work automatically, the user must also supply a cold start loader, similar to the one provided wi CP/M (listed in Appendices A and B). A skeletal form of a cold start loader is given in Appendix F which can serve as a model for your leader. 2. FIRST LEVEL SYSTEM REGENERNTION The procedure to follow to patch the CP/M system is given below in several steps. Address references in each step are shown with a following "H" which denotes the hexadecimal radix, and are given for a 16K CP/M system. For larger CP/M systems, add a "bias" to each address which is shown with a "+b" following it, where b is actual to the memory size - 16K. Values for b in various standard memory sizes are 32K: b = 32K - 16K = 16K = 04000H 1 48K: b = 48K = 16K = 32K = 08000H 62K: b = 62K = 16K = 46K = 0B800H 64K: b = 64K = 16K = 48K = 0C000H (1) Review Section 4 and write a GETSYS program which reads the first two tracks of a diskette into memory. The data from the diskette must begin at location 2880H+b. Code GETSYS so that it starts at location 100H (base of the TPA), as shown in the first part of Appendix E. (2) Test the GE'ISYS program by reading a blank diskette into memory, and check to see that the data has been read properly, and that the diskette has not been altered in any way by the GETSYS program. (3) Run the GETSYS program using an initialized CP/M diskette to see if GETSYS loads CP/M startinq at 2880H+b (the operating system actually starts 128 bytes later at 2900H+b) (4) Review Section 4 and write the PUTSYS Program which writes memory starting at 2880H+b back onto the first two tracks of the diskette. The PU.RSYS proqram should be located at 200H, as shown in the second part of Appendix E. (5) Test the PUTSYS program using a blank uninitialized diskette by writing a portion of memory to the first two tracks; clear memory and read it back using GETSYS. Test PUTSYS completely, since this program will be used to alter CP/M on disk. (6) Study Sections 5, 6, and 7, along with the distribution version of the BIOS given in Appendix C, and write a simple version vhich performs a similar function for the customized environment. Use the program given in Appendix D as a model. Call this new BIOS by the name CBIOS (customized BIOS). Implement only the primitive disk operations on a single drive, and simple console input/output functions in this phase. (7) Test CBIOS completely to ensure that it properly performs console character I/0 and disk reads and writes. Be especially careful to ensure that no disk write operations occur accidently durinq read operations, and check that the proper track and sectors are addressed on all reads and writes. Failure to make these checks way cause distruction of the initialized CP/M system after it is patched. (8) Referring to Figure 1 in Section 5, note that the BIOS is located between locations 3E00H+b and 3FFFH+b. Read the CP/M system using GETSYS and replace the BIOS segment by the new CBIOS developed in step (6) and tested in step (7). This replacement is done in the memory of the machine, and will be placed on the diskette in the next step. (9) Use PUTSYS to place the patched memory image of CP/M onto the first two tracks of a blank diskette for testinq. 2 (10) Use GETSYS to bring the copied memory image from the test diskette back into memory at 2880H+b, and check to ensure that it has loaded @ck properly (clear memory, if possible, before the load). Upon,successful load, branch to the CCP module at location 2900H+b. The CCP will call the BDOS, which will call the CBIOS. The CBIOS will be asked to read several sectors on track 2 twice in succession, and, if successful, CP/M will type "A>". When you make it this far, you are almost on the air. If you have trouble, use whatever debug facilities you have available to trace and breakpoint your CBIOS. (11) Upon completion of step (10), CP/M has prompted the console for a command input. Test the disk write operation by typing SAVE 1 X.COM (recall that all commands must be followed by a carriage return). CP/M should respond with another prompt (after several disk accesses): A> If it does not, debug your disk write functions and retry. (12) Then test the directory command by typing DIR *.* CP/M should respond with X COM (13) Test the erase command by typing ERA X.COM CP/M should respond with the A prompt. When you make it this far, you have an operational system which only requires a bootstrap loader to function completely. (14) Write a bootstrap loader which is similar to GETSYS, and place it into read-only-memory, or into track 0, sector 1 usinq PUTSYS (again using the test diskette, not the distribution diskette). See Sections 5 and 8 for more information on the bootstrap operation. (15) Retest the new test diskette with the bootstrap loader installed by executing steps (11), (12), and (13). Upon completion of these tests, type a control-C (control and C keys simultaneously). The system should then execute a "warm start" which reboots the system, and types the A prompt. (16) At this point, you probably have a good version of your customized 3 CP/M system on your test diskette. Use GETSYS to load CP/M from your test diskette. Remove the test diskette place the distribution diskette (or a legal copy) into the drive, and use PUTSYS to replace the distribution version by your customized version. Do not make this replacement if you are unsure of your patch since this step destroys the system which was sent to you from Digital Research. (17) Load your modified CP/M system and test it by typing DIR CP/M should respond with a list of files which are provided on the initialized diskette. One such file should be the memory image for the debugger, called DDT.COM. NOTE: from now on, it is important that you always reboot the CP/M system when the diskette is removed and replaced by another diskette, unless the new diskette is read-only. (18) Load and test the debugger by typing DDT (see the document "CP/M Dynamic Debugging Tool (DDT)" for operating information and examples). Take time to familiarize yourself with DDT; it will be your best friend in later steps. (19) Before making further CBIOS modifications, practice using the editor (see the ED user's guide), and assembler (see the ASM user's guide). Then recode and test the GETSYS, RJTSYS, and CBIOS programs using ED, ASM, and DDT. Code and test a COPY program which does a sector-to-sector copy from one diskette to another to obtain back-up copies of the original diskette (NOTE: read your CP/M Licensing Agreement; it specifies your legal responsibilities when copying the CP/M system). Place the copyright notice Copyright (c) 1976 Digital Research on each copy vbich is made with your COPY program. (20) Modify your CBIOS to include the extra functions for punches, readers, siqnon messages, and so-forth, and add the facilities for a second drive, if it exists on your system. You can make these changes with the GETSYS and PUTSYS programs which you have developed, or you can refer to the following section, which outlines CP/M facilities which will aid you in the regeneration process. You now have a good copy of the customized CP/M system. Note that although the CBICS portion of CP/M which you have developed belongs to you, the modified version of CP/M which you have created can be copied for your use only (again, read your Licensing Agreement), and cannot be legally copied for 4 anyone else's use. If you wish, you may send vou name and address to Digital Research, along with a description of your hardware environment and the modifications which you have made. Diaital Research will make the information available to other interested parties, and inform them of the prices and availability of your CBIOS. It should be noted that your system remains file-compatible with all other CP/M systems, which allows transfer of non-proprietary software between users of CP/M. 3. SECOND LEVEL SYSTEM GENERATION Now that you have the CP/M system running, you may wish to use CP/M facilities in the system regeneration process. In general, we will first qet a memory image of CP/M from the first two tracks of an initialized diskette and place this memory image into a named disk file. The disk file can then be loaded, examined, patched, and replaced using the editor, assembler, debugger, and system generation program. The SYSGEN program, supplied with your diskette, is first used to get a CP/M memory image from the first two tracks. Run the SYSGEN program as shown below SYSGEN start the SYSGEN program *SYSGEN VERSION 1.0 SYSGEN siqnon messace GET SYSTEM (Y/N)?Y Answer yes to GET request SOURCE ON B, THEN TYPE RETURN at this point, place an initialized diskette into drive B and type a return (if you are operating with a single drive, answer "A" to the GET request, rather than "Y", and place the initialized diskette into drive A before typinq the return). The program should respond with: FUNCTION COMPLETE Load is complete PUT SYSTEM (Y/N)?N Answer no to PUT request system will automatically reboot at this point, with the memory image loaded into memory starting at location 900H and ending at 207FH in the transient program area. The memory image for CP/M can then be saved (if you are operating with a single drive, replace your original diskette and reboot). The save operation is accomplished by typing: SAVE 32 CPM.COM Save 20H = 32 paqes of memory The memory image created by the GET function is offset by a negative bias so that it loads into the free area of the TPA, and thus does not interfere with the operation of CP/M in higher memory. This memory image can be subsequently loaded under DDT and examined or chanqed in preparation for a new generation of the svstem. DDT is loaded with the memory image by typing 5 DDT CPM.COM Load DDT, then read the CPM image DDT should respond with NEXT PC 2100 0100 You can then use the display and disassembly commands to examine portions of the memory image between 900H and 207FH. Note, however, that to find any particular address within the memory image, you must apply the negative bias to the CP/M address to find the actual address. Track 00, sector 01 is loaded to location 900H (you should find the cold start loader at 900H to 97FH), track 00, sector 02 is loaded into 980H (this is the base of the CCP), and so-forth through the entire CP/M system load. In a 16K system, for example, the CCP resides at the CP/M address 2900H, but is placed into memory at 980H by the SYSGEN program. Thus, the negative bias, denoted by n, satisfies 2900H + n = 980H, or n = 980H - 2900H Assuming two's canplement arithmetic, n = 0E080H, which can be checked by 2900H + 0E080H = 10980H = 0980H (iqnorinq high-order overflow). Note that for larger systems, n satisfies (2900H+b) + n = 980H, or n = 980H - (2900H + b), or n = 0E080H - b. The value of n for common CP/M systems is given below memory size bias b negative offset n 16K 0000H 0E080H - 0000H = 0E0B0H 32K 4000H 0E0B0H - 4000H = 0A080H 48K 8000H 0E080H - 8000H = 6080H 62K 0B800H 0E080H - 0B800H = 2880H 64K 0C000H 0E080H - OC000H = 2080H Assume, for example, that you want to locate the address x within the memory image loaded under DDT in a 16K system. First type Hx,n Hexadecimal sum and difference and DDT will respond with the value of x+n (sum) and x-n (difference). The first number printed by DDT will be the actual memory address in the image where the data or code will be found. The input H2900,E080 6 for example, will produce 980H as the sum, which is where the CCP is located in the memory image under DDT. Use the L command to disassemble portions of your CBIOS located at (3E00H+b)-n which, when you use the H command, produces an actual address of 1E80H. The disassembly command would thus be L1E80 Terminate DDT by "inq a control-c or "G0" in order to prepare the patch program. Your CBIOS, for example, can be modified using the editor, and assembled usinq ASM, producing a file called CBIOS.HEX which contains the Intel formatted machine code for CBIOS in "hex" format. In order to integrate your new CBIOS, return to DDT by typing DDT CPM.COM Start DDT and load the CPM image Examine the area at 1E80H where the previous version of the CBIOS resides. Then type ICBIOS.HEX Ready the "hex" file for Loading Assume that your CBIOS is being integrated into a 16K CP/M system, and is thus "org'ed" at location 3E00H. In order to properly locate the CBIOS in the memory image under DDT, we must apply the negative bias n for a 16K system when loading the hex file. This is accomplished by typing RE080 Read the file with bias 0E080H Upon completion of the read, re-examine the area where the CBIOS has been loaded (use a "L1E80" command), to ensure that it was loaded properly. When you are satisfied that the patch has been made, return from DDT usinq a control-c or "G0" canmand. Now use SYSGEN to replace the patched memory imaqe back onto a diskette (use a test diskette until you are sure of your patch), as shown in the following interaction SYSGEN Start the SYSGEN program *SYSGEN VERSION 1.0 Siqnon message from SYSGEN GET SYSTEM (Y/N)?N Answer no to GET reauest PUT SYSTEM (Y/N)?Y Answer yes to PUT request DESTINATION ON B, THEN TYPE RETURN Place the test diskette on drive B (if you are operating with a single drive system, answer "A" rather than "Y" to the PUT request, then remove vour diskette, and replace by the test diskette), and type a return. The system will be replaced on the test diskette, and the system will automatically boot from drive A. Test the new CP/M system, and place the Digital Research copyriqht notice 7 on the diskette, as specified in your Licensinq Aqreement: Copyriqht (c), 1976 Diqital Research 4. SAMPLE GETSYS AND PUTSYS PROGRAMS The followirxg program provides a framework for the GEISYS and PURSYS programs referenced in Section 2. The READSEC and WRITESEC subroutines must be inserted by the user to read and write the specific sectors. ; GETSYS PROGRAM READ TRACKS 0 AND 1 TO MEMORY AT 2880H ; REGISTER USE ; A (SCRATCH REGISTER) ; B TRACK COUNT (0, 1) ; C SECTOR COUNT,(1,2,...,26) ; DE (SCRATCH REGISTER PAIR) ; HL LOAD ADDRESS ; SP SET TO STACK ADDRESS ; START: LXI SP,2880H ;SET STACK POMER TO SCRATCH AREA LXI H, 2880H ;SET BASE LOAD ADDRESS MVI B, 0 ;START WITH TPACK 0 RDTRK: ;READ NEXT TRACK (INITIALLY 0) MVI C,1 ;READ STARTING WITH SECTOR 1 PDSEC: ;READ NEXT SBCMR CALL READSEC ;USER-SUPPLIED SUBROUTINE LXI D,128 ;MOVE LOAD ADDRESS TO NEXT 1/2 PAGE DAD D ;HL = HL + 128 INR C ;SECTOR = SECTOR + 1 MOV A,C ;CHECK FOR END OF TRACK CPI 27 JC RDSEC ;CARRY GENERATED IF SECTOR < 27 ; ARRIVE HERE AT END OF TRACK, MOVE TO NEXT TRACK INR B MOV A,B ;TEST FOR LAST TRACK CPI 2 JC RDTRK ;CARRY GENERATED IF TRACK < 2 ; ; ARRIVE HERE AT END OF LOAD, HALT FOR NOW HLT ; ; USER-SUPPLIED SUBROUTINE TO READ THE DISK READSEC: ; ENTER WITH TRACK NUMBER IN REGISTER B, ; SECTOR NUMBER IN REGISTER C, AND ; ADDRESS TO FILL IN HL ; 8 PUSH B ;SAVE B AND C REGISTERS PUSH H ;SAVE HL REGISTERS ........................................... perform disk read at this point, branch to label START if an error occurs ........................................... POP H ;RECOVER HL POP B ;RECOVER B AND C REGISTERS RET ;BACK TO MAIN PROGRAM END START Note that this program is assembled and listed in Appendix D for reference purposes, with an assumed oriain of 100H. The hexadecimal operation codes which are listed on the left may be useful if the program has to be entered throuqh your machine's front panel switches. The PUTSYS proqram can be constructed from GETSYS by chanqing only a few operations in the GETSYS program qiven above, as shown in Appendix E. The register pair HL become the dump address (next address to write), and operations upon these registers do not change within the program. The READSEC subroutine is replaced by a WITESEC subroutine which performs the opposite function: data from address HL is written to the track given by reqister B and sector given by register C. It is often useful to combine GETSYS and PUTSYS into a single proqram during the test and development phase, as shown in the Appendix. 5. DISKETRE ORGANIZATION The sector allocation for the distribution version of CP/M is given here for reference purposes. The first sector (see Fiqure 1) contains an optional software boot section. Disk controllers are often set up to bring track 0, sector 1 into memory at a specific location (often location 0000H). The proqram in this sector, called LBOOT has the responsibility of bringing the rernaining, sectors into memory startinq at location 2900H+b. If your controller does not have a built-in sector load, you can iqnore the program in track 0, sector 1, and beqin the load from track 0 sector 2 to location 2900H+b. As an example, the Intel MDS hardware cold start loader brinqs track 0, sector 1 into absolute address 3000H. Thus, the distribution version contains two very small programs in track 0, sector 1: MBOOT - a storaqe move proqram which moves LBOOT into place following the cold start (Appendix A) LBOOT - the cold start boot loader (Appendix B) Upon MDS start-up, the 128 byte segment on track 0, sector 1 is brouqht 9 into 3000H. The MBOOT program gets control, and moves the LBOOT proqram from location 301EH down to location 80H in memory, in order to qet out the the area where CP/M is loaded in a 16K system. Note that the MBOOT program would not be needed if the MDS loaded directly to 80H. In general, the program could be located anyvhere below the CP/M load location, but is most often located in the area between 000H and 0FFH (below the TPA). After the move, MBOOT transfers to LBOOT at 80H. LBOOT, in turn, loads the remainder of track 0 and the initialized portion of track 1 to memory, starting at 2900H+b. The user should note that MBOOT and LBOOT are of little use in a non-MDS environment, although it is useful to study them since some of their actions will have to be duplicated in your cold start loader. Figure 1. Diskette Allocation Track# Sector# Page# Memory Address CP/M Module name ----------------------------------------------------------------- 00 01 (boot address) Cold Start Loader ----------------------------------------------------------------- 00 02 00 2900H+b CCP " 03 " 2980H+b " " 04 01 2A00H+b " " 05 " 2A80H+b " " 06 02 2B00H+b " " 07 " 2B80H+b " " 08 03 2C00H+b " " 09 " 2C80H+b " " 10 04 2D00H+b " " 11 " 2D80H+b " " 12 05 2E00H+b " " 13 " 2E80H+b " " 14 06 2F00H+b " " 15 " 2F80H+b " " 16 07 3000H+b " " 17 " 3080H+b " " 18 08 3100H+b " 00 19 " 3180H+b CCP ------------------------------------------------------------------- 00 20 09 3200H+b BDOS " 21 " 3280H+b " " 22 10 3300H+b " " 23 " 3380H+b " " 24 11 3400H+b " " 25 " 3480H+b " " 26 12 3500H+b " 01 01 " 3580H+b " " 02 13 3600H+b " " 03 " 3680H+b " " 04 14 3700H+b " " 05 " 3780H+b " 10 " 06 15 3800H+b " " 07 " 3880H+b " " 08 16 3900H+b " " 09 " 3980H+b " " 10 17 3A00H+b " " 11 " 3A8OH+b " " 12 18 3B00H+b " " 13 " 3B80H+b " " 14 19 3C00H+b " " 15 " 3C80H+b " " 16 20 3D00H+b " " 17 " 3D80H+b BDOS ----------------------------------------------------------------- 01 18 21 3E00H+b BIOS " 19 " 3E80H+b " " 20 22 3F00H+b " 01 21 " 3F80H+b BIOS ----------------------------------------------------------------- 01 22-26 (not currently used) ----------------------------------------------------------------- 02-76 01-26 (directory and data) ----------------------------------------------------------------- 6. THE BIOS ENTRY POINTS The entry points into the BIOS from the cold start loader and BDOS are detailed below. Entry to the BIOS is throuqh a "jump vector" between locations 3E00H+b and 3E2CH+b, as shown below (see also Appendices, pages C-2 and D-1). The jump vector is a sequence of 15 jump instructions which send program control to the individual BIOS subroutines. The BIOS subroutines may be empty for certain functions (i.e., they may contain a single RET operation) during regeneration of CP/M, but the entries must be present in the jump vector. It should be noted that there is a 16 byte area reserved in page zero (see Section 9) starting at location 40H, which is available as a "scratch" area in case the BIOS is implemented in ROM by the user. This scratch area is, never accessed by any other CP/M subsystem during operation. The jump vector at 3E00H+b takes the form shown below, where the individual jump addresses are given to the left: 3E00H+b JMP BOOT ;ARRIVE HERE FROM COLD START LOAD 3E03H+b imp WBOOT ;ARRIVE HERE FOR WARM START 3E06H+b JMP CONST ;CHECK FOR CONSOLE CHAR READY 3E09H+b JMP CONIN ;READ CONSOLE CHARACTER IN 3E0CH+b JMP CONOUT ;WRITE CONSOLE CHARACTER OUT 3E0FH+b JMP LIST ;WRITE LISTING CHARACTER OUT 3E12H+b JMP PUNCH ;WRITE CHARACTER TO PUNCH DEVICE 3E15H+b JMP READER ;READ READER DEVICE 11 3E18H+b JMP HOME ;MOVE TO TRACK 00 ON SELECTED DISK 3E1BH+b JMP SELDSK ;SELECT DISK DRIVE 3ElEH+b JMP SETTRK ;SET TRACK NUMBER 3E21H+b JMP SETSEC ;SET SECTOR NUMBER 3E24H+b JMP SETDMA ;SET DMA ADDRESS 3E27H+b JMP READ ;READ SELECTED SECTOR 3E2AH+b JMP WRITE ;WRITE SELECTED SECTOR Each jump address corresponds to a particular subroutine which performs the specific function, as outlined below. There are three mjor divisions in the jump table: the system (re)initialization which results from calls on BOOT and WBOOT, simple character I/0 performed by calls on CONST, CONIN, CONOUT, LIST, PUNCH, and READER, and diskette I/0 performed by calls on HOME, SELDSK, SETTRK, SETSEC, SETDMA, READ, and WRITE. All simple character I/0 operations are assumed to be performed in ASCII, upper and lower case, with high order (parity bit) set to zero. An end-of-file condition is given by an ASCII control-z (1AH). Peripheral devices are seen by CP/M as "logical" devices, and are assigned to physical devices within the BIOS. In order to operate, the BDOS needs only the CONST, CONIN, and CONOUT subroutines (LIST, PUNCH, and READER are used by PIP, but not the BDOS). Thus, the initial version of CBIOS may have empty subroutines for the remaining ASCII devices. The characteristics of each device are CONSOLE The principal interactive console which communicates with the operator, accessed through CONST, CONIN, and CONOUT. Typi- cally, the CONSOLE is a device such as a CRT or Teletype. LIST The principal listing device, if it exists on your system, which is usually a hard-copy device, such as a printer or Teletype. PUNCH The principal tape punching device, if it exists, which is normally a high-speed paper tape punch or Teletype. RFADER The principal tape reading device, such as a simple optical reader or Teletype. Note that a single peripheral can be assigned as the LIST, PUNCH, and READER device simultaneously. If no peripheral device is assiqned as the LIST, PUNCH, or READER device, the CBIOS created by the user should qive an appropriate error message so that the system does not "hang" if the device is accessed by PIP or some other user program. For added flexibility, the user can or)tionally implement the "iobyte" function which allows reassignment of physical and logical devices. The 12 iobyte function creates a mappinq of loqical to physical devices which can be altered during CP/M processing. The definition of the iobyte function corresponds to the Intel standard as follows: a sinqle location in memory (currently location 0003H) is maintained, called IOBYTE, which defines the logical to physical device mapping wbich is in effect at a particular time. The mappinq is performed by splitting the IOBYTE into four distinct fields of two bits each, called the CONSOLE, READER, PUNCH, and LIST fields, as shown below most significant least significant ----------------------------------------- IOBYTE AT 0003H | LIST | PUNCH | READER | CONSOLE | ----------------------------------------- bits 6,7 bits 4,5 bits 2,3 bits 0,1 The value in each field can be in the ranqe 0-3, defining the assiqned source or destination of each Logical device. The values which can be assigned to each field are given below CONSOLE field (bits 0,1) 0 - console is assiqned to the Teletype device (TTY) 1 - console is assiqned to the CRT device (CRT) 2 - batch mode: use the READER as the CONSOLE input, and the LIST device as the CONSOLE output 3 - user defined console device READER field (bits 2,3) 0 - READER is the Teletype device 1 - READER is the high-speed reader device (RDR) 2 - user defined reader # 1 3 - user defined reader # 2 PUNCH field (bits 4,5) 0 - PUNCH is the Teletype device 1 - PUNCH is the high speed punch device (PUN) 2 - user defined punch # 1 3 - user defined punch # 2 LIST field (bits 6,7) 0 - LIST is the Teletype device 1 - LIST is the CRT device 2 - LIST is the line printer device 3 - user defined list device Note again that the implementation of the IOBYRE is optional, and affects only the organization of your CBIOS. No CP/M systems use the IOBYTE (althouqh they tolerate the existence of the IOBYTE at location 0003H), except for PIP which allows access to the TTY: and CRT: devices. If you do not implement the ICBYTE, you cannot access these physical devices throuqh PIP. In any case, the IOBYTE implementation should be omitted until your basic CBIOS is fully 13 implemented and tested; then add the IOBYTE to increase your facilities. Disk I/0 is always performed through a sequence of calls on the various disk access subroutines which set up the disk number to access, the track and sector on a particular disk, and the direct memory access (DMA) address involved in the I/0 operation. After all these parameters have been set up, a call is made on the READ or WRITE function to perform the actual I/0 operation. Note that there is often a single call to SELDSK to select a disk drive, followed by a number of read or write operations to the selected disk before selecting another drive for subsequent operations. Similarly, there may be a single call to set the DMA address, followed by several calls which read or write from the selected DMA address before the DMA address is changed. The track and sector subroutines are called before the read and write operations are performed. Note, however, that the BIOS does not attempt error recovery when a read or write fails, but instead reports the error condition to the BDOS. The BDOS then retries the read or write, assuming the track and sector address remain the same. The HOME subroutine may be called during error recovery, following by a re-seek of the particular track and sector. The HOME subroutine may or may not actually perform the track 00 seek, depending upon your controller characteristics; the important point is that track 00 has been selected for the next operation, and is often treated in exactly the same manner as SETTRK with a parameter of 00. The exact responsibilities of each entry point subroutine are given below: BDOT The BOOT entry point gets control from the cold start loader and is responsible for basic system initialization, includ- ing sending a signon message (which can be omitted in the first version). If the IOBYTE function is implemented, it must be set at this point. The various system parameters which are set by the WBOOT entry point must be initialized, and control is transferred to the CCP at 2900H+b for further processing. WBOOT The WBOOT entry point gets control when a warm start occurs. A warm start is performed whenever a user program branches to location 0000H, or when the CPU is reset from the front panel. The CP/M system must be loaded from the first two tracks of drive A up to, but not including, the BIOS (or CBIOS, if you have completed your patch). System parameters must be ini- tialized as shown below: location 0,1,2 set to JTMP WBOOT for warm starts (0000H: JMP 3E03H+b) location 3 set initial value of IOBYTE, if implemented in your CBIOS location 5,6,7 set to JMP BDOS, which is the primary entry point to CP/M for transient proqrams. (0005H: JMP 3206H+b) (see Section 9 for complete details of page zero use) 14 Upon completion of the initialization, the WBOOT proqran must branch to the CCP at 2900H+b to (re)start the system. Upon entry to the CCP, register C is set to the drive to select after system initialization (normally drive A is selected by setting register C to zero). CONST Sample the status of the currently assigned console device and return a 0FFH in register A if a character is ready to read, and 00H in register A if no console characters are ready. CONIN Read the next console character into register A, and set the parity bit (high order bit) to zero. If no console character is ready, wait until a character is typed before returning. CONOUT Send the character from register C to the console output de- vice. The character is in ASCII, with high order parity bit set to zero. You may want to include a time-out on a line feed or carriage return, if your console device requires some time interval at the end of the line (such as a TI Silent 700 terminal). You can, if you wish, filter out control char- acters vhich cause your console device to react in a strange way (a control-z causes the Lear Seigler terminal to clear the screen, for example). LIST Send the character from register C to the currently assigned listing device. Ihe character is in ASCII with zero parity. PUNCH Send the character from register C to the currently assiqned pinch device. The character is in ASCII with zero parity. READER Read the next character from the currently assigned reader de- vice into register A with zero parity (high order bit must be zero), an end of file condition is reported by returning an ASCII control-z (1AH). HOME Return the disk head of the currently-selected disk (initially disk A) to the track 00 position. If your controller allows access to the track 0 flag from the drive, step the head until the track 0 flag is detected. If your controller does not support this feature, you can translate the HOME call into a call on SETTRK with a parameter of 0. SELDSK Select the disk drive given by register C for further opera- tions, where reqister C contains 0 for drive A, and 1 for drive B (the standard CP/M distribution version supports a maximum of two drives). If your system has only one drive, you may wish to give an error message at the console, and terminate execution. You can, if you wish, type a message at the console to switch diskettes to simulate a two drive 15 system. In this case, you must keep account of the current drive and type an appropriate messaqe when the drive chanaes. SEEK Register C contains the track number for subsequent disk accesses on the currently selected drive. You can choose to seek the selected track at this time, or delay the seek until the next read or write actually occurs. Register C can take on values in the range 0-76 corresponding to valid track numbers. SETSEC Register C contains the sector number (1 through 26) for sub- secjuent disk accesses on the currently selected drive. You can choose to send this information to the controller at this point, or instead delay sector selection until the read or write operation occurs. SETDMA Registers B and C (high order 8 bits in B, low order 8 bits in C) contain the DMA (direct memory access) address for sub- sequent read or write operations. For example, if B = 00H and C = 80H when SETDMA is called, then all subsequent read operations fill their data into 80H throuqh 0FFH, and all subsequent write operations get their data from 80H through 0FFH, until the next call to SETDMA occurs. The initial DMA address is assumed to be 80H. Note that the controller need not actually support direct memory access. If, for example, all data is received and sent through I/0 ports, the CBIOS which you construct uses the 128 byte area starting at the selected DMA address for the memory buffer during the I/0 operation. READ Assuminq the drive has been selected, the track has been set, the sector has been set, and the DMA address has been specified, this subroutine attempts to read the selected sector. The read operation may involve several retries (10 is a qood number) if errors occur durinq the read operation. If the read is completed correctly, the READ subroutine should return a 00 in reqister A. If the read cannot be performed, a 01 should be returned: in this case CP/M prints the message PERM ERROR DISK x. where x is the disk number. 16 WRITE Write the data from the currently selected DMA address to the currently selected drive, track, and sector. The data should be marked as "non deleted data" to maintain compatibility with other CP/M systems. The error codes qiven in the READ command are returned in register A, with error recovery at- tempts as described above. 7. A SAMPLE BIOS The program shown in Appendix D can serve as a basis for your first BIOS. The simplest functions are assumed in this BIOS, so that you can enter it through the front panel, if absolutely necessary. Note that the user must alter and insert code into the subroutines for CONST, CONIN, CONOUT, READ, WRITE, and WAITIO subroutines. Storaqe is reserved for user-supplied code in these regions. The scratch area reserved in page zero (see Section 9) for the BIOS is used in this program, so that it could be imolemented in ROM, if desired. Once operational, this skeletal version can be enhanced to print the initial sign-on message and perform better error recovery. The subroutines for LIST, PUNCH, and READER can be filled-out, and the IOBYTE function can be implemented. 8. A SAMPLE COLD START LOADER The program shown in Appendix E can serve as a basis for your cold start loader. The disk read function must be supplied by the user, and the proaram must be loaded somehow starting at location 0000. Note that space is reserved for your patch so that the total amount of storage required for the cold start loader is 128 bytes. Eventually, you will probably want to get this loader onto the first disk sector (track 0, sector 1) , and cause your controller to load it into memory automatically upon system start-up. Alternatively, you may wish to place the cold start loader into ROM, and place it above the CP/M system. In this case, it will be necessary to originate the proqram at a higher address, and key-in a jump instruction at system start-up which branches to the loader. Subsequent warm starts will not require this key-in operation, since the entry point 'WBOOT' gets control, thus bringing the system in from disk automatically. Note also that the skeletal cold start loader has minimal error recovery, which may be enhanced on later versions. 9. RESERVED LOCATIONS IN PAGE ZERO Main memory page zero, between locations 00H and 0FFH, contains several segments of code and data which are used during CP/M processing. The code and 17 data areas are given below for reference purposes. Locations Contents from to 0000H - 0002H Contains a jump instruction to the warm start entry point at location 3E03H+b. This allows a simple programmed restart (JMP 0000H) or manual restart from the front panel. 0003H - 0003H Contains the Intel standard IOBYTE, which is optionally included in the user's CBIOS, as described in Section 6. 0004H - 0004H (not currently used - reserved) 0005H - 0007H Contains a jump instruction to the BDOS, and serves two purposes: JMP 0005H provides the primary entry coint to the BDOS, as described in the manual "CP/M Interface Guide," and LHLD 0006H brings the address field of the instruction to the HL register pair. This value is the lowest address in memory used by CP/M (assuming the CCP is beinq overlayed). Note that the DDT program will chanqe the address field to reflect the reduced memory size in debug mode. 0008H - 0027H (interrupt locations 1 through 5 not used) 0030H - 0037H (interrupt location 6, not currently used - reserved) 0038H - 003AH Contains a jump instruction into the DDT program when running in debug mode for programmed breakpoints, but is not otherwise used by CP/M. 003BH - 003FH (not currently used - reserved) 0040H - 004FH 16 byte area reserved for scratch by CBIOS, but is not used for any purpose in the distribution version of CP/M 0050H - 005BH (not currently used - reserved) 005CH - 007CH default file control block produced for a transient pro- gram by the Console Command Processor. 007DH - 007FH (not currently used - reserved) 0080H - OOFFH default 128 byte disk buffer (also filled with the com- mand line when a transient is loaded under the CCP). Note that this information is set-up for normal operation under the CP/M system, but can be overwritten by a transient program if the BDOS facilities are not reguired by the transient. If, for example, a particular program 18 performs only simple 1/0 and must beain execution at location 0, it can be first loaded into the TPA, using normal CP/M facilities, with a small memory move program which gets control when loaded (the memory move program must qet control from location 100H, which is the assumed beginning of all transient proqrams). The move program can then proceed to move the entire memory imaqe down to location 0, and pass control to the starting address of the memory load. Note that if the BIOS is overwritten, or if location 0 (containing the warm start entry point) is overwritten, then the programmer must bring the CP/M system back into memory with a cold start sequence. 19 ; MDS LOADER MOVE PROGRAM, PLACES COLD START BOOT AT BOOTB ; 3000 ORG 3000H ;WE ARE LOADED HERE ON COLD START 0080 = BOOTB EQU 80H ;STARR OF COLD BOOT PROGRAM 0080 = BOOTL EQU 80H ;LENGTH OF BOOT D900 = MBIAS EQU 900H-$ ;BIAS TO ADD DURING LOAD 0078 = BASE EQU 078H ;'BASE' USED BY DISK CONTROLLER 0079 = RTYPE EQU BASE+1 ;RESULT TYPE 007B = RBYTE EQU BASE+3 ;RESULI TYPE ; OOFE = BSW EQU 0FFH ;BOOT SWITCH ; ;CLEAR DISK STATUS 3000 DB79 IN RTYPE 3002 DB7B IN RBYTE ; COLDSTART: 3004 DBFF IN BSW 3006 E602 ANI 2H ;SWITCH ON? 3008 C20430 JNZ COLDSTART ; 300B 211E30 LXI H,BOOTV ;VIRTUAL BASE 300E 0680 MVI B,BOOTL ;LENGTH OF BOOT 3010 118000 LXI D,BOOTB ;DESTINATION OF BOOT 3013 7E MOVE: MOV A,M 3014 12 STAX D ;TRANSFERRED ONE BYTE 3015 23 INX H 3016 13 INX D 3017 05 DCR B 3018 C21330 JNZ MOVE 301B C38000 JMP BOOTB TO BOOT SYSTEM ; BOOTV: ;BOOT LOADER PLACE HERE AT SYSTEM GENERATICN 089E = LBIAS EQU $-80H+MBIAS ;COLD START BOOT BEGINS AT 80H 301E END A-1 ;MDS COLD START LOADER FOR CP/M 0000 = FALSE EQU 0 FFFF = TRUE EQU NOT FALSE 0000 = TESTING EQU FALSE ;IF TRUE, THEN GO TO MON80 ON ERRORS ; 0010 = MSIZE EQU 16 ;MEMORY SIZE IN KILOBYTES 2000 = CBASE EQU (MSIZE-8)*1024 ;CPM BASE ADDRESS BIAS BEYOND 8K 2900 = BDOSB EQU CBASE+900H ;BASE OF DOS LOAD 3206 = BDOS EQU CBASE+1206H ;ENTRY OF DOS FOR CALLS 4000 = BDOSE EQU MSIZE*1024 ;END OF DOS LOAD 3E00 = BOOT EQU BDOSE-2*256 ;COLD START ENTRY POINT 3E03 = RBOCT EQU BOOT+3 ;WARM START ENTRY POINT ; 0080 ORG 80H ;LOADED DOWN FROM HARDWARE BOOT AT 3000H ; 1700 = BDOSL EQU BDOSE-BDOSB 0002 = NTRKS EQU 2 ;NUMBER OF TRACKS TO READ 002E = BDOSS EC)U BDOSL/128 ;NUMBER OF SECTORS IN DOS 0019 = BDOS0 EQU 25 ;NUMBER OF BDOS SECTORS ON TRACK 0 0015 = BDOS1 EQU BDOSS-BDOSO ;NUMBER OF SECTORS ON TRACK 1 ; F800 = MON80 EQU OF800H ;INTEL MONITOR BASE FF0F = RMON80 EQU OFFOFH ;RESTART LWATION FOR MON80 0078 = BASE EQU 078H ;'BASE' USED BY CONTROLLER 0079 = RTYPE EQU BASE+1 ;RESULT TYPE 007B = RBYTE EQU BASE+3 ;RESULT BYTE 007F = RESET EQU BASE+7 ;RESET CONTROLLER ; 0078 = DSTAT EQU BASE ;DISK STATUS PORT 0079 = LOW EQU BASE+1 ;LOW IOPB ADDRESS 007A = HIGH EQU BASE+2 ;HIGH IOPB ADDRESS 0003 = RECAL EQU 3H ;RECALIBRATE SELECTED DRIVE 0004 = READF EQU 4H ;DISK READ FUNCTION 0100 = STACK EQU 100E ;USE END CF BOOT FOR STACK ; RSTART: 0080 310001 LXI SP,STACK;IN CASE OF CALL TO MON80 ;CLEAR THE CONTROLLER 0083 D37F OUT RESET ;LOGIC CLEARED ; ; 0085 0602 MVI NTRKS ;NUMBER CF TRACKS TO READ 0087 21B700 LXI H,IOPB0 ; START: ; ; READ FIRST/NEXT TRACK INTO BDOSB 008A 7D MOV A,L B-1 008B D379 OUT LOW 008D 7C MOV A,H 008E D37A OUT HIGH 0090 D878 WAIT0: IN DSTAT 0092 E604 ANI 4 0094 CA9000 JZ WAIT0 ; ; CHECK DISK STATUS 0097 DB79 IN RTYPE 0099 E603 ANI 11B 0098 FE02 CPI 2 ; IF TESTING CNC RMON80 ;GO TO MONITOR IF 11 OR 10 ENDIF IF NOT TESTING 009D D28000 JNC RSTART ;RETRY THE LOAD ENDIF ; 00A0 DB7B IN RBYTE ;I/0 COMPLETE, CHECK STATUS ;IF NOT READY, THEN GOTO MON80 00A2 17 RAL 00A3 ECOFFF CC RMON80 ;NOT READY BIT SET 00A6 1F RAR ;RESTORE 00A7 E61E ANI 11110B ;OVERRUN/ADDR ERR/SEEK/CRC/XXXX ; IF TESTING CNZ RMON80 ;GO TO MDNIICR ENDIF IF NOT TESTING 00A9 C28000 JNZ RSTART ;RETRY THE LOAD ENDIF ; ; 00AC 110700 LXI D,IOPBL ;LENGTH OF IOPB 00AF 19 DAD D ;ADDRESSING NEXT IOPS 00B0 05 DCR B ;COUNT DOWN TPACKS 00B1 C28A00 JNZ START ; ; ;JMP TO BOOT TO PRINT INITIAL MESSAGE, AND SET UP JMPS 00B4 C3003E JMP BOOT ; ; PARAMETER BLOCKS 00B7 80 IOPB0: DB 80H ;IOCW, NO UPDATE 00B8 04 DB READF ;READ FUNCTION 00B9 19 DB BDOS0 ;# SECTORS TO READ ON TRACK 0 00BA 00 DB 0 ;TRACK 0 00BB 02 DB 2 ;START WITH SECTOR 2 ON TRACK 0 00BC 0029 DW BDOSB ;START AT BASE OF BDOS B-2 0007 = IOPBL EQU $-IOPBO ; 00BE 80 IOPB1: DB 80H 00BF 04 DB READF 00C0 15 DB BDOS1 ;SECTORS TO READ ON TRACK 1 00C1 01 DB 1 ;TRACK 1 00C2 01 DB 1 ;SECTOR 1 00C3 8035 DW BDOSB+BDOS0*128 ;BASE OF SECOND ; 00C5 END B-3 ; MDS I/0 DRIVERS FOR CP/M ; VERSION 1.1 OCTOBER, 1976 ; ; COPYRIGHT (C) 1976 ; DIGITAL RESEARCH ; BOX 579, PACIFIC GROVE CA. ; ; ; 0010 = MSIZE EQU 16 ;MEMORY SIZE IN KILOBYTES 000B = VERS EQU 11 ;CPM VERSION NUMBER 3E00 = PATCH EQU MSIZE*1024-2*256 ;BASE OF THIS MODULE (ABOVE DOS) ; 3E00 ORG PATCH 2000 = CBASE EQU (MSIZE-8)*1024 ;BIAS FOR SYSTEMS LARGER THAN 8K 2900 = CPMB EQU CBASE+900H ;BASE OF CPM (CONSOLE PROCESSOR 3206 = BDOS EQU CBASE+1206H ;BASIC DOS (RESIDENT PORTION) 1500 = CPML EQU $-CPMB ;LENGTH (IN BYTES) OF CPM SYSTEM 002A = NSECTS EQU CPML/128 ;NUMBER OF SECTORS TO LOAD E080 = LBIAS EQU 980H-CPMB ;LOADER BIAS VALUE USED IN SYSGEN 0002 = OFFSET EQU 2 ;NUMBER OF DISK TRACKS USED BY CP/M 0080 = BUFF EQU 80H ;DEFAULT BUFFER ADDRESS 000A = RETRY EQU 10 ;MAX RETRIES ON DISK I/0 BEFORE ERROR ; ;PERFORM FOLLOWING FUNCTIONS ;BOOT COLD START ;WBOOT WARM START (SAVE I/0 BYTE) ;(BOOT AND WBOOT ARE THE SAME FOR MDS) ;CONST CONSOLE STAIUS ; REG-A = 00 IF NO CHARACTER READY ; REG-A = FF IF CHARACTER READY ;CONIN CONSOLE CHARACTER IN (RESULT IN REG-A) ;CONOUT CONSOLE CHARACTER OUT (CHAR IN REG-C) ;LIST LIST OUT (CHAR IN REG-C) ;PUNCH PUNCH OUT (CHAR IN REG-C) ;READER PAPER TAPE READER IN (RESULT TO REG-A) ;HOME MOVE TO TRACK 00 ; ;(THE FOLLOWING CALLS SET-UP THE IO PARAMETER BLOCK FOR THE ;MDS, WHICH IS USED TO PERFORM SUBSEQUENT READS AND WRITES) ;SELDSK SELECT DISK GIVEN BY REG-C (0,1,2 ... ) ;SETTRK SET TRACK ADDRESS (0,...76) FOR SUBSEQUENT READ/WRITE ;SETSEC SET SECTOR ADDRESS (1,...,26) FOR SUBSEQUENT READ/WRITE ;SETDMA SET SUBSEQUENT DMA ADDRESS (INITIALLY 80H) ; ;(READ AND WRITE ASSUME PREVIOUS CALLS TO SET UP THE IO PARAMETERS) ;READ READ TRACK/SECTOR TO PRESET DMA ADDRESS ;WRITE WRITE TRACK/SECTOR FROM PRESET DMA ADDRESS C-1 ; ;JUMP VECTOR FOR INDIVIDUAL ROUTINES 3E00 C3443E JMP BOOT 3E03 C3543E WBOOTE: JMP WBOOT 3E06 C3073F JMP CONST 3E09 C30A3F JMP CONIN 3E0C C3103F JMP CONOUT 3E0F C3293F JMP LIST 3E12 C32C3F JMP PUNCH 3E15 C32F3F JMP READER 3E18 C3323F JMP HOME 3E1B C3373F JMP SELDSK 3E1E C3503F JMP SETTRK 3E21 C3553F JMP SETSEC 3E24 C35A3F JMP SETDMA 3E27 C3603F JMP READ 3E2A C3693F JMP WRITE ; ; ; END OF CONTROLLER - INDEPENDENT CODE, THE REMAINING SUBROUTINES ; ARE TAILORED TO THE PARTICULAR OPERATING ENVIRONMENT, AND MUST ; BE ALTERED FOR ANY SYSTEM WHICH DIFFERS FROM THE INTEL MDS. ; ;THE FOLLOWING CODE ASSUMES THE MDS MONITOR EXISTS AT OF800H ; AND USES THE I/0 SUBROUTINES WITHIN THE MONITOR ; ;WE ALSO ASSUME THE MDS SYSTEM HAS TWO DISK DRIVES AVAILABLE 0002 = NDISKS EQU 2 ;NUMBER OF DRIVES AVAILABLE 00FD = REVRT EQU OFDH ;INTERRUPT REVERT PORT 00FC = INX EQU OFCH ;INTERRUPT MASK PORT 00F3 = ICON EQU OF3H ;INTERRUPT CONTROL PORT 007E = INTE EQU 0111$1110B ;ENABLE RST 0(WARM BOOT), RST 7 ; ; MDS MDNITOR EQUATES F800 = MON80 EQU OF800H ;MDS MONITOR FF0F = RMON80 EQU OFFOFH ;RESTART MON80 (DISK SELECT ERROR) F803 = CI EQU OF803H ;CONSOLE CHARACTER TO REG-A F806 = RI EQU OF806H ;READER IN TO REG-A F809 = CO EQU OF809H ;CONSOLE CHAR FROM C TO CONSOLE OUT F80C = PO EQU OF8OCH ;PUNCH CHAR FROM C TO PUNCH DEVICE F80F = LO EQU OF8OFH ;LIST FROM C TO LIST DEVICE F812 = CSTS EQU OF812H ;CONSOLE STATUS 00/FF TO REGISTER A ; ;DISK PORTS AND COMMANDS 0078 = BASE EQU 78H ;BASE OF DISK COMMAND IO PORTS 0078 = DSTAT EQU BASE ;DISK STATUS (INPUT) 0079 = RTYPE EQU BASE+1 ;RESULT TYPE (INPUT) 007B = RBYTE EQU BASE+3 ;RESULT BYTE (INPUT) ; 0079 = LOW EQU BASE+1 ;IOPB LOW ADDRESS (OUTPUT) C-2 007A = HIGH EQU BASE+2 ;IOPB HIGH ADDRESS (OUTPUT) ; 0004 = READF EQU 4H ;READ FUNCTION 0006 = WRITF EQU 6H ;WRITE FUNCTICN 0003 = RECAL EQU 3H ;RECALIBRATE DRIVE 0004 = IORDY EQU 4H ;I/0 FINISHED MASK 000D = CR EQU 0DH ;CARRIAGE RETURN 000A = LF EQU 0AH ;LINE FEED ; SIGNON: ;SIGNON MESSAGE: XXK CP/M VERS Y.Y 3E2D 0D0A0A DB CR, LF, LF 3E30 3136 DB MSIZE/10+'0',MSIZE MOD 10 + '0' 3E32 4B2043502F DB '.K CP/M VERS ' 3E3E 312E31 DB VERS/10+'0','.',VERS MOD 10+'0' 3E41 0D0A00 DB CR,LF,0 ; BOOT: ;PRINT SIGNON MESSAGE AND GO TO DOS 3E44 310001 LXI SP,BUFF+80H 3E47 212D3E LXI H,SIGNON 3E4A CD723F CALL PRMSG ;PRINT MESSAGE 3E4D AF XRA A ;CLEAR ACCUMULATOR 3E4E 32D33F STA DISKT ;SELECT DISK 0 ON ENTRY 3E51 C3A63E JET GOPM ;GO TO CP/M ; WBOOT:; LOADER ON TRACK 0, SECTOR 1, WHICH WILL BE SKIPPED FOR WARM BOOT ; READ CP/M FROM DISK - ASSUMING THERE IS A 128 BYTE COLD START ; START. ; 3E54 318000 LXI SP,BUFF ;USING DMA - THUS 80 THRU FF AVAILABLE FOR STACK 3ES7 3AD23F LDA DISKN ;CURRENTLY LOGGED DISK, RETURN TO DISKN IF NOT 0 3E5A 32D33F STA DISKT ;STORE INTO DISK TEMP SINCE WE BOOT OFF OF 0 ; 3E50 0E0A MVI C,RETRY ;MAX RETRIES 3ESF C5 PUSH B WBOOT0: ;ENTER HERE ON ERROR RETRIES 3E60 010029 LXI B,CPMB ;SET DMA ADDRESS TO START OF DISK SYSTEM 3E63 CD5A3F CALL SETDMA 3E66 0E02 MVI C,2 ;STA1RT READING SECTOR 2 3E68 CD553F CALL SETSEC 3E6B 0E00 MVI C,0 ;START RFADING TRACK 0 3E6D CD503F CALL SETTRK 3E70 0E00 MVI C,0 ;START WITH DISK 0 3E72 CD373F CALL SELDSK ;CHANGES DISKN TO 0 ; ;READ SECTORS, COUNT NSECTS TO ZERO 3E75 Cl POP B ;10-ERROR COUNT 3E76 062A MVI B,NSECTS RDSEC: ;READ NEXT SECTOR 3E78 C5 PUSH B ;SAVE SECTOR COUNT C-3 3E79 CD603F CALL READ 3E7C C2E03E JNZ BOOTERR ;RETRY IF ERRORS OCCUR 3E7F 2AD93F LHLD IOD ;INCREMENT DMA ADDRESS 3882 118000 LXI D,128 ;SECTOR SIZE 3E85 19 DAD D ;INCREMENTED DMA ADDRESS IN HL 3E86 44 MOV B,H 3E87 4D MOV C,L ;READY FOR CALL TO SET DMA 3E88 CD5A3F CALL SETDMA 3E8B 3AD83F LDA IOS ;SECTOR NUMBER JUST READ 3E8E FE1A CPI 26 ;READ LAST SECTOR? 3E90 DA9C3E JC RD1 ;MUST BE SECTOR 26, ZERO AND GO TO NEXT TRACK 3E93 3AD73F LDA IOT ;GET TRACK TO REGISTER A 3E96 3C INR A 3E97 4F MOV C,A ;READY FOR CALL 3E98 CD503F CALL SETTRK 3E9B AF XRA A ;CLEAR SECTOR NUMBER 3E9C 3C RD1: INR A ;TO NEXT SECTOR 3E9D 4F MOV C,A ;READY FOR CALL 3E9E CD553F CALL SETSEC 3EA1 Cl POP B ;RECALL SECTOR COUNT 3EA2 05 DCR B ;DONE? 3EA3 C2783E JNZ RDSEC ; ;DONE WITH THE LOAD, RESET DEFAULT BUFFER ADDRESS GOCPM: ;(ENTER HERE FROM COLD START BOOT) ;ENABLE RST0 AND RST7 3EA6 F3 DI 3EA7 3E12 MVI A,12H ;INITIALIZE COMMAND 3EA9 D3FD OUT REVRT 3EAB AF XRA A 3EAC D3FC OUT INTC ;CLEARED 3EAE 3E7E MVI A,INTE ;RST0 AND RST7 BITS CN 3EB0 D3FC OUT INTC 3EB2 AF XRA A 3EB3 D3F3 OUT ICON ;INTERRUPT CONTROL ; ;SET DEFAULT BUFFER ADDRESS TO 80H 3EB5 018000 LXI B,BUFF 3EB8 CD5A3F CALL SETDMA ; ;RESET MONITOR ENTRY POINTS 3EBB 3EC3 MVI A,JMP 3EBD 320000 STA 0 3ECO 21033E LXI H,WBOOTE 3EC3 220100 SHLD 1 ;JMP WBOOT AT LOCATION 00 3EC6 320500 STA 5 3EC9 210632 LXI H,BDOS 3ECC 220600 SHLD 6 ;JMP BDOS AT LOCATICN 5 3ECF 323800 STA 7*8 ;JMP TO MON80 (MAY HAVE BEEN CHANGED BY DDT) C-4 3ED2 2100F8 LXI H,MON80 3ED5 223900 SHLD 7*8+1 ;LEAVE IOBYTE SET ;PREVIOUSLY SELECTED DISK WAS B, SEND PARAMETER TO CPM 3ED8 3AD33F LDA DISKT 3EDB 4F MOV C,A ;LOOKS LIKE A SINGLE PARAMETER TO CPM 3EDC FB EI 3EDD C30029 JMP CPMB ;ERROR CONDITION OCCURRED, PRINT MESSAGE AND RETRY BOOTERR: 3EE0 Cl POP B ;RECALL COUNTS 3EE1 0D DCR C 3EE2 CAE93E JZ BOOTER0 ;TRY AGAIN 3EES C5 PUSH B 3EE6 C3603E JMP WBOOT0 ; BOOTER0: ;OTHERWISE TOO MANY RETRIES 3EE9 21F23E LXI H,BOOTMSG 3EEC CD7F3F CALL ERROR 3EEF C3543E JMP WBOOT ;FOR ANOTHER TRY ; BOOTMSG: 3EF2 2A43414E4E DB 'CANNOT BOOT SYSTEM*',0 ; ; CONST: ;CONSOLE STATUS TO REG-A ;(EXACTLY THE SAME AS MDS CALL) 3F07 C312F8 JMP CSTS ; CONIN: ;CONSOLE CHARACTER TO REG-A 3F0A CD03F8 CALL CI 3F0D E67F ANI 7FH ;REMOVE PARITY BIT 3F0F C9 RET CONOUR: ;CONSOLE CHARACTER FROM C TO CONSOLE OUT ; SAME AS MDS CALL, BUT WAIT FOR SLOW CONSOLES ON LINE FEED 3F10 79 MOV A,C -GET CHARACTER TO ACCUM 3F11 FEOA CPI LF ;END OF LINE? 3F13 F5 PUSH FSW ;SAVE CDNDITION FOR LATER 3F14 CD09F8 CALL CO ;SEND THE CHARACTER (MAY BE LINE FEED) 3F17 Fl POP PSW 3F18 C0 RNZ ;RETURN IF IT WASN'T A LINE FEED ; ; WAIT 13 CHARACTER TIMES (AT 2400 BAUD) FOR LINE FEED TO HAPPEN ; (THIS WORKS OUT TO ABOUT 50 MILLISECS) 3F19 0632 MVI B,50 ;NUMBER CF KILLISECS TO WikIT 3F1B 0EB6 Tl: MVI C,182 ;COUNTER TO CONTROL 1 MILLISEC LOOP C-5 3F1D 0D T2: DCR C ;1 CYCLE = .5 USEC 3F1E C21D3F JNZ T2 ;10 CYCLES= 5.5 USEC ; ---------- ; = 5.5 USEC PER LOOP* 182 = 1001 USEC 3F21 05 DCR B 3F22 C21B3F JNZ Tl ;FOR ANOTHER LOOP 3F25 C9 RET ; 3F26 C309F8 JMP CD ; LIST: ;LIST DEVICE OUT ;(EXACTLY THE SAME AS MDS CALL) 3F29 C30FF8 JMP LO ; PUNCH: ;PUNCH DEVICE OUT ;(EXACTLY THE SAME AS MDS CALL) 3F2C C30CF8 JMP PO ; READER: ;READER CHARACTER IN TO REG-A ;(EXACTLY THE SAME AS MDS CALL) 3F2F C306F8 JMP RI ; HOME: ;MOVE TO HOME POSITION ;TREAT AS TRACK 00 SEEK 3F32 0E00 MVI C,0 3F34 C3503F JMP SETTRK ; SELDSK: ;SELECT DISK GIVEN BY REGISTER C ;CP/M HAS CHECKED FOR DISK SELECT 0 OR 1, BUT WE MAY HAVE ;A SINGLE DRIVE MDS SYSTEM, SO CHECK AGAIN AND GIVE ERROR ;BY CALLING MON80 3F37 79 MOV A,C 3F38 FE02 CPI NDISKS ;TOO LARGE? 3F3A D40FFF CNC RMON80 ;GIVES #ADDR MESSAGE AT CONSOLE 3F3D 32D23F STA DISKN ;SELECT DISK N ; 3F40 17 RAL 3F41 17 RAL 3F42 17 RAL 3F43 17 RAL 3F44 E610 ANI 10000B ;UNIT NUMBER IN POSITION 3F46 4F MOV C,A ;SAVE IT 3F47 21D53F LXI H,IOF ;IO FUNCTION 3F4A 7E MOV A,M 3F4B E6CF ANI 11001111B ;MASK OUT DISK NUMBER 3F4D Bl ORA C ;MASK IN NEW DISK NUMBER 3F4E 77 MOV M,A ;SAVE IT IN IOPB 3F4F C9 RET ; C-6 ; ;SET TRACK ADDRESS GIVEN BY C 3F50 21D73F LXI H, IOT 3F53 71 MOV M,C 3F54 C9 RET ; SETSEC: ;SET SECTOR NUMBER GIVEN BY C 3F55 21083F LXI H,IOS 3F58 71 MOV M,C 3F59 C9 RET ; SETDMA: ;SET DMA ADDRESS GIVEN BY REGS B,C 3F5A 69 MOV L,C 3F5B 60 MOV H,B 3F5C 22D93F SHLD IOD 3F5F C9 RET ; READ: ;READ NEXT DISK RECORD (ASSUMING DISK/TRK/SEC/DMA SET) 3F60 0E04 MVI C,READF ;SET TO READ EDCTICN 3F62 CD903F CALL SETFUNC 3F65 CD993F CALL WAITIO ;PERFORM READ FUNCTICN 3F68 C9 RET ;MAY HAVE ERROR SET IN REG-A ; WRITE: ;DISK WRITE FUNCTION 3F69 0E06 MVI C,WRITF 3F6B CD903F CALL SETFUNC ;SET TO WRITE FUNCTION 3F6E CD993F CALL WAITIO 3F71 C9 RET ;MAY HAVE ERROR SET ; ; ;UTILITY SUBROUTINES PRMSG: ;PRINT MESSAGE AT H,L TO 0 3F72 7E MOV A,M 3F73 B7 ORA A ;ZERO? 3F74 C8 RZ ;MORE TO PRINT 3F75 E5 PUSH H 3F76 4F MOV C,A 3F77 CD09F8 CALL CO 3F7A El POP H 3F7B 23 INX H 3F7C C3723F JMP PRMSG ; ERROR: ;ERROR MESSAGE ADDDRESSES BY H,L 3F7F CD723F CALL PRMSG ;ERROR MESSAGE WRITTEN, WAIT FOR RESPONSE FROM CONSOLE 3F82 CD0A3F CALL CONIN 3F85 0E0D MVI C,CR ;CARRIAGE RETURN 3F87 CD103F CALL C-7 3F8A 0E0A MVI C,LF ;LINE FEED 3F8C CD103E CALL CONOUT 3F8F C9 RET ;MAY BE RETURNING FOR ANOTHER, RETRY ; SETFUNC: ;SET FUNCTION FOR NEXT I/0 (COMMAND IN REG-C) 3F90 21D53F LXI H,IOF ;IO FUNCTION ADDRESS 3F93 7E MOV A,M ;GET IT TO ACCUMULATOR FOR MASKING 3F94 E6F8 ANI 11111000B ;REMOVE PREVIOUS COMMAND 3F96 Bl ORA C ;SET TO NEW COMMAND 3F97 77 MOV M,A ;REPLACED IN IOPB 3F98 C9 RET ; WAITIO: 3F99 0E0A MVI C,RETRY ;MAX RETRIES BEFORE PERM ERROR RWAIT: ;START THE I/0 FUNCTION AND WAIT FOR COMPLETION 3F9B DB79 IN RTYPE 3F9D DB7B IN RBYTE ;CLEARS THE CONTROLLER ; 3F9F 3E04 MVI A,IOPB AND 0FFH ;LOW ADDRESS FOR IOPB 3FA1 D379 OUT LOW ;TO THE CONTROLLER 3FA3 3E3F MVI A,IOPB SHR 8 ;HIGH ADDRESS FOR IOPB 3FA5 D37A OUT HIGH ;TO THE CONTROLLER, STARTS OPERATION ; 3FA7 DB78 WAITO: IN DSTAT ;WAIT FOR COMPLETION 3FA9 E604 ANI IORDY ;READY? 3FAB CAA73F JZ WAIT0 ; ;CHECK IO COMPLETION OK 3FAE DB79 IN RTYPE ;MUST BE I/0 ODMPLETE (00) UNLINKED ; 00 UNLINKED I/0 COMPLETE, 01 LINKED I/0 COMPLETE (NOT USED) ;10 DISK STATUS CHANGED 11 (NOT USED) 3FB0 FE02 CPI 10B ;READY STATUS CHANGE? 3FB2 CAC63F JZ WREADY ; ; MUST BE 00 IN THE ACCUMULATOR 3FBS B7 ORA A 3FB6 C2CB3F JNZ WERROR ;SOME OTHER CONDITION, RETRY ; ;CHECK I/0 ERROR BITS 3FB9 DB7B IN RBYTE 3FBB 17 RAL 3FBC IAC63F JC WREADY ;UNIT NOT READY 3FBF 1F RAR 3FC0 E6FE ANI 11111110B ;ANY OTHER ERRORS? (DELETED DATA CK) 3FC2 C2CB3F JNZ WERROR ; ;READ OR WRITE IS OK, ACCUMULATOR C0NTAINS ZERO 3FC5 C9 RET C-8 ; WREADY: ;NOT READY, TREAT AS ERROR FOR NOW 3FC6 DB7B IN RBYTE ;CLEAR RESULT BYTE 3FC8 C3CB3F JMP TRYCOUNT ; WERROR: ;RETURN HARDWARE MALFUNCTION (CRC, TRACK, SEEK, ETC.) ; THE MDS CONTROLLER HAS RETURNED A BIT IN EACH POSITION ; OF THE ACCUMULATOR, CORRESPONDING TO THE CONDITIONS: ;0 -DELETED DATA (ACCEPTED AS OK ABOVE) ;1 -CRC ERROR ;2 -SEEK ERROR ;3 -ADDRESS ERROR (HARDWARE MALFNCTICN) ;4 -DATA OVER/UNDER FLOW (HARDWARE MALFUNCTION) ;5 -WRITE PROTECT (TREATED AS NOT READY) ;6 -WRITE ERROR (HARDWARE MALFUNCTION) ;7 -NOT READY ; (ACCUMULATOR BITS ARE NUMBERED 7 6 5 4 3 2 1 0) ; ; IT MAY BE USEFUL TO FILTER OUT THE VARIOUS CONDITIONS, ; BUT WE WILL GET A PERMANENT ERROR MESSAGE IF IT IS NOT ; RECOVERABLE. IN ANY CASE, THE NOT READY CONDITION IS ; TREATED AS A SEPARATE CONDITION FOR LATER IMPROVEMENT TRYCOUNT: ; REGISTER C CONTAINS RETRY COUNT, DECREMENT 'TIL ZERO 3FCB 0D DCR C 3FCC C29B3F JNZ REWAIT ;FOR ANOTHER TRY ; ; CANNOT RECOVER FROM ERROR 3FCF 3E01 MVI A,1 ;ERROR CODE 3FD1 C9 RET ; ;DATA AREAS (MUST BE IN RAM) 3FD2 00 DISKN: DB 0 ;CURRENT DISK 3FD3 00 DISKR: DB 0 ;TEMP FOR CURRENT DISK DURING WARM START ICPB: ;IO PARAMETER BLOCK 3FD4 80 DB 80H ;NORMAL I/0 OPERATION 3FD5 04 IOF: DB READF ;IO FUNCTION, INITIAL READ 3FD6 01 ION: DB 1 ;NUMBER OF SECTORS TO READ 3FD7 02 IOT: DB OFFSET ;TRACK NUMBER 3FD8 01 IOS: DB 1 ;SECTOR NUMBER 3FD9 8000 IOD: DW BUFF ;IO ADDRESS ; ; 3FDB END C-9 ;SKELETAL CBIOS FOR FIRST LEVEL OF CP/M ALTERATION ; ;NOTE : MSIZE DETERMINES WHERE THIS CBIOS IS LOCATED 0010 = MSIZE EQU 16 ;CP/M VERSION MEMORY SIZE IN KILOBYTES 3E00 = PATCH EQU MSIZE*1024-2*256 ;START OF THE CBIOS PATCH ; ;WE WILL USE THE AREA RESERVED STARTING AT LOCATION ;40H IN PAGE 0 FOR HOLDING THE VALUES OF: ; TRACK = LAST SELECTED TRACK ; SECTOR = LAST SELECTED SECTOR ; DMAAD = LAST SELECTED DMA ADDRESS ; DISKNO = LAST SELECTED DISK NUMBER ;(NOTE THAT ALL ARE BYTE VALUES EXCEPT FOR DMAAD) ; ; 0040 = SCRAT EQU 40H ;BASE OF SCRATCH AREA (FROM 40H T 0040 = TRACK EQU SCRAT ;CURRENTLY SELECTED TRACK 0041 = SECTOR EQU SCRAT+1 ;CURRERILY SELECTED SECTOR 0042 = DMAAD EQU SCRAT+2 ;CURRENT DMA ADDRESS 0046 = DISKNO EQU DMAAD+4 ;CURRENT DISK NUMBER ; ; 3E00 ORG PATCH ;0RGIN OF THIS PROGRAM 0000 = CBASE EQU (MSIZE-16)*1024 ;BIAS FOR SYSTEMS LARGER THAN 16K 2900 = CPMB EQU CBASE+2900H ;BASE OF CP/M (= BASE OF CCP) 3206 = BDOS EQU CBASE+3206H ;BASE OF RESIDENT PORTION OF CP/M 1500 = CPML EQU $-CPMB ;LENGTH OF THE CPM SYSTEM IN BYTES 002A = NSECTS EQU CPML/128 ;NUMBER OF SECTORS TO LOAD ON WARM START ; ;JUMP VECTOR FOR INDIVIDUAL SUBROUTINES 3E00 C32D3E JMP BOOT ;COLD START WBOOTE: 3E03 C33038 JMP WBOOT ;WARM START 3E06 C3993E JMP CONST ;CONSOLE STATUS 3E09 C3AC3E JMP CONIN ;CONSOLE CHARACTER IN 3E0C C38F3E JMP CONOUT ;CONSOLE CHARACTER OUT 3E0F C3D13E JMP LIST ;LIST CHARACTER OUT 3E12 C3D33E JMP PUNCH ;PUNCH CHARACTER OUT 3E15 C3D53E JMP READER ;READER CHARACTER OUT 3E18 C3DA3E JMP HOME ;MOVE HEAD TO HOME POSITION 3E1B C3E03E JMP SELDSK ;SELECT DISK 3E1E C3F53E JMP SETTRK ;SET TRACK NUMBER 3E21 C30A3F JMP SETSEC ;SET SECTOR NUMBER 3E24 C31F3F JMP SETDMA ;SET DMA ADDRESS 3E27 C3353F JMP READ ;READ DISK 3E2A C3483F JMP WRITE ;WRITE DISK ; ; ;INDIVIDUAL SUBROUTINES TO PERFORM EACH FUNCTION D-1 BOOT: ;SIMPLEST CASE IS TO JUST PERFORM PARAMETER INITIALIZATION 3E2D C3793E JMP GOCPM ;INITIALIZE AND GO TO CP/M ; WBOOT: ;SIMPLEST CASE IS TO READ THE DISK UNTIL ALL SECTORS LOADED 3E30 318000 LXI SP,80H ;USE SPACE BELOW BUFFER FOR STACK 3E33 0E00 JMP C,0 ;SELECT DISK 0 3E35 CDE03E CALL SELDSK 3E38 CD1A3E CALL HOME ;GO TO TRACK 00 ; 3E3B 062A MVI B,NSECTS ;B COUNTS THE NUMBER OF SECTORS TO LOAD 3E3D 0E00 MVI C,0 ;C HAS THE CURRENT TRACK NUMBER 3E3F 1602 MVI D,2 ;D HAS THE NEXT SECTOR TO READ ;NOTE THAT WE BEGIN BY READING TRACK 0, SECTOR 2 SINCE SECTOR 1 ;CONTAINS THE COLD START LOADER, WHICH IS SKIPPED IN A WARM START 3E41 210029 LXI H,CPMB ;BASE OF CP/M (INITIAL LOAD POINT) LOAD1: ;LOAD ONE MORE SECTOR 3E44 C5 PUSH B ;SAVE SECTOR COUNT, CURRENT TRACK 3E45 D5 PUSH D ;SAVE NEXT SECTOR TO READ 3E46 E5 PUSH H ;SAVE DMA ADDRESS 3E47 4A MOV C,D ;GET SECTOR ADDRESS TO REGISTER C 3E48 CD0A3F CALL SETSEC ;SET SECTOR ADDRESS FROM REGISTER C 3E4B Cl POP B ;RECALL DMA ADDRESS TO B,C 3E4C C5 PUSH B ;REPLACE ON STACK FOR LATER RECALL 3E4D CD1F3F CALL SETDMA ;SET DMA ADDRESS FROM B,C ; ;DRIVE SET TO 0, TRACK SET, SECTOR SET, DMA ADDRESS SET 3E50 CD353F CALL READ 3E53 FE00 CPI 00H ;ANY ERRORS? 3E55 C2303E JNZ WBOOT ;RETRY THE ENTIRE BOOT IF AN ERROR OCCURS ; ;NO ERROR, MOVE TO NEXT SECTOR 3E58 El POP H ;RECALL DMA ADDRESS 3ES9 118000 LXI D,128 ;DMA=DMA+128 3E5C 19 DAD D ;NEW DMA ADDRESS IS IN H,L 3E5D Dl POP D ;RECALL SECTOR ADDRESS 3E5E Cl POP B ;RECALL NUMBER OF SECTORS REMAINING, AND CURRENT TRK 3ESF 05 DCR B ;SECTORS=SECTORS-1 3E60 CA793E JZ GOCPM ;TPANSFER TO CP/M IF ALL HAVE BEEN LOADED ; ;MORE SECTORS REMAIN TO LOAD, CHECK FOR TRACK CHANGE 3E63 14 INR D 3E64 7A MOV A,D ;SECTOR=27?, IF SO, CHANGE TRACKS 3E65 FE1B CPI 27 3E67 DA443E JC LOAD1 ;CARRY GENERATED IF SECTOR<27 ; ;END OF CURRENT TRACK, GO TO NEXT TRACK 3E6A 1601 MVI D,1 ;BEGIN WITH FIRST SECTOR OF NEXT TRACK 3E6C 0C INR C ;TRACK=TRACK+1 ; ;SAVE REGISTER STATE, AND CHANGE TRACKS D-2 3E6D C5 PUSH B 3E6E D5 PUSH D 3E6F E5 PUSH H 3E70 CDF53E CALL SETTRK ;TRACK ADDRESS SET FROM REGISTER C 3E73 El POP H 3E74 Dl POP D 3E75 Cl POP B 3E76 C3443E JMP LOAD1 ;FOR ANOTHER SECTOR ; ;END OF LOAD OPERATION, SET PARAMETERS AND GO TO CP/M GOCPM: 3E79 3EC3 MVI A,0C3H ;C3 IS A JMP INSTRUCTION 3E7B 320000 STA 0 ;FOR JMP TO WBOOT 3E7E 21033E LXI H,WBOOTE ;WBOOT ENTRY POINT 3E81 220100 SHLD 1 ;SET ADDRESS FIELD FOR JMP AT 0 ; 3E84 320500 STA 5 ;FOR JMP TO BDOS 3E87 210632 LXI H,BDOS ;BDOS ENTRY POINT 3E8A 220600 SHLD 6 ;ADDRESS FIELD OF JUMP AT 5 TO BDOS ; 3E8D 018000 LXI B,80H ;DEFAULT DM ADDRESS IS 80H 3E90 CD1F3F CALL SETDMA ; 3E93 FB EI ;ENABLE THE INTERRUPT SYSTEM ;FUTURE VERSIONS OF CCP WILL SELECT THE DISK GIVEN BY REGISTER ;C UPON ENTRY, HENCE ZERO IT IN THIS VERSION OF THE BIOS FOR ;FUTURE COMPATIBILITY. 3E94 0E00 MVI C,0 ;SELECT DISK ZERO AFTER INITIALIZATION 3E96 C30029 JMP CPMB ;GO TO CP/M FOR FURTHER PROCESSING ; ; ;SIMPLE I/0 HANDLERS. (MUST BE FILLED IN BY USER) ;IN EACH CASE, THE ENTRY POINT IS PROVIDED, WITH SPACE RESERVED ;TO INSERT YOUR OWN CODE ; CONST: ;CONSOLE STATUS,RETURN 0FFH IF CHARACTER READY, 00H IF NOT 3E99 DS 10H ;SPACE FOR STATUS SUBROUTINE 3EA9 3E00 MVI A,00H 3EAB C9 RET ; CONIN: ;CONSOLE CHARACTER INTO REGISTER A 3EAC DS 10H ;SPACE FOR INPUT ROUTINE 3EBC E67F ANI 7FH ;STRIP PARITY BIT 3EBE C9 RET ; CONOUT: ;CONSOLE CHARACTER OUTPUT FROM REGISTER C 3EBF 79 MOV A,C ;GET TO ACCUMULATOR 3EC0 DS 10H ;SPACE FOR OUTPUT ROUTINE 3ED0 C9 RET ; D-3 ;LIST: ;LIST CHARACTER FROM REGISTER C 3ED1 79 MOV A,C ;CHARACTER TO REGISTER A 3ED2 C9 RET ;NULL SUBROUTINE ; PUNCH: ;PUNCH CHARACTER FROM REGISTER C 3ED3 79 MOV A,C ;CHARACTER TO REGISTER A 3ED4 C9 RET ;NULL SUBROUTINE ; READER: ;READ CHARACTER INTO REGISTER A FROM READER DEVICE 3ED5 3E1A MVI A,1AH ;ENTER END OF FILE FOR NOW (REPLACE LATER) 3ED7 E67F ANI 7FH ;REMEMBER TO STRIP PARITY BIT 3ED9 C9 RET ; ; ; I/0 DRIVERS FOR THE DISK FOLLOW ; FOR NOW, WE WILL SIMPLY STORE THE PARAMETERS AWAY FOR USE ; IN THE READ AND WRITE SUBROUTINES ; HOME: ;MOVE TO THE TRACK 00 POSITION OF CUPRENT DRIVE ;TPANSLATE THIS CALL INTO A SETTRK CALL WITH PARAMETER 00 3EDA OEOO MVI C,0 ;SELECT TRACK 0 3EDC CDFS3E CALL SETTRK 3EDF C9 RET ;WE WILL MOVE TO 00 ON FIRST READ/WRITE ; SELDSK: ;SELECT DISK GIVEN BY REGISTER C 3EEO 79 MOV A,C 3EE1 324600 STA DISKNO 3EE4 DS 10H ;SPACE FOR DISK SELECTION ROUTINE 3EF4 C9 RET ; SETTRK: ;SET TRACK GIVEN BY REGISTER C 3EF5 79 MOV A,C 3EF6 324000 STA TRACK 3EF9 DS 10H ;SPACE FOR TRACK SELECT 3F09 C9 RET ; SETSEC: ;SET SECTOR GIVEN BY REGISTER C 3FOA 79 MOV A,C 3FOB 324100 STA SECTOR 3F0E DS 10H ;SPACE FOR SECTOR SELECT 3F1E C9 RET ; SETDMA: ;SET DMA ADDRESS GIVEN BY REGISTERS B AND C 3FIF 69 MOV L,C ;LOW ORDER ADDRESS 3F20 60 MOV H,B ;HIGH ORDER ADDRESS 3F21 224200 SHLD DMAAD ;SAVE THE ADDRESS 3F24 DS 10H ;SPACE FOR SETTING THE DMA ADDRESS 3F34 C9 RET D-4 READ: ;PERFORM READ OPERATION (USUALLY THIS IS SIMILAR TO WRITE ;SO WE WILL ALLOW SPACE TO SET UP READ COMMAND, THEN USE ;COMMON CODE IN WRITE) 3F35 DS 10H ;SET UP READ COMMAND 3F45 C3583F JMP WAITIO ;TO PERFORM THE ACTUAL I/0 ; WRITE: ;PERFORM A WRITE OPERATION 3F48 DS 10H ;SET UP WRITE COMMAND ; WAITIO: ;ENTER HERE FROM READ AND WRITE TO PERFORM THE ACTUAL I/0 ;OPERATION. RETURN A 00H IN REGISTER A IF THE OPERATION COMPLETES ;PROPERLY, AND 01H IF AN ERROR OCCURS DURING THE READ OR WRITE ; ; IN THIS CASE, WE HAVE SAVED THE DISK NUMBER IN 'DISKNO' (0,1) ; THE TRACK NUMBER IN 'TRACK' (0-76) ; THE SECTOR NUMBER IN 'SECTOR' (1-26) ; THE DMA ADDRESS IN 'DMAAD' (0-65535) ;ALL REMAINING SPACE FROM $ THRU MSIZE*1024-1 IS AVAILABLE: 00A7 LEFT EQU (MSIZE*1024-1)-$ ;SPACE REMAINING IN CBIOS ; 3F58 3E01 MVI A,1 ;ERROR CONDITION 3F5A C9 RET ;REPLACED WHEN FILLED-IN 3F5B END D-5 ;COMBINED GETSYS AND PUTSYS PROGRAMS FROM SECTION 4 ; ;START THE PROGRAMS AT THE BASE OF THE TRANSIENT PROGRAM AREA 0100 ORG 100H 0010 = MSIZE EQU 16 ;SIZE OF MEMORY IN KILOBYTES ;BIAS IS THE AMOUNT TO ADD TO ADDRESSES FOR SYSTEMS LARGER THAN 16K ;(REFERRED TO AS 'B' THROUGHOUT THE TEXT) 0000 = BIAS EQU (MSIZE-16)*1024 ; ;GETSYS PROGRAM - READ TRACKS 0 AND 1 TO MEMORY AT 2880H+BIAS ;REGISTER USE ; A (SCRATCH REGISTER) ; B TRACK COUNT (0...76) ; C SECTOR COUNT (1...26) ; D,E (SCRATCH REGISTER PAIR) ; H,L LOAD ADDRESS ; SP SET TO STACK ADDRESS ; GSTART: ;START OF THE GETSYS PROGRAM 0100 318028 LXI SP,2880H+BIAS ;SET STACK POINTER TO SCRATCH AREA 0103 218028 LXI H,2880H+BIAS ;SET BASE LOAD ADDRESS 0106 0600 MVI B,0 ;START WITH TRACK 00 RDTRK: ;READ FIRST (NEXT) TRACK 0108 0E01 MVI C,1 ;READ STARTING WITH SECTOR 1 RDSEC: 010A CD0003 CALL READSEC ;READ NEXT SECTOR 010D 118000 LXI D,128 ;CHANGE LOAD ADDRESS TO NEXT 1/2 PAGE 0110 19 DAD D ;HL=HL+128 TO NEXT ADDRESS 0111 0C INR C ;SECTOR=SECTOR+1 0112 79 MOV A,C ;CHECK FOR END OF TRACK 0113 FE1B CPI 27 0115 CA0A01 JC RDTRK ;CARRY GENERATED IF C<27 ; ; ARRIVE HERE AT END CF TRACK, MOVE TO NEXT TRACK 0118 04 INR B ;TRACK=TRACK+1 0119 78 MOV A,B ;CHECK FOR LAST TRACK 011A FE02 CPI 2 ;TRACK=2? 011C DA0801 JC RDTRK ;CARRY GENERATED IF TRACK < 2 ; ARRIVE HERE AT END OF LOAD, HALT FOR NOW 011F FB EI 0120 76 HIT ; ;PUTSYS PROGRAM - PLACE MEMORY STARTING AT 2880H+BIAS BACK TO TRACKS ;0 AND 1. START THIS PROGRAM ON THE NEXT PAGE 0200 ORG ($+100H) AND 0FF00H ;REGISTER USE ; A (SCRATCH REGISTER) E-1 ; B TRACK COUNT (0, 1) ; C SECTOR COUNT (1 ... 26) ; D,E (SCRATCH REGISTER PAIR) ; H,L DUMP ADDRESS ; SP SET TO STACK ADDRESS ; PSTART: ;START OF THE PUTSYS PROGRAM 0200 318028 LXI SP,2880H+BIAS ;SET STACK POINTER TO SCRATCH AREA 0203 218028 LXI H,2880H+BIAS ;SET BASE DUMP ADDRESS 0206 0600 MVI B,0 ;START WITH TRACK 0 MTRK: ;WRITE FIRST (NEXT) TRACK 0208 0E01 MVI C,1 ;START WRITING AT SECTOR 1 WSEC: ;WRITE FIRST (NEXT) SECTOR 020A CD8003 CALL WRITESEC ;PERFORM THE WRITE 020D 118000 LXI D,128 ;MOVE DUMP ADDRESS TO NEXT 1/2 PAGE 0210 19 DAD D ;HL=HL+128 0211 OC INR C ;SECTOR=SECTOR+1 0212 79 MOV A,C ;CHECK FOR END OF TRACK 0213 FE1B CPI 27 ;SECTOR=27? 0215 DA0A02 JC WRSEC ;CARRY GENERATED IF SECTOR < 27 ; ;ARRIVE HERE AT END OF TRACK, MOVE TO NEXT TRACK 0218 04 INR B ;TRACK=TRACK+1 0219 78 MOV A,B ;TEST FOR LAST TRACK 021A FE02 CPI 2 ;TRACK=2? 021C A0802 JC WRTRK ;CARRY GENERATED IF TRACK < 2 ; ;ARRIVE HERE AT END OF DUMP, HALT FOR NOW 021F FB EI 0220 76 HIT ; ; ;USER-SUPPLIED SUBROUTINES FOR SECTOR READ AND SECTOR WRITE ; ;MOVE TO NEXT PAGE FOR SECTOR READ AND SECTOR WRITE 0300 ORG ($+100H) AND 0FF00H ; READSEC: ;READ THE NEXT SECTOR ;TRACK TO READ IS IN REGISTER B ;SECTOR TO READ IS IN REGISTER C ;BRANCH TO LABEL GSTART, IF ERROR OCCURS ;READ 128 BYTES OF DATA TO ADDRESS GIVEN BY H,L 0300 C5 PUSH B 0301 E5 PUSH H ;** PLACE READ OPERATION HERE ** 0302 El POP H 0303 Cl POP B 0304 C9 ;RET ;MOVE TO NEXT 1/2 PAGE FOR WRITESEC SUBROUTINE E-2 0380 ORG ($ AND 0FF00H) + 80H WITESEC: ;WRITE THE NEXT PE)C'I'OR ;TRACK TO WRITE IS IN REGISTER B ;SECTOR TO WRITE IS IN REGISTER C ;BRANCH TO LABEL PSTART IF ERROR OCCURS ;WRITE 128 BYTES OF DATA FROM ADDRESS GIVEN BY H,L 0380 C5 PUSH B 0381 ES PUSH H ;** PLACE WRITE OPERATION HERE ** 0382 El POP H 0383 Cl POP B 0384 C9 RET ;END OF GETSYS/PUTSYS PROGRAM 0385 END E-3 ; THIS IS A SAMPLE COLD STAR@T LOADER WHICH, WHEN MODIFIED, RESIDES ; ON TRACK 00, SECTOR 01 (THE FIRST SECTOR ON THE DISKETTE). WE ; ASSUME THAT THE CONTROLLER HAS LOADED THIS SECTOR IN MEMORY ; UPON SYSTEM STARTUP (THIS PROGRAM CAN BE KEYED-IN, OR EXIST IN ; A PAGE OF READ-ONLY MEMORY BEYOND THE ADDRESS SPACE OF THE CP/M ; VERSION YOU ARE RUNNING). THE COLD START LOADER BRINGS THE CP/M ; SYSTEM INT0 MEMORY AT 'LOADP' (NOMINALLY 29OOH) + 'BIAS' WHERE ; THE BIAS VALUE ACCOUNTS FOR MEMORY SYSTEMS LARGER THM 16K, AND ; CP/M VERSIONS WHICH HANDLE THE LARGER MEMORY SPACE. IN A 16K ; SYSTEM, THE VALUE OF BIAS IS 0000H. AFTER LOADING THE CP/M SYS- ; TEM, THE COLD START LOADER BRANCHES TO THE 'BOOT' ENTRY POINT OF ; THE BIOS, WHICH BEGINS AT 'BIOS' + 'BIAS'. THE COLD START LOADER ; IS NOT USED AGAIN UNTIL THE SYSTEM IS POWERED UP AGAIN, AS LONG ; AS THE BIOS IS NOT OVEWRITTEN. ; ; THE ORIGIN IS 0, ASSUMING THE CONTROLLER LOADS THE COLD START ; PROGRAM AT THE BASE OF MEMORY. THIS ORIGIN MUST BE IN HIGH ; MEMORY (BEYOND THE END OF THE BIOS) IF THE COLD START LOADER ; IS IMPLEMENTED IN READ-ONLY-MEMORY. 0000 ORG 0000H ;BASE OF MEMORY 0010 = MSIZE EQU 16 ;MEMORY SIZE IN KILOBYTES 0000 = BIAS EQU (MSIZE-16)*1024 ;BIAS TO ADD TO LOAD ADDRESSES 2900 = LOADP EOU 2900H ;LOAD POINT FOR CP/M SYSTEM 3E00 = BIOS EQU 3E00H ;BASIC I/0 SYSTEM (2 PACES = 512 BYTES) 3E00 = BOOT EQU BIOS ;COLD START ENTRY P0INT IN BIOS 1700 = SIZE EQU BIOS+512-LOADP ;SIZE OF THE CP/M SYSTEM TO LOAD 002E = SECTS EQU SIZE/128 ;NUMBER OF SECTORS TO LOAD ; ;BEGIN THE LOAD OPERATION 0000 010200 COLD: LXI B,2 ;CLEAR B TO 0, SET C TO SECTOR 2 0003 162E MVI D,SECTS ;NUMBER OF SECTORS TO LOAD IS IN D 0005 21002C LXI H,LOADP+BIAS ;LCAD POINT IN H,L ; LSECT: ;LOAD NEXT SECTOR ;INSERT INLINE CODE AT THIS POINT TO READ ONE 128-BYTE SECTOR ;FROM TRACK GIVEN BY REGISTER B, ; SECTOR GIVEN BY REGISTER C, ;INTO ADDRESS GIVEN BY REGISTER PAIR H,L ;BRANCH TO LOCATION 'COLD' IF A READ ERROR OCCURS ; ;************************************************************ ; USER SUPPLIED READ OPERATION GOES HERE ;************************************************************ ;(SPACE IS RESERVED FOR YOUR RATCH) 0008 C36B00 JMP PASTPATCH ;REMOVE THIS JUMP WHEN PATCHED 000B DS 60H ; PASTPATCH: F-1 ;GO TO NEXT SECTOR IF LCAD IS INCOMPLETE 006B 15 DCR D ;SECTS=SECTS-1 006C CA003E JZ BOOT+BIAS ;GO TO BOOT LOADER AT 3E00H+BIAS ; ;MORE SECTORS TO LOAD ;USE SP FOR SCRATCH REGISTER TO HOLD LOAD ADDRESS INCREMENT 006F 318000 LXI SP,128 0072 39 DAD SP ;HL=HL+128 TO NEXT LOAD ADDRESS ; 0073 OC INR C ;SECTOR=SECTOR+1 0074 79 MOV A,C ;MOVE SECTIOR COUNT TO A FOR COMPARE 0075 FE1B CPI 27 ;END OF CURRENT TRACK? 0077 DA0800 JC LECT ;CARRY GENERATED IF SECTOR < 27 ; ;END OF TRACK, MOVE TO NEXT TRACK 007A 0E01 MVI C,1 ;SECTOR=1 007C 04 INR B ;TRACK-TRACK+1 007D C30800 JMP LSECT ;FOR ANOTHER SECTOR 0080 END F-2