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/GETSET.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/GETSET.ASM')
| -rw-r--r-- | v4.0/src/DOS/GETSET.ASM | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/v4.0/src/DOS/GETSET.ASM b/v4.0/src/DOS/GETSET.ASM new file mode 100644 index 0000000..70c8cd3 --- /dev/null +++ b/v4.0/src/DOS/GETSET.ASM | |||
| @@ -0,0 +1,912 @@ | |||
| 1 | ; SCCSID = @(#)getset.asm 1.2 85/07/23 | ||
| 2 | TITLE GETSET - GETting and SETting MS-DOS system calls | ||
| 3 | NAME GETSET | ||
| 4 | ; | ||
| 5 | ; System Calls which get and set various things | ||
| 6 | ; | ||
| 7 | ; $GET_VERSION | ||
| 8 | ; $GET_VERIFY_ON_WRITE | ||
| 9 | ; $SET_VERIFY_ON_WRITE | ||
| 10 | ; $INTERNATIONAL | ||
| 11 | ; $GET_DRIVE_FREESPACE | ||
| 12 | ; $GET_DMA | ||
| 13 | ; $SET_DMA | ||
| 14 | ; $GET_DEFAULT_DRIVE | ||
| 15 | ; $SET_DEFAULT_DRIVE | ||
| 16 | ; $GET_INTERRUPT_VECTOR | ||
| 17 | ; $SET_INTERRUPT_VECTOR | ||
| 18 | ; RECSET | ||
| 19 | ; $CHAR_OPER | ||
| 20 | ; $GetExtendedError DOS 3.3 | ||
| 21 | ; Get_Global_CdPg DOS 4.0 | ||
| 22 | ; $ECS_CALL DOS 4.0 | ||
| 23 | ; | ||
| 24 | ; Revision history: | ||
| 25 | ; | ||
| 26 | ; Created: ARR 30 March 1983 | ||
| 27 | ; | ||
| 28 | ; A000 version 4.0 Jan. 1988 | ||
| 29 | ; A006 D503-- fake version for IBMCACHE | ||
| 30 | ; A008 P4070- faske version for MS WINDOWS | ||
| 31 | |||
| 32 | .xlist | ||
| 33 | ; | ||
| 34 | ; get the appropriate segment definitions | ||
| 35 | ; | ||
| 36 | include dosseg.asm | ||
| 37 | |||
| 38 | IFNDEF ALTVECT | ||
| 39 | ALTVECT EQU 0 ; FALSE | ||
| 40 | ENDIF | ||
| 41 | |||
| 42 | CODE SEGMENT BYTE PUBLIC 'CODE' | ||
| 43 | ASSUME SS:DOSGROUP,CS:DOSGROUP | ||
| 44 | |||
| 45 | .xcref | ||
| 46 | include dossym.inc | ||
| 47 | include devsym.inc | ||
| 48 | include doscntry.inc | ||
| 49 | .cref | ||
| 50 | .list | ||
| 51 | |||
| 52 | i_need USERNUM,WORD | ||
| 53 | i_need MSVERS,WORD | ||
| 54 | i_need VERFLG,BYTE | ||
| 55 | i_need CNTCFLAG,BYTE | ||
| 56 | i_need DMAADD,DWORD | ||
| 57 | i_need CURDRV,BYTE | ||
| 58 | i_need chSwitch,BYTE | ||
| 59 | i_need COUNTRY_CDPG,byte ;DOS 3.3 | ||
| 60 | I_need CDSCount,BYTE | ||
| 61 | I_need ThisCDS,DWORD | ||
| 62 | i_need EXTERR,WORD | ||
| 63 | i_need EXTERR_ACTION,BYTE | ||
| 64 | i_need EXTERR_CLASS,BYTE | ||
| 65 | i_need EXTERR_LOCUS,BYTE | ||
| 66 | i_need EXTERRPT,DWORD | ||
| 67 | i_need UCASE_TAB,BYTE | ||
| 68 | i_need FILE_UCASE_TAB,BYTE | ||
| 69 | i_need InterCon,BYTE | ||
| 70 | i_need CURRENTPDB,WORD | ||
| 71 | i_need DBCS_TAB,BYTE ;AN000; | ||
| 72 | i_need Special_version,WORD ;AN006; | ||
| 73 | i_need Fake_Count,BYTE ;AN008; | ||
| 74 | i_need NLS_YES,BYTE ;AN000; | ||
| 75 | i_need NLS_yes2,BYTE ;AN000; | ||
| 76 | i_need NLS_NO,BYTE ;AN000; | ||
| 77 | i_need NLS_no2,BYTE ;AN000; | ||
| 78 | |||
| 79 | |||
| 80 | BREAK <$Get_Version -- Return DOS version number> | ||
| 81 | procedure $GET_VERSION,NEAR | ||
| 82 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 83 | |||
| 84 | ; Inputs: | ||
| 85 | ; None | ||
| 86 | ; Function: | ||
| 87 | ; Return DOS version number | ||
| 88 | ; Outputs: | ||
| 89 | ; OEM number in BH | ||
| 90 | ; User number in BL:CX (24 bits) | ||
| 91 | ; Version number as AL.AH in binary | ||
| 92 | ; NOTE: On pre 1.28 DOSs AL will be zero | ||
| 93 | |||
| 94 | context DS | ||
| 95 | MOV BX,[USERNUM + 2] | ||
| 96 | MOV CX,[USERNUM] | ||
| 97 | MOV AX,[MSVERS] | ||
| 98 | invoke get_user_stack | ||
| 99 | ASSUME DS:NOTHING | ||
| 100 | MOV [SI.user_BX],BX | ||
| 101 | MOV [SI.user_CX],CX | ||
| 102 | CMP CS:[Fake_Count],0FFH ;AN008; | ||
| 103 | JZ reg ;AN008; | ||
| 104 | CMP CS:[Fake_Count],0 ;AN008; | ||
| 105 | JZ usual ;AN008; | ||
| 106 | DEC CS:[Fake_Count] ;AN008; | ||
| 107 | reg: ;AN008; | ||
| 108 | CMP CS:[Special_version],0 ;AN006; | ||
| 109 | JZ usual ;AN006; | ||
| 110 | MOV AX,CS:[Special_version] ;AN006; | ||
| 111 | usual: ;AN006; | ||
| 112 | MOV [SI.user_AX],AX ; Really only sets AH | ||
| 113 | return | ||
| 114 | EndProc $GET_VERSION | ||
| 115 | |||
| 116 | BREAK <$Get_Verify_on_Write - return verify-after-write flag> | ||
| 117 | procedure $GET_VERIFY_ON_WRITE,NEAR | ||
| 118 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 119 | |||
| 120 | ; Inputs: | ||
| 121 | ; none. | ||
| 122 | ; Function: | ||
| 123 | ; returns flag | ||
| 124 | ; Returns: | ||
| 125 | ; AL = value of VERIFY flag | ||
| 126 | |||
| 127 | MOV AL,[VERFLG] | ||
| 128 | return | ||
| 129 | EndProc $GET_VERIFY_ON_WRITE | ||
| 130 | |||
| 131 | BREAK <$Set_Verify_on_Write - Toggle verify-after-write flag> | ||
| 132 | procedure $SET_VERIFY_ON_WRITE,NEAR | ||
| 133 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 134 | |||
| 135 | ; Inputs: | ||
| 136 | ; AL = desired value of VERIFY flag | ||
| 137 | ; Function: | ||
| 138 | ; Sets flag | ||
| 139 | ; Returns: | ||
| 140 | ; None | ||
| 141 | |||
| 142 | AND AL,1 | ||
| 143 | MOV [VERFLG],AL | ||
| 144 | return | ||
| 145 | EndProc $SET_VERIFY_ON_WRITE | ||
| 146 | |||
| 147 | BREAK <$International - return country-dependent information> | ||
| 148 | ; | ||
| 149 | ; Inputs: | ||
| 150 | ; MOV AH,International | ||
| 151 | ; MOV AL,country (al = 0 => current country) | ||
| 152 | ; [MOV BX,country] | ||
| 153 | ; LDS DX,block | ||
| 154 | ; INT 21 | ||
| 155 | ; Function: | ||
| 156 | ; give users an idea of what country the application is running | ||
| 157 | ; Outputs: | ||
| 158 | ; IF DX != -1 on input (get country) | ||
| 159 | ; AL = 0 means return current country table. | ||
| 160 | ; 0<AL<0FFH means return country table for country AL | ||
| 161 | ; AL = 0FF means return country table for country BX | ||
| 162 | ; No Carry: | ||
| 163 | ; Register BX will contain the 16-bit country code. | ||
| 164 | ; Register AL will contain the low 8 bits of the country code. | ||
| 165 | ; The block pointed to by DS:DX is filled in with the information | ||
| 166 | ; for the particular country. | ||
| 167 | ; BYTE Size of this table excluding this byte and the next | ||
| 168 | ; BYTE Country code represented by this table | ||
| 169 | ; A sequence of n bytes, where n is the number specified | ||
| 170 | ; by the first byte above and is not > internat_block_max, | ||
| 171 | ; in the correct order for being returned by the | ||
| 172 | ; INTERNATIONAL call as follows: | ||
| 173 | ; WORD Date format 0=mdy, 1=dmy, 2=ymd | ||
| 174 | ; 5 BYTE Currency symbol null terminated | ||
| 175 | ; 2 BYTE thousands separator null terminated | ||
| 176 | ; 2 BYTE Decimal point null terminated | ||
| 177 | ; 2 BYTE Date separator null terminated | ||
| 178 | ; 2 BYTE Time separator null terminated | ||
| 179 | ; 1 BYTE Bit field. Currency format. | ||
| 180 | ; Bit 0. =0 $ before # =1 $ after # | ||
| 181 | ; Bit 1. no. of spaces between # and $ (0 or 1) | ||
| 182 | ; 1 BYTE No. of significant decimal digits in currency | ||
| 183 | ; 1 BYTE Bit field. Time format. | ||
| 184 | ; Bit 0. =0 12 hour clock =1 24 hour | ||
| 185 | ; DWORD Call address of case conversion routine | ||
| 186 | ; 2 BYTE Data list separator null terminated. | ||
| 187 | ; Carry: | ||
| 188 | ; Register AX has the error code. | ||
| 189 | ; IF DX = -1 on input (set current country) | ||
| 190 | ; AL = 0 is an error | ||
| 191 | ; 0<AL<0FFH means set current country to country AL | ||
| 192 | ; AL = 0FF means set current country to country BX | ||
| 193 | ; No Carry: | ||
| 194 | ; Current country SET | ||
| 195 | ; Register AL will contain the low 8 bits of the country code. | ||
| 196 | ; Carry: | ||
| 197 | ; Register AX has the error code. | ||
| 198 | |||
| 199 | |||
| 200 | procedure $INTERNATIONAL,NEAR ; DOS 3.3 | ||
| 201 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 202 | CMP AL,0FFH | ||
| 203 | JZ BX_HAS_CODE ; -1 means country code is in BX | ||
| 204 | MOV BL,AL ; Put AL country code in BX | ||
| 205 | XOR BH,BH | ||
| 206 | BX_HAS_CODE: | ||
| 207 | PUSH DS | ||
| 208 | POP ES | ||
| 209 | PUSH DX | ||
| 210 | POP DI ; User buffer to ES:DI | ||
| 211 | context DS | ||
| 212 | CMP DI,-1 | ||
| 213 | JZ international_set | ||
| 214 | OR BX,BX | ||
| 215 | JNZ international_find | ||
| 216 | MOV SI,OFFSET DOSGROUP:COUNTRY_CDPG | ||
| 217 | JMP SHORT international_copy | ||
| 218 | |||
| 219 | international_find: | ||
| 220 | MOV BP,0 ; flag it for GetCntry only | ||
| 221 | CALL international_get | ||
| 222 | JC errtn | ||
| 223 | CMP BX,0 ; nlsfunc finished it ? | ||
| 224 | JNZ SHORT international_copy ; no, copy by myself | ||
| 225 | MOV BX,DX ; put country back | ||
| 226 | JMP SHORT international_ok3 | ||
| 227 | |||
| 228 | international_get: | ||
| 229 | MOV SI,OFFSET DOSGROUP:COUNTRY_CDPG | ||
| 230 | CMP BX,[SI.ccDosCountry] ; = current country id | ||
| 231 | retz ; return if equal | ||
| 232 | MOV DX,BX | ||
| 233 | XOR BX,BX ; bx = 0, default code page | ||
| 234 | CallInstall NLSInstall,NLSFUNC,0 ; check if NLSFUNC in memory | ||
| 235 | CMP AL,0FFH | ||
| 236 | JNZ interr ; not in memory | ||
| 237 | CMP BP,0 ; GetCntry ? | ||
| 238 | JNZ stcdpg | ||
| 239 | CallInstall GetCntry,NLSFUNC,4 ; get country info | ||
| 240 | JMP chkok | ||
| 241 | stcdpg: | ||
| 242 | CallInstall SetCodePage,NLSFUNC,3 ; set country info | ||
| 243 | chkok: | ||
| 244 | CMP AL,0 ; success ? | ||
| 245 | retz ; yes | ||
| 246 | setcarry: | ||
| 247 | STC ; set carry | ||
| 248 | ret | ||
| 249 | interr: | ||
| 250 | MOV AL,0FFH ; flag nlsfunc error | ||
| 251 | JMP setcarry | ||
| 252 | |||
| 253 | international_copy: | ||
| 254 | MOV BX,[SI.ccDosCountry] ; = current country id | ||
| 255 | MOV SI,OFFSET DOSGROUP:COUNTRY_CDPG.ccDFormat | ||
| 256 | MOV CX,OLD_COUNTRY_SIZE | ||
| 257 | REP MOVSB ;copy country info | ||
| 258 | international_ok3: | ||
| 259 | invoke get_user_stack | ||
| 260 | ASSUME DS:NOTHING | ||
| 261 | MOV [SI.user_BX],BX | ||
| 262 | international_ok: | ||
| 263 | MOV AX,BX ; Return country code in AX too. | ||
| 264 | transfer SYS_RET_OK | ||
| 265 | |||
| 266 | international_set: | ||
| 267 | ASSUME DS:DOSGROUP | ||
| 268 | MOV BP,1 ; flag it for SetCodePage only | ||
| 269 | CALL international_get | ||
| 270 | JNC international_ok | ||
| 271 | errtn: | ||
| 272 | CMP AL,0FFH | ||
| 273 | JZ errtn2 | ||
| 274 | transfer SYS_RET_ERR ; return what we got from NLSFUNC | ||
| 275 | errtn2: | ||
| 276 | error error_Invalid_Function ; NLSFUNC not existent | ||
| 277 | |||
| 278 | |||
| 279 | EndProc $INTERNATIONAL | ||
| 280 | |||
| 281 | |||
| 282 | |||
| 283 | BREAK <$GetExtCntry - return extended country-dependent information> | ||
| 284 | ; | ||
| 285 | ; Inputs: | ||
| 286 | ; if AL >= 20H | ||
| 287 | ; AL= 20H capitalize single char, DL= char | ||
| 288 | ; 21H capitalize string ,CX= string length | ||
| 289 | ; 22H capitalize ASCIIZ string | ||
| 290 | ; 23H YES/NO check, DL=1st char DH= 2nd char (DBCS) | ||
| 291 | ; 80H bit 0 = use normal upper case table | ||
| 292 | ; 1 = use file upper case table | ||
| 293 | ; DS:DX points to string | ||
| 294 | ; | ||
| 295 | ; else | ||
| 296 | ; | ||
| 297 | ; MOV AH,GetExtCntry ; DOS 3.3 | ||
| 298 | ; MOV AL,INFO_ID ( info type,-1 selects all) | ||
| 299 | ; MOV BX,CODE_PAGE ( -1 = active code page ) | ||
| 300 | ; MOV DX,COUNTRY_ID ( -1 = active country ) | ||
| 301 | ; MOV CX,SIZE ( amount of data to return) | ||
| 302 | ; LES DI,COUNTRY_INFO ( buffer for returned data ) | ||
| 303 | ; INT 21 | ||
| 304 | ; Function: | ||
| 305 | ; give users extended country dependent information | ||
| 306 | ; or capitalize chars | ||
| 307 | ; Outputs: | ||
| 308 | ; No Carry: | ||
| 309 | ; extended country info is succesfully returned | ||
| 310 | ; Carry: | ||
| 311 | ; Register AX has the error code. | ||
| 312 | ; AX=0, NO for YES/NO CHECK | ||
| 313 | ; 1, YES | ||
| 314 | |||
| 315 | |||
| 316 | procedure $GetExtCntry,NEAR ; DOS 3.3 | ||
| 317 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 318 | CMP AL,CAP_ONE_CHAR ;AN000;MS. < 20H ? | ||
| 319 | JAE capcap ;AN000;MS. | ||
| 320 | JMP notcap ;AN000;MS. yes | ||
| 321 | capcap: ;AN000; | ||
| 322 | TEST AL,UPPER_TABLE ;AN000;MS. which upper case table | ||
| 323 | JNZ fileupper ;AN000;MS. file upper case | ||
| 324 | MOV BX,OFFSET DOSGROUP:UCASE_TAB+2 ;AN000;MS. get normal upper case | ||
| 325 | JMP SHORT capit ;AN000;MS. | ||
| 326 | fileupper: ;AN000; | ||
| 327 | MOV BX,OFFSET DOSGROUP:FILE_UCASE_TAB+2;AN000;MS. get file upper case | ||
| 328 | capit: ;AN000; | ||
| 329 | CMP AL,CAP_ONE_CHAR ;AN000;;MS.cap one char ? | ||
| 330 | JNZ chkyes ;AN000;;MS. no | ||
| 331 | MOV AL,DL ;AN000;;MS. set up AL | ||
| 332 | invoke GETLET3 ;AN000;;MS. upper case it | ||
| 333 | invoke get_user_stack ;AN000;;MS. get user stack | ||
| 334 | MOV byte ptr [SI.user_DX],AL;AN000;;MS. user's DL=AL | ||
| 335 | JMP SHORT nono ;AN000;;MS. done | ||
| 336 | chkyes: ;AN000; | ||
| 337 | CMP AL,CHECK_YES_NO ;AN000;;MS. check YES or NO ? | ||
| 338 | JNZ capstring ;AN000;;MS. no | ||
| 339 | XOR AX,AX ;AN000;;MS. presume NO | ||
| 340 | IF DBCS ;AN000; | ||
| 341 | PUSH AX ;AN000;;MS. | ||
| 342 | MOV AL,DL ;AN000;;MS. | ||
| 343 | invoke TESTKANJ ;AN000;;MS. DBCS ? | ||
| 344 | POP AX ;AN000;;MS. | ||
| 345 | JNZ dbcs_char ;AN000;;MS. yes, return error | ||
| 346 | ENDIF ;AN000; | ||
| 347 | ;AN000; | ||
| 348 | CMP DL,NLS_YES ;AN000;;MS. is 'Y' ? | ||
| 349 | JZ yesyes ;AN000;;MS. yes | ||
| 350 | CMP DL,NLS_yes2 ;AN000;;MS. is 'y' ? | ||
| 351 | JZ yesyes ;AN000;;MS. yes | ||
| 352 | CMP DL,NLS_NO ;AN000;;MS. is 'N'? | ||
| 353 | JZ nono ;AN000;;MS. no | ||
| 354 | CMP DL,NLS_no2 ;AN000;;MS. is 'n' ? | ||
| 355 | JZ nono ;AN000;;MS. no | ||
| 356 | dbcs_char: ;AN000; | ||
| 357 | INC AX ;AN000;;MS. not YES or NO | ||
| 358 | yesyes: ;AN000' | ||
| 359 | INC AX ;AN000;;MS. return 1 | ||
| 360 | nono: ;AN000; | ||
| 361 | transfer SYS_RET_OK ;AN000;;MS. done | ||
| 362 | capstring: ;AN000; | ||
| 363 | MOV SI,DX ;AN000;;MS. si=dx | ||
| 364 | CMP AL,CAP_STRING ;AN000;;MS. cap string ? | ||
| 365 | JNZ capascii ;AN000;;MS. no | ||
| 366 | CMP CX,0 ;AN000;;MS. check count 0 | ||
| 367 | JZ nono ;AN000;;MS. yes finished | ||
| 368 | concap: ;AN000; | ||
| 369 | LODSB ;AN000;;MS. get char | ||
| 370 | IF DBCS ;AN000;;MS. | ||
| 371 | invoke TESTKANJ ;AN000;;MS. DBCS ? | ||
| 372 | JZ notdbcs ;AN000;;MS. no | ||
| 373 | INC SI ;AN000;;MS. skip 2 chars | ||
| 374 | DEC CX ;AN000;;MS. bad input, one DBCS char at end | ||
| 375 | JZ nono ;AN000;;MS. yes | ||
| 376 | JMP SHORT next99 ;AN000;;MS. | ||
| 377 | notdbcs: ;AN000; | ||
| 378 | ENDIF ;AN000; | ||
| 379 | |||
| 380 | invoke GETLET3 ;AN000;;MS. upper case it | ||
| 381 | MOV byte ptr [SI-1],AL ;AN000;;MS. store back | ||
| 382 | next99: ;AN000; | ||
| 383 | LOOP concap ;AN000;;MS. continue | ||
| 384 | JMP nono ;AN000;;MS. done | ||
| 385 | capascii: ;AN000; | ||
| 386 | CMP AL,CAP_ASCIIZ ;AN000;;MS. cap ASCIIZ string ? | ||
| 387 | JNZ capinval ;AN000;;MS. no | ||
| 388 | concap2: ;AN000; | ||
| 389 | LODSB ;AN000;;MS. get char | ||
| 390 | CMP AL,0 ;AN000;;MS. end of string ? | ||
| 391 | JZ nono ;AN000;;MS. yes | ||
| 392 | IF DBCS ;AN000;;MS. | ||
| 393 | invoke TESTKANJ ;AN000;;MS. DBCS ? | ||
| 394 | JZ notdbcs2 ;AN000;;MS. no | ||
| 395 | CMP BYTE PTR [SI],0 ;AN000;;MS. bad input, one DBCS char at end | ||
| 396 | JZ nono ;AN000;;MS. yes | ||
| 397 | INC SI ;AN000;;MS. skip 2 chars | ||
| 398 | JMP concap2 ;AN000;;MS. | ||
| 399 | notdbcs2: ;AN000; | ||
| 400 | ENDIF ;AN000; | ||
| 401 | invoke GETLET3 ;AN000;;MS. upper case it | ||
| 402 | MOV byte ptr [SI-1],AL ;AN000;;MS. store back | ||
| 403 | JMP concap2 ;AN000;;MS. continue | ||
| 404 | |||
| 405 | |||
| 406 | notcap: | ||
| 407 | CMP CX,5 ; minimum size is 5 | ||
| 408 | JB sizeerror | ||
| 409 | context DS | ||
| 410 | MOV SI,OFFSET DOSGROUP:COUNTRY_CDPG | ||
| 411 | CMP DX,-1 ; active country ? | ||
| 412 | JNZ GETCDPG ; no | ||
| 413 | MOV DX,[SI.ccDosCountry] ; get active country id | ||
| 414 | GETCDPG: | ||
| 415 | CMP BX,-1 ; active code page? | ||
| 416 | JNZ CHKAGAIN ; no, check again | ||
| 417 | MOV BX,[SI.ccDosCodePage] ; get active code page id | ||
| 418 | CHKAGAIN: | ||
| 419 | CMP DX,[SI.ccDosCountry] ; same as active country id? | ||
| 420 | JNZ CHKNLS ; no | ||
| 421 | CMP BX,[SI.ccDosCodePage] ; same as active code page id? | ||
| 422 | JNZ CHKNLS ; no | ||
| 423 | CHKTYPE: | ||
| 424 | MOV BX,[SI.ccSysCodePage] ; bx = sys code page id | ||
| 425 | ; CMP AL,SetALL ; select all? | ||
| 426 | ; JNZ SELONE | ||
| 427 | ; MOV SI,OFFSET DOSGROUP:COUNTRY_CDPG.ccNumber_of_entries | ||
| 428 | SELONE: | ||
| 429 | PUSH CX ; save cx | ||
| 430 | MOV CX,[SI.ccNumber_of_entries] | ||
| 431 | MOV SI,OFFSET DOSGROUP:COUNTRY_CDPG.ccSetUcase | ||
| 432 | NXTENTRY: | ||
| 433 | CMP AL,[SI] ; compare info type | ||
| 434 | JZ FOUNDIT | ||
| 435 | ADD SI,5 ; next entry | ||
| 436 | LOOP NXTENTRY | ||
| 437 | POP CX | ||
| 438 | capinval: | ||
| 439 | error error_Invalid_Function ; info type not found | ||
| 440 | FOUNDIT: | ||
| 441 | MOVSB ; move info id byte | ||
| 442 | POP CX ; retsore char count | ||
| 443 | CMP AL,SetCountryInfo ; select country info type ? | ||
| 444 | JZ setsize | ||
| 445 | MOV CX,4 ; 4 bytes will be moved | ||
| 446 | MOV AX,5 ; 5 bytes will be returned in CX | ||
| 447 | OK_RETN: | ||
| 448 | REP MOVSB ; copy info | ||
| 449 | MOV CX,AX ; CX = actual length returned | ||
| 450 | MOV AX,BX ; return sys code page in ax | ||
| 451 | GETDONE: | ||
| 452 | invoke get_user_stack ; return actual length to user's CX | ||
| 453 | MOV [SI.user_CX],CX | ||
| 454 | transfer SYS_RET_OK | ||
| 455 | setsize: | ||
| 456 | SUB CX,3 ; size after length field | ||
| 457 | CMP WORD PTR [SI],CX ; less than table size | ||
| 458 | JAE setsize2 ; no | ||
| 459 | MOV CX,WORD PTR [SI] ; truncate to table size | ||
| 460 | setsize2: | ||
| 461 | MOV ES:[DI],CX ; copy actual length to user's | ||
| 462 | ADD DI,2 ; update index | ||
| 463 | ADD SI,2 | ||
| 464 | MOV AX,CX | ||
| 465 | ADD AX,3 ; AX has the actual length | ||
| 466 | JMP OK_RETN ; go move it | ||
| 467 | CHKNLS: | ||
| 468 | XOR AH,AH | ||
| 469 | PUSH AX ; save info type | ||
| 470 | POP BP ; bp = info type | ||
| 471 | CallInstall NLSInstall,NLSFUNC,0 ; check if NLSFUNC in memory | ||
| 472 | CMP AL,0FFH | ||
| 473 | JZ NLSNXT ; in memory | ||
| 474 | sizeerror: | ||
| 475 | error error_Invalid_Function | ||
| 476 | NLSNXT: CallInstall GetExtInfo,NLSFUNC,2 ;get extended info | ||
| 477 | CMP AL,0 ; success ? | ||
| 478 | JNZ NLSERROR | ||
| 479 | MOV AX,[SI.ccSysCodePage] ; ax = sys code page id | ||
| 480 | JMP GETDONE | ||
| 481 | NLSERROR: | ||
| 482 | transfer SYS_RET_ERR ; return what is got from NLSFUNC | ||
| 483 | |||
| 484 | EndProc $GetExtCntry | ||
| 485 | |||
| 486 | BREAK <$GetSetCdPg - get or set global code page> | ||
| 487 | ; | ||
| 488 | ; Inputs: | ||
| 489 | ; MOV AH,GetSetCdPg ; DOS 3.3 | ||
| 490 | ; MOV AL,n ; n = 1 : get code page, n = 2 : set code page | ||
| 491 | ; MOV BX,CODE_PAGE ( set code page only) | ||
| 492 | ; INT 21 | ||
| 493 | ; Function: | ||
| 494 | ; get or set the global code page | ||
| 495 | ; Outputs: | ||
| 496 | ; No Carry: | ||
| 497 | ; global code page is set (set global code page) | ||
| 498 | ; BX = active code page id (get global code page) | ||
| 499 | ; DX = system code page id (get global code page) | ||
| 500 | ; Carry: | ||
| 501 | ; Register AX has the error code. | ||
| 502 | |||
| 503 | |||
| 504 | procedure $GetSetCdPg,NEAR ; DOS 3.3 | ||
| 505 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 506 | context DS | ||
| 507 | MOV SI,OFFSET DOSGROUP:COUNTRY_CDPG | ||
| 508 | CMP AL,1 ; get global code page | ||
| 509 | JNZ setglpg ; set global cod epage | ||
| 510 | MOV BX,[SI.ccDosCodePage] ; get active code page id | ||
| 511 | MOV DX,[SI.ccSysCodePage] ; get sys code page id | ||
| 512 | invoke get_user_stack | ||
| 513 | ASSUME DS:NOTHING | ||
| 514 | MOV [SI.user_BX],BX ; update returned bx | ||
| 515 | MOV [SI.user_DX],DX ; update returned dx | ||
| 516 | OK_RETURN: | ||
| 517 | transfer SYS_RET_OK | ||
| 518 | ASSUME DS:DOSGROUP | ||
| 519 | setglpg: | ||
| 520 | CMP AL,2 | ||
| 521 | JNZ nomem | ||
| 522 | ;;;;;;; CMP BX,[SI.ccDosCodePage] ; same as active code page | ||
| 523 | ;;;;;;; JZ OK_RETURN ; yes | ||
| 524 | MOV DX,[SI.ccDosCountry] | ||
| 525 | CallInstall NLSInstall,NLSFUNC,0 ; check if NLSFUNC in memory | ||
| 526 | CMP AL,0FFH | ||
| 527 | JNZ nomem ; not in memory | ||
| 528 | CallInstall SetCodePage,NLSFUNC,1 ;set the code page | ||
| 529 | CMP AL,0 ; success ? | ||
| 530 | JZ OK_RETURN ; yes | ||
| 531 | CMP AL,65 ; set device code page failed | ||
| 532 | JNZ seterr | ||
| 533 | MOV AX,65 | ||
| 534 | MOV [EXTERR],AX | ||
| 535 | MOV [EXTERR_ACTION],errACT_Ignore | ||
| 536 | MOV [EXTERR_CLASS],errCLASS_HrdFail | ||
| 537 | MOV [EXTERR_LOCUS],errLOC_SerDev | ||
| 538 | transfer From_GetSet | ||
| 539 | |||
| 540 | seterr: | ||
| 541 | transfer SYS_RET_ERR | ||
| 542 | nomem: | ||
| 543 | error error_Invalid_Function ; function not defined | ||
| 544 | ; | ||
| 545 | EndProc $GetSetCdPg | ||
| 546 | |||
| 547 | |||
| 548 | |||
| 549 | |||
| 550 | BREAK <$Get_Drive_Freespace -- Return bytes of free disk space on a drive> | ||
| 551 | procedure $GET_DRIVE_FREESPACE,NEAR | ||
| 552 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 553 | |||
| 554 | ; Inputs: | ||
| 555 | ; DL = Drive number | ||
| 556 | ; Function: | ||
| 557 | ; Return number of free allocation units on drive | ||
| 558 | ; Outputs: | ||
| 559 | ; BX = Number of free allocation units | ||
| 560 | ; DX = Total Number of allocation units on disk | ||
| 561 | ; CX = Sector size | ||
| 562 | ; AX = Sectors per allocation unit | ||
| 563 | ; = -1 if bad drive specified | ||
| 564 | ; This call returns the same info in the same registers (except for FAT pointer) | ||
| 565 | ; as the old FAT pointer calls | ||
| 566 | |||
| 567 | context DS | ||
| 568 | MOV AL,DL | ||
| 569 | invoke GetThisDrv ; Get drive | ||
| 570 | SET_AX_RET: | ||
| 571 | JC BADFDRV | ||
| 572 | invoke DISK_INFO | ||
| 573 | XCHG DX,BX | ||
| 574 | JC SET_AX_RET ; User FAILed to I 24 | ||
| 575 | XOR AH,AH ; Chuck Fat ID byte | ||
| 576 | DoSt: | ||
| 577 | invoke get_user_stack | ||
| 578 | ASSUME DS:NOTHING | ||
| 579 | MOV [SI.user_DX],DX | ||
| 580 | MOV [SI.user_CX],CX | ||
| 581 | MOV [SI.user_BX],BX | ||
| 582 | MOV [SI.user_AX],AX | ||
| 583 | return | ||
| 584 | BADFDRV: | ||
| 585 | ; MOV AL,error_invalid_drive ; Assume error | ||
| 586 | invoke FCB_RET_ERR | ||
| 587 | MOV AX,-1 | ||
| 588 | JMP DoSt | ||
| 589 | EndProc $GET_DRIVE_FREESPACE | ||
| 590 | |||
| 591 | BREAK <$Get_DMA, $Set_DMA -- Get/Set current DMA address> | ||
| 592 | procedure $GET_DMA,NEAR | ||
| 593 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 594 | |||
| 595 | ; Inputs: | ||
| 596 | ; None | ||
| 597 | ; Function: | ||
| 598 | ; Get DISK TRANSFER ADDRESS | ||
| 599 | ; Returns: | ||
| 600 | ; ES:BX is current transfer address | ||
| 601 | |||
| 602 | MOV BX,WORD PTR [DMAADD] | ||
| 603 | MOV CX,WORD PTR [DMAADD+2] | ||
| 604 | invoke get_user_stack | ||
| 605 | MOV [SI.user_BX],BX | ||
| 606 | MOV [SI.user_ES],CX | ||
| 607 | return | ||
| 608 | EndProc $GET_DMA | ||
| 609 | |||
| 610 | procedure $SET_DMA,NEAR | ||
| 611 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 612 | |||
| 613 | ; Inputs: | ||
| 614 | ; DS:DX is desired new disk transfer address | ||
| 615 | ; Function: | ||
| 616 | ; Set DISK TRANSFER ADDRESS | ||
| 617 | ; Returns: | ||
| 618 | ; None | ||
| 619 | |||
| 620 | MOV WORD PTR [DMAADD],DX | ||
| 621 | MOV WORD PTR [DMAADD+2],DS | ||
| 622 | return | ||
| 623 | EndProc $SET_DMA | ||
| 624 | |||
| 625 | BREAK <$Get_Default_Drive, $Set_Default_Drive -- Set/Get default drive> | ||
| 626 | procedure $GET_DEFAULT_DRIVE,NEAR | ||
| 627 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 628 | |||
| 629 | ; Inputs: | ||
| 630 | ; None | ||
| 631 | ; Function: | ||
| 632 | ; Return current drive number | ||
| 633 | ; Returns: | ||
| 634 | ; AL = drive number | ||
| 635 | |||
| 636 | MOV AL,[CURDRV] | ||
| 637 | return | ||
| 638 | EndProc $GET_DEFAULT_DRIVE | ||
| 639 | |||
| 640 | procedure $SET_DEFAULT_DRIVE,NEAR | ||
| 641 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 642 | |||
| 643 | ; Inputs: | ||
| 644 | ; DL = Drive number for new default drive | ||
| 645 | ; Function: | ||
| 646 | ; Set the default drive | ||
| 647 | ; Returns: | ||
| 648 | ; AL = Number of drives, NO ERROR RETURN IF DRIVE NUMBER BAD | ||
| 649 | |||
| 650 | MOV AL,DL | ||
| 651 | INC AL ; A=1, b=2... | ||
| 652 | invoke GetVisDrv ; see if visible drive | ||
| 653 | JC SETRET ; errors do not set | ||
| 654 | ; LDS SI,ThisCDS ; get CDS | ||
| 655 | ; TEST [SI].curdir_flags,curdir_splice ; was it spliced? | ||
| 656 | ; JNZ SetRet ; yes, do not set | ||
| 657 | MOV [CURDRV],AL ; no, set | ||
| 658 | SETRET: | ||
| 659 | MOV AL,[CDSCOUNT] ; let user see what the count really is | ||
| 660 | RET17: return | ||
| 661 | EndProc $SET_DEFAULT_DRIVE | ||
| 662 | |||
| 663 | BREAK <$Get_Interrupt_Vector - Get/Set interrupt vectors> | ||
| 664 | procedure $GET_INTERRUPT_VECTOR,NEAR | ||
| 665 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 666 | |||
| 667 | ; Inputs: | ||
| 668 | ; AL = interrupt number | ||
| 669 | ; Function: | ||
| 670 | ; Get the interrupt vector | ||
| 671 | ; Returns: | ||
| 672 | ; ES:BX is current interrupt vector | ||
| 673 | |||
| 674 | CALL RECSET | ||
| 675 | LES BX,DWORD PTR ES:[BX] | ||
| 676 | invoke get_user_stack | ||
| 677 | MOV [SI.user_BX],BX | ||
| 678 | MOV [SI.user_ES],ES | ||
| 679 | return | ||
| 680 | EndProc $GET_INTERRUPT_VECTOR | ||
| 681 | |||
| 682 | procedure $SET_INTERRUPT_VECTOR,NEAR | ||
| 683 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 684 | |||
| 685 | ; Inputs: | ||
| 686 | ; AL = interrupt number | ||
| 687 | ; DS:DX is desired new interrupt vector | ||
| 688 | ; Function: | ||
| 689 | ; Set the interrupt vector | ||
| 690 | ; Returns: | ||
| 691 | ; None | ||
| 692 | |||
| 693 | CALL RECSET | ||
| 694 | CLI ; Watch out!!!!! Folks sometimes use | ||
| 695 | MOV ES:[BX],DX ; this for hardware ints (like timer). | ||
| 696 | MOV ES:[BX+2],DS | ||
| 697 | STI | ||
| 698 | return | ||
| 699 | EndProc $SET_INTERRUPT_VECTOR | ||
| 700 | |||
| 701 | IF ALTVECT | ||
| 702 | TABLE SEGMENT | ||
| 703 | VECIN: | ||
| 704 | ; INPUT VECTORS | ||
| 705 | Public GSET001S,GSET001E | ||
| 706 | GSET001S label byte | ||
| 707 | DB 22H ; Terminate | ||
| 708 | DB 23H ; ^C | ||
| 709 | DB 24H ; Hard error | ||
| 710 | DB 28H ; Spooler | ||
| 711 | LSTVEC DB ? ; ALL OTHER | ||
| 712 | |||
| 713 | VECOUT: | ||
| 714 | ; GET MAPPED VECTOR | ||
| 715 | DB int_terminate | ||
| 716 | DB int_ctrl_c | ||
| 717 | DB int_fatal_abort | ||
| 718 | DB int_spooler | ||
| 719 | LSTVEC2 DB ? ; Map to itself | ||
| 720 | |||
| 721 | NUMVEC = VECOUT-VECIN | ||
| 722 | GSET001E label byte | ||
| 723 | TABLE ENDS | ||
| 724 | ENDIF | ||
| 725 | |||
| 726 | procedure RECSET,NEAR | ||
| 727 | |||
| 728 | IF ALTVECT | ||
| 729 | context ES | ||
| 730 | MOV [LSTVEC],AL ; Terminate list with real vector | ||
| 731 | MOV [LSTVEC2],AL ; Terminate list with real vector | ||
| 732 | MOV CX,NUMVEC ; Number of possible translations | ||
| 733 | MOV DI,OFFSET DOSGROUP:VECIN ; Point to vectors | ||
| 734 | REPNE SCASB | ||
| 735 | MOV AL,ES:[DI+NUMVEC-1] ; Get translation | ||
| 736 | ENDIF | ||
| 737 | |||
| 738 | XOR BX,BX | ||
| 739 | MOV ES,BX | ||
| 740 | MOV BL,AL | ||
| 741 | SHL BX,1 | ||
| 742 | SHL BX,1 | ||
| 743 | return | ||
| 744 | EndProc recset | ||
| 745 | |||
| 746 | BREAK <$Char_Oper - hack on paths, switches so that xenix can look like PCDOS> | ||
| 747 | ; | ||
| 748 | ; input: AL = function: | ||
| 749 | ; 0 - read switch char | ||
| 750 | ; 1 - set switch char (char in DL) | ||
| 751 | ; 2 - read device availability | ||
| 752 | ; Always returns available | ||
| 753 | ; 3 - set device availability | ||
| 754 | ; No longer supported (NOP) | ||
| 755 | ; output: (get) DL - character/flag | ||
| 756 | ; | ||
| 757 | procedure $CHAR_OPER,NEAR | ||
| 758 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 759 | context DS | ||
| 760 | CMP AL,1 | ||
| 761 | JB CharGetSw | ||
| 762 | JZ CharSetSw | ||
| 763 | CMP AL,3 | ||
| 764 | JB CharGetDev | ||
| 765 | JZ CharSetDev | ||
| 766 | MOV AL,-1 | ||
| 767 | return | ||
| 768 | CharGetSw: | ||
| 769 | MOV DL,chSwitch | ||
| 770 | JMP SHORT CharSet | ||
| 771 | CharSetSw: | ||
| 772 | MOV chSwitch,DL | ||
| 773 | return | ||
| 774 | CharGetDev: | ||
| 775 | MOV DL,-1 | ||
| 776 | CharSet: | ||
| 777 | Invoke Get_User_Stack | ||
| 778 | ASSUME DS:NOTHING | ||
| 779 | MOV [SI.User_DX],DX | ||
| 780 | CharSetDev: | ||
| 781 | return | ||
| 782 | EndProc $CHAR_OPER | ||
| 783 | |||
| 784 | BREAK <$GetExtendedError - Return Extended DOS error code> | ||
| 785 | ; | ||
| 786 | ; input: None | ||
| 787 | ; output: AX = Extended error code (0 means no extended error) | ||
| 788 | ; BL = recommended action | ||
| 789 | ; BH = class of error | ||
| 790 | ; CH = locus of error | ||
| 791 | ; ES:DI = may be pointer | ||
| 792 | ; | ||
| 793 | procedure $GetExtendedError,NEAR | ||
| 794 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 795 | Context DS | ||
| 796 | MOV AX,[EXTERR] | ||
| 797 | LES DI,[EXTERRPT] | ||
| 798 | MOV BX,WORD PTR [EXTERR_ACTION] ; BL = Action, BH = Class | ||
| 799 | MOV CH,[EXTERR_LOCUS] | ||
| 800 | invoke get_user_stack | ||
| 801 | ASSUME DS:NOTHING | ||
| 802 | MOV [SI.user_DI],DI | ||
| 803 | MOV [SI.user_ES],ES | ||
| 804 | MOV [SI.user_BX],BX | ||
| 805 | MOV [SI.user_CX],CX | ||
| 806 | transfer SYS_RET_OK | ||
| 807 | EndProc $GetExtendedError | ||
| 808 | |||
| 809 | BREAK <$Get_Global_CdPg - Return Global Code Page> | ||
| 810 | ; | ||
| 811 | ; input: None | ||
| 812 | ; output: AX = Global Code Page | ||
| 813 | ; | ||
| 814 | procedure Get_Global_CdPg,NEAR | ||
| 815 | ASSUME DS:NOTHING,ES:NOTHING | ||
| 816 | PUSH SI | ||
| 817 | MOV SI,OFFSET DOSGROUP:COUNTRY_CDPG | ||
| 818 | MOV AX,CS:[SI.ccDosCodePage] | ||
| 819 | POP SI | ||
| 820 | return | ||
| 821 | EndProc Get_Global_CdPg | ||
| 822 | |||
| 823 | ;-------------------------------Start of DBCS 2/13/KK | ||
| 824 | BREAK <ECS_call - Extended Code System support function> | ||
| 825 | |||
| 826 | ASSUME DS:NOTHING, ES:NOTHING | ||
| 827 | |||
| 828 | procedure $ECS_call,NEAR | ||
| 829 | |||
| 830 | ; Inputs: | ||
| 831 | ; AL = 0 get lead byte table | ||
| 832 | ; on return DS:SI has the table location | ||
| 833 | ; | ||
| 834 | ; AL = 1 set / reset interim console flag | ||
| 835 | ; DL = flag (00H or 01H) | ||
| 836 | ; no return | ||
| 837 | ; | ||
| 838 | ; AL = 2 get interim console flag | ||
| 839 | ; on return DL = current flag value | ||
| 840 | ; | ||
| 841 | ; AL = OTHER then error, and returns with: | ||
| 842 | ; AX = error_invalid_function | ||
| 843 | ; | ||
| 844 | ; NOTE: THIS CALL DOES GUARANTEE THAT REGISTER OTHER THAN | ||
| 845 | ; SS:SP WILL BE PRESERVED! | ||
| 846 | |||
| 847 | IF DBCS ;AN000; | ||
| 848 | ;AN000; | ||
| 849 | or al, al ; AL = 0 (get table)? ;AN000; | ||
| 850 | je get_lbt ;AN000; | ||
| 851 | cmp al, SetInterimMode ; AL = 1 (set / reset interim flag)? ;AN000; | ||
| 852 | je set_interim ;AN000; | ||
| 853 | cmp al, GetInterimMode ; AL = 2 (get interim flag)? ;AN000; | ||
| 854 | je get_interim ;AN000; | ||
| 855 | error error_invalid_function ;AN000; | ||
| 856 | ;AN000; | ||
| 857 | get_lbt: ; get lead byte table ;AN000; | ||
| 858 | push ax ;AN000; | ||
| 859 | push bx ;AN000; | ||
| 860 | push ds ;AN000; | ||
| 861 | context DS ;AN000; | ||
| 862 | MOV BX,offset DOSGROUP:COUNTRY_CDPG.ccSetDBCS ;AN000; | ||
| 863 | MOV AX,[BX+1] ; set EV address to DS:SI ;AN000; | ||
| 864 | MOV BX,[BX+3] ;AN000; | ||
| 865 | ADD AX,2 ; Skip Lemgth ;AN000; | ||
| 866 | invoke get_user_stack ;AN000; | ||
| 867 | assume ds:nothing ;AN000; | ||
| 868 | MOV [SI.user_SI], AX ;AN000; | ||
| 869 | MOV [SI.user_DS], BX ;AN000; | ||
| 870 | pop ds ;AN000; | ||
| 871 | pop bx ;AN000; | ||
| 872 | pop ax ;AN000; | ||
| 873 | transfer SYS_RET_OK ;AN000; | ||
| 874 | |||
| 875 | set_interim: ; Set interim console flag ;AN000; | ||
| 876 | push dx ;AN000; | ||
| 877 | and dl,01 ; isolate bit 1 ;AN000; | ||
| 878 | mov [InterCon], dl ;AN000; | ||
| 879 | push ds ;AN000; | ||
| 880 | mov ds, [CurrentPDB] ;AN000; | ||
| 881 | mov byte ptr ds:[PDB_InterCon], dl ; update value in pdb ;AN000; | ||
| 882 | pop ds ;AN000; | ||
| 883 | pop dx ;AN000; | ||
| 884 | transfer SYS_RET_OK ;AN000; | ||
| 885 | |||
| 886 | get_interim: ;AN000; | ||
| 887 | push dx ;AN000; | ||
| 888 | push ds ;AN000; | ||
| 889 | mov dl,[InterCon] ;AN000; | ||
| 890 | invoke get_user_stack ; get interim console flag ;AN000; | ||
| 891 | assume ds:nothing ;AN000; | ||
| 892 | mov [SI.user_DX],DX ;AN000; | ||
| 893 | pop ds ;AN000; | ||
| 894 | pop dx ;AN000; | ||
| 895 | transfer SYS_RET_OK ;AN000; | ||
| 896 | ELSE ;AN000; | ||
| 897 | or al, al ; AL = 0 (get table)? ;AN000; | ||
| 898 | jnz okok ;AN000; | ||
| 899 | get_lbt: ;AN000; | ||
| 900 | invoke get_user_stack ;AN000; | ||
| 901 | assume ds:nothing ;AN000; | ||
| 902 | MOV [SI.user_SI], Offset Dosgroup:DBCS_TAB+2 ;AN000; | ||
| 903 | MOV [SI.user_DS], CS ;AN000; | ||
| 904 | okok: ;AN000; | ||
| 905 | transfer SYS_RET_OK ; ;AN000; | ||
| 906 | |||
| 907 | ENDIF ;AN000; | ||
| 908 | |||
| 909 | $ECS_call endp ;AN000; | ||
| 910 | |||
| 911 | CODE ENDS | ||
| 912 | END | ||