To: opendos AT delorie DOT com Subject: This device driver fails under OD Message-ID: <19970408.163726.6391.0.editor@juno.com> From: editor AT juno DOT com (Bruce Morgen) Date: Tue, 08 Apr 1997 16:39:14 EDT The ancient PC Magazine freeware device driver REDD.SYS fails under OD. Instead of stuffing the keyboard buffer with a command for execution by the shell (I use 4DOS), it stuffs the command followed by a long string of space (20h) characters until the speaker beeps like mad with the overflow. Any assembler jocks interested in looking into this and either patching the driver or reporting an OD bug? _______________________________________________________________________ ;==================================================================== ; REDD - Remote Exec Device Driver ; ; Copyright (c) 1992 Douglas Boling ;==================================================================== page 66,132 ;-------------------------------------------------------------------- ; BIOS Data segment ;-------------------------------------------------------------------- bios_data segment at 40h org 17h shift_state db ? ;State of shift keys org 1Ah keybuff_head dw ? ;Start ptr for key buff keybuff_tail dw ? ;End ptr for key buff org 4Eh video_buffoff dw ? ;Offset of video buffer org 63h video_ioregs dw ? ;I/O addr of video ctlr org 80h keybuff_start dw ? ;Start ptr for keybuff keybuff_end dw ? ;End ptr for keybuff video_rows db ? ;Screen rows bios_data ends ;-------------------------------------------------------------------- ; CODE segment ;-------------------------------------------------------------------- code segment assume cs:code generic_req struc Len db ? ;Size of structure Unit db ? Function db ? ;Requested function Status dw ? ;Returned status Reserved db 8 dup (?) generic_req ends init_req struc irLen db ? ;Size of structure irUnit db ? irFunction db ? ;Requested function irStatus dw ? ;Returned status irReserved db 8 dup (?) irUnits db ? ;Number of block drives irEndAddress dd ? ;End addr of driver irParmAddress dd ? ;Ptr to cmd line parms irDriveNumber db ? ;1st drive number irMessageflag dw ? ;Error word init_req ends media_req struc mrLen db ? ;Size of structure mrUnit db ? mrFunction db ? ;Requested function mrStatus dw ? ;Returned status mrReserved db 8 dup (?) mrMediaID db ? ;Media descripter mrReturn db ? ;return value mrVolumeID dd ? ;Ptr to volumne ID string media_req ends buildbpb_req struc bbrLen db ? ;Size of structure bbrUnit db ? bbrFunction db ? ;Requested function bbrStatus dw ? ;Returned status bbrReserved db 8 dup (?) bbrMediaID db ? ;Media descripter bbrFATSector dd ? ;Ptr to 1st FAT sector bbrBPBAddress dd ? ;Ptr to BPB block buildbpb_req ends read_req struc rrLen db ? ;Size of structure rrUnit db ? rrFunction db ? ;Requested function rrStatus dw ? ;Returned status rrReserved db 8 dup (?) rrMediaID db ? ;Media descripter rrBuffer dd ? ;Ptr to data buffer rrBytesSec dw ? ;Number of sectors to read rrStartSec dw ? ;Starting sector number rrVolumeID dd ? ;Ptr to volume ID string rrHugeStartSec dd ? ;Start sec for >32 Meg drive read_req ends ;==================================================================== ;Device driver header ;==================================================================== org 0 ;Offset 0 for DD header dd -1 ;Ptr to next driver dw 0800h ;Attribute word bits ;15 Character device ;14 IOCTL read/write support ;13 (Char) Output till busy ; (Blk) Needs FAT for BPB ;11 Open/Close dev supported ; 7 IOCTL querys supported ; 6 Log drive mapping support ; 4 Fast char input support ; 3 Device is clock device ; 2 Device is NULL device ; 1 (Char) Std out device ; (Blk) 32 bit sector nums ; 0 Device is Std Input dev dw offset strategy ;Ptr to strategy routine dw offset interrupt ;Ptr to interrupt routine db 1,0,0,0,0,0,0,0 ;Num of block devices program db 10,13,"REDD",10,13 db "Copyright (c) 1992 Douglas Boling",13,10 db "First published in PC Magazine, September 15, 1992" db 13,10,"$",1Ah ;-------------------------------------------------------------------- ;Boot Sector data for our imaginary drive ;-------------------------------------------------------------------- boot_sector = $ jmp short boot_sector_end ;obligitory jmp instruction nop db "IBM x.x " ;Stupid IBM tag for DOS 4 BPB_start = $ BytesPerSec dw 200h ;512 bytes per sector SecPerCluster db 1 ;One sec per cluster ResSectors dw 1 ;Reserved sectors NumFATs db 1 ;Number of File Alloc tables RootDirEntries dw 8 ;Num of entries in root dir Sectors dw 8 ;Total number of sectors Media db 0F0h ;Media descriptor byte FATSectors dw 1 ;Number of sectors per FAT SecPerTrack dw 8 ;Num sectors per track Heads dw 1 ;Num of heads HiddenSectors dd 0 ;Num hidden sectors HugeSectors dd 0 ;Num of sec if > 32 Meg DriveNumber db 0 ;Used by DOS Reserved1 db 0 ;Used by DOS BootSignature db 29h ;IDs boot sector format VolumeID dd 12345678h ;Volume ID number VolumeLabel db "REDD " ;ASCII volume label FileSysType db "FAT12 " ;FAT system used boot_sector_end = $ ; ;Data needed for the driver ; req_header_ptr dd 0 ;Ptr to request header bpb_array dw offset BPB_start ;Array of BPB pointers keyname db "KEYBOARDIN " ;Name of keyboard file volume_name db "REDD ",0 ;ASCIIZ string of vol name cr_flag db 1 ;Append CR to video LFs even sys_year db 0 ;Time needed to set sys_month db 0 ; the time and date for sys_day db 0 ; screen file. sys_hours db 0 sys_minutes db 0 sys_seconds db 0 timer_low dw 0 ;Timer ticks since last timer_high dw 0 ; GetSysTime call. scrtick_low dw 0 ;Tick count at last screen scrtick_high dw 0 ; write. int08_active dw -1 ;Interrupt 8 active flag int08h dd -1 ;Old Timer Interrupt int10h dd -1 ;Old Video Interrupt ScreenHead dd -1 ;Head of screen buffer queue ScreenTop dw -1 ;Top of screen buffer ScreenBot dw -1 ;End of screen buffer ScreenTxtSec dw 2 ;Screen file num of sectors fileopen_count dw 0 ;Number of opened files root_update db 0 ;Indicates change in Root dir paste_flag db 0 pastedata_ptr dw 0 pastedata_end dw 0 fpclust_size dw 0 pastecluster dw 0 pastedirent_ptr dw 0 FATPtr dw 0 ;Ptr to FAT table FATSize dw 0 ;Sectors * 1.5 RootSize dw 0 ;Root entries * 32 DataPtr dw ? ;Ptr to data sector start jmptable dw offset init ;0 Initialize driver dw offset media_check ;1 Block device media check dw offset build_bpb ;2 Build BIOS parameter blk dw offset not_implimented ;3 I/O control read dw offset read ;4 Read dw offset not_implimented ;5 Non-destructive read dw offset not_implimented ;6 Get input status dw offset not_implimented ;7 Flush input dw offset write ;8 Write dw offset write ;9 Write with verify dw offset not_implimented ;A Get output status dw offset not_implimented ;B Output flush dw offset not_implimented ;C I/O control write dw offset open_device ;D Open device dw offset close_device ;E Close device dw offset removable_media ;F Removable media check dw offset not_implimented ;10 Output until busy dw offset not_implimented ;11 Reserved dw offset not_implimented ;12 Reserved dw offset not_implimented ;13 Generic I/O control dw offset not_implimented ;14 Reserved dw offset not_implimented ;15 Reserved dw offset not_implimented ;16 Reserved dw offset not_implimented ;17 Get logic drive mapping dw offset not_implimented ;18 Set logic drive mapping dw offset not_implimented ;19 I/O control query jmptable_end = $ max_cmd equ (offset jmptable_end - offset jmptable) shr 1 ;==================================================================== ;STRATEGY - Handles strategy calls from DOS by copying the request ; header pointer into an internal buffer. ;==================================================================== strategy proc far assume cs:code,ds:nothing,es:nothing mov word ptr cs:[req_header_ptr],bx mov word ptr cs:[req_header_ptr+2],es ret strategy endp ;==================================================================== ;INTERRUPT - Handles interrupt calls from DOS by implimenting the ; device driver functions ;==================================================================== interrupt proc far assume cs:code,ds:nothing,es:nothing push ax push bx push cx push dx push di push si push bp push ds push es pushf cld ;All string operations UP mov di,cs mov ds,di assume ds:code les di,req_header_ptr ;Get ptr to request header xor bx,bx mov bl,es:[di.Function] ;Get requested function cmp bl,max_cmd mov ax,8003h ;Load error status ja interrupt_exit nop shl bx,1 call [bx+jmptable] ;Call proper routine push ax cmp cs:root_update,0 je interrupt_1 call check_root interrupt_1: pop ax les di,req_header_ptr ;Get ptr to request header or ax,0100h ;Set done bit interrupt_exit: mov es:[di.Status],ax ;Save status in drvr header popf pop es pop ds pop bp pop si pop di pop dx pop cx pop bx pop ax ret interrupt endp ;-------------------------------------------------------------------- ;MEDIA CHECK - Media check function allows DOS to determine if the ; disk type in the drive has changed. ;Entry: ES:DI - Point to request header structure ;-------------------------------------------------------------------- media_check proc near assume cs:code,ds:code,es:nothing mov es:[di.mrReturn],0 ;Media may have been changed mov word ptr es:[di.rrVolumeID],offset volume_name mov word ptr es:[di.rrVolumeID+2],cs not_implimented: xor ax,ax ;Clear return code ret media_check endp ;-------------------------------------------------------------------- ;BUILD BPB - Build BIOS Parameter Block. ;Entry: ES:DI - Point to request header structure ;-------------------------------------------------------------------- build_bpb proc near assume cs:code,ds:code,es:nothing mov word ptr es:[di.bbrBPBAddress],offset BPB_start mov word ptr es:[di.bbrBPBAddress+2],cs xor ax,ax ret build_bpb endp ;-------------------------------------------------------------------- ;READ - Reads sectors from the block device ;Entry: ES:DI - Point to request header structure ;-------------------------------------------------------------------- read proc near assume cs:code,ds:code,es:nothing mov bx,es:[di.rrStartSec] mov cx,es:[di.rrBytesSec] push cx push di push es les di,es:[di.rrBuffer] nop read_1: call read_sector ;'Read' a sector inc bx ;Inc starting sector loop read_1 ;Read again if not done pop es ;Set output fields and term pop di pop cx mov es:[di.rrBytesSec],cx mov word ptr es:[di.rrVolumeID],offset volume_name mov word ptr es:[di.rrVolumeID+2],cs xor ax,ax ret read endp ;-------------------------------------------------------------------- ;WRITE - Writes sectors from the block device ;Entry: ES:DI - Point to request header structure ;-------------------------------------------------------------------- write proc near assume cs:code,ds:code,es:nothing mov bx,es:[di.rrStartSec] mov cx,es:[di.rrBytesSec] push cx push si push ds lds si,es:[di.rrBuffer] nop write_1: call write_sector ;'Write' a sector inc bx ;Inc starting sector loop write_1 ;Read again if not done pop ds pop si pop cx mov es:[di.rrBytesSec],cx mov word ptr es:[di.rrVolumeID],offset volume_name mov word ptr es:[di.rrVolumeID+2],cs xor ax,ax ret write endp ;-------------------------------------------------------------------- ;OPEN DEVICE - Indicates that a file has been opened on the drive ;Entry: ES:DI - Point to request header structure ;-------------------------------------------------------------------- open_device proc near assume cs:code,ds:code,es:nothing inc cs:fileopen_count xor ax,ax ret open_device endp ;-------------------------------------------------------------------- ;CLOSE DEVICE - Indicates that a file has been closed on the drive ;Entry: ES:DI - Point to request header structure ;-------------------------------------------------------------------- close_device proc near assume cs:code,ds:code,es:nothing dec cs:fileopen_count xor ax,ax ret close_device endp ;-------------------------------------------------------------------- ;REMOVABLE MEDIA - Informs DOS whether the drive is removable ;Entry: ES:DI - Point to request header structure ;-------------------------------------------------------------------- removable_media proc near assume cs:code,ds:code,es:nothing xor ax,ax ;Bit 9 = 0 - drive removable ret removable_media endp ;-------------------------------------------------------------------- ;READ SECTOR - Simulates a read of a mythical disk sector ;Entry: BX - Starting sector ; ES:DI - Ptr to data buffer to read data ;Exit: ES:DI - Points to byte beyond last data read out to DOS ;-------------------------------------------------------------------- read_sector proc near assume cs:code,ds:code,es:nothing push bx push cx cmp bx,1 ;Check what sector to read jb read_boot je read_FAT sub bx,3 jb read_root cmp bx,ScreenTxtSec ;See if reading SCREEN.TXT jb read_screen call getdata_ptr ;Compute buff for sector mov si,bx mov cx,BytesPerSec jmp short read_sector_1 read_screen: mov dx,BytesPerSec xchg bx,dx mov si,word ptr ScreenHead ;Read this sector as a mov ax,bx ; rotating buffer. The mul dx ; ScreenHead ptr points add si,ax ; to the start of the mov cx,ScreenBot ;See if starting past end cmp si,cx ; of buffer. jb read_screen_0 sub si,cx ;Yes, wrap ptr to buff start add si,ScreenTop read_screen_0: sub cx,si ;Compute len till end of buff sub bx,cx ;Sub read len from total jae read_screen_1 ;If read len more than total, mov cx,BytesPerSec ; change read len to total xor bx,bx ;Clear total. read_screen_1: rep movsb mov si,ScreenTop mov cx,bx jcxz read_sector_exit rep movsb jmp short read_sector_exit read_boot: mov si,offset boot_sector mov cx,offset boot_sector_end - offset boot_sector jmp short read_sector_1 read_FAT: mov si,FATPtr mov cx,FATSize jmp short read_sector_1 read_root: mov si,offset root_dir call set_time ;Set time for screen file mov cx,RootSize read_sector_1: mov dx,BytesPerSec ;Copy data sub dx,cx ;See how much is left for rep movsb ; a complete sector xor al,al mov cx,dx ;Fill in remainder of jcxz read_sector_exit ; sector with zeros. rep stosb read_sector_exit: pop cx pop bx ret read_sector endp ;-------------------------------------------------------------------- ;WRITE SECTOR - Simulates a write of a mythical disk sector ;Entry: BX - Starting sector ; DS:SI - Ptr to data buffer ;Exit: DS:SI - Points to byte beyond last data read into drive ;-------------------------------------------------------------------- write_sector proc near assume cs:code,ds:nothing,es:nothing push bx push cx push di push es mov ax,cs mov es,ax assume es:code cmp bx,1 ;Check what sector to write jb write_boot je write_FAT sub bx,3 jb write_root call getdata_ptr ;Compute buff for sector mov di,bx mov cx,BytesPerSec jmp short write_sector_1 write_boot: mov di,offset boot_sector mov cx,offset boot_sector_end - offset boot_sector jmp short write_sector_1 write_FAT: mov di,cs:FATPtr mov cx,cs:FATSize jmp short write_sector_1 write_root: mov di,offset root_dir mov cx,cs:RootSize inc cs:root_update write_sector_1: rep movsb write_sector_exit: pop es pop di pop cx pop bx ret write_sector endp ;-------------------------------------------------------------------- ;GETDATA PTR - Returns the offet in RAM for a given data sector ;Entry: BX - Cluster - 2 ;Exit: BX - Ptr to data ;-------------------------------------------------------------------- getdata_ptr proc near assume cs:code,ds:nothing,es:nothing push ax push dx mov ax,bx ;Copy starting data sec mul cs:BytesPerSec ;Mul by size of data sec mov bx,ax ;Add starting offset of add bx,cs:DataPtr ; data buffer. pop dx pop ax ret getdata_ptr endp ;-------------------------------------------------------------------- ;CHECK ROOT - Checks changes in the root directory ;-------------------------------------------------------------------- check_root proc near assume cs:code,ds:nothing,es:nothing push ds push es mov ax,cs mov ds,ax mov es,ax assume cs:code,ds:code,es:code mov root_update,0 ;Clear flag cmp paste_flag,0 ;Don't bother if we are jne check_root_exit ; already pasting. mov si,offset root_dir mov cx,RootDirEntries mov di,offset keyname check_root_1: push cx ;Scan through root looking push si ; for the name of the push di ; keyboard file. mov cx,11 repe cmpsb pop di pop si pop cx je check_root_2 add si,32 loop check_root_1 jmp short check_root_exit check_root_2: xor bx,bx or bx,[si+1ah] ;See if starting cluster set je check_root_exit mov cx,word ptr [si+1ch] ;Get file size or cx,cx je check_root_exit ;If 0, exit mov ax,cx xor dx,dx div BytesPerSec ;Compute number of sectors mov fpclust_size,dx ; and size of last sector. mov pastedirent_ptr,si mov pastecluster,bx call next_pasteclust ;Compute pointers to sector mov paste_flag,1 ;Allow paste check_root_exit: pop es pop ds ret check_root endp ;------------------------------------------------------------------------- ;DELETE FILE - Deletes a file from the driver disk ;Entry: SI - Pointer to directory entry for file ;------------------------------------------------------------------------- delete_file proc near assume cs:code,ds:code,es:nothing push cx xor ax,ax mov bx,[si+1ah] ;Get starting cluster delete_file_1: mov cx,bx call get_fat_entry xchg bx,cx call set_fat_entry ;Zero entry mov bx,cx cmp bx,0fffh ;See if last entry jne delete_file_1 mov byte ptr [si],0e5h ;Clear name pop cx ret delete_file endp ;------------------------------------------------------------------------- ; GET FAT ENTRY - Returns the contents of a 12 bit FAT entry ; Entry: BX - Entry into FAT table ; Exit: BX - Next entry ;------------------------------------------------------------------------- get_fat_entry proc near assume cs:code,ds:code,es:nothing push ax push di mov di,FATPtr add di,bx shr bx,1 ;12 bit FAT mov ax,ds:[bx+di] jnc get_fat_entry_2 shr ax,1 shr ax,1 shr ax,1 shr ax,1 get_fat_entry_2: and ah,0fh ;Clear top nibble get_fat_entry_3: mov bx,ax ;Copy entry data pop di pop ax ret get_fat_entry endp ;------------------------------------------------------------------------- ; SET FAT ENTRY - Writes a 12 bit FAT table entry ; Entry: BX - Pointer to fat table entry ; AX - Data to write to FAT table ;------------------------------------------------------------------------- set_fat_entry proc near assume cs:code,ds:code,es:nothing push ax push bx push di mov di,FATPtr add di,bx shr bx,1 ;12 bit FAT pushf add di,bx popf mov bx,ds:[di] ;Get data jc set_fat_1 ;Jump if odd and bx,0f000h ;Remove old data jmp short set_fat_2 set_fat_1: and bx,000fh ;Remove old data shl ax,1 shl ax,1 shl ax,1 shl ax,1 set_fat_2: or ax,bx mov ds:[di],ax ;Write data pop di pop bx pop ax ret set_fat_entry endp ;==================================================================== ;TIMER INT - Interrupt 8h timer hook. This routine keeps the time ; and, when necessary, pastes data into the keyboard buff ;==================================================================== timerint proc far assume cs:code,ds:nothing,es:nothing pushf add word ptr cs:timer_low,1 adc word ptr cs:timer_high,0 cmp cs:paste_flag,0 jne timerint_1 timerint_exit: popf jmp cs:[int08h] ;Jmp to old interrupt timerint_1: inc cs:int08_active jne timerint_exit2 push ax push ds mov ax,cs mov ds,ax assume ds:code timerint_2: call push_key jc timerint_exit1 cmp paste_flag,0 jne timerint_2 timerint_exit1: pop ds pop ax timerint_exit2: dec cs:int08_active jmp short timerint_exit timerint endp ;----------------------------------------------------------------------------- ; PUSH KEY Fills the keyboard buffer with data ; Exit: CF - Set if keyboard buffer full ; AX - modified ;----------------------------------------------------------------------------- push_key proc near assume ds:nothing,es:nothing push bx push di push si push es cld ;String moves UP mov ax,bios_data mov es,ax assume es:bios_data cli ;No interrupts mov di,es:[keybuff_tail] push di call inckeyptr cmp di,es:[keybuff_head] ;See if buffer full pop di stc je push_key_exit mov si,pastedata_ptr lodsb ;Get character to paste cmp si,pastedata_end jb push_key_3 push ax mov bx,pastecluster ;See if cluster is the last call get_fat_entry cmp bx,0fffh je push_key_1 mov pastecluster,bx call next_pasteclust ;Compute ptrs for next sector jmp short push_key_2 push_key_1: mov paste_flag,0 ;Disable paste mov si,pastedirent_ptr ;Delete paste file call delete_file push_key_2: pop ax push_key_3: mov pastedata_ptr,si or al,al ;Don't stuff null char je push_key_5 jns push_key_31 and ax,007fh ;Remove sign bit, clear AH xchg ah,al ;Place char in scan code jmp short push_key_4 push_key_31: cmp al,0ah ;Don't stuff line feeds je push_key_5 cmp al,1ah ;Don't stuff End Of File char je push_key_5 xor ah,ah ;Clear scan code cmp al,0dh ;If CR character, add proper jne push_key_4 ; scan code for Word Perfect mov ah,1ch push_key_4: mov es:[di],ax ;Don't use STOSW, ptr must call inckeyptr ; be updated by inckeyptr mov es:[keybuff_tail],di ;Save keybuff ptr push_key_5: clc push_key_exit: pop es pop si pop di pop bx ret push_key endp ;-------------------------------------------------------------------- ;NEXT PASTECLUST - Sets the paste variables for a sector ;Entry: BX - new sector ;-------------------------------------------------------------------- next_pasteclust proc near push dx push bx call get_fat_entry mov dx,BytesPerSec cmp bx,0fffh jne next_pc_1 mov dx,fpclust_size next_pc_1: pop bx sub bx,2 call getdata_ptr mov pastedata_ptr,bx add bx,dx mov pastedata_end,bx pop dx ret next_pasteclust endp ;----------------------------------------------------------------------------- ; INCKEYPTR Incriments the keyboard buffer pointer ; Entry: DI - Current Keyboard tail pointer ;----------------------------------------------------------------------------- inckeyptr proc near assume es:bios_data inc di ;Make room in buffer inc di cmp di,es:[keybuff_end] ;Get ptr to end of buffer jne inckeyptr_1 mov di,es:[keybuff_start] ;Get ptr to buffer offset inckeyptr_1: ret inckeyptr endp ;==================================================================== ;VID INT - Interrupt 10h video hook. This routine monitors and ; records any BIOS writes to the screen into the 'file' ; SCREEN.TXT. ;==================================================================== vidint proc far assume cs:code,ds:nothing,es:nothing cmp ah,9 jae vidint_1 vidint_exit: jmp cs:[int10h] ;Jmp to old interrupt vidint_1: push cx push di push es cmp ah,09h je vidint_wrtchr1 cmp ah,0ah je vidint_wrtchr1 cmp ah,0eh je vidint_wrtchr cmp ah,13h jne vidint_exit1 push ax push si push ds mov si,es mov ds,si mov si,bp ;Copy ptr to string les di,cs:ScreenHead ;Get ptr to buffer mov ah,al vidint_2: jcxz vidint_4 lodsb ;Read character call save_char ;Store character cmp ah,2 jb vidint_3 inc si ;Skip past attribute vidint_3: loop vidint_2 vidint_4: pop ds pop si pop ax jmp short vidint_5 vidint_wrtchr: mov cx,1 vidint_wrtchr1: les di,cs:ScreenHead ;Get ptr to buffer jcxz vidint_5 call save_char ;Save character loop vidint_wrtchr1 vidint_5: mov word ptr cs:ScreenHead,di pushf cli mov di,cs:timer_low ;Mark last time screen mov cx,cs:timer_high ; updated. mov cs:scrtick_low,di mov cs:scrtick_high,cx popf vidint_exit1: pop es pop di pop cx jmp vidint_exit vidint endp ;-------------------------------------------------------------------- ;SAVE CHAR - Saves a character into the SCREEN.TXT file ;-------------------------------------------------------------------- save_char proc near assume cs:code,ds:nothing,es:code cmp al,13 ;See if CR je save_char_2 cmp al,10 je save_char_3 save_char_0: stosb save_char_01: cmp di,cs:ScreenBot jae save_char_1 ret save_char_1: mov di,cs:ScreenTop ret save_char_2: mov cs:cr_flag,0 jmp short save_char_0 save_char_3: cmp cs:cr_flag,0 je save_char_0 stosb mov al,13 stosb mov al,10 jmp short save_char_01 ret save_char endp ;-------------------------------------------------------------------- ;INIT TIMER - Calls the BIOS to retrieve the system time ;-------------------------------------------------------------------- init_timer proc near assume cs:code,ds:code,es:nothing push bx mov ah,2 ;Get system time int 1ah mov al,ch call un_bcd mov sys_hours,al mov al,cl call un_bcd mov sys_minutes,al mov al,dh call un_bcd mov sys_seconds,al mov ah,4 ;Get system time int 1ah mov al,cl call un_bcd xor ah,ah cmp ch,19 ;See if next century je init_timer_1 add ax,100 init_timer_1: sub ax,80 ;Convert to relative date mov sys_year,al mov al,dh call un_bcd mov sys_month,al mov al,dl call un_bcd mov sys_day,al pushf cli mov timer_low,0 mov timer_high,0 popf pop bx ret init_timer endp ;-------------------------------------------------------------------- ;SET TIME - Sets the time of file in the directory ;Entry: SI - Pointer to the directory entry ;-------------------------------------------------------------------- set_time proc near assume cs:code,ds:code,es:nothing mov ax,scrtick_low mov dx,scrtick_high call compute_elapsed add al,sys_seconds cmp al,60 jb set_time_1 inc cl sub al,60 set_time_1: add cl,sys_minutes cmp cl,60 jb set_time_2 inc ch sub cl,60 set_time_2: add ch,sys_hours cmp ch,24 jb set_time_3 inc dh sub ch,24 set_time_3: shr al,1 ;Divide seconds by 2 and ax,1fh mov bh,cl ;Get minutes and bx,3f00h shr bx,1 shr bx,1 shr bx,1 or ax,bx mov bh,ch ;Get hours shl bx,1 shl bx,1 shl bx,1 and bx,0f800h or ax,bx mov [si+16h],ax ;Save time in dir entry mov al,sys_day mov cl,sys_month mov ch,sys_year or dh,dh je set_time_4 call inc_date ;Inc to next day set_time_4: and ax,3fh shl cl,1 shl cl,1 shl cl,1 shl cl,1 shl cx,1 or ax,cx mov [si+18h],ax ;Save date in dir entry ret set_time endp ;-------------------------------------------------------------------- ;COMPUTE ELAPSED - Computes the elapsed time from a 32 bit timer tick ; count. ;Entry: AX,DX - Timer tick count ;Exit: DH - Days ; CH - Hours ; CL - Minutes ; DL - Seconds ;-------------------------------------------------------------------- compute_elapsed proc near assume cs:code,ds:code,es:nothing push bx push di xor bx,bx mov ch,dl ;Save hours cmp dl,24 ;If longer than 24 hours, jb compute_1 ; compute days. push ax xor ax,ax xchg ax,dx mov di,24 div di mov bl,al ;Save days mov dx,ax mul di mov ch,al pop ax compute_1: xor dx,dx mov di,1092 ;Ticks per minute div di mov cl,al ;Save minutes xor ax,ax xchg ax,dx mov di,10 mul di mov di,182 div di mov dl,al mov di,ax mov dh,bl pop di pop bx ret compute_elapsed endp ;-------------------------------------------------------------------- ;INC DATE - Propigates a incrimented date though the month and year. ;Entry: AL - Day ; CL - Month ; CH - Years since 1980 ;-------------------------------------------------------------------- inc_date proc near inc al ;Incriment date push bx mov bx,1f1eh ;bh=31,bl=30 cmp cl,7 ;Up till Aug. odd numbered jbe inc_date_1 ; months have 31 days. xchg bl,bh ; After, even months have 31 inc_date_1: test cl,1 ;See if odd month je inc_date_2 ;No, branch xchg bl,bh inc_date_2: cmp cl,2 ;See if Feb jne inc_date_3 mov bl,28 test ch,3 ;Leap year if 4 year multiple jne inc_date_3 ; of 1980. Fails at 2100 mov bl,29 ; since 2100 not a leap year inc_date_3: cmp al,bl jbe inc_date_exit mov al,1 ;Set to 1st day of the month inc cl ;Inc month cmp cl,13 ;See if end of year jne inc_date_exit mov cl,1 ;Set to Jan. inc ch ;Inc year inc_date_exit: pop bx ret inc_date endp ;-------------------------------------------------------------------- ;UN BCD - Returns a binary number for one coded in BCD ;Entry: AL - BCD number ;Exit: AL - Binary number ;-------------------------------------------------------------------- un_bcd proc near assume cs:code,ds:nothing,es:nothing push bx mov bl,al shr al,1 shr al,1 shr al,1 shr al,1 mov ah,10 mul ah and bl,0fh add al,bl pop bx ret un_bcd endp ;-------------------------------------------------------------------- ;FINAL INSTALL - Initializes the data structures needed for the drive ;Entry: ES:DI - Point to request header structure ; DX - Size of data sectors ;-------------------------------------------------------------------- final_install proc near assume cs:code,ds:code,es:nothing mov di,cs mov es,di assume es:code mov di,offset root_dir + 64 mov cx,RootSize sub cx,64 xor ax,ax rep stosb mov di,FATPtr mov al,Media ;Initialize FAT stosb ;First FAT bytes contain mov ax,-1 ; media descriptor byte stosw ; followed by FF FF. mov cx,ScreenTxtSec mov bx,screen_start final_install_0: dec cx jcxz final_install_01 mov ax,bx inc ax call set_fat_entry mov bx,ax jmp short final_install_0 final_install_01: mov ax,-1 call set_fat_entry final_install_02: inc bx cmp bx,Sectors ja final_install_3 xor ax,ax call set_fat_entry jmp short final_install_02 final_install_3: mov di,DataPtr mov cx,dx ;Fill 1st data sector for mov al,' ' ; SCREEN.TXT file. Might rep stosb ; as well init the rest. mov ax,3510h ;Get video interrupt vector int 21h mov word ptr [int10h],bx mov word ptr [int10h+2],es mov ax,2510h ;Set to our handler mov dx,offset vidint int 21h mov ax,3508h ;Get timer interrupt vector int 21h mov word ptr [int08h],bx mov word ptr [int08h+2],es mov ax,2508h ;Set to our handler mov dx,offset timerint int 21h xor ax,ax ;Clear return code ret final_install endp even ;Keep things on even addrs ; ;Start root directory with entry for screen.txt ; root_dir db "SCREEN OUT" ;Name db 1 ;Attribute (Read only) db 10 dup (0) ;Reserved screen_time dw 0 ;Time of file screen_date dw 0 ;Date of file screen_start dw 2 ;Starting cluster screen_size dd 0 ;Size of file volumne_entry db "REDD " ;Name db 8 ;Attribute (Volume) db 10 dup (0) ;Reserved dw 0 ;Time of file dw 0 ;Date of file dw 0 ;Starting cluster dd 0 ;Size of file end_of_resident = $ ;-------------------------------------------------------------------- ;Non-resident data ;-------------------------------------------------------------------- initmsg db 13,10,"REDD installed as drive " initdrv db " :",13,10,10,'$' ;-------------------------------------------------------------------- ;INIT - Initializes the device driver ;Entry: ES:DI - Point to request header structure ;-------------------------------------------------------------------- init proc near assume cs:code,ds:code,es:nothing mov ah,9 ;Print copyright mov dx,offset program int 21h call init_timer ;Init driver clock mov bx,offset root_dir mov ax,32 ;Compute size of root dir mov dx,RootDirEntries mul dx mov RootSize,ax add bx,ax mov FATPtr,bx mov ax,Sectors mov dx,ax ;Mul sectors by 1.5 shr ax,1 adc ax,dx mov FATSize,ax add bx,ax mov DataPtr,bx mov word ptr ScreenHead,bx ;Init screen buffer ptrs to mov word ptr ScreenHead+2,cs ; 1st data sector. mov ScreenTop,bx mov ax,ScreenTxtSec mul BytesPerSec mov word ptr screen_size,ax add ax,bx mov ScreenBot,ax mov ax,Sectors ;Compute size of data mul BytesPerSec ; sectors. mov dx,ax ;Save data sector size add ax,bx les di,req_header_ptr ;Get ptr to request header mov cl,es:[di.irDriveNumber] ; the drive number being add cl,'A' ; used. mov initdrv,cl mov word ptr es:[di.irEndAddress],ax ;Set memory mov word ptr es:[di.irEndAddress+2],cs ; size mov word ptr es:[di.irParmAddress],offset BPB_array mov word ptr es:[di.irParmAddress+2],cs mov word ptr es:[di.irUnits],1 push dx mov ah,9 ;Print message indicating mov dx,offset initmsg ; disk letter. int 21h pop dx jmp final_install init endp code ends end