1;; ----------------------------------------------------------------------- 2;; 3;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved 4;; Copyright 2009 Intel Corporation; author: H. Peter Anvin 5;; 6;; This program is free software; you can redistribute it and/or modify 7;; it under the terms of the GNU General Public License as published by 8;; the Free Software Foundation, Inc., 53 Temple Place Ste 330, 9;; Boston MA 02111-1307, USA; either version 2 of the License, or 10;; (at your option) any later version; incorporated herein by reference. 11;; 12;; ----------------------------------------------------------------------- 13 14;; 15;; getc.inc 16;; 17;; Simple file handling library (open, getc, ungetc) 18;; 19;; WARNING: This interface uses the real_mode_seg/comboot_seg. 20;; 21 22MAX_GETC_LG2 equ 4 ; Max number of file nesting 23MAX_GETC equ (1 << MAX_GETC_LG2) 24bytes_per_getc_lg2 equ 16-MAX_GETC_LG2 25bytes_per_getc equ (1 << bytes_per_getc_lg2) 26MAX_UNGET equ 9 ; Max bytes that can be pushed back 27 28 struc getc_file 29gc_file resw 1 ; File pointer 30gc_bufbytes resw 1 ; Bytes left in buffer 31gc_bufdata resw 1 ; Pointer to data in buffer 32gc_unget_cnt resb 1 ; Character pushed back count 33gc_unget_buf resb MAX_UNGET ; Character pushed back buffer 34 endstruc 35getc_file_lg2 equ 4 ; Size of getc_file as a power of 2 36 37%ifndef DEPEND 38%if (getc_file_size != (1 << getc_file_lg2)) 39%error "getc_file_size != (1 << getc_file_lg2)" 40%endif 41%endif 42 43; 44; open,getc: Load a file a character at a time for parsing in a manner 45; similar to the C library getc routine. 46; Up to MAX_GETC files can be open at the same time, 47; they are accessed in a stack-like fashion. 48; 49; All routines assume CS == DS. 50; 51; open: Input: mangled filename in DS:DI 52; Output: ZF set on file not found or zero length 53; 54; openfd: Input: file handle in SI, file size in EAX 55; Output: ZF set on getc stack overflow 56; 57; getc: Output: CF set on end of file 58; Character loaded in AL 59; 60; close: Output: CF set if nothing open 61; 62 global core_open 63core_open: 64 pm_call pm_searchdir 65 jz openfd.ret 66openfd: 67 push bx 68 69 mov bx,[CurrentGetC] 70 sub bx,getc_file_size 71 cmp bx,GetCStack 72 jb .stack_full ; Excessive nesting 73 mov [CurrentGetC],bx 74 75 mov [bx+gc_file],si ; File pointer 76 xor ax,ax 77 mov [bx+gc_bufbytes],ax ; Buffer empty 78 mov [bx+gc_unget_cnt],al ; ungetc buffer empty 79 80 inc ax ; ZF <- 0 81 pop bx 82.ret: ret 83 84.stack_full: 85 pm_call pm_close_file 86 xor ax,ax ; ZF <- 1 87 pop bx 88 ret 89 90getc: 91 push bx 92 push si 93 push di 94 push es 95 96 mov di,[CurrentGetC] 97 movzx bx,byte [di+gc_unget_cnt] 98 and bx,bx 99 jnz .have_unget 100 101 mov si,real_mode_seg ; Borrow the real_mode_seg 102 mov es,si 103 104.got_data: 105 sub word [di+gc_bufbytes],1 106 jc .get_data ; Was it zero already? 107 mov si,[di+gc_bufdata] 108 mov al,[es:si] 109 inc si 110 mov [di+gc_bufdata],si 111.done: 112 clc 113.ret: 114 pop es 115 pop di 116 pop si 117 pop bx 118 ret 119.have_unget: 120 dec bx 121 mov al,[di+bx+gc_unget_buf] 122 mov [di+gc_unget_cnt],bl 123 jmp .done 124 125.get_data: 126 pushad 127 ; Compute start of buffer 128 mov bx,di 129 sub bx,GetCStack 130 shl bx,bytes_per_getc_lg2-getc_file_lg2 131 132 mov [di+gc_bufdata],bx 133 mov si,[di+gc_file] 134 and si,si 135 mov [di+gc_bufbytes],si ; In case SI == 0 136 jz .empty 137 mov cx,bytes_per_getc 138 pm_call getfsbytes 139 mov [di+gc_bufbytes],cx 140 mov [di+gc_file],si 141 jcxz .empty 142 popad 143 TRACER 'd' 144 jmp .got_data 145 146.empty: 147 TRACER 'e' 148 ; [di+gc_bufbytes] is zero already, thus we will continue 149 ; to get EOF on any further attempts to read the file. 150 popad 151 xor al,al ; Return a predictable zero 152 stc 153 jmp .ret 154 155; 156; This is similar to getc, except that we read up to CX bytes and 157; store them in ES:DI. Eventually this could get optimized... 158; 159; On return, CX and DI are adjusted by the number of bytes actually read. 160; 161readc: 162 push ax 163.loop: 164 call getc 165 jc .out 166 stosb 167 loop .loop 168.out: 169 pop ax 170 ret 171 172; 173; close: close the top of the getc stack 174; 175close: 176 push bx 177 push si 178 mov bx,[CurrentGetC] 179 mov si,[bx+gc_file] 180 pm_call pm_close_file 181 add bx,getc_file_size 182 mov [CurrentGetC],bx 183 pop si 184 pop bx 185 ret 186 187; 188; ungetc: Push a character (in AL) back into the getc buffer 189; Note: if more than MAX_UNGET bytes are pushed back, all 190; hell will break loose. 191; 192ungetc: 193 push di 194 push bx 195 mov di,[CurrentGetC] 196 movzx bx,[di+gc_unget_cnt] 197 mov [bx+di+gc_unget_buf],al 198 inc bx 199 mov [di+gc_unget_cnt],bl 200 pop bx 201 pop di 202 ret 203 204; 205; skipspace: Skip leading whitespace using "getc". If we hit end-of-line 206; or end-of-file, return with carry set; ZF = true of EOF 207; ZF = false for EOLN; otherwise CF = ZF = 0. 208; 209; Otherwise AL = first character after whitespace 210; 211skipspace: 212.loop: call getc 213 jc .eof 214 cmp al,1Ah ; DOS EOF 215 je .eof 216 cmp al,0Ah 217 je .eoln 218 cmp al,' ' 219 jbe .loop 220 ret ; CF = ZF = 0 221.eof: cmp al,al ; Set ZF 222 stc ; Set CF 223 ret 224.eoln: add al,0FFh ; Set CF, clear ZF 225 ret 226 227; 228; getint: Load an integer from the getc file. 229; Return CF if error; otherwise return integer in EBX 230; 231getint: 232 mov di,NumBuf 233.getnum: cmp di,NumBufEnd ; Last byte in NumBuf 234 jae .loaded 235 push di 236 call getc 237 pop di 238 jc .loaded 239 stosb 240 cmp al,'-' 241 jnb .getnum 242 call ungetc ; Unget non-numeric 243.loaded: mov byte [di],0 244 mov si,NumBuf 245 ; Fall through to parseint 246; 247; parseint: Convert an integer to a number in EBX 248; Get characters from string in DS:SI 249; Return CF on error 250; DS:SI points to first character after number 251; 252; Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+[KMG] 253; 254parseint: 255 push eax 256 push ecx 257 push bp 258 xor eax,eax ; Current digit (keep eax == al) 259 mov ebx,eax ; Accumulator 260 mov ecx,ebx ; Base 261 xor bp,bp ; Used for negative flag 262.begin: lodsb 263 cmp al,'-' 264 jne .not_minus 265 xor bp,1 ; Set unary minus flag 266 jmp short .begin 267.not_minus: 268 cmp al,'0' 269 jb .err 270 je .octhex 271 cmp al,'9' 272 ja .err 273 mov cl,10 ; Base = decimal 274 jmp short .foundbase 275.octhex: 276 lodsb 277 cmp al,'0' 278 jb .km ; Value is zero 279 or al,20h ; Downcase 280 cmp al,'x' 281 je .ishex 282 cmp al,'7' 283 ja .err 284 mov cl,8 ; Base = octal 285 jmp short .foundbase 286.ishex: 287 mov al,'0' ; No numeric value accrued yet 288 mov cl,16 ; Base = hex 289.foundbase: 290 call unhexchar 291 jc .km ; Not a (hex) digit 292 cmp al,cl 293 jae .km ; Invalid for base 294 imul ebx,ecx ; Multiply accumulated by base 295 add ebx,eax ; Add current digit 296 lodsb 297 jmp short .foundbase 298.km: 299 dec si ; Back up to last non-numeric 300 lodsb 301 or al,20h 302 cmp al,'k' 303 je .isk 304 cmp al,'m' 305 je .ism 306 cmp al,'g' 307 je .isg 308 dec si ; Back up 309.fini: and bp,bp 310 jz .ret ; CF=0! 311 neg ebx ; Value was negative 312.done: clc 313.ret: pop bp 314 pop ecx 315 pop eax 316 ret 317.err: stc 318 jmp short .ret 319.isg: shl ebx,10 ; * 2^30 320.ism: shl ebx,10 ; * 2^20 321.isk: shl ebx,10 ; * 2^10 322 jmp .fini 323 324 section .bss16 325 alignb 4 326NumBuf resb 15 ; Buffer to load number 327NumBufEnd resb 1 ; Last byte in NumBuf 328 329GetCStack resb getc_file_size*MAX_GETC 330.end equ $ 331 332 section .data16 333CurrentGetC dw GetCStack.end ; GetCStack empty 334 335; 336; unhexchar: Convert a hexadecimal digit in AL to the equivalent number; 337; return CF=1 if not a hex digit 338; 339 section .text16 340unhexchar: 341 cmp al,'0' 342 jb .ret ; If failure, CF == 1 already 343 cmp al,'9' 344 ja .notdigit 345 sub al,'0' ; CF <- 0 346 ret 347.notdigit: or al,20h ; upper case -> lower case 348 cmp al,'a' 349 jb .ret ; If failure, CF == 1 already 350 cmp al,'f' 351 ja .err 352 sub al,'a'-10 ; CF <- 0 353 ret 354.err: stc 355.ret: ret 356 357; 358; 359; getline: Get a command line, converting control characters to spaces 360; and collapsing streches to one; a space is appended to the 361; end of the string, unless the line is empty. 362; The line is terminated by ^J, ^Z or EOF and is written 363; to ES:DI. On return, DI points to first char after string. 364; CF is set if we hit EOF. 365; 366getline: 367 call skipspace 368 mov dl,1 ; Empty line -> empty string. 369 jz .eof ; eof 370 jc .eoln ; eoln 371 call ungetc 372.fillloop: push dx 373 push di 374 call getc 375 pop di 376 pop dx 377 jc .ret ; CF set! 378 cmp al,' ' 379 jna .ctrl 380 xor dx,dx 381.store: stosb 382 jmp short .fillloop 383.ctrl: cmp al,10 384 je .ret ; CF clear! 385 cmp al,26 386 je .eof 387 and dl,dl 388 jnz .fillloop ; Ignore multiple spaces 389 mov al,' ' ; Ctrl -> space 390 inc dx 391 jmp short .store 392.eoln: clc ; End of line is not end of file 393 jmp short .ret 394.eof: stc 395.ret: pushf ; We want the last char to be space! 396 and dl,dl 397 jnz .xret 398 mov al,' ' 399 stosb 400.xret: popf 401 ret 402 403; 404; parseint_esdi: 405; Same as parseint, but takes the input in ES:DI 406; 407parseint_esdi: 408 push ds 409 push es 410 pop ds 411 xchg si,di 412 call parseint 413 xchg si,di 414 pop ds 415 ret 416

