title stream i/o include asm.inc SIO_BUFSIZ equ 8192 ; stream io buffer size stream_io_str struc sio_position dd ? ; stream position sio_offset dd ? ; buffer offset sio_read_bc dw ? ; buffer byte count for reading sio_write_bc dw ? ; buffer byte count for writing sio_file_handle dw ? ; dos file handle sio_buffer db SIO_BUFSIZ dup(?) ; stream buffer stream_io_str ends .data ertx_disk_full db 'Disk full',0 ertx_fopen_mode db 'Bad mode in fopen',0 ertx_out_of_handles db 'Out of stream handles',0 ertx_stream_handle db 'Bad stream handle',0 .data? stream_pointers dd FOPEN_MAX dup(?) .code extn free,ms_dos,ms_dos_strerror,set_strerror,malloc public fclose,fopen,fread,fwrite ;; fclose ; ; entry BX stream handle ; exit Cf if error closing file or bad handle ; uses AX,BX ; fclose proc pushm di,si,es call fwrite_flush ; write bytes left in stream buffer jc fcl1 ; if disk full call stream_struct_write ; access stream structure jc fcl1 ; if bad handle mov si,bx ; clear stream structure pointer add si,si add si,si mov wptr stream_pointers[bp+si],NULL_POINTER mov wptr stream_pointers[bp+si+2],NULL_POINTER mov bx,es:sio_file_handle[di] call free mov ah,3Eh call ms_dos_strerror fcl1: popm es,si,di ret fclose endp ;; fopen ; ; entry AX mode, 'r' or 'w' to open for reading or writing ; DS:SI filename ; exit BX stream handle ; Cf if error ; uses AX ; fopen proc pushm cx,dx,di,es cmp ax,'r' mov dx,3D00h je fop1 ; if opening file for reading only cmp ax,'w' jne fop3 ; if not open for write (unknown mode) mov dh,3Ch mov cx,0 fop1: mov ax,dx mov dx,si call ms_dos_strerror ; (ms dos with error handling) jc fop2 ; if file open failed mov dx,ax call malloc_stream jc fop4 ; if no memory or stream handles mov al,0 ; zero stream structure (except for mov cx,sio_buffer ; buffer space) push di rep stosb pop di mov es:sio_file_handle[di],dx clc fop2: popm es,di,dx,cx ret fop3: lea ax,ertx_fopen_mode ; *Bad mode in fopen* call set_strerror jmp fop2 fop4: mov ah,3Eh ; close file, no memory for structure mov bx,dx call ms_dos stc jmp fop2 fopen endp ;; fread ; ; entry BX stream handle ; CX byte count ; ES:DI destination pointer ; exit AX actual byte count (!=CX for EOF) ; DI updated ; Cf if error ; fread proc pushm di,bx,cx,dx,si,ds cmp bx,stdin je fre3 ; if special handle, standard input call stream_struct_read jc fre4 mov bx,si fre1: jcxz fre4 ; if read finished mov ax,wptr sio_position[bx]; compute stream position w/i buffer mov dx,wptr sio_position[bx+2] sub ax,wptr sio_offset[bx] sbb dx,wptr sio_offset[bx+2] jnz fre2 ; if buffer not w/i 64k of position mov dx,sio_read_bc[bx] ; get number of bytes to end of buffer sub dx,ax jbe fre2 ; if stream position not in buffer lea si,sio_buffer[bx] add si,ax call rep_movsb_limit add wptr sio_position[bx],dx ; update stream position adc wptr sio_position[bx+2],0 jmp fre1 fre2: call fread_primitive ; fill stream buffer jbe fre4 ; if error (Cf==1) or EOF (Zf==1) jmp fre1 ; else successful fre3: mov ah,3Fh ; read from special handle stdin mov dx,es mov ds,dx mov dx,di call ms_dos_strerror jc fre4 add di,ax ; (should not set Cf unless DI wraps) fre4: popm ds,si,dx,cx,bx,ax jc fre5 ; if error neg ax ; else return bytes read add ax,di clc fre5: ret fread endp ;; fread primitive ; ; entry DS:BX stream structure ; exit AX bytes read from file ; Cf if error (Zf unknown) ; Zf if end of file ; uses SI ; fread_primitive proc pushm bx,cx,dx mov si,bx mov ax,4200h ; position file mov bx,sio_file_handle[si] mov dx,wptr sio_position[si] mov cx,wptr sio_position[si+2] call ms_dos_strerror jc frp1 ; if error position file mov wptr sio_offset[si],ax mov wptr sio_offset[si+2],dx mov sio_read_bc[si],0 mov ah,3Fh ; read file mov cx,size sio_buffer lea dx,sio_buffer[si] call ms_dos_strerror jc frp1 ; if read error mov sio_read_bc[si],ax or ax,ax ; set Zf for end of file frp1: popm dx,cx,bx ret fread_primitive endp ;; fwrite ; ; entry BX stream handle ; CX byte count ; DS:SI source pointer ; exit AX bytes written ; SI updated ; Cf if error ; fwrite proc pushm si,cx,dx,di,es cmp bx,stdout ; check for special streams je fwr2 ; if writing to standard out cmp bx,stderr je fwr2 ; if writing to standard error call fwrite_flush_maybe ; flush buffer in event of seek jc fwr3 ; if bad handle or disk full call stream_struct_write ; access stream structure jc fwr3 ; if bad handle fwr1: clc jcxz fwr3 ; if write complete mov ax,es:sio_write_bc[di] ; compute space left in stream buffer mov dx,size sio_buffer sub dx,ax jb fwr3 ; if internal error (Cf=1) push di ; copy to stream buffer lea di,sio_buffer[di] add di,ax call rep_movsb_limit pop di add ax,dx ; update stream buffer byte count mov es:sio_write_bc[di],ax ; and position add wptr es:sio_position[di],dx adc wptr es:sio_position[di+2],0 cmp ax,size sio_buffer ; check stream buffer level jb fwr1 ; if not full call fwrite_flush ; else write buffer to disk jnc fwr1 ; if write successful jmp fwr3 ; else disk full (Cf=1) fwr2: mov ah,40h ; write standard output/error mov dx,si call ms_dos_strerror jc fwr3 ; if write failed add si,ax fwr3: popm es,di,dx,cx,ax jc fwr4 ; if error neg ax ; else return bytes written add ax,si clc fwr4: ret fwrite endp ;; fwrite flush ; ; entry BX stream handle ; exit Cf if disk full, bad handle, or other error ; uses AX ; fwrite_flush proc pushm bx,cx,dx,si,ds call stream_struct_read jc fwf1 ; if bad handle mov cx,sio_write_bc[si] jcxz fwf1 ; if buffer empty (nothing to flush) mov ax,4200h ; position file mov bx,sio_file_handle[si] mov dx,wptr sio_offset[si] mov cx,wptr sio_offset[si+2] call ms_dos_strerror jc fwf1 ; if position failed mov ah,40h ; write file mov cx,sio_write_bc[si] lea dx,sio_buffer[si] call ms_dos_strerror jc fwf1 ; if write failed add wptr sio_offset[si],ax ; update buffer position adc wptr sio_offset[si+2],0 mov sio_write_bc[si],0 ; clear output byte count cmp ax,cx je fwf1 ; if correct byte cnt written (Cf=0) lea ax,ertx_disk_full ; else *Disk full* call set_strerror fwf1: popm ds,si,dx,cx,bx ret fwrite_flush endp ;; fwrite flush maybe ; ; entry BX stream handle ; exit Cf if bad handle or disk full ; uses AX,DX ; fwrite_flush_maybe proc pushm si,ds call stream_struct_read jc ffm2 ; if bad stream handle mov ax,wptr sio_offset[si] ; compute buffer position mov dx,wptr sio_offset[si+2] add ax,sio_write_bc[si] adc dx,0 cmp ax,wptr sio_position[si] jne ffm1 ; if wrong position cmp dx,wptr sio_position[si+2] je ffm2 ; if right position ffm1: call fwrite_flush jc ffm2 ; if disk full mov ax,wptr sio_position[si] ; reposition buffer mov wptr sio_offset[si],ax mov ax,wptr sio_position[si+2] mov wptr sio_offset[si+2],ax ffm2: popm ds,si ret fwrite_flush_maybe endp ;; malloc stream ; ; exit BX stream handle ; ES:DI stream structure ; Cf if no handles or memory left ; uses AX ; malloc_stream proc pushm cx,si mov bx,stderr ; find free stream handle mas1: inc bx cmp bx,FOPEN_MAX jae mas3 ; if no handles left mov si,bx add si,si add si,si les di,stream_pointers[bp+si] mov ax,es or ax,di jnz mas1 ; if stream allocated mov cx,size stream_io_str ; allocate memory for stream structure call malloc jc mas2 ; if no memory mov wptr stream_pointers[bp+si],di mov wptr stream_pointers[bp+si+2],es mas2: popm si,cx ret mas3: lea ax,ertx_out_of_handles ; *Out of stream handles* call set_strerror jmp mas2 malloc_stream endp ;; rep movsb limit ; ; entry DS:SI source pointer ; ES:DI destination pointer ; CX byte count ; DX byte count limit ; exit SI,DI updated ; CX updated (nonzero if original DX