COMMENT\ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ ³ File 1050.S is part of SIO2PC. It contains the major routines ³ ³ used for the 1050-2-PC function which allows connecting the ³ ³ PC to an Atari drive directly. Contains subroutines: ³ ³ ³ ³ D_GETS; D_STATUS; D1050; D_OPENF; D_BHDR; D_CK_MEM ³ ³ D_PART_REC; D_WRITE_ERR; D_CLOSF; D_SCAN; BAD_CHECK ³ ³ GET_HI_LO; D_FORMAT; D_WRITE; WRITE_810 ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ENDOFCOMMENT\ ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ ³ ; ³ D_GETS reads a sector from an Atari disk drive via the ³ ; ³ 1050-2-PC hardware: ³ ; ³ ³ ; ³ Caller will have the sector # in BX. Data is put to SECBUF ³ ; ³ # of bytes per sector must be in D_SIZE ³ ; ³ ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ D_GETS: PUSH ES, AX, BX, CX, DX, DI, SI FILL '_', E_1050, 6 MOV B[E_1050+3], '$' CALL Z_SECBUF MOV DX, LSR IN AL, DX CALL ACC_DLY ; 4.15 added MOV DX, BAUD IN AL, DX CALL ACC_DLY ; 4.15 XCHG BH, BL MOV W[CFAUX2], BX CALL RTS_DN ; lower the command line MOV BX, DT1 ; for 750 us delay CALL TIMER_0 MOV AL, D_ID MOV DEVID, AL MOV AL, 'R' ; indicate COMMAND and calculate checksum of frame MOV CMND, AL ADD AL, DEVID ADC AL, CFAUX1 ADC AL, CFAUX2 ADC AL, 0 ; get last carry MOV CFCKSM, AL ; now, ready to send command frame MOV CX, 5 MOV BX, 4 L1: MOV AL, CFCKSM[BX] CALL PUT1A DEC BX LOOP L1 MOV BX, DT1 ; delay before raising command line CALL TIMER_0 CALL RTS_UP ; now, wait for the drive to send an ACK ... CALL GETSLO JNC > N1 ; carry means timed out in GETSLO MOV B[E_1050], 'T' ; signify timed out waiting for ACK JC > L2 N1: MOV B[E_1050], AL ; assume ACK CMP AL, 'A' JE > R2 ; I'll only record 'T', 'A', or 'N' for ACK - MOV B[E_1050], 'N'; no jibberish allowed ... JMP NEAR > L2 ; now, wait for a COMPLETE ... R2: CALL GETSLO JNC > N2 ; MOV B[E_1050+1], 'T' JC > L2 N2: MOV B[E_1050+1], AL ; assume CPT reported CMP AL, 'C' JE > Q2 MOV B[E_1050+1], 'E' ; I'll accept T or C or assume E for CPT JMP NEAR > L2 ; now, read in a sector's worth of data ... Q2: MOV CX, D_SIZE ; Get # of bytes into CX MOV SI,0 MOV AH,0 ; AH will be checksum J3: CALL GET1 ; read a byte from port JC >L2 ; carry set = timed out L1: CLC ADC AH, AL ; compute checksum ADC AH, 0 MOV [SECBUF+SI], AL ; store data INC SI LOOP J3 ; Now, get one more byte (checksum) CALL GET1 JNC > Z2 ; MOV B[E_1050+2], 1 Z2: MOV B[E_1050+4], AH ; calculated checksum MOV B[E_1050+5], AL ; received checksum MOV B[E_1050+2], 'G'; assume good checksum CMP AH, AL JE > L1 MOV B[E_1050+2], 'B' L2: CALL BEEP ; 4.15 STC ; error exit location JMP NEAR > L3 L1: CLC ; good exit CALL CLICK L3: POP SI, DI, DX, CX, BX, AX, ES RET ; finished ; ******************************************************************* ; D_STATUS requests and gets status from the real Atari drive ; via 1050-2-PC ; Puts status bytes received in 4 byte frame starting ; at D_STATS (in reverse order). Puts 0,0,0,0 if command didn't go ; to proper completion, and sets carry, else carry is cleared. D_STATUS: PUSH AX, BX, CX, DX, SI MOV W[D_STATS], 0 ; initialize status to all 0's MOV W[D_STATS+2], 0 MOV DX, LSR ; clear input registers ... IN AL, DX CALL ACC_DLY MOV DX, BAUD IN AL, DX CALL ACC_DLY ; 4.15 CALL RTS_DN ; lower command line MOV BX, DT1 CALL TIMER_0 MOV AL, D_ID MOV DEVID, AL MOV AL, 'S' ; get status MOV CMND, AL ADD AL, DEVID ADC AL, CFAUX1 ; note that CFAUX1 & 2 are "don't care" ADC AL, CFAUX2 ; except for checksum calc ADC AL, 0 MOV CFCKSM, AL ; now, command frame is ready to send MOV CX, 5 MOV BX, 4 L1: MOV AL, CFCKSM[BX] CALL PUT1A DEC BX LOOP L1 MOV BX, DT1 ; delay before raising command line CALL TIMER_0 CALL RTS_UP ; now, wait for the drive to send an ACK ... CALL GETSLO JC > P1 ; carry means timed out in GET1 CMP AL, 'A' JE > L2 ; MOV B[E_1050], 'A' ; now, wait for a COMPLETE ... L2: CALL GETSLO JC > P1 CMP AL, 'C' JE > L2 ; MOV B[E_1050+1], 'C' ; now, read in the status bytes ... L2: MOV CX, 4 ; Number of status bytes is four ... MOV SI, 3 MOV AH, 0 ; AH will be checksum J3: CALL GET1 ; read a byte from port JC > P1 ; carry set = timed out L1: CLC ADC AH, AL ; compute checksum ADC AH, 0 MOV [D_STATS+SI], AL ; store data DEC SI LOOP J3 ; Now, get one more byte (checksum) CALL GET1 JC > P1 ; MOV B[E_1050+2], 1 L2: MOV B[E_1050+4], AH ; calculated checksum MOV B[E_1050+5], AL ; received checksum CMP AH, AL JNE > P1 ; bad checksum CLC ; flag good result - status obtained P2: POP SI, DX, CX, BX, AX RET ; finished P1: MOV W[D_STATS], 0 ; clear to indicate there was an error MOV W[D_STATS+2], 0 ; with the status command STC ; flag bad result (no status obtained) JMP P2 ; ******************************************************************* ; D1050 is the main shell for the 1050-2-PC routines. It is called by ; action 'D' of the TIMINGS menu and returns to there ... D1050: PUSH W[D_STLINE] ; save status of status line ... D1051: CALL RTS_UP ; get command line up ... (reentry point) CMP D_STLINE, 0 ; means not doing status line now ... JNE > L1 ; don't toggle if already off CALL TOG_STAT L1: CALL CLR_SCRN FILL 0,LBLHDR, 16 ;; zero out 16 bytes of file header MOV AX, CATARI ; SIO2PC file type identifier MOV HEADER, AX CALL LINEFEED PRINTL HEAD1050 CALL GET_UP CMP AL, '1' ; copy 720 sector disk to file? JE > L1 CMP AL, '4' ; user specify range ? JNE > E1 CALL GET_HI_LO JC D1051 ; at this point, sector range is set up, so go get them ... MOV D_TRACKS, 18 ; Temporary assumption, changed if 1050 drive *** JMP NEAR > E2 E1: CMP AL, '6' ; 4.01 - scan sectors, report status JNE > E5 CALL D_SCAN JMP D1051 E5: CMP AL, '2' ; do 1040 sector 1050 disk? JNE > E6 ; 4.05 - was to E3 MOV D_TOTAL, 1040 MOV D_TRACKS, 26 MOV D_LAST, 1040 JMP NEAR > E4 ; forced branch into choice '1' routine ... E6: CMP AL, '3' ; real test for 3: write disk image - (4.05) JNE > E3 CALL D_WRITE JMP NEAR D1051 E3: CMP AL, '5' ; Format disk??? JNE > E7 PRINTL RD2 CALL LINEFEED CALL GET_UP CMP AL, '1' JB > E8 CMP AL, '8' JA >E8 MOV DEVID, AL CALL D_FORMAT E8: JMP NEAR D1051 E7: CMP AL, 'X' ; Quit the D1050 function?? JNE E8 JMP NEAR Q3 L1: MOV D_TOTAL, 720 ; save total number of sectors, from choice #1 MOV D_TRACKS, 18 MOV D_LAST, 720 E4: MOV D_1ST, 1 ; Note: Choice #2 enters here ... revise carefully MOV B[WANT_CPTK], 'N' ; assume NO to below PRINTL DO_CPTK ; user wants to simulate copy protection?? CALL GET_UP CMP AL, 'Y' JNE > E2 MOV [WANT_CPTK], AL OR H_FLAGS, 00010000xB ; flag as copy protected PRINTL W_WPRTK ; write protect?? CALL GET_UP CMP AL, 'Y' JNE > E2 OR H_FLAGS, 00100000xB MOV BAD_1ST, 0 ; initialize 1st bad sector info/flag MOV GUD_FOUND, 'N' ; init flag that a good sector hasn't been found MOV W[DG_STATUS], 02000h ; init standard good status MOV W[DG_STATUS+2], 010FFh E2: CALL DGET_FSPC JNC > X4 JMP D1051 ; start over if error ... X4: CALL D_OPENF ; ask if user wants SIO2PC format or plain disk image ... ; if he asked for copy protection, he must take SIO2PC format ... MOV AD_TYPE, '1' ; assume copy prot is wanted CMP B[WANT_CPTK], 'Y' JE > X1 PRINTL QD_TYPE CALL GET_UP MOV AD_TYPE, AL ; save user's choice of type CMP AL, '2' ; 2 means PLAIN format JE > P1 CMP AL, '1' JE > X1 X2: JMP D1051 X1: CALL D_BHDR ; put a blank header (16 bytes) to file handle JC X2 ; restart if error ; now time to read in the bytes and put them to the file ... ; First, see if enough memory to buffer a track ... P1: CALL D_CK_MEM ; carry clear means OK JC X2 MOV XSIZE, 0 ; disk size in paragraphs, for header PRINTL SA_RDSEC MOV D_CHOSE, ' ' ; reset choice on disk error Q1: CALL GD_TRACK ; get up to 1 track's worth of sectors ; carry set means user abort via ESCAPE key ... JNC > K2 ; 4.02 PRINTL USR_QUIT TICKS 36 ; CMP AL, AL ; JNZ > K2 ; 4.05 - user quit formerly went back to main SIO2PC CALL D_CLOSF JMP NEAR D1051 ; back to menu K2: CALL D_W_TRK ; write information to open file MOV CL, 4 SHR DI, CL; convert number written to paragraphs ADD XSIZE, DI ; keep running total in header CMP D_1ST, 0 JNE Q1 ; more to read ... ; still need to finish out the header, if one was requested ... CMP AD_TYPE, '2' ; plain? JE > Q2 MOV AX, D_SIZE MOV SEC_XSIZE, AX MOV HI_XSIZE, 0; assume not more than 16K * 16 paragraphs ; set file pointer to beginning of file MOV BX, D_HANDL MOV AH, 042h ; Set file pointer MOV AL, 00 ; Method: offset from start of file XOR CX, CX ; MSB of offset = 0 MOV DX, CX ; Start access with 1st byte INT 021h JNC >L1 PRINTL ESFP ; Error setting file ptr MOV AH,0; AX contains error code ADD AL,'0'; Convert to ASCII digit CALL PRINT1 CALL LINEFEED TICKS 54 xbug_off: nop ; put the 16 byte header L1: CALL D_BHDR ; write the 16 byte header Q2: CALL D_CLOSF ; close the file, we're done CALL LINEFEED CMP TAIL_CNT, 0 ; 4.14 - if command tail is not empty, then user JNE > Q3 ; is running from cmnd line - don't prompt for key PRINTL D_DONE CALL WAIT4KEY Q3: POP W[D_STLINE] ; restore status line status ... RET ; ******************************************************************* ; A routine to create/open the file named at RFSPEC D_OPENF: ; 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 [D_HANDL],AX; First, save handle CALL LINEFEED PRINTL FARE CALL GET_UP AND AL,11011111xB; Uppercase CMP AL,'Y' JNE > N1 JMP > N5 N1: MOV BX,[D_HANDL] MOV AH,03Eh; Close file INT 021h STC ; signify error JC > N2 L1: CMP AX,02; ERROR 2 means file doesn't exist, JE >L1; so, go down and create it @ W6. PRINTL EOPF; If other error, print error msg MOV AH,0; and retry. ADD AL,'0'; Convert to ASCII digit CALL PRINT1 CALL LINEFEED TICKS 36 STC JC > N2 ; 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 > N3; *** REV 2.4: THIS STMT WAS AFTER NEXT STMT*** MOV [D_HANDL],AX; Save handle JMP > N4 ; good status exit N3: PRINTL ECF MOV AH,0 ADD AL,'0'; Convert to ASCII digit CALL PRINT1 TICKS 36 CALL LINEFEED N5: STC JC > N2 N4: CLC N2: RET ; ******************************************************************* ; D_CLOSF closes the file opened by D_OPENF D_CLOSF: MOV BX, D_HANDL MOV AH, 03Eh ; Close file INT 021h JNC >L1 PRINTL ECLOSE MOV AH,0; AX contains error code ADD AL,'0'; Convert to ASCII digit CALL PRINT1 TICKS 50 CALL LINEFEED L1: RET ; ******************************************************************* ; Put a 16 byte header to file. D_BHDR: ; Put 16 bytes: ; *** NOTE: ASSUMED THAT CREATE FUNCTION ALSO ; LEFT FILE OPEN FOR WRITE. MOV AH,040h; Write MOV BX,D_HANDL MOV CX,16 PUSH CS POP DS MOV DX,OFFSET HEADER INT 021h JNC >L1; No error CALL D_WRITE_ERR; status and close file ; Now, do # of bytes written match # requested to write? L1: CMP AX,CX JE >L1; OK if equal CALL D_PART_REC; status and close file E1: STC RET L1: CLC RET ; ******************************************************************* D_WRITE_ERR: PRINTL EWF CALL LINEFEED MOV AH,03Eh; and close file MOV BX,D_HANDL INT 021h TICKS 36 RET ; ******************************************************************* ; PART_REC ; A subroutine to report a partial record has been written, ; and close the affected file of handle @ [HANDLE+BP] D_PART_REC: PRINTL PARTW CALL LINEFEED MOV AH,03Eh MOV BX,D_HANDL INT 021h TICKS 36 RET ; ******************************************************************* ; Check that there is enough memory to hold a complete ; track. I'm checking 26 sectors/track only since most likely there ; will always be enough memory. D_CK_MEM: PUSH AX, CX MOV AX, D_SIZE ; # of bytes/sector MOV CL, 4 SHR AX, CL ; divide by 16, I want paragraphs MUL D_TRACKS ; multiply AL X 26, result -> AX ADD AX, NEXTS; Check new top against memory size. ; Note below: QUICK ASSEMBLER requires segment modifier (even in ; default segment) when using an absolute address (offset). CMP AX,DS:[02]; PSP memory top JNAE > C1 ; AE means AX > memory top PRINTL D_NMEM TICKS 40 STC JMP NEAR > C2 C1: CLC C2: POP CX, AX RET ; ******************************************************************* ; GD_TRACK gets in up to a whole track of sectors. Variables ; D_1ST and D_LAST define the start and end of the total desired ; This routine will update (increment) D_1ST. D_TRACKS must also hold the ; total number of sectors per track. ; On return, D_1ST holds the next sector needed, or = 0 if all ; complete. DI contains the number of bytes read, stored at ; NEXTS:0000. ; Checks for user keypress after each sector operation, and returns ; carry set if user pressed ESCAPE. GD_TRACK: PUSH ES, CX, SI MOV D_AS_CT, 0 ; initialize counter for print sector numbers MOV BX, 1 ; incr BX until it equals desired sector LL6: MOV CX, W[D_TRACKS] ; and CX keeps track of place in TRACK LL2: CMP D_1ST, BX JE LL1 INC BX LOOP LL2 JMP NEAR LL6 ; reset to next track ; now BX = D_1ST and CX is in proper position to count down end of TRACK LL1: MOV DI, 0 MOV ES, NEXTS LL3: MOV D_RETRY2, 3 ; max number of retries CMP D_CHOSE, '3' ; did user say ignore rest of track? (4.05) JE > K9 ; if yes, don't bother to try to read ... CMP D_CHOSE, '4' ; same for 4 - ignore rest of disk JE > K9 CALL IS_A_KEY ; did user press a key? ; 4.02 JNC > K1 CMP AL, 27 ; was it ESCAPE? JNE > K1 STC JNC > K1 JMP NEAR LL5 K1: CALL D_PUTSN ; put the sector number to screen ... CALL D_GETS ; read in sector #BX to SECBUF JC > V9 ; 4.02 up till label K2 4.05 - was JNC > K2 ; at this point, we just read a sector with good status reported, so ; if this is to be copy protected AND good status has not yet been ; recorded, then get status and store at DG_STATUS ; rev 4.05 here to V9 CMP B[WANT_CPTK], 'Y' ; are we doing copy protection? JNE > K9 CMP GUD_FOUND, 'Y' ; already got status? JE > K9 CALL D_STATUS ; reads status to D_STATS MOV AX, W[D_STATS] ; copy it to DG_STATUS MOV W[DG_STATUS], AX MOV AX, W[D_STATS+2] MOV W[DG_STATUS+2], AX MOV GUD_FOUND, 'Y' K9: JMP NEAR > K2 V9: CMP D_RETRY2, 0 ; 0 means no more retries ... JNE > K4 F3: CALL SET_BADS ; put bad sector info into into SECBUF CALL LINEFEED PRINTL D_EXCEED ; Revision 4.05 will give a choice when bad sector read is encountered ; instead of automatically aborting the operation ... CALL GET_TORK ; get user's choice on error ... HOLDIT: MOV D_CHOSE, AL CMP AL, 27 ; ESCAPE KEY? JE > F1 CMP AL, '1' JNE > F2 MOV D_CHOSE, 27 ; choice 1 is same as escape key JE > F1 F2: CMP AL, '2' ; means write dummy data for bad sector and JE > K2 ; treat same as if good read just happened CMP AL, '3' ; 3 means skip rest of track JE > K2 ; CMP AL, '4' ; skip rest of disk? JNE F3 ; choice must be 1,2,3,4, or ESC JE > K2 ; same as '2' except persistent for rest of track F1: STC JC LL5 K4: DEC D_RETRY2 ; retry count not exceeded; tell user about error CALL LINEFEED PRINTL SA_RETRY CMP AL, AL JZ K1 ; retry ... ; CALL IS_A_KEY ; key waiting MOV D_AS_CT, 0 ; reset sector # print routine's count ; JNC K1 ; retry if user didn't press key ; CMP AL, 27 ; ESCAPE key? ; JNE K1 ; STC ; JC LL5 ; return with carry set to abort ... K2: MOV SI, SECBUF ; trap errors here on carry ... ; now move from secbuf to memory block starting at NEXTS:0000 PUSH CX MOV CX, D_SIZE ; number of bytes to move SHR CX, 1 ; make move in words, not bytes, since it's even CLD REP MOVSW ; automatically advances DI & SI POP CX ; ADD DI, D_SIZE ; increment destination by sector size CMP D_LAST, BX ; see if we just got the last one? JNE LL4 MOV D_1ST, 0 ; flag last has been read JMP NEAR LL7 LLL3: JMP LL3 ; fix "long loop"; [4.05] LL4: INC BX ; next sector number LOOP LLL3 ; get another sector, if still within same track MOV D_1ST, BX ; otherwise, mark next sector to get LL7: CLC LL5: POP SI, CX, ES PUSHF ; save status of carry CMP D_CHOSE, '4' ; don't clear if '4' - skip rest of disk JE > Y1 MOV D_CHOSE, ' ' ; reset -- 4.05 Y1: POPF RET ; at this point, all sectors in current track up to max requested have ; been read. The total number of bytes read is in DI, and the next sector ; needed is in BX. If the last sector has been read, D_1ST = 0. ; ******************************************************************* ; SET_BADS - puts bad sector info to buffer SECBUF so it can ; can be written to the disk image. Puts the identifier and ; gets status of the bad disk and puts it also. The format of ; the sector start is: ; ; 1CC21E3D nn nn nn nn ACG TIME_G TIME_B nn nn nn nn where, ; ; the 32 bit number is an identifier, 1st nn nn nn nn are 4 status ; bytes to be returned when bad sector status is requested, and ACG ; means ACK, CPT and GOOD (or bad) checksum. A & C might be other ; letters such as N for NAK, E for ERROR, or T, meaning timed out ; i.e. - don't send anything. I suspect there normally wouldn't ; be a checksum, since no data would have been sent. ; ; TIME_G is the number of jiffies to pause after a good sector and ; TIME_B is the number after a bad sector. ; The 2nd nn nn nn nn is the 4 status bytes for a GOOD sector access. ; The bad status sectors go to B_STATUS[BP] and the good ones go to ; G_STATUS[BP] when the disk is loaded. ; Note: Since in 1050 mode, BP has NOT been set up, location ; DG_STATUS is used to temp. hold good status. SET_BADS: PUSH AX, CX, SI, DI MOV DI, SECBUF MOV [DI], CP_WHI ; immediate value 1CC2 - set the bad sector MOV [DI+2], CP_WLO ; " " 1E3D - flag. MOV AX, W[E_1050] ; set the ACK, CPT, CKSM stuff MOV [DI+8], AX MOV AL, B[E_1050+2] MOV [DI+10], AL MOV CX, 1 ; RETRIES FOR STATUS S2: CALL D_STATUS ; get the status to be used and store it CMP W[D_STATS], 0 JNE > S1 CMP W[D_STATS+2], 0 JNE > S1 ; JNC > S1 ; no carry - status was obtained DEC CX ; CMP CX, 0 JNE > S1 ; 1 retry used up already PUSH BX TICKS 18 ; wait a second ... POP BX JMP NEAR S2 ; try again S1: MOV DI, SECBUF MOV AX, W[D_STATS] ; copy bad sector status to sector data MOV [DI+4], AX MOV AX, W[D_STATS+2] MOV [DI+6], AX MOV AX, W[DG_STATUS] ; copy good sector status to sector data MOV W[SECBUF+13], AX MOV AX, W[DG_STATUS+2] MOV W[SECBUF + 15], AX ; now flag if this is is first sector with valid good and bad sector ; data ... CMP GUD_FOUND, 'N'; has good sector status been found? JE > W1 CMP BAD_1ST, 0 ; flagged yet? JNE > W1 ; MOV AX, D_1ST ; current sector number MOV BAD_1ST, BX ; was AX - but D_1ST not current sector # W1: POP DI, SI, CX, AX RET ; D_W_TRK takes information returned by GD_TRACK and writes sectors to ; the open PC file handle D_HANDL. Expected that number of bytes is in ; DI and place to find them is at NEXTS:0000. Carry set on return ; indicates error. D_W_TRK: PUSH DI ; GONNA NEED THAT INFO MOV AH,040h; Write MOV BX,D_HANDL MOV CX,DI ; number of bytes PUSH DS MOV DS, NEXTS; DS now points to NEXTS SEGMENT XOR DX, DX ; offset of 0 INT 021h POP DS JNC >L2; No error CALL D_WRITE_ERR; status and close file JMP > E1 ; Now, do # of bytes written match # requested to write? L2: CMP AX,CX JE >L1; OK if equal CALL D_PART_REC; status and close file E1: STC JC > E2 L1: CLC E2: POP DI RET ; ******************************************************************* ; D_PUTSN converts number in BX to ASCII number at A_SNUM and prints ; it to the current cursor position. After 15 numbers, it prints a ; linefeed. D_PUTSN: PUSH AX, BX, DI MOV AX, BX MOV DI, A_SNUM + 4 ; point to 1's digit of field CALL INT2DEC PRINTL A_SNUM INC D_AS_CT CMP D_AS_CT, 14 ; 4.02 JNE > L1 MOV D_AS_CT, 0 CALL LINEFEED L1: POP DI, BX, AX RET ; ******************************************************************* GET_HI_LO: PUSH BX, CX Y2: CALL LINEFEED PRINTL D_SAGH MOV AX, ' '+' ' * 0100h ; two blanks MOV W[A_LOSEC], AX MOV W[A_LOSEC+2], AX MOV W[A_HISEC], AX MOV W[A_HISEC+2], AX MOV BX, A_LOSEC ; place to put ASCII LO digits MOV CX, 4 ; max # of digits to get CALL GET_STR ; note: return with carry if ESC JC > D1 CALL RANGE10 ; carry set means out of range JC GET_HI_LO MOV BX, A_LOSEC CALL DECTOINT ; convert string [BX] to interger in AX MOV D_1ST, AX ; save low sector number ; now get higher number: PRINTL D_SAGL MOV BX, A_HISEC MOV CX, 4 CALL GET_STR ; note: return with carry if ESC JC > D1 CALL RANGE10 ; carry set means out of range JC GET_HI_LO MOV BX, A_HISEC CALL DECTOINT ; convert string [BX] to interger in AX MOV D_LAST, AX ; save high number CMP D_1ST, AX ; 4.05 JA Y2 ; can't allow last to be < first CLC D1: POP CX, BX RET ; ********************************************************************** ; D_SCAN scans selected sectors and reports the status ; D_SCAN: PRINTL DSA_E15 CALL LINEFEED D1: CALL GET_HI_LO ; get range from user - D_1ST & D_LAST ; CALL WARM_UP CALL LINEFEED PRINTL E_STOP ; stop on error? CALL GET_UP MOV E_STOPY, AL ; 'Y' stored in E_STOPY means stop on error MOV PSD_CH, 'N' ; default is no for put sector data PRINTL QPSDTS ; put sector data to screen (4.05) CALL GET_UP CMP AL, 'Y' JNE > L4 MOV PSD_CH, AL L4: CALL LINEFEED MOV AH,1 ; is a key ready for input? INT 016h JZ > L9 ; zero means no key waiting CALL GET_TORK ; CMP AL, 27 ; escape pressed? JE > D4 ; then exit L9: MOV BX, D_1ST ; get next sector CMP D_LAST, BX JAE > D3 ; finished if sector # above D_LAST D4: JMP NEAR L1A D3: MOV AX, BX MOV DI, DA_SPLC + 4 ; point to 1's digit of field CALL INT2DEC MOV D_RETRY, 1 ; do 1 retry ... R1: ; CALL LINEFEED PRINTL DA_SEC ; print header (no CR/LF) INC D_1ST ; prepare for next ... CALL D_GETS ; read the sector PUSHF ; preserve carry flag PRINTL D_RESPONS POPF JNC > L2 ; carry means command frame error ; CALL LINEFEED TICKS 36 ; wait 2 seconds after error ... DEC D_RETRY CMP D_RETRY, 0FFh JE >L2 CALL LINEFEED DEC D_1ST ; if retry, undo extra INC JMP R1 L2: CMP PSD_CH, 'Y' ; user wanted to print data? JNE > Q3 CALL VIEW_DATA PRINTL KEYWAITE ; wait for key or escape CALL WAIT4KEY CMP AL, 27 ; escape? JE D4 ; outta here Q3: XOR AX, AX ; zero out status bytes MOV W[D_STATS], AX MOV W[D_STATS+2], AX CALL D_STATUS ; get status of last transaction ; below converts the 4 status bytes to ascii and puts them into ; the field for printing. Note that bytes are recorded last first ; but printed first first. ... Huh? MOV CX, 4 MOV DI, 3 MOV SI, DA_STFRX L3: MOV AL, [D_STATS + DI] CALL BYTE2HEX ; converts byte in AL to ASCII digits in BX MOV [SI], BX ; ADD SI, 3 DEC DI LOOP L3 PRINTL DA_STFRAME CALL LINEFEED CMP D_RETRY, 0FFh ; means errors were encountered JNE > D2 CMP E_STOPY, 'Y' ; set up to stop on error? JNE > D2 PRINTL E_CONT; continue? CALL GET_UP CMP AL, 'C' ; C to continue present series JNE > L1 D2: JMP L4 L1: NOP L1A: CALL LINEFEED ; 4.02 - was commented out PRINTL SCAN_MOR CALL GET_TORK CMP AL, ' ' ; spacebar? JNE > Q1 CALL LINEFEED JMP D1 Q1: RET ; ***************************************************************** COMMENT\ ; WARM_UP starts the disk motor and waits 2 seconds ... WARM_UP: RET ; ********** COMMAND PROBABLY NOT SUPPORTED BY DRIVES **** MOV DX, LSR ; clear input registers ... IN AL, DX CALL ACC_DLY ; 4.15 MOV DX, BAUD IN AL, DX CALL ACC_DLY: CALL RTS_DN ; lower command line MOV BX, DT1 CALL TIMER_0 MOV AL, D_ID MOV DEVID, AL MOV AL, 'U' ; Motor On MOV CMND, AL ADD AL, DEVID ADC AL, CFAUX1 ; note that CFAUX1 & 2 are "don't care" ADC AL, CFAUX2 ; except for checksum calc ADC AL, 0 MOV CFCKSM, AL ; now, command frame is ready to send MOV CX, 5 MOV BX, 4 L1: MOV AL, CFCKSM[BX] CALL PUT1A DEC BX LOOP L1 MOV BX, DT1 ; delay before raising command line CALL TIMER_0 CALL RTS_UP ; now, wait for the drive to send an ACK ... CALL GETSLO ; JNC > L2 ; carry means timed out in GET1 CMP AL, 'A' JE > L2 MOV B[E_1050], 'A' ; now, wait for a COMPLETE ... L2: CALL GETSLO ; JC > L2 ; CMP AL, 'C' JE > L2 MOV B[E_1050+1], 'C' STC ; error exit location L2: NOP ; TICKS 36 L1: RET ; finished ENDOFCOMMENT\ ; ****************************************************************** ; ; BAD_CHECK looks at sector info pointed to by ES:DI, and if a bad ; sector is indicated, it copies STATUS and bus response info into ; the disk data table. BP must point to disk data table. Carry ; means sector WAS bad. ; ; ******************************************************************* BAD_CHECK: CMP ES:[DI], CP_WHI JNE > B1 ; not a bad sector CMP ES:[DI+2], CP_WLO JNE > B1 ; now we know it's a bad sector, so copy up status info MOV AX, ES:[DI+4] ; 1st two bytes of status info MOV [B_STATUS+BP], AX MOV AX, ES:[DI+6] ; 2nd two ... MOV [B_STATUS+2+BP], AX ; 4.07 added the +2 part ... MOV [B_STAT+BP], 0FFh ; flag last read was bad STC ; flag bad sector JC > B2 B1: MOV [B_STAT+BP], 0 ; flag good status CLC B2: RET ; ********************************************************************* ; D_WRITE ; Writes data from an SIO2PC disk image to a file. Rev 4.05 D_WRITE: PRINTL SA_DWRITE PRINTL RD2 CALL LINEFEED CALL GET_TORK CMP AL, 27 JE > X9 ; ABORT MOV DEVID, AL CALL SET_BP ; point BP to disk structure JNC > G1 PRINTL DDNE TICKS 36 X9: JMP NEAR > X1 G1: CMP B[WRITTN][BP], 'W' JE > G5 ; making sure this is a ramdisk CMP B[WRITTN][BP], 'N' JE > G5 PRINTL NOT_RDSK TICKS 48 JMP NEAR D_WRITE G5: PUSH BP CALL D_STATUS ; see if warm-up prevents initial error POP BP PRINTL ASK_FMT CALL GET_UP CMP AL, 'Y' JNE > X3 CALL D_FORMAT ; At last, we're ready to actually write the information to the disk X3: PRINTL Q_DO_ALL CALL GET_TORK ; 1 means do all sectors, 2 means input range CMP AL, '1' JE > Y1 CMP AL, '2' JNE X3 CALL GET_HI_LO ; gets choice in D_1ST & D_LAST MOV BX, D_1ST MOV CX, D_LAST SUB CX, BX ; get total # in CX INC CX JNZ > Y2 ; forced branch Y1: MOV CX, MAX_SEX[BP] ; get max number of sectors MOV BX, 1 Y2: CALL LINEFEED PRINTL SA_WSEC G6: MOV ERROR, 'N' ; if WRITE_810 puts a Y here, abort loop ... CALL WRITE_810 CMP ERROR, 'Y' JE > X1 CALL D_PUTSN ; writes sector number INC BX LOOP G6 X1: MOV D_RETRY, 1 MOV ERROR, 'N' RET ; temporary return ; WRITE_810 ; WRITE_810 writes a sector's worth of data to the Atari drive. ; The sector number is in BX on entry, and BP is set up to point ; to the ramdisk's table of information. DEVID points to the correct ; device ID #. ; This command should result in 2 ACK's and a CPT. The status of these ; will be stored at E_1050 for debugging: WRITE_810: MOV D_RETRY, 1 RE_W810: PUSH CX, BX ; Since SEC_ADR uses DVSEG - non-indexed to BP - instead of STARS, I ; have to set up DVSEG here ... MOV CX, [STARS+BP] MOV DVSEG, CX ; chance to bail out here ... CALL IS_A_KEY JNC > B1 CMP AL, 27 ; ESCAPE? JNE > B1 PRINTL USR_QUIT MOV ERROR, 'Y' ; tell calling routine ... JMP NEAR > G9 B1: XCHG BH, BL MOV W[CFAUX2], BX FILL '_', E_1050, 6 MOV B[E_1050+3], '$' MOV DX, LSR IN AL, DX CALL ACC_DLY ; 4.15 MOV DX, BAUD IN AL, DX ; clear input register CALL ACC_DLY ; 4.15 CALL RTS_DN ; lower the command line MOV BX, DT1 ; for 750 us delay CALL TIMER_0 MOV CMND, 'W' ; put with verify MOV AL, CMND ; do checksum ADD AL, DEVID ADC AL, CFAUX1 ADC AL, CFAUX2 ADC AL, 0 ; get last carry MOV CFCKSM, AL ; now, ready to send command frame MOV CX, 5 MOV BX, 4 L1: MOV AL, CFCKSM[BX] CALL PUT1 DEC BX LOOP L1 MOV BX, DT1 ; delay before raising command line CALL TIMER_0 CALL RTS_UP ; now, wait for the drive to send an ACK ... CALL GETSLO JNC > N1 ; carry means timed out in GETSLO MOV B[E_1050], 'T' ; signify timed out waiting for ACK JC > X2 N1: MOV B[E_1050], AL ; record ACK CMP AL, 'A' JNE > X2 ; Now, send sector data to the drive: CALL SEC_ADR ; Points ES:DI to data based on BP & CFAUX1/2 MOV CX, 128 ; assumed sector size MOV BX, W[CFAUX2] ; get sector # back in BX XCHG BH, BL CMP BX, 3 JBE > G7 ; all <= sector 3 are 128 bytes MOV CX, SEC_SIZE[BP] ; else, get sector size from table G7: MOV BX, DT2 ; get about 1500 us delay CALL TIMER_0 MOV DL, 0 ; for checksum G8: ES:MOV AL, [DI] CLC ADC DL, AL ADC DL, 0 ; add in carry CALL PUT1A INC DI LOOP G8 MOV AL, DL CALL PUT1 ; send the checksum ; All data sent; now we expect an ACK and a CPT from the drive: MOV B[E_1050+1], 'T' ; anticipate timeout ... CALL GETSLO JC > X2 MOV [E_1050+1], AL ; store actual byte received CMP AL, 'A' ; JNE > X2 MOV B[E_1050+2], 'T' CALL GETSLO JC > X2 MOV [E_1050+2], AL CMP AL, 'C' JE > G9 X2: ;BUGX2: ; PUSH DI, AX PRINTL E_1050 MOV DI, WS_FNBR + 4 CALL INT2DEC PRINTL WS_FAIL CALL BEEP TICKS 18 ; POP AX, DI MOV D_AS_CT, 0 ; reset sector number print routine DEC D_RETRY CMP D_RETRY, 0 JNE > G9 ; after 1 retry D_RETRY WILL = -1 POP BX, CX JMP NEAR RE_W810 ; one retry G9: CALL CLICK1 POP BX, CX RET ; D_FORMAT ; Formats a real Atari disk (sends format command to drive) ; device ID already stored at DEVID ... ; Added by Rev. 4.05 D_FORMAT: PUSH BP CALL LINEFEED H2: PRINTL FMT_HOW CALL GET_UP CMP AL, 27 JNE > X4 ; bail out JMP NEAR > X2 X4: MOV CMND, '!' ; assume normal 810 format CMP AL, 'Y' JE > H1 CMP AL, 'A' ; 1.5 density format? JNE H2 ; must be either Y or A MOV CMND, 022h ; hex 22 for 1.5 density H1: MOV AL, CMND ; 4.15 added save CMND for D_STATUS call PUSH BP, AX CALL D_STATUS ; wake up drive POP AX, BP MOV CMND, AL FILL '_', E_1050, 6 MOV B[E_1050+3], '$' CALL Z_SECBUF MOV DX, LSR IN AL, DX CALL ACC_DLY ; 4.15 added MOV DX, BAUD IN AL, DX ; clear input register CALL ACC_DLY ; 4.15 MOV BX, 360 ; arbitrary sector number, 4.15 moved down here XCHG BH, BL MOV W[CFAUX2], BX CALL RTS_DN ; lower the command line MOV BX, DT1 ; for 750 us delay CALL TIMER_0 MOV AL, CMND ; do checksum ADD AL, DEVID ADC AL, CFAUX1 ADC AL, CFAUX2 ADC AL, 0 ; get last carry MOV CFCKSM, AL ; now, ready to send command frame MOV CX, 5 MOV BX, 4 L1: MOV AL, CFCKSM[BX] CALL PUT1A DEC BX LOOP L1 MOV BX, DT1 ; delay before raising command line CALL TIMER_0 CALL RTS_UP ; now, wait for the drive to send an ACK ... CALL GETSLO JNC > N1 ; carry means timed out in GETSLO MOV B[E_1050], 'T' ; signify timed out waiting for ACK JC > X2 N1: MOV B[E_1050], AL ; record ACK CMP AL, 'A' JNE > X2 ; now, wait for a COMPLETE ... ; It might take a drive a couple of minutes to format, so I'll allow ; 160 seconds. MOV CX, 40 R2: CALL GETSLO ; GETSLO tries for 4 seconds, then returns with C set JNC > N2 ; PUSH CX CALL IS_A_KEY ; key waiting? POP CX JNC > H2 CMP AL, 27 ; escape? JE > X2 H2: LOOP R2 MOV B[E_1050+1], 'T' JMP NEAR > X2 N2: MOV B[E_1050+1], AL CMP AL, 'C' JNE > X2 ; now, read in the bad sector count ... MOV DI, 0 MOV CX, 128 G3: CALL GET1 MOV SECBUF[DI], AL INC DI ; 4.15 added this INC LOOP G3 MOV AX, W[SECBUF] CMP AX, 0FFFFh ; indicates no bad sectors JE > G4 CALL LINEFEED PRINTL HAS_BADS JMP NEAR > G4 X2: PRINTL ABT_FMT JMP NEAR > G5 G4: PRINTL FMT_CPT G5: PRINTL KEYWAIT CALL WAIT4KEY POP BP RET