blob: 275c0fabc9dc7e6fc3cbefc0313f4629a99df632 [file] [log] [blame]
;; -----------------------------------------------------------------------
;;
;; Copyright 1994-2008 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)
;;
;; WARNING: This interface uses the real_mode_seg/comboot_seg.
;;
MAX_GETC_LG2 equ 4 ; Max number of file nesting
MAX_GETC equ (1 << MAX_GETC_LG2)
bytes_per_getc_lg2 equ 16-MAX_GETC_LG2
bytes_per_getc equ (1 << bytes_per_getc_lg2)
secs_per_getc equ bytes_per_getc/SECTOR_SIZE
MAX_UNGET equ 5 ; Max bytes that can be pushed back
struc getc_file
gc_file resw 1 ; File pointer
gc_bufbytes resw 1 ; Bytes left in buffer
gc_bytes resd 1 ; Bytes left in file
gc_bufdata resw 1 ; Pointer to data in buffer
gc_unget_cnt resb 1 ; Character pushed back count
gc_unget_buf resb MAX_UNGET ; Character pushed back buffer
endstruc
getc_file_lg2 equ 4 ; Size of getc_file as a power of 2
%ifndef DEPEND
%if (getc_file_size != (1 << getc_file_lg2))
%error "getc_file_size != (1 << getc_file_lg2)"
%endif
%endif
;
; open,getc: Load a file a character at a time for parsing in a manner
; similar to the C library getc routine.
; Up to MAX_GETC files can be open at the same time,
; they are accessed in a stack-like fashion.
;
; All routines assume CS == DS.
;
; open: Input: mangled filename in DS:DI
; Output: ZF set on file not found or zero length
;
; openfd: Input: file handle in SI
; Output: ZF set on getc stack overflow
;
; getc: Output: CF set on end of file
; Character loaded in AL
;
; close: Output: CF set if nothing open
;
open:
call searchdir
jz openfd.ret
openfd:
push bx
mov bx,[CurrentGetC]
sub bx,getc_file_size
cmp bx,GetCStack
jb .stack_full ; Excessive nesting
mov [CurrentGetC],bx
mov [bx+gc_file],si ; File pointer
mov [bx+gc_bytes],ax ; Bytes available
mov [bx+gc_bytes+2],dx
xor ax,ax
mov [bx+gc_bufbytes],ax ; Buffer empty
mov [bx+gc_unget_cnt],al ; ungetc buffer empty
inc ax ; ZF <- 0
pop bx
.ret: ret
.stack_full:
call close_file
xor ax,ax ; ZF <- 1
pop bx
ret
getc:
push bx
push si
push di
push es
mov di,[CurrentGetC]
movzx bx,byte [di+gc_unget_cnt]
and bx,bx
jnz .have_unget
mov si,real_mode_seg ; Borrow the real_mode_seg
mov es,si
.got_data:
sub word [di+gc_bufbytes],1
jc .get_data ; Was it zero already?
mov si,[di+gc_bufdata]
mov al,[es:si]
inc si
mov [di+gc_bufdata],si
.done:
clc
.ret:
pop es
pop di
pop si
pop bx
ret
.have_unget:
dec bx
mov al,[di+bx+gc_unget_buf]
mov [di+gc_unget_cnt],bl
jmp .done
.get_data:
pushad
; Compute start of buffer
mov bx,di
sub bx,GetCStack
shl bx,bytes_per_getc_lg2-getc_file_lg2
mov [di+gc_bufdata],bx
mov si,[di+gc_file]
mov ecx,[di+gc_bytes]
jecxz .empty
cmp ecx,bytes_per_getc
jna .sizeok
mov ecx,bytes_per_getc
.sizeok:
mov [di+gc_bufbytes],cx
sub [di+gc_bytes],ecx
add cx,SECTOR_SIZE-1
shr cx,SECTOR_SHIFT
call getfssec
mov [di+gc_file],si
popad
jmp .got_data
.empty:
; CX == 0 at this point; gc_bufbytes was clobbered
; by the subtract; we need to restore it to zero so
; we will continue to get EOF on any further attempts
; to read the file.
mov [di+gc_bufbytes],cx
popad
xor al,al ; Return a predictable zero
stc
jmp .ret
close:
push bx
push si
mov bx,[CurrentGetC]
mov si,[bx+gc_file]
call close_file
add bx,getc_file_size
mov [CurrentGetC],bx
pop si
pop bx
ret
;
; ungetc: Push a character (in AL) back into the getc buffer
; Note: if more than MAX_UNGET bytes are pushed back, all
; hell will break loose.
;
ungetc:
push di
push bx
mov di,[CurrentGetC]
movzx bx,[di+gc_unget_cnt]
mov [bx+di+gc_unget_buf],al
inc bx
mov [di+gc_unget_cnt],bl
pop bx
pop di
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+[KMG]
;
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
cmp al,'g'
je .isg
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
.isg: shl ebx,10 ; * 2^30
.ism: shl ebx,10 ; * 2^20
.isk: shl ebx,10 ; * 2^10
jmp .fini
section .bss1
alignb 4
NumBuf resb 15 ; Buffer to load number
NumBufEnd resb 1 ; Last byte in NumBuf
GetCStack resb getc_file_size*MAX_GETC
.end equ $
section .data
CurrentGetC dw GetCStack.end ; GetCStack empty
;
; 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