;-------------------------------------------------------------------------------------------------------- ;- ;- Name: cbiosII.asm ;- Created: January 2020 ;- Author(s): Philip Smart ;- Description: Sharp MZ series CPM BIOS System. ;- This assembly language program is written to utilise the paged RAM memory modes of ;- the tranZPUter hardware running on the Sharp MZ80A computer. The purpose is to ;- provide more TPA to CP/M by hiding the support logic in a RAM bank outside of the ;- 64K address space of CPM. The basics have to exist within the 64K for CP/M to ;- function correctly, ie. disk buffers, stack and jump tables but the remainder ;- are placed in a different bank which has 48K available RAM just for CBIOS. ;- ;- Credits: ;- Copyright: (c) 2018-20 Philip Smart ;- ;- History: Jan 2020 - Seperated Bank from RFS for dedicated use with CPM CBIOS. ; May 2020 - Advent of the new RFS PCB v2.0, quite a few changes to accommodate the ; additional and different hardware. The SPI is now onboard the PCB and ; not using the printer interface card. ; May 2020 - Cut taken from the MZ80A RFS version of CPM CBIOS to create a version of ; CPM suitable to run on the tranZPUter. The memory models are different ; providing more memory at different locations for use by CPM. ;-------------------------------------------------------------------------------------------------------- ;- This source file is free software: you can redistribute it and-or modify ;- it under the terms of the GNU General Public License as published ;- by the Free Software Foundation, either version 3 of the License, or ;- (at your option) any later version. ;- ;- This source file is distributed in the hope that it will be useful, ;- but WITHOUT ANY WARRANTY; without even the implied warranty of ;- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;- GNU General Public License for more details. ;- ;- You should have received a copy of the GNU General Public License ;- along with this program. If not, see . ;-------------------------------------------------------------------------------------------------------- ;====================================== ; ; RAM BANK - CPM CBIOS II ; ;====================================== ORG 00000H ; In order to preserve the interrupt vectors when CPM mode 7 is enabled (ie. this ; bank is paged in), the area from 0x0000:0x003F maps to the memory block in mode 6 ; so this is unuseable space in this bank. In addition, CPM uses 0x0000:0x0100 ; for many vectors and buffers so this memory is also mapped to the memory block in ; mode 6. ;------------ 0x0000 ----------------------------------------------------------- PAD 00040H ;------------ 0x0040 ----------------------------------------------------------- PAD 00100H ;------------ 0x0100 ----------------------------------------------------------- LVARSTART EQU $ ; Start of local page variables. SPV: IBUFE: ; TAPE BUFFER (128 BYTES) ATRB: DS 1 ; ATTRIBUTE NAME: DS 17 ; FILE NAME SIZE: DS 2 ; BYTESIZE DTADR: DS 2 ; DATA ADDRESS EXADR: DS 2 ; EXECUTION ADDRESS COMNT: DS 92 ; Comment / code area of CMT header. SWPW: DS 10 ; SWEEP WORK KDATW: DS 2 ; KEY WORK KANAF: DS 1 ; KANA FLAG (01=GRAPHIC MODE) DSPXY: DS 2 ; DISPLAY COORDINATES MANG: DS 6 ; COLUMN MANAGEMENT MANGE: DS 1 ; COLUMN MANAGEMENT END PBIAS: DS 1 ; PAGE BIAS ROLTOP: DS 1 ; ROLL TOP BIAS MGPNT: DS 1 ; COLUMN MANAG. POINTER PAGETP: DS 2 ; PAGE TOP ROLEND: DS 1 ; ROLL END DS 14 ; BIAS FLASH: DS 1 ; FLASHING DATA SFTLK: DS 1 ; SHIFT LOCK REVFLG: DS 1 ; REVERSE FLAG FLSDT: DS 1 ; CURSOR DATA STRGF: DS 1 ; STRING FLAG DPRNT: DS 1 ; TAB COUNTER SWRK: DS 1 ; KEY SOUND FLAG TEMPW: DS 1 ; TEMPO WORK ONTYO: DS 1 ; ONTYO WORK OCTV: DS 1 ; OCTAVE WORK RATIO: DS 2 ; ONPU RATIO DSPXYADDR: DS 2 ; Address of last known position. DRVAVAIL DS 1 ; Flag to indicate which drive controllers are available. Bit 2 = SD, Bit 1 = ROM, Bit 0 = FDC MOTON DS 1 ; MOTOR ON = 1, OFF = 0 TMPADR DS 2 ; TEMPORARY ADDRESS STORAGE TMPSIZE DS 2 ; TEMPORARY SIZE TMPCNT DS 2 ; TEMPORARY COUNTER ; FLASHCTL: DS 1 ; CURSOR FLASH CONTROL. BIT 0 = Cursor On/Off, BIT 1 = Cursor displayed. TIMESEC: DS 6 ; RTC 48bit TIME IN MILLISECONDS NDISKS: DS 1 ; Dynamically calculated number of disks on boot. DISKTYPE: DS 1 ; Disk type of current selection. MTROFFTIMER:DS 1 ; Second down counter for FDC motor off. ; SEKDSK: DS 1 ; Seek disk number SEKTRK: DS 2 ; Seek disk track SEKSEC: DS 1 ; Seek sector number SEKHST: DS 1 ; Seek sector host ; HSTACT: DS 1 ; ; UNACNT: DS 1 ; Unalloc rec cnt UNADSK: DS 1 ; Last unalloc disk UNATRK: DS 2 ; Last unalloc track UNASEC: DS 1 ; Last unalloc sector ; READOP: DS 1 ; If read operation then 1, else 0 for write. RSFLAG: DS 1 ; Read sector flag. WRTYPE: DS 1 ; Write operation type. DMAADDR: DS 2 ; Last DMA address FDCCMD DS 1 ; LAST FDC COMMAND SENT TO CONTROLLER. INVFDCDATA: DS 1 ; INVERT DATA COMING FROM FDC, 1 = INVERT, 0 = AS IS TRK0FD1 DS 1 ; FD 1 IS AT TRACK 0 = BIT 0 set TRK0FD2 DS 1 ; FD 2 IS AT TRACK 0 = BIT 0 set TRK0FD3 DS 1 ; FD 3 IS AT TRACK 0 = BIT 0 set TRK0FD4 DS 1 ; FD 4 IS AT TRACK 0 = BIT 0 set RETRIES DS 2 ; DATA READ RETRIES DISKMAP: DS MAXDISKS ; Disk map of CPM logical to physical controller disk. FDCDISK: DS 1 ; Physical disk number. SECPERTRK: DS 1 ; Sectors per track for 1 head. SECPERHEAD: DS 1 ; Sectors per head. SECTORCNT: DS 1 ; Sector size as a count of how many sectors make 512 bytes. HSTDSK: DS 1 ; Host disk number HSTTRK: DS 2 ; Host track number HSTSEC: DS 1 ; Host sector number HSTWRT: DS 1 ; Host write flag ERFLAG: DS 1 ; Error number, 0 = no error. TRACKNO: DS 2 ; Host controller track number SECTORNO: DS 1 ; Host controller sector number CURSORPSAV DS 2 ; Cursor save position;default 0,0 HAVELOADED DS 1 ; To show that a value has been put in for Ansi emualtor. ANSIFIRST DS 1 ; Holds first character of Ansi sequence NUMBERBUF DS 20 ; Buffer for numbers in Ansi NUMBERPOS DS 2 ; Address within buffer CHARACTERNO DS 1 ; Byte within Ansi sequence. 0=first,255=other CURSORCOUNT DS 1 ; 1/50ths of a second since last change FONTSET DS 1 ; Ansi font setup. JSW_FF DS 1 ; Byte value to turn on/off FF routine JSW_LF DS 1 ; Byte value to turn on/off LF routine CHARACTER DS 1 ; To buffer character to be printed. CURSORPOS DS 2 ; Cursor position, default 0,0. BOLDMODE DS 1 HIBRITEMODE DS 1 ; 0 means on, &C9 means off UNDERSCMODE DS 1 ITALICMODE DS 1 INVMODE DS 1 CHGCURSMODE DS 1 ANSIMODE DS 1 ; 1 = on, 0 = off COLOUR EQU 0 LVAREND EQU $ ; End of local page variables IF $ > 0300H ERROR "Keybuf var not aligned, addr=%s, required=%s"; % $, 0300H ENDIF ALIGN_NOPS 0300H KEYBUF: DS KEYBUFSIZE, 0DH ; Interrupt driven keyboard buffer. KEYCOUNT: DS 1, 000H KEYWRITE: DS 2, 000H ; Pointer into the buffer where the next character should be placed. KEYREAD: DS 2, 000H ; Pointer into the buffer where the next character can be read. KEYLAST: DS 1, 000H ; KEY LAST VALUE KEYRPT: DS 1, 000H ; KEY REPEAT COUNTER DS 64, 0FFH ; Stack space for cold and warm boot. BOOTSTACK EQU $ ; ; Start of CPM entry points. ; IF $ > 0400H ERROR "Stack var not aligned, addr=%s, required=%s"; % $, 0400H ENDIF ALIGN_NOPS 0400H ;------------------------------------------------------------------------------- ; BOOT ; ; The BOOT entry point gets control from the cold start loader and is ; responsible for basic system initialization, including sending a sign-on ; message, which can be omitted in the first version. ; If the IOBYTE function is implemented, it must be set at this point. ; The various system parameters that are set by the WBOOT entry point must be ; initialized, and control is transferred to the CCP at 3400 + b for further ; processing. Note that register C must be set to zero to select drive A. ; ; NB. This code is executed by the MZF loader. The following code is assembled ; in the header at $1108 to ensure correct startup. ; BOOTSTG1: LD A,TZMM_CPM2 ; Switch to CPM memory mode 2 ; OUT (MMCFG), A ; JP QBOOT_ ; Execute cold boot. ; REBOOT: LD A,TZMM_TZFS ; Switch to TZFS memory mode. ; OUT (MMCFG),A ; JP MROMADDR ; Now restart in the SA1510 monitor. ;------------------------------------------------------------------------------- QBOOT_: DI ; Disable Interrupts and sat mode. NB. Interrupts are physically disabled by 8255 Port C2 set to low. IM 1 ; LD SP,BOOTSTACK ; Setup to use local stack until CPM takes over. ; LD HL,0066H ; Set NMI so it doesnt bother us. LD (HL), 0EDH ; Set to RETN instruction. INC HL LD (HL), 045H ; LD HL,GVARSTART ; Start of global variable area LD BC,GVAREND-GVARSTART ; Size of global variable area. XOR A LD D,A INIT1: LD (HL),D ; Clear variable memory including stack space. INC HL DEC BC LD A,B OR C JR NZ,INIT1 ; LD HL,LVARSTART ; Start of local page variable area LD BC,LVAREND-LVARSTART ; Size of local page variable area. XOR A LD D,A INIT2: LD (HL),D ; Clear variable memory. INC HL DEC BC LD A,B OR C JR NZ,INIT2 ; CALL MODE ; Configure 8255 port C, set Motor Off, VGATE to 1 (off) and INTMSK to 0 (interrupts disabled). LD A,004H LD (TEMPW),A ; Setup the tempo for sound output. INIT3: ; Setup keyboard buffer control. LD A,0 LD (KEYCOUNT),A ; Set keyboard buffer to empty. LD HL,KEYBUF LD (KEYWRITE),HL ; Set write pointer to beginning of keyboard buffer. LD (KEYREAD),HL ; Set read pointer to beginning of keyboard buffer. ; Setup keyboard rate control and set to CAPSLOCK mode. ; (0 = Off, 1 = CAPSLOCK, 2 = SHIFTLOCK). LD A,000H ; Initialise key repeater. LD (KEYRPT),A LD A,001H LD (SFTLK),A ; Setup shift lock, default = off. ; Setup the initial cursor, for CAPSLOCK this is a double underscore. LD A,03EH LD (FLSDT),A LD A,080H ; Cursor on (Bit D7=1). LD (FLASHCTL),A ; Initialise the display, either the Video Module, 40/80 Colour Card or standard 40 column display. IF BUILD_VIDEOMODULE = 1 IN A, (CPLDINFO) ; Get hardware information. BIT 3,A JR Z, INIT80CHAR ; If no video module present then see if the 40/80 column board installed else need to use 40 char mode. AND 007H LD D, A OR MODE_VIDEO_FPGA ; Ensure the video hardware is enabled. OUT (CPLDCFG),A LD A, D OR MODE_80CHAR ; Enable 80 char display. OUT (VMCTRL),A ; Activate. LD A, D CP MODE_MZ80A ; Check to see if this is the MZ80A, if so, change BUS speed. JR NZ, INIT80END ; LD A, SYSMODE_MZ80B ; Set bus and default CPU speed to 4MHz ; OUT (SYSCTRL),A ; Activate. JR INIT80END ENDIF INIT80CHAR: IF BUILD_80C = 1 LD HL,DSPCTL ; Setup address of display control register latch. LD A, 128 ; 80 char mode. LD E,(HL) ; Dummy operation to enable latch write via multivibrator. LD (HL), A ENDIF ; ; If no video module and no 40/80 Colour Card then the default will be 40 column display. ; INIT80END: LD A,016H CALL PRNT LD A,071H ; Blue background, white characters in colour mode. Bit 7 is set as a write to bit 7 @ DFFFH selects 80Char mode. LD HL,ARAM CALL CLR8 CALL MLDSP CALL NL LD DE,CBIOSSIGNON ; Start of sign on message, as devices are detected they are added to the sign on. CALL MONPRTSTR CALL BEL ; Beep to indicate startup - for cases where screen is slow to startup. LD A,0FFH LD (SWRK),A LD HL,NUMBERBUF LD (NUMBERPOS),HL ; XOR A LD (IOBYT),A LD (CDISK),A ; DRVAVAIL flag definition. Version 1.25 - removed ROM Drives and RAM drives as they provided no speed or use benefit compared with SD drives. ; ; 1 = Active. ; ; 7 6 5 4 3 2 1 0 ; ^ ^ ^ ^ ^ ^ ^ ^ ; | | | | | | | |-- Floppy Drive Present ; | | | | | | |---- ROM Drive Present ; | | | | | |------ SD Card Present ; | | | | |-------- RAM Drive Present ; | | | |---------- ; | | |------------ ; | |-------------- ; |---------------- Drives present ; ; Initialise the disk subsystem. ; LD A,0 ; No drives yet detected so zero available mask. SET 2,A ; The SD Card is always present on the I/O processor, we cant run without it.. ; LD DE,SDAVAIL CALL PRTSTRTMSG ; CALL DSKINIT ; Initialise the floppy disk subsystem. JR NZ,STRT5 LD A,(DRVAVAIL) SET 0,A ; Indicate Floppy drives are available. LD DE,FDCAVAIL ; Output indicator that FDC drives are available. CALL PRTSTRTMSG ; STRT5: LD DE,CBIOSIGNEND ; Terminate the signon message which now includes list of drives detected. CALL MONPRTSTR CALL NL ; ; Setup the disk parameter blocks according ; to connected drives and available memory. ; LD DE,DPBASE ; Base of parameter block. LD A,0 ; Using scratch area, setup the disk count, pointer to ALV memory and pointer to CSV memory. LD (CDIRBUF),A LD HL,CSVALVMEM LD (CDIRBUF+1),HL ; Add as many SD Drives as RAM permits. STRT6: CALL ADDSDDRIVE ; Add a drive, NZ return means error, memory limit or max disks reached. JR NZ,STRT8 JR STRT6 ; Setup the 1.44MB Floppy Disk Drives. ; STRT8: CALL ADDFLPYDRV CALL ADDFLPYDRV ; No Floppy drives available then skip. ; LD A,(CDIRBUF) LD (NDISKS),A ; Setup max number of system disks found on this boot up. ; Setup timer interrupts LD IX,TIMIN ; Pass the interrupt service handler vector. LD BC,00000H ; Time starts at 00:00:00 01/01/1980 on initialisation. LD DE,00000H LD HL,00000H CALL TIMESET ; Signon message after all the hardware has been initialised. LD DE,CPMSIGNON CALL MONPRTSTR ; ; CP/M init CPMINIT: LD A,(DRVAVAIL) BIT 0,A JR Z,CPMINIT1 ; CALL DSKINIT ; Re-initialise the disk subsystem if available. XOR A ; 0 to accumulator LD (HSTACT),A ; Host buffer inactive LD (UNACNT),A ; Clear unalloc count ; CPMINIT1: LD A, 0C3H ; C3 IS A JMP INSTRUCTION LD (00000H), A ; FOR JMP TO WBOOT LD HL, WBOOTE ; WBOOT ENTRY POINT LD (00001H), HL ; SET ADDRESS FIELD FOR JMP AT 0 LD (00005H), A ; FOR JMP TO BDOS LD HL, CPMBDOS ; BDOS ENTRY POINT LD (00006H), HL ; ADDRESS FIELD OF JUMP AT 5 TO BDOS LD HL,TIMIN ; Re-install interrupt vector for RTC incase it was overwritten. LD (00038H),A LD (00039H),HL LD BC,CPMUSERDMA CALL QSETDMA_ ; ; check if current disk is valid LD A,(NDISKS) ; Get the dynamic disk count. LD L,A LD A, (CDISK) ; GET CURRENT USER/DISK NUMBER (UUUUDDDD) AND 00FH ; Isolate the disk number. CP L ; Drive number ok? JR C, CPMINIT2 ; Yes, jump (Carry set if A < NDISKS) LD A, (CDISK) ; No, set disk 0 (previous user) AND 0F0H LD (CDISK), A ; Save User/Disk CPMINIT2: CALL SETDRVMAP ; Refresh the map of physical floppies to CPM drive number. CALL SETDRVCFG ; LD A,(DISKTYPE) ; OR A ; CALL Z,SELDRIVE ; Select and start disk drive motor if floppy disk. ; LD A, (CDISK) LD C, A ; C = current User/Disk for CCP jump (UUUUDDDD) JP BOOT_ ; Cold boot CPM now that most initialisation is complete. This is a direct jump to the fixed bios area 0xF000 ; Method to add an SD drive into the CPM disk definitions. ; If the SD controller hasnt been detected the routine exits without configuration. ADDSDDRIVE: LD A,(DRVAVAIL) BIT 2,A JR Z,ADDSDDRVEX ; No SD interface so skip. ; ; 16MB SD Card Drives. ; LD BC,0 ; Setup CSV/ALV parameters for a 16MB SD Card drive. LD (CDIRBUF+3),BC LD BC,257 ; 2048/8 + 1 LD (CDIRBUF+5),BC LD BC,DPB4 LD (CDIRBUF+7),BC ; Address of Disk Parameters LD A,(CDIRBUF) CP MAXDISKS - 2 ; Make sure we have parameter table entries available to add further drives, ensuring slots for the FDC. JR Z,ADDSDDRVEX ; LD (TZSVC_FILE_NO),A ; Indicate the drive number the file will be attached to. LD A,TZSVC_CMD_ADDSDDRIVE ; I/O processor command to attach a CPM drive to the file number given. CALL SVC_CMD OR A JR NZ,ADDSDDRVEX ; No drive available, skip. ; ; Check that RAM is available to add this drive. ; LD BC,(CDIRBUF+1) LD HL,CSVALVEND - 2048/8 + 1 ; Subtract the size of the ALV (CSV has no size for a fixed SD drive) OR A SBC HL,BC JR C,ADDSDDRVEX ; If there is no more space, exit. CALL COPYDPB ; Add in an SD drive. XOR A RET ADDSDDRVEX: LD A,1 OR A RET ; Method to add a Floppy drive into the CPM disk definitions table. ; If the Floppy controller hasnt been detected then skip without configuration. ADDFLPYDRV: LD A,(DRVAVAIL) BIT 0,A JR Z,ADDSDDRVEX ; No Floppy drives available then skip. LD A,(CDIRBUF) CP MAXDISKS ; Use the disk count to ensure we only add upto 2 FDC drives. JR Z,ADDSDDRVEX LD BC,128/4 ; Setup CSV/ALV parameters for a 1.4MB Floppy drive. LD (CDIRBUF+3),BC LD BC,91 ; 720/8 + 1 LD (CDIRBUF+5),BC LD BC,DPB3 LD (CDIRBUF+7),BC ; Address of Disk Parameters LD BC,(CDIRBUF+1) ; Make sure there is memory available for the FDC drives. LD HL,CSVALVEND - 720/8 + 1 OR A SBC HL,BC JR C,ADDSDDRVEX ; If there is no more space, exit. CALL COPYDPB ; Add in a floppy drive. XOR A RET ; Helper method to print a message with a comma if Bit 7 of A is set. PRTSTRTMSG: PUSH AF BIT 7,A JR Z,PRTCOMMA1 LD A,',' CALL PRNT PRTCOMMA1: CALL MONPRTSTR POP AF SET 7,A LD (DRVAVAIL),A RET ;------------------------------------------------------------------------------- ; WBOOT ; ; The WBOOT entry point gets control when a warm start occurs. ; A warm start is performed whenever a user program branches to location ; 0000H, or when the CPU is reset from the front panel. The CP/M system must ; be loaded from the first two tracks of drive A up to, but not including, ; the BIOS, or CBIOS, if the user has completed the patch. System parameters ; must be initialized as follows: ; ; location 0,1,2 ; Set to JMP WBOOT for warm starts (000H: JMP 4A03H + b) ; ; location 3 ; Set initial value of IOBYTE, if implemented in the CBIOS ; ; location 4 ; High nibble = current user number, low nibble = current drive ; ; location 5,6,7 ; Set to JMP BDOS, which is the primary entry point to CP/M for transient ; programs. (0005H: JMP 3C06H + b) ; ; Refer to Section 6.9 for complete details of page zero use. Upon completion ; of the initialization, the WBOOT program must branch to the CCP at 3400H+b ; to restart the system. ; Upon entry to the CCP, register C is set to the drive to select after system ; initialization. The WBOOT routine should read location 4 in memory, verify ; that is a legal drive, and pass it to the CCP in register C. ;------------------------------------------------------------------------------- QWBOOT_: DI ; LD SP,BOOTSTACK ; Reload the CCP and BDOS from SD. LD A,TZSVC_CMD_LOADBDOS ; I/O processor command to load the CCP+BDOS LD HL,CBASE LD (TZSVC_LOADADDR),HL ; Address where to load the CCP+BDOS LD HL,CPMBIOS-CBASE LD (TZSVC_LOADSIZE),HL ; Size of the CCP+BDOS area. CALL SVC_CMD OR A JP Z, CPMINIT ; Initialise CPM and run. LD DE,NOBDOS CALL MONPRTSTR WBOOT2: JR WBOOT2 ; Cannot continue, no BDOS/CCP. ;------------------------------------------------------------------------------- ; CONOUT ; ; The character is sent from register C to the console output device. ; The character is in ASCII, with high-order parity bit set to zero. You ; might want to include a time-out on a line-feed or carriage return, if the ; console device requires some time interval at the end of the line (such as ; a TI Silent 700 terminal). You can filter out control characters that cause ; the console device to react in a strange way (CTRL_Z causes the Lear- ; Siegler terminal to clear the screen, for example). ;------------------------------------------------------------------------------- QCONOUT_: LD A,C CALL ANSITERM RET ;------------------------------------------------------------------------------- ; CONIN ; ; The next console character is read into register A, and the parity bit is ; set, high-order bit, to zero. If no console character is ready, wait until ; a character is typed before returning. ;------------------------------------------------------------------------------- QCONIN_: CALL GETKY RET ;------------------------------------------------------------------------------- ; CONST ; ; You should sample the status of the currently assigned console device and ; return 0FFH in register A if a character is ready to read and 00H in ; register A if no console characters are ready. ;------------------------------------------------------------------------------- QCONST_: CALL CHKKY RET ;------------------------------------------------------------------------------- ; READER ; ; The next character is read from the currently assigned reader device into ; register A with zero parity (high-order bit must be zero); an end-of-file ; condition is reported by returning an ASCII CTRL_Z(1AH). ;------------------------------------------------------------------------------- QREADER_: LD A, 01AH ; Reader not implemented. RET ;------------------------------------------------------------------------------- ; PUNCH ; ; The character is sent from register C to the currently assigned punch ; device. The character is in ASCII with zero parity. ;------------------------------------------------------------------------------- QPUNCH_: RET ; Punch not implemented ;------------------------------------------------------------------------------- ; LIST ; ; The character is sent from register C to the currently assigned listing ; device. The character is in ASCII with zero parity bit. ;------------------------------------------------------------------------------- QLIST_: RET ;------------------------------------------------------------------------------- ; LISTST ; ; You return the ready status of the list device used by the DESPOOL program ; to improve console response during its operation. The value 00 is returned ; in A if the list device is not ready to accept a character and 0FFH if a ; character can be sent to the printer. A 00 value should be returned if LIST ; status is not implemented. ;------------------------------------------------------------------------------- QLISTST_: XOR A ; Not implemented. RET ;------------------------------------------------------------------------------- ; HOME ; ; The disk head of the currently selected disk (initially disk A) is moved to ; the track 00 position. If the controller allows access to the track 0 flag ; from the drive, the head is stepped until the track 0 flag is detected. If ; the controller does not support this feature, the HOME call is translated ; into a call to SETTRK with a parameter of 0. ;------------------------------------------------------------------------------- QHOME_: LD BC,00000H ;------------------------------------------------------------------------------- ; SETTRK ; ; Register BC contains the track number for subsequent disk accesses on the ; currently selected drive. The sector number in BC is the same as the number ; returned from the SECTRN entry point. You can choose to seek the selected ; track at this time or delay the seek until the next read or write actually ; occurs. Register BC can take on values in the range 0-76 corresponding to ; valid track numbers for standard floppy disk drives and 0-65535 for ; nonstandard disk subsystems. ;------------------------------------------------------------------------------- QSETTRK_: LD (SEKTRK),BC ; Set track passed from BDOS in register BC. RET ;------------------------------------------------------------------------------- ; SETSEC ; ; Register BC contains the sector number, 1 through 26, for subsequent disk ; accesses on the currently selected drive. The sector number in BC is the ; same as the number returned from the SECTRAN entry point. You can choose to ; send this information to the controller at this point or delay sector ; selection until a read or write operation occurs. ;------------------------------------------------------------------------------- QSETSEC_: LD A,C ; Set sector passed from BDOS in register BC. LD (SEKSEC), A RET ;------------------------------------------------------------------------------- ; SETDMA ; ; Register BC contains the DMA (Disk Memory Access) address for subsequent ; read or write operations. For example, if B = 00H and C = 80H when SETDMA ; is called, all subsequent read operations read their data into 80H through ; 0FFH and all subsequent write operations get their data from 80H through ; 0FFH, until the next call to SETDMA occurs. The initial DMA address is ; assumed to be 80H. The controller need not actually support Direct Memory ; Access. If, for example, all data transfers are through I/O ports, the ; CBIOS that is constructed uses the 128 byte area starting at the selected ; DMA address for the memory buffer during the subsequent read or write ; operations. ;------------------------------------------------------------------------------- QSETDMA_: LD (DMAADDR),BC RET ;------------------------------------------------------------------------------- ; SELDSK ; ; The disk drive given by register C is selected for further operations, ; where register C contains 0 for drive A, 1 for drive B, and so on up to 15 ; for drive P (the standard CP/M distribution version supports four drives). ; On each disk select, SELDSK must return in HL the base address of a 16-byte ; area, called the Disk Parameter Header, described in Section 6.10. ; For standard floppy disk drives, the contents of the header and associated ; tables do not change; thus, the program segment included in the sample ; CBIOS performs this operation automatically. ; ; If there is an attempt to select a nonexistent drive, SELDSK returns ; HL = 0000H as an error indicator. Although SELDSK must return the header ; address on each call, it is advisable to postpone the physical disk select ; operation until an I/O function (seek, read, or write) is actually ; performed, because disk selects often occur without ultimately performing ; any disk I/O, and many controllers unload the head of the current disk ; before selecting the new drive. This causes an excessive amount of noise ; and disk wear. The least significant bit of register E is zero if this is ; the first occurrence of the drive select since the last cold or warm start. ;------------------------------------------------------------------------------- QSELDSK_: LD HL, 00000H ; HL = error code LD A,(NDISKS) LD B,A LD A,C CP B JR NC,SELDSK1 ; Ensure we dont select a non existant disk. LD (CDISK),A ; Setup drive. SELDSK0: CALL SETDRVCFG LD A,(DISKTYPE) CP DSKTYP_SDC JR Z,SELSDCDSK ; Select SD Card. ; If it is not an SD drive then it must be a floppy disk. LD A,C JR CALCHL SELDSK1: RET ; For SD Cards, check that the SD Card is present, otherwise illegal disk. SELSDCDSK: LD A,(DRVAVAIL) BIT 2,A JR Z,SELDSK1 ; No SD Card drives available then skip. LD A,C ; Code for blocking and deblocking algorithm ; (see CP/M 2.2 Alteration Guide p.34 and APPENDIX G) CALCHL: LD (SEKDSK),A RLC A ; *2 RLC A ; *4 RLC A ; *8 RLC A ; *16 LD HL,DPBASE LD B,0 LD C,A ADD HL,BC JR SELDSK1 ;------------------------------------------------------------------------------- ; SECTRAN ; ; Logical-to-physical sector translation is performed to improve the overall ; response of CP/M. Standard CP/M systems are shipped with a skew factor of ; 6, where six physical sectors are skipped between each logical read ; operation. This skew factor allows enough time between sectors for most ; programs to load their buffers without missing the next sector. In ; particular computer systems that use fast processors, memory, and disk ; subsystems, the skew factor might be changed to improve overall response. ; However, the user should maintain a single-density IBM-compatible version ; of CP/M for information transfer into and out of the computer system, using ; a skew factor of 6. ; ; In general, SECTRAN receives a logical sector number relative to zero in BC ; and a translate table address in DE. The sector number is used as an index ; into the translate table, with the resulting physical sector number in HL. ; For standard systems, the table and indexing code is provided in the CBIOS ; and need not be changed. ;------------------------------------------------------------------------------- QSECTRN_: LD H,B LD L,C RET ;------------------------------------------------------------------------------- ; READ ; ; Assuming the drive has been selected, the track has been set, and the DMA ; address has been specified, the READ subroutine attempts to read one sector ; based upon these parameters and returns the following error codes in ; register A: ; ; 0 - no errors occurred ; 1 - non recoverable error condition occurred ; ; Currently, CP/M responds only to a zero or nonzero value as the return ; code. That is, if the value in register A is 0, CP/M assumes that the disk ; operation was completed properly. If an error occurs the CBIOS should ; attempt at least 10 retries to see if the error is recoverable. When an ; error is reported the BDOS prints the message BDOS ERR ON x: BAD SECTOR. ; The operator then has the option of pressing a carriage return to ignore ; the error, or CTRL_C to abort. ;------------------------------------------------------------------------------- ; ; Code for blocking and deblocking algorithm ; (see CP/M 2.2 Alteration Guide p.34 and APPENDIX G) ; QREAD_: XOR A LD (UNACNT), A LD A, 1 LD (READOP), A ; read operation LD (RSFLAG), A ; must read data LD A, WRUAL LD (WRTYPE), A ; treat as unalloc CALL RWOPER ; to perform the read, returns with A=0 no errors or A > 0 errors. RET ;------------------------------------------------------------------------------- ; WRITE ; ; Data is written from the currently selected DMA address to the currently ; selected drive, track, and sector. For floppy disks, the data should be ; marked as nondeleted data to maintain compatibility with other CP/M ; systems. The error codes given in the READ command are returned in register ; A, with error recovery attempts as described above. ;------------------------------------------------------------------------------- ; ; Code for blocking and deblocking algorithm ; (see CP/M 2.2 Alteration Guide p.34 and APPENDIX G) ; QWRITE_: XOR A ; 0 to accumulator LD (READOP), A ; not a read operation LD A, C ; write type in c LD (WRTYPE), A CP WRUAL ; write unallocated? JR NZ, CHKUNA ; check for unalloc ; write to unallocated, set parameters LD A, BLKSIZ/128 ; next unalloc recs LD (UNACNT), A LD A, (SEKDSK) ; disk to seek LD (UNADSK), A ; unadsk = sekdsk LD HL, (SEKTRK) LD (UNATRK), HL ; unatrk = sectrk LD A, (SEKSEC) LD (UNASEC), A ; unasec = seksec ; check for write to unallocated sector CHKUNA: LD A,(UNACNT) ; any unalloc remain? OR A JR Z, ALLOC ; skip if not ; more unallocated records remain DEC A ; unacnt = unacnt-1 LD (UNACNT), A LD A, (SEKDSK) ; same disk? LD HL, UNADSK CP (HL) ; sekdsk = unadsk? JP NZ, ALLOC ; skip if not ; disks are the same LD HL, UNATRK CALL SEKTRKCMP ; sektrk = unatrk? JP NZ, ALLOC ; skip if not ; tracks are the same LD A, (SEKSEC) ; same sector? LD HL, UNASEC CP (HL) ; seksec = unasec? JP NZ, ALLOC ; skip if not ; match, move to next sector for future ref INC (HL) ; unasec = unasec+1 LD A, (HL) ; end of track? CP CPMSPT ; count CP/M sectors JR C, NOOVF ; skip if no overflow ; overflow to next track LD (HL), 0 ; unasec = 0 LD HL, (UNATRK) INC HL LD (UNATRK), HL ; unatrk = unatrk+1 ; match found, mark as unnecessary read NOOVF: XOR A ; 0 to accumulator LD (RSFLAG), A ; rsflag = 0 JR ALLOC2 ; to perform the write ; not an unallocated record, requires pre-read ALLOC: XOR A ; 0 to accum LD (UNACNT), A ; unacnt = 0 INC A ; 1 to accum ALLOC2: LD (RSFLAG), A ; rsflag = 1 CALL RWOPER RET ;------------------------------------------------------------------------------- ; SERVICE COMMAND METHODS ;------------------------------------------------------------------------------- ; Method to send a command to the I/O processor and verify it is being acted upon. ; The method, after sending the command, polls the service structure result to see if the I/O processor has updated it. If it doesnt update the result ; then after a period of time the command is resent. After a number of retries the command aborts with error. This is needed in case of the I/O processor crashing ; we dont want the host to lock up. ; ; Inputs: ; A = Command. ; Outputs: ; A = 0 - Success, command being processed. ; A = 1 - Failure, no contact with I/O processor. ; A = 2 - Failure, no result from I/O processor, it could have crashed or SD card removed! SVC_CMD: DI PUSH BC LD (TZSVCCMD), A ; Load up the command into the service record. LD A,TZSVC_STATUS_REQUEST LD (TZSVCRESULT),A ; Set the service structure result to REQUEST, if this changes then the K64 is processing. LD BC, TZSVCWAITIORETRIES ; Safety in case the IO request wasnt seen by the I/O processor, if we havent seen a response in the service SVC_CMD1: PUSH BC LD A,(TZSVCCMD) OUT (SVCREQ),A ; Make the service request via the service request port. LD BC,0 SVC_CMD2: LD A,(TZSVCRESULT) CP TZSVC_STATUS_REQUEST ; I/O processor when it recognises the request sets the status to PROCESSING or gives a result, if this hasnt occurred the the K64F hasnt begun processing. JR NZ, SVC_CMD3 DEC BC LD A,B OR C JR NZ, SVC_CMD2 POP BC DEC BC LD A,B OR C JR NZ,SVC_CMD1 ; Retry sending the I/O command. ; PUSH DE LD DE,SVCIOERR CALL MONPRTSTR POP DE LD A,1 ; No response, error. EI RET SVC_CMD3: POP BC ; LD BC,TZSVCWAITCOUNT ; Number of loops to wait for a response before setting error. SVC_CMD4: PUSH BC LD BC,0 SVC_CMD5: LD A,(TZSVCRESULT) CP TZSVC_STATUS_PROCESSING ; Wait until the I/O processor sets the result, again timeout in case it locks up. JR NZ, SVC_CMD6 DEC BC LD A,B OR C JR NZ,SVC_CMD5 POP BC DEC BC LD A,B OR C JR NZ,SVC_CMD4 ; Retry polling for result. ; PUSH DE LD DE,SVCRESPERR CALL MONPRTSTR POP DE LD A,2 EI RET SVC_CMD6: XOR A ; Success. POP BC POP BC EI RET ;------------------------------------------------------------------------------- ; END OF SERVICE COMMAND METHODS ;------------------------------------------------------------------------------- ; Method to clear memory either to 0 or a given pattern. ; CLR8Z: XOR A CLR8: LD BC,00800H CLRMEM: PUSH DE LD D,A L09E8: LD (HL),D INC HL DEC BC LD A,B OR C JR NZ,L09E8 POP DE RET ;------------------------------------------------------------------------------- ; START OF SD CARD DRIVE FUNCTIONALITY ;------------------------------------------------------------------------------- ; Method to read a sector from the SD Card. ; All reads occur in a 512byte sector. ; ; CPM Provides us with a track and sector, take these and pass them to the I/O ; processor which will map the params into the file associated with the drive. ; ; Inputs: (HSTTRK) - 16bit track number. ; (HSTSEC) - 8bit sector number. ; (CDISK) - Disk drive number. SDCREAD: LD HL,(HSTTRK) ; 16bit track. LD (TZSVC_TRACK_NO),HL LD H,0 ; Only use 8 bit sector numbers at the moment. LD A,(HSTSEC) LD L,A LD (TZSVC_SECTOR_NO),HL LD A,(CDISK) ; Disk drive no. LD (TZSVC_FILE_NO),A ; LD A,TZSVC_CMD_READSDDRIVE CALL SVC_CMD OR A JP READHST3 ; Method to write a sector for CPM using its track/sector addressing. ; All writes occur in a 512byte sector. ; ; Inputs: (HSTTRK) - 16bit track number. ; (HSTSEC) - 8bit sector number. ; (CDISK) - Disk drive number. ; Outputs: A = 1 - Error. ; A = 0 - Success. SDCWRITE: LD HL,(HSTTRK) ; 16bit track. LD (TZSVC_TRACK_NO),HL LD H,0 ; Only use 8 bit sector numbers at the moment. LD A,(HSTSEC) LD L,A LD (TZSVC_SECTOR_NO),HL LD A,(CDISK) ; Disk drive no. LD (TZSVC_FILE_NO),A ; LD A,TZSVC_CMD_WRITESDDRIVE CALL SVC_CMD OR A JP WRITEHST3 ;------------------------------------------------------------------------------- ; END OF SD CARD DRIVE FUNCTIONALITY ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; START OF CPM DEBLOCKING ALGORITHM ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; RWOPER ; ; The common blocking/deblocking algorithm provided by DR to accommodate devices ; which have sector sizes bigger than the CPM 128byte standard sector. ; In this implementation a sector size of 512 has been chosen regardless of ; what the underlying hardware uses (ie. FDC is 256 byte for a standard MZ800 ; format disk). ;------------------------------------------------------------------------------- RWOPER: XOR A ; zero to accum LD (ERFLAG), A ; no errors (yet) LD A, (SEKSEC) ; compute host sector OR A ; carry = 0 RRA ; shift right OR A ; carry = 0 RRA ; shift right LD (SEKHST), A ; host sector to seek ; active host sector? LD HL, HSTACT ; host active flag LD A, (HL) LD (HL), 1 ; always becomes 1 OR A ; was it already? JR Z, FILHST ; fill host if not ; host buffer active, same as seek buffer? LD A, (SEKDSK) LD HL, HSTDSK ; same disk? CP (HL) ; sekdsk = hstdsk? JR NZ, NOMATCH ; same disk, same track? LD HL, HSTTRK CALL SEKTRKCMP ; sektrk = hsttrk? JR NZ, NOMATCH ; same disk, same track, same buffer? LD A, (SEKHST) LD HL, HSTSEC ; sekhst = hstsec? CP (HL) JR Z, MATCH ; skip if match ; proper disk, but not correct sector NOMATCH: LD A, (HSTWRT) ; host written? OR A CALL NZ, WRITEHST ; clear host buff ; may have to fill the host buffer FILHST: LD A, (SEKDSK) LD (HSTDSK), A LD HL, (SEKTRK) LD (HSTTRK), HL LD A, (SEKHST) LD (HSTSEC), A LD A, (RSFLAG) ; need to read? OR A CALL NZ, READHST ; yes, if 1 OR A JR NZ,RWEXIT ; If A > 0 then read error occurred. XOR A ; 0 to accum LD (HSTWRT), A ; no pending write ; copy data to or from buffer MATCH: LD A, (SEKSEC) ; mask buffer number AND SECMSK ; least signif bits LD L, A ; ready to shift LD H, 0 ; double count ADD HL, HL ADD HL, HL ADD HL, HL ADD HL, HL ADD HL, HL ADD HL, HL ADD HL, HL ; hl has relative host buffer address LD DE, HSTBUF ADD HL, DE ; hl = host address EX DE, HL ; now in DE LD HL, (DMAADDR) ; get/put CP/M data LD C, 128 ; length of move LD A, (READOP) ; which way? OR A JR NZ, RWMOVE ; skip if read ; write operation, mark and switch direction LD A, 1 LD (HSTWRT), A ; hstwrt = 1 EX DE, HL ; source/dest swap ; c initially 128, DE is source, HL is dest RWMOVE: LD A,(INVFDCDATA) ; Check to see if FDC data needs to be inverted. MB8866 controller works on negative logic. RRCA JR NC,RWMOVE3 RWMOVE2: CALL MEMCPYINV JR RWMOVE4 RWMOVE3: CALL MEMCPY ; data has been moved to/from host buffer RWMOVE4: LD A, (WRTYPE) ; write type CP WRDIR ; to directory? LD A, (ERFLAG) ; in case of errors RET NZ ; no further processing ; clear host buffer for directory write OR A ; errors? RET NZ ; skip if so XOR A ; 0 to accum LD (HSTWRT), A ; buffer written CALL WRITEHST RWEXIT: LD A, (ERFLAG) RET ; utility subroutine for 16-bit compare ; HL = .unatrk or .hsttrk, compare with sektrk SEKTRKCMP: EX DE, HL LD HL, SEKTRK LD A, (DE) ; low byte compare CP (HL) ; same? RET NZ ; return if not ; low bytes equal, test high 1s INC DE INC HL LD A, (DE) CP (HL) ; sets flags RET ;------------------------------------------------------------------------------------------------ ; Read physical sector from host ; ; Read data from the floppy disk or SD. A = 1 if an error occurred. ;------------------------------------------------------------------------------------------------ READHST: PUSH BC PUSH HL LD A,(DISKTYPE) CP DSKTYP_SDC ; Is the drive an SD Card? JP Z,SDCREAD READHST2: CALL DSKREAD ; Floppy card, use the FDC Controller. READHST3: POP HL POP BC RET ;------------------------------------------------------------------------------------------------ ; Write physical sector to host ; ; Write data to the floppy disk or SD. A = 1 if an error occurred. ;------------------------------------------------------------------------------------------------ WRITEHST: PUSH BC PUSH HL LD A,(DISKTYPE) CP DSKTYP_SDC ; Is the drive an SD Card? JP Z,SDCWRITE CALL DSKWRITE WRITEHST3: POP HL POP BC RET WRITEHST4: LD A,1 ; Error, cannot write. JR WRITEHST3 ;------------------------------------------------------------------------------- ; END OF CPM DEBLOCKING ALGORITHM ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; START OF AUDIO CONTROLLER FUNCTIONALITY ;------------------------------------------------------------------------------- ; Melody function. MLDY: PUSH BC PUSH DE PUSH HL LD A,002H LD (OCTV),A LD B,001H MLD1: LD A,(DE) CP 00DH JR Z,MLD4 CP 0C8H JR Z,MLD4 CP 0CFH JR Z,MLD2 CP 02DH JR Z,MLD2 CP 02BH JR Z,MLD3 CP 0D7H JR Z,MLD3 CP 023H LD HL,MTBL JR NZ,MLD1A LD HL,M?TBL INC DE MLD1A: CALL ONPU JR C,MLD1 CALL RYTHM JR C,MLD5 CALL MLDST LD B,C JR MLD1 MLD2: LD A,003H MLD2A: LD (OCTV),A INC DE JR MLD1 MLD3: LD A,001H JR MLD2A MLD4: CALL RYTHM MLD5: PUSH AF CALL MLDSP POP AF POP HL POP DE POP BC RET ONPU: PUSH BC LD B,008H LD A,(DE) ONP1A: CP (HL) JR Z,ONP2 INC HL INC HL INC HL DJNZ ONP1A SCF INC DE POP BC RET ONP2: INC HL PUSH DE LD E,(HL) INC HL LD D,(HL) EX DE,HL LD A,H OR A JR Z,ONP2B LD A,(OCTV) ONP2A: DEC A JR Z,ONP2B ADD HL,HL JR ONP2A ONP2B: LD (RATIO),HL LD HL,OCTV LD (HL),002H DEC HL POP DE INC DE LD A,(DE) LD B,A AND 0F0H CP 030H JR Z,ONP2C LD A,(HL) JR ONP2D ONP2C: INC DE LD A,B AND 00FH LD (HL),A ONP2D: LD HL,OPTBL ADD A,L LD L,A LD C,(HL) LD A,(TEMPW) LD B,A XOR A JP MLDDLY RYTHM: LD HL,KEYPA LD (HL),0F0H INC HL LD A,(HL) AND 081H JR NZ,L02D5 SCF RET L02D5: LD A,(SUNDG) RRCA JR C,L02D5 L02DB: LD A,(SUNDG) RRCA JR NC,L02DB DJNZ L02D5 XOR A RET MLDST: LD HL,(RATIO) LD A,H OR A JR Z,MLDSP PUSH DE EX DE,HL LD HL,CONT0 LD (HL),E LD (HL),D LD A,001H POP DE JR L02C4 MLDSP: LD A,034H LD (CONTF),A XOR A L02C4: LD (SUNDG),A RET MLDDLY: ADD A,C DJNZ MLDDLY POP BC LD C,A XOR A RET TEMPO: PUSH AF PUSH BC AND 00FH LD B,A LD A,008H SUB B LD (TEMPW),A POP BC POP AF RET ; ; Method to sound the bell, basically play a constant tone. ; BEL: PUSH DE LD DE,00DB1H CALL MLDY POP DE RET ; ; Melody (note) lookup table. ; MTBL: DB 043H DB 077H DB 007H DB 044H DB 0A7H DB 006H DB 045H DB 0EDH DB 005H DB 046H DB 098H DB 005H DB 047H DB 0FCH DB 004H DB 041H DB 071H DB 004H DB 042H DB 0F5H DB 003H DB 052H DB 000H DB 000H M?TBL: DB 043H DB 00CH DB 007H DB 044H DB 047H DB 006H DB 045H DB 098H DB 005H DB 046H DB 048H DB 005H DB 047H DB 0B4H DB 004H DB 041H DB 031H DB 004H DB 042H DB 0BBH DB 003H DB 052H DB 000H DB 000H OPTBL: DB 001H DB 002H DB 003H DB 004H DB 006H DB 008H DB 00CH DB 010H DB 018H DB 020H ;------------------------------------------------------------------------------- ; END OF AUDIO CONTROLLER FUNCTIONALITY ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; START OF RTC FUNCTIONALITY (INTR HANDLER IN MAIN CBIOS) ;------------------------------------------------------------------------------- ; ; BC:DE:HL contains the time in milliseconds (100msec resolution) since 01/01/1980. In IX is held the interrupt service handler routine address for the RTC. ; HL contains lower 16 bits, DE contains middle 16 bits, BC contains upper 16bits, allows for a time from 00:00:00 to 23:59:59, for > 500000 days! ; NB. Caller must disable interrupts before calling this method. TIMESET: LD (TIMESEC),HL ; Load lower 16 bits. EX DE,HL LD (TIMESEC+2),HL ; Load middle 16 bits. PUSH BC POP HL LD (TIMESEC+4),HL ; Load upper 16 bits. ; LD HL,CONTF LD (HL),074H ; Set Counter 1, read/load lsb first then msb, mode 2 rate generator, binary LD (HL),0B0H ; Set Counter 2, read/load lsb first then msb, mode 0 interrupt on terminal count, binary DEC HL LD DE,TMRTICKINTV ; 100Hz coming into Timer 2 from Timer 1, set divisor to set interrupts per second. LD (HL),E ; Place current time in Counter 2 LD (HL),D DEC HL IF BUILD_MZ80A = 1 LD (HL),03BH ; Place divisor in Counter 1, = 315, thus 31500/315 = 100 LD (HL),001H ENDIF IF BUILD_MZ700 = 1 LD (HL),09CH ; Place divisor in Counter 1, = 156, thus 15611/156 = 100 LD (HL),000H ENDIF NOP NOP NOP ; LD A, 0C3H ; Install the interrupt vector for when interrupts are enabled. LD (00038H),A LD (00039H),IX RET ; Time Read; ; Returns BC:DE:HL where HL is lower 16bits, DE is middle 16bits and BC is upper 16bits of milliseconds since 01/01/1980. TIMEREAD: LD HL,(TIMESEC+4) PUSH HL POP BC LD HL,(TIMESEC+2) EX DE,HL LD HL,(TIMESEC) RET ;------------------------------------------------------------------------------- ; END OF RTC FUNCTIONALITY ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; START OF FDC CONTROLLER FUNCTIONALITY ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------ ; Initialise drive and reset flags, Set motor off ;------------------------------------------------------------------------------------------------ DSKINIT: XOR A OUT (FDC_MOTOR),A ; Motor off LD (TRK0FD1),A ; Track 0 flag drive 1 LD (TRK0FD2),A ; Track 0 flag drive 2 LD (TRK0FD3),A ; Track 0 flag drive 3 LD (TRK0FD4),A ; Track 0 flag drive 4 LD (MOTON),A ; Motor on flag LD (MTROFFTIMER),A ; Clear the down counter for motor off. LD A,(FDCJMP1) ; Check to see if the FDC AFI ROM is installed, use this as AND 0DFH ; Byte can be either DD or FD dependent on the FDC wait line, CP 0DDH ; so check both to determine if the FDC is present. RET ; Function to create a mapping table between a CPM disk and a physical disk. SETDRVMAP: PUSH HL PUSH DE PUSH BC ; Zero out the map. LD B,MAXDISKS LD HL,DISKMAP LD A,0FFH SETDRVMAP1: LD (HL),A INC HL DJNZ SETDRVMAP1 LD HL,DISKMAP ; Place in the Map for next drive. ; Now go through each disk from the Disk Parameter Base list. LD B,0 ; Disk number count = CDISK. LD DE,0 ; Physical disk number, D = FDC, E = SDC. SETDRVMAP2: LD A,B CP MAXDISKS JR Z,SETDRVMAP6 INC B PUSH HL PUSH DE PUSH BC ; For the Disk in A, find the parameter table. RLC A ; *2 RLC A ; *4 RLC A ; *8 RLC A ; *16 LD HL,DPBASE ; Base of disk description block. LD B,0 LD C,A ADD HL,BC ; HL contains address of actual selected disk block. LD C,10 ADD HL,BC ; HL contains address of pointer to disk parameter block. LD E,(HL) INC HL LD D,(HL) ; DE contains address of disk parameter block. EX DE,HL LD A,(HL) LD E,A LD BC,15 ADD HL,BC ; Move to configuuration byte which identifies the disk type. ; POP BC POP DE LD A,(HL) POP HL BIT 4,A ; Disk type = FDC JR Z,SETDRVMAP4 ; Is this an FDC controlled disk, if so store the mapping number in the map unchanged. ; BIT 3,A ; Is this an SD Card disk, if so, add 080H to the mapping number and store. JR Z,SETDRVMAP5 LD A,E OR 080H INC E JR SETDRVMAP5 ; SETDRVMAP4: LD A,D INC D SETDRVMAP5: LD (HL),A INC HL JR SETDRVMAP2 ; SETDRVMAP6: POP BC POP DE POP HL RET ; Function to setup the drive parameters according to the CFG byte in the disk parameter block. SETDRVCFG: PUSH HL PUSH DE PUSH BC LD A,(CDISK) RLC A ; *2 RLC A ; *4 RLC A ; *8 RLC A ; *16 LD HL,DPBASE ; Base of disk description block. LD B,0 LD C,A ADD HL,BC ; HL contains address of actual selected disk block. LD C,10 ADD HL,BC ; HL contains address of pointer to disk parameter block. LD E,(HL) INC HL LD D,(HL) ; DE contains address of disk parameter block. EX DE,HL LD A,(HL) LD E,A LD BC,15 ADD HL,BC ; Move to configuration byte. XOR A BIT 2,(HL) JR Z,SETDRV0 INC A SETDRV0: LD (INVFDCDATA),A ; Data inversion is set according to drive parameter. LD A,4 BIT 1,(HL) JR Z,SETDRV1 LD A,2 BIT 0,(HL) JR Z,SETDRV1 LD A,1 SETDRV1: LD (SECTORCNT),A ; Set the disk sector size. LD D,A CP 4 LD A,E JR Z,SETDRV1A OR A RR A LD E,A LD A,D CP 2 LD A,E JR Z,SETDRV1A OR A RR A ; Convert sectors per track from 128 bytes to 256 byte sectors. SETDRV1A: INC A ; Add 1 to ease comparisons. LD (SECPERTRK),A ; Only cater for 8bit, ie. 256 sectors. DEC A OR A RR A INC A ; Add 1 to ease comparisons. LD (SECPERHEAD),A ; Convert sectors per track to sectors per head. ; XOR A ; Disk type = FDC BIT 4,(HL) JR Z,SETDRV2 LD A,DSKTYP_SDC ; Disk type = SD Card SETDRV2: LD (DISKTYPE),A POP BC POP DE POP HL RET ; Method to get the current disk drive mapped to the correct controller. ; The CPM CDISK is mapped via MAPDISK[CDISK] and the result: ; Bit 7 = 1 - SD Card drive. ; BIT 7:6 = 00 - Floppy drive. GETMAPDSK: PUSH HL PUSH BC LD A,(CDISK) LD HL,DISKMAP LD C,A LD B,0 ADD HL,BC LD A,(HL) ; Get the physical number after mapping from the CDISK. POP BC POP HL RET ; Select FDC drive (make active) based on value in DISKMAP[CDISK]. SELDRIVE: CALL GETMAPDSK CP 040H ; Anything with bit 6 or 7 set is not an FDC drive. RET NC ; This isnt a physical floppy disk, no need to perform any actions, exit. ; LD (FDCDISK),A CALL DSKMTRON ; yes, set motor on and wait LD A,(FDCDISK) ; select drive no OR 084H OUT (FDC_MOTOR),A ; Motor on for drive 0-3 XOR A LD (FDCCMD),A ; clr latest FDC command byte LD HL,00000H SELDRV2: DEC HL LD A,H OR L JP Z,SELDRVERR ; Reset and print message that this is not a valid disk. IN A,(FDC_STR) ; Status register. CPL RLCA JR C,SELDRV2 ; Wait on Drive Ready Bit (bit 7) LD A,(FDCDISK) ; Drive number LD C,A LD HL,TRK0FD1 ; 1 track 0 flag for each drive LD B,000H ADD HL,BC ; Compute related flag 1002/1003/1004/1005 BIT 0,(HL) JR NZ,SELDRV3 ; If the drive hasnt been intialised to track 0, intialise and set flag. CALL DSKSEEKTK0 ; Seek track 0. SET 0,(HL) ; Set bit 0 of trk 0 flag SELDRV3: CALL SETDRVCFG RET ; Turn disk motor on if not already running. DSKMTRON: LD A,255 ; Ensure motor is kept running whilst we read/write. LD (MTROFFTIMER),A LD A,(MOTON) ; Test to see if motor is on, if it isnt, switch it on. RRCA JR NC, DSKMOTORON RET DSKMOTORON: PUSH BC LD A,080H OUT (FDC_MOTOR),A ; Motor on LD B,010H ; DSKMTR2: CALL MTRDELAY ; DJNZ DSKMTR2 ; Wait until becomes ready. LD A,001H ; Set motor on flag. LD (MOTON),A ; POP BC RET FDCDLY1: PUSH DE LD DE,00007H JP MTRDEL2 MTRDELAY: PUSH DE LD DE,01013H MTRDEL2: DEC DE LD A,E OR D JR NZ,MTRDEL2 POP DE RET DSKWRITE: LD A,MAXWRRETRY LD (RETRIES),A LD A,(SECTORCNT) LD B,A LD A,(HSTSEC) DSKWRITE0A: DJNZ DSKWRITE0B JR DSKWRITE1 DSKWRITE0B: OR A RL A JR DSKWRITE0A DSKWRITE1: INC A LD (SECTORNO), A ; Convert from Host 512 byte sector into local sector according to paraameter block. LD HL,(HSTTRK) LD (TRACKNO),HL DSKWRITE2: CALL SETTRKSEC ; Set current track & sector, get load address to HL DSKWRITE3: CALL SETHEAD ; Set side reg CALL SEEK ; Command 1b output (seek) JP NZ,SEEKRETRY ; CALL OUTTKSEC ; Set track & sector reg LD IX, 0F3FEH ; As below. L03FE LD IY,WRITEDATA ; Write sector from memory. DI ; LD A,0B4H ; Write Sector multipe with Side Compare for side 1. CALL DISKCMDWAIT LD D,2 ; Regardless of 4x128, 2x256 or 1x512, we always read 512bytes by the 2x INI instruction with B=256. STRTDATWR: LD B,0 ; 256 bytes to load. JP (IX) WRITEDATA: OUTI JP NZ, 0F3FEH ; This is crucial, as the Z80 is running at 2MHz it is not fast enough so needs ; hardware acceleration in the form of a banked ROM, if disk not ready jumps to IX, if ; data ready, jumps to IY. DEC D JP NZ,0F3FEH ; If we havent read all sectors to form a 512 byte block, go for next sector. JR DATASTOP ; Read disk starting at the first logical sector in param block 1009/100A ; Continue reading for the given size 100B/100C and store in the location ; Pointed to by the address stored in the parameter block. 100D/100E DSKREAD: LD A,MAXRDRETRY LD (RETRIES),A LD A,(SECTORCNT) LD B,A LD A,(HSTSEC) DSKREAD0A: DJNZ DSKREAD0B JR DSKREAD1 DSKREAD0B: OR A RL A JR DSKREAD0A DSKREAD1: INC A LD (SECTORNO), A ; Convert from Host 512 byte sector into local sector according to paraameter block. LD HL,(HSTTRK) LD (TRACKNO),HL DSKREAD2: CALL SETTRKSEC ; Set current track & sector, get load address to HL DSKREAD3: CALL SETHEAD ; Set side reg CALL SEEK ; Command 1b output (seek) JP NZ,SEEKRETRY ; CALL OUTTKSEC ; Set track & sector reg LD IX, 0F3FEH ; As below. L03FE LD IY,READDATA ; Read sector into memory. DI ; LD A,094H ; Read Sector multiple with Side Compare for side 1. CALL DISKCMDWAIT LD D,2 ; Regardless of 4x128, 2x256 or 1x512, we always read 512bytes by the 2x INI instruction with B=256. STRTDATRD: LD B,0 ; 256 bytes to load. JP (IX) ; Get data from disk sector to staging area. READDATA: INI JP NZ,0F3FEH ; This is crucial, as the Z80 is running at 2MHz it is not fast enough so needs ; hardware acceleration in the form of a banked ROM, if disk not ready jumps to IX, if ; data ready, jumps to IY. DEC D JP NZ,0F3FEH ; If we havent read all sectors to form a 512 byte block, go for next sector. ; ; DATASTOP: LD A,0D8H ; Force interrupt command, Immediate interrupt (I3 bit 3=1) of multiple sector read. CPL OUT (FDC_CR),A CALL WAITRDY ; Wait for controller to become ready, acknowledging interrupt. IN A,(FDC_STR) ; Check for errors. CPL AND 0FFH JR NZ,SEEKRETRY UPDSECTOR: PUSH HL LD A,(SECTORCNT) LD HL,SECTORNO ADD A,(HL) ; Update sector to account for sectors read. NB. All reads will start at such a position LD (HL), A ; that a read will not span a track or head. Ensure that disk formats meet an even 512byte format. POP HL MOTOROFF: LD A,MTROFFMSECS ; Schedule motor to be turned off. LD (MTROFFTIMER),A XOR A ; Successful read, return 0 EI RET SEEKRETRY: LD B,A ; Preserve the FDC Error byte. LD A,(RETRIES) DEC A LD (RETRIES),A LD A,B JP Z,RETRIESERR CALL DSKSEEKTK0 LD A, (READOP) OR A LD A,(TRACKNO) ; NB. Track number is 16bit, FDC only uses lower 8bit and assumes little endian read. JP Z, DSKWRITE2 ; Try write again. JP DSKREAD2 ; Try the read again. DISKCMDWAIT:LD (FDCCMD),A CPL OUT (FDC_CR),A CALL WAITBUSY RET ; Send a command to the disk controller. DSKCMD: LD (FDCCMD),A ; Store latest FDC command. CPL ; Compliment it (FDC bit value is reversed). OUT (FDC_CR),A ; Send command to FDC. CALL WAITRDY ; Wait to become ready. IN A,(FDC_STR) ; Get status register. CPL ; Inverse (FDC is reverse bit logic). RET ; Seek to programmed track. SEEK: LD A,01BH ; Seek command, load head, verify stepping 6ms. CALL DSKCMD AND 099H RET ; Set current track & sector, get load address to HL SETTRKSEC: CALL SELDRIVE LD A,(TRACKNO) ; NB. Track number is 16bit, FDC only uses lower 8bit and assumes little endian read. LD HL, HSTBUF RET ; Compute side/head. SETHEAD: CPL ; OUT (FDC_DR),A ; Output track no for SEEK command. PUSH HL LD HL,SECPERHEAD LD A,(SECTORNO) ; Check sector, if greater than sector per track, change head. CP (HL) POP HL JR NC,SETHD2 ; Yes, even, set side/head 1 LD A,001H ; No, odd, set side/head 0 JR SETHD3 ; Set side/head register. SETHD2: XOR A ; Side 0 SETHD3: CPL ; Side 1 OUT (FDC_SIDE),A ; Side/head register. RET ; Set track and sector register. OUTTKSEC: PUSH HL LD HL,SECPERHEAD ; LD C,FDC_DR ; Port for data retrieval in the INI instruction in main block. LD A,(TRACKNO) ; Current track number, NB. Track number is 16bit, FDC only uses lower 8bit and assumes little endian read. CPL OUT (FDC_TR),A ; Track reg ; LD A,(SECTORNO) ; Current sector number CP (HL) JR C,OUTTKSEC2 SUB (HL) INC A ; Account for the +1 added to ease comparisons. OUTTKSEC2: CPL OUT (FDC_SCR),A ; Sector reg POP HL RET ; Seek to track 0. DSKSEEKTK0: CALL DSKMTRON ; Make sure disk is spinning. LD A,00BH ; Restore command, seek track 0. CALL DSKCMD ; Send command to FDC. AND 085H ; Process result. XOR 004H RET Z JP DSKSEEKERR ; Wait for the drive to become ready. WAITRDY: PUSH DE PUSH HL CALL FDCDLY1 LD E,007H WAITRDY2: LD HL,00000H WAITRDY3: DEC HL LD A,H OR L JR Z,WAITRDY4 IN A,(FDC_STR) CPL RRCA JR C,WAITRDY3 POP HL POP DE RET WAITRDY4: DEC E JR NZ,WAITRDY2 POP HL POP DE JP WAITRDYERR WAITBUSY: PUSH DE PUSH HL CALL FDCDLY1 LD E,007H ; 7 Chances of a 16bit down count delay waiting for DRQ. WAITBUSY2: LD HL,00000H WAITBUSY3: DEC HL LD A,H OR L JR Z,WAITBUSY4 ; Down counter expired, decrement retries, error on 0. IN A,(FDC_STR) ; Get the FDC Status CPL ; Switch to positive logic. RRCA ; Shift Busy flag into Carry. JR NC,WAITBUSY3 ; Busy not set, decrement counter and retry. POP HL POP DE RET WAITBUSY4: DEC E JR NZ,WAITBUSY2 POP HL POP DE JP DSKERR ; Error processing. Consists of printing a message followed by debug data (if enabled) and returning with carry set ; to indicate error. DSKERR: LD DE,LOADERR ; Loading error message JR HDLERROR SELDRVERR: LD DE,SELDRVMSG ; Select drive error message. JR HDLERROR WAITRDYERR: LD DE,WAITRDYMSG ; Waiting for ready timeout error message. JR HDLERROR DSKSEEKERR: LD DE,DSKSEEKMSG ; Disk seek to track error message. JR HDLERROR RETRIESERR: BIT 2,A ; Data overrun error if 1. LD DE,DATAOVRMSG JR NZ, RETRIESERR2 BIT 3,A ; CRC error if 1. LD DE,CRCERRMSG JR NZ,RETRIESERR2 LD DE,RETRIESMSG ; Data sector read error message. RETRIESERR2: ; Process error, dump debug data and return fail code. HDLERROR: SCF CALL DEBUG HOLPRTSTR: LD A,(DE) OR A JR Z,HDLPRTSTR3 INC DE HOLPRTSTR2: CALL PRNT JR HOLPRTSTR HDLPRTSTR3: XOR A CALL DSKINIT CALL DSKMTRON LD A,001H ; Indicate error by setting 1 in A register. EI RET ;------------------------------------------------------------------------------- ; END OF FDC CONTROLLER FUNCTIONALITY ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; UTILITIES ;------------------------------------------------------------------------------- ; Function to print a string with control character interpretation. MONPRTSTR: LD A,(DE) OR A RET Z INC DE MONPRTSTR2: CALL PRNT JR MONPRTSTR ; Helper method to set up a Disk Parameter Block. ; Input: Drive Count = (CDIRBUF) ; CSV/ALV Memory Pointer (CDIRBUF+1) ; CSV Size (CDIRBUF+3) ; ALV Size (CDIRBUF+5) ; Disk parameters address CDIRBUF+7) ; Output: Updated CSV/ALV Pointer (CDIRBUF+1) ; Updated disk count (CDIRBUF) COPYDPB: LD HL,DPBTMPL ; Base of parameter template. LD BC,10 LDIR ; Copy the lower part of the DPB as it is static. LD HL,CDIRBUF+7 ; Get the address of the disk parameters. LDI LDI LD BC,(CDIRBUF+3) ; Add the CSV size for this entry to the pointer and store. LD A,B ; Fixed drives dont have a CSV, so if 0, copy 0 and not allocate memory. OR C LD HL,CDIRBUF+1 ; Now get the free CSV/ALV pointer. JR NZ,COPYDPB1 LD HL,CDIRBUF+3 COPYDPB1: LDI LDI LD HL,(CDIRBUF+1) LD BC,(CDIRBUF+3) ; Add the CSV size for this entry to the pointer and store. ADD HL,BC LD (CDIRBUF+1),HL LD HL,CDIRBUF+1 LDI LDI LD HL,(CDIRBUF+1) LD BC,(CDIRBUF+5) ; Now add the size of the ALV for this drive to the pointer for the next drive. ADD HL,BC LD (CDIRBUF+1),HL ; Store. LD A,(CDIRBUF) INC A LD (CDIRBUF),A ; Update drive count. RET ; A function from the z88dk stdlib, a delay loop with T state accuracy. ; ; enter : hl = tstates >= 141 ; uses : af, bc, hl T_DELAY: LD BC,-141 ADD HL,BC LD BC,-23 TDELAYLOOP: ADD HL,BC JR C, TDELAYLOOP LD A,L ADD A,15 JR NC, TDELAYG0 CP 8 JR C, TDELAYG1 OR 0 TDELAYG0: INC HL TDELAYG1: RRA JR C, TDELAYB0 NOP TDELAYB0: RRA JR NC, TDELAYB1 OR 0 TDELAYB1: RRA RET NC RET ; Method to multiply a 16bit number by another 16 bit number to arrive at a 32bit result. ; Input: DE = Factor 1 ; BC = Factor 2 ; Output:DEHL = 32bit Product ; MULT16X16: LD HL,0 LD A,16 MULT16X1: ADD HL,HL RL E RL D JR NC,$+6 ADD HL,BC JR NC,$+3 INC DE DEC A JR NZ,MULT16X1 RET ; Method to add a 16bit number to a 32bit number to obtain a 32bit product. ; Input: DEHL = 32bit Addend ; BC = 16bit Addend ; Output:DEHL = 32bit sum. ; ADD3216: ADD HL,BC EX DE,HL LD BC,0 ADC HL,BC EX DE,HL RET ;------------------------------------------------------------------------------- ; END OF UTILITIES ;------------------------------------------------------------------------------- ; CPM Boot code. ; ; When CPM is loaded by TZFS it will execute startup code at this vector in 64K block 0. Once the memory mode ; switch executed then this block is paged in, so the follow on instructions must be in place otherwise the CPU ; will go into the wilderness! ; IF $ > 01108H ERROR "CMT comment area not aligned, needed for CPM bootstrap. Addr=%s, required=%s"; % $, 01108H ENDIF ALIGN_NOPS 01108H BOOTSTG1: LD A,TZMM_CPM2 OUT (MMCFG), A JP QBOOT_ ; Method to reboot the machine back into TZFS. This code exists both in memory mode 2 and 7 at the same location so when a switch is made from ; mode 7 to mode 2, the code continues. REBOOT: LD A,TZMM_TZFS OUT (MMCFG),A ; Switch machine back to default state. IF BUILD_VIDEOMODULE = 1 IN A,(VMCTRL) ; Get current display mode. AND ~MODE_80CHAR ; Disable 80 char display. OUT (VMCTRL),A ; Activate. ; LD A, SYSMODE_MZ80A ; Set bus and default CPU speed to 2MHz ; OUT (SYSCTRL),A ; Activate. ELSE ; Change to 40 character mode on the 40/80 Char Colour board v1.0. LD (DSPCTL), A LD HL,DSPCTL ; Setup address of display control register latch. LD A, 0 ; 40 char mode. LD E,(HL) ; Dummy operation to enable latch write via multivibrator. LD (HL), A ENDIF ; JP MROMADDR ; Now restart in the SA1510 monitor. ALIGN_NOPS 01200H ;------------------------------------------------------------------------------- ; START OF KEYBOARD FUNCTIONALITY (INTR HANDLER SEPERATE IN CBIOS) ;------------------------------------------------------------------------------- MODE: LD HL,KEYPF LD (HL),08AH LD (HL),007H ; Set Motor to Off. LD (HL),004H ; Disable interrupts by setting INTMSK to 0. LD (HL),001H ; Set VGATE to 1. RET ; Method to check if a key has been pressed and stored in buffer.. CHKKY: LD A, (KEYCOUNT) OR A JR Z,CHKKY2 LD A,0FFH RET CHKKY2: XOR A RET GETKY: PUSH HL LD A,(KEYCOUNT) OR A JR Z,GETKY2 GETKY1: DI ; Disable interrupts, we dont want a race state occurring. LD A,(KEYCOUNT) DEC A ; Take 1 off the total count as we are reading a character out of the buffer. LD (KEYCOUNT),A LD HL,(KEYREAD) ; Get the position in the buffer where the next available character resides. LD A,(HL) ; Read the character and save. PUSH AF INC L ; Update the read pointer and save. LD A,L AND KEYBUFSIZE-1 LD L,A LD (KEYREAD),HL POP AF EI ; Interrupts back on so keys and RTC are actioned. JR PRCKEY ; Process the key, action any non ASCII keys. ; GETKY2: LD A,(KEYCOUNT) ; No key available so loop until one is. OR A JR Z,GETKY2 JR GETKY1 ; PRCKEY: CP CR ; CR JR NZ,PRCKY3 JR PRCKYE PRCKY3: CP HOMEKEY ; HOME JR NZ,PRCKY4 JR GETKY2 PRCKY4: CP CLRKEY ; CLR JR NZ,PRCKY5 JR GETKY2 PRCKY5: CP INSERT ; INSERT JR NZ,PRCKY6 JR GETKY2 PRCKY6: CP DBLZERO ; 00 JR NZ,PRCKY7 LD A,'0' LD (KEYBUF),A ; Place a character into the keybuffer so we double up on 0 JR PRCKYX PRCKY7: CP BREAKKEY ; Break key processing. JR NZ,PRCKY8 JR PRCKYE PRCKY8: CP DELETE JR NZ,PRCKYX LD A,BACKS ; Map DELETE to BACKSPACE, BACKSPACE is Rubout, DELETE is echo in CPM. PRCKYX: PRCKYE: POP HL RET ;------------------------------------------------------------------------------- ; END OF KEYBOARD FUNCTIONALITY ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; START OF SCREEN FUNCTIONALITY ;------------------------------------------------------------------------------- ; CR PAGE MODE1 .CR: CALL .MANG RRCA JP NC,CURS2 LD L,000H INC H CP ROW - 1 ; End of line? JR Z,.CP1 INC H JP CURS1 .CP1: LD (DSPXY),HL ; SCROLLER .SCROL: LD BC,SCRNSZ - COLW ; Scroll COLW -1 lines LD DE,SCRN ; Start of the screen. LD HL,SCRN + COLW ; Start of screen + 1 line. PUSH BC ; 1000 STORE LDIR POP BC PUSH DE LD DE,SCRN + 800H ; COLOR RAM SCROLL LD HL,SCRN + 800H + COLW ; SCROLL TOP + 1 LINE LDIR LD B,COLW ; ONE LINE EX DE,HL IF BUILD_80C = 0 LD A,071H ; Black background, white characters. Bit 7 is clear as a write to bit 7 @ DFFFH selects 40Char mode. ELSE LD A,071H ; Blue background, white characters in colour mode. Bit 7 is set as a write to bit 7 @ DFFFH selects 80Char mode. ENDIF ;LD A,71H ; COLOR RAM INITIAL DATA CALL DINT POP HL LD B,COLW CALL CLER ; LAST LINE CLEAR LD BC,ROW + 1 ; ROW NUMBER+1 LD DE,MANG ; LOGICAL MANAGEMENT LD HL,MANG+1 LDIR LD (HL),0 LD A,(MANG) OR A JP Z,RSTR LD HL,DSPXY+1 DEC (HL) JR .SCROL DPCT: PUSH AF ; Display control, character is mapped to a function call. PUSH BC PUSH DE PUSH HL LD B,A AND 0F0H CP 0C0H JP NZ,RSTR XOR B RLCA LD C,A LD B,000H LD HL,.CTBL DPCT1: ADD HL,BC LD E,(HL) INC HL LD D,(HL) EX DE,HL JP (HL) PRT: LD A,C CALL ADCN LD C,A AND 0F0H CP 0F0H RET Z CP 0C0H LD A,C JR NZ,PRNT3 PRNT5: CALL DPCT CP 0C3H JR Z,PRNT4 CP 0C5H JR Z,PRNT2 CP 0CDH ; CR JR Z,PRNT2 CP 0C6H RET NZ PRNT2: XOR A PRNT2A: LD (DPRNT),A RET PRNT3: CALL DSP PRNT4: LD A,(DPRNT) INC A CP COLW*2 ; 050H JR C,PRNT4A SUB COLW*2 ; 050H PRNT4A: JR PRNT2A NL: LD A,(DPRNT) OR A RET Z LTNL: LD A,0CDH JR PRNT5 PRTT: CALL PRTS LD A,(DPRNT) OR A RET Z L098C: SUB 00AH JR C,PRTT JR NZ,L098C RET ; Function to disable the cursor display. ; CURSOROFF: DI CALL CURSRSTR ; Restore character under the cursor. LD HL,FLASHCTL ; Indicate cursor is now off. RES 7,(HL) EI RET ; ; Function to enable the cursor display. ; CURSORON: DI CALL DSPXYTOADDR ; Update the screen address for where the cursor should appear. LD HL,FLASHCTL ; Indicate cursor is now on. SET 7,(HL) EI RET ; ; Function to restore the character beneath the cursor iff the cursor is being dislayed. ; CURSRSTR: PUSH HL PUSH AF LD HL,FLASHCTL ; Check to see if there is a cursor at the current screen location. BIT 6,(HL) JR Z,CURSRSTR1 RES 6,(HL) LD HL,(DSPXYADDR) ; There is so we must restore the original character before further processing. LD A,(FLASH) LD (HL),A CURSRSTR1: POP AF POP HL RET ; ; Function to convert XY co-ordinates to a physical screen location and save. ; DSPXYTOADDR:PUSH HL PUSH DE PUSH BC LD BC,(DSPXY) ; Calculate the new cursor position based on the XY coordinates. LD DE,COLW LD HL,SCRN - COLW DSPXYTOA1: ADD HL,DE DEC B JP P,DSPXYTOA1 LD B,000H ADD HL,BC RES 3,H LD (DSPXYADDR),HL ; Store the new address. LD A,(HL) ; Store the new character. LD (FLASH),A DSPXYTOA2: POP BC POP DE POP HL RET ; ; Function to print a space. ; PRTS: LD A,020H ; Function to print a character to the screen. If the character is a control code it is processed as necessary ; otherwise the character is converted from ASCII display and displayed. ; PRNT: DI CALL CURSRSTR ; Restore char under cursor. CP 00DH JR Z,NEWLINE CP 00AH JR Z,NEWLINE CP 07FH JR Z,DELCHR CP BACKS JR Z,DELCHR PUSH BC LD C,A LD B,A CALL PRT LD A,B POP BC PRNT1: CALL DSPXYTOADDR EI RET ; Delete a character on screen. DELCHR: LD A,0C7H CALL DPCT JR PRNT1 NEWLINE: CALL NL JR PRNT1 ; ; ; Function to print out the contents of HL as 4 digit Hexadecimal. ; PRTHL: LD A,H CALL PRTHX LD A,L JR PRTHX RET ; ; Function to print out the contents of A as 2 digit Hexadecimal ; PRTHX: PUSH AF RRCA RRCA RRCA RRCA CALL ASC CALL PRNT POP AF CALL ASC JP PRNT ASC: AND 00FH CP 00AH JR C,NOADD ADD A,007H NOADD: ADD A,030H RET ;CLR8Z: XOR A ; LD BC,00800H ; PUSH DE ; LD D,A ;L09E8: LD (HL),D ; INC HL ; DEC BC ; LD A,B ; OR C ; JR NZ,L09E8 ; POP DE ; RET REV: LD HL,REVFLG LD A,(HL) OR A CPL LD (HL),A JR Z,REV1 LD A,(INVDSP) JR REV2 REV1: LD A,(NRMDSP) REV2: JP RSTR .MANG: LD HL,MANG .MANG2: LD A,(DSPXY + 1) ADD A,L LD L,A LD A,(HL) INC HL RL (HL) OR (HL) RR (HL) RRCA EX DE,HL LD HL,(DSPXY) RET L09C7: PUSH DE PUSH HL LD HL,PBIAS XOR A RLD LD D,A LD E,(HL) RRD XOR A RR D RR E LD HL,SCRN ADD HL,DE LD (PAGETP),HL POP HL POP DE RET DSP: PUSH AF PUSH BC PUSH DE PUSH HL LD B,A CALL PONT LD (HL),B LD HL,(DSPXY) LD A,L DSP01: CP COLW - 1 ; End of line. JP NZ,CURSR CALL .MANG JR C,CURSR .DSP03: EX DE,HL LD (HL),001H INC HL LD (HL),000H JP CURSR CURSD: LD HL,(DSPXY) LD A,H CP ROW - 1 JR Z,CURS4 INC H CURS1: ;CALL MGP.I CURS3: LD (DSPXY),HL JR RSTR CURSU: LD HL,(DSPXY) LD A,H OR A JR Z,CURS5 DEC H CURSU1: JR CURS3 CURSR: LD HL,(DSPXY) LD A,L CP COLW - 1 ; End of line JR NC,CURS2 INC L JR CURS3 CURS2: LD L,000H INC H LD A,H CP ROW JR C,CURS1 LD H,ROW - 1 LD (DSPXY),HL CURS4: JP .SCROL CURSL: LD HL,(DSPXY) LD A,L OR A JR Z,CURS5A DEC L JR CURS3 CURS5A: LD L,COLW - 1 ; End of line DEC H JP P,CURSU1 LD H,000H LD (DSPXY),HL CURS5: JR RSTR CLRS: LD HL,MANG LD B,01BH CALL CLER LD HL,SCRN PUSH HL CALL CLR8Z IF BUILD_80C = 0 LD A,071H ; Black background, white characters. Bit 7 is clear as a write to bit 7 @ DFFFH selects 40Char mode. ELSE LD A,071H ; Blue background, white characters in colour mode. Bit 7 is set as a write to bit 7 @ DFFFH selects 80Char mode. ENDIF ;LD A,71H ; COLOR DATA CALL CLR8 ; D800H-DFFFH CLEAR POP HL CLRS1: LD A,(SCLDSP) HOM0: LD HL,00000H JP CURS3 RSTR: POP HL RSTR1: POP DE POP BC POP AF RET DEL: LD HL,(DSPXY) LD A,H OR L JR Z,RSTR LD A,L OR A JR NZ,DEL1 CALL .MANG JR C,DEL1 CALL PONT DEC HL LD (HL),000H JR CURSL DEL1: CALL .MANG RRCA LD A,COLW JR NC,L0F13 RLCA L0F13: SUB L LD B,A CALL PONT PUSH HL POP DE DEC DE SET 4,D DEL2: RES 3,H RES 3,D LD A,(HL) LD (DE),A INC HL INC DE DJNZ DEL2 DEC HL LD (HL),000H JP CURSL INST: CALL .MANG RRCA LD L,COLW - 1 ; End of line LD A,L JR NC,INST1A INC H INST1A: CALL PNT1 PUSH HL LD HL,(DSPXY) JR NC,INST2 LD A,(COLW*2)-1 ; 04FH INST2: SUB L LD B,A POP DE LD A,(DE) OR A JR NZ,RSTR CALL PONT LD A,(HL) LD (HL),000H INST1: INC HL RES 3,H LD E,(HL) LD (HL),A LD A,E DJNZ INST1 JR RSTR PONT: LD HL,(DSPXY) PNT1: PUSH AF PUSH BC PUSH DE PUSH HL POP BC LD DE,COLW LD HL,SCRN - COLW PNT2: ADD HL,DE DEC B JP P,PNT2 LD B,000H ADD HL,BC RES 3,H POP DE POP BC POP AF RET CLER: XOR A JR DINT CLRFF: LD A,0FFH DINT: LD (HL),A INC HL DJNZ DINT RET ADCN: PUSH BC PUSH HL LD HL,ATBL ;00AB5H LD C,A LD B,000H ADD HL,BC LD A,(HL) JR DACN3 DACN: PUSH BC PUSH HL PUSH DE LD HL,ATBL LD D,H LD E,L LD BC,00100H CPIR JR Z,DACN1 LD A,0F0H DACN2: POP DE DACN3: POP HL POP BC RET DACN1: OR A DEC HL SBC HL,DE LD A,L JR DACN2 ; CTBL PAGE MODE1 .CTBL: DW .SCROL DW CURSD DW CURSU DW CURSR DW CURSL DW HOM0 DW CLRS DW DEL DW INST DW RSTR DW RSTR DW RSTR DW REV DW .CR DW RSTR DW RSTR ; ASCII TO DISPLAY CODE TABLE ATBL: DB 0CCH ; NUL '\0' (null character) DB 0E0H ; SOH (start of heading) DB 0F2H ; STX (start of text) DB 0F3H ; ETX (end of text) DB 0CEH ; EOT (end of transmission) DB 0CFH ; ENQ (enquiry) DB 0F6H ; ACK (acknowledge) DB 0F7H ; BEL '\a' (bell) DB 0F8H ; BS '\b' (backspace) DB 0F9H ; HT '\t' (horizontal tab) DB 0FAH ; LF '\n' (new line) DB 0FBH ; VT '\v' (vertical tab) DB 0FCH ; FF '\f' (form feed) DB 0FDH ; CR '\r' (carriage ret) DB 0FEH ; SO (shift out) DB 0FFH ; SI (shift in) DB 0E1H ; DLE (data link escape) DB 0C1H ; DC1 (device control 1) DB 0C2H ; DC2 (device control 2) DB 0C3H ; DC3 (device control 3) DB 0C4H ; DC4 (device control 4) DB 0C5H ; NAK (negative ack.) DB 0C6H ; SYN (synchronous idle) DB 0E2H ; ETB (end of trans. blk) DB 0E3H ; CAN (cancel) DB 0E4H ; EM (end of medium) DB 0E5H ; SUB (substitute) DB 0E6H ; ESC (escape) DB 0EBH ; FS (file separator) DB 0EEH ; GS (group separator) DB 0EFH ; RS (record separator) DB 0F4H ; US (unit separator) DB 000H ; SPACE DB 061H ; ! DB 062H ; " DB 063H ; # DB 064H ; $ DB 065H ; % DB 066H ; & DB 067H ; ' DB 068H ; ( DB 069H ; ) DB 06BH ; * DB 06AH ; + DB 02FH ; , DB 02AH ; - DB 02EH ; . DB 02DH ; / DB 020H ; 0 DB 021H ; 1 DB 022H ; 2 DB 023H ; 3 DB 024H ; 4 DB 025H ; 5 DB 026H ; 6 DB 027H ; 7 DB 028H ; 8 DB 029H ; 9 DB 04FH ; : DB 02CH ; ; DB 051H ; < DB 02BH ; = DB 057H ; > DB 049H ; ? DB 055H ; @ DB 001H ; A DB 002H ; B DB 003H ; C DB 004H ; D DB 005H ; E DB 006H ; F DB 007H ; G DB 008H ; H DB 009H ; I DB 00AH ; J DB 00BH ; K DB 00CH ; L DB 00DH ; M DB 00EH ; N DB 00FH ; O DB 010H ; P DB 011H ; Q DB 012H ; R DB 013H ; S DB 014H ; T DB 015H ; U DB 016H ; V DB 017H ; W DB 018H ; X DB 019H ; Y DB 01AH ; Z DB 052H ; [ DB 059H ; \ '\\' DB 054H ; ] DB 0BEH ; ^ DB 03CH ; _ DB 0C7H ; ` DB 081H ; a DB 082H ; b DB 083H ; c DB 084H ; d DB 085H ; e DB 086H ; f DB 087H ; g DB 088H ; h DB 089H ; i DB 08AH ; j DB 08BH ; k DB 08CH ; l DB 08DH ; m DB 08EH ; n DB 08FH ; o DB 090H ; p DB 091H ; q DB 092H ; r DB 093H ; s DB 094H ; t DB 095H ; u DB 096H ; v DB 097H ; w DB 098H ; x DB 099H ; y DB 09AH ; z DB 0BCH ; { DB 080H ; | DB 040H ; } DB 0A5H ; ~ DB 0C0H ; DEL DB 040H DB 0BDH DB 09DH DB 0B1H DB 0B5H DB 0B9H DB 0B4H DB 09EH DB 0B2H DB 0B6H DB 0BAH DB 0BEH DB 09FH DB 0B3H DB 0B7H DB 0BBH DB 0BFH DB 0A3H DB 085H DB 0A4H DB 0A5H DB 0A6H DB 094H DB 087H DB 088H DB 09CH DB 082H DB 098H DB 084H DB 092H DB 090H DB 083H DB 091H DB 081H DB 09AH DB 097H DB 093H DB 095H DB 089H DB 0A1H DB 0AFH DB 08BH DB 086H DB 096H DB 0A2H DB 0ABH DB 0AAH DB 08AH DB 08EH DB 0B0H DB 0ADH DB 08DH DB 0A7H DB 0A8H DB 0A9H DB 08FH DB 08CH DB 0AEH DB 0ACH DB 09BH DB 0A0H DB 099H DB 0BCH DB 0B8H DB 080H DB 03BH DB 03AH DB 070H DB 03CH DB 071H DB 05AH DB 03DH DB 043H DB 056H DB 03FH DB 01EH DB 04AH DB 01CH DB 05DH DB 03EH DB 05CH DB 01FH DB 05FH DB 05EH DB 037H DB 07BH DB 07FH DB 036H DB 07AH DB 07EH DB 033H DB 04BH DB 04CH DB 01DH DB 06CH DB 05BH DB 078H DB 041H DB 035H DB 034H DB 074H DB 030H DB 038H DB 075H DB 039H DB 04DH DB 06FH DB 06EH DB 032H DB 077H DB 076H DB 072H DB 073H DB 047H DB 07CH DB 053H DB 031H DB 04EH DB 06DH DB 048H DB 046H DB 07DH DB 044H DB 01BH DB 058H DB 079H DB 042H DB 060H DB 0FDH DB 0CBH DB 000H DB 01EH ;------------------------------------------------------------------------------- ; END OF SCREEN FUNCTIONALITY ;------------------------------------------------------------------------------- ;---------------------------------------- ; ; ANSI EMULATION ; ; Emulate the Ansi standard ; N.B. Turned on when Chr ; 27 recieved. ; Entry - A = Char ; Exit - None ; Used - None ; ;---------------------------------------- ANSITERM: PUSH HL PUSH DE PUSH BC PUSH AF LD C,A ; Move character into C for safe keeping ; LD A,(ANSIMODE) OR A JR NZ,ANSI2 LD A,C CP 27 JP NZ,NOTANSI ; If it is Chr 27 then we haven't just ; been turned on, so don't bother with ; all the checking. LD A,1 ; Turn on. LD (ANSIMODE),A JP AnsiMore ANSI2: LD A,(CHARACTERNO) ; CHARACTER number in sequence OR A ; Is this the first character? JP Z,AnsiFirst ; Yes, deal with this strange occurance! LD A,C ; Put character back in C to check CP ";" ; Is it a semi colon? JP Z,AnsiSemi CP "0" ; Is it a number? JR C,ANSI_NN ; If <0 then no CP "9"+1 ; If >9 then no JP C,AnsiNumber ANSI_NN: CP "?" ; Simple trap for simple problem! JP Z,AnsiMore CP "@" ; Is it a letter? JP C,ANSIEXIT ; Abandon if not letter; something wrong ANSIFOUND: CALL CURSRSTR ; Restore any character under the cursor. LD HL,(NUMBERPOS) ; Get value of number buffer LD A,(HAVELOADED) ; Did we put anything in this byte? OR A JR NZ,AF1 LD (HL),255 ; Mark the fact that nothing was put in AF1: INC HL LD A,254 LD (HL),A ; Mark end of sequence (for unlimited length sequences) ;Disable cursor as unwanted side effects such as screen flicker may occur. LD A,(FLASHCTL) BIT 7,A CALL NZ,CURSOROFF XOR A LD (CURSORCOUNT),A ; Restart count LD A,0C9h LD (CHGCURSMODE),A ; Disable flashing temp. LD HL,NUMBERBUF ; For the routine called. LD A,C ; Restore number ; ; Now work out what happens... ; CP "A" ; Check for supported Ansi characters JP Z,CUU ; Upwards CP "B" JP Z,CUD ; Downwards CP "C" JP Z,CUF ; Forward CP "D" JP Z,CUB ; Backward CP "H" JP Z,CUP ; Locate CP "f" JP Z,HVP ; Locate CP "J" JP Z,ED ; Clear screen CP "m" JP Z,SGR ; Set graphics renditon CP "K" JP Z,EL ; Clear to end of line CP "s" JP Z,SCP ; Save the cursor position CP "u" JP Z,RCP ; Restore the cursor position ANSIEXIT: CALL CURSORON ; If t LD HL,NUMBERBUF ; Numbers buffer position LD (NUMBERPOS),HL XOR A LD (CHARACTERNO),A ; Next time it runs, it will be the ; first character LD (HAVELOADED),A ; We haven't filled this byte! LD (CHGCURSMODE),A ; Cursor allowed back again! XOR A LD (ANSIMODE),A JR AnsiMore NOTANSI: CP 000h ; Filter unprintable characters. JR Z,AnsiMore CALL PRNT AnsiMore: POP AF POP BC POP DE POP HL RET ; ; The various routines needed to handle the filtered characters ; AnsiFirst: LD A,255 LD (CHARACTERNO),A ; Next character is not first! LD A,C ; Get character back LD (ANSIFIRST),A ; Save first character to check later CP "(" ; ( and [ have characters to follow JP Z,AnsiMore ; and are legal. CP "[" JP Z,AnsiMore CP 09Bh ; CSI JP Z,AnsiF1 ; Pretend that "[" was first ;-) JP ANSIEXIT ; = and > don't have anything to follow ; them but are legal. ; Others are illegal, so abandon anyway. AnsiF1: LD A,"[" ; Put a "[" for first character LD (ANSIFIRST),A JP ANSIEXIT AnsiSemi: LD HL,(NUMBERPOS) ; Move the number pointer to the LD A,(HAVELOADED) ; Did we put anything in this byte? OR A JR NZ,AS1 LD (HL),255 ; Mark the fact that nothing was put in AS1: INC HL ; move to next byte LD (NUMBERPOS),HL XOR A LD (HAVELOADED),A ; New byte => not filled! JP AnsiMore AnsiNumber: LD HL,(NUMBERPOS) ; Get address for number LD A,(HAVELOADED) OR A ; If value is zero JR NZ,AN1 LD A,C ; Get value into A SUB "0" ; Remove ASCII offset LD (HL),A ; Save and Exit LD A,255 LD (HAVELOADED),A ; Yes, we _have_ put something in! JP AnsiMore AN1: LD A,(HL) ; Stored value in A; TBA in C ADD A,A ; 2 * LD D,A ; Save the 2* for later ADD A,A ; 4 * ADD A,A ; 8 * ADD A,D ; 10 * ADD A,C ; 10 * + new num SUB "0" ; And remove offset from C value! LD (HL),A ; Save and Exit. JP AnsiMore ; Note routine will only work up to 100 ; which should be okay for this application. ;-------------------------------- ; GET NUMBER ; ; Gets the next number from ; the list ; ; Entry - HL = address to get ; from ; Exit - HL = next address ; A = value ; IF a=255 then default value ; If a=254 then end of sequence ; Used - None ;-------------------------------- GetNumber: LD A,(HL) ; Get number CP 254 RET Z ; Return if end of sequence,ie still point to ; end INC HL ; Return pointing to next byte RET ; Else next address and return ;*** ANSI UP ; CUU: CALL GetNumber ; Number into A LD B,A ; Save value into B CP 255 JR NZ,CUUlp LD B,1 ; Default value CUUlp: LD A,(DSPXY+1) ; A <- Row CP B ; Is it too far? JR C,CUU1 SUB B ; No, then go back that far. JR CUU2 CUU1: LD A,0 ; Make the choice, top line. CUU2: LD (DSPXY+1),A ; Row <- A JP ANSIEXIT ;*** ANSI DOWN ; CUD: LD A,(ANSIFIRST) CP "[" JP NZ,ANSIEXIT ; Ignore ESC(B CALL GetNumber LD B,A ; Save value in b CP 255 JR NZ,CUDlp LD B,1 ; Default CUDlp: LD A,(DSPXY+1) ; A <- Row ADD A,B CP ROW ; Too far? JP C,CUD1 LD A,ROW-1 ; Too far then bottom of screen CUD1: LD (DSPXY+1),A ; Row <- A JP ANSIEXIT ;*** ANSI RIGHT ; CUF: CALL GetNumber ; Number into A LD B,A ; Value saved in B CP 255 JR NZ,CUFget LD B,1 ; Default CUFget: LD A,(DSPXY) ; A <- Column ADD A,B ; Add movement. CP 80 ; Too far? JR C,CUF2 LD A,79 ; Yes, right edge CUF2: LD (DSPXY),A ; Column <- A JP ANSIEXIT ;*** ANSI LEFT ; CUB: CALL GetNumber ; Number into A LD B,A ; Save value in B CP 255 JR NZ,CUBget LD B,1 ; Default CUBget: LD A,(DSPXY) ; A <- Column CP B ; Too far? JR C,CUB1a SUB B JR CUB1b CUB1a: LD A,0 CUB1b: LD (DSPXY),A ; Column <-A JP ANSIEXIT ;*** ANSI LOCATE ; HVP: CUP: CALL GetNumber CP 255 CALL Z,DefaultLine ; Default = 1 CP 254 ; Sequence End -> 1 CALL Z,DefaultLine CP ROW+1 ; Out of range then don't move JP NC,ANSIEXIT OR A CALL Z,DefaultLine ; 0 means default, some strange reason LD D,A CALL GetNumber CP 255 ; Default = 1 CALL Z,DefaultColumn CP 254 ; Sequence End -> 1 CALL Z,DefaultColumn CP 81 ; Out of range, then don't move JP NC,ANSIEXIT OR A CALL Z,DefaultColumn ; 0 means go with default LD E,A EX DE,HL DEC H ; Translate from Ansi co-ordinates to hardware DEC L ; co-ordinates LD (DSPXY),HL ; Set the cursor position. JP ANSIEXIT DefaultColumn: DefaultLine:LD A,1 RET ;*** ANSI CLEAR SCREEN ; ED: CALL GetNumber OR A JP Z,ED1 ; Zero means first option CP 254 ; Also default JP Z,ED1 CP 255 JP Z,ED1 CP 1 JP Z,ED2 CP 2 JP NZ,ANSIEXIT ;*** Option 2 ; ED3: LD HL,0 LD (DSPXY),HL ; Home the cursor LD A,(JSW_FF) OR A JP NZ,ED_Set_LF CALL CALCSCADDR CALL CLRSCRN JP ANSIEXIT ED_Set_LF: XOR A ; Note simply so that LD (JSW_LF),A ; ESC[2J works the same as CTRL-L JP ANSIEXIT ;*** Option 0 ; ED1: LD HL,(DSPXY) ; Get and save cursor position LD A,H OR L JP Z,ED3 ; If we are at the top of the ; screen and clearing to the bottom ; then we are clearing all the screen! PUSH HL LD A,ROW-1 SUB H ; ROW - Row LD HL,0 ; Zero start OR A ; Do we have any lines to add? JR Z,ED1_2 ; If no bypass that addition! LD B,A ; Number of lines to count LD DE,80 ED1_1: ADD HL,DE DJNZ ED1_1 ED1_2: EX DE,HL ; Value into DE POP HL LD A,80 SUB L ; 80 - Columns LD L,A ; Add to value before LD H,0 ADD HL,DE PUSH HL ; Value saved for later LD HL,(DSPXY) ; _that_ value again! POP BC ; Number to blank CALL CALCSCADDR CALL CLRSCRN ; Now do it! JP ANSIEXIT ; Then exit properly ;*** Option 1 - clear from cursor to beginning of screen ; ED2: LD HL,(DSPXY) ; Get and save cursor position PUSH HL LD A,H LD HL,0 ; Zero start OR A ; Do we have any lines to add? JR Z,ED2_2 ; If no bypass that addition! LD B,A ; Number of lines LD DE,80 ED2_1: ADD HL,DE DJNZ ED2_1 ED2_2: EX DE,HL ; Value into DE POP HL LD H,0 ADD HL,DE PUSH HL ; Value saved for later LD HL,0 ; Find the begining! POP BC ; Number to blank CALL CLRSCRN ; Now do it! JP ANSIEXIT ; Then exit properly ; *** ANSI CLEAR LINE ; EL: CALL GetNumber ; Get value CP 0 JP Z,EL1 ; Zero & Default are the same CP 255 JP Z,EL1 CP 254 JP Z,EL1 CP 1 JP Z,EL2 CP 2 JP NZ,ANSIEXIT ; Otherwise don't do a thing ;*** Option 2 - clear entire line. ; LD HL,(DSPXY) LD L,0 LD (DSPXY),HL CALL CALCSCADDR LD BC,80 ; 80 bytes to clear (whole line) CALL CLRSCRN JP ANSIEXIT ;*** Option 0 - Clear from Cursor to end of line. ; EL1: LD HL,(DSPXY) LD A,80 ; Calculate distance to end of line SUB L LD C,A LD B,0 LD (DSPXY),HL PUSH HL POP DE CALL CALCSCADDR CALL CLRSCRN JP ANSIEXIT ;*** Option 1 - clear from cursor to beginning of line. ; EL2: LD HL,(DSPXY) LD C,L ; BC = distance from start of line LD B,0 LD L,0 LD (DSPXY),HL CALL CALCSCADDR CALL CLRSCRN JP ANSIEXIT ; In HL = XY Pos ; Out = Screen address. CALCSCADDR: PUSH AF PUSH BC PUSH DE PUSH HL LD A,H LD B,H LD C,L LD HL,SCRN OR A JR Z,CALC3 LD DE,80 CALC2: ADD HL,DE DJNZ CALC2 CALC3: POP DE ADD HL,BC POP DE POP BC POP AF RET ; HL = address ; BC = length CLRSCRN: PUSH HL ; 1 for later! LD D,H LD E,L INC DE ; DE <- HL +1 PUSH BC ; Save the value a little longer! XOR A LD (HL), A ; Blank this area! LDIR ; *** just like magic *** ; only I forgot it in 22a! POP BC ; Restore values POP HL LD DE,2048 ; Move to attributes block ADD HL,DE LD D,H LD E,L INC DE ; DE = HL + 1 LD A,(FONTSET) ; Save in the current values. LD (HL),A LDIR RET ;*** ANSI SET GRAPHICS RENDITION ; SGR: CALL GetNumber CP 254 ; 254 signifies end of sequence JP Z,ANSIEXIT OR A CALL Z,AllOff CP 255 ; Default means all off CALL Z,AllOff CP 1 CALL Z,BoldOn CP 2 CALL Z,BoldOff CP 4 CALL Z,UnderOn CP 5 CALL Z,ItalicOn CP 6 CALL Z,ItalicOn CP 7 CALL Z,InverseOn JP SGR ; Code is re-entrant ;-------------------------------- ; ; RESET GRAPHICS ; ; Entry - None ; Exit - None ; Used - None ;-------------------------------- AllOff: PUSH AF ; Save registers LD A,0C9h ; = off LD (BOLDMODE),A ; Turn the flags off LD (ITALICMODE),A LD (UNDERSCMODE),A LD (INVMODE),A LD A,017h ; Black background, white chars. LD (FONTSET),A ; Reset the bit map store POP AF ; Restore register RET ;-------------------------------- ; ; TURN BOLD ON ; ; Entry - None ; Exit - None ; Used - None ;-------------------------------- BoldOn: PUSH AF ; Save register XOR A ; 0 means on LD (BOLDMODE),A BOn1: LD A,(FONTSET) SET 0,A ; turn ON indicator flag LD (FONTSET),A POP AF ; Restore register RET ;-------------------------------- ; ; TURN BOLD OFF ; ; Entry - None ; Exit - None ; Used - None ;-------------------------------- BoldOff: PUSH AF ; Save register PUSH BC LD A,0C9h ; &C9 means off LD (BOLDMODE),A BO1: LD A,(FONTSET) RES 0,A ; turn OFF indicator flag LD (FONTSET),A POP BC POP AF ; Restore register RET ;-------------------------------- ; ; TURN ITALICS ON ; (replaces flashing) ; Entry - None ; Exit - None ; Used - None ;-------------------------------- ItalicOn: PUSH AF ; Save AF XOR A LD (ITALICMODE),A ; 0 means on LD A,(FONTSET) SET 1,A ; turn ON indicator flag LD (FONTSET),A POP AF ; Restore register RET ;-------------------------------- ; ; TURN UNDERLINE ON ; ; Entry - None ; Exit - None ; Used - None ;-------------------------------- UnderOn: PUSH AF ; Save register XOR A ; 0 means on LD (UNDERSCMODE),A LD A,(FONTSET) SET 2,A ; turn ON indicator flag LD (FONTSET),A POP AF ; Restore register RET ;-------------------------------- ; ; TURN INVERSE ON ; ; Entry - None ; Exit - None ; Used - None ;-------------------------------- InverseOn: PUSH AF ; Save register XOR A ; 0 means on LD (INVMODE),A LD A,(FONTSET) SET 3,A ; turn ON indicator flag LD (FONTSET),A POP AF ; Restore AF RET ;*** ANSI SAVE CURSOR POSITION ; SCP: LD HL,(DSPXY) ; (backup) <- (current) LD (CURSORPSAV),HL JP ANSIEXIT ;*** ANSI RESTORE CURSOR POSITION ; RCP: LD HL,(CURSORPSAV) ; (current) <- (backup) LD (DSPXY),HL JP ANSIEXIT ;------------------------------------------------------------------------------- ; END OF ANSI TERMINAL FUNCTIONALITY ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; START OF STATIC LOOKUP TABLES AND CONSTANTS ;------------------------------------------------------------------------------- ; Disk Parameter Header template. DPBTMPL: DW 0000H, 0000H, 0000H, 0000H, CDIRBUF ;-------------------------------------- ; Test Message table ;-------------------------------------- CBIOSSIGNON:IF BUILD_80C = 1 DB "** CBIOS v1.12, (C) P.D. Smart, 2019-21. Drives:", NUL ELSE DB "CBIOS v1.12, (C) P.D. Smart, 2019-21.", CR DB "Drives:", NUL ENDIF CBIOSIGNEND:IF BUILD_80C = 1 DB " **", CR, NUL ELSE DB CR, NUL ENDIF CPMSIGNON: IF BUILD_80C = 1 DB "CP/M v2.23 (64K) Copyright(c) 1979 by Digital Research", CR, LF, NUL ELSE DB "CP/M v2.23 (c) 1979 by Digital Research", CR, LF, NUL ENDIF SDAVAIL: DB "SD", NUL FDCAVAIL: DB "FDC", NUL NOBDOS: DB "I/O Processor failed to load BDOS, aborting!", CR, LF, NUL SVCRESPERR: DB "I/O Response Error, time out!", CR, NUL SVCIOERR: DB "I/O Error, time out!", CR, NUL LOADERR: DB "DISK ERROR - LOADING", CR, NUL SELDRVMSG: DB "DISK ERROR - SELECT", CR, NUL WAITRDYMSG: DB "DISK ERROR - WAIT", CR, NUL DSKSEEKMSG: DB "DISK ERROR - SEEK", CR, NUL RETRIESMSG: DB "DISK ERROR - RETRIES", CR, NUL DATAOVRMSG: DB "DISK ERROR - DATA OVERRUN", CR, NUL CRCERRMSG: DB "DISK ERROR - CRC ERROR", CR, NUL ;------------------------------------------------------------------------------- ; END OF STATIC LOOKUP TABLES AND CONSTANTS ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; START OF DEBUGGING FUNCTIONALITY ;------------------------------------------------------------------------------- ; Debug routine to print out all registers and dump a section of memory for analysis. ; DEBUG: IF ENADEBUG = 1 LD (DBGSTACKP),SP LD SP,DBGSTACK ; PUSH AF PUSH BC PUSH DE PUSH HL ; PUSH AF PUSH HL PUSH DE PUSH BC PUSH AF LD DE, INFOMSG CALL MONPRTSTR POP BC LD A,B CALL PRTHX LD A,C CALL PRTHX LD DE, INFOMSG2 CALL MONPRTSTR POP BC LD A,B CALL PRTHX LD A,C CALL PRTHX LD DE, INFOMSG3 CALL MONPRTSTR POP DE LD A,D CALL PRTHX LD A,E CALL PRTHX LD DE, INFOMSG4 CALL MONPRTSTR POP HL LD A,H CALL PRTHX LD A,L CALL PRTHX LD DE, INFOMSG5 CALL MONPRTSTR LD HL,(DBGSTACKP) LD A,H CALL PRTHX LD A,L CALL PRTHX CALL NL LD DE, DRVMSG CALL MONPRTSTR LD A, (CDISK) CALL PRTHX LD DE, FDCDRVMSG CALL MONPRTSTR LD A, (FDCDISK) CALL PRTHX LD DE, SEKTRKMSG CALL MONPRTSTR LD BC,(SEKTRK) LD A,B CALL PRTHX LD A,C CALL PRTHX CALL PRTS LD A,(SEKSEC) CALL PRTHX CALL PRTS LD A,(SEKHST) CALL PRTHX LD DE, HSTTRKMSG CALL MONPRTSTR LD BC,(HSTTRK) LD A,B CALL PRTHX LD A,C CALL PRTHX CALL PRTS LD A,(HSTSEC) CALL PRTHX LD DE, UNATRKMSG CALL MONPRTSTR LD BC,(UNATRK) LD A,B CALL PRTHX LD A,C CALL PRTHX CALL PRTS LD A,(UNASEC) CALL PRTHX LD DE, CTLTRKMSG CALL MONPRTSTR LD A,(TRACKNO) ; NB. Track number is 16bit, FDC only uses lower 8bit and assumes little endian read. CALL PRTHX CALL PRTS LD A,(SECTORNO) CALL PRTHX LD DE, DMAMSG CALL MONPRTSTR LD BC,(DMAADDR) LD A,B CALL PRTHX LD A,C CALL PRTHX CALL NL ; POP AF JR C, SKIPDUMP ; LD HL,DPBASE ; Dump the startup vectors. LD DE, 1000H ADD HL, DE EX DE,HL LD HL,DPBASE CALL DUMPX LD HL,00000h ; Dump the startup vectors. LD DE, 00A0H ADD HL, DE EX DE,HL LD HL,00000h CALL DUMPX LD HL,IBUFE ; Dump the data area. LD DE, 0300H ADD HL, DE EX DE,HL LD HL,IBUFE CALL DUMPX LD HL,CBASE ; Dump the CCP + BDOS area. LD DE,CPMBIOS - CBASE ADD HL, DE EX DE,HL LD HL,CBASE CALL DUMPX SKIPDUMP: ;JR SKIPDUMP POP HL POP DE POP BC POP AF ; LD SP,(DBGSTACKP) RET ; HL = Start ; DE = End DUMPX: LD A,10 DUM1: LD (TMPCNT),A DUM3: LD B,010h LD C,02Fh CALL NLPHL DUM2: CALL SPHEX INC HL PUSH AF LD A,(DSPXY) ADD A,C LD (DSPXY),A POP AF CP 020h JR NC,L0D51 LD A,02Eh L0D51: CALL PRNT LD A,(DSPXY) INC C SUB C LD (DSPXY),A DEC C DEC C DEC C PUSH HL SBC HL,DE POP HL JR NC,DUM7 L0D78: DJNZ DUM2 LD A,(TMPCNT) DEC A LD (TMPCNT),A JR NZ,DUM3 DUM4: CALL CHKKY CP 0FFH JR NZ,DUM4 CALL GETKY CP 'D' JR NZ,DUM5 LD A,8 JR DUM1 DUM5: CP 'U' JR NZ,DUM6 PUSH DE LD DE,00100H OR A SBC HL,DE POP DE LD A,8 JR DUM1 DUM6: CP 'X' JR Z,DUM7 JR DUMPX DUM7: CALL NL RET NLPHL: CALL NL CALL PRTHL RET ; SPACE PRINT AND DISP ACC ; INPUT:HL=DISP. ADR. SPHEX: CALL PRTS ; SPACE PRINT LD A,(HL) CALL PRTHX ; DSP OF ACC (ASCII) LD A,(HL) RET ; Debugger messages, bit cryptic but this is due to limited space on the screen. ; DRVMSG: DB "DRV=", 000H FDCDRVMSG: DB ",FDC=", 000H SEKTRKMSG: DB ",S=", 000H HSTTRKMSG: DB ",H=", 000H UNATRKMSG: DB ",U=", 000H CTLTRKMSG: DB ",C=", 000H DMAMSG: DB ",DMA=", 000H INFOMSG: DB "AF=", NUL INFOMSG2: DB ",BC=", 000H INFOMSG3: DB ",DE=", 000H INFOMSG4: DB ",HL=", 000H INFOMSG5: DB ",SP=", 000H ; Seperate stack for the debugger so as not to affect anything it is reporting on. ; DBGSTACKP: DS 2 DS 128, 0AAH DBGSTACK: EQU $ ALIGN 00400H ENDIF ;------------------------------------------------------------------------------- ; END OF DEBUGGING FUNCTIONALITY ;-------------------------------------------------------------------------------