;       File DISKS.S contains most of the routines for
;       creating, reading, and writing disk images and
;       ramdisks. This file is part of SIO2PC


;	includes:

;  ΥΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΈ
;  ³								   ³
;  ³								   ³
;  ³	XCHANGE;  CRUD;  DO_BLANK;  READ_DSK;  WRITE_DISK	   ³
;  ³	ZSTARS;  INEXTS;  SET_BP;  UN_DISK;  FIND_MT		   ³
;  ³	GET_FSPC;  ORDER;  DELETE;  DO_BLANK;  CUS_SIZE 	   ³
;  ³	STD_SIZE;  REL_DEL;  WRITE_ERROR;  PART_REC		   ³
;  ³	GET_DISKNO;  USE_4K; V_CHECK; GET_PNAME 		   ³
;  ³    SIM_IO; SHO_MOR; DO_CFINFO; W_CRC; BLANK_RFS               ³
;  ³    INST_PC; SIM_READ; D_OPENF                                 ³
;  ³								   ³
;  ³								   ³
;  ³								   ³
;  ΤΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΎ


;       CRUD - Create an Atari ramdisk based on user
;       supplied parameters.


; Get disk number and see if it already exists:

CRUD:   CALL GET_DISKNO
        JNC >L1 ; No error
        CMP ERROR, 27; ESCAPE?
        PSTATUS PRMT_DSK, ATTR9 ; 4.11d
        JMP CRUD; For other than ESC, try again
        STC
        RET; Return with error status

L1:	MOV LOAD_OPT, ' '; 3.17 start with neutral load option
	CMP ERROR,'N'
        JE >L1
        PSTATUS X10, ATTR10
        TICKS 36
        CALL FLUSH
        JMP CRUD

; Now, the disk chosen did NOT exist, so now find an open structure
; record and set BP to base of it.

L1:     CALL FIND_MT    ; carry clear means one was available
        JNC >L1
        PSTATUS ALLUSED, ATTR10
        TICKS 50
        STC
        RET

L1:     MOV AX,080h; assume single density
        AND B[TYPE_CODE+BP], 11111011xB
        CMP CCRUD, 'C'
        JE >L1
        MOV AX,0100h; If CCRUD <> 'C' density is double
        OR B[TYPE_CODE+BP], 00000100xB
L1:     MOV [SEC_SIZE+BP], AX
;	 AND B[TYPE_CODE+BP], 11111011xB; single density 3.17 removed


; Now get disk size from user:

        MOV AL, DISK
        MOV [N_DISK+BP], AL     ; disk # was ok, so record it
        MOV B[N_DISK+BP+1], ':'
        PSTATUS GET_DSIZE, ATTR9
        CALL GET_TORK; Now get size info...
        CMP AL, 27      ; escape pressed?
        JNE >L1
        CALL UN_DISK
        STC
        RET

L1:     SUB AL, '1'     ; convert to range of 0 to 5
        CMP AL,06; 3.05 - WAS 5
        JB >L1
        PSTATUS VOR, ATTR10; Value out of range
        TICKS 36
        JMP RE_CRUD
L1:     MOV SIZE_CH, AL ; save for reference

	CMP AL, 4; 5 means custom size (rev. 3.00)
        JB >L1; 0,1,2,3 are the old standard sizes
        CALL CUS_SIZE; routine handles getting size, etc.
        CMP ERROR, 'E'
        JNE ENTRY
	CALL UN_DISK ; 3.17 - formerly fell thru to RE_CRUD
	STC
	RET

RE_CRUD:
        CALL UN_DISK    ; clear disk record fields
	JMP CRUD; for error in - start over @ CRUD

L1:     CALL STD_SIZE



ENTRY: ; This is the entry point for call from READ_DSK
       ; procedure. It will have AX & DX & SEC_SIZE & BP & BX set up.
       ; and MAX_SEC[BP] (BX holds SIZE_CH * 2)

; NOTE: [02] BELOW IS A VARIABLE IN THE PSP. IF THIS FILE IS
; CHANGED FROM COM TO EXE, THIS MAY HAVE TO CHANGE ***

; REV 3.00: first, if DX <> 0, we KNOW this is too big to be
; a ramdisk:

        CMP B[LOAD_OPT], 'P'; force physical file access?
        JE ACS

        CMP DX, 0
        JNE ACS ; can't be ramdisk, don't bother checking
        MOV AX, [LO_SIZE+BP]
        ADD AX, NEXTS; Check new top against memory size.
        JC ACS; overflow means too big ... rev 3.00

; Note below: QUICK ASSEMBLER requires segment modifier (even in
; default segment) when using an absolute address (offset).

        CMP AX,DS:[02]; PSP memory top
	JAE ACS
	JMP NEAR > L1
ACS:	CMP LOAD_OPT, 'P'; user requested file access? (3.17)
	JNE > P1
	PSTATUS X111, ATTR9
	JMP > P2
P1:	PSTATUS X11, ATTR9
P2:	TICKS 27
; REV 3.00 no error - don't flush
        MOV B[WRITTN+BP], 'F'
        MOV B[WRITTN+BP+1], ','
        CALL COLOR_FFIELD

; At this point, we know the disk size is too big for a ramdisk.
; If this is a CREATE operation, we will now ask for a filename and
; create a blank file of the desired size.  If this is a LOAD operation
; we just continue setting up the data for the disk, with the flag
; showing it's a FILE disk and not a RAM disk.

        CMP XCRUD, 'L' ; Load file operation?
        JE ACT
        CALL DO_BLANK
        JNC ACT         ; carry set means abort crud operation
        CALL UN_DISK
        STC             ; Bad status return
        RET             ; error return

; Now, there is enough memory so make old top [NEXTS] be new
; start [STARS], and old top plus size equal new top [NEXTS].


L1:     MOV CX, NEXTS
        MOV [STARS+BP],CX; save start'g seg. of new disk
        MOV NEXTS, AX; New next available segment
        MOV CX, DS:[02]; top of memory
        SUB CX, AX      ; get free ram in paragraphs
        MOV RAM, CX
        CALL PUT_RAM
        CMP SIZE_CH, 4; For custom, stuff below has been done
        JAE ACT; 3.06

; Changed above 9/11. was [NEXTS+DI], but there's only one
; NEXTS, right?

; Now set up the max. number of sectors variable, equal
; to size in para's divided by sector size in para's.

        MOV AX,[SIZES+BX]; Lookup size in paragraphs
        MOV [LO_SIZE+BP],AX; Also store size this specific disk
        MOV W[HI_SIZE+BP], 0 ; REV 3.00
        MOV BX,[SEC_SIZE+BP]; Sector size; *** 11/27/89
        MOV CL,4; CAN'T USE SHR BL,4 ON 8086
        SHR BX,CL; Since numerator is in paragraphs **** 11/27/89
        MOV DX,0; high 16 bits of numerator
        DIV BX; DX:AX/BX, result is in AX and equals max sectors

        MOV [MAX_SEX+BP],AX
        CMP W[SEC_SIZE+BP], 080h; single?
        JE >L1   ; then no adjustment
        ADD W[MAX_SEX+BP], 2

L1:     CALL CLR_ALL

; Now, set up the text info for the ramdisk info lines

;       First, ramdisk size:

ACT:    CALL ASCI_SIZE

;       Set up DSTATS density information based on size choice.
;	For 130K, status = 144, else = 16.


        MOV B[STATS+3+BP],16; assume <> 143K
	CMP W[LO_SIZE+BP], (1040/16)*128
        JNE >L1
        CMP W[HI_SIZE+BP],0
        JNE >L1
        MOV B[STATS+3+BP],144

; Make bit 5 = 1 for double density

L1:     CMP W[SEC_SIZE+BP],0100h ; this loc had no label before REV 3.00
        JNE >L1
        OR B[STATS+3+BP],020h

;       Then, ramdisk density:

L1:     PUSH CS
        POP ES
        MOV AX,[SEC_SIZE+BP]
        MOV CL,8
        SHR AX,CL; makes 1 for 256, 0 for 128
        SHL AX,1; 4 for 256, 0 for 128: string pntr
        SHL AX,1; counting this shift
        MOV SI, OFFSET ASDNS;   String 'SD, '
        ADD SI,AX;              Or string 'DD, '
        MOV DI, OFFSET ADENS
        ADD DI,BP
        MOV CX,4
        CLD
        REP MOVSB

        CMP B[WRITTN+BP], 'F'
        JNE >L1
        MOV B[STATS+1+BP], 060h ; long timeout for physical format
        JMP ADA

; Now, show this ramdisk hasn't been written yet:

L1:     MOV B[WRITTN+BP],'N'
        MOV B[WRITTN+1+BP],','
        CALL COLOR_FFIELD
        MOV B[STATS+1+BP],020h   ; Normal timeout value for ramdisk

; 3.01A BELOW -
ADA:    CALL DO_CFINFO; Put config. into into 12 byte table
        CALL SU_DSKHDR

        CLC ; Good return status
        RET

; DGET_FSPC is a new entry point for GET_FSPC which doesn't print
; on the status line, it uses the current cursor position ...

DGET_FSPC:
	  PRINTL RD3
          MOV TAG_RD3, 0FFh ; tag that don't want to fix screen
	  JMP > D1


;       GET_FSPC - This subroutine gets a filespec from the
;       user. If ESC is pressed, the carry is set on return.
;       The string is stored at RFSPEC and terminated by 0.

GET_FSPC:

        PSTATUS RD3, ATTR9

; rev. 4.11 - if tail isn't empty then get the string all at once
; from it.  Else, use the drop down file list

D1:     CMP TAIL_CNT, 0 ; tail empty?  (Note: D1 is separate entry point)
        JE > F1
        MOV DI, RFSPEC ; GET_TAILS puts string in [DI]
        CALL GET_TAILS ; get filename from tail
        MOV B [DI], 0 ; make it ASCIIZ
        JMP ABK

F1:     MOV CX, 54; Max chars allowed (note D1 is separate entry point)
        MOV DI, 0; Index counter to put chars in buffer
;AAI:
L0:     CALL GET_TORK; WAS K6

        CMP AL, 08; Backspace?
        JNE >L1
        CMP DI, 0; Don't backspace if at start
        JE L0
        DEC DI; Backspace actions
        INC CX; Note: I'm not preventing screen bkspce.
	CALL PRINT1 ; backspace just moves cursor left ...
	MOV AL, ' ' ; so print a space over deleted character
	CALL PRINT1 ; and then
	MOV AL, 08  ; move character left again!
        CALL PRINT1
        JMP L0

L1:     CMP AL,27; ESC? ESC means cancel function...
        JNE > L4
F4:     MOV [ERROR], 27
        STC
        RET


L4:	CMP AL,' '; Space means end of filename...
        JE >L1
        CMP AL, 0Dh; So does return
        JE >L1

; REV 3.17:

      CMP AL, ' '; reject all codes below 020H
      JB  L0

      CMP AL, 127
      JA L0

; OK, its just a regular character...

        MOV [RFSPEC+DI], AL; Put it in filespec block,
        INC DI
        CALL PRINT1
        LOOP L0 ;AAI; Get more if haven't used 54
        JMP ABK

