diff --git a/branches/s100/Applegate/Makefile b/branches/s100/Applegate/Makefile new file mode 100644 index 00000000..0f330c29 --- /dev/null +++ b/branches/s100/Applegate/Makefile @@ -0,0 +1,32 @@ +# Makefile 1/31/2013 dwg - + +TASM := ../XSource/bin/TASM +TASMTABS := . +export TASMTABS + +ASMOPT80 := -t$(CPU) -g3 +ASMOPT85 := -t85 -g3 + +ASM80 := $(TASM) $(ASMOPT80) +ASM85 := $(TASM) $(ASMOPT85) +ASMIMG := $(TASM) $(ASMOPT80) -b -fE5 + +all: zapple.bin cpm.bin bios.bin + +tasm80.tab: bin/TASM80.TAB + cp bin/TASM80.TAB tasm80.tab + +tasm85.tab: bin/TASM85.TAB + cp bin/TASM85.TAB tasm85.tab + +cpm.bin: cpm22.asm + $(ASMIMG) $< $@ + +bios.bin: bios.asm s100iobd.inc + $(ASMIMG) bios.asm $@ + +zapple.bin: zapple.asm zapple.z80 + $(ASMIMG) zapple.asm $@ + +clean: + rm -f *.lst *.bin diff --git a/branches/s100/Applegate/bios.asm b/branches/s100/Applegate/bios.asm new file mode 100644 index 00000000..4342ceec --- /dev/null +++ b/branches/s100/Applegate/bios.asm @@ -0,0 +1,1502 @@ +;******************************************************** +; Bob's very simple BIOS. +; +; Work started in, oh, I don't know, probably 2008. + +; Work RE-started on 02/14/2013 +; +; Hardware description: +; +; Northstar ZPU +; CCS 2710 serial port. Console on first port. +; N8VEM 4MB RAM with only 1 MB installed +; N8VEM IDE with a CF card +; +; This is a merge of many pieces of code from all over +; the net, very little of it is mine other than some +; glue logic. So, here are some of the sources of info +; and code I borrowed from: +; +; www.speakeasy.org/~rzh/bios.mac +; http://www.s100computers.com/My%20System%20Pages/IDE%20Board/My%20IDE%20Card.htm +; +; These are some of my include files to make things easier +; for me. +; +#include "tasm.inc" +#include "cpm22.inc" +; +; Put out a bit of diagnostics +; + .echo "Building a BIOS for a RAM size of " + .echo RAMSIZE + .echo "K\n" +; +; Common CP/M items +; +DefaultDisk equ TDRIVE ;I like this name better +BDOSEntry equ (CCPBASE+CCPSIZE+6) +CPM_SECTOR_SIZE equ 128 +; +; Base ports of various I/O devices. +; +IDE_PORT EQU 030H ;IDE board +; +; Base memory locations of various things +; +CCPEntry EQU CCPBASE +CPM_VERSION .equ 22H ;CP/M version +; +; Constants +; +FALSE .equ 0 +TRUE .equ ~FALSE +; +; ASCII constants +; +CR .equ 0dh +LF .equ 0ah +BELL .equ 07h +EOF .equ 01ah ;CTRL-Z is CP/M EOF +; +; IDE board constants +; +IDEportA EQU IDE_PORT+0 ;Lower 8 bits of IDE interface (8255) +IDEportB EQU IDE_PORT+1 ;Upper 8 bits of IDE interface +IDEportC EQU IDE_PORT+2 ;Control lines for IDE interface +IDEportCtrl EQU IDE_PORT+3 ;8255 configuration port + +READcfg8255 EQU 10010010b ;Set 8255 IDEportC out, IDEportA/B input +WRITEcfg8255 EQU 10000000b ;Set all three 8255 ports output + +;IDE control lines for use with IDEportC. Change these 8 +;constants to reflect where each signal of the 8255 each of the +;IDE control signals is connected. All the control signals must +;be on the same port, but these 8 lines let you connect them to +;whichever pins on that port. + +IDEa0line EQU 01H ;direct from 8255 to IDE interface +IDEa1line EQU 02H ;direct from 8255 to IDE interface +IDEa2line EQU 04H ;direct from 8255 to IDE interface +IDEcs0line EQU 08H ;inverter between 8255 and IDE interface +IDEcs1line EQU 10H ;inverter between 8255 and IDE interface +IDEwrline EQU 20H ;inverter between 8255 and IDE interface +IDErdline EQU 40H ;inverter between 8255 and IDE interface +IDErstline EQU 80H ;inverter between 8255 and IDE interface +; +;Symbolic constants for the IDE Drive registers, which makes the +;code more readable than always specifying the address pins + +REGdata EQU IDEcs0line +REGerr EQU IDEcs0line + IDEa0line +REGseccnt EQU IDEcs0line + IDEa1line +REGsector EQU IDEcs0line + IDEa1line + IDEa0line +REGcylinderLSB EQU IDEcs0line + IDEa2line +REGcylinderMSB EQU IDEcs0line + IDEa2line + IDEa0line +REGshd EQU IDEcs0line + IDEa2line + IDEa1line ;(0EH) +REGcommand EQU IDEcs0line + IDEa2line + IDEa1line + IDEa0line ;(0FH) +REGstatus EQU IDEcs0line + IDEa2line + IDEa1line + IDEa0line +REGcontrol EQU IDEcs1line + IDEa2line + IDEa1line +REGastatus EQU IDEcs1line + IDEa2line + IDEa1line + IDEa0line + +;IDE Command Constants. These should never change. + +COMMANDrecal EQU 10H +COMMANDread EQU 20H +COMMANDwrite EQU 30H +COMMANDinit EQU 91H +COMMANDid EQU 0ECH +COMMANDspindown EQU 0E0H +COMMANDspinup EQU 0E1H +; +; +; IDE Status Register: +; bit 7: Busy 1=busy, 0=not busy +; bit 6: Ready 1=ready for command, 0=not ready yet +; bit 5: DF 1=fault occured insIDE drive +; bit 4: DSC 1=seek complete +; bit 3: DRQ 1=data request ready, 0=not ready to xfer yet +; bit 2: CORR 1=correctable error occured +; bit 1: IDX vendor specific +; bit 0: ERR 1=error occured +; +; + +; +; A Z80 JP instruction is manually put into a few locations +; by the BIOS, so here is an equate for that instruction. +; +JMP equ 0c3h + .page +;******************************************************** +; PRIMARY JUMP TABLE. ALL CALLS FROM CP/M TO THE CBIOS +; COME THROUGH THIS TABLE. +;******************************************************** +; + .org BIOSBASE +; + JP CBOOT ;COLD BOOT +WBOOTE: JP WBOOT ;WARM BOOT + JP CONST ;CONSOLE STATUS + JP CONIN ;CONSOLE CHARACTER IN + JP CONOUT ;CONSOLE CHARACTER OUT, + JP LIST ;LIST CHARACTER OUT + JP PUNCH ;PUNCH CHARACTER OUT + JP READER ;READER CHARACTER IN + JP HOME ;MOVE HEAD TO HOME POSITION + JP SELDSK ;SELECT DISK + JP SETTRK ;SET TRACK NUMBER + JP SETSEC ;SET SECTOR NUMBER + JP SETDMA ;SET DMA ADDRESS + JP READ ;READ DISK + JP WRITE ;WRITE DISK + JP LISTST ;RETURN LIST STATUS + JP SECTRA ;SECTOR TRANSLATE +; +;******************************************************** +; Sneaking little Bob trick. My program to write CP/M +; to the disk needs to know the end of the BIOS, so I +; decided to put the last memory location used by the +; BIOS into the two bytes before the CBOOT code. So the +; program can find the end by picking up the first JP +; address in the jump table, and backing up two bytes. +; + DW BIOS_END + .page +; +;******************************************************** +; +; This is the cold boot entry point, called from the first +; entry in the jump table. Control will be transfered here +; by the CP/M bootstrap loader. +; + +CBOOT: ld sp,0080h ;set stack pointer + call initser ;initialize the serial port + call IDEinit ;initialize the IDE interface + + + call msg + .db CR,LF,LF + .text "Bob's BIOS :)" + .db CR,LF + .text "??" + .text "K CP/M v2.2" + .db CR,LF, + .text "Drives:" + .db CR,LF + .text " A: = IDE" + .db CR,LF,LF,0 + +gocpm: di ;disable interrupts +; +; Reset the IOBYTE to go to the main terminal +; + ld a,00000001B + ld (IOBYTE),a +; +; Set the default drive to A: and user to 0 +; + xor a + ld (DefaultDisk),a +; + jp EnterCPM ;continue set-up +; +;******************************************************** +; WARNING... The warm boot code is NOT COMPLETE!!! +; +WBOOT: + call msg + TEXT "Entering WBOOT" + db CR,LF,0 + +; +; Need to add a lot of logic here to reload the CCP and +; BDOS from disk, but for now we know they're already +; loaded and no need to load again. + + + jp EnterCPM +; +;******************************************************** +; +; This routine is entered either from the cold or warm +; boot code. It sets up the JP instructions in the +; base page, and also sets the high-level disk driver's +; input/output address (also known as the DMA address). +; +EnterCPM: ld a,JMP ;Get machine code for JP + ld (0),a ;Set up Jp at location 0 + ld (ENTRY),a ;...and at location 5 +; + ld hl,WBOOTE ;Get BIOS vector address + ld (1),hl ;Put it at location 1 + + ld hl,BDOSEntry ;Get BDOS entry point address + ld (ENTRY+1),hl ;Put it at location 6 +; + ld bc,80H ;Set disk I/O address to default + call SETDMA ;Use normal BIOS routine +; + ei ;Ensure interrupts are enabled + ld a,(DefaultDisk) ;Transfer current default disk to + ld c,a ; Console Command Processor + jp CCPEntry ;Jump to CCP + + + +;******************************************************** +; Select the disk drive for future reads/writes. On entry +; C contains the drive index: C = 0 for A:, 1 for B:, etc. +; On Exit, HL contains the pointer to the disk parameter +; header (section 6.10 in the CP/M book) or 0000 if the +; requested drive is out of range. +;******************************************************** + +SELDSK: push bc + call msg + TEXT "SELDSK: " + db 0 + pop bc + push bc + ld a,c + add a,'A' + ld c,a + call CONOUT + call msg + db ':',CR,LF,0 + pop bc +; +; Temp patch... always do deblocking for now. Some day +; I might add a disk that doesn't need it, but for now +; we always need it. +; + push af + ld a,0ffh + ld (DeblockingRequired),a + pop af +; + ld hl,0 ;assume an error + ld a,c ;move drive into A + cp 1 ;compare to max drives + ret nc ;return if bad drive +; +; Compute offset to disk parameter table... drive * 16 +; + ld l,a + ld h,0 + add hl,hl ;*2 + add hl,hl ;*4 + add hl,hl ;*8 + add hl,hl ;*16 + ld de,DPBASE + add hl,de + ret +; +;******************************************************** +; FLOPPY DISK DPH TABLES. +;******************************************************** +; +; CP/M manual, 6.10, page145 +; +DPBASE: +IdeDPH: dw 0 ;no sector translation + dw 0 ;scratchpad + dw 0 + dw 0 + dw DirectoryBuffer + dw IdeDPB ;disk parameter block + dw IdeCSV ;checksum area + dw IdeALV + +IdeCSV: .FILL 0 ;no directory checksum; non-removable drive +IdeALV: .FILL 256,0 ;drive size / BLS / 8 + +;__________________________________________________________________________________________________ +; +; 8MB HARD DISK DRIVE, 65 TRKS, 1024 SECS/TRK, 128 BYTES/SEC +; BLOCKSIZE (BLS) = 4K, DIRECTORY ENTRIES = 128 +; SEC/TRK ENGINEERED SO THAT AFTER DEBLOCKING, SECTOR NUMBER OCCUPIES 1 BYTE (0-255) +; +; The SPT value was taken from the track count from MYIDE (an Andrew Lynch +; program) and then multiplied by 4 since there are 4 CP/M 128 byte sectors +; in each 512 byte physical sector. +; +; Too many magic numbers in here; add some EQUs for these and use real math +; to compute the values at assembly time. +; + .DB (4096 / 128) ; RECORDS PER BLOCK (BLS / 128) +IdeDPB: +; .DW 1024 ; SPT: SECTORS PER TRACK + .DW 32*4 ; SPT: Sectors per track, gotten from drive + .DB 5 ; BSH: BLOCK SHIFT FACTOR + .DB 31 ; BLM: BLOCK MASK + .DB 1 ; EXM: EXTENT MASK + .DW 2047 ; DSM: TOTAL STORAGE IN BLOCKS - 1 BLK = ((8MB - 128K OFF) / 4K BLS) - 1 = 2047 + .DW 511 ; DRM: DIR ENTRIES - 1 = 512 - 1 = 511 + .DB 11110000B ; AL0: DIR BLK BIT MAP, FIRST BYTE + .DB 00000000B ; AL1: DIR BLK BIT MAP, SECOND BYTE + .DW 0 ; CKS: DIRECTORY CHECK VECTOR SIZE = 256 / 4 + .DW 1 ; OFF: RESERVED TRACKS = 1 TRKS * (512 B/SEC * 1024 SEC/TRK) = 128K +; +DirectoryBuffer ds CPM_SECTOR_SIZE + + +; +; OLD CODE WARNING... +; This is my first attempt to calculate disk parameters. Left it all in +; here because it's the right way to do it, but needs debugging. +; +;SECSZ: equ 512 ;bytes per sector +;CPMSECS: equ (SECSZ/128) ;CP/M records per phys sector +;SECTORS: equ 64 ;sectors per track +;TRACKS: equ (65536/SECTORS/CPMSECS); +; +;SPT: equ (SECTORS*CPMSECS) +;BLS: equ 8192 +;BLM: equ (BLS/128-1) +; #if (BLS == 1024) +;BSH equ 3 +; #endif +; #if (BLS == 2048) +;BSH equ 4 +; #endif +; #if (BLS == 4096) +;BSH equ 5 +; #endif +; #if (BLS == 8192) +;BSH equ 6 +; #endif +; #if (BLS == 16384) +;BSH equ 7 +; #endif +;OFF: equ 1 +;DSM: equ (((TRACKS-OFF)*SECSZ*SECTORS/BLS)-1) +; #if (DSM < 256) +;EXM equ (BLS/1024-1) +; #else +;EXM equ (BLS/2048-1) +; #endif +;DRM: equ (1024-1);is this a good value? +; +;fldpb: .dw SPT ;CP/M records per track +; .db BSH ;allocation block shift +; .db BLM ;allocation block mask +; .db 0 ;extent mask +; .dw DSM ;max allocation block # +; .dw 255 ;max directory entry # +; .db 0f0h,0 ;directory allocation mask +; .dw 64 ;directory check alloc size +; .dw OFF ;number of reserved tracks + +;IdeDPB: dw SECTORS ;sectors per track +; db BSH +; db BLM +; db EXM +; dw DSM +; dw DRM +; db AL0 +; db AL1 +; dw CKS +; dw OFF + + +;dphbase0: .dw trans ;LOGICAL TO PHYSICAL XLATE TAB +; .dw 0 ;SCRATCH +; .dw 0 +; .dw 0 +; .dw DIRBUF ;DIRECTORY BUFFER +; .dw fldpb ;DISK PARAMETER BLOCK +; .dw chk00 ;CHECKSUM VECTOR +; .dw ALV0 ;ALLOCATION VECTOR +; +;dphbase1: .dw trans +; .dw 0 +; .dw 0 +; .dw 0 +; .dw DIRBUF +; .dw fldpb +; .dw chk01 +; .dw ALV1 +; +;******************************************************** +; Sector translation table. The IDE drive does not do +; translation but left it here for future use. Ie, it +; might be used later but could be removed for now. +; +trans: .db 0,1,2,3,4,5,6,7 + .db 8,9,10,11,12,13,14,15 + .db 16,17,18,19,20,21,22,23 + .db 24,25,26,27,28,29,30,31 +; +;******************************************************** +; CP/M DPB +; +; AGAIN, OLD CODE WARNING!!!! Some of this was carry-over +; from my previous attempt to read BoGUS format disks. +; BoGUS was a CP/M box made by Franklin Computer in 1984. +; There are about 3 in existance today. +; +; BoGUS = Bob Grieb's Underground System +; +; This has always been one of the most confusing bits of +; CP/M legacy. To make things a tad easier, here are some +; bits of data that will make reading section 6.10 of the +; "CP/M Operating System Manual" a bit clearer. +; +; The CF drive I'm using (Sandisk SDCFB-192) has the +; following raw parameters. Hex numbers first, decimal +; in parens: +; +; Cylinders: 02DE (734) +; Heads: 10 (16) +; Sectors: 20 (32) +; Sector size: (512) +; +; Total disk size = 734*16*32*512 = 192,413,696 bytes +; +; SPT = 128 Sectors per track +; BLS = +; 1 Reserved track + +; +; BLS = 2048 (why, I don't know) +; 9 physical sectors per track, 4 CP/M sectors each +; DOuble sided +; 2 reserved tracks +; +;TRACKS: .equ (80 * 2);total tracks (count both sides) +;SECTORS: .equ 9 ;sectors per track +;SECSZ: .equ 512 ;bytes per sector +;CPMSECS: .equ (SECSZ/128) ;CP/M records per phys sector +; +;SPT: .equ (SECTORS*CPMSECS) +;BLS: .equ 2048 ;ask Dave ask to why +;BSH: .equ 4 +;BLM: .equ ((BLS/128)-1) +;OFF: .equ 2 +;DSM: .equ (((TRACKS-OFF)*SECSZ*SECTORS/BLS)-1) +; +;fldpb: .dw SPT ;CP/M records per track +; .db BSH ;allocation block shift +; .db BLM ;allocation block mask +; .db 0 ;extent mask +; .dw DSM ;max allocation block # +; .dw 255 ;max directory entry # +; .db 0f0h,0 ;directory allocation mask +; .dw 64 ;directory check alloc size +; .dw OFF ;number of reserved tracks + + + + + +;******************************************************** +; SET TRACK FOR FUTURE READS OR WRITES TO TRACK 0. ALSO +; PARTIALLY RESET THE DISK SYSTEM TO ALLOW FOR CHANGED +; DISKS. +;******************************************************** + +HOME: call msg + .text "HOME" + db CR,LF,0 + + ld a,(MustWriteBuffer) ;unwritten data? + or a + jp nz,HomeNoWrite + ld (DataInDiskBuffer),a +HomeNoWrite: ld c,0 ;set to track 0 + jp SETTRK + + +;******************************************************** +; SET TRACK FOR FUTURE READS OR WRITES TO THAT PASSED +; IN REGISTER PAIR BC. +;******************************************************** + +SETTRK: ld l,c + ld h,b + ld (SelectedTrack),hl + +; call msg +; db "SETTRK: ",0 +; ld a,h +; call a_hex +; ld a,l +; call a_hex +; call crlf + + ret + +;******************************************************** +; SET SECTOR FOR FUTURE READS OR WRITES TO THAT PASSED +; IN REGISTER PAIR BC. +;******************************************************** + +SETSEC: ld a,c + ld (SelectedSector),a + +; push af +; call msg +; db "SETSEC: ",0 +; pop af +; call a_hex +; call crlf + ret + +;******************************************************** +; SET DMA ADDRESS FOR FUTURE READS OR WRITES TO THAT +; PASSED IN REGISTER PAIR BC. +;******************************************************** + +SETDMA: ld l,c + ld h,b + ld (DMAAddress),hl + +#IF 0 ;debug code + push hl ;DEBUG + call msg + db "SETDMA: ",0 + pop hl + push hl + ld a,h + call a_hex + ld a,l + call a_hex + call crlf + pop hl ;END DEBUG +#ENDIF + + ret + +;******************************************************** +; SECTOR TRANSLATION ROUTINE. THE ROUTINE ONLY +; TRANSLATES SECTORS ON THE USER TRACKS, SINCE CP/M +; ACCESSES THE SYSTEM TRACKS WITHOUT CALLING FOR +; TRANSLATION. +; BC contains the sector number to translate, and the +; translation table address is in DE. Returns result +; in HL. +;******************************************************** + +SECTRA: ld l,c ;no translation + ld h,b + ret + +;******************************************************** +; The main events... read and write routines! They share +; a lot of common code, so each of these is pretty small, +; falls into common area, then eventually splits again. +;******************************************************** + +READ: +; Read in the 128-byte CP/M sector specified by previous calls +; to select disk and to set track and sector. The sector will be read +; into the address specified in the previous call to set DMA address. +; +; If reading from a disk drive using sectors larger than 128 bytes, +; deblocking code will be used to "unpack" a 128-byte sector from +; the physical sector. + +; call msg +; db "READ",CR,LF,0 + ld a,(DeblockingRequired) ;Check if deblocking needed + or a ;(flag was set in SELDSK call) + jp z,ReadNoDeblock ;No, use normal nondeblocked + +; The deblocking algorithm used is such +; that a read operation can be viewed +; up until the actual data transfer as +; though it was the first write to an +; unallocated allocation block. + + xor a ;Set the record count to 0 + ld (UnallocatedRecordCount),a ; for first "write" + inc a ;Indicate that it is really a read + ld (ReadOperation),a ;that is to be performed + ld (MustPrereadSector),a ;and force a preread of the sector + ;to get it into the disk buffer + ld a,WriteUnallocated ;Fake deblocking code into responding + ld (WriteType),a ;as if this is the first write to an + ;unallocated allocation block. + jp PerformReadWrite ;Use common code to execute read + + + +WRITE: +; +; Write a 128-byte sector from the current DMA address to +; the previously selected disk, track, and sector. +; +; On arrival here, the BDOS will have set register C to indicate +; whether this write operation is to an already allocated allocation +; block (which means a preread of the sector may be needed), +; to the directory (in which case the data will be written to the +; disk immediately), or to the first 128-byte sector of a previously +; unallocated allocation block (in which case no preread is required). +; +; Only writes to the directory take place immediately. In all other +; cases, the data will be moved from the DMA address into the disk +; buffer, and only written out when circumstances force the +; transfer. The number of physical disk operations can therefore +; be reduced considerably. +; + + ld a,(DeblockingRequired) ;Check if deblocking is required + or a ;(flag set in SELDSK call) + jp z,WriteNoDeblock + + xor a ;Indicate that a write operation + ld (ReadOperation),a ; is required (i.e. NOT a read) + ld a,c ;Save the BDOS write type + ld (WriteType),a + cp WriteUnallocated ;Check if the first write to an + ; unallocated allocation block + jp nz,CheckUnallocatedBlock ;No, check if in the middle of + ; writing to an unallocated block + ;Yes, first write to unallocated + ; allocation block -- initialize + ; variables associated with + ; unallocated writes. + ld a,AllocationBlockSize/128 ;Get number of 128-byte + ; sectors and + ld (UnallocatedRecordCount),a ; set up a count. + ; + ld hl,SelectedDkTrkSec ;Copy disk, track and sector + ld de,UnallocatedDkTrkSec ; into unallocated variables + call MoveDkTrkSec + +; +; Check if this is not the first write to an unallocated +; allocation block -- if it is, the unallocated record count +; has just been set to the number of 128-byte sectors in the +; allocation block. +; +CheckUnallocatedBlock: + ld a,(UnallocatedRecordCount) + or a + jp z,RequestPreread ;No, this is a write to an + ; allocated block + ;Yes, this is a write to an + ; unallocated block + dec a ;Count down on number of 128-byte sectors + ; left unwritten to in allocation block + ld a,(UnallocatedRecordCount) ; and store back new value. + + ld hl,SelectedDkTrkSec ;Check if the selected disk, track, + ld de,UnallocatedDkTrkSec ; and sector are the same as for + call CompareDkTrkSec ; those in the unallocated block. + jp nz,RequestPreread ;No, a preread is required + ;Yes, no preread is needed. + ;Now is a convenient time to + ; update the current sector and see + ; if the track also needs updating. +; + ;By design, Compare$Dk$Trk$Sec + ; returns with + ; DE -> Unallocated$Sector + ex de,hl ; HL -> Unallocated$Sector + inc (hl) ;Update Unallocated$Sector + ld a,(hl) ;Check if sector now > maximum + cp CPMSecPerTrack ; on a track + jp c,NoTrackChange ;No (A < (HL) ) + ;Yes, + ld (hl),0 ;Reset sector to 0 + ld hl,(UnallocatedTrack) ;Increase track by 1 + inc hl + ld (UnallocatedTrack),hl +; +NoTrackChange: + ;Indicate to later code that + ; no preread is needed. + xor a + ld (MustPrereadSector),a ;Must$Preread$Sector=0 + jp PerformReadWrite + +RequestPreread: + xor a ;Indicate that this is not a write + ld (UnallocatedRecordCount),a ; into an unallocated block. + inc a + ld (MustPrereadSector),a ;Indicate that a preread of the + ; physical sector is required. +; +; +PerformReadWrite: ;Common code to execute both reads and + ; writes of 128-byte sectors. + xor a ;Assume that no disk errors will + ld (DiskErrorFlag),a ; occur + + ld a,(SelectedSector) ;Convert selected 128-byte sector + rra ; into physical sector by dividing by 4 + rra + and 3fh ;Remove any unwanted bits + ld (SelectedPhysicalSector),a + ; + ld hl,DataInDiskBuffer ;Check if disk buffer already has + ld a,(hl) ; data in it. + ld (hl),1 ;(Unconditionally indicate that + ; the buffer now has data in it) + or a ;Did it indeed have data in it? + jp z,ReadSectorIntoBuffer ;No, proceed to read a physical + ; sector into the buffer. + ; + ;The buffer does have a physical sector + ; in it. + ; Note: The disk, track, and PHYSICAL + ; sector in the buffer need to be + ; checked, hence the use of the + ; Compare$Dk$Trk subroutine. + ; + ld de,InBufferDkTrkSec ;Check if sector in buffer is the + ld hl,SelectedDkTrkSec ; same as that selected earlier + call CompareDkTrk ;Compare ONLY disk and track + jp nz,SectorNotInBuffer ;No, it must be read in + + ld a,(InBufferSector) ;Get physical sector in buffer + ld hl,SelectedPhysicalSector + cp (hl) ;Check if correct physical sector + jp z,SectorInBuffer ;Yes, it is already in memory +SectorNotInBuffer: + ;No, it will have to be read in + ; over current contents of buffer + ld a,(MustWriteBuffer) ;Check if buffer has data in that + or a ; must be written out first + call nz,WritePhysical ;Yes, write it out +; +ReadSectorIntoBuffer: + call SetInBufferDkTrkSec ;Set in buffer variables from + ; selected disk, track, and sector + ; to reflect which sector is in the + ; buffer now. + ld a,(MustPrereadSector) ;In practice, the sector need only + or a ; be physically read in if a preread + ; is required + call nz,ReadPhysical ;Yes, preread the sector + xor a ;Reset the flag to reflect buffer + ld (MustWriteBuffer),a ; contents. +; +SectorInBuffer: ;Selected sector on correct track and + ; disk is already in the buffer. + ;Convert the selected CP/M (128-byte) + ; sector into a relative address down + ; the buffer. + ld a,(SelectedSector) ;Get selected sector number + and SectorMask ;Mask off only the least significant bits + ld l,a ;Multiply by 128 by shifting 16-bit value + ld h,0 ; left 7 bits + add hl,hl ;* 2 + add hl,hl ;* 4 + add hl,hl ;* 8 + add hl,hl ;* 16 + add hl,hl ;* 32 + add hl,hl ;* 64 + add hl,hl ;* 128 +; + ld de,DiskBuffer ;Get base address of disk buffer + add hl,de ;Add on sector number * 128 + ;HL -> 128-byte sector number start + ; address in disk buffer + ex de,hl ;DE -> sector in disk buffer + ld hl,(DMAAddress) ;Get DMA address set in SETDMA call + ex de,hl ;Assume a read operation, so + ; DE -> DMA address + ; HL -> sector in disk buffer + ld c,128/8 ;Because of the faster method used + ; to move data in and out of the + ; disk buffer, (eight bytes moved per + ; loop iteration) the count need only + ; be 1/8th of normal. + ;At this point -- + ; C = loop count + ; DE -> DMA address + ; HL -> sector in disk buffer + ld a,(ReadOperation) ;Determine whether data is to be moved + or a ; out of the buffer (read) or into the + jp nz,BufferMove ; buffer (write) + ;Writing into buffer + ;(A must be 0 get here) + inc a ;Set flag to force a write + ld (MustWriteBuffer),a ; of the disk buffer later on. + ex de,hl ;Make DE -> sector in disk buffer + ; HL -> DMA address +; +; +BufferMove: ;The following move loop moves eight bytes + ; at a time from (HL) to (DE), C contains + ; the loop count. + ld a,(hl) ;Get byte from source + ld (de),a ;Put into destination + inc de ;Update pointers + inc hl + ld a,(hl) ;Get byte from source + ld (de),a ;Put into destination + inc de ;Update pointers + inc hl + ld a,(hl) ;Get byte from source + ld (de),a ;Put into destination + inc de ;Update pointers + inc hl + ld a,(hl) ;Get byte from source + ld (de),a ;Put into destination + inc de ;Update pointers + inc hl + ld a,(hl) ;Get byte from source + ld (de),a ;Put into destination + inc de ;Update pointers + inc hl + ld a,(hl) ;Get byte from source + ld (de),a ;Put into destination + inc de ;Update pointers + inc hl + ld a,(hl) ;Get byte from source + ld (de),a ;Put into destination + inc de ;Update pointers + inc hl + ld a,(hl) ;Get byte from source + ld (de),a ;Put into destination + inc de ;Update pointers + inc hl + + dec c ;Count down on loop counter + jp nz,BufferMove ;Repeat until CP/M sector moved + ; + ld a,(WriteType) ;If write to directory, write out + cp WriteDirectory ; buffer immediately + ld a,(DiskErrorFlag) ;Get error flag in case delayed write or read + ret nz ;Return if delayed write or read + ; + or a ;Check if any disk errors have occured + ret nz ;Yes, abandon attempt to write to directory + ; + xor a ;Clear flag that indicates buffer must be + ld (MustWriteBuffer),a ; written out + call WritePhysical ;Write buffer out to physical sector + ld a,(DiskErrorFlag) ;Return error flag to caller + ret +; +; +SetInBufferDkTrkSec: ;Indicate selected disk, track, and + ; sector now residing in buffer + ld a,(SelectedDisk) + ld (InBufferDisk),a + + ld hl,(SelectedTrack) + ld (InBufferTrack),hl + + ld a,(SelectedPhysicalSector) + ld (InBufferSector),a + + ret +; +CompareDkTrk: ;Compares just the disk and track + ; pointed to by DE and HL + ld c,3 ;Disk (1), track (2) + jp CompareDkTrkSecLoop ;Use common code + +CompareDkTrkSec: ;Compares the disk, track, and sector + ; variables pointed to by DE and HL + ld c,4 ;Disk (1), track (2), and sector (1) +CompareDkTrkSecLoop: + ld a,(de) ;Get comparitor + cp (hl) ;Compare with comparand + ret nz ;Abandon comparison if inequality found + inc de ;Update comparitor pointer + inc hl ;Update comparand pointer + dec c ;Count down on loop count + ret z ;Return (with zero flag set) + jp CompareDkTrkSecLoop +; +; Moves the disk, track and sector +; variables pointed at by HL to +; those pointed at by DE +; +MoveDkTrkSec: ld c,4 ;Disk (1), track (2), and sector (1) +MoveDkTrkSecLoop: + ld a,(hl) ;Get source byte + ld (de),a ;Store in destination + inc de ;Update pointers + inc hl + dec c ;Count down on byte count + ret z ;Return if all bytes moved + jp MoveDkTrkSecLoop + + + + + +; +; Should not get here with the IDE! +; + +ReadNoDeblock: + call msg + db "ERROR - ReadNoDeblock",CR,LF,0 + ret + +WriteNoDeblock: + call msg + db "ERROR - WriteNoDeblock",CR,LF,0 + ret + + + +; +; Here's the big high-level view of the logic here. It's +; rather involved since a lot of decisions needs to be made. +; +; First, the variables: +; +; DRIVE = This is the drive the user has selected. It may not +; be the same drive currently in use. +; +; TRACK = This is the track the user wants to use, 0-79. It +; may not be the current track we're on. +; +; SECTOR = The CP/M logic sector the user wants to use. 0 - 35. +; Again, this might not be the same as the current one. +; +; CURDRV = This is the currently selected drive OR NODRIVE if +; none selected yet. If NODRIVE that implies this is +; the first disk access. +; +; CURTRK = Track where the current drive has its head located. +; NOTRACK implies the location is unknown. +; +; CURSEC = Current physical sector with the side bit (bit 0) still +; included. +; + +; So here's the drive select logic... +; +; if (DRIVE != CURDRV) - a new drive is being selected +; if (CURDRV != NODRIVE) - if a valid drive is already selected +; Save the current track into the drive data area. +; Load the new drive's data from the drive data area. +; if (CURTRK == NOTRACK) - first time for this disk +; Home disk +; set CURTRK to 0 +; else +; save track to FDC +; else +; Home disk +; set CURTRK to 0 +; +; ...followed by the track select logic... +; +; if (TRACK != CURTRK) +; seek to TRACK +; set CURTRK to TRACK +; +; ...and then compute physical sector and side... +; +; divide sector by 4 for temp sector +; if bit 0 is set then select side 0, else select side 1 +; divide the temp sector by 2. This is now CURSEC. +; load CURSEC into the sector register +; +; Perform read or write operation +; + + + + + +; +;******************************************************** +; Initilze the 8255 and drive then do a hard reset on the drive, +; +IDEinit: ld a,READcfg8255 ;10010010b + OUT (IDEportCtrl),a ;Config 8255 chip, READ mode + + ld a,IDErstline + OUT (IDEportC),a ;Hard reset the disk drive + + ld B,020H ;<<<<< fine tune later +ResetDelay: DJNZ ResetDelay ;Delay (reset pulse width) + xor a + out (IDEportC),a ;No IDE control lines asserted + + ld D,11100000b ;Data for IDE SDH reg (512bytes, LBA mode,single drive,head 0000) + ;For Trk,Sec,head (non LBA) use 10100000 + ;Note. Cannot get LBA mode to work with an old Seagate Medalist 6531 drive. + ;have to use teh non-LBA mode. (Common for old hard disks). + + ld E,REGshd ;00001110,(0EH) for CS0,A2,A1, + CALL IDEwr8D ;Write byte to select the MASTER device +; + ld B,0FFH ;<<< May need to adjust delay time +WaitInit: ld E,REGstatus ;Get status after initilization + CALL IDErd8D ;Check Status (info in [D]) + BIT 7,D + jp z,DoneInit ;Return if ready bit is zero + ld A,2 + CALL DELAYX ;Long delay, drive has to get up to speed + DJNZ WaitInit + CALL SHOWerrors ;Ret with NZ flag set if error (probably no drive) + RET +DoneInit: xor A + RET +; +DELAYX: ld (DELAYStore),a + PUSH BC + ld bc,0FFFFH ;<<< May need to adjust delay time to allow cold drive to +DELAY2: LD a,(DELAYStore) ; get up to speed. +DELAY1: dec a + jp nz,DELAY1 + dec bc + ld A,C + or b + jp nz,DELAY2 + POP BC + RET +; +;******************************************************** +; Read a sector, specified by the 4 bytes in LBA +; Z on success, NZ call error routine if problem +; +ReadPhysical: ld hl,DiskBuffer + ld a,042h + ld b,0 +rpfill ld (hl),a + inc hl + ld (hl),a + inc hl + djnz rpfill + + call msg + db "ReadPhysical",CR,LF,0 + + CALL wrlba ;Tell which sector we want to read from. + ;Note: Translate first in case of an error otherewise we + ;will get stuck on bad sector + CALL IDEwaitnotbusy ;make sure drive is ready + jp c,SHOWerrors ;Returned with NZ set if error + + ld D,COMMANDread + ld E,REGcommand + CALL IDEwr8D ;Send sec read command to drive. + CALL IDEwaitdrq ;wait until it's got the data + jp c,SHOWerrors +; + ld hl,DiskBuffer ;DMA address + ld B,0 ;Read 512 bytes to [HL] (256X2 bytes) +MoreRD16: ld A,REGdata ;REG regsiter address + OUT (IDEportC),a + + OR IDErdline ;08H+40H, Pulse RD line + OUT (IDEportC),a + + IN a,(IDEportA) ;Read the lower byte first (Note early versions had high byte then low byte + ld (hl),a ;this made sector data incompatable with other controllers). + inc hl + IN a,(IDEportB) ;THEN read the upper byte + ld (hl),A + inc hl + + ld A,REGdata ;Deassert RD line + OUT (IDEportC),a + DJNZ MoreRD16 + + ld E,REGstatus + CALL IDErd8D + ld A,D + BIT 0,A + call nz,SHOWerrors ;If error display status + RET +; +;******************************************************** +;Write a sector, specified by the 3 bytes in LBA (@ IX+0)", +;Z on success, NZ to error routine if problem +; +WritePhysical: +; call msg +; db "WritePhysical",CR,LF,0 + + CALL wrlba ;Tell which sector we want to read from. + ;Note: Translate first in case of an error otherewise we + ;will get stuck on bad sector + CALL IDEwaitnotbusy ;make sure drive is ready + jp c,SHOWerrors + + ld D,COMMANDwrite + ld E,REGcommand + CALL IDEwr8D ;tell drive to write a sector + CALL IDEwaitdrq ;wait unit it wants the data + jp c,SHOWerrors +; + ld hl,DiskBuffer + ld B,0 ;256X2 bytes + + ld A,WRITEcfg8255 + OUT (IDEportCtrl),a +WRSEC1: ld A,(hl) + inc hl + OUT (IDEportA),a ;Write the lower byte first (Note early versions had high byte then low byte + ld A,(hl) ;this made sector data incompatable with other controllers). + inc hl + OUT (IDEportB),a ;THEN High byte on B + ld A,REGdata + PUSH af + OUT (IDEportC),a ;Send write command + OR IDEwrline ;Send WR pulse + OUT (IDEportC),a + POP af + OUT (IDEportC),a + DJNZ WRSEC1 + + ld A,READcfg8255 ;Set 8255 back to read mode + OUT (IDEportCtrl),a + + ld E,REGstatus + CALL IDErd8D + ld A,D + BIT 0,A + call nz,SHOWerrors ;If error display status + RET +; +;******************************************************** +; Write the logical block address to the drive's registers +; Note we do not need to set the upper nibble of the LBA +; It will always be 0 for these small drives. Use the +; physical sector number which has already been divided +; down from the logical sector number. +; +wrlba: ld hl,(SelectedPhysicalSector) + ld a,l + inc a ;Sectors are numbered 1 to MAXSEC + ld (DRIVESEC),a ;For Diagnostic Display Only + ld D,A + ld E,REGsector ;Send info to drive + call IDEwr8D + ;Note: For drive we will have 0 - MAXSEC sectors only +; ld hl,TRK + ld hl,(SelectedTrack) + ld A,L + ld (DRIVETRK),a + ld D,L ;Send Low TRK# + ld E,REGcylinderLSB + call IDEwr8D + + ld A,H + ld (DRIVETRK+1),a + ld D,H ;Send High TRK# + ld E,REGcylinderMSB + call IDEwr8D + + ld D,1 ;For now, one sector at a time + ld E,REGseccnt + call IDEwr8D + +;DEBUG + call msg + .text "wrlba - Track " + .db 0 + ld a,(DRIVETRK+1) + call a_hex + ld a,(DRIVETRK) + call a_hex + call msg + .db ", Sector ",0 + ld a,(DRIVESEC) + call a_hex + call msg + .db CR,LF,0 +;END DEBUG + RET +; +;******************************************************** +;ie Drive READY if 01000000 +; +IDEwaitnotbusy: ld B,0FFH + ld A,0FFH ;Delay, must be above 80H for 4MHz Z80. Leave longer for slower drives + ld (DELAYStore),a + +MoreWait: ld E,REGstatus ;wait for RDY bit to be set + CALL IDErd8D + ld A,D + and 11000000B + xor 01000000B + jp z,DoneNotBusy + DJNZ MoreWait + ld a,(DELAYStore) ;Check timeout delay + dec A + ld (DELAYStore),a + jp nz,MoreWait + scf ;Set carry to indicqate an error + ret +DoneNotBusy: or A ;Clear carry it indicate no error + RET +; +;Wait for the drive to be ready to transfer data. +;Returns the drive's status in Acc +; +IDEwaitdrq: ld B,0FFH + ld A,0FFH ;Delay, must be above 80H for 4MHz Z80. Leave longer for slower drives + ld (DELAYStore),a + +MoreDRQ: ld E,REGstatus ;wait for DRQ bit to be set + CALL IDErd8D + ld A,D + and 10001000B + cp 00001000B + jp z,DoneDRQ + DJNZ MoreDRQ + ld a,(DELAYStore) ;Check timeout delay + dec A + ld (DELAYStore),a + jp nz,MoreDRQ + scf ;Set carry to indicate error + RET +DoneDRQ: OR A ;Clear carry + RET +; +;******************************************************** +; Low Level 8 bit R/W to the drive controller. These are +; the routines that talk directly to the drive controller +; registers, via the 8255 chip. Note the 16 bit I/O to +; the drive (which is only for SEC R/W) is done directly +; in the read/write functions for speed reasons. +; +; READ 8 bits from IDE register in [E], return info in [D] +; +IDErd8D: ld A,E + OUT (IDEportC),a ;drive address onto control lines + + OR IDErdline ;RD pulse pin (40H) + OUT (IDEportC),a ;assert read pin + + IN a,(IDEportA) + ld D,A ;return with data in [D] + + xor A + OUT (IDEportC),a ;Zero all port C lines + ret +; +; WRITE Data in [D] to IDE register in [E] +; +IDEwr8D: ld A,WRITEcfg8255 ;Set 8255 to write mode + OUT (IDEportCtrl),a + + ld A,D ;Get data put it in 8255 A port + OUT (IDEportA),a + + ld A,E ;select IDE register + OUT (IDEportC),a + + OR IDEwrline ;lower WR line + OUT (IDEportC),a + NOP + + xor A ;Deselect all lines including WR line + OUT (IDEportC),a + + ld A,READcfg8255 ;Config 8255 chip, read mode on return + OUT (IDEportCtrl),a + RET + + +SHOWerrors: call msg + .TEXT "SHOWerrors called" + .db CR,LF,0 + + .page +;******************************************************** +; Input/Output devices. For now, just call the TTY +; functions, but later I'll add iobyte support. +; +CONST: jp ttystat +CONIN: jp ttyin +CONOUT: jp ttyout +; +;******************************************************** +; Message output subroutines. msghl will print the null- +; terminated message pointed to by HL. msg will +; print the null terminated string immediately following +; the "call MSG" line. +; +msg: ex (sp),hl + call msghl + ex (sp),hl + ret +; +msghl: ld a,(hl) + or a + ret z + ld c,a + call CONOUT + inc hl + jr msghl +; +;******************************************************** +; Display the contents of A as two hex digits +; +a_hex: push af + rrca + rrca + rrca + rrca + call ahex2 + pop af +ahex2: and 0fh + add a,90h + daa + adc a,40h + daa + ld c,a + jr ttyout +; +;******************************************************** +; Do a CR/LF +; +crlf: ld c,CR + call ttyout + ld c,LF + jr ttyout + .page +; +;******************************************************** +; Other I/O devices. Make them all do nothing for now. +; +LIST: jp ttyout +PUNCH: ret +READER: ld a,EOF + ret +LISTST: ld a,TRUE + ret + +#include "ccs2710.inc" + + + +; stuff from bios.mac + +InBufferDkTrkSec: ;Variables for physical sector + ; currently in Disk$Buffer in memory +InBufferDisk: db 0 ; These are moved and compared +InBufferTrack: dw 0 ; as a group, so do not alter +InBufferSector: db 0 ; these lines. +; +DataInDiskBuffer: db 0 ;When nonzero, the disk buffer has + ; data from the disk in it. +MustWriteBuffer: db 0 ;Nonzero when data has been + ; written into Disk$Buffer but + ; not yet written out to disk + +DMAAddress: dw 0 + +WriteType db 0 + + ;These are the values handed over by the BDOS + ; when it calls the WRITE operation. + ;The allocated/unallocated indicates whether the + ; BDOS is set to write to an unallocated allocation + ; block (it only indicates this for the first + ; 128-byte sector write) or to an allocation block + ; that has already been allocated to a file. + ;The BDOS also indicates if it is set to write to + ; the file directory. + ; +WriteAllocated equ 0 +WriteDirectory equ 1 +WriteUnallocated equ 2 + +; +;Variables for selected disk, track, and sector +; (Selected by SELDSK, SETTRK,n and SETSEC) +; +; Note that these are in a specific order, so do not change +; the size nor order without checking where other pieces of +; code are affected! +; +SelectedDkTrkSec: +SelectedDisk: db 0 ; These are moved and +SelectedTrack: dw 0 ; compared as a group so +SelectedSector: db 0 ; do not alter order. +SelectedPhysicalSector: db 0 ;Selected physical sector derived + ; from selected (CP/M) sector by + ; shifting it right the number of + ; bits specified by + ; Sector$Bit$Shift +; +SelectedDiskType: db 0 ;Set by SELDSK to indicate either + ; 8" or 5 1/4" floppy +SelectedDiskDeblock: db 0 ;Set by SELDSK to indicate whether + ; deblocking is required. + +UnallocatedDkTrkSec: ;Parameters for writing to a previously + ; unallocated allocation block. +UnallocatedDisk: db 0 ; These are moved and compared +UnallocatedTrack: dw 0 ; as a group so do not alter +UnallocatedSector: db 0 ; these lines. + +UnallocatedRecordCount: db 0 ;Number of unallocated "records" + ; in current previously unallocated + ; allocation block. + +DiskErrorFlag: db 0 ;Nonzero to indicate an error + ; that could not be recovered + ; by the disk drivers. BDOS will + ; output a "bad sector" message. + +; +;Flags used inside the deblocking code + +MustPrereadSector: db 0 ;Nonzero if a physical sector must + ; be read into the disk buffer + ; either before a write to an + ; allocated block can occur, or + ; for a normal CP/M 128-byte + ; sector read +ReadOperation: db 0 ;Nonzero when a CP/M 128-byte + ; sector is to be read +DeblockingRequired: db 0 ;Nonzero when the selected disk + ; needs deblocking (set in SELDSK) +DiskType: db 0 ;Indicates 8" or 5 1/4" floppy + ; selected (set in SELDSK). + + +; +; This is the actual sector size +; for the 5 1/4" mini-floppy diskettes. +; The 8" diskettes use 128-byte sectors. +; Declare the physical disk buffer for the +; 5 1/4" diskettes +; +; This is the low level buffer used to hold a sector of data. +; +PhysicalSectorSize equ 512 +DiskBuffer: ds PhysicalSectorSize +; +; Data written to, or read from, the mini-floppy drive is transferred +; via a physical buffer that is actually 512 bytes long (it was +; declared at the front of the BIOS and holds the "one-time" +; initialization code used for the cold boot procedure). +; +; The blocking/deblocking code attempts to minimize the amount +; of actual disk I/O by storing the disk, track, and physical sector +; currently residing in the Physical Buffer. If a read request is for +; a 128-byte CP/M "sector" that already is in the physical buffer, +; then no disk access occurs. +; +; +AllocationBlockSize equ 2048 +PhysicalSecPerTrack equ 18 +CPMSecPerPhysical equ PhysicalSectorSize/128 +CPMSecPerTrack equ CPMSecPerPhysical*PhysicalSecPerTrack +SectorMask equ CPMSecPerPhysical-1 +SectorBitShift equ 2 ;LOG2(CPM$Sec$Per$Physical) + + + +; stuff from MYIDE + +DELAYStore: DS 1 +DRIVESEC: db 0 +DRIVETRK: dw 0 + +; +; This MUST BE the last label! +; +BIOS_END .equ * + + .end diff --git a/branches/s100/Applegate/ccs2710.inc b/branches/s100/Applegate/ccs2710.inc new file mode 100644 index 00000000..4129733a --- /dev/null +++ b/branches/s100/Applegate/ccs2710.inc @@ -0,0 +1,77 @@ + .page +;******************************************************** +; These are I/O functions for the CCS 2710 four-port +; serial I/O board. +; +; Bob Applegate, 02/16/2008 +; +SIObase .equ 0E0H +; +SIODATA .equ SIObase +SIOBAUDL .equ SIObase ;also used for baud rate +SIOINTEN .equ SIObase+1 +SIOBAUDH .equ SIObase+1 ;ditto +SIOIDENT .equ SIObase+2 +SIOLCTRL .equ SIObase+3 +SIOMDMCT .equ SIObase+4 +SIOLSTAT .equ SIObase+5 +SIOMDMST .equ SIObase+6 +BAUD03 .equ 0180H ;divisor for 300 baud +BAUD12 .equ 060H ;1200 baud +BAUD_2400 .equ 030h ;2400 baud +BAUD96 .equ 00CH ;9600 baud +DATRDY .equ 01H ;rec'd data ready +TXMTY .equ 20H ;transmitter holding reg empty +HSMSK .equ 20H + +BAUD_RATE .equ BAUD_2400 + +; +; This function initializes the main console port for the +; default baud rate. +; +initser: ld a,0fH + out (SIOMDMCT),a + ld a,083H ;enable divisor latch access + out (SIOLCTRL),a + ld a,BAUD_RATE / 256 ;get hi byte + out (SIOBAUDH),a + ld a,BAUD_RATE % 256 ;get low byte + out (SIOBAUDL),a + ld a,03H ;8 data bits, one stop bit, no parity + out (SIOLCTRL),a + xor a ;clear acc + out (SIOINTEN),a ;disable ints + out (SIOLSTAT),a ;clear status + in a,(SIODATA) ;clear out any garbage in rec'd data reg + ret +; +; TTY output of character in C. Modifies A. +; +ttyout: IN A,(SIOLSTAT) ;was A,TTS, read status port + AND TXMTY ;check buffer empty bit + JR Z,ttyout ;branch if not empty + LD A,C + OUT (SIODATA),A ;was TTO,A, send out character + RET ;thassit +; +; Check to see if a character is ready to be read from the TTY. +; Returns TRUE (0ffh) in A is there is a character waiting, or +; FALSE (0) if there is nothing. +; +ttystat: IN A,(SIOLSTAT) ;was A,TTS + AND DATRDY + LD A,TRUE ;was FALSE + RET NZ + CPL + RET +; +; This gets the next character from the TTY and returns it in A. +; This will block if there is nothing waiting. +; +ttyin: IN A,(SIOLSTAT) ;read status reg + AND DATRDY ;look for data ready + JR Z,ttyin ;wait for char + IN a,(SIODATA) ;read character + RET + .page diff --git a/branches/s100/Applegate/convert.py b/branches/s100/Applegate/convert.py new file mode 100755 index 00000000..3b53fa25 --- /dev/null +++ b/branches/s100/Applegate/convert.py @@ -0,0 +1,71 @@ +#!/usr/bin/python +# Written by Douglas Goodall 17:25 Wed, Jan 30, 2013 +# load cpm.bin and bios.bin then jump + +import sys +import os +import serial + +# passing in a string either "12" or "0x12" +# return value is string of hex digits only (no 0x) +def safebyte(parm): + xyz = parm + myord = ord(xyz) + hexdata = hex(myord) + newstr = hexdata + if (hexdata[0] == '0'): + if(hexdata[1] == 'x'): + newstr = hexdata[2] + if(len(hexdata)>3): + newstr = newstr + hexdata[3] + return newstr + +# passing in a string either "1234" of "0x1234" +# return value is string of hex digits only (1234) (no 0x) +def safeword(parm): + xyz = parm + myint = int(xyz) + hexdata = hex(myint) + newstr = hexdata + if (hexdata[0] == '0'): + if(hexdata[1] == 'x'): + newstr = hexdata[2] + if(len(hexdata)>3): + newstr = newstr + hexdata[3] + if(len(hexdata)>4): + newstr = newstr + hexdata[4] + if(len(hexdata)>5): + newstr = newstr + hexdata[5] + return newstr + +def loadngo(filename): + statinfo = os.stat(filename) + filelen = statinfo.st_size + infile = open(filename,'rb') + filedata = infile.read() + infile.close() + outfile = open("cpm.trans","w") + +# ser = serial.Serial('/dev/cu.PL2303-0000201D', 19200, timeout=10) +# ser.write("\n\n") + outfile.write("sa400\n") +# print ser.read(12); + for x in range(1,filelen): + outfile.write(safebyte(filedata[x-1])) + outfile.write(" ") + outfile.write("\n") + outfile.close() +# print ser.read(12) +# ser.close() + +print "*******************************************************************" +print "loadcpm.py 1/30/2013 dwg - load&go S-100 CP/M using master-yoda ROM" +print "*******************************************************************" +#loadngo("cpm.bin") +ser = serial.Serial('/dev/cu.PL2303-0000201D', 19200, timeout=1) +ser.read(128) +ser.read(128) +ser.write("\n") +ser.close() +loadngo("cpm.bin") + diff --git a/branches/s100/Applegate/cpm.trans b/branches/s100/Applegate/cpm.trans new file mode 100644 index 00000000..b6335325 --- /dev/null +++ b/branches/s100/Applegate/cpm.trans @@ -0,0 +1,2 @@ +sa400 +c3 5c a7 c3 58 a7 7f 0 43 6f 70 79 72 69 67 68 74 20 31 39 37 39 20 28 63 29 20 62 79 20 44 69 67 69 74 61 6c 20 52 65 73 65 61 72 63 68 20 20 20 20 20 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 a4 0 0 5f e 2 c3 5 0 c5 cd 8c a4 c1 c9 3e d cd 92 a4 3e a c3 92 a4 3e 20 c3 92 a4 c5 cd 98 a4 e1 7e b7 c8 23 e5 cd 8c a4 e1 c3 ac a4 e d c3 5 0 5f e e c3 5 0 cd 5 0 32 ee ab 3c c9 e f c3 c3 a4 af 32 ed ab 11 cd ab c3 cb a4 e 10 c3 c3 a4 e 11 c3 c3 a4 e 12 c3 c3 a4 11 cd ab c3 df a4 e 13 c3 5 0 cd 5 0 b7 c9 e 14 c3 f4 a4 11 cd ab c3 f9 a4 e 15 c3 f4 a4 e 16 c3 c3 a4 e 17 c3 5 0 1e ff e 20 c3 5 0 cd 13 a5 87 87 87 87 21 ef ab b6 32 4 0 c9 3a ef ab 32 4 0 c9 fe 61 d8 fe 7b d0 e6 5f c9 3a ab ab b7 ca 96 a5 3a ef ab b7 3e 0 c4 bd a4 11 ac ab cd cb a4 ca 96 a5 3a bb ab 3d 32 cc ab 11 ac ab cd f9 a4 c2 96 a5 11 7 a4 21 80 0 6 80 cd 42 a8 21 ba ab 36 0 23 35 11 ac ab cd da a4 ca 96 a5 3a ef ab b7 c4 bd a4 21 8 a4 cd ac a4 cd c2 a5 ca a7 a5 cd dd a5 c3 82 a7 cd dd a5 cd 1a a5 e a 11 6 a4 cd 5 0 cd 29 a5 21 7 a4 46 23 78 b7 ca ba a5 7e cd 30 a5 77 5 c3 ab a5 77 21 8 a4 22 88 a4 c9 e b cd 5 0 b7 c8 e 1 cd 5 0 b7 c9 e 19 c3 5 0 11 80 0 e 1a c3 5 0 21 ab ab 7e b7 c8 36 0 af cd bd a4 11 ac ab cd ef a4 3a ef ab c3 bd a4 11 28 a7 21 0 ac 6 6 1a be c2 cf a7 13 23 5 c2 fd a5 c9 cd 98 a4 2a 8a a4 7e fe 20 ca 22 a6 b7 ca 22 a6 e5 cd 8c a4 e1 23 c3 f a6 3e 3f cd 8c a4 cd 98 a4 cd dd a5 c3 82 a7 1a b7 c8 fe 20 da 9 a6 c8 fe 3d c8 fe 5f c8 fe 2e c8 fe 3a c8 fe 3b c8 fe 3c c8 fe 3e c8 c9 1a b7 c8 fe 20 c0 13 c3 4f a6 85 6f d0 24 c9 3e 0 21 cd ab cd 59 a6 e5 e5 af 32 f0 ab 2a 88 a4 eb cd 4f a6 eb 22 8a a4 eb e1 1a b7 ca 89 a6 de 40 47 13 1a fe 3a ca 90 a6 1b 3a ef ab 77 c3 96 a6 78 32 f0 ab 70 13 6 8 cd 30 a6 ca b9 a6 23 fe 2a c2 a9 a6 36 3f c3 ab a6 77 13 5 c2 98 a6 cd 30 a6 ca c0 a6 13 c3 af a6 23 36 20 5 c2 b9 a6 6 3 fe 2e c2 e9 a6 13 cd 30 a6 ca e9 a6 23 fe 2a c2 d9 a6 36 3f c3 db a6 77 13 5 c2 c8 a6 cd 30 a6 ca f0 a6 13 c3 df a6 23 36 20 5 c2 e9 a6 6 3 23 36 0 5 c2 f2 a6 eb 22 88 a4 e1 1 b 0 23 7e fe 3f c2 9 a7 4 d c2 1 a7 78 b7 c9 44 49 52 20 45 52 41 20 54 59 50 45 53 41 56 45 52 45 4e 20 55 53 45 52 0 16 0 0 0 0 21 10 a7 e 0 79 fe 6 d0 11 ce ab 6 4 1a be c2 4f a7 13 23 5 c2 3c a7 1a fe 20 c2 54 a7 79 c9 23 5 c2 4f a7 c c3 33 a7 af 32 7 a4 31 ab ab c5 79 1f 1f 1f 1f e6 f 5f cd 15 a5 cd b8 a4 32 ab ab c1 79 e6 f 32 ef ab cd bd a4 3a 7 a4 b7 c2 98 a7 31 ab ab cd 98 a4 cd d0 a5 c6 41 cd 8c a4 3e 3e cd 8c a4 cd 39 a5 11 80 0 cd d8 a5 cd d0 a5 32 ef ab cd 5e a6 c4 9 a6 3a f0 ab b7 c2 a5 aa cd 2e a7 21 c1 a7 5f 16 0 19 19 7e 23 66 6f e9 77 a8 1f a9 5d a9 ad a9 10 aa 8e aa a5 aa 21 f3 76 22 0 a4 21 0 a4 e9 1 df a7 c3 a7 a4 52 65 61 64 20 65 72 72 6f 72 0 1 f0 a7 c3 a7 a4 4e 6f 20 66 69 6c 65 0 cd 5e a6 3a f0 ab b7 c2 9 a6 21 ce ab 1 b 0 7e fe 20 ca 33 a8 23 d6 30 fe a d2 9 a6 57 78 e6 e0 c2 9 a6 78 7 7 7 80 da 9 a6 80 da 9 a6 82 da 9 a6 47 d c2 8 a8 c9 7e fe 20 c2 9 a6 23 d c2 33 a8 78 c9 6 3 7e 12 23 13 5 c2 42 a8 c9 21 80 0 81 cd 59 a6 7e c9 af 32 cd ab 3a f0 ab b7 c8 3d 21 ef ab be c8 c3 bd a4 3a f0 ab b7 c8 3d 21 ef ab be c8 3a ef ab c3 bd a4 cd 5e a6 cd 54 a8 21 ce ab 7e fe 20 c2 8f a8 6 b 36 3f 23 5 c2 88 a8 1e 0 d5 cd e9 a4 cc ea a7 ca 1b a9 3a ee ab f f f e6 60 4f 3e a cd 4b a8 17 da f a9 d1 7b 1c d5 e6 3 f5 c2 cc a8 cd 98 a4 c5 cd d0 a5 c1 c6 41 cd 92 a4 3e 3a cd 92 a4 c3 d4 a8 cd a2 a4 3e 3a cd 92 a4 cd a2 a4 6 1 78 cd 4b a8 e6 7f fe 20 c2 f9 a8 f1 f5 fe 3 c2 f7 a8 3e 9 cd 4b a8 e6 7f fe 20 ca e a9 3e 20 cd 92 a4 4 78 fe c d2 e a9 fe 9 c2 d9 a8 cd a2 a4 c3 d9 a8 f1 cd c2 a5 c2 1b a9 cd e4 a4 c3 98 a8 d1 c3 86 ab cd 5e a6 fe b c2 42 a9 1 52 a9 cd a7 a4 cd 39 a5 21 7 a4 35 c2 82 a7 23 7e fe 59 c2 82 a7 23 22 88 a4 cd 54 a8 11 cd ab cd ef a4 3c cc ea a7 c3 86 ab 41 6c 6c 20 28 79 2f 6e 29 3f 0 cd 5e a6 c2 9 a6 cd 54 a8 cd d0 a4 ca a7 a9 cd 98 a4 21 f1 ab 36 ff 21 f1 ab 7e fe 80 da 87 a9 e5 cd fe a4 e1 c2 a0 a9 af 77 34 21 80 0 cd 59 a6 7e fe 1a ca 86 ab cd 8c a4 cd c2 a5 c2 86 ab c3 74 a9 3d ca 86 ab cd d9 a7 cd 66 a8 c3 9 a6 cd f8 a7 f5 cd 5e a6 c2 9 a6 cd 54 a8 11 cd ab d5 cd ef a4 d1 cd 9 a5 ca fb a9 af 32 ed ab f1 6f 26 0 29 11 0 1 7c b5 ca f1 a9 2b e5 21 80 0 19 e5 cd d8 a5 11 cd ab cd 4 a5 d1 e1 c2 fb a9 c3 d4 a9 11 cd ab cd da a4 3c c2 1 aa 1 7 aa cd a7 a4 cd d5 a5 c3 86 ab 4e 6f 20 73 70 61 63 65 0 cd 5e a6 c2 9 a6 3a f0 ab f5 cd 54 a8 cd e9 a4 c2 79 aa 21 cd ab 11 dd ab 6 10 cd 42 a8 2a 88 a4 eb cd 4f a6 fe 3d ca 3f aa fe 5f c2 73 aa eb 23 22 88 a4 cd 5e a6 c2 73 aa f1 47 21 f0 ab 7e b7 ca 59 aa b8 70 c2 73 aa 70 af 32 cd ab cd e9 a4 ca 6d aa 11 cd ab cd e a5 c3 86 ab cd ea a7 c3 86 ab cd 66 a8 c3 9 a6 1 82 aa cd a7 a4 c3 86 ab 46 69 6c 65 20 65 78 69 73 74 73 0 cd f8 a7 fe 10 d2 9 a6 5f 3a ce ab fe 20 ca 9 a6 cd 15 a5 c3 89 ab cd f5 a5 3a ce ab fe 20 c2 c4 aa 3a f0 ab b7 ca 89 ab 3d 32 ef ab cd 29 a5 cd bd a4 c3 89 ab 11 d6 ab 1a fe 20 c2 9 a6 d5 cd 54 a8 d1 21 83 ab cd 40 a8 cd d0 a4 ca 6b ab 21 0 1 e5 eb cd d8 a5 11 cd ab cd f9 a4 c2 1 ab e1 11 80 0 19 11 0 a4 7d 93 7c 9a d2 71 ab c3 e1 aa e1 3d c2 71 ab cd 66 a8 cd 5e a6 21 f0 ab e5 7e 32 cd ab 3e 10 cd 60 a6 e1 7e 32 dd ab af 32 ed ab 11 5c 0 21 cd ab 6 21 cd 42 a8 21 8 a4 7e b7 ca 3e ab fe 20 ca 3e ab 23 c3 30 ab 6 0 11 81 0 7e 12 b7 ca 4f ab 4 23 13 c3 43 ab 78 32 80 0 cd 98 a4 cd d5 a5 cd 1a a5 cd 0 1 31 ab ab cd 29 a5 cd bd a4 c3 82 a7 cd 66 a8 c3 9 a6 1 7a ab cd a7 a4 c3 86 ab 42 61 64 20 6c 6f 61 64 0 43 4f 4d cd 66 a8 cd 5e a6 3a ce ab d6 20 21 f0 ab b6 c2 9 a6 c3 82 a7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 24 24 24 20 20 20 20 20 53 55 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 20 20 20 20 20 20 20 20 20 20 0 0 0 0 0 20 20 20 20 20 20 20 20 20 20 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 0 0 0 0 c3 11 ac 99 ac a5 ac ab ac b1 ac eb 22 43 af eb 7b 32 d6 b9 21 0 0 22 45 af 39 22 f af 31 41 af af 32 e0 b9 32 de b9 21 74 b9 e5 79 fe 29 d0 4b 21 47 ac 5f 16 0 19 19 5e 23 56 2a 43 af eb e9 3 ba c8 ae 90 ad ce ae 12 ba f ba d4 ae ed ae f3 ae f8 ae e1 ad fe ae 7e b8 83 b8 45 b8 9c b8 a5 b8 ab b8 c8 b8 d7 b8 e0 b8 e6 b8 ec b8 f5 b8 fe b8 4 b9 a b9 11 b9 2c b1 17 b9 1d b9 26 b9 2d b9 41 b9 47 b9 4d b9 e b8 53 b9 4 af 4 af 9b b9 21 ca ac cd e5 ac fe 3 ca 0 0 c9 21 d5 ac c3 b4 ac 21 e1 ac c3 b4 ac 21 dc ac cd e5 ac c3 0 0 42 64 6f 73 20 45 72 72 20 4f 6e 20 20 3a 20 24 42 61 64 20 53 65 63 74 6f 72 24 53 65 6c 65 63 74 24 46 69 6c 65 20 52 2f 4f 24 e5 cd c9 ad 3a 42 af c6 41 32 c6 ac 1 ba ac cd d3 ad c1 cd d3 ad 21 e af 7e 36 0 b7 c0 c3 9 ba cd fb ac cd 14 ad d8 f5 4f cd 90 ad f1 c9 fe d c8 fe a c8 fe 9 c8 fe 8 c8 fe 20 c9 3a e af b7 c2 45 ad cd 6 ba e6 1 c8 cd 9 ba fe 13 c2 42 ad cd 9 ba fe 3 ca 0 0 af c9 32 e af 3e 1 c9 3a a af b7 c2 62 ad c5 cd 23 ad c1 c5 cd c ba c1 c5 3a d af b7 c4 f ba c1 79 21 c af fe 7f c8 34 fe 20 d0 35 7e b7 c8 79 fe 8 c2 79 ad 35 c9 fe a c0 36 0 c9 79 cd 14 ad d2 90 ad f5 e 5e cd 48 ad f1 f6 40 4f 79 fe 9 c2 48 ad e 20 cd 48 ad 3a c af e6 7 c2 96 ad c9 cd ac ad e 20 cd c ba e 8 c3 c ba e 23 cd 48 ad cd c9 ad 3a c af 21 b af be d0 e 20 cd 48 ad c3 b9 ad e d cd 48 ad e a c3 48 ad a fe 24 c8 3 c5 4f cd 90 ad c1 c3 d3 ad 3a c af 32 b af 2a 43 af 4e 23 e5 6 0 c5 e5 cd fb ac e6 7f e1 c1 fe d ca c1 ae fe a ca c1 ae fe 8 c2 16 ae 78 b7 ca ef ad 5 3a c af 32 a af c3 70 ae fe 7f c2 26 ae 78 b7 ca ef ad 7e 5 2b c3 a9 ae fe 5 c2 37 ae c5 e5 cd c9 ad af 32 b af c3 f1 ad fe 10 c2 48 ae e5 21 d af 3e 1 96 77 e1 c3 ef ad fe 18 c2 5f ae e1 3a b af 21 c af be d2 e1 ad 35 cd a4 ad c3 4e ae fe 15 c2 6b ae cd b1 ad e1 c3 e1 ad fe 12 c2 a6 ae c5 cd b1 ad c1 e1 e5 c5 78 b7 ca 8a ae 23 4e 5 c5 e5 cd 7f ad e1 c1 c3 78 ae e5 3a a af b7 ca f1 ad 21 c af 96 32 a af cd a4 ad 21 a af 35 c2 99 ae c3 f1 ad 23 77 4 c5 e5 4f cd 7f ad e1 c1 7e fe 3 78 c2 bd ae fe 1 ca 0 0 b9 da ef ad e1 70 e d c3 48 ad cd 6 ad c3 1 af cd 15 ba c3 1 af 79 3c ca e0 ae 3c ca 6 ba c3 c ba cd 6 ba b7 ca 91 b9 cd 9 ba c3 1 af 3a 3 0 c3 1 af 21 3 0 71 c9 eb 4d 44 c3 d3 ad cd 23 ad 32 45 af c9 3e 1 c3 1 af 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 21 b ac 5e 23 56 eb e9 c d c8 1a 77 13 23 c3 50 af 3a 42 af 4f cd 1b ba 7c b5 c8 5e 23 56 23 22 b3 b9 23 23 22 b5 b9 23 23 22 b7 b9 23 23 eb 22 d0 b9 21 b9 b9 e 8 cd 4f af 2a bb b9 eb 21 c1 b9 e f cd 4f af 2a c6 b9 7c 21 dd b9 36 ff b7 ca 9d af 36 0 3e ff b7 c9 cd 18 ba af 2a b5 b9 77 23 77 2a b7 b9 77 23 77 c9 cd 27 ba c3 bb af cd 2a ba b7 c8 21 9 ac c3 4a af 2a ea b9 e 2 cd ea b0 22 e5 b9 22 ec b9 21 e5 b9 4e 23 46 2a b7 b9 5e 23 56 2a b5 b9 7e 23 66 6f 79 93 78 9a d2 fa af e5 2a c1 b9 7b 95 5f 7a 9c 57 e1 2b c3 e4 af e5 2a c1 b9 19 da f b0 79 95 78 9c da f b0 eb e1 23 c3 fa af e1 c5 d5 e5 eb 2a ce b9 19 44 4d cd 1e ba d1 2a b5 b9 73 23 72 d1 2a b7 b9 73 23 72 c1 79 93 4f 78 9a 47 2a d0 b9 eb cd 30 ba 4d 44 c3 21 ba 21 c3 b9 4e 3a e3 b9 b7 1f d c2 45 b0 47 3e 8 96 4f 3a e2 b9 d ca 5c b0 b7 17 c3 53 b0 80 c9 2a 43 af 11 10 0 19 9 3a dd b9 b7 ca 71 b0 6e 26 0 c9 9 5e 23 56 eb c9 cd 3e b0 4f 6 0 cd 5e b0 22 e5 b9 c9 2a e5 b9 7d b4 c9 3a c3 b9 2a e5 b9 29 3d c2 90 b0 22 e7 b9 3a c4 b9 4f 3a e3 b9 a1 b5 6f 22 e5 b9 c9 2a 43 af 11 c 0 19 c9 2a 43 af 11 f 0 19 eb 21 11 0 19 c9 cd ae b0 7e 32 e3 b9 eb 7e 32 e1 b9 cd a6 b0 3a c5 b9 a6 32 e2 b9 c9 cd ae b0 3a d5 b9 fe 2 c2 de b0 af 4f 3a e3 b9 81 77 eb 3a e1 b9 77 c9 c d c8 7c b7 1f 67 7d 1f 6f c3 eb b0 e 80 2a b9 b9 af 86 23 d c2 fd b0 c9 c d c8 29 c3 5 b1 c5 3a 42 af 4f 21 1 0 cd 4 b1 c1 79 b5 6f 78 b4 67 c9 2a ad b9 3a 42 af 4f cd ea b0 7d e6 1 c9 21 ad b9 4e 23 46 cd b b1 22 ad b9 2a c8 b9 23 eb 2a b3 b9 73 23 72 c9 cd 5e b1 11 9 0 19 7e 17 d0 21 f ac c3 4a af cd 1e b1 c8 21 d ac c3 4a af 2a b9 b9 3a e9 b9 85 6f d0 24 c9 2a 43 af 11 e 0 19 7e c9 cd 69 b1 36 0 c9 cd 69 b1 f6 80 77 c9 2a ea b9 eb 2a b3 b9 7b 96 23 7a 9e c9 cd 7f b1 d8 13 72 2b 73 c9 7b 95 6f 7a 9c 67 c9 e ff 2a ec b9 eb 2a cc b9 cd 95 b1 d0 c5 cd f7 b0 2a bd b9 eb 2a ec b9 19 c1 c ca c4 b1 be c8 cd 7f b1 d0 cd 2c b1 c9 77 c9 cd 9c b1 cd e0 b1 e 1 cd b8 af c3 da b1 cd e0 b1 cd b2 af 21 b1 b9 c3 e3 b1 21 b9 b9 4e 23 46 c3 24 ba 2a b9 b9 eb 2a b1 b9 e 80 c3 4f af 21 ea b9 7e 23 be c0 3c c9 21 ff ff 22 ea b9 c9 2a c8 b9 eb 2a ea b9 23 22 ea b9 cd 95 b1 d2 19 b2 c3 fe b1 3a ea b9 e6 3 6 5 87 5 c2 20 b2 32 e9 b9 b7 c0 c5 cd c3 af cd d4 b1 c1 c3 9e b1 79 e6 7 3c 5f 57 79 f f f e6 1f 4f 78 87 87 87 87 87 b1 4f 78 f f f e6 1f 47 2a bf b9 9 7e 7 1d c2 56 b2 c9 d5 cd 35 b2 e6 fe c1 b1 f 15 c2 64 b2 77 c9 cd 5e b1 11 10 0 19 c5 e 11 d1 d c8 d5 3a dd b9 b7 ca 88 b2 c5 e5 4e 6 0 c3 8e b2 d c5 4e 23 46 e5 79 b0 ca 9d b2 2a c6 b9 7d 91 7c 98 d4 5c b2 e1 23 c1 c3 75 b2 2a c6 b9 e 3 cd ea b0 23 44 4d 2a bf b9 36 0 23 b 78 b1 c2 b1 b2 2a ca b9 eb 2a bf b9 73 23 72 cd a1 af 2a b3 b9 36 3 23 36 0 cd fe b1 e ff cd 5 b2 cd f5 b1 c8 cd 5e b1 3e e5 be ca d2 b2 3a 41 af be c2 f6 b2 23 7e d6 24 c2 f6 b2 3d 32 45 af e 1 cd 6b b2 cd 8c b1 c3 d2 b2 3a d4 b9 c3 1 af c5 f5 3a c5 b9 2f 47 79 a0 4f f1 a0 91 e6 1f c1 c9 3e ff 32 d4 b9 21 d8 b9 71 2a 43 af 22 d9 b9 cd fe b1 cd a1 af e 0 cd 5 b2 cd f5 b1 ca 94 b3 2a d9 b9 eb 1a fe e5 ca 4a b3 d5 cd 7f b1 d1 d2 94 b3 cd 5e b1 3a d8 b9 4f 6 0 79 b7 ca 83 b3 1a fe 3f ca 7c b3 78 fe d ca 7c b3 fe c 1a ca 73 b3 96 e6 7f c2 2d b3 c3 7c b3 c5 4e cd 7 b3 c1 c2 2d b3 13 23 4 d c3 53 b3 3a ea b9 e6 3 32 45 af 21 d4 b9 7e 17 d0 af 77 c9 cd fe b1 3e ff c3 1 af cd 54 b1 e c cd 18 b3 cd f5 b1 c8 cd 44 b1 cd 5e b1 36 e5 e 0 cd 6b b2 cd c6 b1 cd 2d b3 c3 a4 b3 50 59 79 b0 ca d1 b3 b d5 c5 cd 35 b2 1f d2 ec b3 c1 d1 2a c6 b9 7b 95 7a 9c d2 f4 b3 13 c5 d5 42 4b cd 35 b2 1f d2 ec b3 d1 c1 c3 c0 b3 17 3c cd 64 b2 e1 d1 c9 79 b0 c2 c0 b3 21 0 0 c9 e 0 1e 20 d5 6 0 2a 43 af 9 eb cd 5e b1 c1 cd 4f af cd c3 af c3 c6 b1 cd 54 b1 e c cd 18 b3 2a 43 af 7e 11 10 0 19 77 cd f5 b1 c8 cd 44 b1 e 10 1e c cd 1 b4 cd 2d b3 c3 27 b4 e c cd 18 b3 cd f5 b1 c8 e 0 1e c cd 1 b4 cd 2d b3 c3 40 b4 e f cd 18 b3 cd f5 b1 c8 cd a6 b0 7e f5 e5 cd 5e b1 eb 2a 43 af e 20 d5 cd 4f af cd 78 b1 d1 21 c 0 19 4e 21 f 0 19 46 e1 f1 77 79 be 78 ca 8b b4 3e 0 da 8b b4 3e 80 2a 43 af 11 f 0 19 77 c9 7e 23 b6 2b c0 1a 77 13 23 1a 77 1b 2b c9 af 32 45 af 32 ea b9 32 eb b9 cd 1e b1 c0 cd 69 b1 e6 80 c0 e f cd 18 b3 cd f5 b1 c8 1 10 0 cd 5e b1 9 eb 2a 43 af 9 e 10 3a dd b9 b7 ca e8 b4 7e b7 1a c2 db b4 77 b7 c2 e1 b4 7e 12 be c2 1f b5 c3 fd b4 cd 94 b4 eb cd 94 b4 eb 1a be c2 1f b5 13 23 1a be c2 1f b5 d 13 23 d c2 cd b4 1 ec ff 9 eb 9 1a be da 17 b5 77 1 3 0 9 eb 9 7e 12 3e ff 32 d2 b9 c3 10 b4 21 45 af 35 c9 cd 54 b1 2a 43 af e5 21 ac b9 22 43 af e 1 cd 18 b3 cd f5 b1 e1 22 43 af c8 eb 21 f 0 19 e 11 af 77 23 d c2 46 b5 21 d 0 19 77 cd 8c b1 cd fd b3 c3 78 b1 af 32 d2 b9 cd a2 b4 cd f5 b1 c8 2a 43 af 1 c 0 9 7e 3c e6 1f 77 ca 83 b5 47 3a c5 b9 a0 21 d2 b9 a6 ca 8e b5 c3 ac b5 1 2 0 9 34 7e e6 f ca b6 b5 e f cd 18 b3 cd f5 b1 c2 ac b5 3a d3 b9 3c ca b6 b5 cd 24 b5 cd f5 b1 ca b6 b5 c3 af b5 cd 5a b4 cd bb b0 af c3 1 af cd 5 af c3 78 b1 3e 1 32 d5 b9 3e ff 32 d3 b9 cd bb b0 3a e3 b9 21 e1 b9 be da e6 b5 fe 80 c2 fb b5 cd 5a b5 af 32 e3 b9 3a 45 af b7 c2 fb b5 cd 77 b0 cd 84 b0 ca fb b5 cd 8a b0 cd d1 af cd b2 af c3 d2 b0 c3 5 af 3e 1 32 d5 b9 3e 0 32 d3 b9 cd 54 b1 2a 43 af cd 47 b1 cd bb b0 3a e3 b9 fe 80 d2 5 af cd 77 b0 cd 84 b0 e 0 c2 6e b6 cd 3e b0 32 d7 b9 1 0 0 b7 ca 3b b6 4f b cd 5e b0 44 4d cd be b3 7d b4 c2 48 b6 3e 2 c3 1 af 22 e5 b9 eb 2a 43 af 1 10 0 9 3a dd b9 b7 3a d7 b9 ca 64 b6 cd 64 b1 73 c3 6c b6 4f 6 0 9 9 73 23 72 e 2 3a 45 af b7 c0 c5 cd 8a b0 3a d5 b9 3d 3d c2 bb b6 c1 c5 79 3d 3d c2 bb b6 e5 2a b9 b9 57 77 23 14 f2 8c b6 cd e0 b1 2a e7 b9 e 2 22 e5 b9 c5 cd d1 af c1 cd b8 af 2a e5 b9 e 0 3a c4 b9 47 a5 b8 23 c2 9a b6 e1 22 e5 b9 cd da b1 cd d1 af c1 c5 cd b8 af c1 3a e3 b9 21 e1 b9 be da d2 b6 77 34 e 2 0 0 21 0 0 f5 cd 69 b1 e6 7f 77 f1 fe 7f c2 0 b7 3a d5 b9 fe 1 c2 0 b7 cd d2 b0 cd 5a b5 21 45 af 7e b7 c2 fe b6 3d 32 e3 b9 36 0 c3 d2 b0 af 32 d5 b9 c5 2a 43 af eb 21 21 0 19 7e e6 7f f5 7e 17 23 7e 17 e6 1f 4f 7e 1f 1f 1f 1f e6 f 47 f1 23 6e 2c 2d 2e 6 c2 8b b7 21 20 0 19 77 21 c 0 19 79 96 c2 47 b7 21 e 0 19 78 96 e6 7f ca 7f b7 c5 d5 cd a2 b4 d1 c1 2e 3 3a 45 af 3c ca 84 b7 21 c 0 19 71 21 e 0 19 70 cd 51 b4 3a 45 af 3c c2 7f b7 c1 c5 2e 4 c ca 84 b7 cd 24 b5 2e 5 3a 45 af 3c ca 84 b7 c1 af c3 1 af e5 cd 69 b1 36 c0 e1 c1 7d 32 45 af c3 78 b1 e ff cd 3 b7 cc c1 b5 c9 e 0 cd 3 b7 cc 3 b6 c9 eb 19 4e 6 0 21 c 0 19 7e f e6 80 81 4f 3e 0 88 47 7e f e6 f 80 47 21 e 0 19 7e 87 87 87 87 f5 80 47 f5 e1 7d e1 b5 e6 1 c9 e c cd 18 b3 2a 43 af 11 21 0 19 e5 72 23 72 23 72 cd f5 b1 ca c b8 cd 5e b1 11 f 0 cd a5 b7 e1 e5 5f 79 96 23 78 9e 23 7b 9e da 6 b8 73 2b 70 2b 71 cd 2d b3 c3 e4 b7 e1 c9 2a 43 af 11 20 0 cd a5 b7 21 21 0 19 71 23 70 23 77 c9 2a af b9 3a 42 af 4f cd ea b0 e5 eb cd 59 af e1 cc 47 af 7d 1f d8 2a af b9 4d 44 cd b b1 22 af b9 c3 a3 b2 3a d6 b9 21 42 af be c8 77 c3 21 b8 3e ff 32 de b9 2a 43 af 7e e6 1f 3d 32 d6 b9 fe 1e d2 75 b8 3a 42 af 32 df b9 7e 32 e0 b9 e6 e0 77 cd 45 b8 3a 41 af 2a 43 af b6 77 c9 3e 22 c3 1 af 21 0 0 22 ad b9 22 af b9 af 32 42 af 21 80 0 22 b1 b9 cd da b1 c3 21 b8 cd 72 b1 cd 51 b8 c3 51 b4 cd 51 b8 c3 a2 b4 e 0 eb 7e fe 3f ca c2 b8 cd a6 b0 7e fe 3f c4 72 b1 cd 51 b8 e f cd 18 b3 c3 e9 b1 2a d9 b9 22 43 af cd 51 b8 cd 2d b3 c3 e9 b1 cd 51 b8 cd 9c b3 c3 1 b3 cd 51 b8 c3 bc b5 cd 51 b8 c3 fe b5 cd 72 b1 cd 51 b8 c3 24 b5 cd 51 b8 cd 16 b4 c3 1 b3 2a af b9 c3 29 b9 3a 42 af c3 1 af eb 22 b1 b9 c3 da b1 2a bf b9 c3 29 b9 2a ad b9 c3 29 b9 cd 51 b8 cd 3b b4 c3 1 b3 2a bb b9 22 45 af c9 3a d6 b9 fe ff c2 3b b9 3a 41 af c3 1 af e6 1f 32 41 af c9 cd 51 b8 c3 93 b7 cd 51 b8 c3 9c b7 cd 51 b8 c3 d2 b7 2a 43 af 7d 2f 5f 7c 2f 2a af b9 a4 57 7d a3 5f 2a ad b9 eb 22 af b9 7d a3 6f 7c a2 67 22 ad b9 c9 3a de b9 b7 ca 91 b9 2a 43 af 36 0 3a e0 b9 b7 ca 91 b9 77 3a df b9 32 d6 b9 cd 45 b8 2a f af f9 2a 45 af 7d 44 c9 cd 51 b8 3e 2 32 d5 b9 e 0 cd 7 b7 cc 3 b6 c9 e5 0 0 0 0 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 0 c3 0 diff --git a/branches/s100/Applegate/cpm22.asm b/branches/s100/Applegate/cpm22.asm new file mode 100644 index 00000000..1a8b2766 --- /dev/null +++ b/branches/s100/Applegate/cpm22.asm @@ -0,0 +1,3746 @@ +;************************************************************** +;* +;* C P / M version 2 . 2 +;* +;* Reconstructed from memory image on February 27, 1981 +;* +;* by Clark A. Calkins +;* +;************************************************************** +; +#include "tasm.inc" + +#include "cpm22.inc" + +; +;IOBYTE EQU 3 ;i/o definition byte. +;TDRIVE EQU 4 ;current drive name and user number. +;ENTRY EQU 5 ;entry point for the cp/m bdos. +TFCB EQU 5CH ;default file control block. +TBUFF EQU 80H ;i/o buffer and command line storage. +TBASE EQU 100H ;transiant program storage area. +; +; Set control character equates. +; +CNTRLC EQU 3 ;control-c +CNTRLE EQU 05H ;control-e +BS EQU 08H ;backspace +TAB EQU 09H ;tab +LF EQU 0AH ;line feed +FF EQU 0CH ;form feed +CR EQU 0DH ;carriage return +CNTRLP EQU 10H ;control-p +CNTRLR EQU 12H ;control-r +CNTRLS EQU 13H ;control-s +CNTRLU EQU 15H ;control-u +CNTRLX EQU 18H ;control-x +CNTRLZ EQU 1AH ;control-z (end-of-file mark) +DEL EQU 7FH ;rubout +; +; Set origin for CP/M +; + ORG CCPBASE +; ORG (RAMSIZE-7)*1024 +; +CBASE: JP COMMAND ;execute command processor (ccp). + JP CLEARBUF ;entry to empty input buffer before starting ccp. + +; +; Standard cp/m ccp input buffer. Format is (max length), +; (actual length), (char #1), (char #2), (char #3), etc. +; +INBUFF: DEFB 127 ;length of input buffer. + DEFB 0 ;current length of contents. + TEXT "Copyright" + TEXT " 1979 (c) by Digital Research " + DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +INPOINT:DEFW INBUFF+2 ;input line pointer +NAMEPNT:DEFW 0 ;input line pointer used for error message. Points to +; ;start of name in error. +; +; Routine to print (A) on the console. All registers used. +; +PRINT: LD E,A ;setup bdos call. + LD C,2 + JP ENTRY +; +; Routine to print (A) on the console and to save (BC). +; +PRINTB: PUSH BC + CALL PRINT + POP BC + RET +; +; Routine to send a carriage return, line feed combination +; to the console. +; +CRLF: LD A,CR + CALL PRINTB + LD A,LF + JP PRINTB +; +; Routine to send one space to the console and save (BC). +; +SPACE: LD A,' ' + JP PRINTB +; +; Routine to print character string pointed to be (BC) on the +; console. It must terminate with a null byte. +; +PLINE: PUSH BC + CALL CRLF + POP HL +PLINE2: LD A,(HL) + OR A + RET Z + INC HL + PUSH HL + CALL PRINT + POP HL + JP PLINE2 +; +; Routine to reset the disk system. +; +RESDSK: LD C,13 + JP ENTRY +; +; Routine to select disk (A). +; +DSKSEL: LD E,A + LD C,14 + JP ENTRY +; +; Routine to call bdos and save the return code. The zero +; flag is set on a return of 0ffh. +; +ENTRY1: CALL ENTRY + LD (RTNCODE),A ;save return code. + INC A ;set zero if 0ffh returned. + RET +; +; Routine to open a file. (DE) must point to the FCB. +; +OPEN: LD C,15 + JP ENTRY1 +; +; Routine to open file at (FCB). +; +OPENFCB:XOR A ;clear the record number byte at fcb+32 + LD (FCB+32),A + LD DE,FCB + JP OPEN +; +; Routine to close a file. (DE) points to FCB. +; +CLOSE: LD C,16 + JP ENTRY1 +; +; Routine to search for the first file with ambigueous name +; (DE). +; +SRCHFST:LD C,17 + JP ENTRY1 +; +; Search for the next ambigeous file name. +; +SRCHNXT:LD C,18 + JP ENTRY1 +; +; Search for file at (FCB). +; +SRCHFCB:LD DE,FCB + JP SRCHFST +; +; Routine to delete a file pointed to by (DE). +; +DELETE: LD C,19 + JP ENTRY +; +; Routine to call the bdos and set the zero flag if a zero +; status is returned. +; +ENTRY2: CALL ENTRY + OR A ;set zero flag if appropriate. + RET +; +; Routine to read the next record from a sequential file. +; (DE) points to the FCB. +; +RDREC: LD C,20 + JP ENTRY2 +; +; Routine to read file at (FCB). +; +READFCB:LD DE,FCB + JP RDREC +; +; Routine to write the next record of a sequential file. +; (DE) points to the FCB. +; +WRTREC: LD C,21 + JP ENTRY2 +; +; Routine to create the file pointed to by (DE). +; +CREATE: LD C,22 + JP ENTRY1 +; +; Routine to rename the file pointed to by (DE). Note that +; the new name starts at (DE+16). +; +RENAM: LD C,23 + JP ENTRY +; +; Get the current user code. +; +GETUSR: LD E,0FFH +; +; Routne to get or set the current user code. +; If (E) is FF then this is a GET, else it is a SET. +; +GETSETUC: LD C,32 + JP ENTRY +; +; Routine to set the current drive byte at (TDRIVE). +; +SETCDRV:CALL GETUSR ;get user number + ADD A,A ;and shift into the upper 4 bits. + ADD A,A + ADD A,A + ADD A,A + LD HL,CDRIVE ;now add in the current drive number. + OR (HL) + LD (TDRIVE),A ;and save. + RET +; +; Move currently active drive down to (TDRIVE). +; +MOVECD: LD A,(CDRIVE) + LD (TDRIVE),A + RET +; +; Routine to convert (A) into upper case ascii. Only letters +; are affected. +; +UPPER: CP 'a' ;check for letters in the range of 'a' to 'z'. + RET C + CP '{' + RET NC + AND 5FH ;convert it if found. + RET +; +; Routine to get a line of input. We must check to see if the +; user is in (BATCH) mode. If so, then read the input from file +; ($$$.SUB). At the end, reset to console input. +; +GETINP: LD A,(BATCH) ;if =0, then use console input. + OR A + JP Z,GETINP1 +; +; Use the submit file ($$$.sub) which is prepared by a +; SUBMIT run. It must be on drive (A) and it will be deleted +; if and error occures (like eof). +; + LD A,(CDRIVE) ;select drive 0 if need be. + OR A + LD A,0 ;always use drive A for submit. + CALL NZ,DSKSEL ;select it if required. + LD DE,BATCHFCB + CALL OPEN ;look for it. + JP Z,GETINP1 ;if not there, use normal input. + LD A,(BATCHFCB+15) ;get last record number+1. + DEC A + LD (BATCHFCB+32),A + LD DE,BATCHFCB + CALL RDREC ;read last record. + JP NZ,GETINP1 ;quit on end of file. +; +; Move this record into input buffer. +; + LD DE,INBUFF+1 + LD HL,TBUFF ;data was read into buffer here. + LD B,128 ;all 128 characters may be used. + CALL HL2DE ;(HL) to (DE), (B) bytes. + LD HL,BATCHFCB+14 + LD (HL),0 ;zero out the 's2' byte. + INC HL ;and decrement the record count. + DEC (HL) + LD DE,BATCHFCB ;close the batch file now. + CALL CLOSE + JP Z,GETINP1 ;quit on an error. + LD A,(CDRIVE) ;re-select previous drive if need be. + OR A + CALL NZ,DSKSEL ;don't do needless selects. +; +; Print line just read on console. +; + LD HL,INBUFF+2 + CALL PLINE2 + CALL CHKCON ;check console, quit on a key. + JP Z,GETINP2 ;jump if no key is pressed. +; +; Terminate the submit job on any keyboard input. Delete this +; file such that it is not re-started and jump to normal keyboard +; input section. +; + CALL DELBATCH ;delete the batch file. + JP CMMND1 ;and restart command input. +; +; Get here for normal keyboard input. Delete the submit file +; incase there was one. +; +GETINP1:CALL DELBATCH ;delete file ($$$.sub). + CALL SETCDRV ;reset active disk. + LD C,10 ;get line from console device. + LD DE,INBUFF + CALL ENTRY + CALL MOVECD ;reset current drive (again). +; +; Convert input line to upper case. +; +GETINP2:LD HL,INBUFF+1 + LD B,(HL) ;(B)=character counter. +GETINP3:INC HL + LD A,B ;end of the line? + OR A + JP Z,GETINP4 + LD A,(HL) ;convert to upper case. + CALL UPPER + LD (HL),A + DEC B ;adjust character count. + JP GETINP3 +GETINP4:LD (HL),A ;add trailing null. + LD HL,INBUFF+2 + LD (INPOINT),HL ;reset input line pointer. + RET +; +; Routine to check the console for a key pressed. The zero +; flag is set is none, else the character is returned in (A). +; +CHKCON: LD C,11 ;check console. + CALL ENTRY + OR A + RET Z ;return if nothing. + LD C,1 ;else get character. + CALL ENTRY + OR A ;clear zero flag and return. + RET +; +; Routine to get the currently active drive number. +; +GETDSK: LD C,25 + JP ENTRY +; +; Set the stabdard dma address. +; +STDDMA: LD DE,TBUFF +; +; Routine to set the dma address to (DE). +; +DMASET: LD C,26 + JP ENTRY +; +; Delete the batch file created by SUBMIT. +; +DELBATCH: LD HL,BATCH ;is batch active? + LD A,(HL) + OR A + RET Z + LD (HL),0 ;yes, de-activate it. + XOR A + CALL DSKSEL ;select drive 0 for sure. + LD DE,BATCHFCB ;and delete this file. + CALL DELETE + LD A,(CDRIVE) ;reset current drive. + JP DSKSEL +; +; Check to two strings at (PATTRN1) and (PATTRN2). They must be +; the same or we halt.... +; +VERIFY: LD DE,PATTRN1 ;these are the serial number bytes. + LD HL,PATTRN2 ;ditto, but how could they be different? + LD B,6 ;6 bytes each. +VERIFY1:LD A,(DE) + CP (HL) + JP NZ,HALT ;jump to halt routine. + INC DE + INC HL + DEC B + JP NZ,VERIFY1 + RET +; +; Print back file name with a '?' to indicate a syntax error. +; +SYNERR: CALL CRLF ;end current line. + LD HL,(NAMEPNT) ;this points to name in error. +SYNERR1:LD A,(HL) ;print it until a space or null is found. + CP ' ' + JP Z,SYNERR2 + OR A + JP Z,SYNERR2 + PUSH HL + CALL PRINT + POP HL + INC HL + JP SYNERR1 +SYNERR2:LD A,'?' ;add trailing '?'. + CALL PRINT + CALL CRLF + CALL DELBATCH ;delete any batch file. + JP CMMND1 ;and restart from console input. +; +; Check character at (DE) for legal command input. Note that the +; zero flag is set if the character is a delimiter. +; +CHECK: LD A,(DE) + OR A + RET Z + CP ' ' ;control characters are not legal here. + JP C,SYNERR + RET Z ;check for valid delimiter. + CP '=' + RET Z + CP '_' + RET Z + CP '.' + RET Z + CP ':' + RET Z + CP 3bH ;semicolon; TASM can't do it in a quote + RET Z + CP '<' + RET Z + CP '>' + RET Z + RET +; +; Get the next non-blank character from (DE). +; +NONBLANK: LD A,(DE) + OR A ;string ends with a null. + RET Z + CP ' ' + RET NZ + INC DE + JP NONBLANK +; +; Add (HL)=(HL)+(A) +; +ADDHL: ADD A,L + LD L,A + RET NC ;take care of any carry. + INC H + RET +; +; Convert the first name in (FCB). +; +CONVFST:LD A,0 +; +; Format a file name (convert * to '?', etc.). On return, +; (A)=0 is an unambigeous name was specified. Enter with (A) equal to +; the position within the fcb for the name (either 0 or 16). +; +CONVERT:LD HL,FCB + CALL ADDHL + PUSH HL + PUSH HL + XOR A + LD (CHGDRV),A ;initialize drive change flag. + LD HL,(INPOINT) ;set (HL) as pointer into input line. + EX DE,HL + CALL NONBLANK ;get next non-blank character. + EX DE,HL + LD (NAMEPNT),HL ;save pointer here for any error message. + EX DE,HL + POP HL + LD A,(DE) ;get first character. + OR A + JP Z,CONVRT1 + SBC A,'A'-1 ;might be a drive name, convert to binary. + LD B,A ;and save. + INC DE ;check next character for a ':'. + LD A,(DE) + CP ':' + JP Z,CONVRT2 + DEC DE ;nope, move pointer back to the start of the line. +CONVRT1:LD A,(CDRIVE) + LD (HL),A + JP CONVRT3 +CONVRT2:LD A,B + LD (CHGDRV),A ;set change in drives flag. + LD (HL),B + INC DE +; +; Convert the basic file name. +; +CONVRT3:LD B,08H +CONVRT4:CALL CHECK + JP Z,CONVRT8 + INC HL + CP '*' ;note that an '*' will fill the remaining + JP NZ,CONVRT5 ;field with '?'. + LD (HL),'?' + JP CONVRT6 +CONVRT5:LD (HL),A + INC DE +CONVRT6:DEC B + JP NZ,CONVRT4 +CONVRT7:CALL CHECK ;get next delimiter. + JP Z,GETEXT + INC DE + JP CONVRT7 +CONVRT8:INC HL ;blank fill the file name. + LD (HL),' ' + DEC B + JP NZ,CONVRT8 +; +; Get the extension and convert it. +; +GETEXT: LD B,03H + CP '.' + JP NZ,GETEXT5 + INC DE +GETEXT1:CALL CHECK + JP Z,GETEXT5 + INC HL + CP '*' + JP NZ,GETEXT2 + LD (HL),'?' + JP GETEXT3 +GETEXT2:LD (HL),A + INC DE +GETEXT3:DEC B + JP NZ,GETEXT1 +GETEXT4:CALL CHECK + JP Z,GETEXT6 + INC DE + JP GETEXT4 +GETEXT5:INC HL + LD (HL),' ' + DEC B + JP NZ,GETEXT5 +GETEXT6:LD B,3 +GETEXT7:INC HL + LD (HL),0 + DEC B + JP NZ,GETEXT7 + EX DE,HL + LD (INPOINT),HL ;save input line pointer. + POP HL +; +; Check to see if this is an ambigeous file name specification. +; Set the (A) register to non zero if it is. +; + LD BC,11 ;set name length. +GETEXT8:INC HL + LD A,(HL) + CP '?' ;any question marks? + JP NZ,GETEXT9 + INC B ;count them. +GETEXT9:DEC C + JP NZ,GETEXT8 + LD A,B + OR A + RET +; +; CP/M command table. Note commands can be either 3 or 4 characters long. +; +NUMCMDS EQU 6 ;number of commands +CMDTBL: DEFB "DIR " + DEFB "ERA " + DEFB "TYPE" + DEFB "SAVE" + DEFB "REN " + DEFB "USER" +; +; The following six bytes must agree with those at (PATTRN2) +; or cp/m will HALT. Why? +; +PATTRN1:DEFB 0,22,0,0,0,0 ;(* serial number bytes *). +; +; Search the command table for a match with what has just +; been entered. If a match is found, then we jump to the +; proper section. Else jump to (UNKNOWN). +; On return, the (C) register is set to the command number +; that matched (or NUMCMDS+1 if no match). +; +SEARCH: LD HL,CMDTBL + LD C,0 +SEARCH1:LD A,C + CP NUMCMDS ;this commands exists. + RET NC + LD DE,FCB+1 ;check this one. + LD B,4 ;max command length. +SEARCH2:LD A,(DE) + CP (HL) + JP NZ,SEARCH3 ;not a match. + INC DE + INC HL + DEC B + JP NZ,SEARCH2 + LD A,(DE) ;allow a 3 character command to match. + CP ' ' + JP NZ,SEARCH4 + LD A,C ;set return register for this command. + RET +SEARCH3:INC HL + DEC B + JP NZ,SEARCH3 +SEARCH4:INC C + JP SEARCH1 +; +; Set the input buffer to empty and then start the command +; processor (ccp). +; +CLEARBUF: XOR A + LD (INBUFF+1),A ;second byte is actual length. +; +;************************************************************** +;* +;* +;* C C P - C o n s o l e C o m m a n d P r o c e s s o r +;* +;************************************************************** +;* +COMMAND:LD SP,CCPSTACK ;setup stack area. + PUSH BC ;note that (C) should be equal to: + LD A,C ;(uuuudddd) where 'uuuu' is the user number + RRA ;and 'dddd' is the drive number. + RRA + RRA + RRA + AND 0FH ;isolate the user number. + LD E,A + CALL GETSETUC ;and set it. + CALL RESDSK ;reset the disk system. + LD (BATCH),A ;clear batch mode flag. + POP BC + LD A,C + AND 0FH ;isolate the drive number. + LD (CDRIVE),A ;and save. + CALL DSKSEL ;...and select. + LD A,(INBUFF+1) + OR A ;anything in input buffer already? + JP NZ,CMMND2 ;yes, we just process it. +; +; Entry point to get a command line from the console. +; +CMMND1: LD SP,CCPSTACK ;set stack straight. + CALL CRLF ;start a new line on the screen. + CALL GETDSK ;get current drive. + ADD A,'A' + CALL PRINT ;print current drive. + LD A,'>' + CALL PRINT ;and add prompt. + CALL GETINP ;get line from user. +; +; Process command line here. +; +CMMND2: LD DE,TBUFF + CALL DMASET ;set standard dma address. + CALL GETDSK + LD (CDRIVE),A ;set current drive. + CALL CONVFST ;convert name typed in. + CALL NZ,SYNERR ;wild cards are not allowed. + LD A,(CHGDRV) ;if a change in drives was indicated, + OR A ;then treat this as an unknown command + JP NZ,UNKNOWN ;which gets executed. + CALL SEARCH ;else search command table for a match. +; +; Note that an unknown command returns +; with (A) pointing to the last address +; in our table which is (UNKNOWN). +; + LD HL,CMDADR ;now, look thru our address table for command (A). + LD E,A ;set (DE) to command number. + LD D,0 + ADD HL,DE + ADD HL,DE ;(HL)=(CMDADR)+2*(command number). + LD A,(HL) ;now pick out this address. + INC HL + LD H,(HL) + LD L,A + JP (HL) ;now execute it. +; +; CP/M command address table. +; +CMDADR: DEFW DIRECT,ERASE,TYPE,SAVE + DEFW RENAME,USER,UNKNOWN +; +; Halt the system. Reason for this is unknown at present. +; +HALT: LD HL,76F3H ;'DI HLT' instructions. + LD (CBASE),HL + LD HL,CBASE + JP (HL) +; +; Read error while TYPEing a file. +; +RDERROR:LD BC,RDERR + JP PLINE +RDERR: DEFB "Read error",0 +; +; Required file was not located. +; +NONE: LD BC,NOFILE + JP PLINE +NOFILE: DEFB "No file",0 +; +; Decode a command of the form 'A>filename number{ filename}. +; Note that a drive specifier is not allowed on the first file +; name. On return, the number is in register (A). Any error +; causes 'filename?' to be printed and the command is aborted. +; +DECODE: CALL CONVFST ;convert filename. + LD A,(CHGDRV) ;do not allow a drive to be specified. + OR A + JP NZ,SYNERR + LD HL,FCB+1 ;convert number now. + LD BC,11 ;(B)=sum register, (C)=max digit count. +DECODE1:LD A,(HL) + CP ' ' ;a space terminates the numeral. + JP Z,DECODE3 + INC HL + SUB '0' ;make binary from ascii. + CP 10 ;legal digit? + JP NC,SYNERR + LD D,A ;yes, save it in (D). + LD A,B ;compute (B)=(B)*10 and check for overflow. + AND 0E0H + JP NZ,SYNERR + LD A,B + RLCA + RLCA + RLCA ;(A)=(B)*8 + ADD A,B ;.......*9 + JP C,SYNERR + ADD A,B ;.......*10 + JP C,SYNERR + ADD A,D ;add in new digit now. +DECODE2:JP C,SYNERR + LD B,A ;and save result. + DEC C ;only look at 11 digits. + JP NZ,DECODE1 + RET +DECODE3:LD A,(HL) ;spaces must follow (why?). + CP ' ' + JP NZ,SYNERR + INC HL +DECODE4:DEC C + JP NZ,DECODE3 + LD A,B ;set (A)=the numeric value entered. + RET +; +; Move 3 bytes from (HL) to (DE). Note that there is only +; one reference to this at (A2D5h). +; +MOVE3: LD B,3 +; +; Move (B) bytes from (HL) to (DE). +; +HL2DE: LD A,(HL) + LD (DE),A + INC HL + INC DE + DEC B + JP NZ,HL2DE + RET +; +; Compute (HL)=(TBUFF)+(A)+(C) and get the byte that's here. +; +EXTRACT:LD HL,TBUFF + ADD A,C + CALL ADDHL + LD A,(HL) + RET +; +; Check drive specified. If it means a change, then the new +; drive will be selected. In any case, the drive byte of the +; fcb will be set to null (means use current drive). +; +DSELECT:XOR A ;null out first byte of fcb. + LD (FCB),A + LD A,(CHGDRV) ;a drive change indicated? + OR A + RET Z + DEC A ;yes, is it the same as the current drive? + LD HL,CDRIVE + CP (HL) + RET Z + JP DSKSEL ;no. Select it then. +; +; Check the drive selection and reset it to the previous +; drive if it was changed for the preceeding command. +; +RESETDR:LD A,(CHGDRV) ;drive change indicated? + OR A + RET Z + DEC A ;yes, was it a different drive? + LD HL,CDRIVE + CP (HL) + RET Z + LD A,(CDRIVE) ;yes, re-select our old drive. + JP DSKSEL +; +;************************************************************** +;* +;* D I R E C T O R Y C O M M A N D +;* +;************************************************************** +; +DIRECT: CALL CONVFST ;convert file name. + CALL DSELECT ;select indicated drive. + LD HL,FCB+1 ;was any file indicated? + LD A,(HL) + CP ' ' + JP NZ,DIRECT2 + LD B,11 ;no. Fill field with '?' - same as *.*. +DIRECT1:LD (HL),'?' + INC HL + DEC B + JP NZ,DIRECT1 +DIRECT2:LD E,0 ;set initial cursor position. + PUSH DE + CALL SRCHFCB ;get first file name. + CALL Z,NONE ;none found at all? +DIRECT3:JP Z,DIRECT9 ;terminate if no more names. + LD A,(RTNCODE) ;get file's position in segment (0-3). + RRCA + RRCA + RRCA + AND 60H ;(A)=position*32 + LD C,A + LD A,10 + CALL EXTRACT ;extract the tenth entry in fcb. + RLA ;check system file status bit. + JP C,DIRECT8 ;we don't list them. + POP DE + LD A,E ;bump name count. + INC E + PUSH DE + AND 03H ;at end of line? + PUSH AF + JP NZ,DIRECT4 + CALL CRLF ;yes, end this line and start another. + PUSH BC + CALL GETDSK ;start line with ('A:'). + POP BC + ADD A,'A' + CALL PRINTB + LD A,':' + CALL PRINTB + JP DIRECT5 +DIRECT4:CALL SPACE ;add seperator between file names. + LD A,':' + CALL PRINTB +DIRECT5:CALL SPACE + LD B,1 ;'extract' each file name character at a time. +DIRECT6:LD A,B + CALL EXTRACT + AND 7FH ;strip bit 7 (status bit). + CP ' ' ;are we at the end of the name? + JP NZ,DRECT65 + POP AF ;yes, don't print spaces at the end of a line. + PUSH AF + CP 3 + JP NZ,DRECT63 + LD A,9 ;first check for no extension. + CALL EXTRACT + AND 7FH + CP ' ' + JP Z,DIRECT7 ;don't print spaces. +DRECT63:LD A,' ' ;else print them. +DRECT65:CALL PRINTB + INC B ;bump to next character psoition. + LD A,B + CP 12 ;end of the name? + JP NC,DIRECT7 + CP 9 ;nope, starting extension? + JP NZ,DIRECT6 + CALL SPACE ;yes, add seperating space. + JP DIRECT6 +DIRECT7:POP AF ;get the next file name. +DIRECT8:CALL CHKCON ;first check console, quit on anything. + JP NZ,DIRECT9 + CALL SRCHNXT ;get next name. + JP DIRECT3 ;and continue with our list. +DIRECT9:POP DE ;restore the stack and return to command level. + JP GETBACK +; +;************************************************************** +;* +;* E R A S E C O M M A N D +;* +;************************************************************** +; +ERASE: CALL CONVFST ;convert file name. + CP 11 ;was '*.*' entered? + JP NZ,ERASE1 + LD BC,YESNO ;yes, ask for confirmation. + CALL PLINE + CALL GETINP + LD HL,INBUFF+1 + DEC (HL) ;must be exactly 'y'. + JP NZ,CMMND1 + INC HL + LD A,(HL) + CP 'Y' + JP NZ,CMMND1 + INC HL + LD (INPOINT),HL ;save input line pointer. +ERASE1: CALL DSELECT ;select desired disk. + LD DE,FCB + CALL DELETE ;delete the file. + INC A + CALL Z,NONE ;not there? + JP GETBACK ;return to command level now. +YESNO: DEFB "All (y/n)?",0 +; +;************************************************************** +;* +;* T Y P E C O M M A N D +;* +;************************************************************** +; +TYPE: CALL CONVFST ;convert file name. + JP NZ,SYNERR ;wild cards not allowed. + CALL DSELECT ;select indicated drive. + CALL OPENFCB ;open the file. + JP Z,TYPE5 ;not there? + CALL CRLF ;ok, start a new line on the screen. + LD HL,NBYTES ;initialize byte counter. + LD (HL),0FFH ;set to read first sector. +TYPE1: LD HL,NBYTES +TYPE2: LD A,(HL) ;have we written the entire sector? + CP 128 + JP C,TYPE3 + PUSH HL ;yes, read in the next one. + CALL READFCB + POP HL + JP NZ,TYPE4 ;end or error? + XOR A ;ok, clear byte counter. + LD (HL),A +TYPE3: INC (HL) ;count this byte. + LD HL,TBUFF ;and get the (A)th one from the buffer (TBUFF). + CALL ADDHL + LD A,(HL) + CP CNTRLZ ;end of file mark? + JP Z,GETBACK + CALL PRINT ;no, print it. + CALL CHKCON ;check console, quit if anything ready. + JP NZ,GETBACK + JP TYPE1 +; +; Get here on an end of file or read error. +; +TYPE4: DEC A ;read error? + JP Z,GETBACK + CALL RDERROR ;yes, print message. +TYPE5: CALL RESETDR ;and reset proper drive + JP SYNERR ;now print file name with problem. +; +;************************************************************** +;* +;* S A V E C O M M A N D +;* +;************************************************************** +; +SAVE: CALL DECODE ;get numeric number that follows SAVE. + PUSH AF ;save number of pages to write. + CALL CONVFST ;convert file name. + JP NZ,SYNERR ;wild cards not allowed. + CALL DSELECT ;select specified drive. + LD DE,FCB ;now delete this file. + PUSH DE + CALL DELETE + POP DE + CALL CREATE ;and create it again. + JP Z,SAVE3 ;can't create? + XOR A ;clear record number byte. + LD (FCB+32),A + POP AF ;convert pages to sectors. + LD L,A + LD H,0 + ADD HL,HL ;(HL)=number of sectors to write. + LD DE,TBASE ;and we start from here. +SAVE1: LD A,H ;done yet? + OR L + JP Z,SAVE2 + DEC HL ;nope, count this and compute the start + PUSH HL ;of the next 128 byte sector. + LD HL,128 + ADD HL,DE + PUSH HL ;save it and set the transfer address. + CALL DMASET + LD DE,FCB ;write out this sector now. + CALL WRTREC + POP DE ;reset (DE) to the start of the last sector. + POP HL ;restore sector count. + JP NZ,SAVE3 ;write error? + JP SAVE1 +; +; Get here after writing all of the file. +; +SAVE2: LD DE,FCB ;now close the file. + CALL CLOSE + INC A ;did it close ok? + JP NZ,SAVE4 +; +; Print out error message (no space). +; +SAVE3: LD BC,NOSPACE + CALL PLINE +SAVE4: CALL STDDMA ;reset the standard dma address. + JP GETBACK +NOSPACE:DEFB "No space",0 +; +;************************************************************** +;* +;* R E N A M E C O M M A N D +;* +;************************************************************** +; +RENAME: CALL CONVFST ;convert first file name. + JP NZ,SYNERR ;wild cards not allowed. + LD A,(CHGDRV) ;remember any change in drives specified. + PUSH AF + CALL DSELECT ;and select this drive. + CALL SRCHFCB ;is this file present? + JP NZ,RENAME6 ;yes, print error message. + LD HL,FCB ;yes, move this name into second slot. + LD DE,FCB+16 + LD B,16 + CALL HL2DE + LD HL,(INPOINT) ;get input pointer. + EX DE,HL + CALL NONBLANK ;get next non blank character. + CP '=' ;only allow an '=' or '_' seperator. + JP Z,RENAME1 + CP '_' + JP NZ,RENAME5 +RENAME1:EX DE,HL + INC HL ;ok, skip seperator. + LD (INPOINT),HL ;save input line pointer. + CALL CONVFST ;convert this second file name now. + JP NZ,RENAME5 ;again, no wild cards. + POP AF ;if a drive was specified, then it + LD B,A ;must be the same as before. + LD HL,CHGDRV + LD A,(HL) + OR A + JP Z,RENAME2 + CP B + LD (HL),B + JP NZ,RENAME5 ;they were different, error. +RENAME2:LD (HL),B ; reset as per the first file specification. + XOR A + LD (FCB),A ;clear the drive byte of the fcb. +RENAME3:CALL SRCHFCB ;and go look for second file. + JP Z,RENAME4 ;doesn't exist? + LD DE,FCB + CALL RENAM ;ok, rename the file. + JP GETBACK +; +; Process rename errors here. +; +RENAME4:CALL NONE ;file not there. + JP GETBACK +RENAME5:CALL RESETDR ;bad command format. + JP SYNERR +RENAME6:LD BC,EXISTS ;destination file already exists. + CALL PLINE + JP GETBACK +EXISTS: DEFB "File exists",0 +; +;************************************************************** +;* +;* U S E R C O M M A N D +;* +;************************************************************** +; +USER: CALL DECODE ;get numeric value following command. + CP 16 ;legal user number? + JP NC,SYNERR + LD E,A ;yes but is there anything else? + LD A,(FCB+1) + CP ' ' + JP Z,SYNERR ;yes, that is not allowed. + CALL GETSETUC ;ok, set user code. + JP GETBACK1 +; +;************************************************************** +;* +;* T R A N S I A N T P R O G R A M C O M M A N D +;* +;************************************************************** +; +UNKNOWN:CALL VERIFY ;check for valid system (why?). + LD A,(FCB+1) ;anything to execute? + CP ' ' + JP NZ,UNKWN1 + LD A,(CHGDRV) ;nope, only a drive change? + OR A + JP Z,GETBACK1 ;neither??? + DEC A + LD (CDRIVE),A ;ok, store new drive. + CALL MOVECD ;set (TDRIVE) also. + CALL DSKSEL ;and select this drive. + JP GETBACK1 ;then return. +; +; Here a file name was typed. Prepare to execute it. +; +UNKWN1: LD DE,FCB+9 ;an extension specified? + LD A,(DE) + CP ' ' + JP NZ,SYNERR ;yes, not allowed. +UNKWN2: PUSH DE + CALL DSELECT ;select specified drive. + POP DE + LD HL,COMFILE ;set the extension to 'COM'. + CALL MOVE3 + CALL OPENFCB ;and open this file. + JP Z,UNKWN9 ;not present? +; +; Load in the program. +; + LD HL,TBASE ;store the program starting here. +UNKWN3: PUSH HL + EX DE,HL + CALL DMASET ;set transfer address. + LD DE,FCB ;and read the next record. + CALL RDREC + JP NZ,UNKWN4 ;end of file or read error? + POP HL ;nope, bump pointer for next sector. + LD DE,128 + ADD HL,DE + LD DE,CBASE ;enough room for the whole file? + LD A,L + SUB E + LD A,H + SBC A,D + JP NC,UNKWN0 ;no, it can't fit. + JP UNKWN3 +; +; Get here after finished reading. +; +UNKWN4: POP HL + DEC A ;normal end of file? + JP NZ,UNKWN0 + CALL RESETDR ;yes, reset previous drive. + CALL CONVFST ;convert the first file name that follows + LD HL,CHGDRV ;command name. + PUSH HL + LD A,(HL) ;set drive code in default fcb. + LD (FCB),A + LD A,16 ;put second name 16 bytes later. + CALL CONVERT ;convert second file name. + POP HL + LD A,(HL) ;and set the drive for this second file. + LD (FCB+16),A + XOR A ;clear record byte in fcb. + LD (FCB+32),A + LD DE,TFCB ;move it into place at(005Ch). + LD HL,FCB + LD B,33 + CALL HL2DE + LD HL,INBUFF+2 ;now move the remainder of the input +UNKWN5: LD A,(HL) ;line down to (0080h). Look for a non blank. + OR A ;or a null. + JP Z,UNKWN6 + CP ' ' + JP Z,UNKWN6 + INC HL + JP UNKWN5 +; +; Do the line move now. It ends in a null byte. +; +UNKWN6: LD B,0 ;keep a character count. + LD DE,TBUFF+1 ;data gets put here. +UNKWN7: LD A,(HL) ;move it now. + LD (DE),A + OR A + JP Z,UNKWN8 + INC B + INC HL + INC DE + JP UNKWN7 +UNKWN8: LD A,B ;now store the character count. + LD (TBUFF),A + CALL CRLF ;clean up the screen. + CALL STDDMA ;set standard transfer address. + CALL SETCDRV ;reset current drive. + CALL TBASE ;and execute the program. +; +; Transiant programs return here (or reboot). +; + LD SP,BATCH ;set stack first off. + CALL MOVECD ;move current drive into place (TDRIVE). + CALL DSKSEL ;and reselect it. + JP CMMND1 ;back to comand mode. +; +; Get here if some error occured. +; +UNKWN9: CALL RESETDR ;inproper format. + JP SYNERR +UNKWN0: LD BC,BADLOAD ;read error or won't fit. + CALL PLINE + JP GETBACK +BADLOAD:DEFB "Bad load",0 +COMFILE:DEFB "COM" ;command file extension. +; +; Get here to return to command level. We will reset the +; previous active drive and then either return to command +; level directly or print error message and then return. +; +GETBACK:CALL RESETDR ;reset previous drive. +GETBACK1: CALL CONVFST ;convert first name in (FCB). + LD A,(FCB+1) ;if this was just a drive change request, + SUB ' ' ;make sure it was valid. + LD HL,CHGDRV + OR (HL) + JP NZ,SYNERR + JP CMMND1 ;ok, return to command level. +; +; ccp stack area. +; + DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +CCPSTACK EQU $ ;end of ccp stack area. +; +; Batch (or SUBMIT) processing information storage. +; +BATCH: DEFB 0 ;batch mode flag (0=not active). +BATCHFCB: DEFB 0 + TEXT "$$$ SUB" + DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +; +; File control block setup by the CCP. +; +FCB: DEFB 0 + TEXT " " + DEFB 0,0,0,0,0, + TEXT " " + DEFB 0,0,0,0,0 +RTNCODE:DEFB 0 ;status returned from bdos call. +CDRIVE: DEFB 0 ;currently active drive. +CHGDRV: DEFB 0 ;change in drives flag (0=no change). +NBYTES: DEFW 0 ;byte counter used by TYPE. +; +; Room for expansion? +; + DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0 +; +; Note that the following six bytes must match those at +; (PATTRN1) or cp/m will HALT. Why? +; +PATTRN2:DEFB 0,22,0,0,0,0 ;(* serial number bytes *). +; +;************************************************************** +;* +;* B D O S E N T R Y +;* +;************************************************************** +; +FBASE: JP FBASE1 +; +; Bdos error table. +; +BADSCTR:DEFW ERROR1 ;bad sector on read or write. +BADSLCT:DEFW ERROR2 ;bad disk select. +RODISK: DEFW ERROR3 ;disk is read only. +ROFILE: DEFW ERROR4 ;file is read only. +; +; Entry into bdos. (DE) or (E) are the parameters passed. The +; function number desired is in register (C). +; +FBASE1: EX DE,HL ;save the (DE) parameters. + LD (PARAMS),HL + EX DE,HL + LD A,E ;and save register (E) in particular. + LD (EPARAM),A + LD HL,0 + LD (STATUS),HL ;clear return status. + ADD HL,SP + LD (USRSTACK),HL ;save users stack pointer. + LD SP,STKAREA ;and set our own. + XOR A ;clear auto select storage space. + LD (AUTOFLAG),A + LD (AUTO),A + LD HL,GOBACK ;set return address. + PUSH HL + LD A,C ;get function number. + CP NFUNCTS ;valid function number? + RET NC + LD C,E ;keep single register function here. + LD HL,FUNCTNS ;now look thru the function table. + LD E,A + LD D,0 ;(DE)=function number. + ADD HL,DE + ADD HL,DE ;(HL)=(start of table)+2*(function number). + LD E,(HL) + INC HL + LD D,(HL) ;now (DE)=address for this function. + LD HL,(PARAMS) ;retrieve parameters. + EX DE,HL ;now (DE) has the original parameters. + JP (HL) ;execute desired function. +; +; BDOS function jump table. +; +NFUNCTS EQU 41 ;number of functions in followin table. +; +FUNCTNS:DEFW WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB + DEFW SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL + DEFW CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE + DEFW RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR + DEFW GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN + DEFW RTN,WTSPECL +; +; Bdos error message section. +; +ERROR1: LD HL,BADSEC ;bad sector message. + CALL PRTERR ;print it and get a 1 char responce. + CP CNTRLC ;re-boot request (control-c)? + JP Z,0 ;yes. + RET ;no, return to retry i/o function. +; +ERROR2: LD HL,BADSEL ;bad drive selected. + JP ERROR5 +; +ERROR3: LD HL,DISKRO ;disk is read only. + JP ERROR5 +; +ERROR4: LD HL,FILERO ;file is read only. +; +ERROR5: CALL PRTERR + JP 0 ;always reboot on these errors. +; +BDOSERR:DEFB "Bdos Err On " +BDOSDRV:DEFB " : $" +BADSEC: DEFB "Bad Sector$" +BADSEL: DEFB "Select$" +FILERO: DEFB "File " +DISKRO: DEFB "R/O$" +; +; Print bdos error message. +; +PRTERR: PUSH HL ;save second message pointer. + CALL OUTCRLF ;send (cr)(lf). + LD A,(ACTIVE) ;get active drive. + ADD A,'A' ;make ascii. + LD (BDOSDRV),A ;and put in message. + LD BC,BDOSERR ;and print it. + CALL PRTMESG + POP BC ;print second message line now. + CALL PRTMESG +; +; Get an input character. We will check our 1 character +; buffer first. This may be set by the console status routine. +; +GETCHAR:LD HL,CHARBUF ;check character buffer. + LD A,(HL) ;anything present already? + LD (HL),0 ;...either case clear it. + OR A + RET NZ ;yes, use it. + JP CONIN ;nope, go get a character responce. +; +; Input and echo a character. +; +GETECHO:CALL GETCHAR ;input a character. + CALL CHKCHAR ;carriage control? + RET C ;no, a regular control char so don't echo. + PUSH AF ;ok, save character now. + LD C,A + CALL OUTCON ;and echo it. + POP AF ;get character and return. + RET +; +; Check character in (A). Set the zero flag on a carriage +; control character and the carry flag on any other control +; character. +; +CHKCHAR:CP CR ;check for carriage return, line feed, backspace, + RET Z ;or a tab. + CP LF + RET Z + CP TAB + RET Z + CP BS + RET Z + CP ' ' ;other control char? Set carry flag. + RET +; +; Check the console during output. Halt on a control-s, then +; reboot on a control-c. If anything else is ready, clear the +; zero flag and return (the calling routine may want to do +; something). +; +CKCONSOL: LD A,(CHARBUF) ;check buffer. + OR A ;if anything, just return without checking. + JP NZ,CKCON2 + CALL CONST ;nothing in buffer. Check console. + AND 01H ;look at bit 0. + RET Z ;return if nothing. + CALL CONIN ;ok, get it. + CP CNTRLS ;if not control-s, return with zero cleared. + JP NZ,CKCON1 + CALL CONIN ;halt processing until another char + CP CNTRLC ;is typed. Control-c? + JP Z,0 ;yes, reboot now. + XOR A ;no, just pretend nothing was ever ready. + RET +CKCON1: LD (CHARBUF),A ;save character in buffer for later processing. +CKCON2: LD A,1 ;set (A) to non zero to mean something is ready. + RET +; +; Output (C) to the screen. If the printer flip-flop flag +; is set, we will send character to printer also. The console +; will be checked in the process. +; +OUTCHAR:LD A,(OUTFLAG) ;check output flag. + OR A ;anything and we won't generate output. + JP NZ,OUTCHR1 + PUSH BC + CALL CKCONSOL ;check console (we don't care whats there). + POP BC + PUSH BC + CALL CONOUT ;output (C) to the screen. + POP BC + PUSH BC + LD A,(PRTFLAG) ;check printer flip-flop flag. + OR A + CALL NZ,LIST ;print it also if non-zero. + POP BC +OUTCHR1:LD A,C ;update cursors position. + LD HL,CURPOS + CP DEL ;rubouts don't do anything here. + RET Z + INC (HL) ;bump line pointer. + CP ' ' ;and return if a normal character. + RET NC + DEC (HL) ;restore and check for the start of the line. + LD A,(HL) + OR A + RET Z ;ingnore control characters at the start of the line. + LD A,C + CP BS ;is it a backspace? + JP NZ,OUTCHR2 + DEC (HL) ;yes, backup pointer. + RET +OUTCHR2:CP LF ;is it a line feed? + RET NZ ;ignore anything else. + LD (HL),0 ;reset pointer to start of line. + RET +; +; Output (A) to the screen. If it is a control character +; (other than carriage control), use ^x format. +; +SHOWIT: LD A,C + CALL CHKCHAR ;check character. + JP NC,OUTCON ;not a control, use normal output. + PUSH AF + LD C,'^' ;for a control character, preceed it with '^'. + CALL OUTCHAR + POP AF + OR '@' ;and then use the letter equivelant. + LD C,A +; +; Function to output (C) to the console device and expand tabs +; if necessary. +; +OUTCON: LD A,C + CP TAB ;is it a tab? + JP NZ,OUTCHAR ;use regular output. +OUTCON1:LD C,' ' ;yes it is, use spaces instead. + CALL OUTCHAR + LD A,(CURPOS) ;go until the cursor is at a multiple of 8 + + AND 07H ;position. + JP NZ,OUTCON1 + RET +; +; Echo a backspace character. Erase the prevoius character +; on the screen. +; +BACKUP: CALL BACKUP1 ;backup the screen 1 place. + LD C,' ' ;then blank that character. + CALL CONOUT +BACKUP1:LD C,BS ;then back space once more. + JP CONOUT +; +; Signal a deleted line. Print a '#' at the end and start +; over. +; +NEWLINE:LD C,'#' + CALL OUTCHAR ;print this. + CALL OUTCRLF ;start new line. +NEWLN1: LD A,(CURPOS) ;move the cursor to the starting position. + LD HL,STARTING + CP (HL) + RET NC ;there yet? + LD C,' ' + CALL OUTCHAR ;nope, keep going. + JP NEWLN1 +; +; Output a (cr) (lf) to the console device (screen). +; +OUTCRLF:LD C,CR + CALL OUTCHAR + LD C,LF + JP OUTCHAR +; +; Print message pointed to by (BC). It will end with a '$'. +; +PRTMESG:LD A,(BC) ;check for terminating character. + CP '$' + RET Z + INC BC + PUSH BC ;otherwise, bump pointer and print it. + LD C,A + CALL OUTCON + POP BC + JP PRTMESG +; +; Function to execute a buffered read. +; +RDBUFF: LD A,(CURPOS) ;use present location as starting one. + LD (STARTING),A + LD HL,(PARAMS) ;get the maximum buffer space. + LD C,(HL) + INC HL ;point to first available space. + PUSH HL ;and save. + LD B,0 ;keep a character count. +RDBUF1: PUSH BC + PUSH HL +RDBUF2: CALL GETCHAR ;get the next input character. + AND 7FH ;strip bit 7. + POP HL ;reset registers. + POP BC + CP CR ;en of the line? + JP Z,RDBUF17 + CP LF + JP Z,RDBUF17 + CP BS ;how about a backspace? + JP NZ,RDBUF3 + LD A,B ;yes, but ignore at the beginning of the line. + OR A + JP Z,RDBUF1 + DEC B ;ok, update counter. + LD A,(CURPOS) ;if we backspace to the start of the line, + LD (OUTFLAG),A ;treat as a cancel (control-x). + JP RDBUF10 +RDBUF3: CP DEL ;user typed a rubout? + JP NZ,RDBUF4 + LD A,B ;ignore at the start of the line. + OR A + JP Z,RDBUF1 + LD A,(HL) ;ok, echo the prevoius character. + DEC B ;and reset pointers (counters). + DEC HL + JP RDBUF15 +RDBUF4: CP CNTRLE ;physical end of line? + JP NZ,RDBUF5 + PUSH BC ;yes, do it. + PUSH HL + CALL OUTCRLF + XOR A ;and update starting position. + LD (STARTING),A + JP RDBUF2 +RDBUF5: CP CNTRLP ;control-p? + JP NZ,RDBUF6 + PUSH HL ;yes, flip the print flag filp-flop byte. + LD HL,PRTFLAG + LD A,1 ;PRTFLAG=1-PRTFLAG + SUB (HL) + LD (HL),A + POP HL + JP RDBUF1 +RDBUF6: CP CNTRLX ;control-x (cancel)? + JP NZ,RDBUF8 + POP HL +RDBUF7: LD A,(STARTING) ;yes, backup the cursor to here. + LD HL,CURPOS + CP (HL) + JP NC,RDBUFF ;done yet? + DEC (HL) ;no, decrement pointer and output back up one space. + CALL BACKUP + JP RDBUF7 +RDBUF8: CP CNTRLU ;cntrol-u (cancel line)? + JP NZ,RDBUF9 + CALL NEWLINE ;start a new line. + POP HL + JP RDBUFF +RDBUF9: CP CNTRLR ;control-r? + JP NZ,RDBUF14 +RDBUF10:PUSH BC ;yes, start a new line and retype the old one. + CALL NEWLINE + POP BC + POP HL + PUSH HL + PUSH BC +RDBUF11:LD A,B ;done whole line yet? + OR A + JP Z,RDBUF12 + INC HL ;nope, get next character. + LD C,(HL) + DEC B ;count it. + PUSH BC + PUSH HL + CALL SHOWIT ;and display it. + POP HL + POP BC + JP RDBUF11 +RDBUF12:PUSH HL ;done with line. If we were displaying + LD A,(OUTFLAG) ;then update cursor position. + OR A + JP Z,RDBUF2 + LD HL,CURPOS ;because this line is shorter, we must + SUB (HL) ;back up the cursor (not the screen however) + LD (OUTFLAG),A ;some number of positions. +RDBUF13:CALL BACKUP ;note that as long as (OUTFLAG) is non + LD HL,OUTFLAG ;zero, the screen will not be changed. + DEC (HL) + JP NZ,RDBUF13 + JP RDBUF2 ;now just get the next character. +; +; Just a normal character, put this in our buffer and echo. +; +RDBUF14:INC HL + LD (HL),A ;store character. + INC B ;and count it. +RDBUF15:PUSH BC + PUSH HL + LD C,A ;echo it now. + CALL SHOWIT + POP HL + POP BC + LD A,(HL) ;was it an abort request? + CP CNTRLC ;control-c abort? + LD A,B + JP NZ,RDBUF16 + CP 1 ;only if at start of line. + JP Z,0 +RDBUF16:CP C ;nope, have we filled the buffer? + JP C,RDBUF1 +RDBUF17:POP HL ;yes end the line and return. + LD (HL),B + LD C,CR + JP OUTCHAR ;output (cr) and return. +; +; Function to get a character from the console device. +; +GETCON: CALL GETECHO ;get and echo. + JP SETSTAT ;save status and return. +; +; Function to get a character from the tape reader device. +; +GETRDR: CALL READER ;get a character from reader, set status and return. + JP SETSTAT +; +; Function to perform direct console i/o. If (C) contains (FF) +; then this is an input request. If (C) contains (FE) then +; this is a status request. Otherwise we are to output (C). +; +DIRCIO: LD A,C ;test for (FF). + INC A + JP Z,DIRC1 + INC A ;test for (FE). + JP Z,CONST + JP CONOUT ;just output (C). +DIRC1: CALL CONST ;this is an input request. + OR A + JP Z,GOBACK1 ;not ready? Just return (directly). + CALL CONIN ;yes, get character. + JP SETSTAT ;set status and return. +; +; Function to return the i/o byte. +; +GETIOB: LD A,(IOBYTE) + JP SETSTAT +; +; Function to set the i/o byte. +; +SETIOB: LD HL,IOBYTE + LD (HL),C + RET +; +; Function to print the character string pointed to by (DE) +; on the console device. The string ends with a '$'. +; +PRTSTR: EX DE,HL + LD C,L + LD B,H ;now (BC) points to it. + JP PRTMESG +; +; Function to interigate the console device. +; +GETCSTS:CALL CKCONSOL +; +; Get here to set the status and return to the cleanup +; section. Then back to the user. +; +SETSTAT:LD (STATUS),A +RTN: RET +; +; Set the status to 1 (read or write error code). +; +IOERR1: LD A,1 + JP SETSTAT +; +OUTFLAG:DEFB 0 ;output flag (non zero means no output). +STARTING: DEFB 2 ;starting position for cursor. +CURPOS: DEFB 0 ;cursor position (0=start of line). +PRTFLAG:DEFB 0 ;printer flag (control-p toggle). List if non zero. +CHARBUF:DEFB 0 ;single input character buffer. +; +; Stack area for BDOS calls. +; +USRSTACK: DEFW 0 ;save users stack pointer here. +; + DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +STKAREA EQU $ ;end of stack area. +; +USERNO: DEFB 0 ;current user number. +ACTIVE: DEFB 0 ;currently active drive. +PARAMS: DEFW 0 ;save (DE) parameters here on entry. +STATUS: DEFW 0 ;status returned from bdos function. +; +; Select error occured, jump to error routine. +; +SLCTERR:LD HL,BADSLCT +; +; Jump to (HL) indirectly. +; +JUMPHL: LD E,(HL) + INC HL + LD D,(HL) ;now (DE) contain the desired address. + EX DE,HL + JP (HL) +; +; Block move. (DE) to (HL), (C) bytes total. +; +DE2HL: INC C ;is count down to zero? +DE2HL1: DEC C + RET Z ;yes, we are done. + LD A,(DE) ;no, move one more byte. + LD (HL),A + INC DE + INC HL + JP DE2HL1 ;and repeat. +; +; Select the desired drive. +; +SELECT: LD A,(ACTIVE) ;get active disk. + LD C,A + CALL SELDSK ;select it. + LD A,H ;valid drive? + OR L ;valid drive? + RET Z ;return if not. +; +; Here, the BIOS returned the address of the parameter block +; in (HL). We will extract the necessary pointers and save them. +; + LD E,(HL) ;yes, get address of translation table into (DE). + INC HL + LD D,(HL) + INC HL + LD (SCRATCH1),HL ;save pointers to scratch areas. + INC HL + INC HL + LD (SCRATCH2),HL ;ditto. + INC HL + INC HL + LD (SCRATCH3),HL ;ditto. + INC HL + INC HL + EX DE,HL ;now save the translation table address. + LD (XLATE),HL + LD HL,DIRBUF ;put the next 8 bytes here. + LD C,8 ;they consist of the directory buffer + CALL DE2HL ;pointer, parameter block pointer, + LD HL,(DISKPB) ;check and allocation vectors. + EX DE,HL + LD HL,SECTORS ;move parameter block into our ram. + LD C,15 ;it is 15 bytes long. + CALL DE2HL + LD HL,(DSKSIZE) ;check disk size. + LD A,H ;more than 256 blocks on this? + LD HL,BIGDISK + LD (HL),0FFH ;set to samll. + OR A + JP Z,SELECT1 + LD (HL),0 ;wrong, set to large. +SELECT1:LD A,0FFH ;clear the zero flag. + OR A + RET +; +; Routine to home the disk track head and clear pointers. +; +HOMEDRV:CALL HOME ;home the head. + XOR A + LD HL,(SCRATCH2) ;set our track pointer also. + LD (HL),A + INC HL + LD (HL),A + LD HL,(SCRATCH3) ;and our sector pointer. + LD (HL),A + INC HL + LD (HL),A + RET +; +; Do the actual disk read and check the error return status. +; +DOREAD: CALL READ + JP IORET +; +; Do the actual disk write and handle any bios error. +; +DOWRITE:CALL WRITE +IORET: OR A + RET Z ;return unless an error occured. + LD HL,BADSCTR ;bad read/write on this sector. + JP JUMPHL +; +; Routine to select the track and sector that the desired +; block number falls in. +; +TRKSEC: LD HL,(FILEPOS) ;get position of last accessed file + LD C,2 ;in directory and compute sector #. + CALL SHIFTR ;sector #=file-position/4. + LD (BLKNMBR),HL ;save this as the block number of interest. + LD (CKSUMTBL),HL ;what's it doing here too? +; +; if the sector number has already been set (BLKNMBR), enter +; at this point. +; +TRKSEC1:LD HL,BLKNMBR + LD C,(HL) ;move sector number into (BC). + INC HL + LD B,(HL) + LD HL,(SCRATCH3) ;get current sector number and + LD E,(HL) ;move this into (DE). + INC HL + LD D,(HL) + LD HL,(SCRATCH2) ;get current track number. + LD A,(HL) ;and this into (HL). + INC HL + LD H,(HL) + LD L,A +TRKSEC2:LD A,C ;is desired sector before current one? + SUB E + LD A,B + SBC A,D + JP NC,TRKSEC3 + PUSH HL ;yes, decrement sectors by one track. + LD HL,(SECTORS) ;get sectors per track. + LD A,E + SUB L + LD E,A + LD A,D + SBC A,H + LD D,A ;now we have backed up one full track. + POP HL + DEC HL ;adjust track counter. + JP TRKSEC2 +TRKSEC3:PUSH HL ;desired sector is after current one. + LD HL,(SECTORS) ;get sectors per track. + ADD HL,DE ;bump sector pointer to next track. + JP C,TRKSEC4 + LD A,C ;is desired sector now before current one? + SUB L + LD A,B + SBC A,H + JP C,TRKSEC4 + EX DE,HL ;not yes, increment track counter + POP HL ;and continue until it is. + INC HL + JP TRKSEC3 +; +; here we have determined the track number that contains the +; desired sector. +; +TRKSEC4:POP HL ;get track number (HL). + PUSH BC + PUSH DE + PUSH HL + EX DE,HL + LD HL,(OFFSET) ;adjust for first track offset. + ADD HL,DE + LD B,H + LD C,L + CALL SETTRK ;select this track. + POP DE ;reset current track pointer. + LD HL,(SCRATCH2) + LD (HL),E + INC HL + LD (HL),D + POP DE + LD HL,(SCRATCH3) ;reset the first sector on this track. + LD (HL),E + INC HL + LD (HL),D + POP BC + LD A,C ;now subtract the desired one. + SUB E ;to make it relative (1-# sectors/track). + LD C,A + LD A,B + SBC A,D + LD B,A + LD HL,(XLATE) ;translate this sector according to this table. + EX DE,HL + CALL SECTRN ;let the bios translate it. + LD C,L + LD B,H + JP SETSEC ;and select it. +; +; Compute block number from record number (SAVNREC) and +; extent number (SAVEXT). +; +GETBLOCK: LD HL,BLKSHFT ;get logical to physical conversion. + LD C,(HL) ;note that this is base 2 log of ratio. + LD A,(SAVNREC) ;get record number. +GETBLK1:OR A ;compute (A)=(A)/2^BLKSHFT. + RRA + DEC C + JP NZ,GETBLK1 + LD B,A ;save result in (B). + LD A,8 + SUB (HL) + LD C,A ;compute (C)=8-BLKSHFT. + LD A,(SAVEXT) +GETBLK2:DEC C ;compute (A)=SAVEXT*2^(8-BLKSHFT). + JP Z,GETBLK3 + OR A + RLA + JP GETBLK2 +GETBLK3:ADD A,B + RET +; +; Routine to extract the (BC) block byte from the fcb pointed +; to by (PARAMS). If this is a big-disk, then these are 16 bit +; block numbers, else they are 8 bit numbers. +; Number is returned in (HL). +; +EXTBLK: LD HL,(PARAMS) ;get fcb address. + LD DE,16 ;block numbers start 16 bytes into fcb. + ADD HL,DE + ADD HL,BC + LD A,(BIGDISK) ;are we using a big-disk? + OR A + JP Z,EXTBLK1 + LD L,(HL) ;no, extract an 8 bit number from the fcb. + LD H,0 + RET +EXTBLK1:ADD HL,BC ;yes, extract a 16 bit number. + LD E,(HL) + INC HL + LD D,(HL) + EX DE,HL ;return in (HL). + RET +; +; Compute block number. +; +COMBLK: CALL GETBLOCK + LD C,A + LD B,0 + CALL EXTBLK + LD (BLKNMBR),HL + RET +; +; Check for a zero block number (unused). +; +CHKBLK: LD HL,(BLKNMBR) + LD A,L ;is it zero? + OR H + RET +; +; Adjust physical block (BLKNMBR) and convert to logical +; sector (LOGSECT). This is the starting sector of this block. +; The actual sector of interest is then added to this and the +; resulting sector number is stored back in (BLKNMBR). This +; will still have to be adjusted for the track number. +; +LOGICAL:LD A,(BLKSHFT) ;get log2(physical/logical sectors). + LD HL,(BLKNMBR) ;get physical sector desired. +LOGICL1:ADD HL,HL ;compute logical sector number. + DEC A ;note logical sectors are 128 bytes long. + JP NZ,LOGICL1 + LD (LOGSECT),HL ;save logical sector. + LD A,(BLKMASK) ;get block mask. + LD C,A + LD A,(SAVNREC) ;get next sector to access. + AND C ;extract the relative position within physical block. + OR L ;and add it too logical sector. + LD L,A + LD (BLKNMBR),HL ;and store. + RET +; +; Set (HL) to point to extent byte in fcb. +; +SETEXT: LD HL,(PARAMS) + LD DE,12 ;it is the twelth byte. + ADD HL,DE + RET +; +; Set (HL) to point to record count byte in fcb and (DE) to +; next record number byte. +; +SETHLDE:LD HL,(PARAMS) + LD DE,15 ;record count byte (#15). + ADD HL,DE + EX DE,HL + LD HL,17 ;next record number (#32). + ADD HL,DE + RET +; +; Save current file data from fcb. +; +STRDATA:CALL SETHLDE + LD A,(HL) ;get and store record count byte. + LD (SAVNREC),A + EX DE,HL + LD A,(HL) ;get and store next record number byte. + LD (SAVNXT),A + CALL SETEXT ;point to extent byte. + LD A,(EXTMASK) ;get extent mask. + AND (HL) + LD (SAVEXT),A ;and save extent here. + RET +; +; Set the next record to access. If (MODE) is set to 2, then +; the last record byte (SAVNREC) has the correct number to access. +; For sequential access, (MODE) will be equal to 1. +; +SETNREC:CALL SETHLDE + LD A,(MODE) ;get sequential flag (=1). + CP 2 ;a 2 indicates that no adder is needed. + JP NZ,STNREC1 + XOR A ;clear adder (random access?). +STNREC1:LD C,A + LD A,(SAVNREC) ;get last record number. + ADD A,C ;increment record count. + LD (HL),A ;and set fcb's next record byte. + EX DE,HL + LD A,(SAVNXT) ;get next record byte from storage. + LD (HL),A ;and put this into fcb as number of records used. + RET +; +; Shift (HL) right (C) bits. +; +SHIFTR: INC C +SHIFTR1:DEC C + RET Z + LD A,H + OR A + RRA + LD H,A + LD A,L + RRA + LD L,A + JP SHIFTR1 +; +; Compute the check-sum for the directory buffer. Return +; integer sum in (A). +; +CHECKSUM: LD C,128 ;length of buffer. + LD HL,(DIRBUF) ;get its location. + XOR A ;clear summation byte. +CHKSUM1:ADD A,(HL) ;and compute sum ignoring carries. + INC HL + DEC C + JP NZ,CHKSUM1 + RET +; +; Shift (HL) left (C) bits. +; +SHIFTL: INC C +SHIFTL1:DEC C + RET Z + ADD HL,HL ;shift left 1 bit. + JP SHIFTL1 +; +; Routine to set a bit in a 16 bit value contained in (BC). +; The bit set depends on the current drive selection. +; +SETBIT: PUSH BC ;save 16 bit word. + LD A,(ACTIVE) ;get active drive. + LD C,A + LD HL,1 + CALL SHIFTL ;shift bit 0 into place. + POP BC ;now 'or' this with the original word. + LD A,C + OR L + LD L,A ;low byte done, do high byte. + LD A,B + OR H + LD H,A + RET +; +; Extract the write protect status bit for the current drive. +; The result is returned in (A), bit 0. +; +GETWPRT:LD HL,(WRTPRT) ;get status bytes. + LD A,(ACTIVE) ;which drive is current? + LD C,A + CALL SHIFTR ;shift status such that bit 0 is the + LD A,L ;one of interest for this drive. + AND 01H ;and isolate it. + RET +; +; Function to write protect the current disk. +; +WRTPRTD:LD HL,WRTPRT ;point to status word. + LD C,(HL) ;set (BC) equal to the status. + INC HL + LD B,(HL) + CALL SETBIT ;and set this bit according to current drive. + LD (WRTPRT),HL ;then save. + LD HL,(DIRSIZE) ;now save directory size limit. + INC HL ;remember the last one. + EX DE,HL + LD HL,(SCRATCH1) ;and store it here. + LD (HL),E ;put low byte. + INC HL + LD (HL),D ;then high byte. + RET +; +; Check for a read only file. +; +CHKROFL:CALL FCB2HL ;set (HL) to file entry in directory buffer. +CKROF1: LD DE,9 ;look at bit 7 of the ninth byte. + ADD HL,DE + LD A,(HL) + RLA + RET NC ;return if ok. + LD HL,ROFILE ;else, print error message and terminate. + JP JUMPHL +; +; Check the write protect status of the active disk. +; +CHKWPRT:CALL GETWPRT + RET Z ;return if ok. + LD HL,RODISK ;else print message and terminate. + JP JUMPHL +; +; Routine to set (HL) pointing to the proper entry in the +; directory buffer. +; +FCB2HL: LD HL,(DIRBUF) ;get address of buffer. + LD A,(FCBPOS) ;relative position of file. +; +; Routine to add (A) to (HL). +; +ADDA2HL:ADD A,L + LD L,A + RET NC + INC H ;take care of any carry. + RET +; +; Routine to get the 's2' byte from the fcb supplied in +; the initial parameter specification. +; +GETS2: LD HL,(PARAMS) ;get address of fcb. + LD DE,14 ;relative position of 's2'. + ADD HL,DE + LD A,(HL) ;extract this byte. + RET +; +; Clear the 's2' byte in the fcb. +; +CLEARS2:CALL GETS2 ;this sets (HL) pointing to it. + LD (HL),0 ;now clear it. + RET +; +; Set bit 7 in the 's2' byte of the fcb. +; +SETS2B7:CALL GETS2 ;get the byte. + OR 80H ;and set bit 7. + LD (HL),A ;then store. + RET +; +; Compare (FILEPOS) with (SCRATCH1) and set flags based on +; the difference. This checks to see if there are more file +; names in the directory. We are at (FILEPOS) and there are +; (SCRATCH1) of them to check. +; +MOREFLS:LD HL,(FILEPOS) ;we are here. + EX DE,HL + LD HL,(SCRATCH1) ;and don't go past here. + LD A,E ;compute difference but don't keep. + SUB (HL) + INC HL + LD A,D + SBC A,(HL) ;set carry if no more names. + RET +; +; Call this routine to prevent (SCRATCH1) from being greater +; than (FILEPOS). +; +CHKNMBR:CALL MOREFLS ;SCRATCH1 too big? + RET C + INC DE ;yes, reset it to (FILEPOS). + LD (HL),D + DEC HL + LD (HL),E + RET +; +; Compute (HL)=(DE)-(HL) +; +SUBHL: LD A,E ;compute difference. + SUB L + LD L,A ;store low byte. + LD A,D + SBC A,H + LD H,A ;and then high byte. + RET +; +; Set the directory checksum byte. +; +SETDIR: LD C,0FFH +; +; Routine to set or compare the directory checksum byte. If +; (C)=0ffh, then this will set the checksum byte. Else the byte +; will be checked. If the check fails (the disk has been changed), +; then this disk will be write protected. +; +CHECKDIR: LD HL,(CKSUMTBL) + EX DE,HL + LD HL,(ALLOC1) + CALL SUBHL + RET NC ;ok if (CKSUMTBL) > (ALLOC1), so return. + PUSH BC + CALL CHECKSUM ;else compute checksum. + LD HL,(CHKVECT) ;get address of checksum table. + EX DE,HL + LD HL,(CKSUMTBL) + ADD HL,DE ;set (HL) to point to byte for this drive. + POP BC + INC C ;set or check ? + JP Z,CHKDIR1 + CP (HL) ;check them. + RET Z ;return if they are the same. + CALL MOREFLS ;not the same, do we care? + RET NC + CALL WRTPRTD ;yes, mark this as write protected. + RET +CHKDIR1:LD (HL),A ;just set the byte. + RET +; +; Do a write to the directory of the current disk. +; +DIRWRITE: CALL SETDIR ;set checksum byte. + CALL DIRDMA ;set directory dma address. + LD C,1 ;tell the bios to actually write. + CALL DOWRITE ;then do the write. + JP DEFDMA +; +; Read from the directory. +; +DIRREAD:CALL DIRDMA ;set the directory dma address. + CALL DOREAD ;and read it. +; +; Routine to set the dma address to the users choice. +; +DEFDMA: LD HL,USERDMA ;reset the default dma address and return. + JP DIRDMA1 +; +; Routine to set the dma address for directory work. +; +DIRDMA: LD HL,DIRBUF +; +; Set the dma address. On entry, (HL) points to +; word containing the desired dma address. +; +DIRDMA1:LD C,(HL) + INC HL + LD B,(HL) ;setup (BC) and go to the bios to set it. + JP SETDMA +; +; Move the directory buffer into user's dma space. +; +MOVEDIR:LD HL,(DIRBUF) ;buffer is located here, and + EX DE,HL + LD HL,(USERDMA) ; put it here. + LD C,128 ;this is its length. + JP DE2HL ;move it now and return. +; +; Check (FILEPOS) and set the zero flag if it equals 0ffffh. +; +CKFILPOS: LD HL,FILEPOS + LD A,(HL) + INC HL + CP (HL) ;are both bytes the same? + RET NZ + INC A ;yes, but are they each 0ffh? + RET +; +; Set location (FILEPOS) to 0ffffh. +; +STFILPOS: LD HL,0FFFFH + LD (FILEPOS),HL + RET +; +; Move on to the next file position within the current +; directory buffer. If no more exist, set pointer to 0ffffh +; and the calling routine will check for this. Enter with (C) +; equal to 0ffh to cause the checksum byte to be set, else we +; will check this disk and set write protect if checksums are +; not the same (applies only if another directory sector must +; be read). +; +NXENTRY:LD HL,(DIRSIZE) ;get directory entry size limit. + EX DE,HL + LD HL,(FILEPOS) ;get current count. + INC HL ;go on to the next one. + LD (FILEPOS),HL + CALL SUBHL ;(HL)=(DIRSIZE)-(FILEPOS) + JP NC,NXENT1 ;is there more room left? + JP STFILPOS ;no. Set this flag and return. +NXENT1: LD A,(FILEPOS) ;get file position within directory. + AND 03H ;only look within this sector (only 4 entries fit). + LD B,5 ;convert to relative position (32 bytes each). +NXENT2: ADD A,A ;note that this is not efficient code. + DEC B ;5 'ADD A's would be better. + JP NZ,NXENT2 + LD (FCBPOS),A ;save it as position of fcb. + OR A + RET NZ ;return if we are within buffer. + PUSH BC + CALL TRKSEC ;we need the next directory sector. + CALL DIRREAD + POP BC + JP CHECKDIR +; +; Routine to to get a bit from the disk space allocation +; map. It is returned in (A), bit position 0. On entry to here, +; set (BC) to the block number on the disk to check. +; On return, (D) will contain the original bit position for +; this block number and (HL) will point to the address for it. +; +CKBITMAP: LD A,C ;determine bit number of interest. + AND 07H ;compute (D)=(E)=(C and 7)+1. + INC A + LD E,A ;save particular bit number. + LD D,A +; +; compute (BC)=(BC)/8. +; + LD A,C + RRCA ;now shift right 3 bits. + RRCA + RRCA + AND 1FH ;and clear bits 7,6,5. + LD C,A + LD A,B + ADD A,A ;now shift (B) into bits 7,6,5. + ADD A,A + ADD A,A + ADD A,A + ADD A,A + OR C ;and add in (C). + LD C,A ;ok, (C) ha been completed. + LD A,B ;is there a better way of doing this? + RRCA + RRCA + RRCA + AND 1FH + LD B,A ;and now (B) is completed. +; +; use this as an offset into the disk space allocation +; table. +; + LD HL,(ALOCVECT) + ADD HL,BC + LD A,(HL) ;now get correct byte. +CKBMAP1:RLCA ;get correct bit into position 0. + DEC E + JP NZ,CKBMAP1 + RET +; +; Set or clear the bit map such that block number (BC) will be marked +; as used. On entry, if (E)=0 then this bit will be cleared, if it equals +; 1 then it will be set (don't use anyother values). +; +STBITMAP: PUSH DE + CALL CKBITMAP ;get the byte of interest. + AND 0FEH ;clear the affected bit. + POP BC + OR C ;and now set it acording to (C). +; +; entry to restore the original bit position and then store +; in table. (A) contains the value, (D) contains the bit +; position (1-8), and (HL) points to the address within the +; space allocation table for this byte. +; +STBMAP1:RRCA ;restore original bit position. + DEC D + JP NZ,STBMAP1 + LD (HL),A ;and stor byte in table. + RET +; +; Set/clear space used bits in allocation map for this file. +; On entry, (C)=1 to set the map and (C)=0 to clear it. +; +SETFILE:CALL FCB2HL ;get address of fcb + LD DE,16 + ADD HL,DE ;get to block number bytes. + PUSH BC + LD C,17 ;check all 17 bytes (max) of table. +SETFL1: POP DE + DEC C ;done all bytes yet? + RET Z + PUSH DE + LD A,(BIGDISK) ;check disk size for 16 bit block numbers. + OR A + JP Z,SETFL2 + PUSH BC ;only 8 bit numbers. set (BC) to this one. + PUSH HL + LD C,(HL) ;get low byte from table, always + LD B,0 ;set high byte to zero. + JP SETFL3 +SETFL2: DEC C ;for 16 bit block numbers, adjust counter. + PUSH BC + LD C,(HL) ;now get both the low and high bytes. + INC HL + LD B,(HL) + PUSH HL +SETFL3: LD A,C ;block used? + OR B + JP Z,SETFL4 + LD HL,(DSKSIZE) ;is this block number within the + LD A,L ;space on the disk? + SUB C + LD A,H + SBC A,B + CALL NC,STBITMAP ;yes, set the proper bit. +SETFL4: POP HL ;point to next block number in fcb. + INC HL + POP BC + JP SETFL1 +; +; Construct the space used allocation bit map for the active +; drive. If a file name starts with '$' and it is under the +; current user number, then (STATUS) is set to minus 1. Otherwise +; it is not set at all. +; +BITMAP: LD HL,(DSKSIZE) ;compute size of allocation table. + LD C,3 + CALL SHIFTR ;(HL)=(HL)/8. + INC HL ;at lease 1 byte. + LD B,H + LD C,L ;set (BC) to the allocation table length. +; +; Initialize the bitmap for this drive. Right now, the first +; two bytes are specified by the disk parameter block. However +; a patch could be entered here if it were necessary to setup +; this table in a special mannor. For example, the bios could +; determine locations of 'bad blocks' and set them as already +; 'used' in the map. +; + LD HL,(ALOCVECT) ;now zero out the table now. +BITMAP1:LD (HL),0 + INC HL + DEC BC + LD A,B + OR C + JP NZ,BITMAP1 + LD HL,(ALLOC0) ;get initial space used by directory. + EX DE,HL + LD HL,(ALOCVECT) ;and put this into map. + LD (HL),E + INC HL + LD (HL),D +; +; End of initialization portion. +; + CALL HOMEDRV ;now home the drive. + LD HL,(SCRATCH1) + LD (HL),3 ;force next directory request to read + INC HL ;in a sector. + LD (HL),0 + CALL STFILPOS ;clear initial file position also. +BITMAP2:LD C,0FFH ;read next file name in directory + CALL NXENTRY ;and set checksum byte. + CALL CKFILPOS ;is there another file? + RET Z + CALL FCB2HL ;yes, get its address. + LD A,0E5H + CP (HL) ;empty file entry? + JP Z,BITMAP2 + LD A,(USERNO) ;no, correct user number? + CP (HL) + JP NZ,BITMAP3 + INC HL + LD A,(HL) ;yes, does name start with a '$'? + SUB '$' + JP NZ,BITMAP3 + DEC A ;yes, set atatus to minus one. + LD (STATUS),A +BITMAP3:LD C,1 ;now set this file's space as used in bit map. + CALL SETFILE + CALL CHKNMBR ;keep (SCRATCH1) in bounds. + JP BITMAP2 +; +; Set the status (STATUS) and return. +; +STSTATUS: LD A,(FNDSTAT) + JP SETSTAT +; +; Check extents in (A) and (C). Set the zero flag if they +; are the same. The number of 16k chunks of disk space that +; the directory extent covers is expressad is (EXTMASK+1). +; No registers are modified. +; +SAMEXT: PUSH BC + PUSH AF + LD A,(EXTMASK) ;get extent mask and use it to + CPL ;to compare both extent numbers. + LD B,A ;save resulting mask here. + LD A,C ;mask first extent and save in (C). + AND B + LD C,A + POP AF ;now mask second extent and compare + AND B ;with the first one. + SUB C + AND 1FH ;(* only check buts 0-4 *) + POP BC ;the zero flag is set if they are the same. + RET ;restore (BC) and return. +; +; Search for the first occurence of a file name. On entry, +; register (C) should contain the number of bytes of the fcb +; that must match. +; +FINDFST:LD A,0FFH + LD (FNDSTAT),A + LD HL,COUNTER ;save character count. + LD (HL),C + LD HL,(PARAMS) ;get filename to match. + LD (SAVEFCB),HL ;and save. + CALL STFILPOS ;clear initial file position (set to 0ffffh). + CALL HOMEDRV ;home the drive. +; +; Entry to locate the next occurence of a filename within the +; directory. The disk is not expected to have been changed. If +; it was, then it will be write protected. +; +FINDNXT:LD C,0 ;write protect the disk if changed. + CALL NXENTRY ;get next filename entry in directory. + CALL CKFILPOS ;is file position = 0ffffh? + JP Z,FNDNXT6 ;yes, exit now then. + LD HL,(SAVEFCB) ;set (DE) pointing to filename to match. + EX DE,HL + LD A,(DE) + CP 0E5H ;empty directory entry? + JP Z,FNDNXT1 ;(* are we trying to reserect erased entries? *) + PUSH DE + CALL MOREFLS ;more files in directory? + POP DE + JP NC,FNDNXT6 ;no more. Exit now. +FNDNXT1:CALL FCB2HL ;get address of this fcb in directory. + LD A,(COUNTER) ;get number of bytes (characters) to check. + LD C,A + LD B,0 ;initialize byte position counter. +FNDNXT2:LD A,C ;are we done with the compare? + OR A + JP Z,FNDNXT5 + LD A,(DE) ;no, check next byte. + CP '?' ;don't care about this character? + JP Z,FNDNXT4 + LD A,B ;get bytes position in fcb. + CP 13 ;don't care about the thirteenth byte either. + JP Z,FNDNXT4 + CP 12 ;extent byte? + LD A,(DE) + JP Z,FNDNXT3 + SUB (HL) ;otherwise compare characters. + AND 7FH + JP NZ,FINDNXT ;not the same, check next entry. + JP FNDNXT4 ;so far so good, keep checking. +FNDNXT3:PUSH BC ;check the extent byte here. + LD C,(HL) + CALL SAMEXT + POP BC + JP NZ,FINDNXT ;not the same, look some more. +; +; So far the names compare. Bump pointers to the next byte +; and continue until all (C) characters have been checked. +; +FNDNXT4:INC DE ;bump pointers. + INC HL + INC B + DEC C ;adjust character counter. + JP FNDNXT2 +FNDNXT5:LD A,(FILEPOS) ;return the position of this entry. + AND 03H + LD (STATUS),A + LD HL,FNDSTAT + LD A,(HL) + RLA + RET NC + XOR A + LD (HL),A + RET +; +; Filename was not found. Set appropriate status. +; +FNDNXT6:CALL STFILPOS ;set (FILEPOS) to 0ffffh. + LD A,0FFH ;say not located. + JP SETSTAT +; +; Erase files from the directory. Only the first byte of the +; fcb will be affected. It is set to (E5). +; +ERAFILE:CALL CHKWPRT ;is disk write protected? + LD C,12 ;only compare file names. + CALL FINDFST ;get first file name. +ERAFIL1:CALL CKFILPOS ;any found? + RET Z ;nope, we must be done. + CALL CHKROFL ;is file read only? + CALL FCB2HL ;nope, get address of fcb and + LD (HL),0E5H ;set first byte to 'empty'. + LD C,0 ;clear the space from the bit map. + CALL SETFILE + CALL DIRWRITE ;now write the directory sector back out. + CALL FINDNXT ;find the next file name. + JP ERAFIL1 ;and repeat process. +; +; Look through the space allocation map (bit map) for the +; next available block. Start searching at block number (BC-1). +; The search procedure is to look for an empty block that is +; before the starting block. If not empty, look at a later +; block number. In this way, we return the closest empty block +; on either side of the 'target' block number. This will speed +; access on random devices. For serial devices, this should be +; changed to look in the forward direction first and then start +; at the front and search some more. +; +; On return, (DE)= block number that is empty and (HL) =0 +; if no empry block was found. +; +FNDSPACE: LD D,B ;set (DE) as the block that is checked. + LD E,C +; +; Look before target block. Registers (BC) are used as the lower +; pointer and (DE) as the upper pointer. +; +FNDSPA1:LD A,C ;is block 0 specified? + OR B + JP Z,FNDSPA2 + DEC BC ;nope, check previous block. + PUSH DE + PUSH BC + CALL CKBITMAP + RRA ;is this block empty? + JP NC,FNDSPA3 ;yes. use this. +; +; Note that the above logic gets the first block that it finds +; that is empty. Thus a file could be written 'backward' making +; it very slow to access. This could be changed to look for the +; first empty block and then continue until the start of this +; empty space is located and then used that starting block. +; This should help speed up access to some files especially on +; a well used disk with lots of fairly small 'holes'. +; + POP BC ;nope, check some more. + POP DE +; +; Now look after target block. +; +FNDSPA2:LD HL,(DSKSIZE) ;is block (DE) within disk limits? + LD A,E + SUB L + LD A,D + SBC A,H + JP NC,FNDSPA4 + INC DE ;yes, move on to next one. + PUSH BC + PUSH DE + LD B,D + LD C,E + CALL CKBITMAP ;check it. + RRA ;empty? + JP NC,FNDSPA3 + POP DE ;nope, continue searching. + POP BC + JP FNDSPA1 +; +; Empty block found. Set it as used and return with (HL) +; pointing to it (true?). +; +FNDSPA3:RLA ;reset byte. + INC A ;and set bit 0. + CALL STBMAP1 ;update bit map. + POP HL ;set return registers. + POP DE + RET +; +; Free block was not found. If (BC) is not zero, then we have +; not checked all of the disk space. +; +FNDSPA4:LD A,C + OR B + JP NZ,FNDSPA1 + LD HL,0 ;set 'not found' status. + RET +; +; Move a complete fcb entry into the directory and write it. +; +FCBSET: LD C,0 + LD E,32 ;length of each entry. +; +; Move (E) bytes from the fcb pointed to by (PARAMS) into +; fcb in directory starting at relative byte (C). This updated +; directory buffer is then written to the disk. +; +UPDATE: PUSH DE + LD B,0 ;set (BC) to relative byte position. + LD HL,(PARAMS) ;get address of fcb. + ADD HL,BC ;compute starting byte. + EX DE,HL + CALL FCB2HL ;get address of fcb to update in directory. + POP BC ;set (C) to number of bytes to change. + CALL DE2HL +UPDATE1:CALL TRKSEC ;determine the track and sector affected. + JP DIRWRITE ;then write this sector out. +; +; Routine to change the name of all files on the disk with a +; specified name. The fcb contains the current name as the +; first 12 characters and the new name 16 bytes into the fcb. +; +CHGNAMES: CALL CHKWPRT ;check for a write protected disk. + LD C,12 ;match first 12 bytes of fcb only. + CALL FINDFST ;get first name. + LD HL,(PARAMS) ;get address of fcb. + LD A,(HL) ;get user number. + LD DE,16 ;move over to desired name. + ADD HL,DE + LD (HL),A ;keep same user number. +CHGNAM1:CALL CKFILPOS ;any matching file found? + RET Z ;no, we must be done. + CALL CHKROFL ;check for read only file. + LD C,16 ;start 16 bytes into fcb. + LD E,12 ;and update the first 12 bytes of directory. + CALL UPDATE + CALL FINDNXT ;get te next file name. + JP CHGNAM1 ;and continue. +; +; Update a files attributes. The procedure is to search for +; every file with the same name as shown in fcb (ignoring bit 7) +; and then to update it (which includes bit 7). No other changes +; are made. +; +SAVEATTR: LD C,12 ;match first 12 bytes. + CALL FINDFST ;look for first filename. +SAVATR1:CALL CKFILPOS ;was one found? + RET Z ;nope, we must be done. + LD C,0 ;yes, update the first 12 bytes now. + LD E,12 + CALL UPDATE ;update filename and write directory. + CALL FINDNXT ;and get the next file. + JP SAVATR1 ;then continue until done. +; +; Open a file (name specified in fcb). +; +OPENIT: LD C,15 ;compare the first 15 bytes. + CALL FINDFST ;get the first one in directory. + CALL CKFILPOS ;any at all? + RET Z +OPENIT1:CALL SETEXT ;point to extent byte within users fcb. + LD A,(HL) ;and get it. + PUSH AF ;save it and address. + PUSH HL + CALL FCB2HL ;point to fcb in directory. + EX DE,HL + LD HL,(PARAMS) ;this is the users copy. + LD C,32 ;move it into users space. + PUSH DE + CALL DE2HL + CALL SETS2B7 ;set bit 7 in 's2' byte (unmodified). + POP DE ;now get the extent byte from this fcb. + LD HL,12 + ADD HL,DE + LD C,(HL) ;into (C). + LD HL,15 ;now get the record count byte into (B). + ADD HL,DE + LD B,(HL) + POP HL ;keep the same extent as the user had originally. + POP AF + LD (HL),A + LD A,C ;is it the same as in the directory fcb? + CP (HL) + LD A,B ;if yes, then use the same record count. + JP Z,OPENIT2 + LD A,0 ;if the user specified an extent greater than + JP C,OPENIT2 ;the one in the directory, then set record count to 0. + LD A,128 ;otherwise set to maximum. +OPENIT2:LD HL,(PARAMS) ;set record count in users fcb to (A). + LD DE,15 + ADD HL,DE ;compute relative position. + LD (HL),A ;and set the record count. + RET +; +; Move two bytes from (DE) to (HL) if (and only if) (HL) +; point to a zero value (16 bit). +; Return with zero flag set it (DE) was moved. Registers (DE) +; and (HL) are not changed. However (A) is. +; +MOVEWORD: LD A,(HL) ;check for a zero word. + INC HL + OR (HL) ;both bytes zero? + DEC HL + RET NZ ;nope, just return. + LD A,(DE) ;yes, move two bytes from (DE) into + LD (HL),A ;this zero space. + INC DE + INC HL + LD A,(DE) + LD (HL),A + DEC DE ;don't disturb these registers. + DEC HL + RET +; +; Get here to close a file specified by (fcb). +; +CLOSEIT:XOR A ;clear status and file position bytes. + LD (STATUS),A + LD (FILEPOS),A + LD (FILEPOS+1),A + CALL GETWPRT ;get write protect bit for this drive. + RET NZ ;just return if it is set. + CALL GETS2 ;else get the 's2' byte. + AND 80H ;and look at bit 7 (file unmodified?). + RET NZ ;just return if set. + LD C,15 ;else look up this file in directory. + CALL FINDFST + CALL CKFILPOS ;was it found? + RET Z ;just return if not. + LD BC,16 ;set (HL) pointing to records used section. + CALL FCB2HL + ADD HL,BC + EX DE,HL + LD HL,(PARAMS) ;do the same for users specified fcb. + ADD HL,BC + LD C,16 ;this many bytes are present in this extent. +CLOSEIT1: LD A,(BIGDISK) ;8 or 16 bit record numbers? + OR A + JP Z,CLOSEIT4 + LD A,(HL) ;just 8 bit. Get one from users fcb. + OR A + LD A,(DE) ;now get one from directory fcb. + JP NZ,CLOSEIT2 + LD (HL),A ;users byte was zero. Update from directory. +CLOSEIT2: OR A + JP NZ,CLOSEIT3 + LD A,(HL) ;directories byte was zero, update from users fcb. + LD (DE),A +CLOSEIT3: CP (HL) ;if neither one of these bytes were zero, + JP NZ,CLOSEIT7 ;then close error if they are not the same. + JP CLOSEIT5 ;ok so far, get to next byte in fcbs. +CLOSEIT4: CALL MOVEWORD ;update users fcb if it is zero. + EX DE,HL + CALL MOVEWORD ;update directories fcb if it is zero. + EX DE,HL + LD A,(DE) ;if these two values are no different, + CP (HL) ;then a close error occured. + JP NZ,CLOSEIT7 + INC DE ;check second byte. + INC HL + LD A,(DE) + CP (HL) + JP NZ,CLOSEIT7 + DEC C ;remember 16 bit values. +CLOSEIT5: INC DE ;bump to next item in table. + INC HL + DEC C ;there are 16 entries only. + JP NZ,CLOSEIT1 ;continue if more to do. + LD BC,0FFECH ;backup 20 places (extent byte). + ADD HL,BC + EX DE,HL + ADD HL,BC + LD A,(DE) + CP (HL) ;directory's extent already greater than the + JP C,CLOSEIT6 ;users extent? + LD (HL),A ;no, update directory extent. + LD BC,3 ;and update the record count byte in + ADD HL,BC ;directories fcb. + EX DE,HL + ADD HL,BC + LD A,(HL) ;get from user. + LD (DE),A ;and put in directory. +CLOSEIT6: LD A,0FFH ;set 'was open and is now closed' byte. + LD (CLOSEFLG),A + JP UPDATE1 ;update the directory now. +CLOSEIT7: LD HL,STATUS ;set return status and then return. + DEC (HL) + RET +; +; Routine to get the next empty space in the directory. It +; will then be cleared for use. +; +GETEMPTY: CALL CHKWPRT ;make sure disk is not write protected. + LD HL,(PARAMS) ;save current parameters (fcb). + PUSH HL + LD HL,EMPTYFCB ;use special one for empty space. + LD (PARAMS),HL + LD C,1 ;search for first empty spot in directory. + CALL FINDFST ;(* only check first byte *) + CALL CKFILPOS ;none? + POP HL + LD (PARAMS),HL ;restore original fcb address. + RET Z ;return if no more space. + EX DE,HL + LD HL,15 ;point to number of records for this file. + ADD HL,DE + LD C,17 ;and clear all of this space. + XOR A +GETMT1: LD (HL),A + INC HL + DEC C + JP NZ,GETMT1 + LD HL,13 ;clear the 's1' byte also. + ADD HL,DE + LD (HL),A + CALL CHKNMBR ;keep (SCRATCH1) within bounds. + CALL FCBSET ;write out this fcb entry to directory. + JP SETS2B7 ;set 's2' byte bit 7 (unmodified at present). +; +; Routine to close the current extent and open the next one +; for reading. +; +GETNEXT:XOR A + LD (CLOSEFLG),A ;clear close flag. + CALL CLOSEIT ;close this extent. + CALL CKFILPOS + RET Z ;not there??? + LD HL,(PARAMS) ;get extent byte. + LD BC,12 + ADD HL,BC + LD A,(HL) ;and increment it. + INC A + AND 1FH ;keep within range 0-31. + LD (HL),A + JP Z,GTNEXT1 ;overflow? + LD B,A ;mask extent byte. + LD A,(EXTMASK) + AND B + LD HL,CLOSEFLG ;check close flag (0ffh is ok). + AND (HL) + JP Z,GTNEXT2 ;if zero, we must read in next extent. + JP GTNEXT3 ;else, it is already in memory. +GTNEXT1:LD BC,2 ;Point to the 's2' byte. + ADD HL,BC + INC (HL) ;and bump it. + LD A,(HL) ;too many extents? + AND 0FH + JP Z,GTNEXT5 ;yes, set error code. +; +; Get here to open the next extent. +; +GTNEXT2:LD C,15 ;set to check first 15 bytes of fcb. + CALL FINDFST ;find the first one. + CALL CKFILPOS ;none available? + JP NZ,GTNEXT3 + LD A,(RDWRTFLG) ;no extent present. Can we open an empty one? + INC A ;0ffh means reading (so not possible). + JP Z,GTNEXT5 ;or an error. + CALL GETEMPTY ;we are writing, get an empty entry. + CALL CKFILPOS ;none? + JP Z,GTNEXT5 ;error if true. + JP GTNEXT4 ;else we are almost done. +GTNEXT3:CALL OPENIT1 ;open this extent. +GTNEXT4:CALL STRDATA ;move in updated data (rec #, extent #, etc.) + XOR A ;clear status and return. + JP SETSTAT +; +; Error in extending the file. Too many extents were needed +; or not enough space on the disk. +; +GTNEXT5:CALL IOERR1 ;set error code, clear bit 7 of 's2' + JP SETS2B7 ;so this is not written on a close. +; +; Read a sequential file. +; +RDSEQ: LD A,1 ;set sequential access mode. + LD (MODE),A +RDSEQ1: LD A,0FFH ;don't allow reading unwritten space. + LD (RDWRTFLG),A + CALL STRDATA ;put rec# and ext# into fcb. + LD A,(SAVNREC) ;get next record to read. + LD HL,SAVNXT ;get number of records in extent. + CP (HL) ;within this extent? + JP C,RDSEQ2 + CP 128 ;no. Is this extent fully used? + JP NZ,RDSEQ3 ;no. End-of-file. + CALL GETNEXT ;yes, open the next one. + XOR A ;reset next record to read. + LD (SAVNREC),A + LD A,(STATUS) ;check on open, successful? + OR A + JP NZ,RDSEQ3 ;no, error. +RDSEQ2: CALL COMBLK ;ok. compute block number to read. + CALL CHKBLK ;check it. Within bounds? + JP Z,RDSEQ3 ;no, error. + CALL LOGICAL ;convert (BLKNMBR) to logical sector (128 byte). + CALL TRKSEC1 ;set the track and sector for this block #. + CALL DOREAD ;and read it. + JP SETNREC ;and set the next record to be accessed. +; +; Read error occured. Set status and return. +; +RDSEQ3: JP IOERR1 +; +; Write the next sequential record. +; +WTSEQ: LD A,1 ;set sequential access mode. + LD (MODE),A +WTSEQ1: LD A,0 ;allow an addition empty extent to be opened. + LD (RDWRTFLG),A + CALL CHKWPRT ;check write protect status. + LD HL,(PARAMS) + CALL CKROF1 ;check for read only file, (HL) already set to fcb. + CALL STRDATA ;put updated data into fcb. + LD A,(SAVNREC) ;get record number to write. + CP 128 ;within range? + JP NC,IOERR1 ;no, error(?). + CALL COMBLK ;compute block number. + CALL CHKBLK ;check number. + LD C,0 ;is there one to write to? + JP NZ,WTSEQ6 ;yes, go do it. + CALL GETBLOCK ;get next block number within fcb to use. + LD (RELBLOCK),A ;and save. + LD BC,0 ;start looking for space from the start + OR A ;if none allocated as yet. + JP Z,WTSEQ2 + LD C,A ;extract previous block number from fcb + DEC BC ;so we can be closest to it. + CALL EXTBLK + LD B,H + LD C,L +WTSEQ2: CALL FNDSPACE ;find the next empty block nearest number (BC). + LD A,L ;check for a zero number. + OR H + JP NZ,WTSEQ3 + LD A,2 ;no more space? + JP SETSTAT +WTSEQ3: LD (BLKNMBR),HL ;save block number to access. + EX DE,HL ;put block number into (DE). + LD HL,(PARAMS) ;now we must update the fcb for this + LD BC,16 ;newly allocated block. + ADD HL,BC + LD A,(BIGDISK) ;8 or 16 bit block numbers? + OR A + LD A,(RELBLOCK) ;(* update this entry *) + JP Z,WTSEQ4 ;zero means 16 bit ones. + CALL ADDA2HL ;(HL)=(HL)+(A) + LD (HL),E ;store new block number. + JP WTSEQ5 +WTSEQ4: LD C,A ;compute spot in this 16 bit table. + LD B,0 + ADD HL,BC + ADD HL,BC + LD (HL),E ;stuff block number (DE) there. + INC HL + LD (HL),D +WTSEQ5: LD C,2 ;set (C) to indicate writing to un-used disk space. +WTSEQ6: LD A,(STATUS) ;are we ok so far? + OR A + RET NZ + PUSH BC ;yes, save write flag for bios (register C). + CALL LOGICAL ;convert (BLKNMBR) over to loical sectors. + LD A,(MODE) ;get access mode flag (1=sequential, + DEC A ;0=random, 2=special?). + DEC A + JP NZ,WTSEQ9 +; +; Special random i/o from function #40. Maybe for M/PM, but the +; current block, if it has not been written to, will be zeroed +; out and then written (reason?). +; + POP BC + PUSH BC + LD A,C ;get write status flag (2=writing unused space). + DEC A + DEC A + JP NZ,WTSEQ9 + PUSH HL + LD HL,(DIRBUF) ;zero out the directory buffer. + LD D,A ;note that (A) is zero here. +WTSEQ7: LD (HL),A + INC HL + INC D ;do 128 bytes. + JP P,WTSEQ7 + CALL DIRDMA ;tell the bios the dma address for directory access. + LD HL,(LOGSECT) ;get sector that starts current block. + LD C,2 ;set 'writing to unused space' flag. +WTSEQ8: LD (BLKNMBR),HL ;save sector to write. + PUSH BC + CALL TRKSEC1 ;determine its track and sector numbers. + POP BC + CALL DOWRITE ;now write out 128 bytes of zeros. + LD HL,(BLKNMBR) ;get sector number. + LD C,0 ;set normal write flag. + LD A,(BLKMASK) ;determine if we have written the entire + LD B,A ;physical block. + AND L + CP B + INC HL ;prepare for the next one. + JP NZ,WTSEQ8 ;continue until (BLKMASK+1) sectors written. + POP HL ;reset next sector number. + LD (BLKNMBR),HL + CALL DEFDMA ;and reset dma address. +; +; Normal disk write. Set the desired track and sector then +; do the actual write. +; +WTSEQ9: CALL TRKSEC1 ;determine track and sector for this write. + POP BC ;get write status flag. + PUSH BC + CALL DOWRITE ;and write this out. + POP BC + LD A,(SAVNREC) ;get number of records in file. + LD HL,SAVNXT ;get last record written. + CP (HL) + JP C,WTSEQ10 + LD (HL),A ;we have to update record count. + INC (HL) + LD C,2 +; +;* This area has been patched to correct disk update problem +;* when using blocking and de-blocking in the BIOS. +; +WTSEQ10:NOP ;was 'dcr c' + NOP ;was 'dcr c' + LD HL,0 ;was 'jnz wtseq99' +; +; * End of patch. +; + PUSH AF + CALL GETS2 ;set 'extent written to' flag. + AND 7FH ;(* clear bit 7 *) + LD (HL),A + POP AF ;get record count for this extent. +WTSEQ99:CP 127 ;is it full? + JP NZ,WTSEQ12 + LD A,(MODE) ;yes, are we in sequential mode? + CP 1 + JP NZ,WTSEQ12 + CALL SETNREC ;yes, set next record number. + CALL GETNEXT ;and get next empty space in directory. + LD HL,STATUS ;ok? + LD A,(HL) + OR A + JP NZ,WTSEQ11 + DEC A ;yes, set record count to -1. + LD (SAVNREC),A +WTSEQ11:LD (HL),0 ;clear status. +WTSEQ12:JP SETNREC ;set next record to access. +; +; For random i/o, set the fcb for the desired record number +; based on the 'r0,r1,r2' bytes. These bytes in the fcb are +; used as follows: +; +; fcb+35 fcb+34 fcb+33 +; | 'r-2' | 'r-1' | 'r-0' | +; |7 0 | 7 0 | 7 0| +; |0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0| +; | overflow | | extra | extent | record # | +; | ______________| |_extent|__number___|_____________| +; also 's2' +; +; On entry, register (C) contains 0ffh if this is a read +; and thus we can not access unwritten disk space. Otherwise, +; another extent will be opened (for writing) if required. +; +POSITION: XOR A ;set random i/o flag. + LD (MODE),A +; +; Special entry (function #40). M/PM ? +; +POSITN1:PUSH BC ;save read/write flag. + LD HL,(PARAMS) ;get address of fcb. + EX DE,HL + LD HL,33 ;now get byte 'r0'. + ADD HL,DE + LD A,(HL) + AND 7FH ;keep bits 0-6 for the record number to access. + PUSH AF + LD A,(HL) ;now get bit 7 of 'r0' and bits 0-3 of 'r1'. + RLA + INC HL + LD A,(HL) + RLA + AND 1FH ;and save this in bits 0-4 of (C). + LD C,A ;this is the extent byte. + LD A,(HL) ;now get the extra extent byte. + RRA + RRA + RRA + RRA + AND 0FH + LD B,A ;and save it in (B). + POP AF ;get record number back to (A). + INC HL ;check overflow byte 'r2'. + LD L,(HL) + INC L + DEC L + LD L,6 ;prepare for error. + JP NZ,POSITN5 ;out of disk space error. + LD HL,32 ;store record number into fcb. + ADD HL,DE + LD (HL),A + LD HL,12 ;and now check the extent byte. + ADD HL,DE + LD A,C + SUB (HL) ;same extent as before? + JP NZ,POSITN2 + LD HL,14 ;yes, check extra extent byte 's2' also. + ADD HL,DE + LD A,B + SUB (HL) + AND 7FH + JP Z,POSITN3 ;same, we are almost done then. +; +; Get here when another extent is required. +; +POSITN2:PUSH BC + PUSH DE + CALL CLOSEIT ;close current extent. + POP DE + POP BC + LD L,3 ;prepare for error. + LD A,(STATUS) + INC A + JP Z,POSITN4 ;close error. + LD HL,12 ;put desired extent into fcb now. + ADD HL,DE + LD (HL),C + LD HL,14 ;and store extra extent byte 's2'. + ADD HL,DE + LD (HL),B + CALL OPENIT ;try and get this extent. + LD A,(STATUS) ;was it there? + INC A + JP NZ,POSITN3 + POP BC ;no. can we create a new one (writing?). + PUSH BC + LD L,4 ;prepare for error. + INC C + JP Z,POSITN4 ;nope, reading unwritten space error. + CALL GETEMPTY ;yes we can, try to find space. + LD L,5 ;prepare for error. + LD A,(STATUS) + INC A + JP Z,POSITN4 ;out of space? +; +; Normal return location. Clear error code and return. +; +POSITN3:POP BC ;restore stack. + XOR A ;and clear error code byte. + JP SETSTAT +; +; Error. Set the 's2' byte to indicate this (why?). +; +POSITN4:PUSH HL + CALL GETS2 + LD (HL),0C0H + POP HL +; +; Return with error code (presently in L). +; +POSITN5:POP BC + LD A,L ;get error code. + LD (STATUS),A + JP SETS2B7 +; +; Read a random record. +; +READRAN:LD C,0FFH ;set 'read' status. + CALL POSITION ;position the file to proper record. + CALL Z,RDSEQ1 ;and read it as usual (if no errors). + RET +; +; Write to a random record. +; +WRITERAN: LD C,0 ;set 'writing' flag. + CALL POSITION ;position the file to proper record. + CALL Z,WTSEQ1 ;and write as usual (if no errors). + RET +; +; Compute the random record number. Enter with (HL) pointing +; to a fcb an (DE) contains a relative location of a record +; number. On exit, (C) contains the 'r0' byte, (B) the 'r1' +; byte, and (A) the 'r2' byte. +; +; On return, the zero flag is set if the record is within +; bounds. Otherwise, an overflow occured. +; +COMPRAND: EX DE,HL ;save fcb pointer in (DE). + ADD HL,DE ;compute relative position of record #. + LD C,(HL) ;get record number into (BC). + LD B,0 + LD HL,12 ;now get extent. + ADD HL,DE + LD A,(HL) ;compute (BC)=(record #)+(extent)*128. + RRCA ;move lower bit into bit 7. + AND 80H ;and ignore all other bits. + ADD A,C ;add to our record number. + LD C,A + LD A,0 ;take care of any carry. + ADC A,B + LD B,A + LD A,(HL) ;now get the upper bits of extent into + RRCA ;bit positions 0-3. + AND 0FH ;and ignore all others. + ADD A,B ;add this in to 'r1' byte. + LD B,A + LD HL,14 ;get the 's2' byte (extra extent). + ADD HL,DE + LD A,(HL) + ADD A,A ;and shift it left 4 bits (bits 4-7). + ADD A,A + ADD A,A + ADD A,A + PUSH AF ;save carry flag (bit 0 of flag byte). + ADD A,B ;now add extra extent into 'r1'. + LD B,A + PUSH AF ;and save carry (overflow byte 'r2'). + POP HL ;bit 0 of (L) is the overflow indicator. + LD A,L + POP HL ;and same for first carry flag. + OR L ;either one of these set? + AND 01H ;only check the carry flags. + RET +; +; Routine to setup the fcb (bytes 'r0', 'r1', 'r2') to +; reflect the last record used for a random (or other) file. +; This reads the directory and looks at all extents computing +; the largerst record number for each and keeping the maximum +; value only. Then 'r0', 'r1', and 'r2' will reflect this +; maximum record number. This is used to compute the space used +; by a random file. +; +RANSIZE:LD C,12 ;look thru directory for first entry with + CALL FINDFST ;this name. + LD HL,(PARAMS) ;zero out the 'r0, r1, r2' bytes. + LD DE,33 + ADD HL,DE + PUSH HL + LD (HL),D ;note that (D)=0. + INC HL + LD (HL),D + INC HL + LD (HL),D +RANSIZ1:CALL CKFILPOS ;is there an extent to process? + JP Z,RANSIZ3 ;no, we are done. + CALL FCB2HL ;set (HL) pointing to proper fcb in dir. + LD DE,15 ;point to last record in extent. + CALL COMPRAND ;and compute random parameters. + POP HL + PUSH HL ;now check these values against those + LD E,A ;already in fcb. + LD A,C ;the carry flag will be set if those + SUB (HL) ;in the fcb represent a larger size than + INC HL ;this extent does. + LD A,B + SBC A,(HL) + INC HL + LD A,E + SBC A,(HL) + JP C,RANSIZ2 + LD (HL),E ;we found a larger (in size) extent. + DEC HL ;stuff these values into fcb. + LD (HL),B + DEC HL + LD (HL),C +RANSIZ2:CALL FINDNXT ;now get the next extent. + JP RANSIZ1 ;continue til all done. +RANSIZ3:POP HL ;we are done, restore the stack and + RET ;return. +; +; Function to return the random record position of a given +; file which has been read in sequential mode up to now. +; +SETRAN: LD HL,(PARAMS) ;point to fcb. + LD DE,32 ;and to last used record. + CALL COMPRAND ;compute random position. + LD HL,33 ;now stuff these values into fcb. + ADD HL,DE + LD (HL),C ;move 'r0'. + INC HL + LD (HL),B ;and 'r1'. + INC HL + LD (HL),A ;and lastly 'r2'. + RET +; +; This routine select the drive specified in (ACTIVE) and +; update the login vector and bitmap table if this drive was +; not already active. +; +LOGINDRV: LD HL,(LOGIN) ;get the login vector. + LD A,(ACTIVE) ;get the default drive. + LD C,A + CALL SHIFTR ;position active bit for this drive + PUSH HL ;into bit 0. + EX DE,HL + CALL SELECT ;select this drive. + POP HL + CALL Z,SLCTERR ;valid drive? + LD A,L ;is this a newly activated drive? + RRA + RET C + LD HL,(LOGIN) ;yes, update the login vector. + LD C,L + LD B,H + CALL SETBIT + LD (LOGIN),HL ;and save. + JP BITMAP ;now update the bitmap. +; +; Function to set the active disk number. +; +SETDSK: LD A,(EPARAM) ;get parameter passed and see if this + LD HL,ACTIVE ;represents a change in drives. + CP (HL) + RET Z + LD (HL),A ;yes it does, log it in. + JP LOGINDRV +; +; This is the 'auto disk select' routine. The firsst byte +; of the fcb is examined for a drive specification. If non +; zero then the drive will be selected and loged in. +; +AUTOSEL:LD A,0FFH ;say 'auto-select activated'. + LD (AUTO),A + LD HL,(PARAMS) ;get drive specified. + LD A,(HL) + AND 1FH ;look at lower 5 bits. + DEC A ;adjust for (1=A, 2=B) etc. + LD (EPARAM),A ;and save for the select routine. + CP 1EH ;check for 'no change' condition. + JP NC,AUTOSL1 ;yes, don't change. + LD A,(ACTIVE) ;we must change, save currently active + LD (OLDDRV),A ;drive. + LD A,(HL) ;and save first byte of fcb also. + LD (AUTOFLAG),A ;this must be non-zero. + AND 0E0H ;whats this for (bits 6,7 are used for + LD (HL),A ;something)? + CALL SETDSK ;select and log in this drive. +AUTOSL1:LD A,(USERNO) ;move user number into fcb. + LD HL,(PARAMS) ;(* upper half of first byte *) + OR (HL) + LD (HL),A + RET ;and return (all done). +; +; Function to return the current cp/m version number. +; +GETVER: LD A,022H ;version 2.2 + JP SETSTAT +; +; Function to reset the disk system. +; +RSTDSK: LD HL,0 ;clear write protect status and log + LD (WRTPRT),HL ;in vector. + LD (LOGIN),HL + XOR A ;select drive 'A'. + LD (ACTIVE),A + LD HL,TBUFF ;setup default dma address. + LD (USERDMA),HL + CALL DEFDMA + JP LOGINDRV ;now log in drive 'A'. +; +; Function to open a specified file. +; +OPENFIL:CALL CLEARS2 ;clear 's2' byte. + CALL AUTOSEL ;select proper disk. + JP OPENIT ;and open the file. +; +; Function to close a specified file. +; +CLOSEFIL: CALL AUTOSEL ;select proper disk. + JP CLOSEIT ;and close the file. +; +; Function to return the first occurence of a specified file +; name. If the first byte of the fcb is '?' then the name will +; not be checked (get the first entry no matter what). +; +GETFST: LD C,0 ;prepare for special search. + EX DE,HL + LD A,(HL) ;is first byte a '?'? + CP '?' + JP Z,GETFST1 ;yes, just get very first entry (zero length match). + CALL SETEXT ;get the extension byte from fcb. + LD A,(HL) ;is it '?'? if yes, then we want + CP '?' ;an entry with a specific 's2' byte. + CALL NZ,CLEARS2 ;otherwise, look for a zero 's2' byte. + CALL AUTOSEL ;select proper drive. + LD C,15 ;compare bytes 0-14 in fcb (12&13 excluded). +GETFST1:CALL FINDFST ;find an entry and then move it into + JP MOVEDIR ;the users dma space. +; +; Function to return the next occurence of a file name. +; +GETNXT: LD HL,(SAVEFCB) ;restore pointers. note that no + LD (PARAMS),HL ;other dbos calls are allowed. + CALL AUTOSEL ;no error will be returned, but the + CALL FINDNXT ;results will be wrong. + JP MOVEDIR +; +; Function to delete a file by name. +; +DELFILE:CALL AUTOSEL ;select proper drive. + CALL ERAFILE ;erase the file. + JP STSTATUS ;set status and return. +; +; Function to execute a sequential read of the specified +; record number. +; +READSEQ:CALL AUTOSEL ;select proper drive then read. + JP RDSEQ +; +; Function to write the net sequential record. +; +WRTSEQ: CALL AUTOSEL ;select proper drive then write. + JP WTSEQ +; +; Create a file function. +; +FCREATE:CALL CLEARS2 ;clear the 's2' byte on all creates. + CALL AUTOSEL ;select proper drive and get the next + JP GETEMPTY ;empty directory space. +; +; Function to rename a file. +; +RENFILE:CALL AUTOSEL ;select proper drive and then switch + CALL CHGNAMES ;file names. + JP STSTATUS +; +; Function to return the login vector. +; +GETLOG: LD HL,(LOGIN) + JP GETPRM1 +; +; Function to return the current disk assignment. +; +GETCRNT:LD A,(ACTIVE) + JP SETSTAT +; +; Function to set the dma address. +; +PUTDMA: EX DE,HL + LD (USERDMA),HL ;save in our space and then get to + JP DEFDMA ;the bios with this also. +; +; Function to return the allocation vector. +; +GETALOC:LD HL,(ALOCVECT) + JP GETPRM1 +; +; Function to return the read-only status vector. +; +GETROV: LD HL,(WRTPRT) + JP GETPRM1 +; +; Function to set the file attributes (read-only, system). +; +SETATTR:CALL AUTOSEL ;select proper drive then save attributes. + CALL SAVEATTR + JP STSTATUS +; +; Function to return the address of the disk parameter block +; for the current drive. +; +GETPARM:LD HL,(DISKPB) +GETPRM1:LD (STATUS),HL + RET +; +; Function to get or set the user number. If (E) was (FF) +; then this is a request to return the current user number. +; Else set the user number from (E). +; +GETUSER:LD A,(EPARAM) ;get parameter. + CP 0FFH ;get user number? + JP NZ,SETUSER + LD A,(USERNO) ;yes, just do it. + JP SETSTAT +SETUSER:AND 1FH ;no, we should set it instead. keep low + LD (USERNO),A ;bits (0-4) only. + RET +; +; Function to read a random record from a file. +; +RDRANDOM: CALL AUTOSEL ;select proper drive and read. + JP READRAN +; +; Function to compute the file size for random files. +; +WTRANDOM: CALL AUTOSEL ;select proper drive and write. + JP WRITERAN +; +; Function to compute the size of a random file. +; +FILESIZE: CALL AUTOSEL ;select proper drive and check file length + JP RANSIZE +; +; Function #37. This allows a program to log off any drives. +; On entry, set (DE) to contain a word with bits set for those +; drives that are to be logged off. The log-in vector and the +; write protect vector will be updated. This must be a M/PM +; special function. +; +LOGOFF: LD HL,(PARAMS) ;get drives to log off. + LD A,L ;for each bit that is set, we want + CPL ;to clear that bit in (LOGIN) + LD E,A ;and (WRTPRT). + LD A,H + CPL + LD HL,(LOGIN) ;reset the login vector. + AND H + LD D,A + LD A,L + AND E + LD E,A + LD HL,(WRTPRT) + EX DE,HL + LD (LOGIN),HL ;and save. + LD A,L ;now do the write protect vector. + AND E + LD L,A + LD A,H + AND D + LD H,A + LD (WRTPRT),HL ;and save. all done. + RET +; +; Get here to return to the user. +; +GOBACK: LD A,(AUTO) ;was auto select activated? + OR A + JP Z,GOBACK1 + LD HL,(PARAMS) ;yes, but was a change made? + LD (HL),0 ;(* reset first byte of fcb *) + LD A,(AUTOFLAG) + OR A + JP Z,GOBACK1 + LD (HL),A ;yes, reset first byte properly. + LD A,(OLDDRV) ;and get the old drive and select it. + LD (EPARAM),A + CALL SETDSK +GOBACK1:LD HL,(USRSTACK) ;reset the users stack pointer. + LD SP,HL + LD HL,(STATUS) ;get return status. + LD A,L ;force version 1.4 compatability. + LD B,H + RET ;and go back to user. +; +; Function #40. This is a special entry to do random i/o. +; For the case where we are writing to unused disk space, this +; space will be zeroed out first. This must be a M/PM special +; purpose function, because why would any normal program even +; care about the previous contents of a sector about to be +; written over. +; +WTSPECL:CALL AUTOSEL ;select proper drive. + LD A,2 ;use special write mode. + LD (MODE),A + LD C,0 ;set write indicator. + CALL POSITN1 ;position the file. + CALL Z,WTSEQ1 ;and write (if no errors). + RET +; +;************************************************************** +;* +;* BDOS data storage pool. +;* +;************************************************************** +; +EMPTYFCB: DEFB 0E5H ;empty directory segment indicator. +WRTPRT: DEFW 0 ;write protect status for all 16 drives. +LOGIN: DEFW 0 ;drive active word (1 bit per drive). +USERDMA:DEFW 080H ;user's dma address (defaults to 80h). +; +; Scratch areas from parameter block. +; +SCRATCH1: DEFW 0 ;relative position within dir segment for file (0-3). +SCRATCH2: DEFW 0 ;last selected track number. +SCRATCH3: DEFW 0 ;last selected sector number. +; +; Disk storage areas from parameter block. +; +DIRBUF: DEFW 0 ;address of directory buffer to use. +DISKPB: DEFW 0 ;contains address of disk parameter block. +CHKVECT:DEFW 0 ;address of check vector. +ALOCVECT: DEFW 0 ;address of allocation vector (bit map). +; +; Parameter block returned from the bios. +; +SECTORS:DEFW 0 ;sectors per track from bios. +BLKSHFT:DEFB 0 ;block shift. +BLKMASK:DEFB 0 ;block mask. +EXTMASK:DEFB 0 ;extent mask. +DSKSIZE:DEFW 0 ;disk size from bios (number of blocks-1). +DIRSIZE:DEFW 0 ;directory size. +ALLOC0: DEFW 0 ;storage for first bytes of bit map (dir space used). +ALLOC1: DEFW 0 +OFFSET: DEFW 0 ;first usable track number. +XLATE: DEFW 0 ;sector translation table address. +; +; +CLOSEFLG: DEFB 0 ;close flag (=0ffh is extent written ok). +RDWRTFLG: DEFB 0 ;read/write flag (0ffh=read, 0=write). +FNDSTAT:DEFB 0 ;filename found status (0=found first entry). +MODE: DEFB 0 ;I/o mode select (0=random, 1=sequential, 2=special random). +EPARAM: DEFB 0 ;storage for register (E) on entry to bdos. +RELBLOCK: DEFB 0 ;relative position within fcb of block number written. +COUNTER:DEFB 0 ;byte counter for directory name searches. +SAVEFCB:DEFW 0,0 ;save space for address of fcb (for directory searches). +BIGDISK:DEFB 0 ;if =0 then disk is > 256 blocks long. +AUTO: DEFB 0 ;if non-zero, then auto select activated. +OLDDRV: DEFB 0 ;on auto select, storage for previous drive. +AUTOFLAG: DEFB 0 ;if non-zero, then auto select changed drives. +SAVNXT: DEFB 0 ;storage for next record number to access. +SAVEXT: DEFB 0 ;storage for extent number of file. +SAVNREC:DEFW 0 ;storage for number of records in file. +BLKNMBR:DEFW 0 ;block number (physical sector) used within a file or logical sect +LOGSECT:DEFW 0 ;starting logical (128 byte) sector of block (physical sector). +FCBPOS: DEFB 0 ;relative position within buffer for fcb of file of interest. +FILEPOS:DEFW 0 ;files position within directory (0 to max entries -1). +; +; Disk directory buffer checksum bytes. One for each of the +; 16 possible drives. +; +CKSUMTBL: DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +; +; Extra space ? +; + DEFB 0,0,0,0 +; +;************************************************************** +;* +;* B I O S J U M P T A B L E +;* +;************************************************************** +; +BOOT: JP 0 ;NOTE WE USE FAKE DESTINATIONS +WBOOT: JP 0 +CONST: JP 0 +CONIN: JP 0 +CONOUT: JP 0 +LIST: JP 0 +PUNCH: JP 0 +READER: JP 0 +HOME: JP 0 +SELDSK: JP 0 +SETTRK: JP 0 +SETSEC: JP 0 +SETDMA: JP 0 +READ: JP 0 +WRITE: JP 0 +PRSTAT: JP 0 +SECTRN: JP 0 +; +;* +;****************** E N D O F C P / M ***************** +;* + END + diff --git a/branches/s100/Applegate/cpm22.inc b/branches/s100/Applegate/cpm22.inc new file mode 100644 index 00000000..60405ae8 --- /dev/null +++ b/branches/s100/Applegate/cpm22.inc @@ -0,0 +1,39 @@ +;******************************************************** +; This file contains bits of information that are common +; between the CCP, BDOS and BIOS. Since the CCP and +; BDOS are built as one unit and the BIOS as another, it +; is handy to have common things in a common file rather +; that constantly needing to keep things in sync between +; two different files. +; +; 02/16/2013 - Bob Applegate + + +; +; This is the TOTAL RAM in the system. Ie, starts at 64 +; but then gets decreased due to ROM monitors at top of +; memory. This can be set artificially low to allow +; more room for BIOS debugging code. +; +; This is the amount of contiguous RAM from 0000. +; +RAMSIZE EQU 48 ;expressed in K + +; +; Low memory locations +; +IOBYTE EQU 3 ;i/o definition byte. +TDRIVE EQU 4 ;current drive name and user number. +ENTRY EQU 5 ;entry point for the cp/m bdos. + + + +CCPSIZE EQU 0800H ;I hate hard-coding things! +BDOSSIZE EQU 0E00H + +; +; Where CP/M things are located +; +CCPBASE equ (RAMSIZE-7)*1024 +BDOSBASE equ (CCPBASE+CCPSIZE) +BIOSBASE equ (BDOSBASE+BDOSSIZE) diff --git a/branches/s100/Applegate/loadcpm.py b/branches/s100/Applegate/loadcpm.py new file mode 100755 index 00000000..2fc1b81b --- /dev/null +++ b/branches/s100/Applegate/loadcpm.py @@ -0,0 +1,69 @@ +#!/usr/bin/python +# Written by Douglas Goodall 17:25 Wed, Jan 30, 2013 +# load cpm.bin and bios.bin then jump + +import sys +import os +import serial + +# passing in a string either "12" or "0x12" +# return value is string of hex digits only (no 0x) +def safebyte(parm): + xyz = parm + myord = ord(xyz) + hexdata = hex(myord) + newstr = hexdata + if (hexdata[0] == '0'): + if(hexdata[1] == 'x'): + newstr = hexdata[2] + if(len(hexdata)>3): + newstr = newstr + hexdata[3] + return newstr + +# passing in a string either "1234" of "0x1234" +# return value is string of hex digits only (1234) (no 0x) +def safeword(parm): + xyz = parm + myint = int(xyz) + hexdata = hex(myint) + newstr = hexdata + if (hexdata[0] == '0'): + if(hexdata[1] == 'x'): + newstr = hexdata[2] + if(len(hexdata)>3): + newstr = newstr + hexdata[3] + if(len(hexdata)>4): + newstr = newstr + hexdata[4] + if(len(hexdata)>5): + newstr = newstr + hexdata[5] + return newstr + +def loadngo(filename): + statinfo = os.stat(filename) + filelen = statinfo.st_size + infile = open(filename,'rb') + filedata = infile.read() + infile.close() + ser = serial.Serial('/dev/cu.PL2303-0000201D', 19200, timeout=10) + ser.write("\n\n") + ser.write("sa400\n") + print ser.read(12); + for x in range(1,filelen): + ser.write(safebyte(filedata[x-1])) + ser.write(" ") + print ser.read(12) + ser.write("\n") + print ser.read(12) + ser.close() + +print "*******************************************************************" +print "loadcpm.py 1/30/2013 dwg - load&go S-100 CP/M using master-yoda ROM" +print "*******************************************************************" +#loadngo("cpm.bin") +ser = serial.Serial('/dev/cu.PL2303-0000201D', 19200, timeout=1) +ser.read(128) +ser.read(128) +ser.write("\n") +ser.close() +loadngo("cpm.bin") + diff --git a/branches/s100/Applegate/s100iobd.inc b/branches/s100/Applegate/s100iobd.inc new file mode 100644 index 00000000..553ee3cb --- /dev/null +++ b/branches/s100/Applegate/s100iobd.inc @@ -0,0 +1,77 @@ + .page +;******************************************************** +; These are I/O functions for the CCS 2710 four-port +; serial I/O board. +; +; Bob Applegate, 02/16/2008 +; +SIObase .equ 0E0H +; +SIODATA .equ SIObase +SIOBAUDL .equ SIObase ;also used for baud rate +SIOINTEN .equ SIObase+1 +SIOBAUDH .equ SIObase+1 ;ditto +SIOIDENT .equ SIObase+2 +SIOLCTRL .equ SIObase+3 +SIOMDMCT .equ SIObase+4 +SIOLSTAT .equ SIObase+5 +SIOMDMST .equ SIObase+6 +BAUD03 .equ 0180H ;divisor for 300 baud +BAUD12 .equ 060H ;1200 baud +BAUD_2400 .equ 030h ;2400 baud +BAUD96 .equ 00CH ;9600 baud +DATRDY .equ 01H ;rec'd data ready +TXMTY .equ 20H ;transmitter holding reg empty +HSMSK .equ 20H + +BAUD_RATE .equ BAUD_2400 + +; +; This function initializes the main console port for the +; default baud rate. +; +initser: ld a,0fH + out (SIOMDMCT),a + ld a,083H ;enable divisor latch access + out (SIOLCTRL),a + ld a,BAUD_RATE / 256 ;get hi byte + out (SIOBAUDH),a + ld a,BAUD_RATE % 256 ;get low byte + out (SIOBAUDL),a + ld a,03H ;8 data bits, one stop bit, no parity + out (SIOLCTRL),a + xor a ;clear acc + out (SIOINTEN),a ;disable ints + out (SIOLSTAT),a ;clear status + in a,(SIODATA) ;clear out any garbage in rec'd data reg + ret +; +; TTY output of character in C. Modifies A. +; +ttyout: IN A,(SIOLSTAT) ;was A,TTS, read status port + AND TXMTY ;check buffer empty bit + JR Z,ttyout ;branch if not empty + LD A,C + OUT (SIODATA),A ;was TTO,A, send out character + RET ;thassit +; +; Check to see if a character is ready to be read from the TTY. +; Returns TRUE (0ffh) in A is there is a character waiting, or +; FALSE (0) if there is nothing. +; +ttystat: IN A,(SIOLSTAT) ;was A,TTS + AND DATRDY + LD A,TRUE ;was FALSE + RET NZ + CPL + RET +; +; This gets the next character from the TTY and returns it in A. +; This will block if there is nothing waiting. +; +ttyin: IN A,(SIOLSTAT) ;read status reg + AND DATRDY ;look for data ready + JR Z,ttyin ;wait for char + IN a,(SIODATA) ;read character + RET + .page diff --git a/branches/s100/Applegate/tasm.inc b/branches/s100/Applegate/tasm.inc new file mode 100644 index 00000000..e61cd62d --- /dev/null +++ b/branches/s100/Applegate/tasm.inc @@ -0,0 +1,19 @@ +; +; To make it easier to port code from other assemblers, +; this maps some common psuedo-ops into TASM versions. +; +#define EQU .equ +#define equ .equ +#define END .end +#define end .end +#define DEFB .db +#define DB .db +#define db .db +#define DEFW .dw +#define DW .dw +#define dw .dw +#define DS .ds +#define ds .ds +#define ORG .org +#define org .org +#define TEXT .text diff --git a/branches/s100/Applegate/tasm.tab b/branches/s100/Applegate/tasm.tab new file mode 100644 index 00000000..f644ccd4 --- /dev/null +++ b/branches/s100/Applegate/tasm.tab @@ -0,0 +1,594 @@ +"TASM Z80 Assembler. " +/**************************************************************************** +/* $Id: tasm80.tab 1.2 1998/02/28 14:31:22 toma Exp $ +/**************************************************************************** +/* This is the instruction set definition table +/* for the Z80 version of TASM. +/* Thomas N. Anderson, Speech Technology Incorporated +/* This table authored and submitted by Carl A. Wall, VE3APY. +/* +/* Class bits assigned as follows: +/* Bit-0 = Z80 (base instruction set) +/* Bit-1 = HD64180 (extended instructions) +/* See TASM manual for info on table structure. +/* +/*INSTR ARGS OP BYTES RULE CLASS SHIFT OR */ +/*-------------------------------------------*/ + +ADC A,(HL) 8E 1 NOP 1 +ADC A,(IX*) 8EDD 3 ZIX 1 +ADC A,(IY*) 8EFD 3 ZIX 1 +ADC A,A 8F 1 NOP 1 +ADC A,B 88 1 NOP 1 +ADC A,C 89 1 NOP 1 +ADC A,D 8A 1 NOP 1 +ADC A,E 8B 1 NOP 1 +ADC A,H 8C 1 NOP 1 +ADC A,L 8D 1 NOP 1 +ADC A,* CE 2 NOP 1 +ADC HL,BC 4AED 2 NOP 1 +ADC HL,DE 5AED 2 NOP 1 +ADC HL,HL 6AED 2 NOP 1 +ADC HL,SP 7AED 2 NOP 1 + +ADD A,(HL) 86 1 NOP 1 +ADD A,(IX*) 86DD 3 ZIX 1 +ADD A,(IY*) 86FD 3 ZIX 1 +ADD A,A 87 1 NOP 1 +ADD A,B 80 1 NOP 1 +ADD A,C 81 1 NOP 1 +ADD A,D 82 1 NOP 1 +ADD A,E 83 1 NOP 1 +ADD A,H 84 1 NOP 1 +ADD A,L 85 1 NOP 1 +ADD A,* C6 2 NOP 1 +ADD HL,BC 09 1 NOP 1 +ADD HL,DE 19 1 NOP 1 +ADD HL,HL 29 1 NOP 1 +ADD HL,SP 39 1 NOP 1 +ADD IX,BC 09DD 2 NOP 1 +ADD IX,DE 19DD 2 NOP 1 +ADD IX,IX 29DD 2 NOP 1 +ADD IX,SP 39DD 2 NOP 1 +ADD IY,BC 09FD 2 NOP 1 +ADD IY,DE 19FD 2 NOP 1 +ADD IY,IY 29FD 2 NOP 1 +ADD IY,SP 39FD 2 NOP 1 + +AND (HL) A6 1 NOP 1 +AND (IX*) A6DD 3 ZIX 1 +AND (IY*) A6FD 3 ZIX 1 +AND A A7 1 NOP 1 +AND B A0 1 NOP 1 +AND C A1 1 NOP 1 +AND D A2 1 NOP 1 +AND E A3 1 NOP 1 +AND H A4 1 NOP 1 +AND L A5 1 NOP 1 +AND * E6 2 NOP 1 + +BIT *,(HL) 46CB 2 ZBIT 1 +BIT *,(IX*) CBDD 4 ZBIT 1 0 4600 +BIT *,(IY*) CBFD 4 ZBIT 1 0 4600 +BIT *,A 47CB 2 ZBIT 1 +BIT *,B 40CB 2 ZBIT 1 +BIT *,C 41CB 2 ZBIT 1 +BIT *,D 42CB 2 ZBIT 1 +BIT *,E 43CB 2 ZBIT 1 +BIT *,H 44CB 2 ZBIT 1 +BIT *,L 45CB 2 ZBIT 1 + +CALL C,* DC 3 NOP 1 +CALL M,* FC 3 NOP 1 +CALL NC,* D4 3 NOP 1 +CALL NZ,* C4 3 NOP 1 +CALL P,* F4 3 NOP 1 +CALL PE,* EC 3 NOP 1 +CALL PO,* E4 3 NOP 1 +CALL Z,* CC 3 NOP 1 +CALL * CD 3 NOP 1 + +CCF "" 3F 1 NOP 1 + +CP (HL) BE 1 NOP 1 +CP (IX*) BEDD 3 ZIX 1 +CP (IY*) BEFD 3 ZIX 1 +CP A BF 1 NOP 1 +CP B B8 1 NOP 1 +CP C B9 1 NOP 1 +CP D BA 1 NOP 1 +CP E BB 1 NOP 1 +CP H BC 1 NOP 1 +CP L BD 1 NOP 1 +CP * FE 2 NOP 1 +CPD "" A9ED 2 NOP 1 +CPDR "" B9ED 2 NOP 1 +CPIR "" B1ED 2 NOP 1 +CPI "" A1ED 2 NOP 1 +CPL "" 2F 1 NOP 1 + +DAA "" 27 1 NOP 1 + +DEC (HL) 35 1 NOP 1 +DEC (IX*) 35DD 3 ZIX 1 +DEC (IY*) 35FD 3 ZIX 1 +DEC A 3D 1 NOP 1 +DEC B 05 1 NOP 1 +DEC BC 0B 1 NOP 1 +DEC C 0D 1 NOP 1 +DEC D 15 1 NOP 1 +DEC DE 1B 1 NOP 1 +DEC E 1D 1 NOP 1 +DEC H 25 1 NOP 1 +DEC HL 2B 1 NOP 1 +DEC IX 2BDD 2 NOP 1 +DEC IY 2BFD 2 NOP 1 +DEC L 2D 1 NOP 1 +DEC SP 3B 1 NOP 1 +DI "" F3 1 NOP 1 +DJNZ * 10 2 R1 1 + +EI "" FB 1 NOP 1 +EX (SP),HL E3 1 NOP 1 +EX (SP),IX E3DD 2 NOP 1 +EX (SP),IY E3FD 2 NOP 1 +EX AF,AF' 08 1 NOP 1 +EX DE,HL EB 1 NOP 1 +EXX "" D9 1 NOP 1 +HALT "" 76 1 NOP 1 + +IM 0 46ED 2 NOP 1 +IM 1 56ED 2 NOP 1 +IM 2 5EED 2 NOP 1 + +/* Alternate form of above +IM0 46ED 2 NOP 1 +IM1 56ED 2 NOP 1 +IM2 5EED 2 NOP 1 + +IN A,(C) 78ED 2 NOP 1 +IN B,(C) 40ED 2 NOP 1 +IN C,(C) 48ED 2 NOP 1 +IN D,(C) 50ED 2 NOP 1 +IN E,(C) 58ED 2 NOP 1 +IN H,(C) 60ED 2 NOP 1 +IN L,(C) 68ED 2 NOP 1 + +IN A,(*) DB 2 NOP 1 + +IN0 A,(*) 38ED 3 NOP 2 +IN0 B,(*) 00ED 3 NOP 2 +IN0 C,(*) 08ED 3 NOP 2 +IN0 D,(*) 10ED 3 NOP 2 +IN0 E,(*) 18ED 3 NOP 2 +IN0 H,(*) 20ED 3 NOP 2 +IN0 L,(*) 28ED 3 NOP 2 + +INC (HL) 34 1 NOP 1 +INC (IX*) 34DD 3 ZIX 1 +INC (IY*) 34FD 3 ZIX 1 +INC A 3C 1 NOP 1 +INC B 04 1 NOP 1 +INC BC 03 1 NOP 1 +INC C 0C 1 NOP 1 +INC D 14 1 NOP 1 +INC DE 13 1 NOP 1 +INC E 1C 1 NOP 1 +INC H 24 1 NOP 1 +INC HL 23 1 NOP 1 +INC IX 23DD 2 NOP 1 +INC IY 23FD 2 NOP 1 +INC L 2C 1 NOP 1 +INC SP 33 1 NOP 1 + + +IND "" AAED 2 NOP 1 +INDR "" BAED 2 NOP 1 +INI "" A2ED 2 NOP 1 +INIR "" B2ED 2 NOP 1 + +JP (HL) E9 1 NOP 1 +JP (IX) E9DD 2 NOP 1 +JP (IY) E9FD 2 NOP 1 +JP C,* DA 3 NOP 1 +JP M,* FA 3 NOP 1 +JP NC,* D2 3 NOP 1 +JP NZ,* C2 3 NOP 1 +JP P,* F2 3 NOP 1 +JP PE,* EA 3 NOP 1 +JP PO,* E2 3 NOP 1 +JP Z,* CA 3 NOP 1 +JP * C3 3 NOP 1 + +JR C,* 38 2 R1 1 +JR NC,* 30 2 R1 1 +JR NZ,* 20 2 R1 1 +JR Z,* 28 2 R1 1 +JR * 18 2 R1 1 + +LD (BC),A 02 1 NOP 1 +LD (DE),A 12 1 NOP 1 +LD (HL),A 77 1 NOP 1 +LD (HL),B 70 1 NOP 1 +LD (HL),C 71 1 NOP 1 +LD (HL),D 72 1 NOP 1 +LD (HL),E 73 1 NOP 1 +LD (HL),H 74 1 NOP 1 +LD (HL),L 75 1 NOP 1 +LD (HL),* 36 2 NOP 1 +LD (IX*),A 77DD 3 ZIX 1 +LD (IX*),B 70DD 3 ZIX 1 +LD (IX*),C 71DD 3 ZIX 1 +LD (IX*),D 72DD 3 ZIX 1 +LD (IX*),E 73DD 3 ZIX 1 +LD (IX*),H 74DD 3 ZIX 1 +LD (IX*),L 75DD 3 ZIX 1 +LD (IX*),* 36DD 4 ZIX 1 +LD (IY*),A 77FD 3 ZIX 1 +LD (IY*),B 70FD 3 ZIX 1 +LD (IY*),C 71FD 3 ZIX 1 +LD (IY*),D 72FD 3 ZIX 1 +LD (IY*),E 73FD 3 ZIX 1 +LD (IY*),H 74FD 3 ZIX 1 +LD (IY*),L 75FD 3 ZIX 1 +LD (IY*),* 36FD 4 ZIX 1 +LD (*),A 32 3 NOP 1 +LD (*),BC 43ED 4 NOP 1 +LD (*),DE 53ED 4 NOP 1 +LD (*),HL 22 3 NOP 1 +LD (*),IX 22DD 4 NOP 1 +LD (*),IY 22FD 4 NOP 1 +LD (*),SP 73ED 4 NOP 1 +LD A,(BC) 0A 1 NOP 1 +LD A,(DE) 1A 1 NOP 1 +LD A,(HL) 7E 1 NOP 1 +LD A,(IX*) 7EDD 3 ZIX 1 +LD A,(IY*) 7EFD 3 ZIX 1 +LD A,A 7F 1 NOP 1 +LD A,B 78 1 NOP 1 +LD A,C 79 1 NOP 1 +LD A,D 7A 1 NOP 1 +LD A,E 7B 1 NOP 1 +LD A,H 7C 1 NOP 1 +LD A,I 57ED 2 NOP 1 +LD A,L 7D 1 NOP 1 +LD A,R 5FED 2 NOP 1 +LD A,(*) 3A 3 NOP 1 +LD A,* 3E 2 NOP 1 +LD B,(HL) 46 1 NOP 1 +LD B,(IX*) 46DD 3 ZIX 1 +LD B,(IY*) 46FD 3 ZIX 1 +LD B,A 47 1 NOP 1 +LD B,B 40 1 NOP 1 +LD B,C 41 1 NOP 1 +LD B,D 42 1 NOP 1 +LD B,E 43 1 NOP 1 +LD B,H 44 1 NOP 1 +LD B,L 45 1 NOP 1 +LD B,* 06 2 NOP 1 +LD BC,(*) 4BED 4 NOP 1 +LD BC,* 01 3 NOP 1 +LD C,(HL) 4E 1 NOP 1 +LD C,(IX*) 4EDD 3 ZIX 1 +LD C,(IY*) 4EFD 3 ZIX 1 +LD C,A 4F 1 NOP 1 +LD C,B 48 1 NOP 1 +LD C,C 49 1 NOP 1 +LD C,D 4A 1 NOP 1 +LD C,E 4B 1 NOP 1 +LD C,H 4C 1 NOP 1 +LD C,L 4D 1 NOP 1 +LD C,* 0E 2 NOP 1 +LD D,(HL) 56 1 NOP 1 +LD D,(IX*) 56DD 3 ZIX 1 +LD D,(IY*) 56FD 3 ZIX 1 +LD D,A 57 1 NOP 1 +LD D,B 50 1 NOP 1 +LD D,C 51 1 NOP 1 +LD D,D 52 1 NOP 1 +LD D,E 53 1 NOP 1 +LD D,H 54 1 NOP 1 +LD D,L 55 1 NOP 1 +LD D,* 16 2 NOP 1 +LD DE,(*) 5BED 4 NOP 1 +LD DE,* 11 3 NOP 1 +LD E,(HL) 5E 1 NOP 1 +LD E,(IX*) 5EDD 3 ZIX 1 +LD E,(IY*) 5EFD 3 ZIX 1 +LD E,A 5F 1 NOP 1 +LD E,B 58 1 NOP 1 +LD E,C 59 1 NOP 1 +LD E,D 5A 1 NOP 1 +LD E,E 5B 1 NOP 1 +LD E,H 5C 1 NOP 1 +LD E,L 5D 1 NOP 1 +LD E,* 1E 2 NOP 1 +LD H,(HL) 66 1 NOP 1 +LD H,(IX*) 66DD 3 ZIX 1 +LD H,(IY*) 66FD 3 ZIX 1 +LD H,A 67 1 NOP 1 +LD H,B 60 1 NOP 1 +LD H,C 61 1 NOP 1 +LD H,D 62 1 NOP 1 +LD H,E 63 1 NOP 1 +LD H,H 64 1 NOP 1 +LD H,L 65 1 NOP 1 +LD H,* 26 2 NOP 1 +LD HL,(*) 2A 3 NOP 1 +LD HL,* 21 3 NOP 1 +LD I,A 47ED 2 NOP 1 +LD IX,(*) 2ADD 4 NOP 1 +LD IX,* 21DD 4 NOP 1 +LD IY,(*) 2AFD 4 NOP 1 +LD IY,* 21FD 4 NOP 1 +LD L,(HL) 6E 1 NOP 1 +LD L,(IX*) 6EDD 3 ZIX 1 +LD L,(IY*) 6EFD 3 ZIX 1 +LD L,A 6F 1 NOP 1 +LD L,B 68 1 NOP 1 +LD L,C 69 1 NOP 1 +LD L,D 6A 1 NOP 1 +LD L,E 6B 1 NOP 1 +LD L,H 6C 1 NOP 1 +LD L,L 6D 1 NOP 1 +LD L,* 2E 2 NOP 1 +LD R,A 4FED 2 NOP 1 +LD SP,(*) 7BED 4 NOP 1 +LD SP,HL F9 1 NOP 1 +LD SP,IX F9DD 2 NOP 1 +LD SP,IY F9FD 2 NOP 1 +LD SP,* 31 3 NOP 1 +LDD "" A8ED 2 NOP 1 +LDDR "" B8ED 2 NOP 1 +LDI "" A0ED 2 NOP 1 +LDIR "" B0ED 2 NOP 1 +NEG "" 44ED 2 NOP 1 +NOP "" 00 1 NOP 1 + +MLT BC 4CED 2 NOP 2 +MLT DE 5CED 2 NOP 2 +MLT HL 6CED 2 NOP 2 +MLT SP 7CED 2 NOP 2 + +OR (HL) B6 1 NOP 1 +OR (IX*) B6DD 3 ZIX 1 +OR (IY*) B6FD 3 ZIX 1 +OR A B7 1 NOP 1 +OR B B0 1 NOP 1 +OR C B1 1 NOP 1 +OR D B2 1 NOP 1 +OR E B3 1 NOP 1 +OR H B4 1 NOP 1 +OR L B5 1 NOP 1 +OR * F6 2 NOP 1 + +OTDM "" 8BED 2 NOP 2 +OTDMR "" 9BED 2 NOP 2 +OTDR "" BBED 2 NOP 1 +OTIM "" 83ED 2 NOP 2 +OTIMR "" 93ED 2 NOP 2 +OTIR "" B3ED 2 NOP 1 + +OUT (C),A 79ED 2 NOP 1 +OUT (C),B 41ED 2 NOP 1 +OUT (C),C 49ED 2 NOP 1 +OUT (C),D 51ED 2 NOP 1 +OUT (C),E 59ED 2 NOP 1 +OUT (C),H 61ED 2 NOP 1 +OUT (C),L 69ED 2 NOP 1 +OUT (*),A D3 2 NOP 1 + +OUT0 (*),A 39ED 3 NOP 2 +OUT0 (*),B 01ED 3 NOP 2 +OUT0 (*),C 09ED 3 NOP 2 +OUT0 (*),D 11ED 3 NOP 2 +OUT0 (*),E 19ED 3 NOP 2 +OUT0 (*),H 21ED 3 NOP 2 +OUT0 (*),L 29ED 3 NOP 2 + +OUTD "" ABED 2 NOP 1 +OUTI "" A3ED 2 NOP 1 + +POP AF F1 1 NOP 1 +POP BC C1 1 NOP 1 +POP DE D1 1 NOP 1 +POP HL E1 1 NOP 1 +POP IX E1DD 2 NOP 1 +POP IY E1FD 2 NOP 1 + +PUSH AF F5 1 NOP 1 +PUSH BC C5 1 NOP 1 +PUSH DE D5 1 NOP 1 +PUSH HL E5 1 NOP 1 +PUSH IX E5DD 2 NOP 1 +PUSH IY E5FD 2 NOP 1 + +RES *,(HL) 86CB 2 ZBIT 1 +RES *,(IX*) CBDD 4 ZBIT 1 0 8600 +RES *,(IY*) CBFD 4 ZBIT 1 0 8600 +RES *,A 87CB 2 ZBIT 1 +RES *,B 80CB 2 ZBIT 1 +RES *,C 81CB 2 ZBIT 1 +RES *,D 82CB 2 ZBIT 1 +RES *,E 83CB 2 ZBIT 1 +RES *,H 84CB 2 ZBIT 1 +RES *,L 85CB 2 ZBIT 1 + +RET "" C9 1 NOP 1 +RET C D8 1 NOP 1 +RET M F8 1 NOP 1 +RET NC D0 1 NOP 1 +RET NZ C0 1 NOP 1 +RET P F0 1 NOP 1 +RET PE E8 1 NOP 1 +RET PO E0 1 NOP 1 +RET Z C8 1 NOP 1 +RETI "" 4DED 2 NOP 1 +RETN "" 45ED 2 NOP 1 + +RL (HL) 16CB 2 NOP 1 +RL (IX*) CBDD 4 ZIX 1 0 1600 +RL (IY*) CBFD 4 ZIX 1 0 1600 +RL A 17CB 2 NOP 1 +RL B 10CB 2 NOP 1 +RL C 11CB 2 NOP 1 +RL D 12CB 2 NOP 1 +RL E 13CB 2 NOP 1 +RL H 14CB 2 NOP 1 +RL L 15CB 2 NOP 1 +RLA "" 17 1 NOP 1 + +RLC (HL) 06CB 2 NOP 1 +RLC (IX*) CBDD 4 ZIX 1 0 0600 +RLC (IY*) CBFD 4 ZIX 1 0 0600 +RLC A 07CB 2 NOP 1 +RLC B 00CB 2 NOP 1 +RLC C 01CB 2 NOP 1 +RLC D 02CB 2 NOP 1 +RLC E 03CB 2 NOP 1 +RLC H 04CB 2 NOP 1 +RLC L 05CB 2 NOP 1 +RLCA "" 07 1 NOP 1 +RLD "" 6FED 2 NOP 1 + +RR (HL) 1ECB 2 NOP 1 +RR (IX*) CBDD 4 ZIX 1 0 1E00 +RR (IY*) CBFD 4 ZIX 1 0 1E00 +RR A 1FCB 2 NOP 1 +RR B 18CB 2 NOP 1 +RR C 19CB 2 NOP 1 +RR D 1ACB 2 NOP 1 +RR E 1BCB 2 NOP 1 +RR H 1CCB 2 NOP 1 +RR L 1DCB 2 NOP 1 +RRA "" 1F 1 NOP 1 +RRC (HL) 0ECB 2 NOP 1 +RRC (IX*) CBDD 4 ZIX 1 0 0E00 +RRC (IY*) CBFD 4 ZIX 1 0 0E00 +RRC A 0FCB 2 NOP 1 +RRC B 08CB 2 NOP 1 +RRC C 09CB 2 NOP 1 +RRC D 0ACB 2 NOP 1 +RRC E 0BCB 2 NOP 1 +RRC H 0CCB 2 NOP 1 +RRC L 0DCB 2 NOP 1 +RRCA "" 0F 1 NOP 1 +RRD "" 67ED 2 NOP 1 + +RST 00H C7 1 NOP 1 +RST 08H CF 1 NOP 1 +RST 10H D7 1 NOP 1 +RST 18H DF 1 NOP 1 +RST 20H E7 1 NOP 1 +RST 28H EF 1 NOP 1 +RST 30H F7 1 NOP 1 +RST 38H FF 1 NOP 1 + +/* Alternate form of above +RST 00 C7 1 NOP 1 +RST 08 CF 1 NOP 1 +RST 10 D7 1 NOP 1 +RST 18 DF 1 NOP 1 +RST 20 E7 1 NOP 1 +RST 28 EF 1 NOP 1 +RST 30 F7 1 NOP 1 +RST 38 FF 1 NOP 1 + +SBC A,(HL) 9E 1 NOP 1 +SBC A,(IX*) 9EDD 3 ZIX 1 +SBC A,(IY*) 9EFD 3 ZIX 1 +SBC A,A 9F 1 NOP 1 +SBC A,B 98 1 NOP 1 +SBC A,C 99 1 NOP 1 +SBC A,D 9A 1 NOP 1 +SBC A,E 9B 1 NOP 1 +SBC A,H 9C 1 NOP 1 +SBC A,L 9D 1 NOP 1 +SBC HL,BC 42ED 2 NOP 1 +SBC HL,DE 52ED 2 NOP 1 +SBC HL,HL 62ED 2 NOP 1 +SBC HL,SP 72ED 2 NOP 1 +SBC A,* DE 2 NOP 1 +SCF "" 37 1 NOP 1 + +SET *,(HL) C6CB 2 ZBIT 1 +SET *,(IX*) CBDD 4 ZBIT 1 0 C600 +SET *,(IY*) CBFD 4 ZBIT 1 0 C600 +SET *,A C7CB 2 ZBIT 1 +SET *,B C0CB 2 ZBIT 1 +SET *,C C1CB 2 ZBIT 1 +SET *,D C2CB 2 ZBIT 1 +SET *,E C3CB 2 ZBIT 1 +SET *,H C4CB 2 ZBIT 1 +SET *,L C5CB 2 ZBIT 1 + +SLA (HL) 26CB 2 NOP 1 +SLA (IX*) CBDD 4 ZIX 1 0 2600 +SLA (IY*) CBFD 4 ZIX 1 0 2600 +SLA A 27CB 2 NOP 1 +SLA B 20CB 2 NOP 1 +SLA C 21CB 2 NOP 1 +SLA D 22CB 2 NOP 1 +SLA E 23CB 2 NOP 1 +SLA H 24CB 2 NOP 1 +SLA L 25CB 2 NOP 1 + +SLP "" 76ED 2 NOP 2 + +SRA (HL) 2ECB 2 NOP 1 +SRA (IX*) CBDD 4 ZIX 1 0 2E00 +SRA (IY*) CBFD 4 ZIX 1 0 2E00 +SRA A 2FCB 2 NOP 1 +SRA B 28CB 2 NOP 1 +SRA C 29CB 2 NOP 1 +SRA D 2ACB 2 NOP 1 +SRA E 2BCB 2 NOP 1 +SRA H 2CCB 2 NOP 1 +SRA L 2DCB 2 NOP 1 + +SRL (HL) 3ECB 2 NOP 1 +SRL (IX*) CBDD 4 ZIX 1 0 3E00 +SRL (IY*) CBFD 4 ZIX 1 0 3E00 +SRL A 3FCB 2 NOP 1 +SRL B 38CB 2 NOP 1 +SRL C 39CB 2 NOP 1 +SRL D 3ACB 2 NOP 1 +SRL E 3BCB 2 NOP 1 +SRL H 3CCB 2 NOP 1 +SRL L 3DCB 2 NOP 1 + +SUB (HL) 96 1 NOP 1 +SUB (IX*) 96DD 3 ZIX 1 +SUB (IY*) 96FD 3 ZIX 1 +SUB A 97 1 NOP 1 +SUB B 90 1 NOP 1 +SUB C 91 1 NOP 1 +SUB D 92 1 NOP 1 +SUB E 93 1 NOP 1 +SUB H 94 1 NOP 1 +SUB L 95 1 NOP 1 +SUB * D6 2 NOP 1 + +TST A 3CED 2 NOP 2 +TST B 04ED 2 NOP 2 +TST C 0CED 2 NOP 2 +TST D 14ED 2 NOP 2 +TST E 1CED 2 NOP 2 +TST H 24ED 2 NOP 2 +TST L 2CED 2 NOP 2 +TST (HL) 34ED 2 NOP 2 +TST * 64ED 3 NOP 2 + +TSTIO * 74ED 3 NOP 2 + +XOR (HL) AE 1 NOP 1 +XOR (IX*) AEDD 3 ZIX 1 +XOR (IY*) AEFD 3 ZIX 1 +XOR A AF 1 NOP 1 +XOR B A8 1 NOP 1 +XOR C A9 1 NOP 1 +XOR D AA 1 NOP 1 +XOR E AB 1 NOP 1 +XOR H AC 1 NOP 1 +XOR L AD 1 NOP 1 +XOR * EE 2 NOP 1 diff --git a/branches/s100/Applegate/zapple.asm b/branches/s100/Applegate/zapple.asm new file mode 100644 index 00000000..3b522a72 --- /dev/null +++ b/branches/s100/Applegate/zapple.asm @@ -0,0 +1,80 @@ +; +;================================================================================================== +; WRAPPER FOR ZAPPLE MONITOR FOR N8VEM PROJECT +; WAYNE WARTHEN - 2012-11-26 +;================================================================================================== +; +; THE FOLLOWING MACROS DO THE HEAVY LIFTING TO MAKE THE ZAPPLE SOURCE +; COMPATIBLE WITH TASM +; +#DEFINE EQU .EQU +#DEFINE NAME \; +#DEFINE PAGE .PAGE +#DEFINE CSEG .CSEG +#DEFINE DSEG .DSEG +#DEFINE ORG .ORG +#DEFINE END .END +#DEFINE IF .IF +#DEFINE ELSE .ELSE +#DEFINE ENDIF .ENDIF +#DEFINE DEFB .DB +#DEFINE DB .DB +#DEFINE DEFW .DW +#DEFINE DW .DW +#DEFINE . _ +#DEFINE TITLE .TITLE +#DEFINE EXT \; +#DEFINE NOT ~ +; +#ADDINSTR IN A,* DB 2 NOP 1 +#ADDINSTR OUT *,A D3 2 NOP 1 +#ADDINSTR ADD A 87 1 NOP 1 +#ADDINSTR ADD D 82 1 NOP 1 +#ADDINSTR ADD * C6 2 NOP 1 +#ADDINSTR ADC A 8F 1 NOP 1 +#ADDINSTR ADC * CE 2 NOP 1 +#ADDINSTR SBC H 9C 1 NOP 1 +; +; +; +COLOC .EQU 0 +LNLOC .EQU 0 +LULOC .EQU 0 +PTPL .EQU 0 +PULOC .EQU 0 +CSLOC .EQU 0 +CILOC .EQU 0 +RPTPL .EQU 0 +RULOC .EQU 0 +; +; 16C550 SERIAL LINE UART +; +SIO_BASE .EQU 90H +SIO_RBR .EQU SIO_BASE + 0 ; DLAB=0: RCVR BUFFER REG (READ ONLY) +SIO_THR .EQU SIO_BASE + 0 ; DLAB=0: XMIT HOLDING REG (WRITE ONLY) +SIO_IER .EQU SIO_BASE + 1 ; DLAB=0: INT ENABLE REG +SIO_IIR .EQU SIO_BASE + 2 ; INT IDENT REGISTER (READ ONLY) +SIO_FCR .EQU SIO_BASE + 2 ; FIFO CONTROL REG (WRITE ONLY) +SIO_LCR .EQU SIO_BASE + 3 ; LINE CONTROL REG +SIO_MCR .EQU SIO_BASE + 4 ; MODEM CONTROL REG +SIO_LSR .EQU SIO_BASE + 5 ; LINE STATUS REG +SIO_MSR .EQU SIO_BASE + 6 ; MODEM STATUS REG +SIO_SCR .EQU SIO_BASE + 7 ; SCRATCH REGISTER +SIO_DLL .EQU SIO_BASE + 0 ; DLAB=1: DIVISOR LATCH (LS) +SIO_DLM .EQU SIO_BASE + 1 ; DLAB=1: DIVISOR LATCH (MS) +; +BAUDRATE .EQU 38400 +UART_DIV .EQU (1843200 / (16 * BAUDRATE)) +; +; +; +BASE .EQU $6000 + +; +; NOW INCLUDE THE MAIN SOURCE +; +#INCLUDE "zapple.z80" +; + .FILL $7000 - $ +; + .END diff --git a/branches/s100/Applegate/zapple.z80 b/branches/s100/Applegate/zapple.z80 new file mode 100644 index 00000000..2505962c --- /dev/null +++ b/branches/s100/Applegate/zapple.z80 @@ -0,0 +1,1940 @@ +; << ZAPPLE 2-K MONITOR SYSTEM >> +; by +; +; TECHNICAL DESIGN LABS, INC. +; RESEARCH PARK +; PRINCETON, NEW JERSEY 08540 +; +; COPYRIGHT JULY 1976 BY TDL INC. +; +; ASSEMBLED BY ROGER AMIDON +; +; +;BASE: EQU 0F000H ; WW: NOW DEFINED IN WRAPPER +USER: EQU BASE+800H +; + TITLE +; +RST7: EQU 38H ;RST 7 (LOCATION FOR TRAP) +;WW IOBYT: EQU 76H ;R/W PORT FOR TEMP. STORAGE +IOBYT: EQU SIO_SCR ; WW - USING UART SPR FOR TEMP. STORAGE +;WW SENSE: EQU 7AH ;PORT FOR INITIAL I/O CMNFIGURATIOL (IN) +;WW SWITCH: EQU 0FFH ;FRONT PANEL SENSE SWITCHES +;WW RCP: EQU 7AH ;READER CONTROL PORT (OUT) +;WW NN: EQU 0F8H ;"I" REGISTER INITIAL VALUE +; +; EXTERNAL EQUATES +; + NAME ZAPPLE + EXT COLOC + EXT LNLOC + EXT LULOC + EXT PTPL + EXT PULOC + EXT CSLOC + EXT CILOC + EXT RPTPL + EXT RULOC +; +; +; +; +;-TELEPRINTER +; +; WW: VALUES BELOW MODIFIED FOR N8VEM STD UART PORTS +TTI: EQU SIO_RBR ;DATA IN PORT +TTO: EQU SIO_THR ;DATA OUT PORT +TTS: EQU SIO_LSR ;STATUS PORT (IN) +TTYDA: EQU $01 ;DATA AVAILABLE MASK BIT +TTYBE: EQU $20 ;XMTR BUFFER EMPTY MASK +; +;-C.R.T. SYSTEM +; +CRTI: EQU 51H ;DATA PORT (IN) +CRTS: EQU 50H ;STATUS PORT (IN) +CRTO: EQU 51H ;DATA PORT (OUT) +CRTDA: EQU 40H ;DATA AVAILABLE MASK +CRTBE: EQU 80H ;XMTR BUFFER EMPTY MASK +; +;-CASSETTE SYSTEM +; +RCSD: EQU 61H ;DATA IN PORT +RCSS: EQU 60H ;STATUS PORT (IN) +RCSDA: EQU 40H ;DATA AVAILABLE MASK +PCASO: EQU 61H ;DATA PORT (OUT) +PCASS: EQU 60H ;CONTROL PORT (OUT) +PCSBE: EQU 80H ;XMTR BUFFER EMPTY MASK +; +; +; +FALSE: EQU 0 ;ISN'T SO +TRUE: EQU NOT FALSE ;IT IS SO +CR: EQU 0DH ;ASCII CARRIAGE RETURN +LF: EQU 0AH ;ASCII LINE FEED +BELL: EQU 7 ;DING +RUB: EQU 0FFH ;RUB OUT +FIL: EQU 00 ;FILL CHARACTER AFTER CRLF +MAX: EQU 7 ;NUMBER OF QUES IN EOF +; +; +; +CMSK: EQU 11111100B ;CONSOLE DEVICE +RMSK: EQU 11110011B ;STORAGE DEVICE (IN) +PMSK: EQU 11001111B ;STORAGE DEVICE (OUT) +LMSK: EQU 00111111B ;LIST DEVICE +; +; +;-CONSOLE CONFIGURATION +CTTY: EQU 0 ;TELEPRINTER +CCRT: EQU 1 ;C.R.T. +BATCH: EQU 2 ;READER FOR INPUT, LIST FOR OUTPUT +CUSE: EQU 3 ;USER DEFINED +; +;-STORAGE INPUT CONFIGURATION +RTTY: EQU 0 ;TELEPRINTER READER +RPTR: EQU 4 ;HIGH-SPEED RDR (EXTERNAL ROUTINE) +RCAS: EQU 8 ;CASSETTE +RUSER: EQU 0CH ;USER DEFINED +; +;-STORAGE OUTPUT CONFIGURATION +PTTY: EQU 0 ;TELEPRINTER PUNCH +PPTP: EQU 10H ;HIGH-SPEED PUNCH (EXTERNAL ROUTINE) +PCAS: EQU 20H ;CASSETTE +PUSER: EQU 30H ;USER DEFINED +; +;-LIST DEVICE CONFIGURATION +LTTY: EQU 0 ;TELEPRINTER PRINTER +LCRT: EQU 40H ;C.R.T. SCREEN +LINE: EQU 80H ;LINE PRINTER (EXTERNAL ROUTINE) +LUSER: EQU 0C0H ;USER DEFINED +; +; +; VECTOR FOR USER DEFINED ROUTINES +; +;.LOC: USER +;CILOC: .BLKB 3 ;CONSOLE INPUT +;COLOC: .BLKB 3 ;CONSOLE OUTPUT +;RPTPL: .BLKB 3 ;HIGH-SPEED READER +;RULOC: .BLKB 3 ;USER DEFINED STORAGE (INPUT) +;PTPL: .BLKB 3 ;HIGH-SPEED PUNCH +;PULOC: .BLKB 3 ;USER DEFINED STORAGE (OUTPUT) +;LNLOC: .BLKB 3 ;LINE PRINTER +;LULOC: .BLKB 3 ;USER DEFINED PRINTER +;CSLOC: .BLKB 3 ;CONSOLE INPUT STATUS ROUTINE +;J: =. +; +; PROGRAM CODE BEGINS HERE +; +; + .ORG BASE ; WW: MODIFIED FOR TASM SYNTAX + JP BEGIN ;GO AROUND VECTORS +; +; +; +; THESE VECTORS MAY BE USED BY USER WRITTEN +; PROGRAMS TO SIMPLIFY THE HANDLING OF I/O +; FROM SYSTEM TO SYSTEM. WHATEVER THE CURRENT +; ASSIGNED DEVICE, THESE VECTORS WILL PERFORM +; THE REQUIRED I/O OPERATION, AND RETURN TO +; THE CALLING PROGRAM. (RET) +; +; THE REGISTER CONVENTION USED FOLLOWS- +; +; ANY INPUT OR OUTPUT DEVICE +; CHARACTER TO BE OUTPUT IN 'C' REGISTER. +; CHARACTER WILL BE IN 'A' REGISTER UPON +; RETURNING FROM AN INPUT OR OUTPUT. +; 'CSTS'- +; RETURN TRUE (0FFH IN 'A' REG.) IF THERE IS +; SOMETHING WAITING, AND ZERO (00) IF NOT. +; 'IOCHK'- +; RETURN WITH THE CURRENT I/O CONFIGURATION +; BYTE IN 'A' REGISTER. +; 'IOSET'- +; ALLOWS A PROGRAM TO DYNAMICALLY ALTER THE +; CURRENT I/O CONFIGURATION, AND REQUIRES +; THE NEW BYTE IN 'C' REGISTER. +; 'MEMCK'- +; RETURNS WITH THE HIGHEST ALLOWED USER +; MEMORY LOCATION. 'B'=HIGH BYTE, 'A'=LOW. +; 'TRAP'- +; THIS IS THE 'BREAKPOINT' ENTRY POINT, +; BUT MAY BE 'CALLED'. IT WILL SAVE +; THE MACHINE STATE. RETURN CAN BE MADE WITH +; A SIMPLE 'G[CR]' ON THE CONSOLE. +; + JP CI ;CONSOLE INPUT + JP RI ;READER INPUT + JP CO ;CONSOLE OUTPUT + JP PUO ;PUNCH OUTPUT + JP LO ;LIST OUTPUT + JP CSTS ;CONSOLE STATUS + JP IOCHK ;I/O CHECK + JP IOSET ;I/O SET + JP MEMCK ;MEMORY LIMIT CHECK +TRAP: JP RESTART ;BREAKPOINT +; +; ANNOUNCEMENT OF MONITOR NAME & VERSION +MSG: DB CR,LF,FIL,FIL,FIL + DB "Zapple V" ; WW: CHANGED TO DQUOTES + DB "1.1" ; WW: CHANGED TO DQUOTES +MSGL: EQU $-MSG +; +; LET US BEGIN +; +; +; *NOTE- THE CODE UP TO THE 'IN SENSE' MAY +; BE REPLACED BY ENOUGH CODE TO INITIALIZE +; AN ACIA OR SIO DEVICE. ADDITIONAL DEVICES +; MAY BE INITIALIZED USING THE 'Q' COMMAND. +; (OR STANDARD ROUTINES FOR INITILIZATION +; MAY BE LOADED & EXECUTED IN THE USER AREA). +; +BEGIN: ;WW LD A,NN ;FOR 'I' REG. IF NEEDED. + ;WW LD I,A + ;WW NOP ;SPARE BYTE + ;WW XOR A ;CLEAR READER CONTROL PORT + ;WW OUT RCP,A +; + ;WW IN A,SENSE ;INITIALIZE I/O CONFIGURARTION + LD A,$FF ; WW - INIT IOBYT TO $FF (NOTE IT IS INVERTED WHEN USED) + OUT IOBYT,A + LD SP,AHEAD-4 ;SET UP A FAKE STACK + JP MEMSIZ+1 ;GET MEMORY SIZE + DEFW AHEAD +AHEAD: LD SP,HL ;SET TRUE STACK + EX DE,HL + LD BC,ENDX-EXIT + LD HL,EXIT + LDIR ;MOVE TO RAM + EX DE,HL + LD BC,-5FH ;SET UP A USER'S STACK VALUE + ADD HL,BC + PUSH HL ;PRE-LOAD USER'S STACK VALUE + LD HL,0 ;INITIALIZE OTHER REGISTERS + LD B,10 ; (16 OF THEM) +STKIT: PUSH HL ; TO ZERO + DJNZ STKIT +HELLO: LD B,MSGL ;SAY HELLO TO THE FOLKS + CALL TOM ;OUTPUT SIGN-ON MSG +START: LD DE,START ;MAIN 'WORK' LOOP + PUSH DE ;SET UP A RETURN TO HERE + CALL CRLF + LD C,'>' + CALL CO +STARO: CALL TI ;GET A CONSOLE CHARACTER + AND 7FH ;IGNORE NULLS + JR Z,STARO ;GET ANOTHER + SUB 'A' ;QUALIFY THE CHARACTER + RET M ;Z + ADD A ;A*2 + LD B,0 + LD C,A ;POINT TO PLACE ON TABLE + LD HL,TBL ;POINT TO COMMAND TABLE + ADD HL,BC ;ADD IN DISPLACEMENT + LD E,(HL) + INC HL + LD D,(HL) + EX DE,HL ;D&E=ROUTINE ADDRESS + LD C,2 ;SET C UP + JP (HL) ;GO EXECUTE COMMAND +; +; +; +TBL: + DEFW ASSIGN ;A - ASSIGN I/O + DEFW BYE ;B - SYSTEM SHUT-DOWN + DEFW COMP ;C - COMPARE MEMORY VS. READER INPUT + DEFW DISP ;D - DISPLAY MEMORY ON CONS. IN HEX + DEFW EOF ;E - END OF FILE TAG FOR HEX DUMPS + DEFW FILL ;F - FILL MEMORY WITH A CONSTANT + DEFW GOTO ;G - GOTO [ADDR] <,>BREAKPOINTS (2) + DEFW HEXN ;H - HEX MATH. , + DEFW ERROR ;I * USER DEFINED, INSERT VECTOR +; J=J ;VECTOR ADDR + DEFW TEST ;J - NON-DESTRUCTIVE MEMORY TEST + DEFW ERROR ;K * USER DEFINED, INSERT VECTOR +; J=J+3 ;VECTOR ADDR + DEFW LOAD ;L - MOVE A BINARY FORMAT FILE + DEFW MOVE ;M - MOVE BLOCKS OF MEMORY + DEFW NULL ;N - PUNCH NULLS ON PUNCH DEVICE + DEFW ERROR ;O * USER DEFINED, INSERT VECTOR +; J=J+3 ;VECTOR ADDR + DEFW PUTA ;P - 'PUT' ASCII INTO MEMORY. + DEFW QUERY ;Q - QI(N)=DISP. N; QO(N,V)=OUT N,V + DEFW READ ;R - READ A HEX FILE (W/CHECKSUMS) + DEFW SUBS ;S - SUBSTITUTE &/OR EXAMINE MEMORY + DEFW TYPE ;T - TYPE MEMORY IN ASCII + DEFW UNLD ;U - MEMORY TO PUNCH (BINARY FORMAT) + DEFW VERIFY ;V - COMPARE MEMORY AGAINST MEMORY + DEFW WRITE ;W - MEMORY TO PUNCH (HEX FORMAT) + DEFW XAM ;X - EXAMINE & MODIFY CUP REGISTERS + DEFW WHERE ;Y - FIND SEQUENCE OF BYTES IN RAM + DEFW SIZE ;Z - ADDRESS OF LAST R/W LOCATION +; +; +; +; THIS ROUTINE CONTROLS THE CONFIGURATION +; OF THE VARIOUS I/O DRIVERS & DEVICES. THIS IS +; ACCOMPLISHED VIA A HARDWARE READ/WRITE PORT. +; THIS PORT IS INITIALIZED UPON SIGN-ON +; BY THE VALUE READ ON PORT 'SENSE'. IF MAY BE +; DYNAMICALLY MODIFIED THROUGH CONSOLE COMMANDS. +; +; THE VALUE ON THE 'IOBYT' PORT REPRESENTS THE +; CURRENT CONFIGURATION. IT IS STURCTURED THUSLY: +; +; 000000XX - WHERE XX REPRESENTS THE CURRENT CONSOLE +; 0000XX00 - WHERE XX REPRESENTS THE CURRENT READER +; 00XX0000 - WHERE XX REPRESENTS THE CURRENT PUNCH +; XX000000 - WHERE XX REPRESENTS THE CURRENT LISTER +; +; WHEN USING A MEMORY LOCATION FOR IOBYT, THE +; POLARITY IS REVERSED. FOR AN I/O PORT, +; WHEN XX = 11, THE DEVICE IS ALWAYS THE +; TELEPRINTER. WHEN XX = 00, THE DEVICE IS ALWAYS +; USER DEFINED. SEE OPERATOR'S MANUAL FOR FURTHER +; DETAILS. +; +ASSIGN: CALL TI ;GET DEVICE NAME + LD HL,LTBL ;POINT TO DEVICE TABLE + LD BC,400H ;4 DEVICES TO LOOK FOR + LD DE,5 ;4 DEV. + IDENT. +..A0: CP (HL) ;LOOK FOR MATCH + JR Z,..A1 + ADD HL,DE ;GO THRU TABLE + INC C ;KEEP TRACK OF DEVICE + DJNZ ..A0 + JR ..ERR ;WRONG IDENTIFIER +..A1: LD E,C ;SAVE DEVICE NUMBER +..A2: CALL TI ;SCAN PAST '=' + CP '=' + JR NZ,..A2 + CALL TI ;GET NEW ASSIGNMENT + LD BC,400H ;4 POSSIBLE ASSIGNMENTS +..A3: INC HL ;POINT TO ASSIGNMENT NAME + CP (HL) ;LOOK FOR PROPER MATCH + JR Z,..A4 ;MATCH FOUND + INC C ;KEEP TRACK OF ASSIGNMENT NMBR + DJNZ ..A3 +..ERR: JP ERROR ;NO MATCH, ERROR +..A4: LD A,3 ;SET UP A MASK + INC E +..A5: DEC E ;DEVICE IN E + JR Z,..A6 ;GOT IT + SLA C ;ELSE MOVE MASKS + SLA C + RLA + RLA ;A=DEVICE MASK + JR ..A5 +..A6: CPL ;INVERT FOR AND'ING + LD D,A ;SAVE IN D +..A7: CALL PCHK ;WAIT FOR [CR] + JR NC,..A7 + CALL IOCHK ;GET PRESENT CONFIGURATION + AND D ;MODIFY ONLY SELECTED DEVICE + OR C ;'OR' NEW BIT PATTERN + LD C,A ;NEW CONFIGURATION +; +; THIS ALLOWS USER PROGRAMS TO MODIFY +; THE I/O CONDIGURATIMN DYNAMICALLY +; DURING EXECUTION. +; +IOSET: LD A,C ;NEW I/O BYTE PASSED IN C REE + CPL ;WE SAVE THE INVERTED BYTE + OUT IOBYT,A ;IN AN I/O PORT LATCH + RET +; +; THIS RETURNS THE CURRENT I/O +; CONFIGURATION IN THEE A REG. +; +IOCHK: IN A,IOBYT ;GET SAVED VALUE + CPL ;AND INVERT IT AGAIN + XOR A ; WW + RET +; +; +; THIS ROUTINE IS USED AS A SIMPLE MEANS TO PREVENT +; UNAUTHORIZED SYSTEM OPERATION. THE SYSTEM LOCKS UP, +; MONITORING FOR A 'CONT.-SHIFT-N', AT WHICH TIME IT +; WILL SIGN-ON AGAIN. NO REGISTER ASSIGHNMENTS OR I/O +; CONFIGURATIONS WILL BE ALTERED. +; +BYE: CALL CRLF +..BY: CALL KI + CP 1EH ;CONTROL-SHIFT-N + JR NZ,..BY + POP DE ;REMOVE THE RETURN + JP HELLO ;AND SIGN-ON AGAIN +; +; THIS ALLOW ENTERING OF ASCII TEXT INTO MEMORY +; FROM THE CONSOLE DEVICE. THE PARITY BIT IS CLEARED, +; AND ALL WILL BE STORED EXCEPT THE BACK-ARROR [ ] +; WHICH DELETES THE PREVIOUS CHARACTER, AND +; CONTROL-D, WHICH RETURNS CONTROL TO THE MONITOR. +; THIS COMMAND, COMBINED WITH THE 'Y' COMMAND, +; PROVIDES A RUDIMENTARY TEXT PROCESSING ABILITY. +; +PUTA: CALL EXPR1 ;GET THE STARTING ADDR. + CALL CRLF + POP HL +...A1: CALL KI ;GET A CHARACTER + CP 4 ;CONTROL-D? (EOT) + JP Z,LFADR ;YES, STOP & PRINT + CP 5FH ;ERASE MISTAKE? + JR Z,...A3 ; YES. + LD (HL),A ;ELSE STORE IT IN MEMORY + LD C,A + INC HL +...A2: CALL CO ;ECHO ON CONSOLE + JR ...A1 +...A3: DEC HL + LD C,(HL) + JR ...A2 ;ECHO & CONTINUE +; +; THIS ROUTINE COMPARES THE READER INPUT +; DEVICE WITH THE MEMORY BLOCK SPECIFIED. +; IT TESTS ALL EIGHT BITS, AND ANY DISCREPENCIES +; WILL BE OUTPUT TO THE CONSOLE. THIS IS USEFUL +; WHEN USED WITH THE BINARY DUMP DORMAT TO BOTH +; VERIFY PROPER READING & STORAGE, OR TO DETECT +; PROGRAM CHANGES SINCE IT WAS LAST LOADED. +; +COMP: CALL EXLF ;GET START ' STOP ADDR. +..C: CALL RIFF ;GET A FULL READER BYTE + CP (HL) ;8 BIT COMPARE + CALL NZ,CERR ;CALL IF INVALID COMPARE + CALL HILOX ;SEE IF RANGE SATISFIED + JR ..C +; +; THIS SUBROUTINE IS USED TO DISPLAY THE +; CURRENT LOCATION OF THE 'M' REGISTER POINTER (HL) , +; AND THE VALUE AT THE LOCATION, AND THE CONTENTS +; OF THE ACCUMULATOR. USED BY TWO ROUTINES. +; +CERR: LD B,A ;SAVE ACC. + CALL HLSP ;DISPLAY H&L + LD A,(HL) + CALL LBYTE ;PRINT 'M' + CALL BLK ;SPACE OVER + LD A,B + CALL LBYTE ;PRINT ACC. + JP CRLF ;CRLF & RETURN +; +; THIS DISPLAYS THE CONTENTS OF MEMORY IN BASE HEX +; WITH THE STARTING LOCATION ON EACH LINE. (BETWEEN +; THE TWO PARAMEETERS GIVEN). 16 BYTES PER LINE MAX. +; +DISP: CALL EXLF ;GET DISPLAY RANGE +..D0: CALL LFADR ;CRLF & PRINT ADDR. +..D1: CALL BLK ;SPACE OVER + LD A,(HL) + CALL LBYTE + CALL HILOX ;RANGE CHECK + LD A,L + AND 0FH ;SEE IF TIME TO CRLF + JR NZ,..D1 + JR ..D0 +; +; THIS OUTPUTS THE END OF FILE (EOF) PATTERN +; FOR THE CHECKSUM LOADER. IT IS USED AFTER +; PUNCHING A BLOCK OF MEMORY WITH THE 'W' +; COMMAND. AN ADDRESS PARAMETER MAY BE GIVEN, +; AND UPON READING, THIS ADDRESS WILL BE +; AUTOMATICALLY PLACED INT THE 'P' COUNTER. THE +; PROGRAM CAN THEN BE RUN WITH A SIMPLE 'G[CR]' +; COMMAND. +; +EOF: CALL EXPR1 ;GET OPTIONAL ADDR. + CALL PEOL ;CRLF TO PUNCH + LD C,':' ;FILE MARKER CUE + CALL PUO + XOR A ;ZERO LENGTH + CALL PBYTE + POP HL + CALL PADR ;PUNCH OPTIONAL ADDR. + LD HL,0 ;FILE TYPE=0 + CALL PADR ;PUNCH IT + JP NULL ;TRAILER & RETURN +; +; THIS COMMAND WILL FILL A BLOCK OF MEMORY +; WITH A VALUE. IE; FO,1FFF,0 FILLS FROM +; <1> TO <2> WITH THE BYTE <3>. HANDY FOR +; INITIALIZING A BLOCK TO A SPECIFIC VALUE, OR +; MEMORY TO A CONSTANT VALUE BEFOR LOADING +; A PROGRAM. (ZERO IS ESPECIALLY USEFUL.) +; +FILL: CALL EXPR3 ;GET 3 PARAMEETERS +..F: LD (HL),C ;STORE THE BYTE + CALL HILO + JR NC,..F + POP DE ;RESTORE STACK + JP START ; IN CASE OF ACCIDENTS +; +; THIS COMMAND ALLOWS EXECUTION OF ANOTHER +; PROGRAM WHILE RETAINING SOME MONITOR +; CONTROL BY SETTING BREAKPOINTS. +; +; TO SIMPLY EXECUTE, TYPE 'G[CR]'. TO SET +; A BREAKPOINT TRAP, ADD THE ADDRESS (ES) TO THE +; COMMAND. IE; G,[CR]. TWO BREAKPOINTS +; ARE ALLOWED, ENOUGH TO SATISFY MOST REQUIREMENTS. +; ONCE A BREAKPOINT HAS BEEN REACHED, THE +; REGISTERS MAY BE EXAMINED OR MODIFIED. THE +; PROGRAM CAN THEN BE CONTINUED BY TYPING ONLY +; A 'G[CR]'. OR ANOTHER BREAKPOINT COULD BE +; IMPLEMENTED AT THAT TIME BY TYPING 'G,[CR]'. +; +; *NOTE: THIS IS SOFTWARE CONTROLLED, AND THE +; BREAKPOINT MUST OCCUR ON AN INSTRUCTION +; BYTE. +; +GOTO: CALL PCHK ;GET A POSSIBLE ADDRESS + JR C,..G3 ;CR ENTERED + JR Z,..G0 ;DELIMETER ENTERED + CALL EXF ;GET ONE EXPRESSION + POP DE + LD HL,PLOC ;PLACE ADDRESS IN 'P' LOCATION + ADD HL,SP + LD (HL),D ;HIGH BYTE + DEC HL + LD (HL),E ;LOW BYTE + LD A,B + CP CR ;SEE IF LAST CHARACTER WAS A CR + JR Z,..G3 ;YES, LEAVE +..G0: LD D,2 ;TWO BREAKPOINTS MAX + LD HL,TLOC ;POINT TO TRAP STORAGE + ADD HL,SP +..G1: PUSH HL ;SAVE STORAGE POINTER + CALL EXPR1 ;GET A TRAP ADDRESS + LD E,B ;SAVE DELIMITER + POP BC ;TRAP ADDR. + POP HL ;STORAGE + LD A,B ;LOOK AT TRAP ADDR + OR C + JR Z,..G2 ;DON'T SET A TRAP AT 0 + LD (HL),C ;SAVE BKPT ADDR + INC HL + LD (HL),B + INC HL + LD A,(BC) ;PICK UP INST. BYTE + LD (HL),A ;SAVE THAT TOO + INC HL + LD A,0FFH ;RST 7 + LD (BC),A ;SOFTWARE INTERUPT +..G2: LD A,E ;LOOK AT DEELIMITER + CP CR + JR Z,..G2A + DEC D ;COUNT BKPTS + JR NZ,..G1 ;GET ONE MORE +..G2A: LD A,0C3H ;SET UP JUMP INSTRUCTION + LD (RST7),A ; AT RESTART TRAP LOC. + LD HL,TRAP ; TO MONITOR VECTOR + LD (RST7+1),HL +..G3: CALL CRLF + POP DE ;CLEAR SYSTEM RETURN + LD HL,22 ;FIND 'EXIT' ROUTINE + ADD HL,SP ;UP IN STACK + JP (HL) ;GO SOMPLACE +; +; THIS IS A 'QUICKIE' MEMORY TEST TO SPOT +; HARD MEMORY FAILURES, OR ACCIDENTLY +; PROTECTED MEEMORY LOCATIONS. IT IS NOT +; MEANT TO BE THE DEFINITIVE MEMORY DIAGNOSTIC. +; IT IS, HOWEVER, NON-DESTRUCTIVEE. ERRORS ARE +; PRINTED ON THE CONSOLE AS FOLLOWS- +; 00000100 WHERE <1> IS THE BAD BIT. +; BIT LOCATION OF THE FAILURE IS EASILY +; DETERMINED. NON-R/W MEMORY WILL RETURN +; WITH- 11111111 +; +TEST: CALL EXLF ;GET TWO PARAMS +...T1: LD A,(HL) ;READ A BYTE + LD B,A ;SAVE IN B REG. + CPL + LD (HL),A ;READ/COMPLIMENT/WRITE + XOR (HL) ; & COMPARE + JR Z,...T2 ;SKIP IF ZERO (OK) + PUSH DE ;SAVE END POINTER + LD D,B ;SAVE BYTE + LD E,A ;SET-UP TO DISPLAY + CALL HLSP ;PRINT BAD ADDR + CALL BITS ;PRINT BAD BIT LOC. + CALL CRLF + LD B,D ;RESTORE BYTE + POP DE ;RESTORE DE +...T2: LD (HL),B ;REPLACE BYTE + CALL HILOX ;RANGE TEST + JR ...T1 +; +; THIS COMMAND MOVES MASS AMOUNTS OF MEMORY +; FROM <1> THRU <2> TO THE ADDRESS STARTING +; AT <3>. THIS ROUTINE SHOULD BE USED WITH +; SOME CAUTION, AS IT COULD SMASH MEMORY IF +; CARLESSLY IMPLEMENTED. +; +; M<1>,<2>,<3> +; +MOVE: CALL EXPR3 ;GET 3 PARAMTERS +..M: LD A,(HL) ;PICK UP + LD (BC),A ;PUT DOWN + INC BC ;MOVE UP + CALL HILOX ;CHECK IF DONE + JR ..M +; +; THIS COMMAND READS THE CHECK-SUMMED HEX FILES +; FOR BOTH THE NORMAL INTEL FORMAT AND THE TDL +; RELOCATING FORMAT. ON BOTH FILES, A 'BIAS' MAY +; BE ADDED, WHICH WILL CAUSE THE OBJECT CODE TO +; BE PLACED IN A LOCATION OTHER THAN ITS +; INTENDED EXECUTION LOCATION. THE BIAS IS ADDED +; TO WHAT WOULD HAVE BEEN THE NORMAL LOADING +; LOCATION, AND WILL WRAP AROUND TO ENABLE +; LOADING ANY PROGRAM ANYWHERE IN MEMORY. +; +; WHEN LOADING A RELOCATABLE FILE, AN ADDITIONAL +; PARAMETER MAY BE ADDED, WHICH REPRESENTS THE +; ACTUAL EXECUTION ADDRESS DESIRED. THIS ALSO MAY +; BE ANY LOCATION IN MEMORY. +; +; EXAMPLES: +; +; R[CR] =0 BIAS, 0 EXECUTION ADDR. +; R[CR] =<1>BIAS, 0 EXECUTION ADDR. +; R,[CR] =0 BIAS, <1> EXECUTION ADDR. +; R,[CR] =<1>BIAS, <2>EXECUTION ADDR. +; +READ: CALL EXPR1 ;GET BIAS, IF ANY + LD A,B ;LOOK AT DELIMITER + SUB CR ;ALL DONE? + LD B,A ;SET UP RELOCATION OF 0 + LD C,A ; IF CR ENTERED + POP DE ;BIAS AMOUNT + JR Z,..R0 ;CR ENTERED + CALL EXPR1 ;GET RELOCATION + POP BC ;ACTUAL RELOCATION VALUE +..R0: EX DE,HL + EXX ;HL'=BIAS, BC'=RELOCATION + CALL CRLF +LOD0: CALL RIX ;GET A CHARACTER + SUB ':' ;ABSOLUTE FILE CUE? + LD B,A ;SAVE CUE CLUE + AND 0FEH ;KILL BIT 0 + JR NZ,LOD0 ; NO, KEEP LOOKING + LD D,A ;ZERO CHECKSUM + CALL SBYTE ;GET FILE LENGTH + LD E,A ;SAVE IN E REG. + CALL SBYTE ;GET LOAD MSB + PUSH AF ;SAVE IT + CALL SBYTE ;GET LOAD LSB + EXX ;CHANGE GEARS + POP DE ;RECOVER MSB + LD E,A ;FULL LOAD ADDR + PUSH BC ;BC'=RELOCATION + PUSH DE ;DE'=LOAD ADDR + PUSH HL ;HL'=BIAS + ADD HL,DE ; BIAS+LOAD + EX (SP),HL ;RESTORE HL' + POP IX ; X=BIAS+LOAD + EXX ;DOWNSHIFT + POP HL ;HL=LOAD ADDR + CALL SBYTE ;GET FILE TYPE + DEC A ;1=REL. FILE, 0=ABS. + LD A,B ;SAVE CUE BIT + POP BC ;BC=RELOCATION + JR NZ,..A ;ABSOLUTE FILE + ADD HL,BC ;ELSE RELMCATE + ADD IX,BC ;BOTH X & HL +..A: INC E ;TEST LENGHT + DEC E ;0=DONE + JR Z,DONE + DEC A ;TEST CUE + JR Z,LODR ;RELATIVE +..LI: CALL SBYTE ;NEXT + CALL STORE ;STORE IT + JR NZ,..L1 ;MORE COMING +LOD4: CALL SBYTE ;GET CHECKSUM + JR Z,LOD0 ;GOOD CHECKSUM +ERR3: PUSH IX + POP HL ;TRANSFER + CALL LADR ;PRINT CURRENT LOAD ADDR +ERR2: JP ERROR ;ABORT +DONE: LD A,H ;DON'T MODIFY IF ZERO + OR L + RET Z + EX DE,HL ;ELSE STORE IN PC + LD HL,PLOC + ADD HL,SP + LD (HL),D ;IN STACK AREA + DEC HL + LD (HL),E + RET +LODR: LD L,1 ;SET-UP BIT COUNTER +..L1: CALL LODCB ;GET THE BIT + JR C,..L3 ;DOUBLE BIT +..L5: CALL STORE ;WRITE IT + JR NZ,..L1 + JR LOD4 ;TEST CHECKSUM +..L3: LD C,A ;SAVE LOW BYTE + CALL LODCB ;NEXT CONTROL BIT + LD B,A ;SAVE HIGH BYTE + EXX + PUSH BC ;GET RELOCATION + EXX + EX (SP),HL ;INTO HL + ADD HL,BC ;RELOCATE + LD A,L ;LOW BYTE + CALL STORE ;STORE IT + LD A,H ;HIGH BYTE + POP HL ;RESTORE HL + JR ..L5 ;DO THIS AGAIN +LODCB: DEC L ;COUNT BITS + JR NZ,..LC1 ;MORE LEFT + CALL SBYTE ;GET NEXT + DEC E ;COUNT BYTES + LD H,A ;SAVE THE BITS + LD L,8 ;8 BITS/BYTE +..LC1: CALL SBYTE ;GET A DATA BYTE + SLA H ;TEST NEXT BIT + RET +SBYTE: PUSH BC ;PRESERVE BC + CALL RIBBLE ;GET A CONVERTED ASCII CHAR. + RLCA + RLCA + RLCA + RLCA ;MOV IT TO HIGH NIBBLE + LD C,A ;SAVE IT + CALL RIBBLE ;GET OTHER HALF + OR C ;MAKE WHOLE + LD C,A ;SAVE AGAIN IN C + ADD D ;UPDATE CHECKSUM + LD D,A ;NEW CHECKKSUM + LD A,C ;CONVERTED BYTE + POP BC + RET +STORE: LD (IX),A ;WRITE TO MEMORY + CP (IX) ;VALID WRITE? + JR NZ,ERR3 ; NO. + INC IX ;ADVANCE POINTER + DEC E ;COUNT DOWN + RET +; +; THIS ROUTINE ALLOWS BOTH INSPECTION OF & +; MODIFICATION OF MEMORY ON A BYTE BY BYTE +; BASIS. IT TAKES ONE ADDRESS PARAMETER, +; FOLLOWED BY A SPACE. THE DATA AT THAT +; LOCATION WILL BE DISPLAYED. IF IT IS +; DESIRED TO CHANGE IT, THE VALUE IS THEN +; ENTERED. A FOLLOWING SPACE WILL DISPLAY +; THE NEXT BYTE. A CARRIAGE RETURN [CR] +; WILL TERMINATE THE COMMAND. THE SYSTEM +; ADDS A CRLF AT LOCATIONS ENDING WITH EITHER +; XXX0 OR XXX8 TO AID IN DTERMINING THE +; PRESENT ADDRESS, IT IS PRINTED AFTER +; EACH CRLF. A BACKARROW [ ] WILL BACK +; UP THE POINTER AND DISPLAY THE +; PREVIOUS LOCATION. +; +SUBS: CALL EXPR1 ;GET STARTING ADDR + POP HL +..S0: LD A,(HL) + CALL LBYTE ;DISPLAY THE BYTE + CALL COPCK ;MODIFY? + RET C ; NO, ALL DONE + JR Z,..S1 ;DON'T MODIFY + CP 05FH ;BACKUP? + JR Z,..S2 + PUSH HL ;SAVE POINTER + CALL EXF ;GET NEW VALUE + POP DE ;VALUE IN EE + POP HL + LD (HL),E ;MODIFY + LD A,B ;TEST DELIMITER + CP CR + RET Z ;DONE +..S1: INC HL +..S3: LD A,L ;SEE IF TIME TO CRLF + AND 7 + CALL Z,LFADR ;TIME TO CRLF + JR ..S0 +..S2: DEC HL ;DECREMENT POINTER + JR ..S3 ;AND PRINT DATA THERE. +; +; THIS ROUTINE TRANSLATES THE DATA IN +; MEMORY TO AN ASCII FORMAT. ALL NON- +; PRINTING CHARACTERS AREE CONVERTED TO +; PERIODS. [.] +; THERE ARE 64 CHARACTERS PER LINE. +; +TYPE: CALL EXLF ;DISPLAY RANGE +..T0: CALL LFADR ;DISPLAY ADDRESS + LD B,64 ;CHARACTERS PER LINE +..T1: LD A,(HL) + AND 7FH ;KILL PARITY BIT + CP ' ' ;RANGE TEST + JR NC,..T3 ;=>SPACEE +..T2: LD A,'.' ;REPLACE NON-PRINTING +..T3: CP 07CH ;ABOVE LOWER CASE z + JR NC,..T2 + LD C,A ;SEND IT + CALL CO + CALL HILOX ;MORE TO GO? + DJNZ ..T1 ;SEE FI TIME TO CRLF + JR ..T0 ;YES +; +; THIS IS A HEXADECIMAL SEARCH ROUTINE. IT +; TAKES NO ADDRESS PARAMETERS. AS MANY +; BYTES MAY BE ENTERED, SEPERATED BY A COMMA, +; AS DESIRED. THE MAXIMUM IS 255, BUT 3-4 IS +; TYPICAL, AND MORE THAN 12 WOULD BE UNUSUAL. +; THE ENTIRE MEMORY IS SEARCHED STARTING +; FROM ZERO, AND ALL STARTING ADDRESSES OF EACH +; OCCURANCE OF THE SEARCH STRING ARE PRINTED +; ON THE CONSOLE DEVICE. +; +WHERE: LD D,0 ;COUNT SEARCH STRING +...W0: CALL EXPR1 ;GET ONE BYTE + POP HL ;PICK IT UP + LD H,L ;STICK IN HIGH BYTE + PUSH HL ;PUT IT IN STACK + INC SP ;ADJUST STACK + INC D ;COUNT UP + LD A,B ;TEST DELIMITER + SUB CR + JR NZ,...W0 ;MORE TO GO + LD B,A ;CHEAP ZEROS + LD C,A + LD H,A + LD L,D ;GET BYTE COUNT IN L + DEC L ;-1 + ADD HL,SP ;BYTES STORED IN STACKK + PUSH HL + PUSH BC +FINDC: PUSH BC ;SAVE THAT POINTER + CALL CRLF + POP BC ;RESTORE +FIND: POP HL ;HL=SEARCH ADDR + POP IX ;X=SEARCH BYTE POINTER + LD E,D ;RESET COUNTER + LD A,(IX) ;GET THE FIRST SEARCH BYTE + CPIR ;COMPARE, INCR., & REPEAT + JP PO,DONE ;ODD PARITY=DONE + PUSH IX ;SAVE POINTERS + PUSH HL +FOUND: DEC E + JR Z,TELL ;FOUND ALL + LD A,(IX-1) ;LOOK AT NEXT MATCH + CP (HL) ;TEST NEXT + JR NZ,FIND ;NO MATCH + INC HL ;BUMP POINTER + DEC IX + JR FOUND ;TEST NEXT MATCH +TELL: POP HL + PUSH HL + DEC HL + PUSH BC ;SAVE SEARCH COUNT LIMIT + CALL LADR ;TELL CONSOLE + POP BC ;RESTORE + JR FINDC +DONE2: INC SP + DEC E ;RESET STACK + JR NZ,DONE2 + RET +; +; THIS ROUTINE DUMPS MEMORY IN THE STANDARD +; INTEL HEX-FILE FORMAT. A START & END +; PARAMETER IS REQUIRED. AT THE CONCLUSION +; OF THE DUMP, AND "END OF FILE" SHOULD BE +; GENERATED WITH THE "E" COMMAND. +; +WRITE: CALL EXLF ;GET TWO PARAMETERS + CALL WAIT ;PAUSE IF TTY CONFIGURATION +..W0: CALL PEOL ;CRLF TO PUNCH + LD BC,':' ;START OF FILE + CALL PUO ;PUNCH IT + PUSH DE ;SAVE + PUSH HL ;POINTERS +..W1: INC B ;CALCULATE FILE LENGTH + CALL HILO + JR C,..W4 ;SHORT FILE + LD A,24 ;24 BYTES PER FILE + SUB B ;ENOUGH YET? + JR NZ,..W1 ; NO. + POP HL ;GET START ADDR BACK. + CALL ..W2 ;SEND THE BLOCK + POP DE ;RESTORE END OF FILE POINTER + JR ..W0 ;KEEP GOING +..W2: LD D,A ;INITIALIZE CHECKSUM + LD A,B ;FILE LENGTH + CALL PBYTE ;PUNCH IT + CALL PADR ;PUNCH ADDRESS + XOR A ;FILE TYPE=0 + CALL PBYTE ;PUNCH IT +..W3: LD A,(HL) ;GET A DATA BYTE + CALL PBYTE ;PUNCH IT + INC HL ;POINT TO NEXT BYTE + DJNZ ..W3 ;DECREMENT FILE COUNT + XOR A + SUB D ;CALCULATE CHECKSUM + JP PBYTE ;PUNCH IT, RETURN +..W4: POP HL ;CLEAR STACK + POP DE ; OF POINTERS + XOR A ;SET UP A + JR ..W2 ;FINISH UP & RETURN +; +; +; THIS ROUTINE ALLOWS DISPLAYING THE +; USER'S CPU REGISTERS. THEY ALSO MAY BE +; USING THE REGISTER NAME AFTER TYPEINT THE "X". +; I.E. XA 00- +; THE REGISTER MAY BE SKIPPED OVER, OR MODIFIED, +; SIMILARLY TO THE "S" COMMAND. +; +; TO DISPLAY THE "NORMAL" SYSTEM STATUS, +; SIMPLY TYPE "X[CR]". TO DISPLAY THE +; ADDISTIONAL Z-80 REGISTERS, FOLLOW +; THE "X" WITH AN APOSTROPHE. I.E. "X'[CR]", +; OR TO EXAMINE A SINGLE "PRIME" REGISTER, +; TYPE THE REGISTER IDENTIFIER AFTER THE +; APOSTROPHE. I.E. X'X 0000- +; +; THESE REGISTER VALUES ARE PLACED INTO THE CPU +; UPON EXECUTING ANY "GO" COMMAND. [G] +; +XAM: CALL TI + LD HL,ACTBL + CP CR ;FULL REG. DISPLAY + JR Z,..X6 + CP 2CH ;SEE IF PRIMES WANTED + JR NZ,..X0 + LD HL,PRMTB + CALL TI + CP CR ;FULL REG. DISPLAY + JR Z,..X6 +..X0: CP (HL) ;TEST FOR REGISTER NAME + JR Z,..X1 + BIT 7,(HL) ;SEE IF END OF TABLE + JP NZ,ERROR + INC HL + INC HL + JR ..X0 +..X1: CALL BLK +..X2: INC HL + LD A,(HL) + LD B,A ;SAVE FOR FLAGS + AND 3FH ;CLEAR FLAGS FOR BIAS + EX DE,HL + LD L,A ;DISPLACEMENT FROM STACK + LD H,0 + ADD HL,SP + EX DE,HL + INC HL + LD A,(DE) ;PICK UP REG. VALUE + CALL LBYTE ;PRINT IT + BIT 7,B + JR Z,..X3 + DEC DE + LD A,(DE) + CALL LBYTE +..X3: CALL COPCK ;MODIFY + RET C ;CR ENTERED, ALL DONE + JR Z,..X5 ;SKIP TO NEXT REG. + PUSH HL + PUSH BC + CALL EXF ;GET NEW VALUE + POP HL + POP AF + PUSH BC + PUSH AF + LD A,L + LD (DE),A + POP BC + BIT 7,B ;SEE IF 8 BIT OR 16 BIT REG. + JR Z,..X4 ;8 BIT + INC DE + LD A,H ;HIGH BYTE OF 16 BIT REG. + LD (DE),A +..X4: POP BC + POP HL + LD A,B ;TEST DELIMITER + CP CR + RET Z ;CR ENTRED, ALL DONE +..X5: BIT 7,(HL) ;SEE IF END OF TABLE + RET NZ ;RETURN IF SO + JR ..X2 +..X6: CALL CRLF +..X7: CALL BLK + LD A,(HL) + INC HL + OR A + RET M + LD C,A + CALL CO + LD C,'=' + CALL CO + LD A,(HL) + LD B,A ;SAVE FLAGS + AND 3FH ;CLEAR UP FOR OFFSET + INC HL + EX DE,HL + LD L,A + LD H,0 + ADD HL,SP + EX DE,HL + BIT 6,B ;TEST FOR SPECIAL "M" + JR NZ,..X9 ;PRINT OUT ACTUAL "M" + LD A,(DE) + CALL LBYTE ;PRINT REG. VALUE + BIT 7,B ;SINGLE OR DOUBLE? + JR Z,..X7 ;SINGLE. + DEC DE + LD A,(DE) +..X8: CALL LBYTE + JR ..X7 +..X9: PUSH HL ;SAVE HL + LD A,(DE) ;GET REG. POINTER + LD H,A ;HIGH BYTE + DEC DE + LD A,(DE) + LD L,A ;LOW BYTE + LD A,(HL) ;GET VALUE + POP HL ;RESTORE HL + JR ..X8 ;PRINT VALUE & CONTINUE +; +; THIS IS A MESSAGE OUTPUT ROUTINE. +; IT IS USED BY THE SIGN-ON AND THE CRLF. +; POINTER IS IN HL (WHEN ENTERED AT +; TOM1) AND LENGTH IN B REG. +; +TOM: LD HL,MSG +TOM1 LD C,(HL) ;GET A CHARACTER + INC HL ;MOVE POINTER + CALL CO ;OUTPUT IT + DJNZ TOM1 ;KEEP GOILG TILL B=0 + CALL CSTS ;SEE IF AN ABORT REQUEST + OR A ; WAITING + RET Z ;NO. +; +; SEE IF CONTROL-C IS WAITING +; ABMRT IF SO. +CCHK: CALL KI + CP 3 ;CONTROL-C? + RET NZ +; +; SYSTEM ERROR ROUTINE. THIS +; WILL RESTORE THE SYTEM AFTER +; A SYSTEM ERROR HAS BEEN TAKEN. +; THE I/O CONFIGURATION IS NOT +; AFFECTED. +; +ERROR: CALL MEMSIZ + LD DE,-22 ;STACK POINTER OFFSET + ADD HL,DE + LD SP,HL + LD C,'*' ;ANNOUNCE ERROR + CALL CO + JP START ;BACK TO WORK +; +; THIS GETS A READER CHARACTER, +; AND COMPARES IT WITH THE 'D' REG. +; IT ABORTS ON AND 'OUT-OF-DATA' +; CONDITION. +; +RIFF: CALL RI ;GET READER CHARACTER + JR C,ERROR ;ABORT ON CARRY + CP D ;TEST D + RET +; +; THIS ROUTINE WILL RETURN THE +; CURRENT VALUE OF THE HIGHEST +; READ/WRITE MEMORY LOCATION THAT +; IS AVAILABLE ON THE SYSTEM. +; IT WILL "SEARCH" FOR MEMORY +; STARTING AT THE BOTTOM OF MEMORY +; AND GO UPWARDS UNTIL NON-R/W MEMORY +; IS FOUND. +; +SIZE: CALL MEMSIZ ;GET THE VALUE + LD BC,ENDX-EXIT + ADD HL,BC ;ADJUST IT +; +; +; CRLF BEFORE HLSP ROUTINE +; +LFADR: CALL CRLF +; +; PRINT THE CURRENT VALUE OF H&L, +; AND A SPACE. +; +HLSP: CALL LADR +; +; PRINT A SPACE ON THE CONSOLE +; +BLK: LD C,' ' +; +; THIS IS THE MAIN CONSOLE +; OUTPUT ROUTINE +; +CO: CALL IOCHK + AND NOT CMSK + JR NZ,CO0 +; +; TELEPRINTER CONFIGURATION +; I/O DRIVER. +; +TTYOUT: IN A,TTS + AND TTYBE + JR Z,TTYOUT ; WW: NZ -> Z + LD A,C + OUT TTO,A + RET +CO0: DEC A ;CCRT? + JR NZ,CO1 ; NO. +; +; C.R.T. CONFIGURATION DRIVER. +; +CRTOUT: IN A,CRTS + AND CRTBE + JR NZ,CRTOUT + LD A,C + OUT CRTO,A + RET +; +CO1: DEC A ;BATCH + JP NZ,COLOC ; NO, MUST BE USER +; +; LIST OUTPUT DRIVER ROUTINE +; -A USER VECTORED ROUTINE, USED +; BYT THE ASSEMBLER, ETC. ALSO, +; WHEN THE ASSIGNED MODE IS "BATCH", +; THIS IS THE ROUTINE USED FOR THE +; MONITOR OUTPUT THAT WOULD NORMALLY +; GO TO THE "CONSOLE". +; +LO: CALL IOCHK + AND NOT LMSK + JR Z,TTYOUT + CP LCRT + JR Z,CRTOUT + CP LINE + JP Z,LNLOC ;EXTERNAL VECTOR + JP LULOC ;USER DEFINED VECTOR +; +; SEND CRLF TO PUNCH DEVICE +; +PEOL: LD C,CR + CALL PUO + LD C,LF +; +; PUNCH OUTPUT DRIVER ROUTINE +; +PUO: CALL IOCHK + AND NOT PMSK + JR Z,TTYOUT ;PUNCH=TELEPRINTER + CP PCAS ;CASSETTE? + JR NZ,PO1 ; NO. +; +PO0: IN A,PCASS + AND PCSBE + JR NZ,PO0 + LD A,C + OUT PCASO,A + RET +; +PO1: CP PPTP + JP Z,PTPL ;EXTERNAL VECTOR + JP PULOC ;USER VECTOR +; +; +; THIS IS A BINARY DUMP ROUTINE THAT MAY BE +; USED WITH BOTH PAPER-TAPE AND/OR CASSETTE +; SYSTEMS. IT PUNCHES A START-OF-FILE MARK +; AND THEN PUNCHES IN FULL 8-BITS DIRECTLY +; FROM MEMORY. IT IS FOLLOWED BY AN END-OF- +; FILE MARKER. THESE DUMPS MAY BE LOADED +; USING THE "L" COMMAND. THEY ARE USEFUL +; FOR FAST LOADING, AND MAY BE VERIFIED +; USING THE "C" (COMPARE) COMMAND. +; +; U,[CR] +; PUNCHES FROM THRU +; +UNLD: CALL EXLF ;GET TWO PARAMETERS + CALL WAIT ;PAUSE FOR PUNCH-ON (TTY) + CALL LEAD ;PUNCH LEADER + CALL MARK ;PUNCH FILE MARKER +..U: LD C,(HL) ;GET MEMORY BYTE + CALL PUO ;PUNCH IT + CALL HILO ;SEE IF DONE + JR NC,..U + CALL MARK ;PUNCH END OF FILE MARKER +; +; THIS PUNCHES NULLS (LEADER/TRAILER). +; IT RETURNS "QUIET" IN CASE THE PUNCH +; AND CONSOLE ARE THE SAME. +; +NULL: CALL LEAD ;PUNCH NULLS +; +; THIS ROUTINE WILL PAUSE FOR +; A KEYBOARD CHARACTER. IT IS +; USED AS A DELAY TO GIVE THE +; OPERATOR TIME TO TURN ON THE +; TELEPRINTER PUNCH BEFORE SENDING +; A HEX FILE OR BINARY FILE TO +; THE PUNCH. IT WIL SIMPLY +; RETURN IF THE PUNCH & CONSOLE +; ARE NO BOTH ASSIGNED TO THE +; DEFAULT. (TELEPRINTER) +; +WAIT: CALL IOCHK + AND ~CMSK | ~PMSK ; WW: WAS "AND NOT CMSK OR NOT PMSK" + RET NZ + JP STARO ;RETURN "QUIET" +; +; CONVERT HEX TO ASCII +; +CONV: AND 0FH ;LOW NIBBLE ONLY + ADD 90H + DAA + ADC 40H + DAA + LD C,A + RET +; +; GET TWO PARAMETERS, PLACE +; THEM IN DE & HL, AND THEN +; CRLF. +; +EXLF: CALL EXPR + POP DE + POP HL +; +; CONSOLE CARRIAGE RETURN & +; LINE FEED ROUTINE. +; +; THE NUMBER OF FILL CHARACTERS +; MAY BE ADJUSTED TO 0-3 BY THE +; VALUE PLACED IN THE B REG. MINIMUM +; VALUE FOR "B" IS TWO (2). MAXIMUM +; IS FIVE (5). +; +CRLF: PUSH HL ;SAVE HL + ;WW LD B,4 ;CRLF LENGTH (SET FOR 2 FILLS) + LD B,2 ; WW - CHANGE TO ZERO FILLS + CALL TOM ;SEND CRLF + POP HL + RET +; +; I +; TEST THE CURRENT CONSOLES +; KEYBOARD FOR A KEY-PRESS +; RETURN TRUE (0FFH IN A REG) +; IF THERE IS A CHARACTER +; WAITING IN THE UART. +; +CSTS: CALL IOCHK + AND NOT CMSK + JR NZ,CS0 + IN A,TTS + JR CS1 +CS0: DEC A ;CCRT + JR NZ,CS3 + IN A,CRTS +CS1: AND TTYDA + LD A,FALSE +CS2: RET Z ; WW: NZ -> Z + CPL + RET +CS3: DEC A ;BATCH + RET Z + JP CSLOC ;USED DEFINED FUNCTION +; +; GET THREE PARAMETERS AND +; CRLF. +; +EXPR3: INC C + CALL EXPR + CALL CRLF + POP BC + POP DE + POP HL + RET +; +; GET ONE PARAMETER. +; NO CRLF. +; +EXPR1: LD C,1 +; +; THIS IS THE MAIN "PARAMETER-GETTING" ROUTINE. +; THIS ROUTINE WILL ABORT ON A NON-HEX CHARACTER. +; IT TAKES THE MOST RECENTELY TYPED FOUR VALID +; HEX CHARACTERS, AND PLACES THEM UP ON THE STACK. +; (AS ONE 16 BIT VALUE, CONTAINED IN TWO +; 8-BIT BYTES.) IF A CARRIAGE RETURN IS ENTERED, +; IT WILL PLACE THE VALUE OF "0000" IN THE STACK. +; +EXPR: LD HL,0 ;INITIALIZE HL TO ZERO +EX0: CALL TI ;GET SOMETHING FROM CONSOLE +EX1: LD B,A ;SAVE IT + CALL NIBBLE ;CONVERT ASCII TO HEX + JR C,..EX2 ;ILLEGAL CHARACTER DECTECTED + ADD HL,HL ;MULTIPLY BY 16 + ADD HL,HL + ADD HL,HL + ADD HL,HL + OR L ;OR IN THE SINGLE NIBBLE + LD L,A + JR EX0 ;GET SOME MORE +..EX2: EX (SP),HL ;SAVE UP IN STACK + PUSH HL ;REPLACE THE RETURN + LD A,B ;TEST THE DELIMITER + CALL QCHK + JR NC,..EX3 ;CR ENTERED + DEC C ;SHOULD GO TO ZERO + RET Z ; RETURN IF IT DOES +..EX3: JP NZ,ERROR ;SOMETHING WRONG + DEC C ;DO THIS AGAIN? + JR NZ,EXPR ; YES. + RET ;ELSE RETURN +EXF: LD C,1 + LD HL,0 + JR EX1 +; +; RANGE TESTING ROUTINES. +; CARRY SET INDICATES RANGE EXCEEDED. +; +HILOX: CALL HILO + RET NC ;OK + POP DE ;RETURN ONE LEVEL BACKK + RET +; +HILO: INC HL ;INCREMENT HL + LD A,H ;TEST FOR CROSSING 64K BORDER + OR L + SCF ;CARRY SET=STOP + RET Z ;YES, BORDER CROSSED + LD A,E ;NOW, TEST HL VS. DE + SUB L + LD A,D + SBC H + RET ;IF CARRY WAS SET, THEN STOP +; +; HEXADECIMAL MATH ROUTINE +; +; THIS ROUTINE IS USEFUL FOR +; DETERMINING RELATIVE JUMP +; OFFSETS. IT RETURNS THE SUM +; & DIFFERENCE OF TWO PARAMETERS. +; +; H, +; +; X+Y X-Y +; +HEXN: CALL EXLF + PUSH HL ;SAVE HL FOR LATER + ADD HL,DE ;GET SUM + CALL HLSP ;PRINT IT + POP HL ;THIS IS LATER + OR A ;CLEAR CARRY + SBC HL,DE ;GET DIFFERENCE & PRINT IT +; +; PRINT H&L ON CONSOLE +; +LADR: LD A,H + CALL LBYTE + LD A,L +LBYTE: PUSH AF + RRCA + RRCA + RRCA + RRCA + CALL ..2 + POP AF +..2: CALL CONV + JP CO +; THIS ROUTINE SENDS EIGHT RUBOUTS +; TO THE PUNCH DEVICE. +; +MARK: LD BC,08FFH ;SET-UP B&C + JR LE0 +; +; THIS ROUTINE SENDS BLANKDS TO THE +; PUNCH DEVICE. +; +LEAD: LD BC,4800H ;PRESET SOME NULLS +LE0: CALL PUO + DJNZ LE0 + RET +; +; THIS ROUTINE RETURNS TO A USER +; PROGRAM THE CURRENT TOP OF +; MEMORY VALUE MINUS WORKSPACE +; AREA USED BY THE MONITOR. +; +MEMCK: PUSH HL + CALL MEMSIZ + LD A,L + SUB 2CH + JR NC,...B + DEC H +...B: LD B,H + POP HL + RET +; +; THIS IS A CALLED ROUTINE USED +; TO CALCULATE THE TOP OF MEMORY +; STARTING FROM THE BOTTOM OF +; MEMORY, AND SEARCHING UPWARD UNTIL +; FIRST R/W MEMORY IS FOUND, AND THEN +; CONTINUING UNTIL THE END OF THE R/W +; MEMORY. THIS ALLOWS R.O.M. AT ZERO, +; AND INSURES A CONTINUOUS MEMORY BLOCK +; HAS BEEN FOUND. +; IT IS USED BY THE ERROR ROUTINE TO +; RESET THE STACK POINTER AS WELL. +; +MEMSIZ: PUSH BC +;WW LD BC,BASE ;POINT TO START OF MONITOR +;WW LD HL,-1 ;RAM SEARCH STARTINT PT. +;WW..M0: INC H ;FIRST FIND R/W MEMORY +;WW LD A,(HL) +;WW CPL +;WW LD (HL),A +;WW CP (HL) +;WW CPL +;WW LD (HL),A +;WW JR NZ,..M0 +;WW..M1: INC H ;R/W FOUND, NOW FIND END +;WW LD A,(HL) +;WW CPL +;WW LD (HL),A +;WW CP (HL) +;WW CPL +;WW LD (HL),A +;WW JR NZ,..M2 +;WW LD A,H ;TEST FOR MONITOR BORDER +;WW CP B +;WW JR NZ,..M1 ;NOT THERE YET +;WW..M2: DEC H ;BACK UP, SUBTRACT WORKSPACE + LD HL,$EFFF ; WW - FIXED TOP OF MEMORY AT $F000 + LD BC,EXIT-ENDX + ADD HL,BC + POP BC ;RESTORE BC + RET ;VALUE IN HL +; +; +RIBBLE: CALL RIX +NIBBLE: SUB '0' ;QUALIFY & CONVERT + RET C ;<0 + CP 'G'-'0' ;>F? + CCF ;PERVERT CARRY + RET C + CP 10 ;NMBR? + CCF ;PERVERT AGAIN + RET NC ;RETURN CLEAN + SUB 'A'-'9'-1 ;ADJUST + CP 0AH ;FILTER ":" THRU "@" + RET +; +; SEND H&L VALUE TO PUNCH DEVICE +; +PADR: LD A,H + CALL PBYTE + LD A,L +; +; PUNCH A SINGLE BYTE +; +PBYTE: PUSH AF ;NIBBLE AT A TIME + RRCA + RRCA + RRCA + RRCA + CALL CONV + CALL PUO + POP AF ;NEXT NIBBLE + PUSH AF ;SAVE FOR CHECKSUM + CALL CONV + CALL PUO + POP AF ;ORIGINAL BYTE HERE + ADD D ;ADDED TO CHECKQUM + LD D,A ;UPDATE CHECKSUM + RET +; +; +COPCK: LD C,'-' + CALL CO +; +PCHK: CALL TI +; +; TEST FOR DELIMITERS +; +QCHK: CP ' ' ;RETURN ZERO IF DELIMITER + RET Z + CP ',' + RET Z + CP CR ;RETURN W/CARRY SET IF CR + SCF + RET Z + CCF ;ELSE NON-ZERO, NO CARRY + RET +; +; MAIN CONSOLE INPUT ROUTINE +; +CI: CALL IOCHK + AND ~CMSK ; WW: WAS "NOT CMSK" + JR NZ,CI1 +; +; TELEPRINTER ROUTINE +; +TTYIN: IN A,TTS + AND TTYDA + JR Z,TTYIN + IN A,TTI + RET +; +CI1: DEC A ;CONSOLE=CRT? + JR NZ,CI2 +; +; C.R.T. INPUT ROUTINE +; +CRTIN: IN A,CRTS + AND CRTDA + JR Z,CRTIN + IN A,CRTI + RET +; +CI2: DEC A ;BATCH? + JP NZ,CILOC ;NO, MUST BE USER DEFINED +; +; +; READER INPUT ROUTINE, WITH +; TIME-OUT DELAY. INCLUDES +; PULSING OF HARDWARE PORT +; TO INDICATE REQUEST FOR +; READER DATA. +; +RI: PUSH HL + CALL IOCHK + AND NOT RMSK + ;WW CPL + ;WW OUT RCP,A ;PULSE READER CONTROL PORT + ;WW CPL ;CLEEAR IT + ;WW OUT RCP,A + JR NZ,RI3 ;NOT TTY + LD H,A ;CLEAR FOR-TIME OUT TEST +RI0: IN A,TTS + AND TTYDA + JR NZ,RI2 ; WW: Z -> NZ + PUSH BC + LD B,0 +DL0: EX (SP),HL ;WASTE TIME + EX (SP),HL ;FOR DELAY + DJNZ DL0 + POP BC + DEC H +; JR NZ,RI0 ; WW + JR RI0 ; WW - ELIMINATE TIMEOUT +RI1: XOR A + SCF + POP HL + RET +RI2: IN A,TTI +RID: OR A + POP HL + RET +RI3: CP RCAS + JR NZ,RI6 + ;WW IN A,SWITCH ;READ INITIAL SENSE CONDX. + ;WW LD L,A +RI4: ;WW IN A,SWITCH ;SEE IF SW. ALTERED + ;WW CP L + ;WW JR NZ,RI1 ;ABORT IF SO + IN A,RCSS + AND RCSDA ;DATA YET? + JR NZ,RI4 ;KEEP LOOKING +RI5: IN A,RCSD + JR RID +RI6: POP HL + CP RPTR + JP Z,RPTPL ;EXTERNAL ROUTINE + JP RULOC ;USER VECTOR +; +; THIS ROUTINE GETS READER INPUT +; AND KILLS THE PARITY BIT. +; +RIX: CALL RIFF + AND 7FH + RET +; +; THIS ROUTINE READS A BINARY FILE +; IMAGE, IN THE FORM AS PUNCHED IN +; THE "U" (UNLOAD) COMMAND. IT TAKES +; ONE PARAMETER, WHICH IS THE STARTING +; ADDRESS OF THE LOAD, AND WILL PRINT +; THE LAST ADDRESS (+1) LOADED ON THE +; CONSOLE DEVICE. +; +LOAD: CALL EXPR1 ;INITIAL LOAD ADDRESS + POP HL + CALL CRLF + LD D,0FFH ;START-OF-FILE TAB +...L0: LD B,4 ;FIND AT LEAST FOUR 0FFH'S +...L1: CALL RIFF + JR NZ,...L0 + DJNZ ...L1 +...L2: CALL RIFF ;4 FOUND, NOW WAIT FOR NON-0FFH + JR Z,...L2 + LD (HL),A ;FIRST REAL DATA BYTE + LD A,BELL ;TELL TTY + OUT TTO,A +...L3: INC HL + CALL RIFF + JR Z,..EL ;POSSIBLE END OF FILE + LD (HL),A + JR ...L3 +..EL: LD E,1 ;INITIALIZE +..EL0: CALL RIFF + JR NZ,..EL1 + INC E ;COUNT QUES + LD A,MAX ;LOOK FOR EOF + CP E ;FOUND MAX? + JR NZ,..EL0 ;NOPE + JP LADR ;YEP, PRINT END ADDR +..EL1: LD (HL),D + INC HL + DEC E ;RESTORE + JR NZ,..EL1 + LD (HL),A ;REAL BYTE + JR ...L3 +; +; THIS IS THE BREAKPOINT "TRAP" HANDLING +; ROUTINE. ALL USER REGISTERS ARE SAVED +; FOR DISPLAY PURPOSES, AND THE CONTENTS +; ARE RESTORED WHEN EXECUTING A "GO" (G) +; COMMAND. +; +RESTART: + PUSH HL ;PUSH ALL REGISTERS + PUSH DE + PUSH BC + PUSH AF + CALL MEMSIZ ;GET MONITOR'S STACK VALUE + EX DE,HL + LD HL,10 ;GO UP 10 BYTES IN STACK + ADD HL,SP + LD B,4 ;PICK OFF REG. + EX DE,HL +...R0: DEC HL + LD (HL),D ;SAVE IN WORKAREA + DEC HL + LD (HL),E + POP DE + DJNZ ...R0 + POP BC + DEC BC ;ADJUST P.C. VALUE + LD SP,HL ;SET MONITOR STACK + LD HL,TLOCX + ADD HL,SP + LD A,(HL) + SUB C ;LOOK FOR A TRAP/MATCH + INC HL + JR NZ,..R1 + LD A,(HL) + SUB B + JR Z,..R3 ;NO TRAP HERE +..R1: INC HL + INC HL + LD A,(HL) + SUB C ;TEST FOR SECOND TRAP + JR NZ,..R2 + INC HL + LD A,(HL) + SUB B + JR Z,..R3 +..R2: INC BC ;NO TRAPS SET, RE-ADJUST P.C. +..R3: LD HL,LLOCX + ADD HL,SP + LD (HL),E ;STORE USER H&L + INC HL + LD (HL),D + INC HL + INC HL + LD (HL),C ;AND USER P.C. + INC HL + LD (HL),B + PUSH BC + LD C,'@' ;DISPLAY BREAK ADDRESS. + CALL CO + POP HL + CALL LADR + LD HL,TLOCX + ADD HL,SP + LD BC,200H +..R4: LD E,(HL) ;REPLACE BYTES TAKEN FOR TRAP + LD (HL),C ;ZERO OUT STORAGE AREA + INC HL + LD D,(HL) + LD (HL),C + INC HL + LD A,E + OR D ;DO NOTHING IF ZERO + JR Z,..R5 + LD A,(HL) + LD (DE),A ;STORE BYTE +..R5: INC HL ;SAME THING + DJNZ ..R4 ;FOR OTHER BREAKPOINT + EX AF,AF' ;GET ALTERNATE SET OF REG.'S + EXX + PUSH HL ;AND STORE IN WORKSPACE + PUSH DE + PUSH BC + PUSH AF + PUSH IX + PUSH IY + LD A,I ;GET INTERUPT VECTOR BYTE + LD B,A + LD A,R ;GET REFRESH BYTE + LD C,A + PUSH BC ;SAVE + JP START ;BACK TO START +; +; THIS IS THE INTERNAL KEYBOARD +; HANDLING ROUTINE. IT WILL IGNORE +; RUBOUTS (0FFH) AND BLANKS (00), +; AND IT WILL NOT ECHO CR'S & N'S. +; (NO N'S FOR THE "NULL" COMMAND). +; IT CONVERTS LOWER CASE TO UPPER +; CASE FOR THE LOOK-UP OF COMMANDS. +; +; OTHER CHARACTERS ARE ECHOED AS THEY +; ARE RECEIVED. +; +KI: CALL CI ;GET CHARACTER FROM CONSOLE + AND 7FH ;CLEAR PARITY BIT + RET +; +TI: CALL KI + INC A ;IGNORE RUBOUTS + RET M + DEC A ;IGNORE NULLS + RET Z + CP 'N' ;IGNORE N'S FOR NULL CMND + RET Z + CP 'n' + JR Z,..T + CP CR ;IGNORE CR'S + RET Z + PUSH BC + LD C,A + CALL CO + LD A,C + POP BC + CP 'A'-1 ;CONVERT TO UPPER CASE + RET C + CP 'z'+1 + RET NC +..T: AND 05FH + RET +; +; THIS ROUTINE ALLOWS EXAMINATION OF +; ANY INPUT PORT, OR THE SEENDING OF +; ANY VALUE TO ANY PORT. +; +; QO,[CR] +; OUTPUT TO PORT , THE VALUE +; +; QI[CR] +; DISPLAY THE PORT +; +QUERY: CALL TI + CP 'O' + JR Z,QUO + CP 'I' + JP NZ,ERROR + CALL EXPR1 + POP BC + IN E,(C) +BITS: LD B,8 ;DISPLAY 8 BITS + CALL BLK +..Q2: SLA E + LD A,'0'>>1 ; WW: WAS "LD A,'0' >1" + ADC A ;MAKE "0" OR "1" + LD C,A + CALL CO + DJNZ ..Q2 + RET +QUO: CALL EXPR + POP DE + POP BC + OUT (C),E + RET +; +; THIS ROUTINE VERIFIES THE CONTENTS +; OF ONE MEMORY BLOCK WITH ANOTHER. +; +; V,, +; VERIFY FROM <1> THRU <2> WITH +; THE CONTENTS OF MEMORY BEGINNING AT <3> +; +VERIFY: CALL EXPR3 ;GET 3 PARAMENTERS +VERI0: LD A,(BC) + CP (HL) + JR Z,..B + PUSH BC + CALL CERR ;DISPLAY ERRORS + POP BC +..B: INC BC + CALL HILOX + JR VERI0 +; +; +; +; THE FIRST CHARACTER IS THE DEVICE NAME +; (ONE LETTER) AND THE NEXT FOUR ARE THE +; NAMES OF THE FOUR POSSIBLE DRIVERS TO BE +; ASSIGNED. +; +LTBL: + DB 'C' ;CONSOLE ASSIGNMENTS + DB 'T' ;CTTY T=TELEPRINTER + DB 'C' ;CCRT C=CRT (VIDEO MONITOR) + DB 'B' ;BATCH= COMMANDS FROM READER + DB 'U' ;CUSE USER +; + DB 'R' ;READER ASSIGNMENTS + DB 'T' ;RTTY + DB 'P' ;RPTR P=PAPER TAPE + DB 'C' ;RCAS C=CASSETTE + DB 'U' ;RUSER USER +; + DB 'P' ;PUNCH ASSIGNMENTS + DB 'T' ;PTTY + DB 'P' ;PPTP + DB 'C' ;PCAS C=CASSETTE + DB 'U' ;PUSER USER +; + DB 'L' ;LIST ASSIGNMENTS + DB 'T' ;LTTY LIST=TELEPRINTER + DB 'C' ;LCRT LIST=CRT + DB 'L' ;LINE PRINTER + DB 'U' ;LUSER USER +; +; +; THIS IS A SHORT PROGRAM, EXECUTED +; UPON EXECUTING A "GO" COMMAND. IT +; IS PLACED IN THE WORK AREA WHEN +; THE MONITOR IS INITIALIZED, AS IT +; :REQUIRES RAM FOR PROPER OPERATION. +; +EXIT: ;EXIT ROUTINE (LOADS ALL REGISTERS) + POP BC + LD A,C + LD R,A + LD A,B + LD I,A + POP IY + POP IX + POP AF + POP BC + POP DE + POP HL + EX AF,AF' + EXX + POP DE + POP BC + POP AF + POP HL + LD SP,HL + NOP ;RESERVED FOR ENABLE INTERUPTS + LD HL,0 + JP 0 +; + DW 0 ;STORAGE AREA FOR TRAP DATA + DB 0 + DW 0 + DB 0 +; +; DISPLACEMENTS OF REGISTER +; STORAGE FROM NORMAL STACK +; LOCATION. +; +ENDX: +; +ALOC: EQU 15H +BLOC: EQU 13H +CLOC: EQU 12H +DLOC: EQU 11H +ELOC: EQU 10H +FLOC: EQU 14H +HLOC: EQU 31H +LLOC: EQU 30H +PLOC: EQU 34H +SLOC: EQU 17H +TLOC: EQU 35H +TLOCX: EQU 25H +LLOCX: EQU 20H +; +APLOC: EQU 09H +BPLOC: EQU 0BH +CPLOC: EQU 0AH +DPLOC: EQU 0DH +EPLOC: EQU 0CH +FPLOC: EQU 08H +HPLOC: EQU 0FH +LPLOC: EQU 0EH +XLOC: EQU 07 +YLOC: EQU 05 +RLOC: EQU 02 +ILOC: EQU 03 +; +; +; THIS IS THE TABLE USED TO DETERMINE +; A VALID REGISTER IDENTIFIER, AND IT'S +; DISPLACEMENT FROM THE STACK POINTER. +; +; POSITION ONE= REGISTER NAME, WITH BIT 7 INDICATING +; END OF TABLE. +; +; POSITION TWO= BIAS FROM CURRENT STACK LEVEL OR'ED +; WITH A TWO-BIT FLAG. 00XXXXXX=BYTE +; 10XXXXXX=WORD +; 11XXXXXX=SPECIAL FOR "M" REG. +; +ACTBL: ;NORMAL SET OF REGISTERS (8080) +; ;PLUS THE INTERUPT REGISTER ("I") +; + DB 'A',ALOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'B',BLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'C',CLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'D',DLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'E',ELOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'F',FLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'H',HLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'L',LLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'M',HLOC | 0C0H ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'P',PLOC | 080H ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'S',SLOC | 080H ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'I',ILOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM +; +; DB " RWA" +; +PRMTB: ;ADDITIONAL SET OF REGISTERS (Z-80) +; + DB 'A',APLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'B',BPLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'C',CPLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'D',DPLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'E',EPLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'F',FPLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'H',HPLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'L',LPLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'M',HPLOC | 0C0H ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'X',XLOC | 080H ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'Y',YLOC | 080H ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 'R',RLOC | 0 ; WW: REPLACED "OR" WITH "|" FOR TASM + DB 0C1H +; +Z: +;WW END