| ;; $Id$ |
| ;; ----------------------------------------------------------------------- |
| ;; |
| ;; Copyright 1994-2002 H. Peter Anvin - All Rights Reserved |
| ;; |
| ;; This program is free software; you can redistribute it and/or modify |
| ;; it under the terms of the GNU General Public License as published by |
| ;; the Free Software Foundation, Inc., 53 Temple Place Ste 330, |
| ;; Boston MA 02111-1307, USA; either version 2 of the License, or |
| ;; (at your option) any later version; incorporated herein by reference. |
| ;; |
| ;; ----------------------------------------------------------------------- |
| |
| ;; |
| ;; getc.inc |
| ;; |
| ;; Simple file handling library (open, getc, ungetc) |
| ;; |
| |
| ; |
| ; open,getc: Load a file a character at a time for parsing in a manner |
| ; similar to the C library getc routine. Only one simultaneous |
| ; use is supported. Note: "open" trashes the trackbuf. |
| ; |
| ; open: Input: mangled filename in DS:DI |
| ; Output: ZF set on file not found or zero length |
| ; |
| ; openfd: Input: file handle in SI |
| ; Output: none |
| ; |
| ; getc: Output: CF set on end of file |
| ; Character loaded in AL |
| ; |
| open: |
| call searchdir |
| jz openfd.ret |
| openfd: |
| pushf |
| mov [FBytes],ax |
| mov [FBytes+2],dx |
| mov eax,[FBytes] |
| add eax,SECTOR_SIZE-1 |
| shr eax,SECTOR_SHIFT |
| mov [FSectors],eax ; Number of sectors |
| mov [FNextClust],si ; Cluster pointer |
| mov ax,[EndOfGetCBuf] ; Pointer at end of buffer -> |
| mov [FPtr],ax ; nothing loaded yet |
| popf ; Restore no ZF |
| .ret: ret |
| |
| getc: |
| stc ; If we exit here -> EOF |
| mov ecx,[FBytes] |
| jecxz .ret |
| mov si,[FPtr] |
| cmp si,[EndOfGetCBuf] |
| jb .loaded |
| ; Buffer empty -- load another set |
| mov ecx,[FSectors] |
| cmp ecx,trackbufsize >> SECTOR_SHIFT |
| jna .oksize |
| mov ecx,trackbufsize >> SECTOR_SHIFT |
| .oksize: sub [FSectors],ecx ; Reduce remaining clusters |
| mov si,[FNextClust] |
| push es ; ES may be != DS, save old ES |
| push ds |
| pop es |
| mov bx,getcbuf |
| push bx |
| call getfssec ; Load a trackbuf full of data |
| mov [FNextClust],si ; Store new next pointer |
| pop si ; SI -> newly loaded data |
| pop es ; Restore ES |
| .loaded: lodsb ; Load a byte, increment SI |
| mov [FPtr],si ; Update next byte pointer |
| dec dword [FBytes] ; Update bytes left counter |
| clc ; Not EOF |
| .ret: ret |
| |
| ; |
| ; ungetc: Push a character (in AL) back into the getc buffer |
| ; Note: if more than one byte is pushed back, this may cause |
| ; bytes to be written below the getc buffer boundary. If there |
| ; is a risk for this to occur, the getcbuf base address should |
| ; be moved up. |
| ; |
| ungetc: |
| mov si,[FPtr] |
| dec si |
| mov [si],al |
| mov [FPtr],si |
| inc dword [FBytes] |
| ret |
| |
| ; |
| ; skipspace: Skip leading whitespace using "getc". If we hit end-of-line |
| ; or end-of-file, return with carry set; ZF = true of EOF |
| ; ZF = false for EOLN; otherwise CF = ZF = 0. |
| ; |
| ; Otherwise AL = first character after whitespace |
| ; |
| skipspace: |
| .loop: call getc |
| jc .eof |
| cmp al,1Ah ; DOS EOF |
| je .eof |
| cmp al,0Ah |
| je .eoln |
| cmp al,' ' |
| jbe .loop |
| ret ; CF = ZF = 0 |
| .eof: cmp al,al ; Set ZF |
| stc ; Set CF |
| ret |
| .eoln: add al,0FFh ; Set CF, clear ZF |
| ret |
| |
| ; |
| ; getint: Load an integer from the getc file. |
| ; Return CF if error; otherwise return integer in EBX |
| ; |
| getint: |
| mov di,NumBuf |
| .getnum: cmp di,NumBufEnd ; Last byte in NumBuf |
| jae .loaded |
| push di |
| call getc |
| pop di |
| jc .loaded |
| stosb |
| cmp al,'-' |
| jnb .getnum |
| call ungetc ; Unget non-numeric |
| .loaded: mov byte [di],0 |
| mov si,NumBuf |
| ; Fall through to parseint |
| |
| ; |
| ; parseint: Convert an integer to a number in EBX |
| ; Get characters from string in DS:SI |
| ; Return CF on error |
| ; DS:SI points to first character after number |
| ; |
| ; Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+K, val+M |
| ; |
| parseint: |
| push eax |
| push ecx |
| push bp |
| xor eax,eax ; Current digit (keep eax == al) |
| mov ebx,eax ; Accumulator |
| mov ecx,ebx ; Base |
| xor bp,bp ; Used for negative flag |
| .begin: lodsb |
| cmp al,'-' |
| jne .not_minus |
| xor bp,1 ; Set unary minus flag |
| jmp short .begin |
| .not_minus: |
| cmp al,'0' |
| jb .err |
| je .octhex |
| cmp al,'9' |
| ja .err |
| mov cl,10 ; Base = decimal |
| jmp short .foundbase |
| .octhex: |
| lodsb |
| cmp al,'0' |
| jb .km ; Value is zero |
| or al,20h ; Downcase |
| cmp al,'x' |
| je .ishex |
| cmp al,'7' |
| ja .err |
| mov cl,8 ; Base = octal |
| jmp short .foundbase |
| .ishex: |
| mov al,'0' ; No numeric value accrued yet |
| mov cl,16 ; Base = hex |
| .foundbase: |
| call unhexchar |
| jc .km ; Not a (hex) digit |
| cmp al,cl |
| jae .km ; Invalid for base |
| imul ebx,ecx ; Multiply accumulated by base |
| add ebx,eax ; Add current digit |
| lodsb |
| jmp short .foundbase |
| .km: |
| dec si ; Back up to last non-numeric |
| lodsb |
| or al,20h |
| cmp al,'k' |
| je .isk |
| cmp al,'m' |
| je .ism |
| dec si ; Back up |
| .fini: and bp,bp |
| jz .ret ; CF=0! |
| neg ebx ; Value was negative |
| .done: clc |
| .ret: pop bp |
| pop ecx |
| pop eax |
| ret |
| .err: stc |
| jmp short .ret |
| .isk: shl ebx,10 ; x 2^10 |
| jmp short .done |
| .ism: shl ebx,20 ; x 2^20 |
| jmp short .done |
| |
| |
| section .bss |
| alignb 4 |
| NumBuf resb 15 ; Buffer to load number |
| NumBufEnd resb 1 ; Last byte in NumBuf |
| FBytes resd 1 ; Number of bytes left in getc file |
| FSectors resd 1 ; Number of sectors in getc file |
| FNextClust resw 1 ; Pointer to next cluster in d:o |
| FPtr resw 1 ; Pointer to next char in buffer |
| |
| ; |
| ; unhexchar: Convert a hexadecimal digit in AL to the equivalent number; |
| ; return CF=1 if not a hex digit |
| ; |
| section .text |
| unhexchar: |
| cmp al,'0' |
| jb .ret ; If failure, CF == 1 already |
| cmp al,'9' |
| ja .notdigit |
| sub al,'0' ; CF <- 0 |
| ret |
| .notdigit: or al,20h ; upper case -> lower case |
| cmp al,'a' |
| jb .ret ; If failure, CF == 1 already |
| cmp al,'f' |
| ja .err |
| sub al,'a'-10 ; CF <- 0 |
| ret |
| .err: stc |
| .ret: ret |
| |
| ; |
| ; |
| ; getline: Get a command line, converting control characters to spaces |
| ; and collapsing streches to one; a space is appended to the |
| ; end of the string, unless the line is empty. |
| ; The line is terminated by ^J, ^Z or EOF and is written |
| ; to ES:DI. On return, DI points to first char after string. |
| ; CF is set if we hit EOF. |
| ; |
| getline: |
| call skipspace |
| mov dl,1 ; Empty line -> empty string. |
| jz .eof ; eof |
| jc .eoln ; eoln |
| call ungetc |
| .fillloop: push dx |
| push di |
| call getc |
| pop di |
| pop dx |
| jc .ret ; CF set! |
| cmp al,' ' |
| jna .ctrl |
| xor dx,dx |
| .store: stosb |
| jmp short .fillloop |
| .ctrl: cmp al,10 |
| je .ret ; CF clear! |
| cmp al,26 |
| je .eof |
| and dl,dl |
| jnz .fillloop ; Ignore multiple spaces |
| mov al,' ' ; Ctrl -> space |
| inc dx |
| jmp short .store |
| .eoln: clc ; End of line is not end of file |
| jmp short .ret |
| .eof: stc |
| .ret: pushf ; We want the last char to be space! |
| and dl,dl |
| jnz .xret |
| mov al,' ' |
| stosb |
| .xret: popf |
| ret |