From 8deca6109442969641ec57132dacef54da7f7ef1 Mon Sep 17 00:00:00 2001 From: Wayne Warthen Date: Sat, 2 Sep 2017 15:43:02 -0700 Subject: [PATCH] Enhanced FDU FD renamed to FDU and enhanced to select FDC at startup to eliminate multiple build variations. --- Doc/ChangeLog.txt | 6 + Doc/FDU.txt | 473 +++ Source/Apps/Build.cmd | 3 +- Source/Apps/Clean.cmd | 3 +- Source/Apps/FDU/Build.cmd | 13 + Source/Apps/FDU/Clean.cmd | 4 + Source/Apps/FDU/FDU.asm | 4421 +++++++++++++++++++++++++ Source/Apps/FDU/FDU.txt | 473 +++ Source/Apps/Format.asm | 6 +- Source/Apps/{XM125 => XM}/Build.cmd | 0 Source/Apps/{XM125 => XM}/Clean.cmd | 0 Source/Apps/{XM125 => XM}/xmdm125.asm | 0 Source/Apps/{XM125 => XM}/xmdm125.zip | Bin Source/Apps/{XM125 => XM}/xmhb.180 | 0 Source/CBIOS/ver.inc | 2 +- Source/HBIOS/util.asm | 14 +- Source/HBIOS/ver.inc | 2 +- Source/RomDsk/MK4/FD_DIDE.COM | Bin 7961 -> 0 bytes Source/RomDsk/MK4/FD_DIO3.COM | Bin 7974 -> 0 bytes Source/RomDsk/N8/FD_N8.COM | Bin 7925 -> 0 bytes Source/RomDsk/SBC/FD_DIDE.COM | Bin 7961 -> 0 bytes Source/RomDsk/SBC/FD_DIO3.COM | Bin 7974 -> 0 bytes Source/RomDsk/ZETA/FD_ZETA.COM | Bin 7974 -> 0 bytes Source/RomDsk/ZETA2/FD_ZETA2.COM | Bin 7961 -> 0 bytes 24 files changed, 5408 insertions(+), 12 deletions(-) create mode 100644 Doc/FDU.txt create mode 100644 Source/Apps/FDU/Build.cmd create mode 100644 Source/Apps/FDU/Clean.cmd create mode 100644 Source/Apps/FDU/FDU.asm create mode 100644 Source/Apps/FDU/FDU.txt rename Source/Apps/{XM125 => XM}/Build.cmd (100%) rename Source/Apps/{XM125 => XM}/Clean.cmd (100%) rename Source/Apps/{XM125 => XM}/xmdm125.asm (100%) rename Source/Apps/{XM125 => XM}/xmdm125.zip (100%) rename Source/Apps/{XM125 => XM}/xmhb.180 (100%) delete mode 100644 Source/RomDsk/MK4/FD_DIDE.COM delete mode 100644 Source/RomDsk/MK4/FD_DIO3.COM delete mode 100644 Source/RomDsk/N8/FD_N8.COM delete mode 100644 Source/RomDsk/SBC/FD_DIDE.COM delete mode 100644 Source/RomDsk/SBC/FD_DIO3.COM delete mode 100644 Source/RomDsk/ZETA/FD_ZETA.COM delete mode 100644 Source/RomDsk/ZETA2/FD_ZETA2.COM diff --git a/Doc/ChangeLog.txt b/Doc/ChangeLog.txt index 586f6e36..ff3a54fd 100644 --- a/Doc/ChangeLog.txt +++ b/Doc/ChangeLog.txt @@ -1,3 +1,9 @@ +Version 2.8.4 +------------- +- WBW: FD.COM renamed to FDU.COM and integrated with build +- WBW: FDU.COM enhanced to select FDC hardare at startup to + eliminate multiple versions. + Version 2.8.3 ------------- - WBW: Added MODE command diff --git a/Doc/FDU.txt b/Doc/FDU.txt new file mode 100644 index 00000000..6e13550c --- /dev/null +++ b/Doc/FDU.txt @@ -0,0 +1,473 @@ +================================================================ +Floppy Disk Utility (FDU) v5.0 for RetroBrew Computers +Disk IO / Zeta / Dual-IDE / N8 +================================================================ + +Updated September 2, 2017 +by Wayne Warthen (wwarthen@gmail.com) + +Application to test the hardware functionality of the Floppy +Disk Controller (FDC) on the ECB DISK I/O, DISK I/O V3, ZETA +SBC, Dual IDE w/ Floppy, or N8 board. + +The intent is to provide a testbed that allows direct testing +of all possible media types and modes of access. The +application supports read, write, and format by sector, track, +and disk as well as a random read/write test. + +The application supports access modes of polling, interrupt, +INT/WAIT, and DRQ/WAIT. At present, it supports 3.5" media at +DD (720KB) and HD (1.44MB) capacities. It also now supports +5.25" media (720KB and 1.2MB) and 8" media (1.11MB) as well. +Additional media will be added when I have time and access to +required hardware. Not all modes are supported on all +platforms and some modes are experimental in all cases. + +In many ways this application is merely reinventing the wheel +and performs functionality similar to existing applications, +but I have not seen any other applications for RetroBrew +Computers hardware that provide this range of functionality. + +While the application is now almost entirely new code, I would +like to acknowledge that much was derived from the previous +work of Andrew Lynch and Dan Werner. I also want to credit +Sergio Gimenez with testing the 5.25" drive support and Jim +Harre with testing the 8" drive support. Support for Zeta 2 +comes from Segey Kiselev. Thanks! + +General Usage +------------- + +In general, usage is self explanatory. At invocation, you +must select the floppy disk controller (FDC) that you are +using. Subsequently, the main menu allows you to set the +unit, media, and mode to test. These settings MUST match your +situation. Read, write, format, and verify functions are +provided. A sub-menu will allow you to choose sector, track, +disk, or random tests. + +The verify function requires a little explanation. It will +take the contents of the current in-memory disk buffer, save +it, and compare it to the selected sectors. So, you must +ensure that the sectors to be verified already have been +written with the same pattern as the buffer contains. I +typically init the buffer to a pattern, write the pattern to +the entire disk, then verify the entire disk. + +Another submenu is provided for FDC commands. This sub-menu +allows you to send low-level commands directly to FDC. You +*must* know what you are doing to use this sub-menu. For +example, in order to read a sector using this sub-menu, you +will need to perform specify, seek, sense int, and read +commands specifying correct values (nothing is value checked +in this menu). + +Required Hardware/BIOS +---------------------- + +Of course, the starting point is to have a supported hardware +configuration. The following Z80 / Z180 based CPU boards are +supported: + + - SBC V1/2 + - Zeta + - Zeta 2 + - N8 + - Mark IV + +You must be using either a RomWBW or UBA based OS version. + +You must have one of the following floppy disk controllers: + + - Disk IO ECB Board FDC + - Disk IO 3 ECB Board FDC + - Dual-IDE ECB Board FDC + - Zeta SBC onboard FDC + - Zeta 2 SBC onboard FDC + - N8 SBC onboard FDC + +Finally, you will need a floppy drive connected via an +appropriate cable: + +Disk IO - no twist in cable, drive unit 0/1 must be selected by jumper on drive +DISK IO 3, Zeta, Zeta 2 - cable with twist, unit 0 after twist, unit 1 before twist +DIDE, N8 - cable with twist, unit 0 before twist, unit 1 after twist + +Note that FDU does not utilize your systems ROM or OS to +access the floppy system. FDU interacts directly with +hardware. Upon exit, you may need to reset your OS to get the +floppy system back into a state that is expected. + +The Disk I/O should be jumpered as follows: + +J1: depends on use of interrupt modes (see interrupt modes below) +J2: pins 1-2, & 3-4 jumpered +J3: hardware dependent timing for DMA mode (see DMA modes below) +J4: pins 2-3 jumpered +J5: off +J6: pins 2-3 jumpered +J7: pins 2-3 jumpered +J8: off +J9: off +J10: off +J11: off +J12: off + +Note that J1 can be left on even when not using interrupt +modes. As long as the BIOS is OK with it, that is fine. Note +also that J3 is only relevant for DMA modes, but also can be +left in place when using other modes. + +The Disk I/O 3 board should be jumpered at the default settings: + +JP2: 3-4 +JP3: 1-2 for int mode support, otherwise no jumper +JP4: 1-2, 3-4 +JP5: 1-2 +JP6: 1-2 +JP7: 1-2, 3-4 + +Zeta & Zeta 2 do not have any relevant jumper settings. The +hardwired I/O ranges are assumed in the code. + +The Dual-IDE board should be jumpered as follows: + +K3 (DT/R or /RD): /RD +P5 (bd ID): 1-2, 3-4 (for $20-$3F port range) + +There are no specific N8 jumper settings, but the default +I/O range starting at $80 is assumed in the published code. + + +Modes of Operation +------------------ + +You can select the following test modes. Please refer to the +chart that follows to determine which modes should work with +combinations of Z80 CPU speed and media format. + +WARNING: In general, only the polling mode is considered fully +reliable. The other modes are basically experimental and +should only be used if you know exactly what you are doing. + +Polling: Traditional polled input/output. Works well and very +reliable with robust timeouts and good error recovery. Also, +the slowest performance which precludes it from being used +with 1.44MB floppy on a 4MHz Z80. This is definitely the mode +you want to get working before any others. It does not require +J1 (interrupt enable) on DISK I/O and does not care about the +setting of J3. + +Interrupt: Relies on FDC interrupts to determine when a byte +is ready to be read/written. It does *not* implement a +timeout during disk operations. For example, if there is no +disk in the drive, this mode will just hang until a disk is +inserted. This mode *requires* that the host has interrupts +active using interrupt mode 1 (IM1) and interrupts attached to +the FDC controller. The BIOS must be configured to handle +these interrupts safely. + +Fast Interrupt: Same as above, but sacrifices additional +reliability for faster operation. This mode will allow a +1.44MB floppy to work with a 4MHz Z80 CPU. However, if any +errors occur (even a transient read error which is not +unusual), this mode will hang. The same FDC interrupt +requirements as above are required. + +INT/WAIT: Same as Fast Interrupt, but uses CPU wait instead of +actual interrupt. This mode is exclusive to the original Disk +IO board. It is subject to all the same issues as Fast +Interrupt, but does not need J1 shorted. J3 is irrelevant. + +DRQ/WAIT: Uses pseudo DMA to handle input/output. Does not +require that interrupts (J1) be enabled on the DISK I/O. +However, it is subject to all of the same reliability issues +as "Fast Interrupt". This mode is exclusive to the original +Disk IO board. At present, the mode is *not* implemented! + +The chart below attempts to describe the combinations that +work for me. By far, the most reliable mode is Polling, but +it requires 8MHz CPU for HD disks. + +DRQ/WAIT --------------------------------+ +INT/WAIT -----------------------------+ | +Fast Interrupt --------------------+ | | +Interrupt ----------------------+ | | | +Polling ---------------------+ | | | | + | | | | | +CPU Speed --------------+ | | | | | + | | | | | | + | | | | | | + +3.5" DD (720K) ------ 4MHz Y Y Y Y X + 8MHz+ Y Y Y Y X + +3.5" HD (1.44M) ----- 4MHz N N Y Y X + 8MHz+ Y Y Y Y X + +5.25" DD (360K) ----- 4MHz Y Y Y Y X + 8MHz+ Y Y Y Y X + +5.25" HD (1.2M) ----- 4MHz N N Y Y X + 8MHz+ Y Y Y Y X + +8" DD (1.11M) ------- 4MHz N N Y Y X + 8MHz+ Y Y Y Y X + +Y = Yes, works +N = No, does not work +X = Experimental, probably won't work + +Tracing +------- + +Command/result activity to/from the FDC will be written out if +the trace setting is changed from '00' to '01' in setup. +Additionally, if a command failure is detected on any command, +that specific comand and results are written regardless of the +trace setting. + +The format of the line written is: +: --> [] + +For example, this is the output of a normal read operation: +READ: 46 01 00 00 01 02 09 1B FF --> 01 00 00 00 00 02 02 [OK] + +Please refer to the i8272 data sheet for information on the +command and result bytes. + +Note that the sense interrupt command can return a non-OK +result. This is completely normal in some cases. It is +necessary to "poll" the drive for seek status using sense +interrupt. If there is nothing to report, then the result +will be INVALID COMMAND. Additionally, during a recalibrate +operation, it may be necessary to issue the command twice +because the command will only step the drive 77 times looking +for track 0, but the head may be up to 80 tracks away. In +this case, the first recalibrate fails, but the second should +succeed. Here is what this would look like if trace is turned +on: + +RECALIBRATE: 07 01 --> [OK] +SENSE INTERRUPT: 08 --> 80 [INVALID COMMAND] + ... + ... + ... +SENSE INTERRUPT: 08 --> 80 [INVALID COMMAND] +SENSE INTERRUPT: 08 --> 71 00 [ABNORMAL TERMINATION] +RECALIBRATE: 07 01 --> [OK] +SENSE INTERRUPT: 08 --> 21 00 [OK] + +Another example is when the FDC has just been reset. In this +case, you will see up to 4 disk change errors. Again these +are not a real problem and to be expected. + +When tracing is turned off, the application tries to be +intelligent about error reporting. The specific errors from +sense interrupt documented above will be suppressed because +they are not a real problem. All other errors will be +displayed. + +Error Handling +-------------- + +There is no automated error retry logic. This is very +intentional since the point is to expose the controller and +drive activity. Any error detected will result in a prompt to +abort, retry, or continue. Note that some number of errors is +considered normal for this technology. An occasional error +would not necessarily be considered a problem. + +CPU Speed +--------- + +Starting with v5.0, the application adjusts it's timing loops +to the actual system CPU speed by querying the BIOS for the +current CPU speed. + +Interleave +---------- + +The format command now allows the specification of a sector +interleave. It is almost always the case that the optimal +interleave will be 2 (meaning 2:1). + +360K Media +---------- + +The 360K media definition should work well for true 360K +drives. However, it will generally not work with 1.2M +drives. This is because these drives spin at 360RPM instead +of the 300RPM speed of true 360K drives. Additionally, 1.2M +drives are 80 tracks and 360K drives are 40 tracks and, so +far, there is no mechanism in FD to "double step" as a way to +use 40 track media in 80 track drives. + +With this said, it is possible to configure some 1.2M 5.25" +drives to automatically spin down to 300RPM based on a density +select signal (DENSEL). This signal is asserted by FD for +360K media, so IF you have configured your drive to react to +this signal correctly, you will be able to use the 360K media +defintion. Most 1.2M 5.25" drives are NOT configured this way +by default. TEAC drives are generally easy to modify and have +been tested by the author and do work in this manner. Note +that this does not address the issue of double stepping above; +you will just be using the first 40 of 80 tracks. + +Support +------- + +I am happy to answer questions as fast and well as I am able. +Best contact is wwarthen@gmail.com or post something on the +RetroBrew Computers Forum +https://www.retrobrewcomputers.org/forum/. + +Changes +------- + +WW 8/12/2011 + +Removed call to pulse TC in the FDC initialization after +determining that it periodically caused the FDC to write bad +sectors. I am mystified by this, but definitely found it to +be true. Will revisit at some point -- probably a timing +issue between puslsing TC and whatever happens next. + +Non-DMA mode was being set incorrectly for FAST-DMA mode. It +was set for non-DMA even though we were doing DMA. It is +interesting that it worked fine anyway. Fixed it anyway. + +DIO_SETMEDIA was not clearing DCD_DSKRDY as it should. Fixed. + +WW 8/26/2011: v1.1 + +Added support for Zeta. Note that INT/WAIT and DRQ/WAIT are +not available on Zeta. Note that Zeta provides the ability to +perform a reset of the FDC independent of a full CPU reset. +This is VERY useful and the FDC is reset anytime a drive reset +is required. + +Added INT/WAIT support. + +WW 8/28/2011: V1.2 + +All changes in this version are Zeta specific. Fixed FDC +reset logic and motor status display for Zeta (code from +Sergey). + +Modified Zeta disk change display to include it in the command +output line. This makes more sense because a command must be +issued to select the desired drive first. You can use the +SENSE INT command id you want to check the disk change value +at any time. It will also be displayed with any other command +output display. + +WW 9/1/2011: V1.3 + +Added CPUFREQ configuration setting to tune delays based on +cpu speed. The build app is set for 8MHz which also seems to +work well for 4MHz CPU's. Faster CPU speeds will probably +require tuning this setting. + +WW 9/5/2011: V1.4 + +Changed the polling execution routines to utilize CPUFREQ +variable to optimize timeout counter. Most importantly, this +should allow the use of faster CPUs (like 20MHz). + +WW 9/19/2011: V1.5 + +Zeta changes only. Added a call to FDC RESET after any +command failure. This solves an issue where the drive remains +selected if a command error occurs. Also added FDC RESET to +FDC CONTROL menu. + +WW 10/7/2011: V2.0 + +Added support for DIDE. Only supports polling IO and it does +not appear any other modes are possible given the hardware +constraints. + +WW 10/13/2011: V2.1 + +Modified to support N8. N8 is essentially identical to Dual +IDE. The only real change is the IO addresses. In theory, I +should be able to support true DMA on N8 and will work on that. + +WW 10/20/2011: v2.2 + +I had some problems with the results being read were sometimes +missing a byte. Fixed this by taking a more strict approach +to watching the MSR for the exact bits that are expected. + +WW 10/22/2011: V2.3 + +After spending a few days trying to track down an intermittent +data corruption issue with my Dual IDE board, I added a verify +function. This helped me isolate the problem very nicely +(turned out to be interference from the bus monitor). + +WW 11/25/2011: V2.4 + +Preliminary support for DISKIO V3. Basically just assumed +that it operates just like the Zeta. Needs to be verified +with real hardware as soon as I can. + +WW 1/9/2012: V2.5 + +Modified program termination to use CP/M reset call so that a +warm start is done and all drives are logged out. This is +important because media may have been formatted during the +program execution. + +WW 2/6/2012: v2.6 + +Added support for 5.25" drives as tested by Sergio. + +WW 4/5/2012: v2.7 + +Added support for 8" drives as tested by Jim Harre. + +WW 4/6/2012: v2.7a + +Fixed issue with media selection menu to remove duplicate +entries. + +WW 4/8/2012: v2.7b + +Corrected the handling of the density select signal. + +WW 5/22/2012: v2.8 + +Added new media definitions (5.25", 320K). + +WW 6/1/2012: v2.9 + +Added interleave capability on format. + +WW 6/5/2012: v3.0 + +Documentation cleanup. + +WW 7/1/2012: v3.1 + +Modified head load time (HLT) for 8" media based on YD-180 +spec. Now set to 50ms. + +WW 6/17/2013: v3.2 + +Cleaned up SRT, HLT, and HUT values. + +SK 2/10/2015: v3.3 + +Added Zeta SBC v2 support (Sergey Kiselev) + +WW 3/25/2015: v4.0 + +Renamed from FDTST --> FD + +WW 9/2/2017: v5.0 + +Renamed from FD to FDU. +Added runtime selection of FDC hardware. +Added runtime timing adjustment. \ No newline at end of file diff --git a/Source/Apps/Build.cmd b/Source/Apps/Build.cmd index 151dea99..13737cef 100644 --- a/Source/Apps/Build.cmd +++ b/Source/Apps/Build.cmd @@ -20,7 +20,8 @@ call :asm Mode || goto :eof zx Z80ASM -SYSGEN/F -setlocal & cd XM125 && call Build || exit /b 1 & endlocal +setlocal & cd XM && call Build || exit /b 1 & endlocal +setlocal & cd FDU && call Build || exit /b 1 & endlocal goto :eof diff --git a/Source/Apps/Clean.cmd b/Source/Apps/Clean.cmd index f10de89e..cf98eb56 100644 --- a/Source/Apps/Clean.cmd +++ b/Source/Apps/Clean.cmd @@ -5,4 +5,5 @@ if exist *.bin del *.bin if exist *.com del *.com if exist *.lst del *.lst -setlocal & cd XM125 && call Clean || exit /b 1 & endlocal +setlocal & cd XM && call Clean || exit /b 1 & endlocal +setlocal & cd FDU && call Clean || exit /b 1 & endlocal diff --git a/Source/Apps/FDU/Build.cmd b/Source/Apps/FDU/Build.cmd new file mode 100644 index 00000000..f8cdeeb2 --- /dev/null +++ b/Source/Apps/FDU/Build.cmd @@ -0,0 +1,13 @@ +@echo off +setlocal + +set TOOLS=../../../Tools +set PATH=%TOOLS%\tasm32;%PATH% +set TASMTABS=%TOOLS%\tasm32 + +tasm -t80 -b -fFF FDU.asm FDU.com FDU.lst + +if errorlevel 1 goto :eof + +move /Y FDU.com .. +copy /Y FDU.txt ..\..\..\Doc\ \ No newline at end of file diff --git a/Source/Apps/FDU/Clean.cmd b/Source/Apps/FDU/Clean.cmd new file mode 100644 index 00000000..011475b3 --- /dev/null +++ b/Source/Apps/FDU/Clean.cmd @@ -0,0 +1,4 @@ +@echo off +if exist *.com del *.com +if exist *.lst del *.lst +if exist *.zip del *.zip diff --git a/Source/Apps/FDU/FDU.asm b/Source/Apps/FDU/FDU.asm new file mode 100644 index 00000000..441e5d22 --- /dev/null +++ b/Source/Apps/FDU/FDU.asm @@ -0,0 +1,4421 @@ +;=============================================================================== +; FDU - FLOPPY DISK UTILITY PROGRAM FOR RETROBREW COMPUTERS +;=============================================================================== +; +; AUTHOR: WAYNE WARTHEN (DERIVED FROM FDCMON BY ANDREW LYNCH & DAN WERNER) +;_______________________________________________________________________________ +; +; CHANGELOG: +; 2011-08-05: v1.0 INITIAL RELEASE +; 2011-08-25: v1.1 SUPPORT ZETA +; SUPPORT INT/WAIT +; 2011-08-28: v1.2 FIX ZETA RESET LOGIC (from Sergey) +; FIX ZETA MOTOR DISPLAY (from Sergey) +; NEW ZETA DISK CHANGE DISPLAY +; 2011-09-01: V1.3 ADDED CONFIGURABLE CPU FREQUENCY FOR DELAYS +; 2011-09-05: V1.4 USE CPU FREQUENCY FOR POLLING TIMEOUT +; 2011-09-19: V1.5 IMPROVED USE OF FDC RESET ON ZETA +; ADDED FDC RESET TO FDC CONTROL MENU +; 2011-09-24: V1.5a MINOR CORRECTION TO POLLING ROUTINE TO AVOID OVERRUN +; 2011-10-07: V2.0 PRELIMINARY SUPPORT FOR DUAL IDE +; 2011-10-13: V2.1 PRELIMINARY SUPPORT FOR N8 +; 2011-10-20: V2.2 INCLUDE BUSY & NDMA BITS IN RESULTS BYTE READY CHECKING +; 2011-10-22: V2.3 ADDED VERIFY FUNCTION +; 2011-11-25: V2.4 ADDED SUPPORT FOR DISKIO V3 +; 2012-01-09: V2.5 WARM START CP/M AT TERMINATION TO LOGOUT ALL DRIVES +; 2012-01-09: V2.6 EXPERIMENTAL SUPPORT FOR 5 1/4" DRIVES +; 2012-04-05: V2.7 SUPPORT FOR 8" DRIVES +; 2012-04-06: V2.7a FIXED MEDIA SELECTION MENU (DUPLICATE ENTRIES) +; 2012-04-08: V2.7b HANDLE DENSITY SELECT PROPERLY +; 2012-05-22: V2.8 ADDED NEW MEDIA DEFINITIONS (5.25", 320K) +; 2012-06-01: V2.9 ADDED INTERLEAVE CAPABILITY IN FORMAT COMMAND +; 2012-06-05: V3.0 DOCUMENTATION CLEANUP +; 2012-07-01: V3.1 MODIFY HLT FOR 8" MEDIA (50ms PER YD-180 SPEC) +; 2013-06-17: V3.2 CLEANED UP THE SRT, HLT, AND HUT VALUES +; 2015-02-10: V3.3 ADDED ZETA SBC V2 SUPPORT (SERGEY KISELEV) +; 2015-03-25: V4.0 RENAMED APP FDTST --> FD +; 2017-09-02: V5.0 RENAMED APP TO FDU (FLOPPY DISK UTILITY) +; DYNAMIC FDC SELECTION AT STARTUP +; DYNAMIC CPU SPEED ADJUSTMENT +; +;_______________________________________________________________________________ +; +; BUILDING: +; CAN BE BUILT WITH TASM LIKE THIS: +; TASM -t80 -b -fFF FD.ASM FD.COM FD.LST +; +; TODO: +; 1) CURRENT TRACK IS UPDATED EVEN IF SEEK FAILS! (DEFER, RECOVERS AUTOMATICALLY) +; 2) CLEAN UP UTILITY ROUTINES (DEFER, NOT IMPORTANT) +; 3) TIGHTEN UP THE TIMING LOOPS (DEFER, LOW PRIORITY, WORKING FINE AS IS) +; 4) MOVE STX EVALUATION TO DIO LEVEL? (NOT SURE..., LOW PRIORITY) +; 5) ALLOW TO ABORT AT PROMPTS +; 6) ALLOW TO ABORT OPERATION IN PROGRESS +; 7) MOVE CONTINUE PROMPT TO A SEPARATE ROUTINE +; +;_______________________________________________________________________________ +; +; +CPUFREQ .EQU 20 ; IN MHZ, USED TO COMPUTE DELAY FACTORS +; +FDC_DIO .EQU 0 +FDC_DIO3 .EQU 1 +FDC_ZETA .EQU 2 +FDC_ZETA2 .EQU 3 +FDC_DIDE .EQU 4 +FDC_N8 .EQU 5 +; +_DIO .EQU 1 << FDC_DIO +_DIO3 .EQU 1 << FDC_DIO3 +_ZETA .EQU 1 << FDC_ZETA +_ZETA2 .EQU 1 << FDC_ZETA2 +_DIDE .EQU 1 << FDC_DIDE +_N8 .EQU 1 << FDC_N8 +; +FALSE .EQU 0 +TRUE .EQU ~FALSE +; +;=============================================================================== +; MAIN PROGRAM PROCEDURE +;=============================================================================== +; + .ORG 00100H + + ; SAVE PREVIOUS STACK POINTER, AND SWITCH TO OUR STACK + LD (STACKSAV),SP + LD SP,STACK + + ; GENERAL INITIALIZATION (BIOS & SPEED DETECTION) + CALL INIT + JR NZ,EXIT + + ; SELECT FD CONTROLLER + CALL NEWLINE + CALL FDCSEL + JR NZ,EXIT + + ; SETUP FOR DEFAULT MEDIA AND RUN THE MAIN MENU + CALL DIO_SETMEDIA + LD HL,MM_INFO + CALL RUNMENU + +EXIT: + LD C,00H ; CP/M SYSTEM RESET (WARM START) + CALL 0005H ; RETURN TO CP/M + + ; CLEAN UP AND RETURN TO OS + CALL NEWLINE + LD SP,(STACKSAV) + RET + + HALT ; SHOULD NEVER GET HERE + +; +;=============================================================================== +; FD CONTROLLER SELECTION +;=============================================================================== +; +INIT: + ; DISPLAY PROGRAM STARTUP BANNER + CALL NEWLINE + LD DE,STR_BANNER + CALL WRITESTR + + ; UNA UBIOS DETECTION... + LD A,($FFFD) ; FIXED LOCATION OF UNA API VECTOR + CP $C3 ; JP INSTRUCTION? + JR NZ,INIT1 ; IF NOT, NOT UNA + LD HL,($FFFE) ; GET JP ADDRESS + LD A,(HL) ; GET BYTE AT TARGET ADDRESS + CP $FD ; FIRST BYTE OF UNA PUSH IX INSTRUCTION + JR NZ,INIT1 ; IF NOT, NOT UNA + INC HL ; POINT TO NEXT BYTE + LD A,(HL) ; GET NEXT BYTE + CP $E5 ; SECOND BYTE OF UNA PUSH IX INSTRUCTION + JR NZ,INIT1 ; IF NOT, NOT UNA + LD DE,STR_UBIOS ; POINT TO UBIOS TAG + CALL WRITESTR ; AND DISPLAY IT + + ; GET CPU SPEED FROM UBIOS + LD C,0F8H ; UNA BIOS GET PHI FUNCTION + RST 08 ; RETURNS SPEED IN HZ IN DE:HL + LD A,E ; HACK TO GET APPROX SPEED IN MHZ + SRL A ; ... BY DIVIDING BY 1,048,576 + SRL A ; ... + SRL A ; ... + SRL A ; ... + INC A ; FIX UP FOR VALUE TRUNCATION + LD (CPUSPD),A ; SAVE IT + JR INIT3 ; AND DONE + +INIT1: + ; ROMWBW HBIOS DETECTION + LD HL,(0FFFEH) ; HL := ADR OR ROMWBW HBIOS IDENT + LD A,(HL) ; GET FIRST BYTE OF ROMWBW MARKER + CP 'W' ; MATCH? + JP NZ,INIT2 ; ABORT WITH INVALID CONFIG BLOCK + INC HL ; NEXT BYTE (MARKER BYTE 2) + LD A,(HL) ; LOAD IT + CP ~'W' ; MATCH? + JP NZ,INIT2 ; ABORT WITH INVALID CONFIG BLOCK + LD DE,STR_HBIOS ; POINT TO HBIOS TAG + CALL WRITESTR ; AND DISPLAY IT + + ; GET CPU SPEED FROM HBIOS + LD B,0F8H ; HBIOS SYSGET FUNCTION 0XF8 + LD C,0F0H ; CPUINFO SUBFUNCTION 0XF0 + RST 08 ; DO IT, L := CPU SPEED IN MHZ + LD A,L ; MOVE IT TO A + LD (CPUSPD),A ; SAVE IT + JR INIT3 ; AND DONE + +INIT2: + ; NO KNOWN BIOS DETECTED, BAIL OUT W/ ERROR + LD DE,STR_BIOERR + CALL WRITESTR + OR 0FFH + RET + +INIT3: + ; COMPUTE CPU SCALER FOR DELAY LOOPS + LD A,(CPUSPD) + CP 3 ; TEST FOR <= 2 (SPECIAL HANDLING) + JR C,INIT4 ; IF <= 2, SPECIAL PROCESSING + SUB 2 ; ADJUST AS REQUIRED BY DELAY FUNCTIONS + JR INIT5 ; AND CONTINUE +INIT4: + LD A,1 ; USE THE MIN VALUE OF 1 +INIT5: + LD (CPUSCL),A ; UPDATE CPU SCALER VALUE + + ; REMAINDER OF BANNER + CALL NEWLINE + LD DE,STR_BANNER2 + CALL WRITESTR + + ; INITIALIZATION DONE + XOR A + RET + +STR_BANNER .DB "Floppy Disk Utility (FDU) v5.0, 02-Sep-2017$" +STR_BANNER2 .DB "Copyright (C) 2017, Wayne Warthen, GNU GPL v3","$" +STR_HBIOS .DB " [HBIOS]$" +STR_UBIOS .DB " [UBIOS]$" +; +STR_BIOERR .DB "\r\n\r\n*** Unknown BIOS ***\r\n$" +; +CPUSPD .DB 20 ; DEFAULT TO SAFE (HIGH) VALUE +CPUSCL .DB 0 ; CPU SPEED DELAY SCALER (COMPUTED) + +; +;=============================================================================== +; FD CONTROLLER SELECTION +;=============================================================================== +; +FDCSEL: + ; PROMPT + LD DE,FSS_MENU + CALL WRITESTR +; +FDCSEL1: + CALL GETKEY + SUB '0' ; ASCII -> BINARY + CP FDCCNT + 1 ; TOO HIGH? + JR NC,FDCSEL1 ; IF SO, TRY AGAIN +; + OR A ; SET FLAGS + JR NZ,FDCSEL2 ; NOT ZERO, KEEP GOING + OR 0FFH ; SET NZ FOR EXIT REQUEST + RET ; AND RETURN +; +FDCSEL2: + ; SAVE SELECTED FDC IDENTIFIER + DEC A ; CONVERT TO ZERO-BASED FDC ID + LD (FDCID),A ; RECORD THE FDC ID + PUSH AF ; SAVE IT +; + ; CREATE AND SAVE A BIT MAPPED VALUE + INC A ; PREPARE LOOP COUNT + LD B,A ; AND PUT IN B + XOR A ; START WITH ALL BITS OFF + SCF ; ... AND CF SET +FDCSEL3: + RLA ; ROTATE BIT TO NEXT POSITION + DJNZ FDCSEL3 ; AND CONTINUE TILL DONE + LD (FDCBM),A ; SAVE BITMAP VALUE +; + POP AF ; RESTORE FDC ID + RLCA ; TIMES 4 + RLCA ; ... FOR 4 BYTE ENTRIES + LD HL,FDCTBL ; POINT TO FDC INSTANCE TABLE + CALL ADDHLA ; OFFSET TO DESRIED ENTRY, A TRASHED + LD E,(HL) ; LOAD LABEL PTR INTO DE + INC HL ; ... + LD D,(HL) ; ... + LD (FDCLBL),DE ; SAVE LABEL PTR + INC HL ; BUMP TO CFG POINTER + LD E,(HL) ; LOAD CFG PTR INTO DE + INC HL ; ... + LD D,(HL) ; ... + LD (FDCCFG),DE ; SAVE CFG PTR + LD IY,(FDCCFG) ; AND INIT A WORKING COPY +; + LD DE,(FDCLBL) ; GET LABEL POINTER + CALL WRITESTR ; AND DISPLAY IT +; + XOR A ; SIGNAL SUCCESS + RET ; DONE +; +; TABLE OF FDC CONTROLLERS +; +FDCTBL: ; LABEL CONFIG DATA + ; ----- ----------- + .DW STR_DIO, CFG_DIO + .DW STR_DIO3, CFG_DIO3 + .DW STR_ZETA, CFG_ZETA + .DW STR_ZETA2, CFG_ZETA2 + .DW STR_DIDE, CFG_DIDE + .DW STR_N8, CFG_N8 +FDCCNT .EQU ($-FDCTBL)/4 ; FD CONTROLLER COUNT +; +; FDC LABEL STRINGS +; +STR_DIO .TEXT "DISKIO$" +STR_DIO3 .TEXT "DISKIO3$" +STR_ZETA .TEXT "ZETA$" +STR_ZETA2 .TEXT "ZETA2$" +STR_DIDE .TEXT "DUAL-IDE$" +STR_N8 .TEXT "N8$" +; +; FDC CONFIGURATION BLOCKS +; +CFG_MSR .EQU 0 +CFG_DATA .EQU 1 +CFG_DIR .EQU 2 +CFG_DOR .EQU 3 +CFG_DCR .EQU 4 +CFG_DACK .EQU 5 +CFG_TC .EQU 6 +CFG_DMA .EQU 7 +; +CFG_DIO: +CFG_DIO3: +CFG_ZETA: + .DB 036H ; FDC MAIN STATUS REGISTER + .DB 037H ; FDC DATA PORT + .DB 038H ; DATA INPUT REGISTER + .DB 03AH ; DIGITAL OUTPUT REGISTER (LATCH) + .DB 0FFH ; DCR + .DB 0FFH ; DACK + .DB 0FFH ; TERMINAL COUNT (W/ DACK) + .DB 03CH ; PSEUDO DMA DATA PORT +; +CFG_ZETA2: + .DB 030H ; FDC MAIN STATUS REGISTER + .DB 031H ; FDC DATA PORT + .DB 0FFH ; DATA INPUT REGISTER + .DB 038H ; DIGITAL OUTPUT REGISTER + .DB 028H ; CONFIGURATION CONTROL REGISTER + .DB 0FFH ; DACK + .DB 0FFH ; TERMINAL COUNT (W/ DACK) + .DB 0FFH ; NOT USED BY ZETA SBC V2 +; +CFG_DIDE: + .DB 02AH ; FDC MAIN STATUS REGISTER + .DB 02BH ; FDC DATA PORT + .DB 0FFH ; DATA INPUT REGISTER + .DB 02CH ; DOR + .DB 02DH ; DCR + .DB 03CH ; DACK + .DB 03DH ; TERMINAL COUNT (W/ DACK) + .DB 0FFH ; NOT USED BY DIDE +; +CFG_N8: + .DB 08CH ; FDC MAIN STATUS REGISTER + .DB 08DH ; FDC DATA PORT + .DB 0FFH ; DATA INPUT REGISTER + .DB 092H ; DOR + .DB 091H ; DCR + .DB 090H ; DACK + .DB 093H ; TERMINAL COUNT (W/ DACK) + .DB 0FFH ; NOT USED BY N8 +; +FDCID .DB 0 ; FDC IDENTIFIER (0 INDEXED) +FDCBM .DB 0 ; FDC ID BITMAP +FDCLBL .DW 0 ; POINTER TO ACTIVE FDC LABEL STRING +FDCCFG .DW 0 ; POINTER TO ACTIVE CFG DATA +; +FSS_MENU: + .TEXT "\r\n" + .TEXT "SELECT FLOPPY DISK CONTROLLER:\r\n" + .TEXT " (0) Exit\r\n" + .TEXT " (1) Disk IO ECB Board\r\n" + .TEXT " (2) Disk IO 3 ECB Board\r\n" + .TEXT " (3) Zeta SBC Onboard FDC\r\n" + .TEXT " (4) Zeta 2 SBC Onboard FDC\r\n" + .TEXT " (5) Dual IDE ECB Board\r\n" + .TEXT " (6) N8 Onboard FDC\r\n" + .TEXT "=== OPTION ===> $\r\n" +; +;=============================================================================== +; MAIN MENU +;=============================================================================== +; +; MAIN MENU DATA +; +MM_TABLE .DB 'S' \ .DW MMS_SETUP, MM_SETUP +MM_ENTSIZ .EQU $ - MM_TABLE + .DB 'R' \ .DW MMS_READ, MM_READ + .DB 'W' \ .DW MMS_WRITE, MM_WRITE + .DB 'F' \ .DW MMS_FORMAT, MM_FORMAT + .DB 'V' \ .DW MMS_VERIFY, MM_VERIFY + .DB 'I' \ .DW MMS_INITBUF, MM_INITBUF + .DB 'D' \ .DW MMS_DUMPBUF, MM_DUMPBUF + .DB 'C' \ .DW MMS_FDCMENU, MM_FDCMENU + .DB 'X' \ .DW MMS_EXIT, 0000H +MM_COUNT .EQU (($ - MM_TABLE) / MM_ENTSIZ) ; # ENTRIES IN TABLE +MM_INFO: .DW MM_DRAW + .DW MM_TABLE + .DB MM_ENTSIZ + .DB MM_COUNT +; +; MAIN MENU DISPLAY STRINGS +; +STR_MAINMENU: + .TEXT "=======================<< FDU MAIN MENU >>======================\r\n" +; .TEXT "(S)ETUP: UNIT=XX MEDIA=XXXXXX MODE=XXXXXXXXXX TRACE=XX\r\n" + .TEXT "(S)ETUP: UNIT=" +MV_UNIT .TEXT "XX" + .TEXT " MEDIA=" +MV_MED .TEXT "XXXXXX" + .TEXT " MODE=" +MV_MODE .TEXT "XXXXXXXXXX" + .TEXT " TRACE=" +MV_TRC .TEXT "XX" + .TEXT "\r\n" + .TEXT "----------------------------------------------------------------\r\n" + .TEXT "(R)EAD (W)RITE (F)ORMAT (V)ERIFY\r\n" + .TEXT "(I)NIT BUFFER (D)UMP BUFFER FDC (C)MDS E(X)IT\r\n" + .TEXT "=== OPTION ===> $\r\n" +; +MMS_SETUP: .TEXT "SETUP$" +MMS_READ: .TEXT "READ$" +MMS_WRITE: .TEXT "WRITE$" +MMS_FORMAT: .TEXT "FORMAT$" +MMS_VERIFY: .TEXT "VERIFY$" +MMS_INITBUF: .TEXT "INITIALIZE BUFFER$" +MMS_DUMPBUF: .TEXT "DUMP BUFFER$" +MMS_FDCMENU: .TEXT "FDC MENU$" +MMS_EXIT: .TEXT "EXIT$" +; +; MAIN MENU DRAW PROCEDURE +; +MM_DRAW: + CALL NEWLINE + CALL NEWLINE + + ; UPDATE FDC LABEL + LD DE,STR_MAINMENU + 5 + LD A,' ' + LD (DE),A + INC DE + LD HL,(FDCLBL) + CALL COPYSTR + LD A,' ' + LD (DE),A + + ; UPDATE UNIT + LD DE,MV_UNIT + LD A,(DCD_UNIT) + CALL HEXSTRBYTE + + ; UPDATE MEDIA + LD DE,(MDB_LABEL) + LD HL,MV_MED + CALL STRCPY + + ; UPDATE MODE + LD A,(DCD_MD) + RLA + LD E,A + LD D,0 + LD HL,MDT + ADD HL,DE + LD E,(HL) + INC HL + LD D,(HL) + EX DE,HL + LD E,(HL) + INC HL + LD D,(HL) + LD HL,MV_MODE + CALL STRCPY + + ; UPDATE TRACE + LD DE,MV_TRC + LD A,(DCD_TRACE) + CALL HEXSTRBYTE + + ; DISPLAY THE MENU + LD DE,STR_MAINMENU + CALL WRITESTR + + RET +; +; MAIN MENU FUNCTIONS +; +MM_SETUP: + CALL MM_GETSETUP + CALL DIO_SETMEDIA + RET + +MM_READ: + LD A,DOP_READ + LD (DCD_DOP),A + CALL MM_GETTGT + CALL MM_GETTGTPARMS + CALL MMOP_PROC + RET + +MM_WRITE: + LD A,DOP_WRITE + LD (DCD_DOP),A + CALL MM_GETTGT + CALL MM_GETTGTPARMS + CALL MMOP_PROC + RET + +MM_FORMAT: + LD A,DOP_FORMAT + LD (DCD_DOP),A + CALL MM_GETTGT + CALL MM_GETTGTPARMS + CALL MM_GETINTRLV + CALL MMOP_PROC + RET + +MM_VERIFY: + LD A,DOP_VERIFY + LD (DCD_DOP),A + CALL MM_GETTGT + CALL MM_GETTGTPARMS + CALL MM_SETVFYBUF + CALL MMOP_PROC + RET + +MM_INITBUF: + CALL PC_SPACE + LD DE,MMPS_IBOPT + CALL WRITESTR +MM_IBKEY: + CALL GETKEYUC + CP 'P' ; PATTERN + JP Z,MM_IBPAT + CP 'F' ; FILL + JP Z,MM_IBFILL + JP MM_IBKEY +MM_IBPAT: + LD DE,MMBS_PATTERN + CALL PC_SPACE + CALL WRITESTR + CALL MM_GETPATTERN + LD HL,BUFFER + LD DE,BUFSIZ + LD A,(DCD_PATTERN) + LD B,A + CALL PAT_BUFFER + RET +MM_IBFILL: + LD DE,MMBS_FILL + CALL WRITESTR + CALL PC_SPACE + CALL MM_GETFILL + LD HL,BUFFER + LD DE,BUFSIZ + LD A,(DCD_FILL) + LD B,A + CALL FILL_BUFFER + RET + +MM_DUMPBUF: + LD HL,BUFFER ; SET ADDRESS TO DUMP + LD DE,BUFFER ; SET END ADDRESS + INC D ; + INC D ; + CALL DUMP_BUFFER ; DUMP BUFFER TO CONSOLE + RET + +MM_FDCMENU: + CALL DIO_INIT + CALL FDCMENU + CALL DIO_TERM + RET +; +; MAIN MENU SUPPORT FUNCTIONS +; +MM_GETSETUP: + LD DE,MMP_UNIT + LD HL,DCD_UNIT + LD BC,0003H + CALL GETHEXBYTERNG + + CALL MM_MEDIALIST + LD DE,MMP_MEDIA + LD HL,DCD_MT + LD B,0 + LD A,MIT_ENTCNT + DEC A + LD C,A + CALL GETHEXBYTERNG + +MM_GETMODE: + CALL MM_MODELIST + LD DE,MMP_MODE + LD HL,DCD_MD + LD B,0 + LD A,MDT_ENTCNT + DEC A + LD C,A + CALL GETHEXBYTERNG + + ; VALIDATE MODE AGAINST CURRENT FDC + INC A ; PREP VALUE FOR LOOP + LD B,A ; PUT IN LOOP COUNTER + XOR A ; CLEAR A + SCF ; SET CF +MM_GETMODE1: + RLA ; ROTATE BIT ONE POSITION + DJNZ MM_GETMODE1 ; UNTIL BIT SET FOR MODE SPECIFIED + PUSH AF ; SAVE IT + LD A,(FDCID) ; A := FDC ID + LD HL,MD_MAP ; HL := START OF MODE MAP + CALL ADDHLA ; OFFSET TO CORRECT ENTRY FOR FDC + POP AF ; RECOVER MODE BIT VALUE + AND (HL) ; MASK WITH MODE MAP OF FDC + JR NZ,MM_TRACE ; NON-ZERO IS A MODE MATCH FOR FDC, CONTINUE + LD DE,MM_MODEERR ; INVALID MODE FOR FDC ERROR MESSAGE + CALL WRITESTR + JR MM_GETMODE + +MM_TRACE: + LD DE,MMP_TRACE + LD HL,DCD_TRACE + LD BC,0001H + CALL GETHEXBYTERNG + + RET + +MM_GETFILL: + LD DE,MMP_FILL + LD HL,DCD_FILL + CALL GETHEXBYTE + RET + +MM_GETPATTERN: + LD DE,MMP_PATTERN + LD HL,DCD_PATTERN + CALL GETHEXBYTE + RET + +MM_GETTGT: + CALL PC_SPACE + LD A,(DCD_DOP) + LD DE,MMPS_TGTF + CP DOP_FORMAT + JP Z,MM_GETTGT0 + LD DE,MMPS_TGTRW +MM_GETTGT0: + CALL WRITESTR +MM_GETTGT1: + CALL GETKEYUC + + LD B,MMT_TRACK + LD DE,MMTS_TRACK + CP 'T' + JP Z,MM_GETTGT3 + LD B,MMT_DISK + LD DE,MMTS_DISK + CP 'D' + JP Z,MM_GETTGT3 + ; FORMAT CANNOT DO THE NEXT ONES (GETTGT2 CHECKS FOR THIS) + LD B,MMT_SECTOR + LD DE,MMTS_SECTOR + CP 'S' + JP Z,MM_GETTGT2 + LD B,MMT_RANDOM + LD DE,MMTS_RANDOM + CP 'R' + JP Z,MM_GETTGT2 + JP MM_GETTGT1 +MM_GETTGT2: ; PREVENT FORMAT FROM USING SECTOR OR RANDOM FUNCTIONS + LD A,(DCD_DOP) + CP DOP_FORMAT + JP Z,MM_GETTGT1 +MM_GETTGT3: + LD A,B + LD (MMD_TGT),A + CALL WRITESTR + RET + +MM_GETTGTPARMS: + LD A,(MMD_TGT) + CP MMT_DISK + JP Z,MM_GETTGTPARMS1 + CP MMT_RANDOM + JP Z,MM_GETTGTPARMS1 + CALL MM_GETTRACK + CALL MM_GETHEAD + LD A,(MMD_TGT) + CP MMT_TRACK + JP Z,MM_GETTGTPARMS1 + CALL MM_GETSECTOR + JP MM_GETTGTPARMSX +MM_GETTGTPARMS1: + LD A,1 ; FIX UP THE SECTOR VALUE TO BE 1 IF NOT SINGLE SECTOR MODE + LD (DCD_SECTOR),A +MM_GETTGTPARMSX: + RET + +MM_GETTRACK: + LD DE,MMP_TRACK + LD HL,DCD_TRACK + LD B,0 + LD A,(MDB_NUMCYL) + DEC A + LD C,A + CALL GETHEXBYTERNG + RET + +MM_GETHEAD: + LD DE,MMP_HEAD + LD HL,DCD_HEAD + LD B,0 + LD A,(MDB_NUMHD) + DEC A + LD C,A + CALL GETHEXBYTERNG + RET + +MM_GETSECTOR: + LD DE,MMP_SECTOR + LD HL,DCD_SECTOR + LD A,(MDB_SOT) + LD B,A + LD A,(MDB_EOT) + LD C,A + CALL GETHEXBYTERNG + RET + +MM_GETINTRLV: + LD DE,MMP_INTRLV + LD HL,DCD_INTRLV + LD B,1 + LD A,(MDB_NUMSEC) + LD C,A + CALL GETHEXBYTERNG + RET + +MM_MEDIALIST: + LD HL,MIT + LD B,MIT_ENTCNT + LD C,0 +MM_MEDIALISTLOOP: + CALL NEWLINE + LD A,C + CALL PRTHEXBYTE + CALL PC_COLON + CALL PC_SPACE + PUSH HL + + LD A,(HL) ; HL = ENTRY VALUE + INC HL ; " + LD H,(HL) ; " + LD L,A ; " + + INC HL + INC HL + + LD A,(HL) ; HL = ENTRY VALUE + INC HL ; " + LD D,(HL) ; " + LD E,A ; " + + CALL WRITESTR + + POP HL + INC HL + INC HL + INC C + DJNZ MM_MEDIALISTLOOP + RET + +MM_MODELIST: + LD HL,MDT + LD B,MDT_ENTCNT + LD C,0 +MM_MODELISTLOOP: + CALL NEWLINE + LD A,C + CALL PRTHEXBYTE + CALL PC_COLON + CALL PC_SPACE + PUSH HL + + LD A,(HL) ; HL = ENTRY VALUE + INC HL ; " + LD H,(HL) ; " + LD L,A ; " + + INC HL + INC HL + + LD A,(HL) ; HL = ENTRY VALUE + INC HL ; " + LD D,(HL) ; " + LD E,A ; " + + CALL WRITESTR + + POP HL + INC HL + INC HL + INC C + DJNZ MM_MODELISTLOOP + RET + +MM_SETVFYBUF: + LD HL,BUFFER + LD DE,VFYBUF + LD BC,BUFSIZ + LDIR + RET +; +; MAIN MENU PROMPTS +; +MMP_FILL .TEXT "FILL VALUE$" +MMP_PATTERN .TEXT "PATTERN START VALUE$" +MMP_UNIT .TEXT "UNIT$" +MMP_TRACE .TEXT "TRACE LEVEL$" +MMP_TRACK .TEXT "TRACK$" +MMP_HEAD .TEXT "HEAD$" +MMP_SECTOR .TEXT "SECTOR$" +MMP_MEDIA .TEXT "MEDIA$" +MMP_MODE .TEXT "MODE$" +MMP_INTRLV .TEXT "INTERLEAVE$" +; +; MAIN MENU TARGET +; +MMT_SECTOR .EQU 'S' ; PERFORM OPERATION ON ONE SECTOR +MMT_TRACK .EQU 'T' ; PERFORM OPERATION ON ONE TRACK +MMT_DISK .EQU 'D' ; PERFORM OPERATION ON ENTIRE DISK +MMT_RANDOM .EQU 'R' ; PERFORM OPERATION ON RANDOM SECTORS +; +; MAIN MENU TARGET STRINGS +; +MMTS_SECTOR .TEXT "SECTOR$" +MMTS_TRACK .TEXT "TRACK$" +MMTS_DISK .TEXT "DISK$" +MMTS_RANDOM .TEXT "RANDOM$" +; +; MAIN MENU DATA +; +MMD_TGT .DB MMT_SECTOR +; +; MAIN MENU PROMPT STRINGS +; +MMPS_TGTRW .TEXT "(S)ECTOR, (T)RACK, (D)ISK, (R)ANDOM ===> $" +MMPS_TGTF .TEXT "(T)RACK, (D)ISK ===> $" +MMPS_IBOPT .TEXT "(P)ATTERN, (F)ILL ===> $" +; +; MAIN MENU BUFFER OPTIONS +; +MMBS_PATTERN .TEXT "PATTERN$" +MMBS_FILL .TEXT "FILL$" +; +; +; +MM_MODEERR .TEXT "\r\n\r\n*** SELECTED MODE NOT SUPPORTED ON HARDWARE ***\r\n$" +; +;________________________________________________________________________________________________________________________________ +; +; MAIN MENU OPERATIONS +;________________________________________________________________________________________________________________________________ +; +MMOP_PROC: + CALL NEWLINE + CALL NEWLINE + + LD A,FALSE + LD (DCD_ABORT),A + + CALL DIO_INIT + + LD A,(MMD_TGT) + CP MMT_DISK + JP Z,MMOP_PROCDISK + CP MMT_TRACK + JP Z,MMOP_PROCTRK + CP MMT_SECTOR + JP Z,MMOP_PROCSEC + CP MMT_RANDOM + JP Z,MMOP_PROCRND + + JP MMOP_PROCX + +MMOP_PROCDISK: + CALL MMOP_DISK + JP MMOP_PROCX + +MMOP_PROCTRK: + CALL MMOP_TRACK + JP MMOP_PROCX + +MMOP_PROCSEC: + CALL MMOP_SECTOR + JP MMOP_PROCX + +MMOP_PROCRND: + CALL MMOP_RANDOM + JP MMOP_PROCX + +MMOP_PROCX: + CALL DIO_TERM + RET + +MMOP_DISK: + LD A,0 + LD (DCD_TRACK),A + LD (DCD_HEAD),A +MMOP_DISK1: + LD A,(DCD_ABORT) + CP TRUE + JP Z,MMOP_DISKX + + CALL MMOP_TRACK + + LD A,(MDB_NUMHD) + LD C,A + LD A,(DCD_HEAD) ; INC HEAD + INC A + LD (DCD_HEAD),A + CP C ; # OF HEADS + JP NZ,MMOP_DISK1 ; LOOP IF LESS THAN # OF HEADS + LD A,0 ; RESET HEAD + LD (DCD_HEAD),A ; AND FALL THROUGH TO INC TRACK + + LD A,(MDB_NUMCYL) + LD C,A + LD A,(DCD_TRACK) ; INC TRACK + INC A + LD (DCD_TRACK),A + CP C ; # OF TRACKS + JP NZ,MMOP_DISK1 +MMOP_DISKX: + LD A,0 ; RESET TRACK TO A VALID VALUE + LD (DCD_TRACK),A + RET + +MMOP_TRACK: + LD A,(DCD_DOP) ; SPECIAL CASE, FORMAT IS TRACK-AT-A-TIME + CP DOP_FORMAT + JP Z,MMOP_TRACKFMT + + LD A,(MDB_SOT) + LD (DCD_SECTOR),A +MMOP_TRACK1: + LD A,(DCD_ABORT) + CP TRUE + JP Z,MMOP_TRACKX + + CALL DIO_RUN + + LD A,(MDB_EOT) + LD C,A + INC C ; ONE MORE THAN EOT + LD A,(DCD_SECTOR) ; INC SECTOR + INC A + LD (DCD_SECTOR),A + CP C ; > MAX SECTOR? + JP NZ,MMOP_TRACK1 + JP MMOP_TRACKX + +MMOP_TRACKFMT: + CALL DIO_RUN + JP MMOP_TRACKX + +MMOP_TRACKX: + LD A,(MDB_SOT) ; RESET SECTOR TO A VALID VALUE + LD (DCD_SECTOR),A + RET + +MMOP_SECTOR: + CALL DIO_RUN + RET + +MMOP_RANDOM: + LD B,20H ; READ 20H SECTORS RANDOMLY + +MMOP_RANDOM0: + LD A,(DCD_ABORT) + CP TRUE + JP Z,MMOP_RANDOMX + + PUSH BC + +MMOP_RANDOM1: ; GENERATE RANDOM TRACK + LD A,(MDB_NUMCYL) + LD C,A + CALL RNDBYTE + AND 7FH ; USE 7 BITS FOR UP TO 128 TRACKS + CP C ; MAX TRACK IS 4F + 1 = 50H + JP P,MMOP_RANDOM1 + LD (DCD_TRACK),A + + ; GENERATE RANDOM HEAD + CALL RNDBYTE + AND 01H ; JUST USE LOW ORDER BIT + LD (DCD_HEAD),A + +MMOP_RANDOM2: ; GENERATE RANDOM SECTOR + LD A,(MDB_EOT) + LD C,A + INC C ; ONE MORE THEN MAX SECTOR + CALL RNDBYTE + AND 1FH ; USE 5 BITS FOR UP TO 32 SECTORS + CP C ; MAX SECTOR NUM IS 9 + 1 = 0AH + JP P,MMOP_RANDOM2 + CP 00H ; SECTOR NUM STARTS AT 1, DON'T ALLOW ZERO + JP Z,MMOP_RANDOM2 + LD (DCD_SECTOR),A + + CALL DIO_RUN + + POP BC + DJNZ MMOP_RANDOM0 + +MMOP_RANDOMX: + RET + +; +;=============================================================================== +; DISK INPUT/OUTPUT SERVICES, DEVICE INDEPENDENT (LOGICAL) ACCESS TO STORAGE +; TRANSLATES BETWEEN LOGICAL AND PHYSICAL DEVICE OPERATIONS +;=============================================================================== +; +; +STR_CONTINUE: + .TEXT "CONTINUE? (A)BORT, (R)ETRY, (I)GNORE ===> $" +; +DIO_SETMEDIA: + ; INITIZLIZE THE MEDIA DESCRIPTION BLOCK + ; FILL IN MDB BASED ON USER SELECTION + LD A,(DCD_MT) ; A = MEDIA ID (OFFSET IN MEDIA INDEX TABLE) + ADD A,A ; * 2 (2 BYTES PER ENTRY) + LD H,0 ; MOVE IT TO HL + LD L,A ; " + LD DE,MIT ; DE = START OF MEDIA INDEX TABLE + ADD HL,DE ; ADD HL TO DE, HL NOW POINTS TO DESIRED ENTRY + LD A,(HL) ; HL = ENTRY VALUE + INC HL ; " + LD H,(HL) ; " + LD L,A ; " + LD DE,MDB ; HL = SOURCE, DE = DESTINATION + LD BC,MDB_LEN ; BC = BYTES TO COPY + LDIR ; COPY ENTRY TO FCD + + LD A,FALSE ; SET DRIVE READY TO FALSE! + LD (DCD_DSKRDY),A ; " + RET +; +DIO_INIT: + ; UPDATE DRIVE SELECTTION + LD A,(FCD_DS) ; GET THE CURRENT DRIVE SELECTION + LD B,A ; SAVE IN B + LD A,(DCD_UNIT) ; GET THE NEW DRIVE SELECTION + CP B ; CHANGED? + ; WE NEED TO SET DRIVE STATUS TO NOT READY IFF IT CHANGED + JP Z,DIO_INIT1 ; DO NOT RESET DRIVE STATUS! + LD (FCD_DS),A ; UPDATE FCD_DS TO NEW VALUE + LD A,FALSE ; SET DRIVE READY TO FALSE! + LD (DCD_DSKRDY),A ; " + +DIO_INIT1: + ; INITIALIZE TRACE SETTING + LD A,(DCD_TRACE) + LD (FCD_TRACE),A + + LD HL,MDB_FCD ; HL = SOURCE + LD DE,FCD ; DE = DESTINATION + LD BC,FCD_LEN ; BC = BYTES TO COPY + LDIR ; BYTES COPY FROM MDB TO FCD + + LD A,0 ; ASSUME DMA (NON-DMA = 0) + LD (FCD_ND),A + LD A,(DCD_MD) + CP MD_DRQWAIT + JP Z,DIO_INIT2 ; YES, DMA NEEDED, DO IT + + LD A,1 ; SET TO NON-DMA (NDMA = 1) + LD (FCD_ND),A + +DIO_INIT2: + CALL FC_INIT + CALL FC_MOTORON + RET + +DIO_TERM: + CALL FC_MOTOROFF + RET +; +; DIO_CLRDSKCHG +; +DIO_CLRDSKCHG: + ; PROCESS ANY PENDING DISK CHANGE NOTIFICATIONS + LD B,5 +DIO_CLRDSKCHG1: + PUSH BC + CALL FC_SENSEINT + POP BC + CALL DIO_CHKFC + CP FRC_DSKCHG + RET NZ + DJNZ DIO_CLRDSKCHG1 + RET +; +; DIO_WTSEEK +; +; WAIT FOR PENDING SEEK OPERATION TO COMPLETE BY POLLING SENSEINT +; AND WAITING FOR ABTERM OR OK. +; +DIO_WTSEEK: + LD BC,1000H +; LD BC,20H ; *DEBUG* +; LD BC,1H ; *DEBUG* +DIO_WTSEEKLOOP: + PUSH BC + CALL FC_SENSEINT + POP BC + + LD A,(FST_RC) ; CHECK RC + CP FRC_ABTERM ; ABTERM = DONE + RET Z + CP FRC_OK ; OK = DONE + RET Z + + DEC BC ; CHECK LOOP COUNTER IN BC + LD A,B ; " + OR C ; " + JP NZ,DIO_WTSEEKLOOP ; LOOP UNTIL COUNTER EXHAUSTED + +DIO_DRIVERESET: + CALL NEWLINE + CALL NEWLINE + LD DE,STR_DRIVERESET + CALL WRITESTR + + CALL FC_RESETFDC + + CALL DIO_CLRDSKCHG + CALL DIO_CHKFC + CP FRC_INVCMD ; INVALID COMMAND IS CORRECT RESPONSE HERE + JP NZ,DIO_NORESP + + ; CONTINUE RESET SEQUENCE WITH 'SPECIFY' COMMAND + CALL FC_SPECIFY + CALL DIO_CHKFC + JP NZ,DIO_NORESP + + CALL FC_RECAL + CALL DIO_CHKFC + JP NZ,DIO_NORESP + + ; CAREFUL... FIRST RECAL MAY FAIL TO REACH TRACK 0 + ; SO WE ALLOW FOR A SECOND TRY IN CASE OF A FAILURE + CALL DIO_WTSEEK + CALL DIO_CHKFC + JP Z,DIO_DRIVERESET1 + + ; SECOND TRY, ONLY IF NEEDED + CALL FC_RECAL + CALL DIO_CHKFC + JP NZ,DIO_NORESP + + CALL DIO_WTSEEK + CALL DIO_CHKFC + JP NZ,DIO_NORESP + JP DIO_DRIVERESET1 + +DIO_NORESP: + CALL NEWLINE + LD DE,STR_NORESP + CALL WRITESTR + RET + +DIO_DRIVERESET1: + LD A,TRUE + LD (DCD_DSKRDY),A + + LD A,0 + LD (DCD_CURTRK),A + + RET + +DIO_CHKFC: + LD A,(FST_RC) + OR A + RET + +DIO_RUN: + LD A,(DCD_DSKRDY) + CP TRUE + JP Z,DIO_RUN0 + + CALL DIO_DRIVERESET + LD A,(DCD_DSKRDY) + CP TRUE + JP NZ,DIO_RUNERR ; DRIVERESET FAILED! + +DIO_RUN0: + CALL DIO_PROGRESS + + ; COPY PARMS OVER + LD A,(DCD_UNIT) + LD (FCD_DS),A + + LD A,(DCD_TRACE) + LD (FCD_TRACE),A + + LD A,(DCD_TRACK) + LD (FCD_C),A + + LD A,(DCD_HEAD) + LD (FCD_H),A + + LD A,(DCD_SECTOR) + LD (FCD_R),A + + LD A,(DCD_INTRLV) + LD (FCD_X),A + + ; FIX: COMBINE WITH DCD_TRACK SETUP ABOVE? + LD A,(DCD_CURTRK) + LD B,A + LD A,(DCD_TRACK) + CP B + JP Z,DIO_RUN1 ; SKIP SEEK IF POSSIBLE + + ; SEEK AND WAIT FOR COMPLETE + CALL FC_SEEK + CALL DIO_CHKFC + JP NZ,DIO_RUNERR + + CALL DIO_WTSEEK + CALL DIO_CHKFC + JP NZ,DIO_RUNERR + + ; RECORD CURRENT TRACK + ; FIX: SHOULD NOT ASSUME SEEK REQUEST SUCCEEDED (FC_READID?) + LD A,(DCD_TRACK) + LD (DCD_CURTRK),A + +DIO_RUN1: + LD A,(DCD_DOP) + + CP DOP_READ + JP Z,DIO_RUNREAD + + CP DOP_WRITE + JP Z,DIO_RUNWRITE + + CP DOP_FORMAT + JP Z,DIO_RUNFORMAT + + CP DOP_VERIFY + JP Z,DIO_RUNVERIFY + + JP DIO_RUNX + +DIO_RUNREAD: + CALL FC_READ + JP DIO_RUNCHK + +DIO_RUNWRITE: + CALL FC_WRITE + JP DIO_RUNCHK + +DIO_RUNFORMAT: + CALL FC_FMTTRK + JP DIO_RUNCHK + +DIO_RUNVERIFY: + CALL FC_READ + JP DIO_RUNCHK + +DIO_RUNCHK: + LD A,(FST_RC) + OR A + CP FRC_OK + JP NZ,DIO_RUNERR ; HANDLE I/O ERROR + LD A,(DCD_DOP) + CP DOP_VERIFY + JP NZ,DIO_RUNX ; NOT VERIFY, ALL DONE + CALL DIO_VERIFY + LD A,(FST_RC) + OR A + CP FRC_OK + JP NZ,DIO_RUNERR ; HANDLE VERIFY ERROR + JP DIO_RUNX + +DIO_RUNERR: + LD A,FALSE + LD (DCD_DSKRDY),A + CALL FC_RESETFDC ; FIX... + CALL NEWLINE + LD DE,STR_CONTINUE ; ABORT/RETRY/IGNORE PROMPT + CALL WRITESTR + +DIO_RUNERR1: + CALL GETKEYUC + + CP 'A' + JP Z,DIO_ABORT + CP 'R' + JP Z,DIO_RETRY + CP 'I' + JP Z,DIO_IGNORE + + JP DIO_RUNERR1 + +DIO_ABORT: + LD DE,DIOCS_ABORT + CALL WRITESTR + LD A,TRUE + LD (DCD_ABORT),A + JP DIO_RUNX + +DIO_RETRY: + LD DE,DIOCS_RETRY + CALL WRITESTR + JP DIO_RUN + +DIO_IGNORE: + LD DE,DIOCS_IGNORE + CALL WRITESTR + JP DIO_RUNX + +DIO_RUNX: + RET + +DIO_PROGRESS: + LD A,(NEWLINE_USED) + OR A + JP Z,DIO_PROGRESS1 + CALL NEWLINE + LD A,0 + LD (NEWLINE_USED),A + +DIO_PROGRESS1: + CALL PC_CR + LD DE,STR_PROGRESS + CALL WRITESTR + CALL PC_COLON + CALL PC_SPACE + LD DE,DPL_TRACK + CALL WRITESTR + CALL PC_EQUAL + LD A,(DCD_TRACK) + CALL PRTHEXBYTE + CALL PC_SPACE + LD DE,DPL_HEAD + CALL WRITESTR + CALL PC_EQUAL + LD A,(DCD_HEAD) + CALL PRTHEXBYTE + CALL PC_SPACE + LD DE,DPL_SECTOR + CALL WRITESTR + CALL PC_EQUAL + LD A,(DCD_SECTOR) + CALL PRTHEXBYTE + + RET + +DIO_VERIFY: + LD HL,BUFFER + LD DE,VFYBUF + LD BC,BUFSIZ + +DIO_VERIFY1: + LD A,(DE) + CP (HL) + JP NZ,DIO_VERIFY2 + INC DE + INC HL + DEC BC + LD A,B + OR C + JP NZ,DIO_VERIFY1 + RET + +DIO_VERIFY2: + PUSH DE + + CALL NEWLINE + LD DE,STR_MISMATCH + CALL WRITESTR + POP DE + LD A,D + SUB VFYBUF >> 8 + CALL PRTHEXBYTE + LD A,E + CALL PRTHEXBYTE + LD A,FRC_MISMATCH + LD (FST_RC),A + RET +; +; CONTINUE PROMPT OPTION STRINGS +; +DIOCS_ABORT .TEXT "ABORT$" +DIOCS_RETRY .TEXT "RETRY$" +DIOCS_IGNORE .TEXT "IGNORE$" +; +; DISK PROGRESS LABELS +; +DPL_TRACK .TEXT "TRACK$" +DPL_HEAD .TEXT "HEAD$" +DPL_SECTOR .TEXT "SECTOR$" +; +; DISK OPERATIONS +; +DOP_READ .EQU 0 ; READ OPERATION +DOP_WRITE .EQU 1 ; WRITE OPERATION +DOP_FORMAT .EQU 2 ; FORMAT OPERATION +DOP_VERIFY .EQU 3 ; VERIFY OPERATION +; +; DEVICE CONTROL DATA +; +DCD_FILL .DB 0E5H ; DEFAULT TO E5 (EMPTY DIRECTORY BYTE) +DCD_PATTERN .DB 000H ; DEFAULT TO 00 +DCD_TRACE .DB 000H ; TRACE LEVEL +DCD_UNIT .DB 000H ; DEFAULT UNIT = 0 +DCD_SECTOR .DB 001H ; DEFAULT SECTOR = 1 +DCD_HEAD .DB 000H ; DEFAULT HEAD = 0 +DCD_TRACK .DB 000H ; DEFAULT TRACK = 0 +DCD_INTRLV .DB 002H ; DEFAULT INTERLEAVE = 2 +DCD_DOP .DB DOP_READ ; DEFAULT OP = READ +DCD_MT .DB MT_PC720 ; DEFAULT FLOPPY DEVICE = PC720 +DCD_MD .DB MD_POLL ; DEFAULT MODE = POLL +; +; DEVICE CONTROL DATA (PRIVATE) +; +DCD_DSKRDY .DB FALSE ; 0 = NOT RDY, 1 = RDY +DCD_ABORT .DB FALSE ; 0 = CONT, 1 = ABORT +DCD_CURTRK .DB 0FFH ; CURRENT TRACK, FF = UNKNOWN +; +; MODES +; +MD_POLL .EQU 0 +MD_INT .EQU 1 +MD_INTFAST .EQU 2 +MD_INTWAIT .EQU 3 +MD_DRQWAIT .EQU 4 +; +; MODE MAPPING +; BIT IS SET FOR ALLOWED MODES PER FDC +; +MD_MAP: + .DB %00011111 ; DIO: POLL,INT,INTFAST,INTWAIT,DRQWAIT + .DB %00000111 ; DIO3: POLL,INT,INTFAST + .DB %00000111 ; ZETA: POLL,INT,INTFAST + .DB %00000001 ; ZETA2:POLL + .DB %00000001 ; DIDE: POLL + .DB %00000001 ; N8: POLL +; +; MEDIA DESCRIPTION BLOCK +; +MDB: +MDB_LABEL .DW 000H ; ADDRESS OF MEDIA LABEL +MDB_DESC .DW 000H ; ADDRESS OF MEDIA DESCRIPTION +MDB_NUMCYL .DB 000H ; NUMBER OF CYLINDERS +MDB_NUMHD .DB 000H ; NUMBER OF HEADS +MDB_NUMSEC .DB 000H ; NUMBER OF SECTORS +MDB_SOT .DB 000H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1) +MDB_FCD: ; FLOPPY CONFIGURATION DATA (PUBLIC) MANAGED AS A "BLOCK", MUST MATCH FCB BELOW +MDB_EOT ; END OF TRACK SECTOR (SAME AS SC SINCE SOT ALWAYS 1) +MDB_SC .DB 000H ; SECTOR COUNT +MDB_SECSZ .DW 000H ; SECTOR SIZE IN BYTES +MDB_GPL .DB 000H ; GAP LENGTH (R/W) +MDB_GPLF .DB 000H ; GAP LENGTH (FORMAT) +MDB_SRTHUT .DB 000H ; STEP RATE, IBM PS/2 CALLS FOR 3ms, 0DH = 3ms SRT, HEAD UNLOAD TIME +MDB_HLT .DB 000H ; HEAD LOAD TIME, IBM PS/2 CALLS FOR 15ms 08H = 16ms HUT +MDB_DORA .DB 000H ; OPERATIONS REGISTER VALUE FOR MEDIA +MDB_DORB .DB 000H ; OPERATIONS REGISTER VALUE FOR MEDIA +MDB_DORC .DB 000H ; OPERATIONS REGISTER VALUE FOR MEDIA +MDB_DCR .DB 000H ; CONTROL REGISTER VALUE FOR MEDIA +MDB_LEN .EQU $ - MDB +; +MDT: ; MODE TABLE + .DW MTB_POLL + .DW MTB_INT + .DW MTB_INTFAST + .DW MTB_INTWAIT + .DW MTB_DRQWAIT +MDT_ENTCNT .EQU (($ - MDT) / 2) +; +MTB_POLL .DW MTL_POLL ; ADDRESS OF MODE LABEL + .DW MTS_POLL ; ADDRESS OF MODE DESCRIPTION +MTL_POLL .TEXT "POLL $" +MTS_POLL .TEXT "POLLING (RECOMMENDED)$" +; +MTB_INT .DW MTL_INT ; ADDRESS OF MODE LABEL + .DW MTS_INT ; ADDRESS OF MODE DESCRIPTION +MTL_INT .TEXT "INT $" +MTS_INT .TEXT "INTERRUPT (!!! READ MANUAL !!!)$" +; +MTB_INTFAST .DW MTL_INTFAST ; ADDRESS OF MODE LABEL + .DW MTS_INTFAST ; ADDRESS OF MODE DESCRIPTION +MTL_INTFAST .TEXT "INT-FAST $" +MTS_INTFAST .TEXT "FAST INTERRUPT (!!! READ MANUAL !!!)$" +; +MTB_INTWAIT .DW MTL_INTWAIT ; ADDRESS OF MODE LABEL + .DW MTS_INTWAIT ; ADDRESS OF MODE DESCRIPTION +MTL_INTWAIT .TEXT "INT/WAIT $" +MTS_INTWAIT .TEXT "INT/WAIT (!!! READ MANUAL !!!)$" +; +MTB_DRQWAIT .DW MTL_DRQWAIT ; ADDRESS OF MODE LABEL + .DW MTS_DRQWAIT ; ADDRESS OF MODE DESCRIPTION +MTL_DRQWAIT .TEXT "DRQ/WAIT $" +MTS_DRQWAIT .TEXT "DRQ/WAIT (!!! NOT YET IMPLEMENTED!!!)$" +; +; MEDIA TYPE INFORMATION +; +MT_PC720 .EQU 0 +MT_PC144 .EQU 1 +MT_PC320 .EQU 2 +MT_PC360 .EQU 3 +MT_PC120 .EQU 4 +MT_PC111 .EQU 5 +; +MIT: ; MEDIA INDEX TABLE + .DW MDB_PC720 + .DW MDB_PC144 + .DW MDB_PC320 + .DW MDB_PC360 + .DW MDB_PC120 + .DW MDB_PC111 +MIT_ENTCNT .EQU (($ - MIT) / 2) +; +; Specify Command: +; +-----+-----+-----+-----+-----+-----+-----+-----+-----+ +; |Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +; +-----+-----+-----+-----+-----+-----+-----+-----+-----+ +; | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | +; | 1 | ----- STEP RATE ----- | -- HEAD UNLOAD TIME - | +; | 2 | ------------ HEAD LOAD TIME ----------- | NDM | +; +-----+-----+-----+-----+-----+-----+-----+-----+-----+ +; +; +; Step Rate (milliseconds): Head Unload Time (milliseconds): Head Load Time (milliseconds): +; +------+------+------+------+------+ +------+------+------+------+------+ +------+------+------+------+------+ +; | | BITRATE | | | BITRATE | | | BITRATE | +; | VAL | 1.0M | 500K | 300K | 250K | | VAL | 1.0M | 500K | 300K | 250K | | VAL | 1.0M | 500K | 300K | 250K | +; +------+------+------+------+------+ +------+------+------+------+------+ +------+------+------+------+------+ +; | 0 | 8.0 | 16.0 | 26.7 | 32.0 | | 0 | 128 | 256 | 426 | 512 | | 0 | 128 | 256 | 426 | 512 | +; | 1 | 7.5 | 15.0 | 25.0 | 30.0 | | 1 | 8 | 16 | 26.7 | 32 | | 1 | 1 | 2 | 3.3 | 4 | +; | 2 | 7.0 | 14.0 | 23.3 | 28.0 | | 2 | 16 | 32 | 53.3 | 64 | | 2 | 2 | 4 | 6.7 | 8 | +; | ... | ... | ... | ... | ... | | ... | ... | ... | ... | ... | | ... | ... | ... | ... | ... | +; | 14 | 1.0 | 2.0 | 3.3 | 4.0 | | 14 | 112 | 224 | 373 | 448 | | 126 | 126 | 252 | 420 | 504 | +; | 15 | 0.5 | 1.0 | 1.7 | 2.0 | | 15 | 120 | 240 | 400 | 480 | | 127 | 127 | 254 | 423 | 508 | +; +------+------+------+------+------+ +------+------+------+------+------+ +------+------+------+------+------+ +; +; IBM PS/2 CALLS FOR: +; STEP RATE: 3ms (6ms FOR ALL 41mm OR 720K DRIVES) +; HEAD LOAD TIME: 15ms +; +; STATIC CONFIGURATION, NEVER CHANGES (PRIVATE) +; +MDB_PC720 .DW DTL_PC720 ; ADDRESS OF MEDIA LABEL + .DW DTS_PC720 ; ADDRESS OF MEDIA DESCRIPTION + .DB 050H ; NUMBER OF CYLINDERS + .DB 002H ; NUMBER OF HEADS + .DB 009H ; NUMBER OF SECTORS + .DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1) +FCB_PC720 .DB 009H ; SECTOR COUNT + .DW 200H ; SECTOR SIZE IN BYTES + .DB 02AH ; GAP LENGTH (R/W) + .DB 050H ; GAP LENGTH (FORMAT) + .DB (13 << 4) | 0 ; SRT = 6ms, HUT = 512ms + .DB 4 ; HLT = 16ms + .DB DORA_BR250 ; OPERATIONS REGISTER VALUE + .DB DORB_BR250 ; OPERATIONS REGISTER VALUE + .DB DORC_BR250 ; OPERATIONS REGISTER VALUE + .DB DCR_BR250 ; CONTROL REGISTER VALUE + .IF (($ - MDB_PC720) != MDB_LEN) + .ECHO "*** FCB SIZE ERROR!!! ***\n" + .ENDIF +DTL_PC720 .TEXT "720KB $" +DTS_PC720 .TEXT "3.5\" 720KB - 9 SECTORS, 2 SIDES, 80 TRACKS, DOUBLE DENSITY$" +; +MDB_PC144 .DW DTL_PC144 ; ADDRESS OF MEDIA LABEL + .DW DTS_PC144 ; ADDRESS OF MEDIA DESCRIPTION + .DB 050H ; NUMBER OF CYLINDERS + .DB 002H ; NUMBER OF HEADS + .DB 012H ; NUMBER OF SECTORS + .DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1) +FCB_PC144 .DB 012H ; SECTOR COUNT + .DW 200H ; SECTOR SIZE IN BYTES + .DB 01BH ; GAP LENGTH (R/W) + .DB 06CH ; GAP LENGTH (FORMAT) + .DB (13 << 4) | 0 ; SRT = 3ms, HUT = 256ms + .DB 8 ; HLT = 16ms + .DB DORA_BR500 ; OPERATIONS REGISTER VALUE + .DB DORB_BR500 ; OPERATIONS REGISTER VALUE + .DB DORC_BR500 ; OPERATIONS REGISTER VALUE + .DB DCR_BR500 ; CONTROL REGISTER VALUE + .IF (($ - MDB_PC144) != MDB_LEN) + .ECHO "*** FCB SIZE ERROR!!! ***\n" + .ENDIF +DTL_PC144 .TEXT "1.44MB$" +DTS_PC144 .TEXT "3.5\" 1.44MB - 18 SECTORS, 2 SIDES, 80 TRACKS, HIGH DENSITY$" +; +MDB_PC320 .DW DTL_PC320 ; ADDRESS OF MEDIA LABEL + .DW DTS_PC320 ; ADDRESS OF MEDIA DESCRIPTION + .DB 028H ; NUMBER OF CYLINDERS + .DB 002H ; NUMBER OF HEADS + .DB 008H ; NUMBER OF SECTORS + .DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1) +FCB_PC320 .DB 008H ; SECTOR COUNT + .DW 200H ; SECTOR SIZE IN BYTES + .DB 02AH ; GAP LENGTH (R/W) + .DB 050H ; GAP LENGTH (FORMAT) + .DB (13 << 4) | 0 ; SRT = 6ms, HUT = 512ms + .DB 4 ; HLT = 16ms + .DB DORA_BR250 ; OPERATIONS REGISTER VALUE + .DB DORB_BR250 ; OPERATIONS REGISTER VALUE + .DB DORC_BR250 ; OPERATIONS REGISTER VALUE + .DB DCR_BR250 ; CONTROL REGISTER VALUE + .IF (($ - MDB_PC320) != MDB_LEN) + .ECHO "*** FCB SIZE ERROR!!! ***\n" + .ENDIF +DTL_PC320 .TEXT "320KB $" +DTS_PC320 .TEXT "5.25\" 320KB - 8 SECTORS, 2 SIDES, 40 TRACKS, DOUBLE DENSITY$" +; +MDB_PC360 .DW DTL_PC360 ; ADDRESS OF MEDIA LABEL + .DW DTS_PC360 ; ADDRESS OF MEDIA DESCRIPTION + .DB 028H ; NUMBER OF CYLINDERS + .DB 002H ; NUMBER OF HEADS + .DB 009H ; NUMBER OF SECTORS + .DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1) +FCB_PC360 .DB 009H ; SECTOR COUNT + .DW 200H ; SECTOR SIZE IN BYTES + .DB 02AH ; GAP LENGTH (R/W) + .DB 050H ; GAP LENGTH (FORMAT) + .DB (13 << 4) | 0 ; SRT = 6ms, HUT = 512ms + .DB 4 ; HLT = 16ms + .DB DORA_BR250 ; OPERATIONS REGISTER VALUE + .DB DORB_BR250 ; OPERATIONS REGISTER VALUE + .DB DORC_BR250 ; OPERATIONS REGISTER VALUE + .DB DCR_BR250 ; CONTROL REGISTER VALUE + .IF (($ - MDB_PC360) != MDB_LEN) + .ECHO "*** FCB SIZE ERROR!!! ***\n" + .ENDIF +DTL_PC360 .TEXT "360KB $" +DTS_PC360 .TEXT "5.25\" 360KB - 9 SECTORS, 2 SIDES, 40 TRACKS, DOUBLE DENSITY$" +; +MDB_PC120 .DW DTL_PC120 ; ADDRESS OF MEDIA LABEL + .DW DTS_PC120 ; ADDRESS OF MEDIA DESCRIPTION + .DB 050H ; NUMBER OF CYLINDERS + .DB 002H ; NUMBER OF HEADS + .DB 00FH ; NUMBER OF SECTORS + .DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1) +FCB_PC120 .DB 00FH ; SECTOR COUNT + .DW 200H ; SECTOR SIZE IN BYTES + .DB 01BH ; GAP LENGTH (R/W) + .DB 054H ; GAP LENGTH (FORMAT) + .DB (10 << 4) | 0 ; SRT = 6ms, HUT = 256ms + .DB 8 ; HLT = 16ms + .DB DORA_BR500 ; OPERATIONS REGISTER VALUE + .DB DORB_BR500 ; OPERATIONS REGISTER VALUE + .DB DORC_BR500 ; OPERATIONS REGISTER VALUE + .DB DCR_BR500 ; CONTROL REGISTER VALUE + .IF (($ - MDB_PC120) != MDB_LEN) + .ECHO "*** FCB SIZE ERROR!!! ***\n" + .ENDIF +DTL_PC120 .TEXT "1.2MB $" +DTS_PC120 .TEXT "5.25\" 1.2MB - 15 SECTORS, 2 SIDES, 80 TRACKS, HIGH DENSITY$" +; +MDB_PC111 .DW DTL_PC111 ; ADDRESS OF MEDIA LABEL + .DW DTS_PC111 ; ADDRESS OF MEDIA DESCRIPTION + .DB 04DH ; NUMBER OF CYLINDERS + .DB 002H ; NUMBER OF HEADS + .DB 00FH ; NUMBER OF SECTORS + .DB 001H ; START OF TRACK (ID OF FIRST SECTOR, USUALLY 1) +FCB_PC111 .DB 00FH ; SECTOR COUNT + .DW 200H ; SECTOR SIZE IN BYTES + .DB 01BH ; GAP LENGTH (R/W) + .DB 054H ; GAP LENGTH (FORMAT) + .DB (13 << 4) | 0 ; SRT = 3ms, HUT = 256ms + .DB 25 ; HLT = 50ms + .DB DORA_BR500 ; OPERATIONS REGISTER VALUE + .DB DORB_BR500 ; OPERATIONS REGISTER VALUE + .DB DORC_BR500 ; OPERATIONS REGISTER VALUE + .DB DCR_BR500 ; CONTROL REGISTER VALUE + .IF (($ - MDB_PC111) != MDB_LEN) + .ECHO "*** FCB SIZE ERROR!!! ***\n" + .ENDIF +DTL_PC111 .TEXT "1.11MB$" +DTS_PC111 .TEXT "8\" 1.11MB - 15 SECTORS, 2 SIDES, 77 TRACKS, DOUBLE DENSITY$" +; +;=============================================================================== +; FLOPPY DISK CONTROL MENU (DIRECT MENU INTERFACE TO FDC & RELATED HARDWARE) +;=============================================================================== +; +; FDC MENU DATA +; +FM_TABLE .DB 'R' \ .DW FMS_READ, FM_READ +FM_ENTSIZ .EQU $ - FM_TABLE + .DB 'D' \ .DW FMS_READDEL, FM_READDEL + .DB 'W' \ .DW FMS_WRITE, FM_WRITE + .DB 'E' \ .DW FMS_WRITEDEL, FM_WRITEDEL + .DB 'T' \ .DW FMS_READTRK, FM_READTRK + .DB 'I' \ .DW FMS_READID, FM_READID + .DB 'F' \ .DW FMS_FMTTRK, FM_FMTTRK + .DB 'Q' \ .DW FMS_SCANEQ, FM_SCANEQ + .DB 'L' \ .DW FMS_SCANLOEQ, FM_SCANLOEQ + .DB 'H' \ .DW FMS_SCANHIEQ, FM_SCANHIEQ + .DB 'C' \ .DW FMS_RECAL, FM_RECAL + .DB 'N' \ .DW FMS_SENSEINT, FM_SENSEINT + .DB 'P' \ .DW FMS_SPECIFY, FM_SPECIFY + .DB 'V' \ .DW FMS_DRVSTAT, FM_DRVSTAT + .DB 'S' \ .DW FMS_SEEK, FM_SEEK + .DB 'O' \ .DW FMS_VERSION, FM_VERSION + .DB 'U' \ .DW FMS_PULSETC, FM_PULSETC + .DB 'A' \ .DW FMS_DOR, FM_DOR + .DB 'M' \ .DW FMS_MOTOR, FM_MOTOR + .DB 'Z' \ .DW FMS_FDCRESET, FM_FDCRESET + .DB 'X' \ .DW FMS_EXIT, 0000H +FM_COUNT .EQU (($ - FM_TABLE) / FM_ENTSIZ) ; # ENTRIES IN TABLE +FM_INFO: .DW FM_DRAW + .DW FM_TABLE + .DB FM_ENTSIZ + .DB FM_COUNT +; +; FDC COMMAND MENU STRINGS +; +STR_FDCMENU: +; .TEXT "===================<< FDU FDC COMMAND MENU >>======= [MSR=XX] ==\r\n" + .TEXT "===================<< FDU FDC COMMAND MENU >>======= [MSR=" +MV_MSR .TEXT "XX" + .TEXT "] ==\r\n" + .TEXT "(R)EAD READ (D)EL (W)RITE WRITE D(E)L\r\n" + .TEXT "READ (T)RK READ (I)D (F)ORMAT SCAN E(Q)\r\n" + .TEXT "SCAN (L)O/EQ SCAN (H)I/EQ RE(C)AL SE(N)SE INT\r\n" + .TEXT "S(P)ECIFY DRI(V)E STAT (S)EEK VERSI(O)N\r\n" +; .TEXT "P(U)LSE TC L(A)TCH:XX (M)OTOR:XXX FDC RESET (Z)\r\n" + .TEXT "P(U)LSE TC L(A)TCH:" +MV_LAT .TEXT "XX" + .TEXT " (M)OTOR:" +MV_MOT .TEXT "XXX" + .TEXT " FDC RESET (Z)" + .TEXT "\r\n" + .TEXT "E(X)IT\r\n" + .TEXT "=== OPTION ===> $" +; +FMS_NOP .TEXT "NOP$" +FMS_READ .TEXT "READ$" +FMS_READDEL .TEXT "READ DELETED$" +FMS_WRITE .TEXT "WRITE$" +FMS_WRITEDEL .TEXT "WRITE DELETED$" +FMS_READTRK .TEXT "READ TRACK$" +FMS_READID .TEXT "READ ID$" +FMS_FMTTRK .TEXT "FORMAT TRACK$" +FMS_SCANEQ .TEXT "SCAN EQUAL$" +FMS_SCANLOEQ .TEXT "SCAN LOW OR EQUAL$" +FMS_SCANHIEQ .TEXT "SCAN HIGH OR EQUAL$" +FMS_RECAL .TEXT "RECALIBRATE$" +FMS_SENSEINT .TEXT "SENSE INTERRUPT$" +FMS_SPECIFY .TEXT "SPECIFY$" +FMS_DRVSTAT .TEXT "DRIVE STATUS$" +FMS_SEEK .TEXT "SEEK$" +FMS_VERSION .TEXT "VERSION$" +FMS_PULSETC .TEXT "PULSE TC$" +FMS_DOR .TEXT "DOR$" +FMS_MOTOR .TEXT "MOTOR$" +FMS_FDCRESET .TEXT "FDC RESET$" +FMS_EXIT .TEXT "EXIT$" +; +; ENTRY POINT FOR FDCMENU +; +FDCMENU: + LD A,01H + LD (FCD_TRACE),A ; FORCE TRACING OF ALL FDC COMMANDS + LD HL,FM_INFO + CALL RUNMENU + RET +; +; FDCMENU DRAW PROCEDURE +; +FM_DRAW: + CALL NEWLINE + CALL NEWLINE + + ; UPDATE MOTOR STATUS + LD HL,FDCBM + LD A,(HL) + AND _DIO + JR NZ,FM_DRAW0A + LD A,(HL) + AND _ZETA | _DIO3 + JR NZ,FM_DRAW0B + LD A,(HL) + AND _DIDE | _N8 | _ZETA2 + JR NZ,FM_DRAW0C + JR FM_DRAW0D +FM_DRAW0A: ; DIO + LD A,(FST_DOR) + AND 00000010B + XOR 00000010B + JR FM_DRAW0D +FM_DRAW0B: ; ZETA, DIO3 + LD A,(FST_DOR) + AND 00000010B + JR FM_DRAW0D +FM_DRAW0C: ; DIDE, N8, ZETA2 + LD A,(FST_DOR) + AND 11110000B + JR FM_DRAW0D +FM_DRAW0D: + LD DE,STR_ON + JP NZ,FM_DRAW1 + LD DE,STR_OFF +FM_DRAW1: + LD HL,MV_MOT + CALL STRCPY + + ; UPDATE MSR VALUE + LD DE,MV_MSR + LD C,(IY+CFG_MSR) + IN A,(C) + CALL HEXSTRBYTE + + ; UPDATE FST_DOR VALUE + LD DE,MV_LAT + LD A,(FST_DOR) + CALL HEXSTRBYTE + + ; DISPLAY THE MENU + LD DE,STR_FDCMENU + CALL WRITESTR + + RET +; +; FDCMENU FUNCTIONS +; +FM_READ: + CALL FM_GETTHS + CALL FC_READ + RET + +FM_READDEL: + CALL FM_GETTHS + CALL FC_READDEL + RET + +FM_WRITE: + CALL FM_GETTHS + CALL FC_WRITE + RET + +FM_WRITEDEL: + CALL FM_GETTHS + CALL FC_WRITEDEL + RET + +FM_READTRK: + CALL FM_GETTHS + CALL FC_READTRK + RET + +FM_READID: + CALL FM_GETHEAD + CALL FC_READID + RET + +FM_FMTTRK: + CALL FM_GETTRK + CALL FM_GETHEAD + CALL FC_FMTTRK + RET + +FM_SCANEQ: + JP FM_NOTIMPL ; NOT IMPLEMENTED! + CALL FM_GETTHS + CALL FC_SCANEQ + RET + +FM_SCANLOEQ: + JP FM_NOTIMPL ; NOT IMPLEMENTED! + CALL FM_GETTHS + CALL FC_SCANLOEQ + RET + +FM_SCANHIEQ: + JP FM_NOTIMPL ; NOT IMPLEMENTED! + CALL FM_GETTHS + CALL FC_SCANHIEQ + RET + +FM_RECAL: + LD A,0 ; UPDATE CYLINDER FOR FUTURE CALLS + LD (FCD_C),A ; FIX: NOT IN THE RIGHT PLACE + CALL FC_RECAL + RET + +FM_SENSEINT: + CALL FC_SENSEINT + RET + +FM_SPECIFY: + CALL FC_SPECIFY + RET + +FM_DRVSTAT: + CALL FM_GETHEAD + CALL FC_DRVSTAT + RET + +FM_SEEK: + CALL FM_GETTRK + CALL FC_SEEK + RET + +FM_VERSION: + CALL FC_VERSION + RET + +FM_PULSETC: + CALL NEWLINE + CALL FC_PULSETC + RET + +FM_DOR: + CALL FM_GETDOR + CALL FC_SETDOR + RET + +FM_MOTOR: + ; TOGGLE MOTOR STATE + LD HL,FDCBM + LD A,(HL) + AND _DIO + JR NZ,FM_MOTOR0A + LD A,(HL) + AND _ZETA | _DIO3 + JR NZ,FM_MOTOR0B + LD A,(HL) + AND _DIDE | _N8 | _ZETA2 + JR NZ,FM_MOTOR0C + JR FM_MOTOR0D +FM_MOTOR0A: ; DIO + LD A,(FST_DOR) + AND 00000010B + XOR 00000010B + JR FM_MOTOR0D +FM_MOTOR0B: ; ZETA, DIO3 + LD A,(FST_DOR) + AND 00000010B + JR FM_MOTOR0D +FM_MOTOR0C: ; DIDE, N8, ZETA2 + LD A,(FST_DOR) + AND 11110000B + JR FM_MOTOR0D +FM_MOTOR0D: + JP Z,FC_MOTORON + JP FC_MOTOROFF + +FM_FDCRESET: + CALL NEWLINE + LD A,(FDCID) + CP FDC_DIO ; RESET NOT POSSIBLE ON DIO + JP NZ,FC_RESETFDC + LD DE,FCS_NORES + JP WRITESTR + +FM_NOTIMPL: + CALL PC_SPACE ; NOT IMPLEMENTED + LD DE,STR_NOTIMPL + CALL WRITESTR + RET + +FM_GETTHS: + CALL FM_GETTRK + CALL FM_GETHEAD + CALL FM_GETSEC + RET + +FM_GETTRK: + LD DE,FCPP_TRK + LD HL,FCD_C + CALL GETHEXBYTE + RET + +FM_GETHEAD: + LD DE,FCPP_HEAD + LD HL,FCD_H + CALL GETHEXBYTE + RET + +FM_GETSEC: + LD DE,FCPP_SEC + LD HL,FCD_R + CALL GETHEXBYTE + RET + +FM_GETDOR: + LD DE,FCPP_DOR + LD HL,FST_DOR + CALL GETHEXBYTE + RET +; +FCS_NORES .TEXT "\r\n*** RESET NOT SUPORTED BY HARDWARE ***$" +; +;=============================================================================== +; FLOPPY DISK CONTROL SERVICES (PHYSICAL DEVICE CONTROL FOR FDC HARDWARE) +;=============================================================================== +; +; FDC RESULT CODES +; +FRC_OK .EQU 0 ; 00 +FRC_NOTIMPL .EQU -01H ; FF +FRC_CMDERR .EQU -02H ; FE +FRC_ERROR .EQU -03H ; FD +FRC_ABORT .EQU -04H ; FC +FRC_BUFMAX .EQU -05H ; FB +FRC_ABTERM .EQU -08H ; F8 +FRC_INVCMD .EQU -09H ; F7 +FRC_DSKCHG .EQU -0AH ; F6 +FRC_ENDCYL .EQU -0BH ; F5 +FRC_DATAERR .EQU -0CH ; F4 +FRC_OVERRUN .EQU -0DH ; F3 +FRC_NODATA .EQU -0EH ; F2 +FRC_NOTWRIT .EQU -0FH ; F1 +FRC_MISADR .EQU -10H ; F0 +FRC_TOFDCRDY .EQU -11H ; EF +FRC_TOSNDCMD .EQU -12H ; EE +FRC_TOGETRES .EQU -13H ; ED +FRC_TOEXEC .EQU -14H ; EC +FRC_TOSEEKWT .EQU -15H ; EB +FRC_MISMATCH .EQU -16H ; EA +; +; FDC STATUS CODE STRINGS +; +FSS_OK .TEXT "OK$" +FSS_NOTIMPL .TEXT "NOT IMPLEMENTED$" +FSS_CMDERR .TEXT "COMMAND ERROR$" +FSS_ERROR .TEXT "ERROR$" +FSS_ABORT .TEXT "ABORT$" +FSS_BUFMAX .TEXT "BUFFER EXCEEDED$" +FSS_ABTERM .TEXT "ABNORMAL TERMINATION$" +FSS_INVCMD .TEXT "INVALID COMMAND$" +FSS_DSKCHG .TEXT "DISK CHANGE$" +FSS_ENDCYL .TEXT "END OF CYLINDER$" +FSS_DATAERR .TEXT "DATA ERROR$" +FSS_OVERRUN .TEXT "OVERRUN$" +FSS_NODATA .TEXT "NO DATA$" +FSS_NOTWRIT .TEXT "NOT WRITABLE$" +FSS_MISADR .TEXT "MISSING ADDRESS MARK$" +FSS_TOFDCRDY .TEXT "FDC READY TIMEOUT$" +FSS_TOSNDCMD .TEXT "SENDCMD TIMEOUT$" +FSS_TOGETRES .TEXT "GET RESULTS TIMEOUT$" +FSS_TOEXEC .TEXT "EXEC TIMEOUT$" +FSS_TOSEEKWT .TEXT "SEEK WAIT TIMEOUT$" +; +; FDC STATUS STRING TABLE +; +FSST: .DB FRC_OK \ .DW FSS_OK +FSST_ENTSIZ .EQU $ - FSST + .DB FRC_NOTIMPL \ .DW FSS_NOTIMPL + .DB FRC_CMDERR \ .DW FSS_CMDERR + .DB FRC_ERROR \ .DW FSS_ERROR + .DB FRC_ABORT \ .DW FSS_ABORT + .DB FRC_BUFMAX \ .DW FSS_BUFMAX + .DB FRC_ABTERM \ .DW FSS_ABTERM + .DB FRC_INVCMD \ .DW FSS_INVCMD + .DB FRC_DSKCHG \ .DW FSS_DSKCHG + .DB FRC_ENDCYL \ .DW FSS_ENDCYL + .DB FRC_DATAERR \ .DW FSS_DATAERR + .DB FRC_OVERRUN \ .DW FSS_OVERRUN + .DB FRC_NODATA \ .DW FSS_NODATA + .DB FRC_NOTWRIT \ .DW FSS_NOTWRIT + .DB FRC_MISADR \ .DW FSS_MISADR + .DB FRC_TOFDCRDY \ .DW FSS_TOFDCRDY + .DB FRC_TOSNDCMD \ .DW FSS_TOSNDCMD + .DB FRC_TOGETRES \ .DW FSS_TOGETRES + .DB FRC_TOEXEC \ .DW FSS_TOEXEC + .DB FRC_TOSEEKWT \ .DW FSS_TOSEEKWT +FSST_COUNT .EQU (($ - FSST) / FSST_ENTSIZ) ; # ENTRIES IN TABLE +; +; FDC COMMAND PHASE +; +FCP_CMD .DB 000H ; INPUT: COMMAND CODE +FCP_BUFLEN .DB 00H +FCP_BUF .DS 10H +FCP_BUFSIZ .EQU $-FCP_BUF +FCP_XFRCNT .DW 00H ; BYTES TRANSFERRED DURING COMMAND PHASE +; +; FDC EXECUTION PHASE +; +FXP_XR .DW 00H ; INPUT: ADDRESS OF EXECUTION ROUTINE TO INVOKE +FXP_TO .DW 00H ; TIMEOUT COUNTDOWN TIMER USED IN SOME VARIATIONS +FXP_XFRCNT .DW 00H ; BYTES TRANSFERRED DURING EXECUTION +FXP_A .DB 00H ; LAST VALUE OF REG A RECORDED DURING EXECUTION +FXP_BC .DW 00H ; LAST VALUE OF REG BC RECORDED DURING EXECUTION +FXP_DE .DW 00H ; LAST VALUE OF REG DE RECORDED DURING EXECUTION +FXP_HL .DW 00H ; LAST VALUE OF REG HL RECORDED DURING EXECUTION +FXP_BUFLEN .DB 00H +FXP_BUF .DS 50H ; USED FOR CERTAIN EXEC ROUTINES (FORMAT TRACK) +FXP_BUFSIZ .EQU $-FXP_BUF +; +; FDC STATUS +; +FST_RC .DB 00H +FST_MSR .DB 00H +FST_DOR .DB 00H +; +; FDC RESULTS BUFFER +; +FRB_LEN .DB 00H +FRB +FRB_ST0 +FRB_ST3 .DB 0 +FRB_ST1 +FRB_PCN .DB 0 +FRB_ST2 .DB 0 +FRB_C .DB 0 +FRB_H .DB 0 +FRB_R .DB 0 +FRB_N .DB 0 + .FILL 10H ; ALLOWS EXTRA CHARACTERS TO BE RETREIEVED +FRB_SIZ .EQU $-FRB +; +; FDC COMMANDS +; +CMD_READ .EQU 00000110B ; ST0,ST1,ST2,C,H,R,N +CMD_READDEL .EQU 00001100B ; ST0,ST1,ST2,C,H,R,N +CMD_WRITE .EQU 00000101B ; ST0,ST1,ST2,C,H,R,N +CMD_WRITEDEL .EQU 00001001B ; ST0,ST1,ST2,C,H,R,N +CMD_READTRK .EQU 00000010B ; ST0,ST1,ST2,C,H,R,N +CMD_READID .EQU 00001010B ; ST0,ST1,ST2,C,H,R,N +CMD_FMTTRK .EQU 00001101B ; ST0,ST1,ST2,C,H,R,N +CMD_SCANEQ .EQU 00010001B ; ST0,ST1,ST2,C,H,R,N +CMD_SCANLOEQ .EQU 00011001B ; ST0,ST1,ST2,C,H,R,N +CMD_SCANHIEQ .EQU 00011101B ; ST0,ST1,ST2,C,H,R,N +CMD_RECAL .EQU 00000111B ; +CMD_SENSEINT .EQU 00001000B ; ST0,PCN +CMD_SPECIFY .EQU 00000011B ; +CMD_DRVSTAT .EQU 00000100B ; ST3 +CMD_SEEK .EQU 00001111B ; +CMD_VERSION .EQU 00010000B ; ST0 +; +; FDC COMMAND DATA +; +FCD: ; FLOPPY CONFIGURATION DATA (PUBLIC) MANAGED AS A "BLOCK", SEE FCB BELOW +FCD_EOT ; END OF TRACK SECTOR (SAME AS SC SINCE SOT ALWAYS 1) +FCD_SC .DB 000H ; SECTOR COUNT +FCD_SECSZ .DW 000H ; SECTOR SIZE IN BYTES +FCD_GPL .DB 000H ; GAP LENGTH (R/W) +FCD_GPLF .DB 000H ; GAP LENGTH (FORMAT) +FCD_SRTHUT .DB 000H ; STEP RATE, IBM PS/2 CALLS FOR 3ms, 0DH = 3ms SRT, HEAD UNLOAD TIME +FCD_HLT .DB 000H ; HEAD LOAD TIME, IBM PS/2 CALLS FOR 15ms 08H = 16ms HUT +FCD_DORA .DB 000H ; DEFAULT DOR VALUE FOR MEDIA +FCD_DORB .DB 000H ; DEFAULT DOR VALUE FOR MEDIA +FCD_DORC .DB 000H ; DEFAULT DOR VALUE FOR MEDIA +FCD_DCR .DB 000H ; DOR VALUE FOR MEDIA +FCD_LEN .EQU $ - FCD + ; DYNAMICALLY MANAGED (PUBLIC) +FCD_DS .DB 001H ; DRIVE SELECT (UNIT NUMBER 0-3) +FCD_C .DB 000H ; CYLINDER +FCD_H .DB 000H ; HEAD +FCD_R .DB 001H ; RECORD +FCD_D .DB 0E5H ; FORMAT DATA FILL BYTE +FCD_X .DB 002H ; FORMAT INTERLEAVE FACTOR (1...N) +FCD_ND .DB 000H ; DMA, 0=DMA, 1=NON-DMA + ; STATIC CONFIGURATION, NEVER CHANGES (PRIVATE) +FCD_SOT .DB 001H ; STARTING SECTOR NUMBER OF TRACK +FCD_MT .DB 000H ; MULTI-TRACK, WE DON'T USE, SET TO 0 +FCD_MFM .DB 001H ; MFM, 0=FM, 1=MFM, WE USE MFM ALWAYS +FCD_SK .DB 000H ; SKIP MODE, WE DON'T USE, SET TO 0 +FCD_N .DB 002H ; SECTOR SIZE, N=2 FOR 512 BYTES +FCD_DTL .DB 0FFH ; DATA LENGTH (WHEN N=0, SET TO FF OTHERWISE) +FCD_STP .DB 001H ; SECTOR SCAN TYPE, 1=CONTIG, 2=ALTERNATING + ; CONTROL STUFF (PUBLIC) +FCD_TRACE .DB 00H ; TRACE LEVEL +; +; +; FDC CMD PARM PROMPTS +; +FCPP_TRK .TEXT "TRACK$" +FCPP_HEAD .TEXT "HEAD$" +FCPP_SEC .TEXT "SECTOR$" +FCPP_DOR .TEXT "DOR$" +; +; FDC EXECUTION ROUTINE JUMP TABLE +; +FXRJ_NOP: JP FXR_NOP +FXRJ_READ: JP FXR_READ +FXRJ_READDEL: JP FXR_READDEL +FXRJ_WRITE: JP FXR_WRITE +FXRJ_WRITEDEL: JP FXR_WRITEDEL +FXRJ_READTRK: JP FXR_READTRK +FXRJ_READID: JP FXR_READID +FXRJ_FMTTRK: JP FXR_FMTTRK +FXRJ_SCANEQ: JP FXR_SCANEQ +FXRJ_SCANLOEQ: JP FXR_SCANLOEQ +FXRJ_SCANHIEQ: JP FXR_SCANHIEQ +FXRJ_RECAL: JP FXR_RECAL +FXRJ_SENSEINT: JP FXR_SENSEINT +FXRJ_SPECIFY: JP FXR_SPECIFY +FXRJ_DRVSTAT: JP FXR_DRVSTAT +FXRJ_SEEK: JP FXR_SEEK +FXRJ_VERSION JP FXR_VERSION +; +; FDC COMMAND STRINGS +; +FCS_NOP: .TEXT "NOP$" +FCS_READ: .TEXT "READ$" +FCS_READDEL: .TEXT "READ DELETED$" +FCS_WRITE: .TEXT "WRITE$" +FCS_WRITEDEL: .TEXT "WRITE DELETED$" +FCS_READTRK: .TEXT "READ TRACK$" +FCS_READID: .TEXT "READ ID$" +FCS_FMTTRK: .TEXT "FORMAT TRACK$" +FCS_SCANEQ: .TEXT "SCAN EQUAL$" +FCS_SCANLOEQ: .TEXT "SCAN LOW OR EQUAL$" +FCS_SCANHIEQ: .TEXT "SCAN HIGH OR EQUAL$" +FCS_RECAL: .TEXT "RECALIBRATE$" +FCS_SENSEINT: .TEXT "SENSE INTERRUPT$" +FCS_SPECIFY: .TEXT "SPECIFY$" +FCS_DRVSTAT: .TEXT "DRIVE STATUS$" +FCS_SEEK: .TEXT "SEEK$" +FCS_VERSION: .TEXT "VERSION$" +; +; FDC COMMAND TABLE +; +FCT .DB CMD_READ \ .DW FCS_READ, FXRJ_READ +FCT_ENTSIZ .EQU $ - FCT + .DB CMD_READDEL \ .DW FCS_READDEL, FXRJ_READDEL + .DB CMD_WRITE \ .DW FCS_WRITE, FXRJ_WRITE + .DB CMD_WRITEDEL \ .DW FCS_WRITEDEL, FXRJ_WRITEDEL + .DB CMD_READTRK \ .DW FCS_READTRK, FXRJ_READTRK + .DB CMD_READID \ .DW FCS_READID, FXRJ_READID + .DB CMD_FMTTRK \ .DW FCS_FMTTRK, FXRJ_FMTTRK + .DB CMD_SCANEQ \ .DW FCS_SCANEQ, FXRJ_SCANEQ + .DB CMD_SCANLOEQ \ .DW FCS_SCANLOEQ, FXRJ_SCANLOEQ + .DB CMD_SCANHIEQ \ .DW FCS_SCANHIEQ, FXRJ_SCANHIEQ + .DB CMD_RECAL \ .DW FCS_RECAL, FXRJ_RECAL + .DB CMD_SENSEINT \ .DW FCS_SENSEINT, FXRJ_SENSEINT + .DB CMD_SPECIFY \ .DW FCS_SPECIFY, FXRJ_SPECIFY + .DB CMD_DRVSTAT \ .DW FCS_DRVSTAT, FXRJ_DRVSTAT + .DB CMD_SEEK \ .DW FCS_SEEK, FXRJ_SEEK + .DB CMD_VERSION \ .DW FCS_VERSION, FXRJ_VERSION +FCT_COUNT .EQU (($ - FCT) / FCT_ENTSIZ) ; # ENTRIES IN TABLE +; +; ENTRY POINTS FOR FDC COMMANDS +; +FC_READ: + LD A,CMD_READ + LD B,0FFH ; MT & MFM & SK & CMD BITS + LD (FCP_CMD),A + LD HL,FXR_READ + LD (FXP_XR),HL + CALL FC_SETUPIO + CALL FC_CMDPROC + RET + +FC_READDEL: + LD A,CMD_READDEL + LD B,0FFH ; MT & MFM & SK & CMD BITS + LD (FCP_CMD),A + LD HL,FXR_READDEL + LD (FXP_XR),HL + CALL FC_SETUPIO + CALL FC_CMDPROC + RET + +FC_WRITE: + LD A,CMD_WRITE + LD B,0DFH ; MT & MFM & CMD BITS + LD (FCP_CMD),A + LD HL,FXR_WRITE + LD (FXP_XR),HL + CALL FC_SETUPIO + CALL FC_CMDPROC + RET + +FC_WRITEDEL: + LD A,CMD_WRITEDEL + LD B,0DFH ; MT & MFM & CMD BITS + LD (FCP_CMD),A + LD HL,FXR_WRITEDEL + LD (FXP_XR),HL + CALL FC_SETUPIO + CALL FC_CMDPROC + RET + +FC_READTRK: + LD A,CMD_READTRK + LD B,07FH ; MFM & SK & CMD BITS + LD (FCP_CMD),A + LD HL,FXR_READTRK + LD (FXP_XR),HL + CALL FC_SETUPIO + CALL FC_CMDPROC + RET + +FC_READID: + LD A,CMD_READID + LD B,05FH ; MFM & CMD BITS + LD (FCP_CMD),A + LD HL,FXR_READID + LD (FXP_XR),HL + CALL FC_SETUPCMD + CALL FC_CMDPROC + RET + +FC_FMTTRK: + LD A,CMD_FMTTRK + LD B,05FH ; MFM & CMD BITS + LD (FCP_CMD),A + LD HL,FXR_FMTTRK + LD (FXP_XR),HL + CALL FC_SETUPFMT + CALL FC_CMDPROC + RET + +FC_SCANEQ: + LD A,CMD_SCANEQ + LD B,0FFH ; MT & MFM & SK & CMD BITS + LD (FCP_CMD),A + LD HL,FXR_SCANEQ + LD (FXP_XR),HL + CALL FC_SETUPSCAN + CALL FC_CMDPROC + RET + +FC_SCANLOEQ: + LD A,CMD_SCANLOEQ + LD B,0FFH ; MT & MFM & SK & CMD BITS + LD (FCP_CMD),A + LD HL,FXR_SCANLOEQ + LD (FXP_XR),HL + CALL FC_SETUPSCAN + CALL FC_CMDPROC + RET + +FC_SCANHIEQ: + LD A,CMD_SCANHIEQ + LD B,0FFH ; MT & MFM & SK & CMD BITS + LD (FCP_CMD),A + LD HL,FXR_SCANHIEQ + LD (FXP_XR),HL + CALL FC_SETUPSCAN + CALL FC_CMDPROC + RET + +FC_RECAL: + LD A,CMD_RECAL + LD B,01FH ; CMD BITS ONLY + LD (FCP_CMD),A + LD HL,FXR_RECAL + LD (FXP_XR),HL + CALL FC_SETUPSEEK ; SPECIALIZATION OF SEEK + LD A,2 ; GENERIC COMMAND, BUT JUST FIRST COMMAND CODE + LD (FCP_BUFLEN),A + CALL FC_CMDPROC + RET + +FC_SENSEINT: + LD A,CMD_SENSEINT + LD B,01FH ; CMD BITS ONLY + LD (FCP_CMD),A + LD HL,FXR_SENSEINT + LD (FXP_XR),HL + CALL FC_SETUPCMD + LD A,1 ; GENERIC COMMAND, BUT JUST FIRST COMMAND CODE + LD (FCP_BUFLEN),A + CALL FC_CMDPROC + RET + +FC_SPECIFY: + LD A,CMD_SPECIFY + LD B,01FH ; CMD BITS ONLY + LD (FCP_CMD),A + LD HL,FXR_SPECIFY + LD (FXP_XR),HL + CALL FC_SETUPSPECIFY + CALL FC_CMDPROC + RET + +FC_DRVSTAT: + LD A,CMD_DRVSTAT + LD B,01FH ; CMD BITS ONLY + LD (FCP_CMD),A + LD HL,FXR_DRVSTAT + LD (FXP_XR),HL + CALL FC_SETUPCMD + CALL FC_CMDPROC + RET + +FC_SEEK: + LD A,CMD_SEEK + LD B,01FH ; CMD BITS ONLY + LD (FCP_CMD),A + LD HL,FXR_SEEK + LD (FXP_XR),HL + CALL FC_SETUPSEEK + CALL FC_CMDPROC + RET + +FC_VERSION: + LD A,CMD_VERSION + LD B,01FH ; CMD BITS ONLY + LD (FCP_CMD),A + LD HL,FXR_VERSION + LD (FXP_XR),HL + CALL FC_SETUPCMD + LD A,1 ; GENERIC COMMAND, BUT JUST FIRST COMMAND CODE + LD (FCP_BUFLEN),A + CALL FC_CMDPROC + RET +; +; HELPER FUNCTIONS TO SETUP CMDBUF +; +FC_SETUPCMD: + PUSH BC ; B CONTAINS BIT MASK TO USE FOR FIRST BYTE + ; SO THAT WE CAN MASK OFF BITS THAT ARE NOT + ; USED FOR CERTAIN COMMANDS + LD DE,FCP_BUF + + AND 01FH ; REMOVE ANY EXTRANEOUS BITS FROM COMMAND BYTE + LD C,A ; SAVE THE COMMAND + LD A,(FCD_MT) ; GET MT BIT + AND 01H ; MASK TO REMOVE IRRELEVANT BITS FOR SAFETY + RLA ; MAKE ROOM FOR MFM + LD B,A ; SAVE WHAT WE HAVE SO FAR IN B + LD A,(FCD_MFM) ; GET MFM BIT + AND 01H ; MASK TO REMOVE IRRELEVANT BITS FOR SAFETY + OR B ; COMBINE WITH SAVED + RLA ; MAKE ROOM FOR SK + LD B,A ; SAVE WHAT WE HAVE SO FAR IN B + LD A,(FCD_SK) ; GET SK BIT + AND 01H ; MASK TO REMOVE IRRELEVANT BITS FOR SAFETY + OR B ; COMBINE WITH SAVED + RLA ; MAKE ROOM FOR THE COMMAND BITS + RLA + RLA + RLA + RLA + LD B,C ; RECOVER THE COMMAND VALUE + OR B ; COMBINE WITH SAVED + POP BC ; GET THE BIT MASK FOR FIRST BYTE + AND B ; APPLY IT + LD (DE),A ; SAVE THE BYTE + INC DE + + LD A,(FCD_H) ; START WITH HDS + AND 01H ; MASK TO REMOVE IRRELEVANT BITS FOR SAFETY + RLA ; MAKE ROOM FOR DS BITS + RLA ; + LD B,A ; SAVE WHAT WE HAVE SO FAR IN B + LD A,(FCD_DS) ; GET DS VALUE + AND 03H ; MASK TO REMOVE IRRELEVANT BITS FOR SAFETY + OR B ; COMBINE WITH SAVED + LD (DE),A ; SAVE THE BYTE + INC DE + + LD A,2 ; THIS IS A 2 BYTE COMMAND STRING + LD (FCP_BUFLEN),A + + RET + +FC_SETUPIO: + CALL FC_SETUPCMD + + LD A,(FCD_C) + LD (DE),A + INC DE + + LD A,(FCD_H) + LD (DE),A + INC DE + + LD A,(FCD_R) + LD (DE),A + INC DE + + LD A,(FCD_N) + LD (DE),A + INC DE + + LD A,(FCD_EOT) + LD (DE),A + INC DE + + LD A,(FCD_GPL) + LD (DE),A + INC DE + + LD A,(FCD_DTL) + LD (DE),A + INC DE + + LD A,9 + LD (FCP_BUFLEN),A + + RET + +FC_SETUPFMT: + CALL FC_SETUPCMD + + LD A,(FCD_N) + LD (DE),A + INC DE + + LD A,(FCD_SC) + LD (DE),A + INC DE + + LD A,(FCD_GPLF) + LD (DE),A + INC DE + + LD A,(FCD_D) + LD (DE),A + INC DE + + LD A,6 + LD (FCP_BUFLEN),A + +; SETUP FORMAT BUFFER WITH SECTOR ID INFO FOR ENTIRE TRACK - (C,H,S,N) FOR EACH SECTOR +; INTERLEAVE AS REQUESTED +; +; B = CURRENT INTERLEAVE PASS STARTING SECTOR ID +; C = CURRENT SECTOR ID +; H = INTERLEAVE STEP +; L = LAST SECTOR ID + 1 +; DE = BUFFER POINTER +; + + PUSH DE + PUSH HL + + ; COMPUTE INTERLEAVE STEP + LD H,-1 + LD A,(FCD_X) + LD B,A + LD A,(FCD_SC) +LOOP: + INC H + SUB B + JR NC,LOOP + + LD DE,FXP_BUF ; DE POINTS TO START OF BUFFER + + LD A,(FCD_SOT) ; GET FIRST SECTOR ID + LD B,A ; B = FIRST SECTOR ID FOR CURRENT INTERLEAVE PASS + LD C,A ; C = SECTOR ID + + LD A,(FCD_SC) ; NUM SECTORS TO SET UP + ADD A,C + LD L,A ; L = LAST SECTOR ID + 1 + +FC_SETUPFMT1: + ; CYLINDER + LD A,(FCD_C) + LD (DE),A + INC DE + + ; HEAD + LD A,(FCD_H) + LD (DE),A + INC DE + + ; SECTOR ID + LD A,C + LD (DE),A + INC DE + + ; SECTOR SIZE + LD A,(FCD_N) + LD (DE),A + INC DE + + ; INC SECTOR ID BY INTERLEAVE STEP + LD A,H + ADD A,C + LD C,A + + ; LOOP IF WE HAVE NOT GOTTEN PAST LAST SECTOR ID + CP L + JP M,FC_SETUPFMT1 + + ; SETUP FOR NEXT INTERLEAVE PASS + INC B + LD C,B + + ; LOOP IF WE ARE NOT DONE + LD A,H + CP C + JP P,FC_SETUPFMT1 + + ; DONE, FINALIZE BUFFER + LD A,(FCD_SC) ; GET SECTOR COUNT + RLCA ; MULTIPLY BY 4 + RLCA + LD (FXP_BUFLEN),A ; STORE AS BUFFER LENGTH + + POP HL + POP DE + RET + +FC_SETUPSCAN: + CALL FC_SETUPIO ; START WITH GENERIC IO CMD + + LD DE,FCP_BUF+8 ; REPLACE DTL WITH STP + LD A,(FCD_STP) + LD (DE),A + + RET + +FC_SETUPSEEK: + CALL FC_SETUPCMD ; START WITH GENERIC IO CMD + + LD A,(FCD_C) + LD (DE),A + INC DE + + LD DE,FCP_BUF ; REMOVE EXTRANEOUS BITS FROM CC0 + LD A,(DE) + AND 0FH + LD (DE),A + + LD A,3 + LD (FCP_BUFLEN),A + + RET + +FC_SETUPSPECIFY: + CALL FC_SETUPCMD + DEC DE ; BACKUP 1 BYTE, WE ONLY WANT FIRST BYTE + + LD A,(FCD_SRTHUT) + LD (DE),A ; SAVE THE BYTE + INC DE + + LD A,(FCD_HLT) + AND 07FH + RLA + LD B,A + LD A,(FCD_ND) + AND 01H + OR B + LD (DE),A ; SAVE THE BYTE + INC DE + + LD A,3 + LD (FCP_BUFLEN),A + + RET +; +; MAIN FDC COMMAND PROCESSOR +; +FC_CMDPROC: + CALL FOP + CALL FC_PRTRESULTS + RET +; +; FDC SEQUENCE INITIALIZATION +; +FC_INIT: + LD HL,FDCBM + LD A,(HL) + AND _DIO + JR NZ,FC_INIT1 + LD A,(HL) + AND _ZETA | _DIO3 + JR NZ,FC_INIT2 + LD A,(HL) + AND _DIDE | _N8 | _ZETA2 + JR NZ,FC_INIT3 + JR FC_INIT4 +FC_INIT1: ; DIO + LD A,(FCD_DORA) + JR FC_INIT4 +FC_INIT2: ; ZETA, DIO3 + LD A,(FCD_DORB) + JR FC_INIT4 +FC_INIT3: ; DIDE, N8, ZETA2 + LD A,(FCD_DORC) + JR FC_INIT4 + +FC_INIT4: + LD (FST_DOR),A + CALL FC_SETDOR + RET +; +; SET FST_DOR +; +FC_SETDOR + PUSH AF + LD A,(FST_DOR) + LD C,(IY+CFG_DOR) + OUT (C),A + POP AF + RET +; +; RESET FDC BY PULSING BIT 7 OF DOR LOW +; +FC_RESETFDC: + LD C,(IY+CFG_DOR) + LD HL,FDCBM + LD A,(HL) + AND _ZETA | _DIO3 + JR NZ,FC_RESETFDC1 + LD A,(HL) + AND _DIDE | _N8 | _ZETA2 + JR NZ,FC_RESETFDC2 + JR FC_RESETFDC3 +FC_RESETFDC1: ; ZETA, DIO3 + LD A,(FST_DOR) + RES 7,A + OUT (C),A + PUSH AF ; SAVE AF BECAUSE DELAY TRASHES IT + CALL DELAY + POP AF ; RESTORE AF + SET 7,A + OUT (C),A + JR FC_RESETFDC3 +FC_RESETFDC2: ; DIDE, N8, ZETA2 + LD A,0 + OUT (C),A + LD A,(FST_DOR) + OUT (C),A + JR FC_RESETFDC3 +FC_RESETFDC3: + LD DE,156 ; DELAY: 16us * 156 = 2.5ms + CALL VDELAY + + RET +; +; PULSE TERMCT TO TERMINATE ANY ACTIVE EXECUTION PHASE +; +FC_PULSETC: + LD A,(FDCBM) + AND _DIDE | _N8 | _ZETA2 + JR NZ,FC_PULSETC1 + ; NOT DIDE, N8, ZETA2 + LD C,(IY+CFG_DOR) + LD A,(FST_DOR) + SET 0,A + OUT (C),A + RES 0,A + OUT (C),A + JR FC_PULSETC2 +FC_PULSETC1: ; DIDE, N8, ZETA2 + LD C,(IY+CFG_TC) + IN A,(C) + JR FC_PULSETC2 +FC_PULSETC2: + RET +; +; SET FST_DOR FOR MOTOR CONTROL ON +; +FC_MOTORON: + LD HL,FDCBM + LD A,(HL) + AND _DIO + JR NZ,FC_MOTORON1 + LD A,(HL) + AND _ZETA | _DIO3 + JR NZ,FC_MOTORON2 + LD A,(HL) + AND _DIDE | _N8 | _ZETA2 + JR NZ,FC_MOTORON3 + JR FC_MOTORON4 +FC_MOTORON1: ; DIO + LD HL,FST_DOR ; POINT TO FDC_DOR + RES 1,(HL) ; SET MOTOR ON + JR FC_MOTORON4 +FC_MOTORON2: ; ZETA, DIO3 + LD HL,FST_DOR ; POINT TO FDC_DOR + SET 1,(HL) + JR FC_MOTORON4 +FC_MOTORON3: ; DIDE, N8, ZETA2 + LD HL,FST_DOR ; POINT TO FDC_DOR + LD A,(HL) ; START WITH CURRENT DOR + AND 11111100B ; GET RID OF ANY ACTIVE DS BITS + LD C,A ; SAVE IT FOR NOW + LD A,(FCD_DS) ; NOW GET CURRENT DS + LD B,A ; PUT IN B FOR LATER + OR C ; COMBINE WITH SAVED DOR + LD C,A ; RE-SAVE IT + INC B ; SET UP B AS LOOP COUNTER (DS + 1) + LD A,00001000B ; STARTING BIT PATTERN FOR MOTOR +FC_MOTORON3A: + RLA ; SHIFT LEFT + DJNZ FC_MOTORON3A ; DS TIMES + OR C ; COMBINE WITH SAVED + LD (HL),A ; COMMIT THE NEW VALUE TO FST_DOR + JR FC_MOTORON4 + +FC_MOTORON4: + CALL FC_SETDOR ; OUTPUT TO CONTROLLER + CALL LDELAY ; WAIT 1/2 SEC ON MOTOR START FOR SPIN-UP + LD A,(FDCBM) + AND _DIDE | _N8 | _ZETA2 + JR Z,FC_MOTORON5 + LD A,(FCD_DCR) + LD C,(IY+CFG_DCR) + OUT (C),A +FC_MOTORON5: + RET +; +; SET FST_DOR FOR MOTOR CONTROL OFF +; +FC_MOTOROFF: + LD HL,FDCBM + LD A,(HL) + AND _DIO + JR NZ,FC_MOTOROFF1 + LD A,(HL) + AND _ZETA | _DIO3 + JR NZ,FC_MOTOROFF2 + LD A,(HL) + AND _DIDE | _N8 | _ZETA2 + JR NZ,FC_MOTOROFF3 + JR FC_MOTOROFF4 +FC_MOTOROFF1: ; DIO + LD HL,FST_DOR ; POINT TO FDC_DOR + SET 1,(HL) ; SET MOTOR OFF + JR FC_MOTOROFF4 +FC_MOTOROFF2: ; ZETA, DIO3 + LD HL,FST_DOR ; POINT TO FDC_DOR + RES 1,(HL) + JR FC_MOTOROFF4 +FC_MOTOROFF3: ; DIDE, N8, ZETA2 + LD HL,FST_DOR ; POINT TO FDC_DOR + LD A,DORC_INIT + LD (HL),A + JR FC_MOTOROFF4 + +FC_MOTOROFF4: + CALL FC_SETDOR ; OUTPUT TO CONTROLLER + RET +; +;=============================================================================== +; FDC OPERATIONS +;=============================================================================== +; +FOP: +; +; INITIALIZATION +; + LD A,0 + LD (FRB_LEN),A + + LD A,FRC_OK + LD (FST_RC),A + + LD B,0 ; B IS LOOP COUNTER + LD C,(IY+CFG_MSR) ; SET C TO MSR PORT +FOP_CLR1: + CALL DELAY ; FDC MAY TAKE UP TO 12us TO UPDATE MSR + IN A,(C) ; GET STATUS + LD (FST_MSR),A ; SAVE IT FOR POTENTIAL LATER DIAGNOSIS + AND 0C0H ; ISOLATE HIGH NIBBLE, RQM/DIO/EXM/CB + CP 0C0H ; LOOKING FOR RQM=1, DIO=1, BYTES PENDING + JP NZ,FOP_CMD1 ; NO BYTES PENDING, GO TO NEXT PHASE + INC C ; SWITCH TO DATA PORT + IN A,(C) ; GET THE PENDING BYTE AND DISCARD + DEC C ; SWITCH BACK TO MSR PORT + DJNZ FOP_CLR1 ; KEEP CHECKING TILL COUNTER EXHAUSTER + JP FOP_TOFDCRDY ; OTHERWISE, TIMEOUT +; +; SEND COMMAND +; +FOP_CMD1: + LD HL,FCP_BUF + LD A,(FCP_BUFLEN) + LD D,A ; D = CMD BYTES TO SEND + +FOP_CMD2: ; START OF LOOP TO SEND NEXT BYTE + LD B,0 ; B IS LOOP COUNTER + +FOP_CMD4: ; START OF STATUS LOOP, WAIT FOR FDC TO BE READY FOR BYTE + CALL DELAY ; FDC MAY TAKE UP TO 12us TO UPDATE MSR + ; TYPICAL MSR TRANSITIONS: 80 10 90 90 + LD C,(IY+CFG_MSR) ; SET C TO MSR PORT + IN A,(C) ; READ MAIN STATUS REGISTER + LD (FST_MSR),A ; SAVE IT FOR POTENTIAL LATER DIAGNOSIS + AND 0C0H ; ISOLATE RQM/DIO + CP 080H ; LOOKING FOR RQM=1, DIO=0 (FDC READY FOR A BYTE) + JP Z,FOP_CMD6 ; GOOD, GO TO SEND BYTE + CP 0C0H ; HMMMM... RQM=1 & DIO=1, FDC WANTS TO SEND US DATA, UNEXPECTED + JP Z,FOP_RES ; GO IMMEDIATELY TO RESULTS??? + DJNZ FOP_CMD4 ; LOOP TILL COUNTER EXHAUSTED + JP FOP_TOSNDCMD ; COUNTER EXHAUSTED, TIMEOUT / EXIT + +FOP_CMD6: ; SEND NEXT BYTE + LD A,(HL) ; POINT TO NEXT BYTE TO SEND + LD C,(IY+CFG_DATA) ; SET C TO DATA PORT + OUT (C),A ; PUSH IT TO FDC + INC HL ; INCREMENT POINTER FOR NEXT TIME + DEC D ; DECREMENT NUM BYTES LEFT TO SEND + JP NZ,FOP_CMD2 ; DO NEXT BYTE + +; +; EXECUTION PHASE +; +FOP_X1: + LD HL,(FXP_XR) ; LOAD THE EXECUTION ROUTINE ADDRESS + CALL JPHL ; CALL INDIRECTLY VIA HL + + ; FIX: NEED TO CHECK REG A FOR STATUS, DEPENDING ON STATUS + ; IT MAY BE NECESSARY TO DO SOMETHING SPECIAL? + +; JP FOP_RES ; CONTINUE WITH GETRESULTS + +; +; RESULTS PHASE +; +FOP_RES: + LD B,0 ; D = BYTES RECEIVED + LD HL,FRB_LEN ; POINT TO BUFFER LENGTH + LD (HL),D ; UPDATE NUMBER OF BYTES RECEIVED + LD HL,FRB ; POINT TO RECEIVE BUFFER + +FOP_RES0: + LD DE,0 ; DE IS LOOP COUNTER + LD C,(IY+CFG_MSR) ; SET C TO MSR PORT + +FOP_RES1: + CALL DELAY ; FDC MAY TAKE UP TO 12us TO UPDATE MSR + IN A,(C) ; READ MAIN STATUS REGISTER + LD (FST_MSR),A ; SAVE IT FOR POTENTIAL LATER DIAGNOSIS + AND 0F0H ; ISOLATE RQM/DIO + CP 0D0H ; LOOKING FOR RQM=1, DIO=1, BUSY=1 (FDC BYTE PENDING) + JP Z,FOP_RES2 ; GOOD, GO TO RECEIVE BYTE + CP 080H ; CHECK FOR RQM=1, DIO=0 (NOTHING LEFT) + JP Z,FOP_EVAL ; IF NOTHING, ALL DONE, LEFT GO TO EOD/EXIT + DEC DE + LD A,D + OR E + JP NZ,FOP_RES1 + JP FOP_TOGETRES ; OTHERWISE TIMEOUT ERROR + +FOP_RES2: ; PROCESS NEXT PENDING BYTE + LD A,FRB_SIZ ; GET BUF SIZE + CP B ; REACHED MAX? + JP Z,FOP_BUFMAX ; HANDLE BUF MAX/EXIT + INC C ; SWITCH TO DATA PORT + IN A,(C) ; GET THE BYTE + DEC C ; SWITCH TO MSR PORT + LD (HL),A ; SAVE VALUE + INC HL ; INCREMENT BUF POS + INC B ; INCREMENT BYTES RECEIVED + PUSH HL ; SAVE HL + LD HL,FRB_LEN ; POINT TO BUFFER LENGTH + LD (HL),B ; UPDATE NUMBER OF BYTES RECEIVED + POP HL ; RESTORE HL + JR FOP_RES0 ; CONTINUE READ LOOP + +; +; EXIT POINTS +; +FOP_NOTIMPL: + LD A,FRC_NOTIMPL + JP FOP_ERR + +FOP_CMDERR: + LD A,FRC_CMDERR + JP FOP_ERR + +FOP_ERROR: + LD A,FRC_ERROR + JP FOP_ERR + +FOP_ABORT: + LD A,FRC_ABORT + JP FOP_ERR + +FOP_BUFMAX: + LD A,FRC_BUFMAX + JP FOP_ERR + +FOP_TOFDCRDY: + LD A,FRC_TOFDCRDY + JP FOP_ERR + +FOP_TOSNDCMD: + LD A,FRC_TOSNDCMD + JP FOP_ERR + +FOP_TOGETRES: + LD A,FRC_TOGETRES + JP FOP_ERR + +FOP_TOEXEC: + LD A,FRC_TOEXEC + JP FOP_ERR + +FOP_ERR: + LD (FST_RC),A + +FOP_EVAL: + LD A,(FCP_CMD) + ; DRVSTAT IS WEIRD, HAS ONLY ST3, NOTHING TO EVAL + CP CMD_DRVSTAT + JP Z,FOP_EXIT + ; DO WE HAVE ST0? + LD A,(FRB_LEN) + CP 1 + JP M,FOP_EXIT + +FOP_EVALST0: + LD A,(FRB_ST0) + AND 11000000B + CP 01000000B ; ABTERM + JR Z,FOP_ABTERM + CP 10000000B ; INVCMD + JR Z,FOP_INVCMD + CP 11000000B ; DSKCHG + JR Z,FOP_DSKCHG + JR FOP_EXIT + +FOP_ABTERM: + ; SENSEINT DOES NOT USE ST1 + LD A,(FCP_CMD) + CP CMD_SENSEINT + JR Z,FOP_ABTERM1 + ; DO WE HAVE ST1? + LD A,(FRB_LEN) + CP 2 + JP M,FOP_ABTERM1 + JR FOP_EVALST1 +FOP_ABTERM1: ; NO FURTHER DATA, SET FST TO ABTERM + LD A,FRC_ABTERM + JP FOP_SETFST + +FOP_INVCMD: + LD A,FRC_INVCMD + JP FOP_SETFST + +FOP_DSKCHG: + LD A,FRC_DSKCHG + JP FOP_SETFST + +FOP_EVALST1: + LD A,(FRB_ST1) + BIT 7,A + JP NZ,FOP_ENDCYL + BIT 5,A + JP NZ,FOP_DATAERR + BIT 4,A + JP NZ,FOP_OVERRUN + BIT 2,A + JP NZ,FOP_NODATA + BIT 1,A + JP NZ,FOP_NOTWRIT + BIT 0,A + JP NZ,FOP_MISADR + JP FOP_EXIT + +FOP_ENDCYL: + LD A,FRC_ENDCYL + JP FOP_SETFST + +FOP_DATAERR: + LD A,FRC_DATAERR + JP FOP_SETFST + +FOP_OVERRUN: + LD A,FRC_OVERRUN + JP FOP_SETFST + +FOP_NODATA: + LD A,FRC_NODATA + JP FOP_SETFST + +FOP_NOTWRIT: + LD A,FRC_NOTWRIT + JP FOP_SETFST + +FOP_MISADR: + LD A,FRC_MISADR + JP FOP_SETFST + +FOP_SETFST: + LD (FST_RC),A + +FOP_EXIT: + RET + +; +; EXECUTION ROUTINES +; +FXR_READID: + JP FXRX + +FXR_READ: + LD HL,BUFFER ; POINT TO SECTOR BUFFER START + LD DE,(FCD_SECSZ) + + LD A,(DCD_MD) ; FIX: SHOULD NOT BE USING DCD HERE + CP MD_POLL + JP Z,FXRR + CP MD_INT + JP Z,IFXRR + CP MD_INTFAST + JP Z,FFXRR + CP MD_INTWAIT + JP Z,WFXRR + CP MD_DRQWAIT + JP Z,WFXRR + JP FXR_NOTIMPL + +FXR_WRITE: + LD HL,BUFFER ; POINT TO SECTOR BUFFER START + LD DE,(FCD_SECSZ) + + LD A,(DCD_MD) ; FIX: SHOULD NOT BE USING DCD HERE + CP MD_POLL + JP Z,FXRW + CP MD_INT + JP Z,IFXRW + CP MD_INTFAST + JP Z,FFXRW + CP MD_INTWAIT + JP Z,WFXRW + CP MD_DRQWAIT + JP Z,WFXRW + JP FXR_NOTIMPL + +FXR_FMTTRK: + LD HL,FXP_BUF ; POINT TO BUFFER START + LD D,0 + LD A,(FXP_BUFLEN) ; GET BYTE COUNT TO WRITE + LD E,A + + LD A,(DCD_MD) ; FIX: SHOULD NOT BE USING DCD HERE + CP MD_POLL + JP Z,FXRW + CP MD_INT + JP Z,IFXRW + CP MD_INTFAST + JP Z,IFXRW ; CAN'T USE FFXRW BECAUSE IT IS NOT 512 BYTES + CP MD_INTWAIT + JP Z,WFXRW + CP MD_DRQWAIT + JP Z,WFXRW + JP FXR_NOTIMPL + +FXR_NOTIMPL: +FXR_READDEL: +FXR_WRITEDEL: +FXR_READTRK: +FXR_SCANEQ: +FXR_SCANLOEQ: +FXR_SCANHIEQ: + LD A,FRC_NOTIMPL + LD (FST_RC),A + ; FALL THROUGH TO RET BELOW +FXR_NOP: +FXR_RECAL: +FXR_SENSEINT: +FXR_SPECIFY: +FXR_DRVSTAT: +FXR_SEEK: +FXR_VERSION: + RET +; +; NULL EXECUTION, NO DATA TO READ/WRITE (USED BY READID) +; +FXRX: + LD DE,1000H ; DE IS LOOP COUNTER, 4096 ITERATIONS OF 25ms + LD C,(IY+CFG_MSR) ; SET C TO MSR PORT +FXRX1: + CALL DELAY + IN A,(C) ; GET MSR + AND 0E0H ; ISOLATE RQM/DIO/EXM + CP 0C0H ; WE WANT RQM=1,DIO=1,EXM=0 (READY TO READ A BYTE W/ EXEC INACTIVE) + JP Z,FXR_END ; GOT IT, EXIT CLEAN + DEC DE ; DECREMENT COUNTER (16 BIT) + LD A,D ; CHECK FOR ZERO + OR E ; " + JR NZ,FXRX1 ; NOT ZERO YET, KEEP CHECKING + JP FXR_TO ; OTHERWISE, TIMEOUT ERROR + RET +; +; READ DATA +; +FXRR: + DI ; DISABLE INTERRUPTS TO AVOID TIMEOUTS + LD A,(CPUFREQ + 3) / 4 ; HIGH BYTE OF OUTER LOOP COUNTER + LD (FXP_TO+1),A ; SAVE HIGH BYTE OF COUNTER + XOR A ; LOW BYTE IS ZERO + LD (FXP_TO),A ; SAVE LOW BYTE + LD B,A ; INIT INNER LOOP COUNTER + LD C,(IY+CFG_MSR) ; SET C TO MSR PORT +FXRR1: LD B,0 ; SETUP FOR 256 ITERATIONS (INNER LOOP) + ; INNER LOOP +FXRR2: IN A,(C) ; GET MSR + CP 0F0H ; WE WANT RQM=1,DIO=1,EXM=1,BUSY=1 (READY TO RECEIVE A BYTE W/ EXEC ACTIVE) + JP Z,FXRR3 ; GOT IT, DO BYTE READ + DJNZ FXRR2 ; NOT READY, LOOP IF COUNTER NOT ZERO + ; OUTER LOOP + AND 0E0H ; MASK TO ISOLATE RQM, DIO, EXM + CP 0C0H ; IF RQM=1, DIO=1, EXM=0 (EXECUTION ABORTED) + JP Z,FXR_ABORT ; IF ZERO, BAIL OUT TO ERR ROUTINE, FIX: GO TO SPECIFIC ROUTINE FOR THIS??? + PUSH HL ; SAVE HL + LD HL,(FXP_TO) ; GET DOWN COUNTER + DEC HL ; DECREMENT + LD (FXP_TO),HL ; SAVE IT + LD A,L ; CHECK COUNTER + OR H ; ... FOR ZERO + POP HL ; RESTORE HL + JR NZ,FXRR1 ; LOOP IF NOT ZERO + JP FXR_TO ; OTHERWISE, TIMEOUT +FXRR3: ; READ A PENDING BYTE + INC C ; SET C TO DATA PORT + INI ; READ PORT C TO (HL) AND INC HL + DEC C ; SET C BACK TO STATUS PORT + DEC DE ; DECREMENT BYTE COUNT + LD A,D ; TEST COUNTER + OR E ; ... FOR ZERO + JP NZ,FXRR1 ; IF NOT ZERO, REPEAT LOOP + JP FXR_END ; CLEAN EXIT + +; +; INT READ DATA - SAFE VERSION +; HANDLES FDC ERRORS, BUT NO TIMEOUT +; + ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! + .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) + .ORG (($ & 0FF00H) + 40H) + .ENDIF +; +IFXRR: + DI + LD C,(IY+CFG_MSR) ; SET C TO MSR PORT +IFXRR2: + EI +IFXRRX .EQU $ - IFXRR + HALT + IN A,(C) + BIT 5,A + JP Z,FXR_ABORT + INC C ; SWITCH C TO DATA PORT + INI + DEC C ; SWITCH C BACK TO MSR PORT + DEC DE + LD A,E + OR D + JP NZ,IFXRR2 + JP FXR_END +; +; INT READ DATA - FAST VERSION +; FIXED SECTOR SIZE OF 512 BYTES +; HANGS ON FDC ERRORS, NO TIMEOUT +; + ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! + .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) + .ORG (($ & 0FF00H) + 40H) + .ENDIF +; +FFXRR: + DI + LD C,(IY+CFG_DATA) ; SET C TO DATA PORT FOR INI +FFXRR2 EI + HALT +FFXRRX1 .EQU $ - FFXRR + INI + JP NZ,FFXRR2 +FFXRR3 EI + HALT +FFXRRX2 .EQU $ - FFXRR + INI + JP NZ,FFXRR3 + JP FXR_END +; +; WAIT READ DATA +; HANGS ON FDC ERRORS, NO TIMEOUT +; +WFXRR: + DI + LD C,(IY+CFG_DMA) +WFXRR2: + INI ; GET PENDING BYTE + DEC DE ; DECREMENT BYTE COUNT + LD A,D + OR E + JP NZ,WFXRR2 ; IF NOT ZERO, REPEAT LOOP + JP FXR_END ; CLEAN EXIT + +; +; WRITE DATA +; +FXRW: + DI ; DISABLE INTERRUPTS TO AVOID TIMEOUTS + LD A,(CPUFREQ + 3) / 4 ; HIGH BYTE OF OUTER LOOP COUNTER + LD (FXP_TO+1),A ; SAVE HIGH BYTE OF COUNTER + XOR A ; LOW BYTE IS ZERO + LD (FXP_TO),A ; SAVE LOW BYTE + LD B,A ; INIT INNER LOOP COUNTER + LD C,(IY+CFG_MSR) ; SET C TO MSR PORT +FXRW1: LD B,0 ; SETUP FOR 256 ITERATIONS + ; INNER LOOP +FXRW2: IN A,(C) ; GET MSR + CP 0B0H ; WE WANT RQM=1,DIO=0,EXM=1,BUSY=1 (READY TO SEND A BYTE W/ EXEC ACTIVE) + JR Z,FXRW3 ; GOT IT, DO BYTE WRITE + DJNZ FXRW2 ; NOT READY, LOOP IF COUNTER NOT ZERO + ; OUTER LOOP + AND 0E0H ; MASK TO ISOLATE RQM, DIO, EXM + CP 0C0H ; IF RQM=1, DIO=1, EXM=0 (EXECUTION ABORTED) + JP Z,FXR_ABORT ; IF ZERO, BAIL OUT TO ERR ROUTINE, FIX: GO TO SPECIFIC ROUTINE FOR THIS??? + PUSH HL ; SAVE HL + LD HL,(FXP_TO) ; GET DOWN COUNTER + DEC HL ; DECREMENT + LD (FXP_TO),HL ; SAVE IT + LD A,L ; CHECK COUNTER + OR H ; ... FOR ZERO + POP HL ; RESTORE HL + JR NZ,FXRW1 ; LOOP IF NOT ZERO + JP FXR_TO ; OTHERWISE, TIMEOUT +FXRW3: ; READ A PENDING BYTE + INC C ; SET C TO DATA PORT + OUTI ; WRITE (HL) TO PORT C AND INC HL + DEC C ; SET C BACK TO STATUS PORT + DEC DE ; DECREMENT LOOP COUNTER + LD A,D ; TEST COUNTER + OR E ; ... FOR ZERO + JP NZ,FXRW1 ; IF NOT ZERO, REPEAT LOOP + JP FXR_END ; CLEAN EXIT + +; +; INT WRITE DATA - SAFE VERSION +; HANDLES FDC ERRORS, BUT NO TIMEOUT +; + ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! + .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) + .ORG (($ & 0FF00H) + 40H) + .ENDIF +; +IFXRW: + DI + LD C,(IY+CFG_MSR) ; SET C TO MSR PORT +IFXRW2: EI + HALT + IN A,(C) + BIT 5,A + JP Z,FXR_ABORT + INC C ; SWITCH TO DATA PORT + OUTI + DEC C ; SWITCH BACK TO MSR PORT + DEC DE + LD A,E + OR D + JP NZ,IFXRW2 + JP FXR_END +; +; INT WRITE DATA - FAST VERSION +; FIXED SECTOR SIZE OF 512 BYTES +; HANGS ON FDC ERRORS, NO TIMEOUT +; + ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! + .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) + .ORG (($ & 0FF00H) + 40H) + .ENDIF +; +FFXRW: + DI + LD C,(IY+CFG_DATA) ; SET C TO DATA PORT +FFXRW2 EI + HALT + OUTI + JP NZ,FFXRW2 +FFXRW3 EI + HALT + OUTI + JP NZ,FFXRW3 + JP FXR_END +; +; WAIT WRITE DATA +; HANGS ON FDC ERRORS, NO TIMEOUT +; +WFXRW: + DI + LD C,(IY+CFG_DMA) +WFXRW2: + OUTI ; WRITE IT 16ts + DEC DE ; DECREMENT BYTE COUNT 6ts + LD A,D ; 4ts + OR E ; 4ts + JP NZ,WFXRW2 ; IF NOT ZERO, REPEAT LOOP 10ts = 40 + JP FXR_END ; CLEAN EXIT + +; +; COMMON COMPLETION CODE FOR ALL EXECUTION ROUTINES +; + +FXR_TO: + LD A,FRC_TOEXEC + JP FXR_ERR + +FXR_ABORT: + LD A,FRC_ABORT + JP FXR_ERR +FXR_ERR: + LD (FST_RC),A + JP FXR_END2 + +FXR_END: + ; DO NOT PULSE TC AT END OF FORMAT + LD A,(FCP_CMD) + CP CMD_FMTTRK + JR Z,FXR_END2 + + ; DO NOT PULSE TC AT END OF READID + CP CMD_READID + JR Z,FXR_END2 + + CALL FC_PULSETC + +FXR_END2: + LD (FXP_A),A + LD (FXP_BC),BC + LD (FXP_DE),DE + LD (FXP_HL),HL + + EI ; I/O FINISHED, INTS BACK ON + RET + +; +;=============================================================================== +; COMMAND PROCESSING STATUS DISPLAY +;=============================================================================== +; +; PRINT STATUS +; +FC_PRTFST: + PUSH AF + PUSH BC + PUSH DE + PUSH HL + LD A,(FST_RC) ; A GETS FST_RC + LD B,FSST_COUNT ; B GETS TABLE ENTRY COUNT + LD HL,FSST + LD DE,FSST_ENTSIZ ; TABLE ENTRY LENGTH + +FC_PRTFST0: ; START OF LOOP + LD C,(HL) + CP C + JP Z,FC_PRTFST1 ; FOUND CODE + + ADD HL,DE ; POINT TO NEXT ENTRY + DJNZ FC_PRTFST0 ; CHECK NEXT ENTRY TILL COUNT IS ZERO + + ; NO MATCHING ENTRY, PRINT THE HEX VALUE + CALL PC_SPACE + CALL PC_LBKT + CALL PRTHEXBYTE + CALL PC_RBKT + JP FC_PRTFSTX + +FC_PRTFST1: ; ENTRY FOUND, PRINT IT + CALL PC_SPACE + INC HL + LD E,(HL) + INC HL + LD D,(HL) + CALL PC_LBKT + CALL WRITESTR + CALL PC_RBKT + +FC_PRTFSTX: + POP HL + POP DE + POP BC + POP AF + RET +; +; PRINT COMMAND +; +FC_PRTCMD: + PUSH AF + PUSH BC + PUSH DE + PUSH HL + LD A,(FCP_CMD) ; A GETS THE COMMAND CODE + LD B,FCT_COUNT ; B GETS TABLE ENTRY COUNT + LD HL,FCT + LD DE,FCT_ENTSIZ ; TABLE ENTRY LENGTH + +FCPC_LOOP: ; START OF LOOP + LD C,(HL) + CP C + JP Z,FCPC_MATCH ; FOUND CODE + + ADD HL,DE ; POINT TO NEXT ENTRY + DJNZ FCPC_LOOP ; CHECK NEXT ENTRY TILL COUNT IS ZERO + + ; NO MATCHING ENTRY, PRINT THE HEX VALUE + CALL PC_SPACE + CALL PC_LBKT + CALL PRTHEXBYTE + CALL PC_RBKT + JP FCPC_EXIT + +FCPC_MATCH: ; ENTRY FOUND, PRINT IT + INC HL + LD E,(HL) + INC HL + LD D,(HL) + CALL WRITESTR + +FCPC_EXIT: + POP HL + POP DE + POP BC + POP AF + RET +; +; PRINT RESULTS +; +FC_PRTRESULTS: + ; IF TRACE IS SET, FORCE PRINT RESULTS + LD A,(FCD_TRACE) + OR A + JP NZ,FCPR2 + + ; IF RC=OK, GET OUT, NOTHING TO PRINT + LD A,(FST_RC) + CP FRC_OK + RET Z + + ; SPECIAL CASE, DON'T PRINT IF SENSEINT & INVCMD/DSK CHG/ABTERM + LD A,(FCP_CMD) + CP CMD_SENSEINT + JP NZ,FCPR2 + + LD A,(FST_RC) + CP FRC_INVCMD + JP Z,FCPR_EXIT + CP FRC_DSKCHG + JP Z,FCPR_EXIT + CP FRC_ABTERM + JP Z,FCPR_EXIT + JP FCPR_EXIT + +FCPR2: + CALL NEWLINE + + CALL FC_PRTCMD + CALL PC_COLON + + LD A,(FCP_BUFLEN) + LD DE,FCP_BUF + CALL PRTHEXBUF + + LD DE,STR_ARROW + CALL WRITESTR + + LD A,(FRB_LEN) + LD DE,FRB + CALL PRTHEXBUF + + LD A,(FDCBM) + AND _ZETA | _DIO3 + JR Z,FCPR3 + LD DE,STR_DSKCHG + CALL WRITESTR + LD C,(IY+CFG_DIR) + IN A,(C) + AND 01H + CALL PRTHEXBYTE + +FCPR3: + LD A,(FST_RC) + CALL FC_PRTFST + +FCPR_EXIT: + RET +; +; DUMP EXECUTION INFO +; +FXR_DUMP: + CALL NEWLINE + LD DE,STR_OP + CALL WRITESTR + CALL PC_COLON + LD DE,STR_MSR + CALL WRITESTR + LD A,(FST_MSR) + CALL PRTHEXBYTE + LD DE,STR_BC + CALL WRITESTR + LD BC,(FXP_BC) + LD A,B + CALL PRTHEXBYTE + LD A,C + CALL PRTHEXBYTE + LD DE,STR_DE + CALL WRITESTR + LD BC,(FXP_DE) + LD A,B + CALL PRTHEXBYTE + LD A,C + CALL PRTHEXBYTE + LD DE,STR_HL + CALL WRITESTR + LD BC,(FXP_HL) + LD A,B + CALL PRTHEXBYTE + LD A,C + CALL PRTHEXBYTE + RET +; +; DOR BITS (3AH) +; +; DISKIO 250KBPS 500KBPS +; ------- ------- ------- +;D7 /DC/RDY 1 (N/A) 1 (N/A) +;D6 /REDWC (DENSITY) 0 (DD) 1 (HD) +;D5 P0* (PRECOMP BIT 0) 1 \ 0 \ +;D4 P1* (PRECOMP BIT 1) 0 (125NS) 1 (125NS) +;D3 P2* (PRECOMP BIT 2) 0 / 0 / +;D2 MINI (BITRATE) 1 (250KBPS) 0 (500KBPS) +;D1 /MOTOR (ACTIVE LO) 1 (OFF) 1 (OFF) +;D0 TC (TERMINAL COUNT) 0 (OFF) 0 (OFF) +; +; *NOTE: FOR 9229 DATA SEPARATOR USED IN DISKIO, VALUE OF PRECOMP BITS CHANGES WITH MINI +; IF MINI=1 (250KBPS), USE 001 FOR 125NS PRECOMP, IF MINI=0, USE 010 FOR 125NS PRECOMP +; +DORA_BR250 .EQU 10100110B ; 250KBPS +DORA_BR500 .EQU 11010010B ; 500KBPS +; +DORA_INIT .EQU DORA_BR250 +; +; ZETA/DISKIO3 250KBPS 500KBPS +; ------------ ------- ------- +;D7 /FDC_RST 1 (RUN) 1 (RUN) +;D6 DENSEL 1 (DD) 0 (HD) +;D5 P0 (PRECOMP BIT 0) 1 \ 1 \ +;D4 P1 (PRECOMP BIT 1) 0 (125NS) 0 (125NS) +;D3 P2 (PRECOMP BIT 2) 0 / 0 / +;D2 MINI (BITRATE) 1 (250KBPS) 0 (500KBPS) +;D1 MOTOR 0 (OFF) 0 (OFF) +;D0 TC 0 (OFF) 0 (OFF) +; +; MOTOR AND DENSITY SELECT ARE INVERTED ON ZETA/DISKIO3 +; +DORB_BR250 .EQU 11100100B ; 250KBPS +DORB_BR500 .EQU 10100000B ; 500KBPS +; +DORB_INIT .EQU DORB_BR250 +; +; *** DIDE/N8/ZETA2 *** +; +DORC_INIT .EQU 00001100B ; SOFT RESET INACTIVE, DMA ENABLED +; +DORC_BR250 .EQU DORC_INIT +DORC_BR500 .EQU DORC_INIT +; +; DCR (ONLY APPLIES TO DIDE, N8, AND ZETA2) +; +DCR_BR250 .EQU 01H ; 250KBPS +DCR_BR500 .EQU 00H ; 500KBPS +; +;=============================================================================== +; GENERAL UTILITY ROUTINES +;=============================================================================== +; +; INITIALIZE BUFFER WITH FILLER BYTE +; HL = ADDRESS OF BUFFER +; DE = SIZE OF BUFFER +; B = FILLER BYTE VALUE +; +FILL_BUFFER: + LD A,B + LD (HL),A + INC HL + DEC DE + LD A,D + OR E + JP NZ,FILL_BUFFER + RET +; +; INITIALIZE BUFFER WITH PATTERN +; HL = ADDRESS OF BUFFER +; DE = SIZE OF BUFFER +; B = STARTING BYTE VALUE +; +PAT_BUFFER: + LD A,B + LD (HL),A + INC HL + DEC DE + INC B + LD A,D + OR E + JP NZ,PAT_BUFFER + RET +; +; PRINT A BLOCK OF MEMORY NICELY FORMATTED +; +DUMP_BUFFER: + CALL NEWLINE ; +BLKRD: + CALL PHL ; PRINT START LOCATION + LD C,16 ; SET FOR 16 LOCS + PUSH HL ; SAVE STARTING HL +NXTONE: + LD A,(HL) ; GET BYTE + CALL PRTHEXBYTE ; PRINT IT + CALL PC_SPACE ; +UPDH: + INC HL ; POINT NEXT + DEC C ; DEC. LOC COUNT + JR NZ,NXTONE ; IF LINE NOT DONE + ; NOW PRINT 'DECODED' DATA TO RIGHT OF DUMP +PCRLF: + CALL PC_SPACE ; SPACE IT + LD C,16 ; SET FOR 16 CHARS + POP HL ; GET BACK START +PCRLF0: + LD A,(HL) ; GET BYTE + AND 060H ; SEE IF A 'DOT' + LD A,(HL) ; O.K. TO GET + JR NZ,PDOT ; +DOT: + LD A,2EH ; LOAD A DOT +PDOT: + CALL COUT ; PRINT IT + INC HL ; + LD A,D ; + CP H ; + JR NZ,UPDH1 ; + LD A,E ; + CP L ; + JP Z,DUMP_END ; +; +;IF BLOCK NOT DUMPED, DO NEXT CHARACTER OR LINE +UPDH1: + DEC C ; DEC. CHAR COUNT + JR NZ,PCRLF0 ; DO NEXT +CONTD: + CALL NEWLINE ; + JP BLKRD ; + +DUMP_END: + RET ; +; +; UTILITY PROCS TO PRINT SINGLE CHARACTERS +; +PC_SPACE: + PUSH AF ; Store AF + LD A,' ' ; LOAD A "SPACE" + CALL COUT ; SCREEN IT + POP AF ; RESTORE AF + RET ; DONE + +PC_HYPHEN: + PUSH AF ; Store AF + LD A,'-' ; LOAD A COLON + CALL COUT ; SCREEN IT + POP AF ; RESTORE AF + RET ; DONE + +PC_COLON: + PUSH AF ; Store AF + LD A,':' ; LOAD A COLON + CALL COUT ; SCREEN IT + POP AF ; RESTORE AF + RET ; DONE + +PC_EQUAL: + PUSH AF ; Store AF + LD A,'=' ; LOAD A COLON + CALL COUT ; SCREEN IT + POP AF ; RESTORE AF + RET ; DONE + +PC_CR: + PUSH AF ; Store AF + LD A,CHR_CR ; LOAD A + CALL COUT ; SCREEN IT + POP AF ; RESTORE AF + RET ; DONE + +PC_LF: + PUSH AF ; Store AF + LD A,CHR_LF ; LOAD A + CALL COUT ; SCREEN IT + POP AF ; RESTORE AF + RET ; DONE + +PC_LBKT: + PUSH AF ; Store AF + LD A,'[' ; LOAD A COLON + CALL COUT ; SCREEN IT + POP AF ; RESTORE AF + RET ; DONE + +PC_RBKT: + PUSH AF ; Store AF + LD A,']' ; LOAD A COLON + CALL COUT ; SCREEN IT + POP AF ; RESTORE AF + RET ; DONE + +PC_LPAREN: + PUSH AF ; Store AF + LD A,'(' ; LOAD A COLON + CALL COUT ; SCREEN IT + POP AF ; RESTORE AF + RET ; DONE + + +PC_RPAREN: + PUSH AF ; Store AF + LD A,')' ; LOAD A COLON + CALL COUT ; SCREEN IT + POP AF ; RESTORE AF + RET ; DONE + +PC_BS: + PUSH AF ; Store AF + LD A,CHR_BS ; LOAD A + CALL COUT ; SCREEN IT + POP AF ; RESTORE AF + RET ; DONE + +NEWLINE_USED .DB 1 +NEWLINE: + CALL PC_CR + CALL PC_LF + LD A,1 + PUSH AF + LD (NEWLINE_USED),A + POP AF ; RESTORE AF + RET ; DONE + +COPYSTR: + LD A,(HL) + CP '$' + RET Z + LDI + JR COPYSTR + +; +;__COUT_________________________________________________________________________________________________________________________ +; +; PRINT CONTENTS OF A +;________________________________________________________________________________________________________________________________ +; +COUT_BUFFER .TEXT " $" +; +COUT: + PUSH BC ; + PUSH AF ; + PUSH HL ; + PUSH DE ; + LD (COUT_BUFFER),A ; + LD DE,COUT_BUFFER ; + LD C,09H ; CP/M WRITE START STRING TO CONSOLE CALL + CALL 0005H + POP DE ; + POP HL ; + POP AF ; + POP BC ; + RET ; DONE +; +;__PHL_________________________________________________________________________________________________________________________ +; +; PRINT THE HL REG ON THE SERIAL PORT +;________________________________________________________________________________________________________________________________ +; +PHL: + LD A,H ; GET HI BYTE + CALL PRTHEXBYTE ; DO HEX OUT ROUTINE + LD A,L ; GET LOW BYTE + CALL PRTHEXBYTE ; HEX IT + CALL PC_SPACE ; + RET ; DONE +; +; GET A LINE BUFFER WITH 2 HEX CHARS, HL=ADDRESS OF LINE BUFFER +; EXIT WITH C = NUMBER OF CHARS, C=0 MEANS NOTHING ENTERED +; +GETLNHEX: + LD C,0 ; C = CHAR COUNT +GLH_LOOP: + CALL GETKEYUC + OR A + JP Z,GLH_LOOP + CP CHR_CR + JP Z,GLH_CHK + CP CHR_BS + JP Z,GLH_BS + CP '0' + JP M,GLH_LOOP + CP '9' + 1 + JP M,GLH_APPEND + CP 'A' + JP M,GLH_LOOP + CP 'F' + 1 + JP M,GLH_APPEND + JP GLH_LOOP +GLH_BS: + LD A,C + OR A + JP Z,GLH_LOOP + CALL PC_BS + CALL PC_SPACE + CALL PC_BS + DEC C + DEC HL + JP GLH_LOOP +GLH_APPEND: + LD B,A + LD A,C + CP 2 + JP P,GLH_LOOP + LD A,B + CALL COUT + LD (HL),A + INC C + INC HL + JP GLH_LOOP +GLH_CHK: + LD A,C + CP 2 + JP Z,GLH_EXIT + CP 0 + JP Z,GLH_EXIT + JP GLH_LOOP +GLH_EXIT: + RET +; +;__HEXIN__________________________________________________________________________________________________________________________ +; +; GET ONE BYTE OF HEX DATA FROM BUFFER IN HL, RETURN IN A +;________________________________________________________________________________________________________________________________ +; +HEXIN: + PUSH BC ; SAVE BC REGS. + CALL NIBL ; DO A NIBBLE + RLC A ; MOVE FIRST BYTE UPPER NIBBLE + RLC A ; + RLC A ; + RLC A ; + LD B,A ; SAVE ROTATED BYTE + CALL NIBL ; DO NEXT NIBBLE + ADD A,B ; COMBINE NIBBLES IN ACC. + POP BC ; RESTORE BC + RET ; DONE +NIBL: + LD A,(HL) ; GET K.B. DATA + INC HL ; INC KB POINTER + CP 40H ; TEST FOR ALPHA + JR NC,ALPH ; + AND 0FH ; GET THE BITS + RET ; +ALPH: + AND 0FH ; GET THE BITS + ADD A,09H ; MAKE IT HEX A-F + RET ; +; +; COPY A $ TERMINATED STRING FROM ADDRESS IN DE TO ADDRESS IN HL +; DE = ADDRESS OF SOURCE STRING +; LH = ADDRESS OF TARGET LOCATION +; +STRCPY: + LD A,(DE) + CP '$' + JP Z,STRCPYX + LD (HL),A + INC HL + INC DE + JP STRCPY + +STRCPYX: + RET +; +; GET A HEX BYTE VALUE FROM THE USER +; WILL UPDATE STORED VALUE, EMPTY RESPONSE LEAVES VALUE ALONE +; DE = ADDRESS OF PROMPT STRING +; HL = ADDRESS OF VALUE +; BC = HI/BO ALLOWABLE RANGE OF VALID VALUES +; +GHB_PROMPTP .DW 0 +GHB_RANGE .DW 0 +GHB_VALUEP .DW 0 +GHB_CLEAR .DB " \b\b$" + +GETHEXBYTE: + LD BC,000FFH +GETHEXBYTERNG: + LD (GHB_PROMPTP),DE + LD (GHB_RANGE),BC + LD (GHB_VALUEP),HL + CALL NEWLINE +GHB_LOOP: + CALL PC_CR + LD DE,STR_ENTER + CALL WRITESTR + CALL PC_SPACE + LD DE,(GHB_PROMPTP) + CALL WRITESTR + CALL PC_SPACE + LD BC,(GHB_RANGE) + CALL PC_LBKT + LD A,B + CALL PRTHEXBYTE + CALL PC_HYPHEN + LD A,C + CALL PRTHEXBYTE + CALL PC_RBKT + CALL PC_SPACE + CALL PC_LPAREN + LD HL,(GHB_VALUEP) + LD A,(HL) + CALL PRTHEXBYTE + CALL PC_RPAREN + CALL PC_COLON + CALL PC_SPACE + LD DE,GHB_CLEAR + CALL WRITESTR + LD HL,KEYBUF + CALL GETLNHEX + LD A,C + CP 0 ; RETAIN CURRENT VALUE + JP Z,GHB_EXIT + CP 2 ; INPUT LOOKS OK, UPDATE IT + JP Z,GHB_CHK + JP GHB_LOOP ; ANYTHING ELSE, BAD INPUT, DO OVER +GHB_CHK: + LD HL,KEYBUF + CALL HEXIN + LD BC,(GHB_RANGE) + CP B + JP C,GHB_LOOP + CP C + JP Z,GHB_OK + JP NC,GHB_LOOP +GHB_OK: + LD HL,(GHB_VALUEP) + LD (HL),A +GHB_EXIT: + RET + +; +; PRINT THE HEX BYTE VALUE IN A +; +HEXSTRBUF .TEXT "XX$" +; +PRTHEXBYTE: + PUSH AF + PUSH DE + LD DE,HEXSTRBUF + CALL HEXSTRBYTE + LD A,'$' + LD (DE),A + LD DE,HEXSTRBUF + CALL WRITESTR + POP DE + POP AF + RET +; +; PRINT THE HEX WORD VALUE IN BC +; +PRTHEXWORD: + PUSH AF + LD A,B + CALL PRTHEXBYTE + LD A,C + CALL PRTHEXBYTE + POP AF + RET +; +; CONVERT VALUE IN A TO A 2 CHARACTER HEX STRING AT DE +; +HEXCHR .TEXT "0123456789ABCDEF" +; +HEXSTRBYTE: + PUSH BC + PUSH HL + PUSH AF + LD BC,0 + RRA + RRA + RRA + RRA + AND 0FH + LD C,A + LD HL,HEXCHR + ADD HL,BC + LD A,(HL) + LD (DE),A + INC DE + POP AF + PUSH AF + LD BC,0 + AND 0FH + LD C,A + LD HL,HEXCHR + ADD HL,BC + LD A,(HL) + LD (DE),A + INC DE + POP AF + POP HL + POP BC + RET +; +; CONVERT VALUE IN BC TO A 4 CHARACTER HEX STRING AT DE +; +HEXSTRWORD: + LD A,B + CALL HEXSTRBYTE + LD A,C + CALL HEXSTRBYTE + RET + +; +; PRINT A BYTE BUFFER IN HEX POINTED TO BY DE +; REGISTER A HAS SIZE OF BUFFER +; +PRTHEXBUF: + CP 0 ; EMPTY BUFFER? + JP Z,PRTHEXBUF2 + + LD B,A +PRTHEXBUF1: + CALL PC_SPACE + LD A,(DE) + CALL PRTHEXBYTE + INC DE + DJNZ PRTHEXBUF1 + JP PRTHEXBUFX + +PRTHEXBUF2: + CALL PC_SPACE + LD DE,STR_EMPTY + CALL WRITESTR + +PRTHEXBUFX: + RET +; +; JP TO ADDRESS IN HL IN HEX POINTED TO BY DE +; MOSTLY USEFUL TO PERFORM AN INDIRECT CALL LIKE: +; LD HL,xxxx +; CALL JPHL +; +JPHL JP (HL) +; +; GENERATE A RANDOM BYTE +; +; RETURNS PSEUDO RANDOM 8 BIT NUMBER IN A. ONLY AFFECTS A. +; (SEED) IS THE BYTE FROM WHICH THE NUMBER IS GENERATED AND MUST BE +; INITIALIZED TO A NON ZERO VALUE OR THIS FUNCTION WILL ALWAYS RETURN +; ZERO. +; +RB_SEED .DB 1 ; RNDBYTE SEED (MUST NOT BE ZERO) +; +RNDBYTE: + LD A,(RB_SEED) ; GET SEED + AND 0B8H ; MASK NON-FEEDBACK BITS + SCF ; SET CARRY + JP PO,RB_NC ; SKIP CLEAR IF ODD + CCF ; COMPLEMENT CARRY (CLEAR IT) +RB_NC LD A,(RB_SEED) ; GET SEED BACK + RLA ; ROTATE CARRY INTO BYTE + LD (RB_SEED),A ; SAVE BACK FOR NEXT + RET ; DONE +; +; ADD HL,A +; +; A REGISTER IS DESTROYED! +; +ADDHLA: + ADD A,L + LD L,A + RET NC + INC H + RET +; +; OUTPUT A '$' TERMINATED STRING +; +WRITESTR: + PUSH AF + PUSH BC + PUSH DE + PUSH HL + LD C,09H + CALL 0005H + POP HL + POP DE + POP BC + POP AF + RET +; +; READ A KEY, RETURN VALUE IN A +; +GETKEY: + PUSH BC + PUSH DE + PUSH HL + LD C,06H + LD E,0FFH + CALL 0005H + POP HL + POP DE + POP BC + + CP 03 + JP Z,EXIT + + RET + +GETKEYUC: + CALL GETKEY + CP 'a' + JP M,GETKEYUC_EXIT + CP 'z' + 1 + JP M,GETKEYUC_FIX + JP GETKEYUC_EXIT +GETKEYUC_FIX: + AND 11011111B +GETKEYUC_EXIT: + RET +; +; DELAY 16US (CPU SPEED COMPENSATED) INCUDING CALL/RET INVOCATION +; REGISTER A AND FLAGS DESTROYED +; NO COMPENSATION FOR Z180 MEMORY WAIT STATES +; THERE IS AN OVERHEAD OF 3TS PER INVOCATION +; IMPACT OF OVERHEAD DIMINISHES AS CPU SPEED INCREASES +; +; CPU SCALER (CPUSCL) = (CPUHMZ - 2) FOR 16US + 3TS DELAY +; NOTE: CPUSCL MUST BE >= 1! +; +; EXAMPLE: 8MHZ CPU (DELAY GOAL IS 16US) +; LOOP = ((6 * 16) - 5) = 91TS +; TOTAL COST = (91 + 40) = 131TS +; ACTUAL DELAY = (131 / 8) = 16.375US +; + ; --- TOTAL COST = (LOOP COST + 40) TS -----------------+ +DELAY: ; 17TS (FROM INVOKING CALL) | + LD A,(CPUSCL) ; 13TS | +; | +DELAY1: ; | + ; --- LOOP = ((CPUSCL * 16) - 5) TS ------------+ | + DEC A ; 4TS | | +#IFDEF CPU_Z180 ; | | + OR A ; +4TS FOR Z180 | | +#ENDIF ; | | + JR NZ,DELAY1 ; 12TS (NZ) / 7TS (Z) | | + ; ----------------------------------------------+ | +; | + RET ; 10TS (RETURN) | + ;-------------------------------------------------------+ +; +; DELAY 16US * DE (CPU SPEED COMPENSATED) +; REGISTER DE, A, AND FLAGS DESTROYED +; NO COMPENSATION FOR Z180 MEMORY WAIT STATES +; THERE IS A 27TS OVERHEAD FOR CALL/RET PER INVOCATION +; IMPACT OF OVERHEAD DIMINISHES AS DE AND/OR CPU SPEED INCREASES +; +; CPU SCALER (CPUSCL) = (CPUHMZ - 2) FOR 16US OUTER LOOP COST +; NOTE: CPUSCL MUST BE > 0! +; +; EXAMPLE: 8MHZ CPU, DE=6250 (DELAY GOAL IS .1 SEC OR 100,000US) +; INNER LOOP = ((16 * 6) - 5) = 91TS +; OUTER LOOP = ((91 + 37) * 6250) = 800,000TS +; ACTUAL DELAY = ((800,000 + 27) / 8) = 100,003US +; + ; --- TOTAL COST = (OUTER LOOP + 27) TS ------------------------+ +VDELAY: ; 17TS (FROM INVOKING CALL) | +; | + ; --- OUTER LOOP = ((INNER LOOP + 37) * DE) TS ---------+ | + LD A,(CPUSCL) ; 13TS | | +; | | +VDELAY1: ; | | + ; --- INNER LOOP = ((CPUSCL * 16) - 5) TS ------+ | | +#IFDEF CPU_Z180 ; | | | + OR A ; +4TS FOR Z180 | | | +#ENDIF ; | | | + DEC A ; 4TS | | | + JR NZ,VDELAY1 ; 12TS (NZ) / 7TS (Z) | | | + ; ----------------------------------------------+ | | +; | | + DEC DE ; 6TS | | +#IFDEF CPU_Z180 ; | | + OR A ; +4TS FOR Z180 | | +#ENDIF ; | | + LD A,D ; 4TS | | + OR E ; 4TS | | + JP NZ,VDELAY ; 10TS | | + ;-------------------------------------------------------+ | +; | + RET ; 10TS (FINAL RETURN) | + ;---------------------------------------------------------------+ +; +; DELAY ABOUT 0.5 SECONDS +; 500000US / 16US = 31250 +; +LDELAY: + PUSH AF + PUSH DE + LD DE,31250 + CALL VDELAY + POP DE + POP AF + RET + +; +; HANDLE USER INPUT FOR A MENU BASED ON TABLE OF MENU DATA. DISPATCH MENU FUNCTIONS. +; ON INPUT, HL=ADDRESS OF MENU TABLE, B=COUNT OF MENU ENTRIES +; + .module MenuInfo +MenuInfo ; TRANSIENT STORAGE FOR CURRENT MENU +_DrawProc .dw 0 ; ADDRESS OF MENU DRAW ROUTINE +_TableAdr .dw 0 ; ADDRESS OF MENU TABLE DATA +_EntryInfo .dw 0 ; ENTRY COUNT / ENTRY SIZE +_Size .equ $ - MenuInfo +; +RUNMENU: + push hl ; save address of menu info data + +_Run: + pop hl ; restore/resave menu init address + push hl + + ld de,MenuInfo + ld bc,_Size + ldir + + ld hl,(_DrawProc) + call JPHL ; call menu draw routine + +_GetKey: + call GETKEYUC ; GET USER KEYPRESS + ld hl,(_TableAdr) + ld bc,(_EntryInfo) ; B=COUNT, C=ENTRY SIZE + ld d,0 ; put entry size in de + ld e,c ; " + + ; a=key pressed, hl=menu table address, b=entry count, de=entry size +_Loop: + ld c,(hl) + cp c + jp z,_Match ; found code + + add hl,de ; point to next entry + djnz _Loop ; check next entry till count is zero + jp _GetKey ; keep trying + +_Match: + inc hl ; load string + ld e,(hl) + inc hl + ld d,(hl) + call WRITESTR ; display it + + inc hl ; load code address + ld e,(hl) + inc hl + ld d,(hl) + + ld a,d ; check for zero + or e + jp z,_Exit ; zero means exit + + ex de,hl + call JPHL ; indirect call to menu function + jp _Run + +_Exit: + pop hl + ret +; +; CONTROL CHARACTERS +; +CHR_CR .EQU 0DH +CHR_LF .EQU 0AH +CHR_BS .EQU 08H +CHR_ESC .EQU 1BH +; +STR_DRIVERESET .TEXT "RESET DRIVE...$" +STR_EXECUTE .TEXT "EXECUTION$" +STR_OP .TEXT "OPERATION$" +STR_FORMAT .TEXT "FORMAT$" +STR_SENDCMD .TEXT "SEND COMMAND$" +STR_GETRESULTS .TEXT "GET RESULTS$" +STR_SEEKWAIT .TEXT "SEEK WAIT$" +STR_DOR .TEXT "SET DOR$" +STR_PROGRESS .TEXT "PROGRESS$" +STR_MISMATCH .TEXT "DATA MISMATCH AT $" +STR_RESET .TEXT "FDCRESET...$" +STR_NOTIMPL .TEXT "*** NOT IMPLEMENTED ***$" +STR_NORESP .TEXT "*** DRIVE NOT RESPONDING ***$" +STR_EOD .TEXT "$" +STR_EMPTY .TEXT "$" +STR_TIMEOUT .TEXT "$" +STR_ARROW .TEXT " -->$" +STR_ENTER .TEXT "ENTER$" +STR_ON .TEXT "ON $" +STR_OFF .TEXT "OFF$" +STR_DRV720 .TEXT "720KB $" +STR_DRV144 .TEXT "1.44MB$" +STR_MODEPOLL .TEXT "POLLING $" +STR_MODEINT .TEXT "INTERRUPT $" +STR_MODEDMA .TEXT "DMA $" +STR_CC0 .TEXT " CC0=$" +STR_CC1 .TEXT " CC1=$" +STR_CC2 .TEXT " CC2=$" +STR_N .TEXT " N=$" +STR_SC .TEXT " SC=$" +STR_GPL .TEXT " GPL=$" +STR_D .TEXT " D=$" +STR_ST0 .TEXT " ST0=$" +STR_ST1 .TEXT " ST1=$" +STR_ST2 .TEXT " ST2=$" +STR_CYL .TEXT " CYL=$" +STR_UNIT .TEXT " UNIT=$" +STR_HEAD .TEXT " HD=$" +STR_REC .TEXT " SEC=$" +STR_NUM .TEXT " NUM=$" +STR_DENS .TEXT " DENS=$" +STR_EOTSEC .TEXT " EOTSEC=$" +STR_GAP .TEXT " GAP=$" +STR_DTL .TEXT " DTL=$" +STR_SN .TEXT " SN=$" +STR_NCN .TEXT " NCN=$" +STR_PCN .TEXT " PCN=$" +STR_MSR .TEXT " MSR=$" +STR_BC .TEXT " BC=$" +STR_DE .TEXT " DE=$" +STR_HL .TEXT " HL=$" +STR_DSKCHG .TEXT " DC=$" +; +KEYBUFLEN .EQU 80 +KEYBUF .FILL KEYBUFLEN,' ' +; +STACKSAV .DW 0 +STACKSIZ .EQU 40H ; WE ARE A STACK PIG + .FILL STACKSIZ,0 +STACK .EQU $ +; +BUFFER .EQU 4000H +VFYBUF .EQU 5000H +BUFSIZ .EQU 0200H + .END diff --git a/Source/Apps/FDU/FDU.txt b/Source/Apps/FDU/FDU.txt new file mode 100644 index 00000000..6e13550c --- /dev/null +++ b/Source/Apps/FDU/FDU.txt @@ -0,0 +1,473 @@ +================================================================ +Floppy Disk Utility (FDU) v5.0 for RetroBrew Computers +Disk IO / Zeta / Dual-IDE / N8 +================================================================ + +Updated September 2, 2017 +by Wayne Warthen (wwarthen@gmail.com) + +Application to test the hardware functionality of the Floppy +Disk Controller (FDC) on the ECB DISK I/O, DISK I/O V3, ZETA +SBC, Dual IDE w/ Floppy, or N8 board. + +The intent is to provide a testbed that allows direct testing +of all possible media types and modes of access. The +application supports read, write, and format by sector, track, +and disk as well as a random read/write test. + +The application supports access modes of polling, interrupt, +INT/WAIT, and DRQ/WAIT. At present, it supports 3.5" media at +DD (720KB) and HD (1.44MB) capacities. It also now supports +5.25" media (720KB and 1.2MB) and 8" media (1.11MB) as well. +Additional media will be added when I have time and access to +required hardware. Not all modes are supported on all +platforms and some modes are experimental in all cases. + +In many ways this application is merely reinventing the wheel +and performs functionality similar to existing applications, +but I have not seen any other applications for RetroBrew +Computers hardware that provide this range of functionality. + +While the application is now almost entirely new code, I would +like to acknowledge that much was derived from the previous +work of Andrew Lynch and Dan Werner. I also want to credit +Sergio Gimenez with testing the 5.25" drive support and Jim +Harre with testing the 8" drive support. Support for Zeta 2 +comes from Segey Kiselev. Thanks! + +General Usage +------------- + +In general, usage is self explanatory. At invocation, you +must select the floppy disk controller (FDC) that you are +using. Subsequently, the main menu allows you to set the +unit, media, and mode to test. These settings MUST match your +situation. Read, write, format, and verify functions are +provided. A sub-menu will allow you to choose sector, track, +disk, or random tests. + +The verify function requires a little explanation. It will +take the contents of the current in-memory disk buffer, save +it, and compare it to the selected sectors. So, you must +ensure that the sectors to be verified already have been +written with the same pattern as the buffer contains. I +typically init the buffer to a pattern, write the pattern to +the entire disk, then verify the entire disk. + +Another submenu is provided for FDC commands. This sub-menu +allows you to send low-level commands directly to FDC. You +*must* know what you are doing to use this sub-menu. For +example, in order to read a sector using this sub-menu, you +will need to perform specify, seek, sense int, and read +commands specifying correct values (nothing is value checked +in this menu). + +Required Hardware/BIOS +---------------------- + +Of course, the starting point is to have a supported hardware +configuration. The following Z80 / Z180 based CPU boards are +supported: + + - SBC V1/2 + - Zeta + - Zeta 2 + - N8 + - Mark IV + +You must be using either a RomWBW or UBA based OS version. + +You must have one of the following floppy disk controllers: + + - Disk IO ECB Board FDC + - Disk IO 3 ECB Board FDC + - Dual-IDE ECB Board FDC + - Zeta SBC onboard FDC + - Zeta 2 SBC onboard FDC + - N8 SBC onboard FDC + +Finally, you will need a floppy drive connected via an +appropriate cable: + +Disk IO - no twist in cable, drive unit 0/1 must be selected by jumper on drive +DISK IO 3, Zeta, Zeta 2 - cable with twist, unit 0 after twist, unit 1 before twist +DIDE, N8 - cable with twist, unit 0 before twist, unit 1 after twist + +Note that FDU does not utilize your systems ROM or OS to +access the floppy system. FDU interacts directly with +hardware. Upon exit, you may need to reset your OS to get the +floppy system back into a state that is expected. + +The Disk I/O should be jumpered as follows: + +J1: depends on use of interrupt modes (see interrupt modes below) +J2: pins 1-2, & 3-4 jumpered +J3: hardware dependent timing for DMA mode (see DMA modes below) +J4: pins 2-3 jumpered +J5: off +J6: pins 2-3 jumpered +J7: pins 2-3 jumpered +J8: off +J9: off +J10: off +J11: off +J12: off + +Note that J1 can be left on even when not using interrupt +modes. As long as the BIOS is OK with it, that is fine. Note +also that J3 is only relevant for DMA modes, but also can be +left in place when using other modes. + +The Disk I/O 3 board should be jumpered at the default settings: + +JP2: 3-4 +JP3: 1-2 for int mode support, otherwise no jumper +JP4: 1-2, 3-4 +JP5: 1-2 +JP6: 1-2 +JP7: 1-2, 3-4 + +Zeta & Zeta 2 do not have any relevant jumper settings. The +hardwired I/O ranges are assumed in the code. + +The Dual-IDE board should be jumpered as follows: + +K3 (DT/R or /RD): /RD +P5 (bd ID): 1-2, 3-4 (for $20-$3F port range) + +There are no specific N8 jumper settings, but the default +I/O range starting at $80 is assumed in the published code. + + +Modes of Operation +------------------ + +You can select the following test modes. Please refer to the +chart that follows to determine which modes should work with +combinations of Z80 CPU speed and media format. + +WARNING: In general, only the polling mode is considered fully +reliable. The other modes are basically experimental and +should only be used if you know exactly what you are doing. + +Polling: Traditional polled input/output. Works well and very +reliable with robust timeouts and good error recovery. Also, +the slowest performance which precludes it from being used +with 1.44MB floppy on a 4MHz Z80. This is definitely the mode +you want to get working before any others. It does not require +J1 (interrupt enable) on DISK I/O and does not care about the +setting of J3. + +Interrupt: Relies on FDC interrupts to determine when a byte +is ready to be read/written. It does *not* implement a +timeout during disk operations. For example, if there is no +disk in the drive, this mode will just hang until a disk is +inserted. This mode *requires* that the host has interrupts +active using interrupt mode 1 (IM1) and interrupts attached to +the FDC controller. The BIOS must be configured to handle +these interrupts safely. + +Fast Interrupt: Same as above, but sacrifices additional +reliability for faster operation. This mode will allow a +1.44MB floppy to work with a 4MHz Z80 CPU. However, if any +errors occur (even a transient read error which is not +unusual), this mode will hang. The same FDC interrupt +requirements as above are required. + +INT/WAIT: Same as Fast Interrupt, but uses CPU wait instead of +actual interrupt. This mode is exclusive to the original Disk +IO board. It is subject to all the same issues as Fast +Interrupt, but does not need J1 shorted. J3 is irrelevant. + +DRQ/WAIT: Uses pseudo DMA to handle input/output. Does not +require that interrupts (J1) be enabled on the DISK I/O. +However, it is subject to all of the same reliability issues +as "Fast Interrupt". This mode is exclusive to the original +Disk IO board. At present, the mode is *not* implemented! + +The chart below attempts to describe the combinations that +work for me. By far, the most reliable mode is Polling, but +it requires 8MHz CPU for HD disks. + +DRQ/WAIT --------------------------------+ +INT/WAIT -----------------------------+ | +Fast Interrupt --------------------+ | | +Interrupt ----------------------+ | | | +Polling ---------------------+ | | | | + | | | | | +CPU Speed --------------+ | | | | | + | | | | | | + | | | | | | + +3.5" DD (720K) ------ 4MHz Y Y Y Y X + 8MHz+ Y Y Y Y X + +3.5" HD (1.44M) ----- 4MHz N N Y Y X + 8MHz+ Y Y Y Y X + +5.25" DD (360K) ----- 4MHz Y Y Y Y X + 8MHz+ Y Y Y Y X + +5.25" HD (1.2M) ----- 4MHz N N Y Y X + 8MHz+ Y Y Y Y X + +8" DD (1.11M) ------- 4MHz N N Y Y X + 8MHz+ Y Y Y Y X + +Y = Yes, works +N = No, does not work +X = Experimental, probably won't work + +Tracing +------- + +Command/result activity to/from the FDC will be written out if +the trace setting is changed from '00' to '01' in setup. +Additionally, if a command failure is detected on any command, +that specific comand and results are written regardless of the +trace setting. + +The format of the line written is: +: --> [] + +For example, this is the output of a normal read operation: +READ: 46 01 00 00 01 02 09 1B FF --> 01 00 00 00 00 02 02 [OK] + +Please refer to the i8272 data sheet for information on the +command and result bytes. + +Note that the sense interrupt command can return a non-OK +result. This is completely normal in some cases. It is +necessary to "poll" the drive for seek status using sense +interrupt. If there is nothing to report, then the result +will be INVALID COMMAND. Additionally, during a recalibrate +operation, it may be necessary to issue the command twice +because the command will only step the drive 77 times looking +for track 0, but the head may be up to 80 tracks away. In +this case, the first recalibrate fails, but the second should +succeed. Here is what this would look like if trace is turned +on: + +RECALIBRATE: 07 01 --> [OK] +SENSE INTERRUPT: 08 --> 80 [INVALID COMMAND] + ... + ... + ... +SENSE INTERRUPT: 08 --> 80 [INVALID COMMAND] +SENSE INTERRUPT: 08 --> 71 00 [ABNORMAL TERMINATION] +RECALIBRATE: 07 01 --> [OK] +SENSE INTERRUPT: 08 --> 21 00 [OK] + +Another example is when the FDC has just been reset. In this +case, you will see up to 4 disk change errors. Again these +are not a real problem and to be expected. + +When tracing is turned off, the application tries to be +intelligent about error reporting. The specific errors from +sense interrupt documented above will be suppressed because +they are not a real problem. All other errors will be +displayed. + +Error Handling +-------------- + +There is no automated error retry logic. This is very +intentional since the point is to expose the controller and +drive activity. Any error detected will result in a prompt to +abort, retry, or continue. Note that some number of errors is +considered normal for this technology. An occasional error +would not necessarily be considered a problem. + +CPU Speed +--------- + +Starting with v5.0, the application adjusts it's timing loops +to the actual system CPU speed by querying the BIOS for the +current CPU speed. + +Interleave +---------- + +The format command now allows the specification of a sector +interleave. It is almost always the case that the optimal +interleave will be 2 (meaning 2:1). + +360K Media +---------- + +The 360K media definition should work well for true 360K +drives. However, it will generally not work with 1.2M +drives. This is because these drives spin at 360RPM instead +of the 300RPM speed of true 360K drives. Additionally, 1.2M +drives are 80 tracks and 360K drives are 40 tracks and, so +far, there is no mechanism in FD to "double step" as a way to +use 40 track media in 80 track drives. + +With this said, it is possible to configure some 1.2M 5.25" +drives to automatically spin down to 300RPM based on a density +select signal (DENSEL). This signal is asserted by FD for +360K media, so IF you have configured your drive to react to +this signal correctly, you will be able to use the 360K media +defintion. Most 1.2M 5.25" drives are NOT configured this way +by default. TEAC drives are generally easy to modify and have +been tested by the author and do work in this manner. Note +that this does not address the issue of double stepping above; +you will just be using the first 40 of 80 tracks. + +Support +------- + +I am happy to answer questions as fast and well as I am able. +Best contact is wwarthen@gmail.com or post something on the +RetroBrew Computers Forum +https://www.retrobrewcomputers.org/forum/. + +Changes +------- + +WW 8/12/2011 + +Removed call to pulse TC in the FDC initialization after +determining that it periodically caused the FDC to write bad +sectors. I am mystified by this, but definitely found it to +be true. Will revisit at some point -- probably a timing +issue between puslsing TC and whatever happens next. + +Non-DMA mode was being set incorrectly for FAST-DMA mode. It +was set for non-DMA even though we were doing DMA. It is +interesting that it worked fine anyway. Fixed it anyway. + +DIO_SETMEDIA was not clearing DCD_DSKRDY as it should. Fixed. + +WW 8/26/2011: v1.1 + +Added support for Zeta. Note that INT/WAIT and DRQ/WAIT are +not available on Zeta. Note that Zeta provides the ability to +perform a reset of the FDC independent of a full CPU reset. +This is VERY useful and the FDC is reset anytime a drive reset +is required. + +Added INT/WAIT support. + +WW 8/28/2011: V1.2 + +All changes in this version are Zeta specific. Fixed FDC +reset logic and motor status display for Zeta (code from +Sergey). + +Modified Zeta disk change display to include it in the command +output line. This makes more sense because a command must be +issued to select the desired drive first. You can use the +SENSE INT command id you want to check the disk change value +at any time. It will also be displayed with any other command +output display. + +WW 9/1/2011: V1.3 + +Added CPUFREQ configuration setting to tune delays based on +cpu speed. The build app is set for 8MHz which also seems to +work well for 4MHz CPU's. Faster CPU speeds will probably +require tuning this setting. + +WW 9/5/2011: V1.4 + +Changed the polling execution routines to utilize CPUFREQ +variable to optimize timeout counter. Most importantly, this +should allow the use of faster CPUs (like 20MHz). + +WW 9/19/2011: V1.5 + +Zeta changes only. Added a call to FDC RESET after any +command failure. This solves an issue where the drive remains +selected if a command error occurs. Also added FDC RESET to +FDC CONTROL menu. + +WW 10/7/2011: V2.0 + +Added support for DIDE. Only supports polling IO and it does +not appear any other modes are possible given the hardware +constraints. + +WW 10/13/2011: V2.1 + +Modified to support N8. N8 is essentially identical to Dual +IDE. The only real change is the IO addresses. In theory, I +should be able to support true DMA on N8 and will work on that. + +WW 10/20/2011: v2.2 + +I had some problems with the results being read were sometimes +missing a byte. Fixed this by taking a more strict approach +to watching the MSR for the exact bits that are expected. + +WW 10/22/2011: V2.3 + +After spending a few days trying to track down an intermittent +data corruption issue with my Dual IDE board, I added a verify +function. This helped me isolate the problem very nicely +(turned out to be interference from the bus monitor). + +WW 11/25/2011: V2.4 + +Preliminary support for DISKIO V3. Basically just assumed +that it operates just like the Zeta. Needs to be verified +with real hardware as soon as I can. + +WW 1/9/2012: V2.5 + +Modified program termination to use CP/M reset call so that a +warm start is done and all drives are logged out. This is +important because media may have been formatted during the +program execution. + +WW 2/6/2012: v2.6 + +Added support for 5.25" drives as tested by Sergio. + +WW 4/5/2012: v2.7 + +Added support for 8" drives as tested by Jim Harre. + +WW 4/6/2012: v2.7a + +Fixed issue with media selection menu to remove duplicate +entries. + +WW 4/8/2012: v2.7b + +Corrected the handling of the density select signal. + +WW 5/22/2012: v2.8 + +Added new media definitions (5.25", 320K). + +WW 6/1/2012: v2.9 + +Added interleave capability on format. + +WW 6/5/2012: v3.0 + +Documentation cleanup. + +WW 7/1/2012: v3.1 + +Modified head load time (HLT) for 8" media based on YD-180 +spec. Now set to 50ms. + +WW 6/17/2013: v3.2 + +Cleaned up SRT, HLT, and HUT values. + +SK 2/10/2015: v3.3 + +Added Zeta SBC v2 support (Sergey Kiselev) + +WW 3/25/2015: v4.0 + +Renamed from FDTST --> FD + +WW 9/2/2017: v5.0 + +Renamed from FD to FDU. +Added runtime selection of FDC hardware. +Added runtime timing adjustment. \ No newline at end of file diff --git a/Source/Apps/Format.asm b/Source/Apps/Format.asm index 96f9bdb5..f7b78e2b 100644 --- a/Source/Apps/Format.asm +++ b/Source/Apps/Format.asm @@ -201,12 +201,12 @@ stksav .dw 0 ; stack pointer saved at start .fill stksiz,0 ; stack stack .equ $ ; stack top ; -msgban1 .db "FORMAT v0.1 for RomWBW CP/M 2.2, 24-Apr-2016",0 +msgban1 .db "FORMAT v0.1a for RomWBW CP/M 2.2, 02-Sep-2017",0 msghb .db " (HBIOS Mode)",0 msgub .db " (UBIOS Mode)",0 -msgban2 .db "Copyright 2016, Wayne Warthen, GNU GPL v3",0 +msgban2 .db "Copyright (C) 2017, Wayne Warthen, GNU GPL v3",0 msguse .db "FORMAT command is not yet implemented!",13,10,13,10 - .db "Use FD command to physically format floppy diskettes",13,10 + .db "Use FDU command to physically format floppy diskettes",13,10 .db "Use CLRDIR command to (re)initialize directories",13,10 .db "Use SYSCOPY command to make disks bootable",13,10 .db "Use FDISK80 command to partition mass storage media",0 diff --git a/Source/Apps/XM125/Build.cmd b/Source/Apps/XM/Build.cmd similarity index 100% rename from Source/Apps/XM125/Build.cmd rename to Source/Apps/XM/Build.cmd diff --git a/Source/Apps/XM125/Clean.cmd b/Source/Apps/XM/Clean.cmd similarity index 100% rename from Source/Apps/XM125/Clean.cmd rename to Source/Apps/XM/Clean.cmd diff --git a/Source/Apps/XM125/xmdm125.asm b/Source/Apps/XM/xmdm125.asm similarity index 100% rename from Source/Apps/XM125/xmdm125.asm rename to Source/Apps/XM/xmdm125.asm diff --git a/Source/Apps/XM125/xmdm125.zip b/Source/Apps/XM/xmdm125.zip similarity index 100% rename from Source/Apps/XM125/xmdm125.zip rename to Source/Apps/XM/xmdm125.zip diff --git a/Source/Apps/XM125/xmhb.180 b/Source/Apps/XM/xmhb.180 similarity index 100% rename from Source/Apps/XM125/xmhb.180 rename to Source/Apps/XM/xmhb.180 diff --git a/Source/CBIOS/ver.inc b/Source/CBIOS/ver.inc index 86d7795c..909ff456 100644 --- a/Source/CBIOS/ver.inc +++ b/Source/CBIOS/ver.inc @@ -2,4 +2,4 @@ #DEFINE RMN 8 #DEFINE RUP 3 #DEFINE RTP 0 -#DEFINE BIOSVER "2.8.3" +#DEFINE BIOSVER "2.8.4" diff --git a/Source/HBIOS/util.asm b/Source/HBIOS/util.asm index 56e7ac4a..ee98fc0d 100644 --- a/Source/HBIOS/util.asm +++ b/Source/HBIOS/util.asm @@ -455,7 +455,6 @@ JPHL: JP (HL) ; ; A REGISTER IS DESTROYED! ; - ADDHLA: ADD A,L LD L,A @@ -520,7 +519,7 @@ BYTE2BCD1: ; IMPACT OF OVERHEAD DIMINISHES AS CPU SPEED INCREASES ; ; CPU SCALER (CPUSCL) = (CPUHMZ - 2) FOR 16US + 3TS DELAY -; NOTE: CPUSCL MUST BE >= 3! +; NOTE: CPUSCL MUST BE >= 1! ; ; EXAMPLE: 8MHZ CPU (DELAY GOAL IS 16US) ; LOOP = ((6 * 16) - 5) = 91TS @@ -602,9 +601,14 @@ LDELAY: ; DELAY_INIT: #IF (PLATFORM == PLT_UNA) - LD C,$F9 ; UBIOS FUNC=GET CPU INFO - RST 08 ; E := CPU MHZ - LD E,A ; PUT SPEED IN MHZ IN ACCUM + LD C,$F8 ; UNA BIOS GET PHI FUNCTION + RST 08 ; RETURNS SPEED IN HZ IN DE:HL + LD A,E ; HACK TO GET APPROX SPEED IN MHZ + SRL A ; ... BY DIVIDING BY 1,048,576 + SRL A ; ... + SRL A ; ... + SRL A ; ... + INC A ; FIX UP FOR VALUE TRUNCATION #ELSE LD B,BF_SYSGET ; HBIOS FUNC=GET SYS INFO LD C,BF_SYSGET_CPUINFO ; HBIOS SUBFUNC=GET CPU INFO diff --git a/Source/HBIOS/ver.inc b/Source/HBIOS/ver.inc index 86d7795c..909ff456 100644 --- a/Source/HBIOS/ver.inc +++ b/Source/HBIOS/ver.inc @@ -2,4 +2,4 @@ #DEFINE RMN 8 #DEFINE RUP 3 #DEFINE RTP 0 -#DEFINE BIOSVER "2.8.3" +#DEFINE BIOSVER "2.8.4" diff --git a/Source/RomDsk/MK4/FD_DIDE.COM b/Source/RomDsk/MK4/FD_DIDE.COM deleted file mode 100644 index 409cf1f8caf10ed76a4e69543e335aefc9b99621..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7961 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 diff --git a/Source/RomDsk/MK4/FD_DIO3.COM b/Source/RomDsk/MK4/FD_DIO3.COM deleted file mode 100644 index dcea26ca44b975b9d92b0bc60620a845ea841419..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7974 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 diff --git a/Source/RomDsk/SBC/FD_DIO3.COM b/Source/RomDsk/SBC/FD_DIO3.COM deleted file mode 100644 index dcea26ca44b975b9d92b0bc60620a845ea841419..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7974 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%0APB_mj#f*fmFu@1RlldTI2D2a? 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