; Now, put zeroes for rest of field.

L1:     MOV AL,0
L1:     MOV [RFSPEC+DI], AL
        INC DI
        LOOP L1

ABK:    MOV AL,0
        MOV [RFSPEC+54], AL; 3.17, # was 55
        CLC
        RET



LDGET_FSPC:
          MOV TAG_RD3, 0FFh ; tag that we don't want to fix screen
	  JMP > D1

; LGET_FSPC will get a filespec from the drop down list using FILELIST
; It's generally for READ FILE needs where a user will select from the
; directory.  WRITE FILE functions will still ask for manual entry via
; GET_FSPC
; The filename is moved to RFSPEC, and DI points to 1 past the 0 at the
; end of the string (String is ASCIIZ) if status is good (carry clear)

LGET_FSPC:

        MOV TAG_RD3, 0 ; tell FILELIST to restore SIO2PC screen
D1:     CMP TAIL_CNT, 0 ; tail empty?  (Note: D1 is separate entry point)
        JE > F1
        MOV DI, RFSPEC ; GET_TAILS puts string in [DI]
        CALL GET_TAILS ; get filename from tail
        MOV B [DI], 0 ; make it ASCIIZ
        JMP SHORT > K1
F1:     CALL FILELIST ; get choice from drop down list
        JC > F4 ; error exit

; filelist returns the filename with spaces following.  Change 1st space
; to a 0

        FIND_BYTE RFSPEC, ' ', 54
        MOV B [DI-1], 0
        JMP SHORT > K1

F4:     MOV [ERROR], 27
        STC
        JC > F5
K1:     CLC
F5:     RET

; sub fills RFSPEC with blanks

BLANK_RFS:
        PUSH AX, SI, CX
        FILL ' ', RFSPEC, 55
        POP CX, SI, AX
        RET

; A subroutine to order the disk table pointers in MIN_TABLE
; in order of increasing size of STARS, for use by the DELETE
; routine in ensuring that the lowest ramdisk gets moved down first.
; MIN_TABLE's 4 word entries are the BLOCK_SIZE * 0, 1, 2, 3
; Note: This is a "bubble sort"

ORDER:
        PUSH CX,BX,BP,SI,DX
ORDER1: MOV SORT_FLAG,0; No swaps were required if = 0
        MOV CX,3; Routine checks in pairs, 1,2 2,3 3,4
ORDER2: MOV BX,3
        SUB BX,CX; Make BX = 0,1,2 for CX = 3,2,1
        SHL BX,1; Point to WORD entry in MIN_TABLE
        MOV BP,MIN_TABLE[BX]; Get pointer to lower entry of pair
        MOV SI,MIN_TABLE[BX+2]; Get pointer to next higher
        MOV DX,[BP+STARS]; Get lower # STARS
        MOV DI,[SI+STARS]; FOR DEBUGGING ONLY
        MOV DI, OFFSET STARS
        MOV DI, BLOCK_SIZE
        CMP DX,[SI+STARS]; Compare to next higher
        JBE >L1; No change needed if lower or equal
        MOV MIN_TABLE[BX+2],BP  ; else, swap pointer entries
        MOV MIN_TABLE[BX],SI
        MOV SORT_FLAG,1; And flag that swap was necessary
L1:     LOOP ORDER2
        CMP SORT_FLAG,1
        JE ORDER1
        POP DX,SI,BP,BX,CX
        RET

;       DELETE -  A routine which deletes a ramdisk and moves the
;       others in memory so there is no gap containing unused
;       data. On entry, the ramdisk has been verified to exist
;       and DISK & DISKW & BP have been set up by GET_DISKNO
;       NOTE: since DS is out of whack in loop, MOV's use CS
;       over-ride, unless BP is used, then SS is assumed.


DELETE:
        CMP B[WRITTN+BP], 'S'; Simulated disk?  (4.11 added this)
        JE > S1
        CMP B[WRITTN+BP], 'F'; Real disk gets spcl treatment
        JNE >L1              ; REV 3.00
S1:     JMP REL_DEL     ; exit for physical file disks
L1:     CMP B[WRITTN+BP], 'W'    ; as of REV 3.00, will issue a
        JNE >L1                  ; warning for disks not saved
        PSTATUS NOT_SAVED, ATTR9
        CALL GET_TORK
        AND AL, 11011111xB; REV 3.03 CONV. TO UPPERCASE
        CMP AL, 'Y'     ; 'Y' means delete anyway
        JE >L1
        RET

