diff options
| author | 2024-04-25 21:24:10 +0100 | |
|---|---|---|
| committer | 2024-04-25 22:32:27 +0000 | |
| commit | 2d04cacc5322951f187bb17e017c12920ac8ebe2 (patch) | |
| tree | 80ee017efa878dfd5344b44249e6a241f2a7f6e2 /v4.0/src/DOS/DEV.ASM | |
| parent | Merge pull request #430 from jpbaltazar/typoptbr (diff) | |
| download | ms-dos-main.tar.gz ms-dos-main.tar.xz ms-dos-main.zip | |
Diffstat (limited to 'v4.0/src/DOS/DEV.ASM')
| -rw-r--r-- | v4.0/src/DOS/DEV.ASM | 841 |
1 files changed, 841 insertions, 0 deletions
diff --git a/v4.0/src/DOS/DEV.ASM b/v4.0/src/DOS/DEV.ASM new file mode 100644 index 0000000..20c3f74 --- /dev/null +++ b/v4.0/src/DOS/DEV.ASM | |||
| @@ -0,0 +1,841 @@ | |||
| 1 | ; SCCSID = @(#)dev.asm 1.2 85/07/23 | ||
| 2 | ; SCCSID = @(#)dev.asm 1.2 85/07/23 | ||
| 3 | TITLE DEV - Device call routines | ||
| 4 | NAME Dev | ||
| 5 | ; Misc Routines to do 1-12 low level I/O and call devices | ||
| 6 | ; | ||
| 7 | ; IOFUNC | ||
| 8 | ; DEVIOCALL | ||
| 9 | ; SETREAD | ||
| 10 | ; SETWRITE | ||
| 11 | ; DEVIOCALL2 | ||
| 12 | ; DEV_OPEN_SFT | ||
| 13 | ; DEV_CLOSE_SFT | ||
| 14 | ; RW_SC | ||
| 15 | ; IN_SC | ||
| 16 | ; INVALIDATE_SC | ||
| 17 | ; VIRREAD | ||
| 18 | ; SC2BUF | ||
| 19 | ; | ||
| 20 | ; Revision history: | ||
| 21 | ; | ||
| 22 | ; A000 version 4.00 Jan. 1988 | ||
| 23 | ; A010 disable change line for SHARE /NC | ||
| 24 | |||
| 25 | ; | ||
| 26 | ; get the appropriate segment definitions | ||
| 27 | ; | ||
| 28 | .xlist | ||
| 29 | include dosseg.asm | ||
| 30 | |||
| 31 | CODE SEGMENT BYTE PUBLIC 'CODE' | ||
| 32 | ASSUME SS:DOSGROUP,CS:DOSGROUP | ||
| 33 | |||
| 34 | .xcref | ||
| 35 | INCLUDE DOSSYM.INC | ||
| 36 | INCLUDE DEVSYM.INC | ||
| 37 | include version.inc | ||
| 38 | .cref | ||
| 39 | .list | ||
| 40 | |||
| 41 | i_need IOXAD,DWORD | ||
| 42 | i_need IOSCNT,WORD | ||
| 43 | i_need DEVIOBUF,4 | ||
| 44 | i_need IOCALL,BYTE | ||
| 45 | i_need IOMED,BYTE | ||
| 46 | i_need IORCHR,BYTE | ||
| 47 | i_need CALLSCNT,WORD | ||
| 48 | i_need DMAAdd,DWORD | ||
| 49 | i_need CallDevAd,DWORD | ||
| 50 | i_need CallXAD,DWORD | ||
| 51 | i_need DPBHead,DWORD | ||
| 52 | i_need ThisSFT,DWORD | ||
| 53 | i_need ThisDPB,DWORD | ||
| 54 | i_need DevCall,DWORD | ||
| 55 | i_need VerFlg,BYTE | ||
| 56 | i_need HIGH_SECTOR,WORD ;AN000; | ||
| 57 | i_need CALLSSEC,WORD ;AN000; | ||
| 58 | i_need CALLNEWSC,DWORD ;AN000; | ||
| 59 | i_need SC_CACHE_COUNT,WORD ;AN000; | ||
| 60 | i_need SC_CACHE_PTR,DWORD ;AN000; | ||
| 61 | i_need CURSC_SECTOR,WORD ;AN000; | ||
| 62 | i_need SEQ_SECTOR,DWORD ;AN000; | ||
| 63 | i_need SC_SECTOR_SIZE,WORD ;AN000; | ||
| 64 | i_need CURSC_DRIVE,BYTE ;AN000; | ||
| 65 | i_need SC_DRIVE,BYTE ;AN000; | ||
| 66 | i_need SC_STATUS,WORD ;AN000; | ||
| 67 | i_need SC_FLAG,BYTE ;AN000; | ||
| 68 | i_need TEMP_VAR,WORD ;AN000; | ||
| 69 | i_need TEMP_VAR2,WORD ;AN000; | ||
| 70 | i_need InterChar,BYTE ;AN000; interim character flag 2/13/KK | ||
| 71 | i_need InterCon,BYTE ;AN000; Console mode flag(1:interim mode) 2/13/KK | ||
| 72 | i_need SaveCurFlg,BYTE ;AN000; Console out mode(1:print & don't adv cursor) 2 /13/KK | ||
| 73 | i_need DDMOVE,BYTE ;AN000; flag for DWORD move | ||
| 74 | i_need DOS34_FLAG,WORD ;AN000; | ||
| 75 | i_need fshare,BYTE ;AN010; share flag | ||
| 76 | |||
| 77 | Break <IOFUNC -- DO FUNCTION 1-12 I/O> | ||
| 78 | |||
| 79 | ; Inputs: | ||
| 80 | ; DS:SI Points to SFT | ||
| 81 | ; AH is function code | ||
| 82 | ; = 0 Input | ||
| 83 | ; = 1 Input Status | ||
| 84 | ; = 2 Output | ||
| 85 | ; = 3 Output Status | ||
| 86 | ; = 4 Flush | ||
| 87 | ; = 5 Input Status - System WAIT invoked for K09 if no char | ||
| 88 | ; present. | ||
| 89 | ; AL = character if output | ||
| 90 | ; Function: | ||
| 91 | ; Perform indicated I/O to device or file | ||
| 92 | ; Outputs: | ||
| 93 | ; AL is character if input | ||
| 94 | ; If a status call | ||
| 95 | ; zero set if not ready | ||
| 96 | ; zero reset if ready (character in AL for input status) | ||
| 97 | ; For regular files: | ||
| 98 | ; Input Status | ||
| 99 | ; Gets character but restores position | ||
| 100 | ; Zero set on EOF | ||
| 101 | ; Input | ||
| 102 | ; Gets character advances position | ||
| 103 | ; Returns ^Z on EOF | ||
| 104 | ; Output Status | ||
| 105 | ; Always ready | ||
| 106 | ; AX altered, all other registers preserved | ||
| 107 | |||
| 108 | procedure IOFUNC,NEAR | ||
| 109 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 110 | |||
| 111 | Assert ISSFT,<DS,SI>,"IOFUNC" | ||
| 112 | MOV WORD PTR [IOXAD+2],SS | ||
| 113 | MOV WORD PTR [IOXAD],OFFSET DOSGROUP:DEVIOBUF | ||
| 114 | MOV WORD PTR [IOSCNT],1 | ||
| 115 | MOV WORD PTR [DEVIOBUF],AX | ||
| 116 | TEST [SI.sf_flags],sf_isnet | ||
| 117 | JZ IOTO22 ;AN000; | ||
| 118 | JMP IOTOFILE ;AN000; | ||
| 119 | IOTO22: | ||
| 120 | TEST [SI.sf_flags],devid_device | ||
| 121 | JNZ IOTo33 ;AN000; | ||
| 122 | JMP IOTOFILE ;AN000; | ||
| 123 | IOTO33: | ||
| 124 | invoke save_world | ||
| 125 | MOV DX,DS | ||
| 126 | MOV BX,SS | ||
| 127 | MOV DS,BX | ||
| 128 | MOV ES,BX | ||
| 129 | ASSUME DS:DOSGroup | ||
| 130 | XOR BX,BX | ||
| 131 | cmp ah,5 ; system wait enabled? | ||
| 132 | jnz no_sys_wait | ||
| 133 | or bx,0400H ; Set bit 10 in status word for driver | ||
| 134 | ; It is up to device driver to carry out | ||
| 135 | ; appropriate action. | ||
| 136 | no_sys_wait: | ||
| 137 | MOV [IOCALL.REQSTAT],BX | ||
| 138 | XOR BX,BX | ||
| 139 | MOV BYTE PTR [IOMED],BL | ||
| 140 | |||
| 141 | Table Segment | ||
| 142 | Public DEV001S, DEV001E ; Pathgen labels | ||
| 143 | DEV001s: | ||
| 144 | ; length of packets | ||
| 145 | LenTab DB DRDWRHL, DRDNDHL, DRDWRHL, DSTATHL, DFLSHL, DRDNDHL | ||
| 146 | |||
| 147 | ; Error Function | ||
| 148 | |||
| 149 | CmdTab DB 86h, DEVRD ; 0 input | ||
| 150 | DB 86h, DEVRDND ; 1 input status | ||
| 151 | DB 87h, DEVWRT ; 2 output | ||
| 152 | DB 87h, DEVOST ; 3 output status | ||
| 153 | DB 86h, DEVIFL ; 4 input flush | ||
| 154 | DB 86H, DEVRDND ; 5 input status with system WAIT | ||
| 155 | DEV001E: | ||
| 156 | Table ENDS | ||
| 157 | |||
| 158 | MOV BL,AH ; get function | ||
| 159 | MOV AH,LenTab[BX] | ||
| 160 | SHL BX,1 | ||
| 161 | MOV CX,WORD PTR CmdTab[BX] | ||
| 162 | |||
| 163 | MOV BX,OFFSET DOSGROUP:IOCALL | ||
| 164 | |||
| 165 | MOV [IOCALL.REQLEN],AH | ||
| 166 | MOV [IOCALL.REQFUNC],CH | ||
| 167 | IF DBCS ;AN000; | ||
| 168 | ;----------------------------- Start of DBCS 2/13/KK | ||
| 169 | PUSH CX ;AN000; | ||
| 170 | MOV CL, [InterCon] ;AN000; | ||
| 171 | CMP CH, DEVRD ;AN000; 0 input | ||
| 172 | JZ SETIN ;AN000; | ||
| 173 | CMP CH, DEVRDND ;AN000; 1(5) input status without(with) system WAIT | ||
| 174 | JZ SETIN ;AN000; | ||
| 175 | MOV CL, [SaveCurflg] ;AN000; | ||
| 176 | CMP CH, DEVWRT ;AN000; 2 output | ||
| 177 | JZ CHKERROUT ;AN000; | ||
| 178 | XOR CL,CL ;AN000; else, do normal | ||
| 179 | SETIN: ;AN000; | ||
| 180 | MOV BYTE PTR [IoMed], CL ;AN000; set interim I/O indication | ||
| 181 | POP CX ;AN000; | ||
| 182 | ;----------------------------- End of DBCS 2/13/KK | ||
| 183 | ENDIF ;AN000; | ||
| 184 | MOV DS,DX | ||
| 185 | ASSUME DS:NOTHING | ||
| 186 | CALL DEVIOCALL | ||
| 187 | MOV DI,[IOCALL.REQSTAT] | ||
| 188 | TEST DI,STERR | ||
| 189 | JNZ DevErr | ||
| 190 | OkDevIO: | ||
| 191 | MOV AX,SS | ||
| 192 | MOV DS,AX | ||
| 193 | ASSUME DS:DOSGroup | ||
| 194 | IF DBCS ;AN000; | ||
| 195 | MOV [InterChar],0 ;AN000; reset interim character flag 2/13/KK | ||
| 196 | TEST DI,Ddkey ;AN000; is this a dead key (interim char)? 2/13/KK | ||
| 197 | JZ NotInterim ;AN000; no, flag already reset... 2/13/KK | ||
| 198 | INC [InterChar] ;AN000; yes, set flag for future 2/13/KK | ||
| 199 | NotInterim: ;AN000; 2/13/KK | ||
| 200 | ENDIF ;AN000; | ||
| 201 | CMP CH,DEVRDND | ||
| 202 | JNZ DNODRD | ||
| 203 | MOV AL,BYTE PTR [IORCHR] | ||
| 204 | MOV [DEVIOBUF],AL | ||
| 205 | |||
| 206 | DNODRD: MOV AH,BYTE PTR [IOCALL.REQSTAT+1] | ||
| 207 | NOT AH ; Zero = busy, not zero = ready | ||
| 208 | AND AH,STBUI SHR 8 | ||
| 209 | |||
| 210 | QuickReturn: ;AN000; 2/13/KK | ||
| 211 | invoke restore_world | ||
| 212 | ASSUME DS:NOTHING | ||
| 213 | MOV AX,WORD PTR [DEVIOBUF] | ||
| 214 | return | ||
| 215 | |||
| 216 | ;IOTOFILEJ: | ||
| 217 | ; JMP SHORT IOTOFILE | ||
| 218 | IF DBCS ;AN000; | ||
| 219 | ;------------------------------ Start of DBCS 2/13/KK | ||
| 220 | CHKERROUT: ;AN000; | ||
| 221 | MOV DS, DX ;AN000; | ||
| 222 | TEST [SI.sf_flags], devid_device_con_out ;AN000; output to console ? | ||
| 223 | JNZ GOOD ;AN000; yes | ||
| 224 | CMP CL, 01 ;AN000; write interim ? | ||
| 225 | JNZ GOOD ;AN000; no, | ||
| 226 | POP CX ;AN000; | ||
| 227 | JMP SHORT QuickReturn ;AN000; avoid writting interims to other than | ||
| 228 | ;AN000; console device | ||
| 229 | GOOD: ;AN000; | ||
| 230 | PUSH SS ;AN000; | ||
| 231 | POP DS ;AN000; | ||
| 232 | JMP SETIN ;AN000; | ||
| 233 | ;------------------------------ End of DBCS 2/13/KK | ||
| 234 | ENDIF ;AN000; | ||
| 235 | DevErr: | ||
| 236 | TEST CS:[DOS34_FLAG],X25_Special ;AN000; from disk.asm | ||
| 237 | JZ notx25 ;AN000; no | ||
| 238 | PUSH AX ;AN000; unknown command ? | ||
| 239 | MOV AX,DI ;AN000; | ||
| 240 | AND AX,error_I24_bad_command ;AN000; | ||
| 241 | CMP AL,error_I24_bad_command ;AN000; | ||
| 242 | POP AX ;AN000; | ||
| 243 | JNZ notx25 ;AN000; no, then error | ||
| 244 | JMP okDevIO ;AN000; | ||
| 245 | notx25: | ||
| 246 | MOV AH,CL | ||
| 247 | invoke CHARHARD | ||
| 248 | CMP AL,1 | ||
| 249 | JNZ NO_RETRY | ||
| 250 | invoke restore_world | ||
| 251 | JMP IOFUNC | ||
| 252 | |||
| 253 | NO_RETRY: | ||
| 254 | ; Know user must have wanted Ignore OR Fail. Make sure device shows ready | ||
| 255 | ; so that DOS doesn't get caught in a status loop when user simply wants | ||
| 256 | ; to ignore the error. | ||
| 257 | AND BYTE PTR [IOCALL.REQSTAT+1], NOT (STBUI SHR 8) | ||
| 258 | JMP OKDevIO | ||
| 259 | |||
| 260 | IOTOFILE: | ||
| 261 | ASSUME DS:NOTHING | ||
| 262 | OR AH,AH | ||
| 263 | JZ IOIN | ||
| 264 | DEC AH | ||
| 265 | JZ IOIST | ||
| 266 | DEC AH | ||
| 267 | JZ IOUT | ||
| 268 | return ; NON ZERO FLAG FOR OUTPUT STATUS | ||
| 269 | |||
| 270 | IOIST: | ||
| 271 | PUSH WORD PTR [SI.sf_position] ; Save position | ||
| 272 | PUSH WORD PTR [SI.sf_position+2] | ||
| 273 | CALL IOIN | ||
| 274 | POP WORD PTR [SI.sf_position+2] ; Restore position | ||
| 275 | POP WORD PTR [SI.sf_position] | ||
| 276 | return | ||
| 277 | |||
| 278 | IOUT: | ||
| 279 | CALL SETXADDR | ||
| 280 | invoke DOS_WRITE | ||
| 281 | CALL RESTXADDR ; If you change this into a jmp don't | ||
| 282 | return ; come crying to me when things don't | ||
| 283 | ; work ARR | ||
| 284 | |||
| 285 | IOIN: | ||
| 286 | CALL SETXADDR | ||
| 287 | OR [DOS34_FLAG],Disable_EOF_I24 ;AN000; | ||
| 288 | invoke DOS_READ | ||
| 289 | AND [DOS34_FLAG],NO_Disable_EOF_I24 ;AN000; | ||
| 290 | OR CX,CX ; Check EOF | ||
| 291 | CALL RESTXADDR | ||
| 292 | MOV AL,[DEVIOBUF] ; Get byte from trans addr | ||
| 293 | retnz | ||
| 294 | MOV AL,1AH ; ^Z if no bytes | ||
| 295 | return | ||
| 296 | |||
| 297 | SETXADDR: | ||
| 298 | POP WORD PTR [CALLSCNT] ; Return address | ||
| 299 | invoke save_world | ||
| 300 | PUSH WORD PTR [DMAADD] ; Save Disk trans addr | ||
| 301 | PUSH WORD PTR [DMAADD+2] | ||
| 302 | MOV WORD PTR [THISSFT+2],DS | ||
| 303 | Context DS | ||
| 304 | MOV WORD PTR [THISSFT],SI ; Finish setting SFT pointer | ||
| 305 | MOV CX,WORD PTR [IOXAD+2] | ||
| 306 | MOV WORD PTR [DMAADD+2],CX | ||
| 307 | MOV CX,WORD PTR [IOXAD] | ||
| 308 | MOV WORD PTR [DMAADD],CX ; Set byte trans addr | ||
| 309 | MOV CX,[IOSCNT] ; ioscnt specifies length of buffer | ||
| 310 | JMP SHORT RESTRET ; RETURN ADDRESS | ||
| 311 | |||
| 312 | RESTXADDR: | ||
| 313 | DOSAssume CS,<DS>,"RestXAddr" | ||
| 314 | POP WORD PTR [CALLSCNT] ; Return address | ||
| 315 | POP WORD PTR [DMAADD+2] ; Restore Disk trans addr | ||
| 316 | POP WORD PTR [DMAADD] | ||
| 317 | invoke restore_world | ||
| 318 | ASSUME DS:NOTHING | ||
| 319 | RESTRET:JMP WORD PTR [CALLSCNT] ; Return address | ||
| 320 | EndProc IOFUNC | ||
| 321 | |||
| 322 | Break <DEV_OPEN_SFT, DEV_CLOSE_SFT - OPEN or CLOSE A DEVICE> | ||
| 323 | |||
| 324 | ; Inputs: | ||
| 325 | ; ES:DI Points to SFT | ||
| 326 | ; Function: | ||
| 327 | ; Issue an OPEN call to the correct device | ||
| 328 | ; Outputs: | ||
| 329 | ; None | ||
| 330 | ; ALL preserved | ||
| 331 | |||
| 332 | procedure DEV_OPEN_SFT,NEAR | ||
| 333 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 334 | |||
| 335 | Assert ISSFT,<ES,DI>,"Dev_Open_SFT" | ||
| 336 | invoke Save_World | ||
| 337 | MOV AL,DEVOPN | ||
| 338 | JMP SHORT DO_OPCLS | ||
| 339 | |||
| 340 | EndProc DEV_OPEN_SFT | ||
| 341 | |||
| 342 | ; Inputs: | ||
| 343 | ; ES:DI Points to SFT | ||
| 344 | ; Function: | ||
| 345 | ; Issue a CLOSE call to the correct device | ||
| 346 | ; Outputs: | ||
| 347 | ; None | ||
| 348 | ; ALL preserved | ||
| 349 | |||
| 350 | procedure DEV_CLOSE_SFT,NEAR | ||
| 351 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 352 | |||
| 353 | Assert ISSFT,<ES,DI>,"Dev_Close_SFT" | ||
| 354 | invoke Save_World | ||
| 355 | MOV AL,DEVCLS | ||
| 356 | |||
| 357 | ; | ||
| 358 | ; Main entry for device open and close. AL contains the function requested. | ||
| 359 | ; Subtlety: if Sharing is NOT loaded then we do NOT issue open/close to block | ||
| 360 | ; devices. This allows networks to function but does NOT hang up with bogus | ||
| 361 | ; change-line code. | ||
| 362 | ; | ||
| 363 | entry DO_OPCLS | ||
| 364 | ; | ||
| 365 | ; Is the SFT for the net? If so, no action necessary. | ||
| 366 | ; | ||
| 367 | invoke Test_IFS_Remote ;AC000; | ||
| 368 | JNZ OPCLS_DONE ; NOP on net SFTs | ||
| 369 | XOR AH,AH ; Unit | ||
| 370 | TEST ES:[DI.sf_flags],devid_device | ||
| 371 | LES DI,ES:[DI.sf_devptr] ; Get DPB or device | ||
| 372 | JNZ Got_Dev_Addr | ||
| 373 | ; | ||
| 374 | ; We are about to call device open/close on a block driver. If no sharing | ||
| 375 | ; then just short circuit to done. | ||
| 376 | ; | ||
| 377 | ;;;;; invoke CheckShare | ||
| 378 | CMP fshare,1 ;AN010; /NC or no SHARE | ||
| 379 | JBE opCLs_Done ;AN010; yes | ||
| 380 | MOV AH,ES:[DI.dpb_UNIT] | ||
| 381 | MOV CL,ES:[DI.dpb_drive] | ||
| 382 | LES DI,ES:[DI.dpb_driver_addr] ; Get device | ||
| 383 | GOT_DEV_ADDR: ; ES:DI -> device | ||
| 384 | TEST ES:[DI.SDEVATT],DEVOPCL | ||
| 385 | JZ OPCLS_DONE ; Device can't | ||
| 386 | PUSH ES | ||
| 387 | POP DS | ||
| 388 | MOV SI,DI ; DS:SI -> device | ||
| 389 | OPCLS_RETRY: | ||
| 390 | Context ES | ||
| 391 | MOV DI,OFFSET DOSGROUP:DEVCALL | ||
| 392 | MOV BX,DI | ||
| 393 | PUSH AX | ||
| 394 | MOV AL,DOPCLHL | ||
| 395 | STOSB ; Length | ||
| 396 | POP AX | ||
| 397 | XCHG AH,AL | ||
| 398 | STOSB ; Unit | ||
| 399 | XCHG AH,AL | ||
| 400 | STOSB ; Command | ||
| 401 | MOV WORD PTR ES:[DI],0 ; Status | ||
| 402 | PUSH AX ; Save Unit,Command | ||
| 403 | invoke DEVIOCALL2 | ||
| 404 | MOV DI,ES:[BX.REQSTAT] | ||
| 405 | TEST DI,STERR | ||
| 406 | JZ OPCLS_DONEP ; No error | ||
| 407 | TEST [SI.SDEVATT],DEVTYP | ||
| 408 | JZ BLKDEV | ||
| 409 | MOV AH,86H ; Read error in data, Char dev | ||
| 410 | JMP SHORT HRDERR | ||
| 411 | |||
| 412 | BLKDEV: | ||
| 413 | MOV AL,CL ; Drive # in AL | ||
| 414 | MOV AH,6 ; Read error in data, Blk dev | ||
| 415 | HRDERR: | ||
| 416 | invoke CHARHARD | ||
| 417 | CMP AL,1 | ||
| 418 | JNZ OPCLS_DONEP ; IGNORE or FAIL | ||
| 419 | ; Note that FAIL is essentually IGNORED | ||
| 420 | POP AX ; Get back Unit, Command | ||
| 421 | JMP OPCLS_RETRY | ||
| 422 | |||
| 423 | OPCLS_DONEP: | ||
| 424 | POP AX ; Clean stack | ||
| 425 | OPCLS_DONE: | ||
| 426 | invoke Restore_World | ||
| 427 | return | ||
| 428 | |||
| 429 | EndProc DEV_CLOSE_SFT | ||
| 430 | |||
| 431 | Break <DEVIOCALL, DEVIOCALL2 - CALL A DEVICE> | ||
| 432 | |||
| 433 | ; Inputs: | ||
| 434 | ; DS:SI Points to device SFT | ||
| 435 | ; ES:BX Points to request data | ||
| 436 | ; Function: | ||
| 437 | ; Call the device | ||
| 438 | ; Outputs: | ||
| 439 | ; DS:SI -> Device driver | ||
| 440 | ; DS:SI,AX destroyed, others preserved | ||
| 441 | |||
| 442 | procedure DEVIOCALL,NEAR | ||
| 443 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 444 | |||
| 445 | Assert ISSFT,<DS,SI>,"DevIOCall" | ||
| 446 | LDS SI,[SI.sf_devptr] | ||
| 447 | |||
| 448 | entry DEVIOCALL2 | ||
| 449 | |||
| 450 | EnterCrit critDevice | ||
| 451 | |||
| 452 | TEST [SI.SDEVATT],DevTyp ;AN000; >32mb block device ? | ||
| 453 | JNZ chardev2 ;AN000; >32mb no | ||
| 454 | CMP ES:[BX.REQFUNC],DEVRD ;AN000; >32mb read ? | ||
| 455 | JZ chkext ;AN000; >32mb yes | ||
| 456 | CMP ES:[BX.REQFUNC],DEVWRT ;AN000; >32mb write ? | ||
| 457 | JZ chkext ;AN000; >32mb yes | ||
| 458 | CMP ES:[BX.REQFUNC],DEVWRTV;AN000; >32mb write/verify ? | ||
| 459 | JNZ chardev2 ;AN000; >32mb no | ||
| 460 | chkext: | ||
| 461 | CALL RW_SC ;AN000;LB. use secondary cache if there | ||
| 462 | JC dev_exit ;AN000;LB. done | ||
| 463 | |||
| 464 | TEST [SI.SDEVATT],EXTDRVR ;AN000;>32mb extended driver? | ||
| 465 | JZ chksector ;AN000;>32mb no | ||
| 466 | ADD BYTE PTR ES:[BX],8 ;AN000;>32mb make length to 30 | ||
| 467 | MOV AX,[CALLSSEC] ;AN000;>32mb | ||
| 468 | MOV [CALLSSEC],-1 ;AN000;>32mb old sector =-1 | ||
| 469 | MOV WORD PTR [CALLNEWSC],AX ;AN000;>32mb new sector = | ||
| 470 | MOV AX,[HIGH_SECTOR] ;AN000; >32mb low sector,high sector | ||
| 471 | MOV WORD PTR [CALLNEWSC+2],AX ;AN000; >32mb | ||
| 472 | JMP chardev2 ;AN000; >32mb | ||
| 473 | chksector: ;AN000; >32mb | ||
| 474 | CMP [HIGH_SECTOR],0 ;AN000; >32mb if >32mb | ||
| 475 | JZ chardev2 ;AN000; >32mb then fake error | ||
| 476 | MOV ES:[BX.REQSTAT],STERR+STDON+ERROR_I24_NOT_DOS_DISK ;AN000; >32mb | ||
| 477 | JMP SHORT dev_exit ;AN000; >32mb | ||
| 478 | |||
| 479 | chardev2: ;AN000; | ||
| 480 | ; As above only DS:SI points to device header on entry, and DS:SI is preserved | ||
| 481 | MOV AX,[SI.SDEVSTRAT] | ||
| 482 | MOV WORD PTR [CALLDEVAD],AX | ||
| 483 | MOV WORD PTR [CALLDEVAD+2],DS | ||
| 484 | CALL DWORD PTR [CALLDEVAD] | ||
| 485 | MOV AX,[SI.SDEVINT] | ||
| 486 | MOV WORD PTR [CALLDEVAD],AX | ||
| 487 | CALL DWORD PTR [CALLDEVAD] | ||
| 488 | CALL VIRREAD ;AN000;LB. move data from SC to buffer | ||
| 489 | JC chardev2 ;AN000;LB. bad sector or exceeds max sec | ||
| 490 | dev_exit: | ||
| 491 | LeaveCrit critDevice | ||
| 492 | return | ||
| 493 | EndProc DEVIOCALL | ||
| 494 | |||
| 495 | Break <SETREAD, SETWRITE -- SET UP HEADER BLOCK> | ||
| 496 | |||
| 497 | ; Inputs: | ||
| 498 | ; DS:BX = Transfer Address | ||
| 499 | ; CX = Record Count | ||
| 500 | ; DX = Starting Record | ||
| 501 | ; AH = Media Byte | ||
| 502 | ; AL = Unit Code | ||
| 503 | ; Function: | ||
| 504 | ; Set up the device call header at DEVCALL | ||
| 505 | ; Output: | ||
| 506 | ; ES:BX Points to DEVCALL | ||
| 507 | ; No other registers effected | ||
| 508 | |||
| 509 | procedure SETREAD,NEAR | ||
| 510 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 511 | |||
| 512 | PUSH DI | ||
| 513 | PUSH CX | ||
| 514 | PUSH AX | ||
| 515 | MOV CL,DEVRD | ||
| 516 | SETCALLHEAD: | ||
| 517 | MOV AL,DRDWRHL | ||
| 518 | PUSH SS | ||
| 519 | POP ES | ||
| 520 | MOV DI,OFFSET DOSGROUP:DEVCALL | ||
| 521 | STOSB ; length | ||
| 522 | POP AX | ||
| 523 | STOSB ; Unit | ||
| 524 | PUSH AX | ||
| 525 | MOV AL,CL | ||
| 526 | STOSB ; Command code | ||
| 527 | XOR AX,AX | ||
| 528 | STOSW ; Status | ||
| 529 | ADD DI,8 ; Skip link fields | ||
| 530 | POP AX | ||
| 531 | XCHG AH,AL | ||
| 532 | STOSB ; Media byte | ||
| 533 | XCHG AL,AH | ||
| 534 | PUSH AX | ||
| 535 | MOV AX,BX | ||
| 536 | STOSW | ||
| 537 | MOV AX,DS | ||
| 538 | STOSW ; Transfer addr | ||
| 539 | POP CX ; Real AX | ||
| 540 | POP AX ; Real CX | ||
| 541 | STOSW ; Count | ||
| 542 | XCHG AX,DX ; AX=Real DX, DX=real CX, CX=real AX | ||
| 543 | STOSW ; Start | ||
| 544 | XCHG AX,CX | ||
| 545 | XCHG DX,CX | ||
| 546 | POP DI | ||
| 547 | MOV BX,OFFSET DOSGROUP:DEVCALL | ||
| 548 | return | ||
| 549 | |||
| 550 | entry SETWRITE | ||
| 551 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 552 | |||
| 553 | ; Inputs: | ||
| 554 | ; DS:BX = Transfer Address | ||
| 555 | ; CX = Record Count | ||
| 556 | ; DX = Starting Record | ||
| 557 | ; AH = Media Byte | ||
| 558 | ; AL = Unit Code | ||
| 559 | ; Function: | ||
| 560 | ; Set up the device call header at DEVCALL | ||
| 561 | ; Output: | ||
| 562 | ; ES:BX Points to DEVCALL | ||
| 563 | ; No other registers effected | ||
| 564 | |||
| 565 | PUSH DI | ||
| 566 | PUSH CX | ||
| 567 | PUSH AX | ||
| 568 | MOV CL,DEVWRT | ||
| 569 | ADD CL,[VERFLG] | ||
| 570 | JMP SHORT SETCALLHEAD | ||
| 571 | EndProc SETREAD | ||
| 572 | |||
| 573 | |||
| 574 | Break <RW_SC -- Read Write Secondary Cache> | ||
| 575 | |||
| 576 | ; Inputs: | ||
| 577 | ; [SC_CACHE_COUNT]= secondary cache count | ||
| 578 | ; [SC_STATUS]= SC validity status | ||
| 579 | ; [SEQ_SECTOR]= last sector read | ||
| 580 | ; Function: | ||
| 581 | ; Read from or write through secondary cache | ||
| 582 | ; Output: | ||
| 583 | ; ES:BX Points to DEVCALL | ||
| 584 | ; carry clear, I/O is not done | ||
| 585 | ; [SC_FLAG]=1 if continuos sectors will be read | ||
| 586 | ; carry set, I/O is done | ||
| 587 | |||
| 588 | |||
| 589 | procedure RW_SC,NEAR ;AN000; | ||
| 590 | ASSUME DS:NOTHING,ES:NOTHING ;AN000; | ||
| 591 | |||
| 592 | CMP [SC_CACHE_COUNT],0 ;AN000;LB. secondary cache exists? | ||
| 593 | JZ scexit4 ;AN000;LB. no, do nothing | ||
| 594 | CMP [CALLSCNT],1 ;AN000;LB. sector count = 1 (buffer I/O) | ||
| 595 | JNZ scexit4 ;AN000;LB. no, do nothing | ||
| 596 | PUSH CX ;AN000;;LB. | ||
| 597 | PUSH DX ;AN000;;LB. yes | ||
| 598 | PUSH DS ;AN000;;LB. save registers | ||
| 599 | PUSH SI ;AN000;;LB. | ||
| 600 | PUSH ES ;AN000;;LB. | ||
| 601 | PUSH DI ;AN000;;LB. | ||
| 602 | MOV DX,WORD PTR [CALLSSEC] ;AN000;;LB. starting sector | ||
| 603 | CMP BYTE PTR [DEVCALL.REQFUNC],DEVRD ;AN000;LB. read ? ;AN000; | ||
| 604 | JZ doread ;AN000;LB. yes ;AN000; | ||
| 605 | CALL INVALIDATE_SC ;AN000;LB. invalidate SC ;AN000; | ||
| 606 | JMP scexit2 ;AN000;LB. back to normal ;AN000; | ||
| 607 | scexit4: ;AN000; ;AN000; | ||
| 608 | CLC ;AN000;LB. I/O not done yet ;AN000; | ||
| 609 | return ;AN000;LB. ;AN000; | ||
| 610 | doread: ;AN000; ;AN000; | ||
| 611 | CALL SC2BUF ;AN000;LB. check if in SC ;AN000; | ||
| 612 | JC readSC ;AN000;LB. ;AN000; | ||
| 613 | MOV [DEVCALL.REQSTAT],STDON ;AN000;LB. fake done and ok ;AN000; | ||
| 614 | STC ;AN000;LB. set carry ;AN000; | ||
| 615 | JMP saveseq ;AN000;LB. save seq. sector # ;AN000; | ||
| 616 | readSC: ;AN000; | ||
| 617 | MOV AX,WORD PTR [HIGH_SECTOR] ;AN000;;LB. subtract sector num from | ||
| 618 | MOV CX,WORD PTR [CALLSSEC] ;AN000;;LB. saved sequential sector | ||
| 619 | SUB CX,WORD PTR [SEQ_SECTOR] ;AN000;;LB. number | ||
| 620 | SBB AX,WORD PTR [SEQ_SECTOR+2] ;AN000;;LB. | ||
| 621 | CMP AX,0 ;AN000;;LB. greater than 64K | ||
| 622 | JNZ saveseq2 ;AN000;;LB. yes,save seq. sector # | ||
| 623 | chklow: ;AN000; | ||
| 624 | CMP CX,1 ;AN000;;LB. <= 1 | ||
| 625 | JA saveseq2 ;AN000;;LB. no, not sequential | ||
| 626 | MOV [SC_STATUS],-1 ;AN000;;LB. prsume all SC valid | ||
| 627 | MOV AX,[SC_CACHE_COUNT] ;AN000;;LB. yes, sequential | ||
| 628 | MOV [CALLSCNT],AX ;AN000;;LB. read continuous sectors | ||
| 629 | readsr: | ||
| 630 | MOV AX,WORD PTR [CALLXAD+2] ;AN000;;LB. save buffer addr | ||
| 631 | MOV [TEMP_VAR2],AX ;AN000;;LB. in temp vars | ||
| 632 | MOV AX,WORD PTR [CALLXAD] ;AN000;;LB. | ||
| 633 | MOV [TEMP_VAR],AX ;AN000;;LB. | ||
| 634 | ;AN000; | ||
| 635 | MOV AX,WORD PTR [SC_CACHE_PTR] ;AN000;LB. use SC cache addr as ;AN000; | ||
| 636 | MOV WORD PTR [CALLXAD],AX ;AN000;LB. transfer addr ;AN000; | ||
| 637 | MOV AX,WORD PTR [SC_CACHE_PTR+2] ;AN000;LB. ;AN000; | ||
| 638 | MOV WORD PTR [CALLXAD+2],AX ;AN000;LB. ;AN000; | ||
| 639 | MOV [SC_FLAG],1 ;AN000;LB. flag it for later ;AN000; | ||
| 640 | MOV AL,[SC_DRIVE] ;AN000;;LB. current drive | ||
| 641 | MOV [CURSC_DRIVE],AL ;AN000;;LB. set current drive | ||
| 642 | MOV AX,WORD PTR [CALLSSEC] ;AN000;;LB. current sector | ||
| 643 | MOV [CURSC_SECTOR],AX ;AN000;;LB. set current sector | ||
| 644 | MOV AX,WORD PTR [CALLSSEC+2] ;AN000;;LB. | ||
| 645 | MOV [CURSC_SECTOR+2],AX ;AN000;;LB. | ||
| 646 | saveseq2: ;AN000; | ||
| 647 | CLC ;AN000;LB. clear carry ;AN000; | ||
| 648 | saveseq: ;AN000; ;AN000; | ||
| 649 | MOV AX,[HIGH_SECTOR] ;AN000;LB. save current sector # ;AN000; | ||
| 650 | MOV WORD PTR [SEQ_SECTOR+2],AX ;AN000;LB. for access mode ref. ;AN000; | ||
| 651 | MOV AX,[CALLSSEC] ;AN000;LB. ;AN000; | ||
| 652 | MOV WORD PTR [SEQ_SECTOR],AX ;AN000;LB. ;AN000; | ||
| 653 | JMP scexit ;AN000;LB. ;AN000; | ||
| 654 | ;AN000; | ||
| 655 | scexit2: ;AN000;LB. ;AN000; | ||
| 656 | CLC ;AN000;LB. clear carry ;AN000; | ||
| 657 | scexit: ;AN000; ;AN000; | ||
| 658 | POP DI ;AN000;;LB. | ||
| 659 | POP ES ;AN000;;LB. restore registers | ||
| 660 | POP SI ;AN000;;LB. | ||
| 661 | POP DS ;AN000;;LB. | ||
| 662 | POP DX ;AN000;;LB. | ||
| 663 | POP CX ;AN000;;LB. | ||
| 664 | return ;AN000;;LB. | ||
| 665 | ;AN000; | ||
| 666 | EndProc RW_SC ;AN000; | ||
| 667 | |||
| 668 | Break <IN_SC -- check if in secondary cache> | ||
| 669 | |||
| 670 | ; Inputs: [SC_DRIVE]= requesting drive | ||
| 671 | ; [CURSC_DRIVE]= current SC drive | ||
| 672 | ; [CURSC_SECTOR] = starting scetor # of SC | ||
| 673 | ; [SC_CACHE_COUNT] = SC count | ||
| 674 | ; [HIGH_SECTOR]:DX= sector number | ||
| 675 | ; Function: | ||
| 676 | ; Check if the sector is in secondary cache | ||
| 677 | ; Output: | ||
| 678 | ; carry clear, in SC | ||
| 679 | ; CX= the index in the secondary cache | ||
| 680 | ; carry set, not in SC | ||
| 681 | ; | ||
| 682 | |||
| 683 | procedure IN_SC,NEAR ;AN000; | ||
| 684 | ASSUME DS:NOTHING,ES:NOTHING ;AN000; | ||
| 685 | |||
| 686 | MOV AL,[SC_DRIVE] ;AN000;;LB. current drive | ||
| 687 | CMP AL,[CURSC_DRIVE] ;AN000;;LB. same as SC drive | ||
| 688 | JNZ outrange2 ;AN000;;LB. no | ||
| 689 | MOV AX,WORD PTR [HIGH_SECTOR] ;AN000;;LB. subtract sector num from | ||
| 690 | MOV CX,DX ;AN000;;LB. secondary starting sector | ||
| 691 | SUB CX,WORD PTR [CURSC_SECTOR] ;AN000;;LB. number | ||
| 692 | SBB AX,WORD PTR [CURSC_SECTOR+2] ;AN000;;LB. | ||
| 693 | CMP AX,0 ;AN000;;LB. greater than 64K | ||
| 694 | JNZ outrange2 ;AN000;;LB. yes | ||
| 695 | CMP CX,[SC_CACHE_COUNT] ;AN000;;LB. greater than SC count | ||
| 696 | JAE outrange2 ;AN000;;LB. yes | ||
| 697 | CLC ;AN000;;LB. clear carry | ||
| 698 | JMP short inexit ;AN000;;LB. in SC | ||
| 699 | outrange2: ;AN000;;LB. set carry | ||
| 700 | STC ;AN000;;LB. | ||
| 701 | inexit: ;AN000;;LB. | ||
| 702 | return ;AN000;;LB. | ||
| 703 | |||
| 704 | EndProc IN_SC ;AN000; | ||
| 705 | |||
| 706 | Break <INVALIDATE_SC - invalide secondary cache> | ||
| 707 | |||
| 708 | ; Inputs: [SC_DRIVE]= requesting drive | ||
| 709 | ; [CURSC_DRIVE]= current SC drive | ||
| 710 | ; [CURSC_SECTOR] = starting scetor # of SC | ||
| 711 | ; [SC_CACHE_COUNT] = SC count | ||
| 712 | ; [SC_STAUS] = SC status word | ||
| 713 | ; [HIGH_SECTOR]:DX= sceotor number | ||
| 714 | ; | ||
| 715 | ; Function: | ||
| 716 | ; invalidate secondary cache if in there | ||
| 717 | ; Output: | ||
| 718 | ; [SC_STATUS] is updated | ||
| 719 | ; | ||
| 720 | |||
| 721 | procedure INVALIDATE_SC,NEAR ;AN000; | ||
| 722 | ASSUME DS:NOTHING,ES:NOTHING ;AN000; | ||
| 723 | |||
| 724 | CALL IN_SC ;AN000;;LB. in secondary cache | ||
| 725 | JC outrange ;AN000;;LB. no | ||
| 726 | MOV AX,1 ;AN000;;LB. invalidate the sector | ||
| 727 | SHL AX,CL ;AN000;;LB. in the secondary cache | ||
| 728 | NOT AX ;AN000;;LB. | ||
| 729 | AND [SC_STATUS],AX ;AN000;;LB. save the status | ||
| 730 | outrange: ;AN000;;LB. | ||
| 731 | return ;AN000;;LB. | ||
| 732 | |||
| 733 | EndProc INVALIDATE_SC ;AN000; | ||
| 734 | |||
| 735 | |||
| 736 | Break <VIRREAD- virtually read data into buffer> | ||
| 737 | |||
| 738 | ; Inputs: SC_FLAG = 0 , no sectors were read into SC | ||
| 739 | ; 1, continous sectors were read into SC | ||
| 740 | ; Function: | ||
| 741 | ; Move data from SC to buffer | ||
| 742 | ; Output: | ||
| 743 | ; carry clear, data is moved to buffer | ||
| 744 | ; carry set, bad sector or exceeds maximum sector | ||
| 745 | ; SC_FLAG =0 | ||
| 746 | ; CALLSCNT=1 | ||
| 747 | ; SC_STATUS= -1 if succeeded | ||
| 748 | ; 0 if failed | ||
| 749 | |||
| 750 | procedure VIRREAD,NEAR ;AN000; | ||
| 751 | ASSUME DS:NOTHING,ES:NOTHING ;AN000; | ||
| 752 | |||
| 753 | CMP [SC_FLAG],0 ;AN000;;LB. from SC fill | ||
| 754 | JZ sc2end ;AN000;;LB. no | ||
| 755 | MOV AX,[TEMP_VAR2] ;AN000;;LB. restore buffer addr | ||
| 756 | MOV WORD PTR [CALLXAD+2],AX ;AN000;;LB. | ||
| 757 | MOV AX,[TEMP_VAR] ;AN000;;LB. | ||
| 758 | MOV WORD PTR [CALLXAD],AX ;AN000;;LB. | ||
| 759 | MOV [SC_FLAG],0 ;AN000;;LB. reset sc_flag | ||
| 760 | MOV [CALLSCNT],1 ;AN000;;LB. one sector transferred | ||
| 761 | |||
| 762 | TEST [DEVCALL.REQSTAT],STERR ;AN000;;LB. error? | ||
| 763 | JNZ scerror ;AN000;;LB. yes | ||
| 764 | PUSH DS ;AN000;;LB. | ||
| 765 | PUSH SI ;AN000;;LB. | ||
| 766 | PUSH ES ;AN000;;LB. | ||
| 767 | PUSH DI ;AN000;;LB. | ||
| 768 | PUSH DX ;AN000;;LB. | ||
| 769 | PUSH CX ;AN000;;LB. | ||
| 770 | XOR CX,CX ;AN000;;LB. we want first sector in SC | ||
| 771 | CALL SC2BUF2 ;AN000;;LB. move data from SC to buffer | ||
| 772 | POP CX ;AN000;;LB. | ||
| 773 | POP DX ;AN000;;LB. | ||
| 774 | POP DI ;AN000;;LB. | ||
| 775 | POP ES ;AN000;;LB. | ||
| 776 | POP SI ;AN000;;LB. | ||
| 777 | POP DS ;AN000;;LB. | ||
| 778 | JMP SHORT sc2end ;AN000;;LB. return | ||
| 779 | |||
| 780 | scerror: ;AN000; | ||
| 781 | MOV [CALLSCNT],1 ;AN000;;LB. reset sector count to 1 | ||
| 782 | MOV [SC_STATUS],0 ;AN000;;LB. invalidate all SC sectors | ||
| 783 | MOV [CURSC_DRIVE],-1 ;AN000;;LB. invalidate drive | ||
| 784 | STC ;AN000;;LB. carry set | ||
| 785 | return ;AN000;;LB. | ||
| 786 | |||
| 787 | sc2end: ;AN000; | ||
| 788 | CLC ;AN000;;LB. carry clear | ||
| 789 | return ;AN000;;LB. | ||
| 790 | |||
| 791 | EndProc VIRREAD ;AN000; | ||
| 792 | |||
| 793 | Break <SC2BUF- move data from SC to buffer> | ||
| 794 | |||
| 795 | ; Inputs: [SC_STATUS] = SC validity status | ||
| 796 | ; [SC_SECTOR_SIZE] = request sector size | ||
| 797 | ; [SC_CACHE_PTR] = pointer to SC | ||
| 798 | ; Function: | ||
| 799 | ; Move data from SC to buffer | ||
| 800 | ; Output: | ||
| 801 | ; carry clear, in SC and data is moved | ||
| 802 | ; carry set, not in SC and data is not moved | ||
| 803 | |||
| 804 | procedure SC2BUF,NEAR ;AN000; | ||
| 805 | ASSUME DS:NOTHING,ES:NOTHING ;AN000; | ||
| 806 | |||
| 807 | CALL IN_SC ;AN000;;LB. in secondary cache | ||
| 808 | JC noSC ;AN000;;LB. no | ||
| 809 | MOV AX,1 ;AN000;;LB. check if valid sector | ||
| 810 | SHL AX,CL ;AN000;;LB. in the secondary cache | ||
| 811 | TEST [SC_STATUS],AX ;AN000;;LB. | ||
| 812 | JZ noSC ;AN000;;LB. invalid | ||
| 813 | entry SC2BUF2 ;AN000; | ||
| 814 | MOV AX,CX ;AN000;;LB. times index with | ||
| 815 | MUL [SC_SECTOR_SIZE] ;AN000;;LB. sector size | ||
| 816 | ADD AX,WORD PTR [SC_CACHE_PTR] ;AN000;;LB. add SC starting addr | ||
| 817 | ADC DX,WORD PTR [SC_CACHE_PTR+2];AN000;;LB. | ||
| 818 | MOV DS,DX ;AN000; ;LB. DS:SI-> SC sector addr | ||
| 819 | MOV SI,AX ;AN000; ;LB. | ||
| 820 | MOV ES,WORD PTR [CALLXAD+2] ;AN000; ;LB. ES:DI-> buffer addr | ||
| 821 | MOV DI,WORD PTR [CALLXAD] ;AN000; ;LB. | ||
| 822 | MOV CX,[SC_SECTOR_SIZE] ;AN000; ;LB. count= sector size | ||
| 823 | SHR CX,1 ;AN000; ;LB. may use DWORD move for 386 | ||
| 824 | entry MOVWORDS ;AN000; | ||
| 825 | CMP [DDMOVE],0 ;AN000; ;LB. 386 ? | ||
| 826 | JZ nodd ;AN000; ;LB. no | ||
| 827 | SHR CX,1 ;AN000; ;LB. words/2 | ||
| 828 | DB 66H ;AN000; ;LB. use double word move | ||
| 829 | nodd: | ||
| 830 | REP MOVSW ;AN000; ;LB. move to buffer | ||
| 831 | CLC ;AN000; ;LB. clear carry | ||
| 832 | return ;AN000; ;LB. exit | ||
| 833 | noSC: ;AN000; | ||
| 834 | STC ;AN000; ;LB. set carry | ||
| 835 | sexit: ;AN000; | ||
| 836 | return ;AN000; ;LB. | ||
| 837 | |||
| 838 | EndProc SC2BUF | ||
| 839 | CODE ENDS | ||
| 840 | END | ||
| 841 | \ No newline at end of file | ||