L1:     CALL ORDER; Sort MIN_TABLE by MIN to MAX STARS

        MOV DX,[LO_SIZE+BP]; Save size (para's) of disk to delete
        MOV DELSIZE,DX
        SUB NEXTS,DX; Lower NEXTS by amount of RAM freed
        ADD RAM, DX; # of paras of free RAM
        CALL PUT_RAM
        MOV AX,[STARS+BP]; Keep starting segment in AX
        PUSH BP; Save pointer for deleted disk


; No need to check 1st ramdisk, it's guaranted at lowest RAM

        MOV CX,3; loop counter, 3 ramdisks
        CLD; Increment in UP direction

NXT_DSK:
        MOV BP,4; Make disk # count up, 1,2,3
        SUB BP,CX
        SHL BP,1; Make point to word, now BP = 2,4,6
        MOV BP,[MIN_TABLE+BP]; Disk #*2 type index
        MOV DX,[LO_SIZE+BP]; Get size of disk to be moved
        MOV CS:DMOVSIZE,DX; And store it in DMOVSIZE
        CMP AX,[STARS+BP]; Is current ramdisk below deleted one?
        JAE CKNEXT; No need to move if above or same disk

; Now the part to move, 32K at a time

        PUSH CX
        MOV SI,0; Using offsets of 0
        MOV DI,SI
        MOV DX,[STARS+BP]; Current (source) segment base
        MOV DS,DX; Source
        SUB DX,CS:DELSIZE
        MOV ES,DX; Destination
        MOV [STARS+BP],DX; New base address for ramdisk

; Now, do 32K block (assumes at least 32K to move):

L1:     MOV CX,04000h; 16K of words = 32K
        REP MOVSW; Move 32K bytes from DS:SI to ES:DI
        MOV DX,ES
        ADD DX,0800h; Point up to next 32K block
        MOV ES,DX
        MOV DX,DS
        ADD DX,0800h; Same
        MOV DS,DX
        MOV DI,0
        MOV SI,DI
        SUB CS:DMOVSIZE,0800h; Count down # to move
        CMP CS:DMOVSIZE,0800h; 32K or less to go?
        JA L1

; Now move last dab, if there is one:

        MOV CL,3; To multiply by 8 (moving words, not bytes)
        SHL CS:DMOVSIZE,CL
        MOV CX,CS:DMOVSIZE; # of words to move
        CMP CX,0; Don't bother moving 0 words
        JE >L1
        REP MOVSW

L1:     POP CX
CKNEXT: LOOP NXT_DSK; Now check next disk

        POP BP
        MOV W[STARS+BP],0; Show that disk no longer exists

        PUSH CS; Fix segment registers
        PUSH CS
        POP DS
        POP ES
        CALL UN_DISK    ; clear disk's text fields
                        ; REV 3.00 made this a subroutine

        CALL SU_DSKHDR
        RET

; label below is where physical file disks exit DELETE. rev 3.00
; this label is also called as a subroutine from the QUIT area

REL_DEL:
;       CALL UN_DISK    ; Clear text field 4.11 move till after HANDLE used
        MOV BX, [HANDLE+BP]

        #IF DEBUGX
        PUSH AX, BX
        MOV AX, BX ; get handle into AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP BX, AX
        #ENDIF

        MOV AH, 03Eh    ; Close file
        PUSH BX, AX
        CALL UN_DISK
        POP AX, BX
        INT 021h
        JNC >L1
        PSTATUS ECLOSE, ATTR9
        MOV AH,0; AX contains error code
        ADD AL,'0'; Convert to ASCII digit
        CALL PRINT1
        TICKS 50
L1:     CALL SU_DSKHDR
        RET

; Clear the text fields of disk addressed by BP
; also, set STARS = 0
; makes simulated disk sector # = 401
; As of 4.11, this routine just copies an entire undisturbed header
; over the one being vacated.  All bytes are reinitialized.

UN_DISK:
        MOV CX, BLOCK_SIZE
        PUSH DS
        POP ES
        MOV DI, OFFSET HANDLE ; start of header
        ADD DI, BP
        MOV SI, GOOD_COPY ; undisturbed header
        CLD
        REP MOVSB
;        MOV CX,(OFFSET DOLL - OFFSET N_DISK)
        ; Length of text fields to be cleared
;L1:     MOV DI,CX
;        MOV BYTE PTR [N_DISK-1][DI][BP], ' '
;        LOOP L1
;        MOV W[STARS+BP], 0
;        MOV B[DSK_FLAGS+BP], 0 ; clear flags - 4.02
        RET


;               FIND_MT

; This routine will search the 4 disk structure records for
; the first unused one.  If it finds one, the carry will be
; clear and BP will point to the base. Else, the carry will be
; set

FIND_MT:
        PUSH CX
        MOV BP, 0
        MOV CX, 4
L0:    CMP B[N_DISK+BP], ' '; means record is unused if space
        JE >L1
        ADD BP, BLOCK_SIZE
        LOOP L0
        POP CX
        STC     ; Not found
        RET
L1:     POP CX
        CLC
        RET

;               SET_BP

; This routine will take the ASCII disk # ('1' to '8') in AL
; and see if it exists in one of the four N_DISK fields. If it
; does exist, BP will be set to the start of the disk structure,
; and the carry will be clear. Otherwise, it will be set:
; NOTE: for device #'s 039h and 03Ah, carry will be clear. For
; device 040h, carry will be clear if PRINTHRU <> 0.
; Also, VALID will be made 0 for non VALID devices, 0FFh otherwise

SET_BP:

; For this first group of SPECIAL device IDs, BP will not be changed:
        CMP AL, 040h    ; no valid devices above 40h
        JA ADJ
        CMP AL, 031h    ; or below 31h
        JB ADJ

        CMP AL, 039h    ; remote control?
        JE ADI
        CMP AL, 03Ah    ; FILE2PC?
        JE ADI
        CMP AL, 040h        ; PRINTHRU
        JNE >L1
        CMP [PRINTHRU],0
        JE ADJ          ; PRINT THRU function not enabled
        JNE ADI

L1:     PUSH CX
        MOV BP, 0
        MOV CX, 4
L0:    CMP [N_DISK+BP], AL
        JE >L1
        ADD BP, BLOCK_SIZE
        LOOP L0
        POP CX
ADJ:    MOV VALID, 0
        STC     ; Not found
        RET
L1:     POP CX
ADI:    MOV VALID, 0FFh
        CLC
        RET



;               GET_DISKNO

; This subroutine gets disk # from user, and checks to
; see if it exists. If, so, variable ERROR contains E, else
; N. If ESC char is encountered, carry is set and ESC is
; returned in ERROR. For value out of range, message is printed
; and then carry is set. Routine also sets up DISK, AND
; BP to access BLOCK. FOR REV. 3.00: If the disk is a physical disk
; file, STARS will be 0, but WRITTN = 'F' will indicate that the
; disk exists. Same stuff returned in this case.

GET_DISKNO:
        MOV ERROR, 0; Init error
        CALL GET_TORK; Get disk #
        CMP AL,' '; Reject space
        JE GET_DISKNO
        CMP AL,27; ESCAPE
        JNE > L2
        MOV ERROR, AL
        STC
        RET; Escape error return

L2:     CMP AL,39h ; 4.11d - label was L1
        JAE > L3
        CMP AL, 031h ; 4.11d added - program did accept disk #0
        JAE > L1
L3:     PSTATUS VOR, ATTR10; Value out of range
        TICKS 16
        STC
        RET; Error exit

L1:     MOV DISK, AL; Save disk # for ref.

; Now, get disk table pointer into BP:

        MOV ERROR,'N'; Assume disk doesn't exist
        CALL SET_BP ;   carry clear will mean disk exists
        JC >L1
        MOV ERROR, 'E'

L1:     CLC     ; No error return
        RET


;               WRITE_DISK
; This subroutine will write out a ramdisk to a physical
; disk file. When the setup program receives 'W' from user,
; this subroutine is called.

WRITE_DISK:

        PSTATUS PRMT_DSK, ATTR9
        CALL GET_DISKNO
        JNC >L1
        CMP ERROR, 27
        JNE WRITE_DISK; Value out of range if not ESC
        STC
        RET; Return with error if ESC

L1:     CMP ERROR,'E'
        JE AAK; OK if disk does exist...
        PSTATUS DDNE, ATTR10; Else, give error msg and retry
        TICKS 36
        CALL FLUSH
        JMP WRITE_DISK

AAK:    CMP B[WRITTN+BP], 'F'; see if this is physical file
        JNE >L1              ; REV. 3.00
        PSTATUS NO_WRITE, ATTR9
        TICKS 50
        RET
L1:     CMP B[F_SPEC+BP],0; See if a filename already exists
        JNE >L1; and don't ask if it does. 10/14/89
        JMP AAJ
; Filename is assoc. with this ramdisk...

L1:     PSTATUS QUEFS, ATTR10; OK to use it?
        CALL GET_TORK
        CMP AL,27
        JNE >L1
        MOV ERROR, AL
        STC
        RET
L1:     AND AL,11011111xB; uppercase
        CMP AL,'Y'
        JNE AAJ

; Now, use old file spec:


        MOV DX,OFFSET F_SPEC
        ADD DX,BP
        MOV AH,03Dh; Open file
        MOV AL,1; Write access
        INT 021h; Call DOS
        JC > L1 ; 4.11 - this line did follow next line before
        MOV [HANDLE+BP],AX
        #IF DEBUGX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        #ENDIF
        JMP WL4

L1:     PSTATUS EOPF, ATTR10; Error opening file
        MOV AH,0
        ADD AL,'0'; Convert to ASCII digit
        CALL PRINT1
        TICKS 36
        CALL FLUSH
        JMP WRITE_DISK

; Get file spec from user:

AAJ:    CALL GET_FSPC
        JNC >L1
        RET; ERROR RETURN on ESC code

; Open file for write:

L1:     MOV DX,OFFSET RFSPEC
        MOV AH,03Dh
        MOV AL,1
        INT 021h
        JC >L1

; If no error, file already exists. Get permission to
; overwrite:

        MOV [HANDLE+BP],AX; First, save handle

        #IF DEBUGX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        #ENDIF
        PSTATUS FARE, ATTR10
        CALL GET_TORK
        AND AL,11011111xB; Uppercase
        CMP AL,'Y'
        JNE ABH

        JMP WL4
ABH:    MOV BX,[HANDLE+BP]
        MOV AH,03Eh; Close file
        INT 021h
        JMP AAK; Ask for new filename

L1:     CMP AX,02;      ERROR 2 means file doesn't exist,
        JE >L1;          so, go down and create it @ W6.
        PSTATUS EOPF, ATTR10;   If other error, print error msg
        MOV AH,0;       and retry.
        ADD AL,'0'; Convert to ASCII digit
        CALL PRINT1
        TICKS 36
        CALL FLUSH
        JMP AAK; retry

; Now, if file not found (AX = 2), go and create new file:

L1:     MOV CX,0; Normal file attribute
        MOV AH,03Ch; Create file
        MOV DX, OFFSET RFSPEC; (DS assumed = CS)        
        INT 021h
        JC XAAM; *** REV 2.4: THIS STMT WAS AFTER NEXT STMT***
        MOV [HANDLE+BP],AX; Save handle

        #IF DEBUGX
        PUSH AX, BX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP BX, AX
        #ENDIF

; Now, put pathname in field for this disk:

        PUSH CS
        POP DS
        MOV SI,-1 
        MOV CX,54; 54 bytes, max
W19:    INC SI; Move 54 bytes, or until byte = 0 moved.
        MOV AL,[RFSPEC+SI]
        MOV [F_SPEC+BP+SI],AL
        LOOP W19
        JMP WL4

XAAM:   PSTATUS ECF, ATTR10
        MOV AH,0
        ADD AL,'0'; Convert to ASCII digit
        CALL PRINT1
        TICKS 36
        CALL FLUSH
        JMP AAK

; Now, set up the header and put it:

WL4:

        MOV AX, CATARI;        File type identifier
        MOV HEADER,AX
        MOV AX,[LO_SIZE+BP];      Ramdisk size in para's
        MOV XSIZE, AX
        MOV AX,[SEC_SIZE+BP]
        MOV SEC_XSIZE, AX
        MOV AL, B[DSK_FLAGS] ; rev 4.05 saves disk flags with header
        MOV H_FLAGS, AL


;       Put 16 bytes:
;       *** NOTE: ASSUMED THAT CREATE FUNCTION ALSO
;       LEFT FILE OPEN FOR WRITE.
        CALL HEADER_0   ; fill unused header bytes w/0s
        MOV AH,040h; Write
        MOV BX,[HANDLE+BP]
        MOV CX,16
        PUSH CS
        POP DS
        MOV DX,OFFSET HEADER
        INT 021h
        JNC >L1; No error
        CALL WRITE_ERROR; status and close file
        JMP WRITE_DISK

; Now, do # of bytes written match # requested to write?

L1:    CMP AX,CX
        JE >L1; OK if equal
        CALL PART_REC; status and close file
        JMP WRITE_DISK

; Now, put the rest of the file:

L1:     MOV AX, [LO_SIZE+BP]; Size in paragraphs
        MOV BX,0
        MOV CX,4
L1:     SHL BX,1; Need to shift BX too, it's a 32 bit shift!
        SHL AX,1; four shifts will give # of bytes
        ADC BX,0; and carries will be # of 64K segments
        LOOP L1

; Now since DOS can't handle 64K at a time, I'll split 
; the 64K chunks into 32K each:

        SHL BX,1; Twice as many

;       Now, BX has # of 32K segments and AX has remainder

        PUSH AX; Save remainder
        MOV DI,0
        CMP BX,0; No 32K segments?
        JE AAN
        
AAO:    PUSH BX
        MOV BX,[HANDLE+BP]
        MOV AX,[STARS+BP]; Starting segment for ramdisk
        ADD AX,DI; DI increments 32K per iteration
        MOV DS,AX
        MOV DX,0; Write data to DS:0000
        MOV AH,040h; WRITE command
        MOV CX,08000h; Write 32K bytes
        INT 021h
        PUSH CS
        POP DS
        JNC >L1

        CALL WRITE_ERROR
        POP BX,AX
        JMP WRITE_DISK

; Now, do # of bytes written match # requested to write?

L1:    CMP AX,CX
        JE >L1; OK if equal
        CALL PART_REC
        POP BX,AX
        JMP WRITE_DISK

;       Now, are there more 32K sections?

L1:    ADD DI,0800h; INC DI by 32K in paragraphs
        POP BX
        DEC BX
        CMP BX,0
        JNE AAO

; Now, put the remainder bytes if there are any:

AAN:    POP CX; This value went on stack as 'AX'
        CMP CX,0; Any remainder?
        JE AAP
        MOV AX,[STARS+BP]
        ADD AX,DI
        MOV DS,AX
        MOV DX,0
        MOV AH,040h
        MOV BX,[HANDLE+BP]
        INT 021h
        PUSH CS
        POP DS
        JNC >L1

        CALL WRITE_ERROR
        JMP WRITE_DISK

; Now, do # of bytes written match # requested to write?

L1:     CMP AX,CX
        JE AAP; OK if equal
        CALL PART_REC
        JMP WRITE_DISK

AAP:    MOV AH,03Eh; Close file
        MOV BX,[HANDLE+BP]

        #IF DEBUGX
        PUSH AX, BX
        MOV AX, BX ; get handle into AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP BX, AX
        #ENDIF

        INT 021h
        MOV B[WRITTN+BP],'N'; REV 1.07 - *** SHOW HAS BEEN SAVED ***
        CALL COLOR_FFIELD
        CALL SU_DSKHDR; Update filename field
        RET; DONE! 10/1/89


;               WRITE_ERROR

; A subroutine to report a write error and then close
; the file of handle @ [HANDLE+BP]

WRITE_ERROR:
        PSTATUS EWF, ATTR10; NOTIFY...
        MOV AH,03Eh;  and close file
        MOV BX,[HANDLE+BP]

        #IF DEBUGX
        PUSH AX, BX
        MOV AX, BX ; get handle into AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP BX, AX
        #ENDIF
        INT 021h
        TICKS 36
        CALL FLUSH
        RET
        
;               PART_REC

; A subroutine to report a partial record has been written,
; and close the affected file of handle @ [HANDLE+BP]

PART_REC:
        PSTATUS PARTW, ATTR10
        MOV AH,03Eh
        MOV BX,[HANDLE+BP]

        #IF DEBUGX
        PUSH AX, BX
        MOV AX, BX ; get handle into AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP BX, AX
        #ENDIF
        INT 021h
        TICKS 36
        RET     

;       INEXTS - init the NEXTS variable:

; create a segment starting at the end of this program
; for the ramdisk area. Assumed free memory above. Could check 
; limit against top of memory found at 2nd word of PSP. W[0002]


INEXTS:
        PUSH CS         ; get program segment
        POP BX
        MOV AX,OFFSET NEXT_SEG ; end of last module
        ADD AX,4        ; padding
        MOV CL,4        ; divide it by 16 for # paragraphs
        SHR AX,CL
        ADD BX,AX        ; create ramdisk segment
        INC BX           ; Plus 1 for safety
        MOV NEXTS, BX   ; save for CRUD routine.
        MOV CX,DS:[02]  ; top of memory in para's
        SUB CX,BX       ; compute amount of free ram
        MOV RAM, CX
        RET

; ZSTARS - a routine to set the STARS variables to 0 to
; indicate no ramdisks have been set up.


ZSTARS: MOV AX,0
        MOV [STARS+BLOCK_SIZE],AX
        MOV [STARS],AX
        MOV [STARS+2*BLOCK_SIZE],AX
        MOV [STARS+3*BLOCK_SIZE],AX
        RET

;       HEADER_0 - sets unused header bytes to 0s
;       to be used before writing file

HEADER_0:
        PUSH CX, AX, SI
        FILL 0, [TUNUSED], 5
        POP SI, AX, CX
        RET

; *******************************************************************
;
;               READ_DSK
;
; This is the subroutine which will load an atari disk image
; from a PC disk file into a ramdisk area. When user chooses
; option 'L' this subroutine is executed.

READ_DSK:
	MOV LOAD_OPT, ' '; 3.17 clear any former /P option
; First, tell the user what info is needed:

        CALL CLR_INFO
        PRINTL RD1
L7:    PSTATUS RD2, ATTR9

; Get disk # from user:

        CALL GET_TORK
        CMP AL, 27; ESC?
        JE >X7
        SUB AL, '1'     ; make AL 0 -7
        CMP AL, 8
        JB >X5
        JAE >X6
X7:   CALL CLR_ALL
        MOV AX, ' ' ; in case calling program expects disk #
        RET; ESC means quit this subroutine     
X6:   PSTATUS VOR, ATTR10
        TICKS 36
        CALL FLUSH
        JMP L7
X5:    ADD AL, '1'     ; convert back to ASCII '1' - '8'
        MOV DISK,AL; Save disk #

;        PUSH AX  ; MOVED TO LOCATION F2 BELOW
;        CURSET C_INFO1
;        PRINTLA SLASHES, ATTR9
;        POP AX

; Now, see if disk already installed. As of REV 3.00 I'm not going
; to allow loading a ramdisk over an existing image! User should
; renumber the existing disk or uninstall it first!

        CALL SET_BP    ; carry clear if disk already exists
        JC >X4

        PSTATUS X10, ATTR10     ; disk already exists!!
        TICKS 45
        JMP READ_DSK

; Now, get filespec

X4:    CALL FIND_MT ; assign an open block and set up BP for it
        JNC > L1
        PSTATUS ALLUSED, ATTR10
        TICKS 50
        CALL UN_DISK
        JMP X7
L1:     MOV AL, DISK
        MOV [N_DISK+BP], AL
        MOV B[N_DISK+BP+1], ':'
;BUG_HERE:
        CMP TAIL_CNT, 0 ; any chars in tail?
        JE > F1
        MOV DI, RFSPEC
        CALL GET_TAILS
        MOV B[DI], 0 ; make it ASCII, DI is pointed to end of string + 1
        JMP  AL1
F1:     CALL LGET_FSPC ; 4.11 - get from file list
        JNC > F2; Carry means ESC pressed
        MOV B[N_DISK+BP], ' '
        MOV B[N_DISK+BP+1], ' '
        JMP X7

; At F2, the file name came from LGET_FSPC, DI is pointing to 1 + the 0
; of the ASCIIZ filename ...
; So now, request the load options and add them to the string ...

F2:     DEC DI
        PUSH DI
        MOV OPT_CNT, 0 ; initialized count of options (2 max)
        CURSET C_INFO1
        PRINTLA SLASHES, ATTR9
        MOV AL, ' '
        CALL PRINT1
        POP DI

F4:     CALL GET_UP
        CALL PRINT1
        INC OPT_CNT
        CMP AL, 13; CR?
        JE > L1
        CMP AL, 'P'
        JNE > F3
F5:     MOV B [DI], '/'
        INC DI
        MOV B [DI], AL
        INC DI
        CMP OPT_CNT, 2
        JNE F4
        JE > L1
F3:     CMP AL, 'N'
        JE F5

        DEC OPT_CNT ; invalid keypresses don't count
        JMP F4

; At L1, we have the filename and load options at RFSPEC, regardless of where
; they came from.

AL1:
L1:     MOV B[LOAD_OPT], ' ' ; assume no load option
        FIND_BYTE RFSPEC, '/', 54
        JNZ >L1; not found
        MOV AL, [DI]; DI points to 1 past '/'
        AND AL, 11011111xB; convert to uppercase

; load option P gets used later in LOAD_OPT, so put any P found there
; load option N gets used to set the "don't clear" bit, so just use it
; that way.  So both load options can be used.

        MOV BYTE PTR [DI-1], 0; pathname must be ASCIIZ cover 1st "/"
        CMP AL, 'P'
        JNE > O1
        MOV [LOAD_OPT], AL
        JE > O2 ; forced branch

O1:     CMP AL, 'N'
        JNE > O2
        OR B[DSK_FLAGS+BP],1; set 'don't clear file' bit

; Now, see if there's a second load option (leading / is optional)

O2:     MOV AL, [DI+1]
        CALL TO_UPPER
O6:     CMP AL, 'P'
        JNE > O3
        MOV [LOAD_OPT], AL
        JE > L1 ; O4 for finished with load options - forced branch
O3:     CMP AL, 'N'
        JNE > O5
        OR B[DSK_FLAGS+BP], 1; set 'don't clear file' bit
        JE > L1 ; forced branch
O5:     CMP AL, '/' ; if leading /, check one more byte
        JNE > L1 ; Not P, N, or /, so no more load options
        MOV AL, [DI+2]
        CALL TO_UPPER
        JE O6 ; forced branch

; Now, we have a filespec, so try to open it...

L1:    MOV DX, OFFSET RFSPEC   ; the filespec string...

        MOV AL,00000010xB       ; Read/WRITE access mode REV 3.00
        MOV AH,03Dh              ; Open file command
        INT 021h                 ; Call DOS

        JNC >L1                ; Carry clear means no error...

; If error opening file:

        PSTATUS EOPF, ATTR10
        MOV AH,0
        ADD AL,'0'; Convert to ASCII digit
        CALL PRINT1
        TICKS 36
        CALL FLUSH
        MOV B[N_DISK+BP], ' '; disk does no longer exist!
        MOV B[N_DISK+BP+1], ' '
        JMP X4

; If file open was OK:

L1:    MOV [HANDLE+BP], AX     ; Save the file handle

        #IF DEBUGX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC field
        #ENDIF

; Now, read in 16 bytes of file header

        READ_FILE [HANDLE+BP], 16, HEADER, K11

        PSTATUS WERF, ATTR10
        TICKS 36
        CALL FLUSH
        MOV B[N_DISK+BP], ' '
        MOV B[N_DISK+BP+1], ' '
        JMP X4

; Is first byte the checksum ATARI identifier?

K11:    MOV W[SEC_PTR+BP],1; in case this ends up as file access
        MOV AX, HEADER; K11 used in macro call above
        CMP AX, CATARI
        JE >L1; Jump if good I.D.

        PSTATUS NATDSK, ATTR10
        TICKS 36
        CALL FLUSH
        MOV WORD PTR [N_DISK+BP], 02020h        ; two spaces

        JMP COUT        ; Close file and re-start


; Now, see if user wants to FIX word in pre-rev 3.00 file:
; 4.11 - this is no longer advertised in instructions

L1:     CMP B[LOAD_OPT], 'X'
        JNE >L1  ; nope
        CMP HI_XSIZE, 0; if already 0, it's no big deal
        JE >L1
        PSTATUS FIXBUG, ATTR10
        CALL GET_TORK
        AND AL, 255 - 32; convert to upper: rev 3.08
        CMP AL, 'Y'
        JNE >L1
        PSTATUS REMIND, ATTR10
        TICKS 54
        MOV HI_XSIZE, 0 ; This fixes it!!!

; This part is to get size 'choice' from actual size in
; paragraphs, to use in status update:

L1:     MOV SIZE_CH, 4  ; assume custom
        CMP HI_XSIZE, 0 ; IF there's a high word, it's custom
        JNE >L1

        MOV AX,CS
        MOV ES,AX
        MOV AX, XSIZE
        MOV DI, OFFSET SIZES
        CLD
        MOV CX,5; THREE DISK SIZES AVAILABLE, PLUS ERROR
        REPNE SCASW; Scan SIZES table, CX = 3 - ENTRY #.
        MOV AL,4
        SUB AL,CL; Now, AX holds choice 0,1,2,3 and 4 if custom.
        MOV SIZE_CH, AL

; Now set up additional information:

L1:     MOV AL, H_FLAGS ; 4.01 - copy flags from header to TABLE
        AND AL, 00110000xB ; at present just look at b4 and b5
        OR [DSK_FLAGS+BP], AL ; transfer copy protect & write protect bits
        MOV AX, XSIZE; Set up info for CRUD routine
        MOV [LO_SIZE+BP],AX
        MOV DX, HI_XSIZE        ; REV 3.00
        MOV HI_SIZE[BP], DX    ;   ""
        MOV BX, SEC_XSIZE
        MOV [SEC_SIZE+BP],BX
        AND B[TYPE_CODE+BP],11111011xB
        MOV CCRUD,'C'; CRUD needs user's choice of density, C or 2...
        CMP BX,128
        JE >L1
        MOV CCRUD,'Z'
        OR B[TYPE_CODE+BP], 00000100xB

; Now set up the max. number of sectors variable, equal
; to size in para's divided by sector size in para's.
; except for double density, add 2 because first 3 sectors
; are always 128 bytes

L1:     MOV CL,4; CAN'T USE SHR BL,4 ON 8086
        SHR BX,CL; Since numerator is in paragraphs **** 11/27/89
        DIV BX; DX:AX/BX, result is in AX and equals max sectors

        MOV [MAX_SEX+BP],AX
        CMP W[SEC_SIZE+BP], 080h; single?
        JE >L1   ; then no adjustment
        ADD W[MAX_SEX+BP], 2

L1:     MOV DX, HI_XSIZE
        MOV AX, XSIZE
        MOV BL, SIZE_CH; CRUD needs SIZE_CH*2 in BX, so...
        MOV BH,0
        SHL BX,1

        CALL ENTRY; Entry point into CRUD when above info set.
        JC >L1; No carry, no error
        CMP B[WRITTN+BP], 'F'
        JNE ADB ; read disk data into RAM for RAM disk
        JMP AAY ; update screen and return for "F" disk

; continue below if CRUD reported an ERROR:

L1:     MOV WORD PTR[SEC_SIZE+BP],080h; Cancel size entry if error
        MOV WORD PTR [LO_SIZE+BP],0; Same
        MOV WORD PTR [N_DISK+BP], 02020h        ; 2 spaces

COUT:   MOV BX,[HANDLE+BP]; ENTRY FOR CLOSE AND RESTART
        MOV AH, 03Eh; Close file

        #IF DEBUGX
        PUSH AX, BX
        MOV AX, BX ; get handle into AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP BX, AX
        #ENDIF
        INT 021h
        JMP X4; Start over

; Now, we're at last ready to move disk info into RAMDISK
; First put the header info into the table:

ADB:    MOV AX, XSIZE
        MOV [LO_SIZE+BP],AX
        MOV CX, SEC_XSIZE
        MOV [SEC_SIZE+BP],CX

; Status disk as NOT written:

        MOV B[WRITTN+BP],'N'
        MOV B[WRITTN+1+BP],','
        CALL COLOR_FFIELD


; Now read in the file into the RAMDISK area:

        MOV BX,0
        MOV CX,4
L1:     SHL BX,1
        SHL AX,1
        ADC BX,0; Add in carry
        LOOP L1

; Now since DOS can't read 64K at a time, break BX into
; 32K sections:

        SHL BX,1

; Now AX holds remainder and BX holds # of 32K blocks

        PUSH AX
        MOV DI,0; Segment counter
        CMP BX,0; First pass: no 32K blocks
        JNE AAW; REV 1.05 HAD TO ADD JMP AAV
        JMP AAV

AAW:    PUSH BX
        MOV BX,[HANDLE+BP]
        MOV CX,08000h; Read 32K
        MOV AX,[STARS+BP]
        ADD AX,DI
        MOV DS,AX
        MOV DX,0
        MOV AH,03Fh; Read from file
        INT 021h
        PUSH CS
        POP DS
        JNC AAX; Jump if no error

        CMP AX,CX
        JAE >L1
        PSTATUS REOF, ATTR10; End of file error
        TICKS 54
        CALL FLUSH
        JMP AAX

L1:     PSTATUS WERF, ATTR10; Other type error
        TICKS 36

AAX:    ADD DI,0800h; Increment to next 32K block
        POP BX
        DEC BX
        CMP BX,0; Any 32K blocks to go?
        JE AAV; Loop back if yes
        JMP AAW

; Now, get the remainder bytes...

AAV:    POP AX
        MOV CX,AX
        MOV BX,[HANDLE+BP]
        MOV AX,[STARS+BP]
        ADD AX,DI
        MOV DS,AX
        MOV DX,0
        MOV AH,03Fh
        INT 021h
        PUSH CS
        POP DS

        JNC AAY; No error

        CMP AX,CX; AX<CX means reached EOF early 
        JAE >L1
        PSTATUS REOF, ATTR10
        TICKS 36
        JMP AAY

L1:     PSTATUS WERF, ATTR10; For other type read errors...
        TICKS 36

AAY:    CALL MOV_FSPEC  ; move RFSPEC to F_SPEC[BP]
        CALL SU_DSKHDR
        CMP B[WRITTN+BP], 'F' ; if file access, don't close
        JE > L2

        MOV BX,[HANDLE+BP] ; 4.11 - added close file, wasn't getting done
        MOV AH, 03Eh; Close file

        #IF DEBUGX
        PUSH AX, BX
        MOV AX, BX ; get handle into AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP BX, AX
        #ENDIF
        INT 021h

        JNC > L2 ; if no error closing file ...
        PSTATUS ECLOSE, ATTR9
        MOV AH,0; AX contains error code
        ADD AL,'0'; Convert to ASCII digit
        CALL PRINT1
        TICKS 50

L2:     RET

; A routine to move the filespec from RFSPEC to the filespec area
; pointed to by F_SPEC[BP]:

; replace MOV_FSPEC with one which gets the whole path.  4.11

COMMENT\

MOV_FSPEC:

; Put the filespec string to the status area of BLOCK:

        PUSH CS
        PUSH CS
        POP ES
        POP DS
        CLD
        MOV CX,54
        MOV DI,OFFSET F_SPEC
        ADD DI,BP
        MOV SI,OFFSET RFSPEC
        REP MOVSB
        RET
ENDOFCOMMENT\

; MOV_FSPEC - 4.11 adapted from GETPATH

MOV_FSPEC:
        FIND_BYTE NEWDRIVE, 0, 67 ;; DI will point to end + 1
L1:     DEC DI
        CMP B [DI], '\' ; search backwards for last \
        JNE  L1
        MOV CX, DI
        SUB CX, NEWDRIVE
        INC CX ; get # of bytes to move

        MOV DI,OFFSET F_SPEC ; want to put string at F_SPEC + BP
        ADD DI,BP
        MOV SI, NEWDRIVE
        CLD
        REP MOVSB

; That gets drive and path and last \, now get filename.  DI should already
; point to proper next location

        MOV SI, RFSPEC ; location of filename only
L2:     MOV AL, B [SI]
        MOV B [DI], AL
        CMP AL, 0 ; was ending 0 just moved?
        JE > L1 ; if so, we're done
        INC SI
        INC DI
        JMP SHORT L2
L1:     RET


; This routine takes the choice for the standard disk size
; (64K, 92K, 143K, 184K) and puts the information into the
; required fields. AL will contain the choice, 0 - 3, on entry.
; The size in paragraphs is returned: low part in AX, high in DX.
; BX contains SIZE_CH * 2 on return

STD_SIZE:

        MOV BH,0
        MOV BL,AL
        SHL BX, 1       ; address word
        PUSH BX
        MOV AX, [SIZES+BX]      ; get size in paragraphs
        MOV DX, 0
        MOV [HI_SIZE+BP], DX
        MOV [LO_SIZE+BP], AX

        MOV BX,[SEC_SIZE+BP]; Sector size; *** 11/27/89
        MOV CL,4; CAN'T USE SHR BL,4 ON 8086
        SHR BX,CL; Since numerator is in paragraphs
        MOV DX,0; high 16 bits of numerator
        DIV BX; DX:AX/BX, result is in AX and equals max sectors

        MOV [MAX_SEX+BP],AX
        CMP W[SEC_SIZE+BP], 080h; single?
        JE >L1   ; then no adjustment
        ADD W[MAX_SEX+BP], 2

L1:     POP BX
        OR b[DSK_FLAGS+BP], 00000010xb; rev 3.06 - start not configurable
        RET

; This routine is called from CRUD and gets information from the
; user as to the desired size of the ramdisk. It gets the size
; in sectors and converts to memory size. *** REV 3.00 ***

CUS_SIZE:
	MOV ERROR, ' '
        CMP AL, 5
        JNE >L2
	CALL SHO_MOR
	JNC AEN
	MOV ERROR, 'E'
        RET

L2:
;        MOV AL, 'Q'; 'Q' is location code
;        CALL PUT_LOC
        MOV ERROR, 'N' ; plan to return no error

        PSTATUS CUSTOM, ATTR9

COMMENT\   REMOVING DOS CALL
        MOV MAXDEC, 6   ; Max # of digits to read; 3.01 made 5 - 6
        MOV AH,0Ah; DOS buffered input function
        PUSH CS
        POP DS
        MOV DX, OFFSET MAXDEC
        INT 021h
        CMP B[DECIMAL],0Dh; just ENTER pressed?
        JNE >L1

ENDOFCOMMENT\

; REV 3.17 uses GET_TORK type input instead of DOS

	MOV CX, 5
	MOV BX, OFFSET DECIMAL
	CALL GET_STR
	JNC > L1


CSIZRT: MOV ERROR, 'E' ; means error; abort command
        RET

L1:	MOV BX, OFFSET DECIMAL
        CALL RANGE10; make sure digits rcvd are OK, carry clear is good
        JC CSIZRT   ; go back if digits are bad
        ; now move digits to printing field:

        SMOVE DECIMAL, USRSZ, 6

        MOV BX, OFFSET DECIMAL
        CALL DECTOINT; convert ASCII number to integer in AX
AEN:    MOV W[MAX_SEX+BP], AX    ; keep max # of sectors

        MOV W[SEC_SIZE+BP], 080h; assumed for now
        AND B[TYPE_CODE+BP], 11111011xB; single density
        CMP AX, 3; 3 is minimum number of sectors allowed
        JA >L1
        MOV W[MAX_SEX+BP], 3
        MOV W[LO_SIZE+BP], (128*3)/16; size in para

L1:     SUB DX, DX; DX will hold high bits of result
        SUB AX, 3; subtract off 3 single density sectors
        CMP CCRUD,'Z'   ; 'Z' means double density
        JNE >L1
        MOV W[SEC_SIZE+BP], 0100h; 256 byte double density
        OR B[TYPE_CODE+BP], 00000100xB; set bit for double
        SHL AX, 1; this, plus 3 shifts below makes 16 para/sector
        ADC DX, 0

; Rev 3.01 change: I was previously shifting only AX and adding
; the carries to DX. DX must be shifted too!

L1:     SHL DX,1
        SHL AX, 1 ; times 2
        ADC DX, 0  ; carry to DX
        SHL DX,1
        SHL AX, 1 ; times 4
        ADC DX, 0
        SHL DX,1
        SHL AX, 1 ; times 8
        ADC DX, 0

; Now, add in the constant # of paragraphs for the first 3
; single density sectors:

        ADD AX, 08h*3   ; 08 is 080h/010h: paras in 1 sector
        ADC DX, 0

; At this point AX holds the low # of paragraphs and DX holds the high

        MOV LO_SIZE [BP], AX ; Update table
        MOV HI_SIZE [BP], DX ; and high part too

;       Return to CRUD

        RET


; SHO_MOR gives the user a list of standard disk sizes to choose from
; and returns with carry clear and # of sectors in AX.

SHO_MOR:
	CALL CLR_SCRN
	PSTATUS ASS_SIZE, ATTR9
	CURSET 0300h
	CMP CCRUD, 'Z'
	JNE >L2
	PRINTL DS_SIZES

; Here, do double density. # of choices is 7

	CALL GET_TORK
	SUB AL, '1'
	CMP AL, 7
        JB >L1
	CALL FIX_SCRN
	STC
	RET

L1:     XOR AH, AH
	SHL AX, 1 ; Point to word
	MOV BX, AX
	MOV AX, [DN_SECTS+BX]
	JMP > L5 ; good exit

; Below, do single density; # of choices is 12

L2:	PRINTL	SS_SIZES
	CALL GET_TORK
	CALL HEXDIGIT ; convert hex digit to number in AL
	DEC AL ; tables 0 based
	JNC > L1
L4:	CALL FIX_SCRN
	STC
	RET

L1:	CMP AL, 11
	JA L4

L1:     XOR AH, AH
	SHL AX, 1 ; Point to word
	MOV BX, AX
	MOV AX, [SN_SECTS+BX]
L5:	PUSH AX
	CALL FIX_SCRN
	POP AX
	CLC
        RET


; This subroutine attempts to set up the 12 byte configuration table
; based on the disk size in paragraphs and density.  If this doesn't
; match a standard, a default is used.

DO_CFINFO:
	SUB SI,SI; counter for byte tables
	MOV AX, [HI_SIZE+BP]
	MOV BX, [LO_SIZE+BP]
	MOV CX, 12; Number of standard sizes to check for single
	MOV DI, OFFSET SPARAS; Assume single density
	TEST B[TYPE_CODE+BP], 4; DOUBLE DENSITY IF b2 SET
	JE >L9
	MOV DI, OFFSET DPARAS
	MOV CX, 7 ; 7 sizes to check for double density

L9:	CMP BX, [DI]; low word
        JNE >L1
	CMP AX, [DI+2]; high word
	JE FOUNDIT
L1:     ADD DI, 4; address next DWORD
	INC SI
	LOOP L9
	JMP NO_MATCH
FOUNDIT:

	TEST B[TYPE_CODE+BP], 4; DOUBLE DENSITY IF b1 SET
	JE >X1 ; go there for single density

	MOV AL, [DSTRACKS+SI]
	MOV [TRACKS_SIDE+BP],AL
	MOV AL,[DSSECS+SI]
	MOV AH,0
	MOV [SECS_TRAK+BP],AX
	MOV AL,[DSSIDES+SI]
	MOV [SIDE_CODE+BP],AL
	JMP > X2 ; normal exit

X1:	MOV AL, [STRACKS+SI]
	MOV [TRACKS_SIDE+BP],AL
	MOV AL,[SSECS+SI]
	MOV AH,0
	MOV [SECS_TRAK+BP],AX
	MOV AL,[SSIDES+SI]
	MOV [SIDE_CODE+BP],AL
X2:	MOV ERR_CHAR,'~'; to tell user std size has been found; 3.07
        CALL PUT_ERCH
	RET

; If non standard, assign all sectors to tracks per sector and
; make # tracks = 1, per MYDOS convention

NO_MATCH:
	MOV AX, [MAX_SEX+BP]
	MOV [SECS_TRAK+BP],AX
        MOV B[TRACKS_SIDE+BP],1
        MOV B[SIDE_CODE+BP],0 ; single side
        RET


; A routine to create a blank disk file in response to a CREATE
; command when the requested disk is too large to be a ramdisk:
; Returns with carry set if user wants to call it all off.

DO_BLANK:
;        MOV AL, 'R'; 'R' is location code for this routine
;        CALL PUT_LOC
        CALL GET_FSPC   ; ASCIIZ pathname is @ RFSPEC on return
        JC ACK;  Escape not pressed if carry clear

; Now, see if file already exists:

L1:
        MOV AH,4Eh; DOS find first match
        MOV CX,00h; Attribute to find normal files & directories
        PUSH CS
        POP DS
        MOV CRITIC, 0; critical error not ignored
        MOV DX,OFFSET RFSPEC
        INT 021h

; Note: carry set on error. AX = 2 means no match found; 3 means
; invalid path ( note AX = 12h means no more files.  How that differs
; from no match, I don't know.)

        JNC >L1; No carry means file already exists
        XOR AX, 010h; to make 012h = 02h ( trim hi nybble of AL )
        CMP AX, 2; No match means ok to create
        JE ACJ
        CMP AX, 3      ; Bad path?
        JNE ACK; if AX not 2 or 3 - must have been critical error
        PSTATUS BADPATH, ATTR10
        TICKS 45
        JMP DO_BLANK
ACK:    STC
        RET


L1:     PSTATUS FARE, ATTR10
        CALL GET_TORK
        AND AL,11011111xB; Uppercase
        CMP AL,'Y'
        JE ACJ
        JMP DO_BLANK

; Now, open file for READ/WRITE:
; Note: function used will CREATE a file, or truncate an existing
; one to zero length.  Leaves open for read/write:

ACJ:    MOV AH, 03Ch; DOS function
        MOV CX, 00h     ; Normal file attribute
        MOV DX, OFFSET RFSPEC
        MOV CRITIC, 0
        INT 021h
        JNC >L1  ; Branch to no error
        PSTATUS EOPF, ATTR10
        MOV AH,0; AX contains error code
        ADD AL,'0'; Convert to ASCII digit
        CALL PRINT1
        TICKS 54
        JMP DO_BLANK

; Now, set up the header and put it:


L1:     CALL HEADER_0   ; zero unused header bytes
        MOV HANDLE[BP], AX      ; Save handle

        #IF DEBUGX
        PUSH AX, BX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP BX, AX
        #ENDIF
        MOV AX, CATARI;        File type identifier
        MOV HEADER,AX
        MOV AX,[LO_SIZE+BP];      Ramdisk size in para's
        MOV XSIZE, AX
        MOV AX,[HI_SIZE+BP]    ; Hi part of above
        MOV HI_XSIZE, AX
        MOV AX,[SEC_SIZE+BP]
        MOV SEC_XSIZE, AX

;       Put 16 bytes:
;       *** NOTE: ASSUMED THAT CREATE FUNCTION ALSO
;       LEFT FILE OPEN FOR WRITE.

        MOV AH,040h; Write
        MOV BX,[HANDLE+BP]
        MOV CX,16
        PUSH CS
        POP DS
        MOV DX,OFFSET HEADER
        INT 021h
        JNC >L1; No error
        CALL WRITE_ERROR; status and close file
ADU:    JMP ACK ; Return with carry set

; Now, do # of bytes written match # requested to write?

L1:    CMP AX,CX
        JE >L1; OK if equal
        CALL PART_REC; status and close file
        JMP ACK

; Now, it's time to fill the file with all blanks

; first see if there's enough ram to do 4K at a time:

L1:     CMP RAM, 0100h; 100h paras = 4K
        JB >L1
        CALL USE_4K
        JC ADU  ; carry set is bad return
        JMP ADT

        CALL Z_SECBUF; Fill SECBUF with all zeroes
        MOV CX, MAX_SEX[BP]; Loop counter: # of sectors

; Loop to fill entire file with 0s:

ATU:    PUSH CX ; Save loop counter
        MOV BX, [HANDLE+BP]
        MOV AH, 040h; Write to file
        MOV CX, SEC_XSIZE; # of bytes to write
        MOV DX, OFFSET SECBUF
        INT 021h
        JNC >L1          ; no error
        CMP AX, CX      ; Check for partial record (disk full)
        JE ACL
        CALL PART_REC
        POP CX  ; Fix stack
        JMP ACK

ACL:    CALL WRITE_ERROR
        POP CX
        JMP ACK

L1:     POP CX
        LOOP ATU

; Now the whole file has been written. Position the file
; pointer to sector #1 and update sector pointer


ADT:    MOV BX, HANDLE[BP]
        MOV AH, 042h    ; Set file pointer
        MOV AL, 00      ; Method: offset from start of file
        SUB CX, CX      ; MSB of offset = 0
        MOV DX, 16      ; Start access with 17th byte
        INT 021h
        JNC >L1
        PSTATUS ESFP, ATTR10    ; Error setting file ptr
        MOV AH,0; AX contains error code
        ADD AL,'0'; Convert to ASCII digit
        CALL PRINT1
        TICKS 54
        JMP ACK
L1:     MOV W[SEC_PTR+BP], 1

        MOV B[WRITTN+BP],'F'      ; Show this is physical file
        MOV B[WRITTN+1+BP], ','  ; access
        CALL COLOR_FFIELD       ; Make the field green



; (some of the code below was copied from WRITE_DISK)

; Now, put pathname in field for this disk:

        PUSH CS
        POP DS
        MOV SI,-1
        MOV CX,54; 54 bytes, max
W19X:   INC SI; Move 54 bytes, or until byte = 0 moved.
        MOV AL,[RFSPEC+SI]
        MOV [F_SPEC+BP+SI],AL
        LOOP W19X

        CALL SU_DSKHDR  ; Update screen
        MOV DUN_BLANK, 0FFh
        CLC             ; Good return
        RET

; USE_4K fills up a disk 4K at a time instead of a sector at a time
; before calling, make sure 4K is available starting at NEXTS

USE_4K:

; first, fill that 4K block with 0's:

        MOV ES, NEXTS   ; starting segment of free memory
        MOV CX, 2048    ; # of words to move
        MOV DI, 0       ; starting offset
        MOV AX, 0       ; word to write into memory
        REP STOSW

        MOV DX, [HI_SIZE+BP]    ; high part of size in paragraphs
        MOV AX, [LO_SIZE+BP]    ; low part
        MOV BX, 0100h
        DIV BX ; gets # of 4K blocks into AX, remainder to DX
        MOV REMAIN, AX      ; actually, saving quotient here...

; first take care of the remainder #:

        MOV CL, 4
        SHL DX, CL      ; remainder is in paragraphs
        MOV CX, DX      ; # of bytes to write
        MOV BX, [HANDLE+BP]
        MOV AH, 040h; Write to file
        MOV DX, 0 ; offset of memory block
        PUSH DS
        MOV DS, NEXTS ; segment
        INT 021h
        POP DS
        JNC >L1          ; no error
        CMP AX, CX      ; Check for partial record (disk full)
        JE ADS          ; other type error
        CALL PART_REC
        POP CX  ; Fix stack
        JMP ADQ

; now write 4K blocks of 0's, CX times

L1:     MOV CX, REMAIN; recover # of 4K blocks
ADR:    PUSH CX ; Save loop counter
        MOV BX, [HANDLE+BP]
        MOV AH, 040h; Write to file
        MOV CX, 4096; # of bytes to write
        MOV DX, 0 ; offset of memory block
        PUSH DS
        MOV DS, NEXTS
        INT 021h
        POP DS
        JNC >L1          ; no error
        CMP AX, CX      ; Check for partial record (disk full)
        JE ADS          ; other type error
        CALL PART_REC
        POP CX  ; Fix stack
        JMP ADQ

ADS:    CALL WRITE_ERROR
        POP CX
        JMP ADQ

L1:     POP CX
        LOOP ADR

; we're done, so do good return:

        CLC
        RET

ADQ:    STC     ; error return
        RET



;       XCHANGE - A routine to swap the ID's of two ramdisks.

XCHANGE:
        PSTATUS TX11, ATTR9
        CALL GET_TORK
        CMP AL,27 ; cancel command?
        JNE > L1
        RET

L1:     CALL RANGE      ; converts '1' to '8' to 0 - 7
        JC XCHANGE; Out of range
        MOV BL, AL; Save first value

L2:     PSTATUS TX12, ATTR9 ; 4.11d: L2 was L1
        CALL GET_TORK
        CMP AL,27
        JNE > L1
        RET

L1:     CALL RANGE
        JC L2 ; 4.11d - this formerly went to L1
        ADD AL, '1'     ; convert them back to ASCII
        ADD BL, '1'

; Now, I have two good values, one in BL and one in AL

        CMP [N_DISK], AL
        JNE >L1
        MOV [N_DISK], BL
        JMP ADG
L1:     CMP [N_DISK], BL
        JNE ADG
        MOV [N_DISK], AL

ADG:    CMP [N_DISK+BLOCK_SIZE], AL
        JNE >L1
        MOV [N_DISK+BLOCK_SIZE], BL
        JMP ADE
L1:     CMP [N_DISK+BLOCK_SIZE], BL
        JNE ADE
        MOV [N_DISK+BLOCK_SIZE], AL

ADE:    CMP [N_DISK+2*BLOCK_SIZE], AL
        JNE >L1
        MOV [N_DISK+2*BLOCK_SIZE], BL
        JMP ADF
L1:     CMP [N_DISK+2*BLOCK_SIZE], BL
        JNE ADF
        MOV [N_DISK+2*BLOCK_SIZE], AL

ADF:    CMP [N_DISK+3*BLOCK_SIZE], AL
        JNE >L1
        MOV [N_DISK+3*BLOCK_SIZE], BL
        JMP XC_OUT
L1:     CMP [N_DISK+3*BLOCK_SIZE], BL
        JNE XC_OUT
        MOV [N_DISK+3*BLOCK_SIZE], AL

XC_OUT: CALL SU_DSKHDR
        RET

; A routine to get the pathname that this program was loaded
; with. The intent is to load a new copy and virus check it.
; REV 3.03 ADDITIONS; Note: CRC calc routines are in NUMBERS.S


GET_PNAME:
        MOV DI, 0
        XOR AX,AX
        MOV ES, DS:[02Ch]; Get segment of environment
L1:     CMP WORD PTR ES:[DI],0; Checking for word 0000, but on
        JE >L2                ; byte boundaries,
	INC DI
        JNZ L1; don't loop forever if not found
L2:     ADD DI, 4; there's another word after the 0 word
	RET     ; ES:DI now points to pathname

V_CHECK:

; did user choose "no check" option?
COMMENT\
; Charley Allen birthday check

        MOV AH, 02Ah; get date function
        INT 021h
        CMP DL, 19 ; greater than or equal 19th of month??
        JB > B1 ; skip if less
        JA > B2 ; don't check time if later than 19th
        MOV AH, 02Ch ; get time function
        INT 021h
        CMP CH, 19 ; later than 7 pm??
        JB > B1 ; skip if the 19th and not later than 7 pm

B2:     PRINTL BDAY
        TICKS 150
        PRINTL BDAY2
        TICKS 150
        PRINTL KEYWAIT
        CALL WAIT4KEY
; End of Charlie Allen Stuff
ENDOFCOMMENT\

B1:    MOV CX, 07Eh   ; # bytes to check
       MOV DI, 081h   ; command tail
L1:     CMP WORD PTR [DI],'V/' ; Checking for word /V, but on
        JE >L2                 ; byte boundaries,
        CMP WORD PTR [DI], 'v/'; also lowercase
        JE >L2
	INC DI
        LOOP L1
        JMP >L3
L2:     RET  ;

; is there enough ram available to read in another copy of SIO2PC?

L3:     MOV AX, (OFFSET NEXT_SEG + 1 - 0100h) ; file length
	MOV CL, 4
	SHR AX, CL; convert to segments
	INC AX; add 1 for safety = # of paras needed
	MOV BX, DS:[02] ; top of memory from PSP
	SUB BX, NEXTS   ; # of paras available
	SUB BX, 010h; I'm going to load at NEXTS:0100
	CMP AX, BX; BX is number of segments available
        JB >L1
	PSTATUS  NO_VMEM, ATTR9
	TICKS 54
	RET

L1:    CALL GET_PNAME; point ES:DI to pathname

; Now, open the file for READ

	MOV DX, DI
	MOV AH, 03Dh
	MOV AL, 0; READ access
	PUSH DS
	PUSH ES
	POP DS
	INT 021h
	POP DS
        JNC >L1
AEE:    PSTATUS EVCF, ATTR9
	RET
; now, read in the file for checking:

L1:

        #IF DEBUGX
        PUSH AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP AX
        #ENDIF

        MOV BX, AX; get handle
	MOV VHANDLE, BX; save handle for close
	MOV CX, 0FFFFh; max bytes to read
	MOV AH, 03Fh; read file
	PUSH DS
	PUSH NEXTS
	POP DS
	MOV DX, 0100h; read file at DS:DX
	INT 021h
	POP DS
	JC AEE

; now check # of bytes read is correct:

	SUB AX, (OFFSET NEXT_SEG + 1 - 0100h)
        JZ >L1
	MOV DI, OFFSET SAY_VLN
	CALL INT2DEC
	PSTATUS VW_LEN, ATTR9
	TICKS 72

L1:
	MOV AH, 03Eh; first, close the file
	MOV BX, VHANDLE

        #IF DEBUGX
        PUSH AX, BX
        MOV AX, BX ; get handle into AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP BX, AX
        #ENDIF
	INT 021h
	CALL DO_CRC
        RET

; Routines to write the correct CRC to the file:


W_CRC:    CALL GET_PNAME; point ES:DI to pathname

; Now, open the file for WRITE:

	MOV DX, DI
	MOV AH, 03Dh
        MOV AL, 1; WRITE access
	PUSH DS
	PUSH ES
	POP DS
	INT 021h
	POP DS
        JNC >L1
	RET


L1:

        #IF DEBUGX
        PUSH AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP AX
        #ENDIF
        MOV BX, AX; get handle
	MOV VHANDLE, BX; save handle for close
        MOV AH, 042h; move pointer
        MOV AL, 00      ; from beginning of file
        MOV CX, 0       ; MSD of offset
        MOV DX, (CRCA-0100h); LSD of offset
        INT 021h

        MOV AH, 040h    ; Write to file
        MOV CX, 2       ; # of bytes to write
        MOV BX, VHANDLE
        MOV DX, CAL_CRCA ; Addr of calculated CRC
        INT 021h        ; Write file


        MOV AH, 03Eh    ; Close file
        MOV BX, VHANDLE

        #IF DEBUGX
        PUSH AX, BX
        MOV AX, BX ; get handle into AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP BX, AX
        #ENDIF
        INT 021h
        RET



; SIM_IO answers all bus commands for the simulated disk

SIM_IO:
	CMP CMND, 'R'
        JE SIM_READ
        CMP CMND, 'S'; status?
        JNE >L1
        CALL STATUS; use existing STATUS routine
        CLC
	RET
L1:     CMP CMND, 'W'
        JE >L1
	CMP CMND, 'P'
        JE >L1
	MOV AL, 'N'; NAK for all other commands
	CALL PUT1
	CLC
	RET

L1:
        CALL PUTSEC; REV. 3.10 pretend to write to SIM disk
        MOV ERR_CHAR,'S'; flag write attempt to SIM disk
	CALL PUT_ERCH
	CLC
	RET

SIM_TOTAL DW 0

SIM_READ:
        MOV BX, T1
	CALL TIMER_0
	MOV AL, 'A'
	CALL PUT1

	MOV AL, CFAUX1
	MOV AH, CFAUX2
	MOV SECTOR, AX
        TEST B[DSK_FLAGS+BP],8; BOOT file?
        JE >L2  ;             jump if not
        CMP AX, 1       ; will point to start of file if sector 1
        JNE >L3
        CALL POINT_0
L3:     CALL GET_125    ; 128, in this case
        JMP SENDSEC
L2:     CMP AX, 361
        JNE >L1
        CALL SET_SIMDIR; SEND THE DIRECTORY SECTOR
	MOV SIM_TOTAL, 0 ; FOR DEBUGGING
	JMP SENDSEC

L1:     CMP AX, 1
        JNE >L6
        CALL DO_SEC1; SEND THE BOOT RECORD - SECTOR 1
	JMP SENDSEC
L6:     CMP AX, 360
        JNE >L1
        CALL DO_360; SEND THE VTOC SECTOR
	JMP SENDSEC

L1:     CALL GET_125
        JMP SENDSEC

; here, we are sending a data sector

SENDSEC:
	MOV BYTE PTR [STATS+2+BP], 0FFh
	MOV BX, T4
	CALL TIMER_0
	MOV AL, 'C'
	CALL PUT1

        MOV BX,T5; *** REV 2.5 - WASN'T ANY DELAY HERE BEFORE ***
	CALL TIMER_0

	MOV BL,0	; checksum
        MOV CX, 128
        MOV SI, 0
L1:     MOV AL, [SECBUF+SI]  ; Get the data byte
	CLC
	ADC BL,AL
	ADC BL,0
	CALL PUT1A	; Send it
        INC SI
        LOOP L1
	MOV AL,BL
	CALL PUT1	; send checksum
;        TEST B[DSK_FLAGS+BP],8
;        JNE >L1
        CMP [VMAX_SEX+BP],0
        JNE >L1

; now, if /N option was chosen as indicated by DSK_FLAGS B2 set
; don't clear out disk, otherwise do...

        TEST B[DSK_FLAGS+BP],4
	JE DO_CLR_SIM
	CALL POINT_0
        JMP >L1
DO_CLR_SIM:
        CALL CLOSE_SIM
	CALL UN_DISK
	CALL SU_DSKHDR
L1:     CLC
	RET

DO_SEC1:
	SMOVE SECTOR_1, SECBUF, 16
	RET

DO_360:
	SMOVE SEC_360, SECBUF, 16
	RET

; SET_SIMDIR puts a simulated directory sector in SECBUF and
; makes sure the file pointer is at the beginning of the file

SET_SIMDIR:
        CALL Z_SECBUF; fill secbuf with 0's
        PUSH DS
        POP ES
        LEA SI, [SIM_ENTRY+BP]
        MOV DI, OFFSET SECBUF
        MOV CX, 16
        REP MOVSB
        CMP B[AT_START], 0
        JE >L1
        CALL POINT_0
        JNC >L1
        PSTATUS ESFP, ATTR10    ; Error setting file ptr
	MOV AH,0; AX contains error code
	ADD AL,'0'; Convert to ASCII digit
	CALL PRINT1
        TICKS 54
        CALL UN_DISK
        STC
        RET

L1:     MOV AX, [MAX_SEX+BP]
	MOV [VMAX_SEX+BP], AX
	CLC
        RET


; GET_125 - read in 125 bytes to SECBUF
; BP must point to data structure of simulated disk
; it also sets up the other 3 bytes for file 0, next sector 100
; rev 3.10: If this is a BOOT disk, get 128 bytes and don't set
; up any pointers ...

GET_125:
	MOV AH, 03Fh
	MOV BX, [HANDLE+BP]
        MOV CX, 128; ASSUME BOOT SECTOR
        TEST B[DSK_FLAGS+BP],8
        JNE >L1
	MOV CX, 125
L1:     MOV DX, OFFSET SECBUF
        INT 021h

; On return, AX = number of bytes read, or if carry set
; AX = 5 for access denied and 6 for invalid handle. If AX < CX, then
; it was a partial read and end of file has been reached.  If carry set
; and AX = 0, EOF was reached before function call ...

        JNC >L3
	PSTATUS ERRRF, ATTR10
        TICKS 55
        STC     ; bad return
        RET
L3:     TEST B[DSK_FLAGS+BP], 8
        JNE >L1; if BOOT file; 3.11
L2:     CALL S_SNXT ; GET NEW SEC # IN BX
        MOV [SECBUF+125],BH
        MOV [SECBUF+126],BL
	MOV [SECBUF+127], AL
	CMP AL, 125     ; end of file/partial sector?
        JE >L1
        MOV B[SECBUF+126], 0; pointer of 0 means last sector
        MOV B[SECBUF+125],0

; NOTE FOR BELOW: SHOULD AT_START BE PART OF STRUCTURE [BP]?

L1:     MOV B[AT_START], 0; file pointer not at start of file
	DEC [VMAX_SEX+BP]
	INC SIM_TOTAL
	JNE >L1
	TEST B[DSK_FLAGS+BP],8; 3.11
        JNZ >L1
        MOV B[SECBUF+126], 0; just in case last sector was 125 bytes
; commemted out below line - 4.02 2/26/95
;	 MOV B[SECBUF+125], 0; added with rev 4.01 ... word pointer ...
	 AND B[SECBUF+125], 11111100xB ; bits 0 and 1 are part of pointer 4.02
L1:     CLC     ; good return
        RET

; INCREMENT SEC # AND PUT SIMULATED SECTOR # IN BX

S_SNXT:
        MOV BX, SECTOR
        INC BX;      SECTOR # WILL GO FROM 401 TO 700
        CMP BX, 701; AND START OVER
        JNE >L1
        MOV BX,401
L1:     RET

; Close "simulated" disk file

CLOSE_SIM:
        MOV BX, [HANDLE+BP]
        MOV AH, 03Eh

        #IF DEBUGX
        PUSH AX, BX
        MOV AX, BX ; get handle into AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP BX, AX
        #ENDIF
        INT 021h
	RET

; Move the file pointer to the beginning of the file

NO_PTS DB 0 ; DEBUGGING: HOW MANY TIMES POINTED TO START?

POINT_0:
	MOV AH, 042h
	MOV AL, 00
	MOV BX, [HANDLE+BP]
	MOV DX, 0
	MOV CX, 0
	INT 021h
        MOV B[AT_START], 0FFh
	INC NO_PTS
	RET

; Get the size of the file in "sectors", returned in AX

GET_125S:
	MOV AH, 042h; move pointer
	MOV AL, 02; from end of file
	XOR CX, CX
	XOR DX, DX
	MOV BX, [HANDLE+BP]
	INT 021h
        JNC >L1
	RET
L1:     MOV BX, 125; divide DX:AX by 125; assume DOS type SIM DISK
        TEST B[DSK_FLAGS+BP],8
        JZ >L2
        MOV BX, 128     ; BOOT SIM disk; REV 3.11
L2:     DIV BX; quotient to AX, remainder to DX
	CMP DX, 0
        JE >L1; no partial sectors
	INC AX
L1:
	CLC
	RET



; INST_PC - installs a PC file to be read as a simulated Atari
; disk image.

INST_PC:
L7:    PSTATUS RD2, ATTR9

; Get disk # from user:

	CALL GET_TORK
	CMP AL, 27; ESC?
        JE >X7
	SUB AL, '1'     ; make AL 0 -7
	CMP AL, 8
        JB >X5
        JAE >X6
X7:   CALL CLR_ALL
	MOV AX, ' ' ; in case calling program expects disk #
	RET; ESC means quit this subroutine
X6:   PSTATUS VOR, ATTR10
	TICKS 36
	CALL FLUSH
        JMP L7
X5: ADD AL, '1'     ; convert back to ASCII '1' - '8'
	MOV DISK,AL; Save disk #

; Now, see if disk already installed. As of REV 3.00 I'm not going
; to allow loading a ramdisk over an existing image! User should
; renumber the existing disk or uninstall it first!

	CALL SET_BP    ; carry clear if disk exists
        JC >X4

	PSTATUS X10, ATTR10     ; disk already exists!!
	TICKS 45
	JMP INST_PC



; Now, get filespec

X4:    CALL FIND_MT
        JNC >L1
	PSTATUS ALLUSED, ATTR10
	TICKS 50
        JMP X7

L1:    CALL CLR_INFO  ; 4.11 moved these lines from start of routine to
       PRINTL RD1     ; here to get options after disk number
       INFO2          ; "
       PRINTL SIM_DISK ; "
       AND B[DSK_FLAGS+BP], 0FFh-4-8; clr bit 2&3 (default)
       OR B[DSK_FLAGS+BP], 2    ; make SIM disks non-configurable

        MOV AL, ' ' ; print a space
        CALL PRINT1
        CMP TAIL_CNT, 0
        JNE > L1 ; if chars in tail, get from there ...

Q4:    CALL GET_UP ; get uppercase from GET_TORK
       CMP AL, 13 ; Enter pressed?
       JE > L1

L2:     CMP AL, 'B'; BOOT FILE?
        JNE > L4
        CALL PRINT1
        OR B[DSK_FLAGS+BP], 8   ; set "boot file" bit

L4:     CMP AL, 'N'
        JNE Q4
        CALL PRINT1
        OR B[DSK_FLAGS+BP], 4    ; set "don't clear" bit
        JMP Q4; GET NEXT OPTION

L1:     MOV AL, DISK
	MOV [N_DISK+BP], AL
        MOV B[N_DISK+BP+1], ':'
;        CALL GET_FSPC ; REV 4.11
; Here, see if bytes are available in command tail - If so, get file
; name and options from there, otherwise use FILELIST:

D1:     CMP TAIL_CNT, 0 ; tail empty?  (Note: D1 is separate entry point)
        JE > F1
        MOV DI, RFSPEC ; GET_TAILS puts string in [DI]
        CALL GET_TAILS ; get filename from tail
        JMP > L1 ; get any options (must follow name/no space)
;        MOV B [DI], 0 ; make it ASCIIZ - later


F1:     CALL FILELIST ; REV 4.11
        JNC > N7; Carry means ESC pressed; skip old style of entry since
                ; file name came from FILELIST

        MOV B[N_DISK+BP], ' '
        MOV B[N_DISK+BP+1], ' '
        JMP X7

L1:     ;AND B[DSK_FLAGS+BP], 0FFh-4-8; clr bit 2&3 (default)

BUG_ME:
	FIND_BYTE RFSPEC, '/', 54
        JNZ > N7; not found

        MOV B[DI-1], 0 ; 3.10: file must be ASCIIZ
A5:     MOV AL, [DI]; DI points to 1 past '/'
        CALL TO_UPPER
        CMP AL, 'B'
        JE > L2
        CMP AL, 'N'
        JE > L6
        JNE > N7 ; if neither option is any good ...
L4:     INC DI
        CMP B [DI], '/' ; another slash - another option?
        JNE > N7
        INC DI ; and get next
        JMP A5
L6:     OR B[DSK_FLAGS+BP], 4    ; set "don't clear" bit
        JMP L4; GET NEXT OPTION
L2:     OR B[DSK_FLAGS+BP], 8   ; set "boot file" bit
        JMP L4

        ;OR B[DSK_FLAGS+BP], 2    ; make SIM disks non-configurable
        ; 4.11 moved above line up

; Rev 3.05: set up /N option meaning don't clear disk when read
; is complete; reset pointer to start instead:

; Now, we have a filespec, so try to open it...

N7:     MOV DX, OFFSET RFSPEC   ; the filespec string...

        MOV AL,00000000Xb         ; Read access
	MOV AH,03Dh              ; Open file command
	INT 021h                 ; Call DOS

        JNC >L1                ; Carry clear means no error...

; If error opening file:

	PSTATUS EOPF, ATTR10
	MOV AH,0
	ADD AL,'0'; Convert to ASCII digit
	CALL PRINT1
	TICKS 36
	CALL FLUSH
        MOV B[N_DISK+BP], ' '; disk does no longer exist!
        MOV B[N_DISK+BP+1], ' '
        JMP X4

; If file open was OK:

L1:

        #IF DEBUGX
        PUSH AX
        CALL BYTE2HEX
        MOV AL, BH
        CALL PUT_LOC ; put handle to LOC FIELD
        POP AX
        #ENDIF

        MOV [HANDLE+BP], AX     ; Save the file handle
        MOV B[WRITTN+BP], 'S'    ; Flag "simulated" disk
        MOV W[SEC_SIZE+BP], 080h
	CALL GET_125S; Get # of sectors
	MOV [MAX_SEX+BP], AX
	MOV [VMAX_SEX+BP], AX
        MOV B[AT_START], 0FFh; file pointer is at start of file

	CALL MOV_FSPEC  ; Move RFSPEC TO F_SPEC[BP]

; now, add /B & /N chars to status line if needed ...
; DI should be pointing to the right place due to MOV_FSPEC
; DI is pointing to 0 at end of string if it came from FILELIST

        CMP B [DI], 0
        JNE > B2 ; don't add if string came from cmnd tail
        INC DI
        MOV AL, 8
        AND AL, B[DSK_FLAGS+BP] ; boot file bit set?
        JZ > B1
        MOV B[DI], '/'
        INC DI
        MOV B[DI], 'B'
        INC DI
B1:     MOV AL, 4
        AND AL, B[DSK_FLAGS+BP]
        JZ > B2
        MOV B[DI], '/'
        INC DI
        MOV B[DI], 'N'

B2:     CALL SA_PCFILE ; 4.11


; I don't see what good any of the rest of this junk does, so ...
; YOU KNUCKLEHEAD!!  This puts the simulated file's filename in the
; simulated sector so the Atari can ask for a directory and see the
; filename.  4.13 put this back in!!  (Removed the COMMENT commands)

;COMMENT\

	MOV CX, 11      ; clear Atari filename field
	LEA DI, [SIM_FN+BP]
L1:     MOV BYTE PTR [DI], ' '
	INC DI
        LOOP L1

; extract filename from pathname:

	CALL GET_FILENAM; returns with DI pointing to filename

        LEA SI, [SIM_FN+BP]
        PUSH DS
	POP ES

B1:     MOV AL, [DI]
        CMP AL, '/'; start of option
        JE >L1
        CMP AL, 0; end of ASCIIZ
        JE >L1
        CMP AL, '.'; start extension
	JE DO_EXT
	CALL TO_UPPER
	MOV [SI], AL
        INC DI
        INC SI
        JMP B1

DO_EXT: LEA SI, [SIM_FN+BP+8]
	INC DI
        JMP B1
L1:     LEA DI, [N_DISK+BP+3]
	PUSH DS
	POP ES
	MOV SI, OFFSET A_PCFILE
	MOV CX, 7
	CLD
	REP MOVSB

; REV 4.02 below

;	 PSTATUS QSIM_SPAR, ATTR9 ;; SPARTADOS FORMAT?


;	 CALL GET_TORK
;	 CALL TO_UPPER
;	 CMP AL, 'Y'
;	 JNE > S1
;	 TEST B[DSK_FLAGS+BP], 00001000xB ; bit 3 means boot disk
;	 JNZ > S1 ; can't be both boot disk and SPARTA disk ...
;	 OR B[DSK_FLAGS+BP], 01000000xB ; flag this is SPARTA disk

;ENDOFCOMMENT\

S1:	CALL SU_DSKHDR

        RET

; SA_PCFILE puts the string "PC FILE" in the string area which normally
; shows number of sectors, etc.

SA_PCFILE:
        LEA DI, [N_DISK+BP+3]
	PUSH DS
	POP ES
	MOV SI, OFFSET A_PCFILE
	MOV CX, 7
	CLD
	REP MOVSB
        RET
; GET_FILENAM searches RFSPEC and returns pointing just to the
; first character of the name, not the path:

GET_FILENAM:
        PUSH DS
        POP ES
        MOV DI, OFFSET RFSPEC
        ADD DI, 54; point to high end
        STD     ; search in downward direction
        MOV AL, '\'; byte to search for
        MOV CX, 55
        REPNE SCASB
        JNE >L1
        INC DI  ; if '\' was not found, need only one INC
L1:     INC DI
	CLD; fix direction flag back
        RET; DS:DI (ES:DI) now points to start of name - 1.

