www.pudn.com > flashdsk.zip > FLASHDSK.ASM


; 
; Comment: 
;   This source is an ASM device driver for DOS that 
;   makes a piece of SAMSUNG flash memory (sorry, I forgot the model name, 
;   long time ago) a normal DOS "fixed" drive. It was original written 
;   to support our PDA product (one of our abortion :<, never mind). 
; 
; 
;   * I AM NOT RESPONSIBLE FOR ANY DAMAGE CAUSED BY THIS SOURCE 
;   * Do NOT WRITE ME ANY EMAIL except that you want to thank me for this 
;     source, DO NOT ASK ME ANY QUESTION about this source file. 
;   * You may copy/use/distribute anything of this source freely.  
;     But please do not change anything of THIS FILE if you want 
;     to distribute it. 
;   * Thank for those people who published their source to others. 
; 
; Author: Edward Guo 
; mailto:edguo@163.net 
; 
 
;****************************************************************************; 
; Project: G&S VG330 PDA                                                     ; 
; File: flashdsk.asm                                                         ; 
; Description: Flash memory DOS disk driver(PDA version)                     ; 
; Copyright: Copr. 1998 Shenzhen G&S Co. Ltd., ALL RIGHT RESERVED !          ; 
; Author: Edward Guo                                                         ; 
; Comment: standard 80186 asm                                                ; 
;****************************************************************************; 
; History:                                                                   ; 
; Ver   When          Who     Changes                                        ; 
; 1.0  May 30th 1998  EdGuo   Original version(beta)                         ; 
;                                                                            ; 
;****************************************************************************; 
 
;############################################################################ 
; To be done: 
; * Device command line 
;############################################################################ 
 
 
; Information (for more info, pls ref to DOS book/interrupt list) 
;============================================================================ 
;  Format of device driver request header: 
;  Offset  Size    Description             
;   00h    BYTE    length of request header 
;   01h    BYTE    subunit within device driver 
;   02h    BYTE    command code (see below) 
;   03h    WORD    status (filled in by device driver) 
;          bit 15: error 
;          bits 14-10: reserved 
;          bit 9: busy 
;          bit 8: done 
;          bits 7-0: error code if bit 15 set (see below) 
;   05h  8 BYTEs   reserved (unused in DOS 2.x and 3.x) 
;  ---command code 00h--- 
;   0Dh    BYTE    number of units (set by driver) 
;   0Eh    DWORD   address of first free byte following driver (set by 
;                     driver) 
;   12h    DWORD   pointer to BPB array (set by block drivers only) 
;   16h    BYTE    (DOS 3+) drive number for first unit of block driver (0=A) 
;  ---command code 01h--- 
;   0Dh    BYTE    media descriptor 
;   0Eh    BYTE    returned status 
;          00h don't know 
;          01h media has not changed 
;          FFh media has been changed 
;   0Fh    DWORD   (DOS 3+) pointer to previous volume ID if OPEN/CLOSE/RM 
;            bit in device header set and disk changed (set by driver) 
;  ---command code 02h--- 
;   0Dh    BYTE    media descriptor 
;   0Eh    DWORD   transfer address 
;          -> scratch sector if NON-IBM FORMAT bit in device header set 
;          -> first FAT sector otherwise 
;   12h    DWORD   pointer to BPB (set by driver) 
;  ---command codes 03h,0Ch--- 
;   0Dh    BYTE    media descriptor (block devices only) 
;   0Eh    DWORD   transfer address 
;   12h    WORD    byte count (character devices) or sector count (block 
;                      devices) 
;   14h    WORD    starting sector number (block devices only) 
;  ---command codes 04h,08h,09h--- 
;   0Dh    BYTE    media descriptor (block devices only) 
;   0Eh    DWORD   transfer address 
;   12h    WORD    byte count (character devices) or sector count (block 
;                      devices) 
;   14h    WORD    starting sector number (block devices only) 
;   16h    DWORD   (DOS 3+) pointer to volume ID if error 0Fh returned 
; 
;  Values for command code: 
;    * 00h INIT 
;    * 01h MEDIA CHECK (block devices) 
;    * 02h BUILD BPB (block devices) 
;    * 04h INPUT 
;    * 08h OUTPUT 
;    * 09h OUTPUT WITH VERIFY 
;      0Dh (DOS 3+) DEVICE OPEN 
;      0Eh (DOS 3+) DEVICE CLOSE 
;      0Fh (DOS 3+) REMOVABLE MEDIA (block devices) 
;    (* minimum requirement) 
; 
;  Values for error code: 
;      00h write-protect violation 
;      01h unknown unit 
;      02h drive not ready 
;      03h unknown command 
;      04h CRC error 
;      05h bad drive request structure length 
;      06h seek error 
;      07h unknown media 
;      08h sector not found 
;      09h printer out of paper 
;      0Ah write fault 
;      0Bh read fault 
;      0Ch general failure 
;      0Dh reserved 
;      0Eh reserved 
;      0Fh invalid disk change 
 
 
; Definition 
;============================================================================ 
_BUGGY    = 1                           ; Use buggy chip 
;_DEBUG    = 1                           ; Debug version 
;_COMFILE  = 1                           ; .com file 
;_DEVTEST  = 1                           ; Test version(.com) of device 
;_NEEDBUF  = 1                           ; Allocated buffer 
;_DEBUGGER = 1                           ; Use int 3 
;_SHOWRQ   = 1                           ; Show request 
 
 
ifdef           _DEBUG 
ifndef          _COMFILE 
ifndef          _NEEDBUF 
_NEEDBUF  = 1 
endif           ;!_NEEDBUF 
endif           ;!_COMFILE 
endif           ;_DEBUG 
 
 
; Constant 
;============================================================================ 
 
;----- Version related 
VOL_NAME        equ     'GSFLASHDSK1'   ; 11 bytes volumn label 
VER_NUM         equ     '1.0'           ; Version number 
RELEASE_NUM     equ     '0', '1'        ; Release number 
DEF_SEC_SIZE    equ     512             ; Driver sector size 
DRV_MEDIA       equ     0fah            ; media descriptor byte 
DEF_ROOT_DIR    equ     64              ; root dir directory count 
MIN_DOS         equ     2               ; minumum DOS version 
MAX_RETRY       equ     3               ; max fault retry 
DEV_READONLY    equ     'R'             ; Readonly flag 
DEV_WRITE       equ     'W'             ; Writable 
 
;----- Char in DEB_PUTC 
CHR_RQ          equ     '#'             ; A request 
CHR_BUILDBPB1   equ     'b'             ; Build BPB start 
CHR_BUILDBPB2   equ     'B'             ; Build BPB end 
CHR_INPUT1      equ     'r'             ; Input/read begin 
CHR_INPUT2      equ     'R'             ; Input/read end 
CHR_OUTPUT1     equ     'w'             ; Output/write begin 
CHR_OUTPUT2     equ     'W'             ; Output/write end 
CHR_OUTPUT3     equ     'v'             ; Output verify begin 
CHR_OUTPUT4     equ     'V'             ; Output verify end 
CHR_INIT1       equ     'i'             ; Initialize begin 
CHR_INIT2       equ     'I'             ; Initialize end 
CHR_BUG         equ     '~'             ; Bug hit 
CHR_RETRY       equ     '!'             ; Fail retry 
CHR_OVERFLOW    equ     '^'             ; Transfer buffer > 64K 
CHR_RANGE       equ     '%'             ; Range clipped 
CHR_INVSEC      equ     '&'             ; Start sector invalid 
CHR_ERROR       equ     'E'             ; Error 
 
;----- Normal constant 
TRUE            equ     1               ; Boolean 
FALSE           equ     0 
 
BELL            equ     7               ; Keys 
TAB             equ     9 
RETURN          equ     13 
LINEFEED        equ     10 
ESCKEY          equ     27 
 
RL              equ     13, 10          ; Return+Linefeed 
EOS             equ     '$'             ; End of sentence 
EOM             equ     RL, EOS         ; End of message 
EOL             equ     RL              ; End of line 
EOF             equ     26              ; End of file 
 
;----- INT values 
DOS             equ     21h             ; DOS function rq interrupt 
VIDEO           equ     10h             ; Video interrupt 
KEYB            equ     16h             ; Keyboard interrupt 
ONEBYTE         equ     03h             ; One-byte interrupt 
 
DOS_PRNMSG      equ     09h             ; Output string($) 
DOS_PRNCHAR     equ     02h             ; Output char 
DOS_EXIT        equ     4ch             ; Exit to caller 
DOS_GETLOL      equ     52h             ; Get list of list 
DOS_GETVER      equ     30h             ; Get DOS version 
DOS_SETVECTOR   equ     25h             ; Set vector 
DOS_FLUSH       equ     0dh             ; Reset disk 
 
VD_GETPAGE      equ     0fh             ; Get page/attr 
VD_PRNCHAR      equ     0eh             ; Display char 
VD_PRNSTR       equ     13h             ; Display string 
VD_GETCURSOR    equ     03h             ; Get cursor shape/position 
VD_GETATTR      equ     08h             ; Get test attr at cursor 
 
KB_READKEY      equ     00h             ; Read a key 
KB_TESTKEY      equ     01h             ; Test if key ready 
 
 
;----- device request   command 
DEV_RQ          equ     es:[di]         ; device request header 
 
DR0_INIT        equ     00h             ; initialize 
DR1_MEDIACHK    equ     01h             ; media check 
DR2_BUILDBPB    equ     02h             ; build BPB 
DR4_INPUT       equ     04h             ; input/read 
DR8_OUTPUT      equ     08h             ; output/write 
DR9_OUTPUTV     equ     09h             ; output verify/write 
DR_MAXNUM       equ     09h             ; max request number 
 
;----- device error code 
DER_NOERR       equ     0000h           ; no error 
DEV_ERROR       equ     8000h           ; error bit 
DER_PROTECT     equ     8000h           ; write-protect violation 
DER_UKUNIT      equ     8001h           ; unknown unit 
DER_NOTREADY    equ     8002h           ; drive not ready 
DER_UKCMD       equ     8003h           ; unknown command 
DER_CRC         equ     8004h           ; CRC error 
DER_BADLEN      equ     8005h           ; bad drive request structure length 
DER_SEEKERR     equ     8006h           ; seek error 
DER_UKMEDIA     equ     8007h           ; unknown media 
DER_MISSEC      equ     8008h           ; sector not found 
DER_OUTPAPER    equ     8009h           ; printer out of paper 
DER_WRFAULT     equ     800Ah           ; write fault 
DER_RDFAULT     equ     800Bh           ; read fault 
DER_GENERAL     equ     800Ch           ; general failure 
DER_DISCHG      equ     800Fh           ; invalid disk change 
 
;----- application error code 
APE_NOERR       equ     00h             ; no error 
APE_READ        equ     01h             ; read fault 
APE_WRITE       equ     02h             ; write fault 
APE_ERASE       equ     03h             ; erase error 
APE_NODEV       equ     04h             ; no device/flash installed 
APE_FORMAT      equ     05h             ; format error 
APE_NOFLASH     equ     06h             ; no flash 
APE_ABORT       equ     0ffh            ; user abort 
 
; Macros 
;============================================================================ 
 
XIN             macro WhichPort         ; In from a port 
                  mov dx, WhichPort 
                  in al, dx 
                endm 
 
XOUTB1          macro Value             ; Out with one value 
                  mov al, Value 
                  out dx, al 
                endm 
 
XOUTB2          macro WhichPort, Value  ; Out with port/value 
                  mov dx, WhichPort 
                  mov al, Value 
                  out dx, al 
                endm 
 
DOSINT          macro                   ; DOS interrupt 
                  int DOS 
                endm 
 
CALLDOS         macro SubFunc           ; DOS function call 
                  mov ah, SubFunc 
                  DOSINT 
                endm 
 
CALLDOSX        macro SubFuncx          ; DOS function call via AX 
                  mov ax, SubFuncx 
                  DOSINT 
                endm 
 
CALLDOS2        macro SubFunc,AL_val    ; DOS function call with al 
                  mov ax, SubFunc*256+Al_val 
                  DOSINT 
                endm 
 
PRINTMSG        macro sMsg              ; Print message use DOS 
                  mov dx, offset sMsg 
                  mov ah, DOS_PRNMSG 
                  DOSINT 
                endm 
 
PRINTERRMSG     macro sErr              ; Print error message 
                  PRINTMSG  msg_error 
                  PRINTMSG  msg_errcode 
                  PRINTMSG  sErr 
                endm     
 
DOSSETVEC       macro Interrupt, Vector 
                  mov ax, DOS_SETVECTOR*256+Interrupt 
                  mov dx, offset Vector 
                  DOSINT 
                endm 
 
DEB_PUTC        macro Char              ; Debug putc() 
ifdef           _DEBUG 
                  push ax 
                  mov al, Char 
                  call _deb_outchar 
                  pop ax 
else            ;_DEBUG 
                ;;; do nothing 
endif           ;_DEBUG 
                endm 
 
GETRQADDR       macro                   ; Get request ptr to ES:DI 
                  les di, [req_headadr] 
                endm 
 
SETBUFADDR      macro                   ; Save transfer buffer addr 
                  mov cx, ax            ; Adjust DX:AX 
                  and ax, 000fh         ; make offset minimum 
                  shr cx, 4             ; SHOULD I ????? 
                  add dx, cx 
                  mov [tbuf_seg], dx 
                  mov [tbuf_ofs], ax 
                endm 
 
DELAY           macro                   ; Delay macro 
                  jmp short $+2 
                endm 
 
BREAK_HERE      macro                   ; Break to debugger 
ifdef           _DEBUGGER 
                  int ONEBYTE 
endif           ;_DEBUGGER 
                endm 
 
 
; Structure 
;============================================================================ 
;---------------------------------------------------------------------------- 
;structure of the device driver request header 
;---------------------------------------------------------------------------- 
REQ_HEAD_PUB    struc 
len             db      ?                       ; request length 
unit            db      ?                       ; unit number 
command         db      ?                       ; command code 
status          dw      ?                       ; return status 
reserved        db      8 dup (?) 
REQ_HEAD_PUB    ends 
 
;----- All requests 
REQ_ALL         struc 
                db      (16h+4+1) dup (?)       ; max size 
REQ_ALL         ends 
 
;----- Request 0: init 
REQ_INIT        struc                            
                db      (type REQ_HEAD_PUB) dup (?) 
init_num_unit   db      ?                       ; Number of unit 
init_free1_ofs  dw      ?                       ; First free byte ofs 
init_free1_seg  dw      ?                       ; First free byte seg 
init_bpb_ofs    dw      ?                       ; BPB array ofs (cmd line ?) 
init_bpb_seg    dw      ?                       ; BPB array seg 
init_drv_num    db      ?                       ; driver number (3+) 
REQ_INIT        ends     
 
;----- Request 1: media check 
REQ_MEDIA       struc 
                db      (type REQ_HEAD_PUB) dup (?) 
mdc_media       db      ?                       ; media descriptor 
mdc_status      db      ?                       ; status 
mdc_pvid_addr   dd      ?                       ; Previous volume ID addr(3+) 
REQ_MEDIA       ends 
 
;----- Request 2: build BPB 
REQ_BUILD_BPB   struc 
                db      (type REQ_HEAD_PUB) dup (?) 
bpb_media       db      ?                       ; media descriptor 
bpb_trans_ofs   dw      ?                       ; transfer address offset 
bpb_trans_seg   dw      ?                       ; transfer address segment 
bpb_bpb_ofs     dw      ? 
bpb_bpb_seg     dw      ?                       ; BPB address 
REQ_BUILD_BPB   ends 
 
;----- Request 4,8,9: I/O 
REQ_IO          struc    
                db      (type REQ_HEAD_PUB) dup (?) 
io_media        db      ?                       ; media descriptor 
io_trans_ofs    dw      ?                       ; transfer address offset 
io_trans_seg    dw      ?                       ; transfer address segment 
io_sec_cnt      dw      ?                       ; sector count 
io_start_sec    dw      ?                       ; starting sector 
io_pvid_addr    dd      ?                       ; Previous volume ID addr(3+) 
REQ_IO          ends 
 
 
 
; Code start 
;============================================================================ 
.186            ; our PDA uses 80186 
 
code            segment public 'code' 
 
ifdef           _COMFILE 
                assume  cs:code, ds:code, es:code 
                org     100h                    ; .com file start at 0100h 
else 
ifdef           _DEVTEST 
                assume  cs:code, ds:code, es:code 
                org     100h                    ; .com file start at 0100h 
else            ;_DEVTEST 
                assume  cs:code, ds:code, es:NOTHING 
                org     0h                      ; drivers start at offset 0 
endif           ;_DEVTEST 
endif           ;_COMFILE 
 
start: 
 
ifdef           _COMFILE 
 
                jmp     @com_start 
 
else            ;_COMFILE 
 
ifdef           _DEVTEST 
 
                jmp     @test_start 
 
myreq           REQ_ALL     <>                  ; Universal request buffer 
 
endif           ;_DEVTEST 
endif           ;_COMFILE 
 
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
; Driver information data block 
;---------------------------------------------------------------------------- 
; device header begin 
;---------------------------------------------------------------------------- 
header          dd      -1                      ; pointer to next driver 
                dw      0000h                   ; device attribute word 
ifndef          _COMFILE 
                dw      offset _strategy        ; pointer to strategy routine 
                dw      offset _interrupt       ; pointer to interrupt routine 
else            ;!_COMFILE 
                dw      0 
                dw      0 
endif           ;!_COMFILE 
                db      1                       ; number of drive 
                db      'GS_FMD_'               ; name of driver 
disknum         db      'X'                     ; which drive 
; device header end 
DEV_HEAD_LEN    equ     this byte - header 
 
                db      0 
 
appname         db      'G&S Flash Disk ', VER_NUM, ' ' 
APP_NAME_LEN    equ     this byte - appname 
ifdef           _DEBUG 
                db      'debug R ' 
else            ;_DEBUG 
                db      'release ' 
endif           ;_DEBUG 
                db      RELEASE_NUM, EOM, EOF 
 
devstatus       db      DEV_WRITE       ; device status r/w 
req_headadr     dd      ?               ; far pointer to request header 
 
bpbary          dw      offset ourbpb 
 
ourboot         label   byte 
                db      'GS_' 
                db      'FLASH_', RELEASE_NUM     ; 8 bytes OEM ID 
 
ourbpb          label   byte            ; These BPB values are arbitrary 
bytes_sector    dw      DEF_SEC_SIZE    ; bytes per sector 
sec_cluster     db      1               ; sectors per cluster 
sec_reserved    dw      1               ; reserved sector 
num_fats        db      1               ; number of FAT 
root_dirs       dw      DEF_ROOT_DIR    ; root dir entries 
num_sec         dw      128             ; number of sectors 
media_byte      db      DRV_MEDIA       ; media type 
sector_fat      dw      3               ; sectors per FAT 
                dw      1               ; sectors per track 
                dw      1               ; sides 
                dd      0               ; special hidden sectors 
                dd      0               ; BigDOS num of sectors(num_sec=0) 
                dw      00h             ; physical drive number 
                db      00h ;29h        ; extended boot record signature 
                dd      88888888h       ; volume serial number 
                db      VOL_NAME        ; volume name(11 bytes) 
                db      'FAT16   '      ; file system id(8 bytes) 
bootcode: 
copyright       db      'Copr. 1998 Shenzhen G&S Co. Ltd.', EOM 
fatsecnum       dw      1               ; FAT sector start number 
freserved       dw      0               ; flash reserved block 
OURBOOTLEN      equ     this byte - ourboot 
 
flash_kb        dw      0               ; total KB 
flash_blk       dw      0               ; total blocks 
 
DEV_INFO_LEN    equ     this byte - header 
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 
ifndef          _COMFILE 
 
tbuf_ofs        dw      ?               ; Transfer buffer(r/w) offset 
tbuf_seg        dw      ?               ; Transfer buffer(r/w) segment 
 
;---------------------------------------------------------------------------- 
;strategy routine. this routine stores the address of the request header 
;---------------------------------------------------------------------------- 
_strategy       proc    far  
                mov     word ptr cs:[req_headadr], bx   ; save offset 
                mov     word ptr cs:[req_headadr+2], es ; save segment 
                ret          
_strategy       endp         
                             
;---------------------------------------------------------------------------- 
;interrupt routine. this routine executes the command code in the req header. 
;---------------------------------------------------------------------------- 
_interrupt      proc    far  
                pushf                           ; Save regs 
                pusha        
                push    ds 
                push    es 
                ; ??? change stack ??? 
 
                mov     ax, cs                  ; set ds 
                mov     ds, ax 
                cld                             ; any string operations move up. 
 
                GETRQADDR                       ; load address of req header 
                mov     bl, DEV_RQ.command 
                cmp     bl, DR_MAXNUM           ; see if command out of range 
                ja      @err_rq 
 
ifdef           _SHOWRQ 
DEB_PUTC CHR_RQ 
                mov     al, bl 
                add     al, '0' 
                call    _deb_outchar 
endif           ;_SHOWRQ 
;;; 
;;;             Process request here 
;;;             A block device must process these requests 
;;;     00h INIT 
;;;     01h MEDIA CHECK (block devices) 
;;;     02h BUILD BPB (block devices) 
;;;     04h INPUT 
;;;     08h OUTPUT 
;;;     09h OUTPUT WITH VERIFY 
;;;                      
@process4:                                      ; Input 
                cmp     bl, DR4_INPUT 
                jne     @process8 
                call    _devinput 
                jmp     @done_int 
 
@process8:                                      ; Output 
                cmp     bl, DR8_OUTPUT 
                jne     @process1 
                call    _devoutput 
                jmp     @done_int 
 
@process1:                                      ; Media check 
                cmp     bl, DR1_MEDIACHK 
                jne     @process9 
                mov     DEV_RQ.mdc_status, 1    ; Media not changed 
                mov     ax, DER_NOERR 
                jmp     @done_int 
 
@process9:                                      ; Output with verify 
                cmp     bl, DR9_OUTPUTV 
                jne     @process0 
                call    _devoutput;v 
                jmp     @done_int 
 
@process0:                                      ; Initialization 
                cmp     bl, DR0_INIT 
                jne     @process2 
                call    _initialize 
                jmp     @done_int 
 
@process2:                                      ; Build BPB 
                cmp     bl, DR2_BUILDBPB 
                jne     @process3 
                call    _buildbpb 
                jmp     @done_int 
 
@process3:      ; other requsts are invalid 
@process5: 
@process6: 
@process7: 
 
@err_rq:                 
                mov     ax, DER_UKCMD           ; unknown command error code 
@done_int: 
                or      ax, 0100h               ; set the 'done' bit 
                GETRQADDR 
                mov     DEV_RQ.status, ax 
 
                ; ??? restore stack ??? 
                pop     es                      ; Restore regs 
                pop     ds 
                popa 
                popf 
                ret 
_interrupt      endp 
 
 
ifdef           _DEBUG 
;---------------------------------------------------------------------------- 
; Display a char to the video(for debug usage) 
;---------------------------------------------------------------------------- 
_deb_outchar    proc    near 
                pushf 
                push bx                         ; save regs 
                push ax 
                mov ah, VD_GETPAGE 
                int VIDEO                       ; get the current page 
                pop ax 
                mov ah, VD_PRNCHAR              ; print the character 
                int VIDEO 
                pop bx                          ; restore regs 
                popf 
                ret 
_deb_outchar    endp 
endif           ;_DEBUG 
 
 
;---------------------------------------------------------------------------- 
; Build BPB 
;---------------------------------------------------------------------------- 
_buildbpb       proc    near 
DEB_PUTC CHR_BUILDBPB1 
                mov     DEV_RQ.bpb_bpb_ofs, offset ourbpb    ; the offset 
                mov     DEV_RQ.bpb_bpb_seg, cs               ; in our CS 
                mov     word ptr DEV_RQ.bpb_trans_ofs, 1     ; first FAT sector 
                mov     ax, DER_NOERR 
DEB_PUTC CHR_BUILDBPB2 
                ret      
_buildbpb       endp     
 
                         
;---------------------------------------------------------------------------- 
; Process device input(read) 
;---------------------------------------------------------------------------- 
_devinput       proc    near 
DEB_PUTC CHR_INPUT1 
                mov     dx, DEV_RQ.io_trans_seg              ; Get transfer 
                mov     ax, DEV_RQ.io_trans_ofs              ; address 
                SETBUFADDR 
                mov     cx, DEV_RQ.io_sec_cnt                ; Get sec/count 
                mov     ax, DEV_RQ.io_start_sec 
                call    _readsec 
DEB_PUTC CHR_INPUT2 
                ret      
_devinput       endp     
                         
                         
;---------------------------------------------------------------------------- 
; Process device output(write) 
;---------------------------------------------------------------------------- 
_devoutput      proc    near 
DEB_PUTC CHR_OUTPUT1 
                cmp     byte ptr [devstatus], DEV_READONLY 
                jne     @can_write          ; Readonly ? 
                mov     ax, DER_PROTECT 
ifdef           _DEBUG 
                jmp     @end_writerq 
else            ;_DEBUG 
                ret 
endif           ;_DEBUG 
@can_write: 
                mov     dx, DEV_RQ.io_trans_seg     ; Get transfer ptr 
                mov     ax, DEV_RQ.io_trans_ofs 
                SETBUFADDR 
                mov     cx, DEV_RQ.io_sec_cnt       ; Get sector count 
                mov     ax, DEV_RQ.io_start_sec     ; Start sector 
                call    _writesec 
@end_writerq: 
DEB_PUTC CHR_OUTPUT2 
                ret      
_devoutput      endp 
 
 
;---------------------------------------------------------------------------- 
; Process device output(write) with verify 
;---------------------------------------------------------------------------- 
;_devoutputv     proc    near 
;DEB_PUTC CHR_OUTPUT3 
;                cmp     byte ptr [devstatus], DEV_READONLY 
;                jne     @can_writev 
;                mov     ax, DER_PROTECT 
;ifdef           _DEBUG 
;                jmp     @end_writerqv 
;else            ;_DEBUG 
;                ret 
;endif           ;_DEBUG 
;@can_writev: 
;                mov     dx, DEV_RQ.io_trans_seg 
;                mov     ax, DEV_RQ.io_trans_ofs 
;                SETBUFADDR 
;                mov     cx, DEV_RQ.io_sec_cnt 
;                mov     ax, DEV_RQ.io_start_sec 
;                call    _writesec 
;@end_writerqv: 
;DEB_PUTC CHR_OUTPUT4 
;                ret 
;_devoutputv     endp 
 
endif            ;!_COMFILE 
 
                         
;**************************************************************************** 
; Device dependent code 
; 
; for each kind of flash memory 
; you need to implement the following procs and make some changes 
; of other messages/procs/vars if needed: 
; 
; 1. _readsec: read sector(s) 
; 2. _writesec: write sector(s) 
; 3. _resetflash: reset flash memory 
; 4. _do_format: initialize flash memory and write boot area to flash 
; 5. _getflashinfo: get flash information 
; 6. _do_scan1: perform read test(.com file) 
; 7. _do_scan2: perform write test(.com file) 
;**************************************************************************** 
TMP_BUF1_SIZE   equ    (16*FLASH_BLK_SIZE) 
TMP_BUF2_SIZE   equ    FLASH_BLK_SIZE 
 
FLASH_SEC_SIZE  equ     512  ; FLASH sector size 
SPARE_SIZE      equ     16   ; Each 512 bytes block has 16 bytes spare space 
FLASH_BLK_SIZE  equ     (FLASH_SEC_SIZE+SPARE_SIZE) 
                         
DEF_DELAY_LOOP  equ     500                 ; Default delay loop times 
MAX_DELAY_LOOP  equ     30000               ; Maximum delay loop times 
DELAY_INC       equ     300                 ; Increment before retry 
                         
; Ports                  
PORT_BASE       equ     570h 
PORT_CMD        equ     (00h+PORT_BASE)     ; Command port 
PORT_ADDR       equ     (01h+PORT_BASE)     ; Address port 
PORT_STATUS     equ     (02h+PORT_BASE)     ; Status port 
PORT_WRITE      equ     (03h+PORT_BASE)     ; Write data port 
PORT_READ       equ     (06h+PORT_BASE)     ; Read data port 
 
; Commands 
CMD_SEQ_DI      equ     080h                ; Sequential Data Input 
CMD_READ0       equ     000h                ; Read 0: 0-255 
CMD_READ1       equ     001h                ; Read 1: 256-511 
CMD_READ2       equ     050h                ; Read 2: 512-527 
CMD_READ_ID     equ     090h                ; Read ID 
CMD_RESET       equ     0ffh                ; Reset 
CMD_PAGE_PRG    equ     010h                ; Page Program 
CMD_BLK_ERA1    equ     060h                ; Block Erase(2 steps) 
CMD_BLK_ERA2    equ     0d0h 
CMD_ERA_SUSP    equ     0b0h                ; Erase suspend 
CMD_ERA_RESM    equ     0d0h                ; Erase Resume 
CMD_READ_STA    equ     070h                ; Read Status 
 
; Status commands 
STAT_WD         equ     00000000b           ; Write protected(disable): bit 0=0 
STAT_WE         equ     00000001b           ; Write enable: bit 0=1 
STAT_CE         equ     00000000b           ; Chip enable: bit 1=0 
STAT_CD         equ     00000010b           ; Chip disable: bit 1=1 
STAT_SE         equ     00000000b           ; Spare enable: bit 2=0 
STAT_SD         equ     00000100b           ; Spare disable: bit 2=1 
STAT_DOWN       equ     (STAT_CD+STAT_SD+STAT_WD) 
STAT_WRITE      equ     (STAT_CE+STAT_SE+STAT_WE) 
 
READ_MASK       equ     01000000b           ; Read state mask 
WRITE_MASK      equ     01000001b           ; Write state mask 
ERASE_MASK      equ     01100001b           ; Erase state mask 
GOOD_STAT       equ     01000000b           ; _readstat return success code 
 
BUG_BYTE        equ     0E0h                ; Bug byte if last written 
 
DEF_RESV_BLK    equ     128                 ; default reserved blocks 
 
flash_sec       dw      0                   ; flash memory sectors 
                         
loop_count1     dw      DEF_DELAY_LOOP      ; Delay loop times 
 
;----- Used in _writesec 
write_size      dw      ?                   ; Write size 
;                dw      ?                   ; high word 
sec_count       dw      ?                   ; Sector count 
wr_retry        db      ?                   ; Write retry 
first_blk_no    dw      ?                   ; First block number 
                                            ; Must and 0fff0h 
 
; IN: AX=sec num 
; OUT: AX=blk num, DX=blk ofs 
; CX is destroied 
SEC_TO_BLK      macro                       ; Calc blk num and ofs via sector 
                  mov     cx, DEF_SEC_SIZE 
                  mul     cx 
                  mov     cx, FLASH_BLK_SIZE 
                  div     cx 
                endm 
 
;---------------------------------------------------------------------------- 
; Read 16 blocks to tmp_buf 
; IN: 
; OUT: 
;   Carry set if failed 
; REG: ALL 
;---------------------------------------------------------------------------- 
_read16blk      proc    near 
                mov     ax, cs              ; Get buffer ptr to ES:DI 
                mov     es, ax 
                lea     di, tmp_buf 
                mov     cx, [first_blk_no]  ; Get start block 
                XOUTB2  PORT_STATUS, STAT_CE 
                XOUTB2  PORT_CMD, CMD_READ0 ; Region select 
 
                XOUTB2  PORT_ADDR, 0        ; Send addresses(from 0) 
                XOUTB1  cl 
                and     ch, 01fh 
                XOUTB1  ch 
 
                mov     dx, PORT_READ 
;                cld 
                mov     cx, TMP_BUF1_SIZE 
@r16b1: 
                in      al, dx              ; Read to buffer (ES:DI) 
                stosb 
                loop    @r16b1 
 
                XOUTB2  PORT_STATUS, STAT_DOWN      ; Shut down chip 
 
                call    _readstat           ; Get result 
                and     al, READ_MASK 
                cmp     al, GOOD_STAT 
                jne     @rberr 
 
                clc 
                ret 
@rberr: 
                stc 
                ret 
_read16blk      endp 
 
            
;---------------------------------------------------------------------------- 
; Write 16 blocks from tmp_buf 
; IN:       
; OUT:      
;   Carry set if failed 
; REG: ALL  
;---------------------------------------------------------------------------- 
_write16blk     proc    near 
;                cld 
                mov     byte ptr [wr_retry], 0      ; retry=0 
@delblk:    
BREAK_HERE 
                mov     ax, [first_blk_no]  ; del 16 blocks 
                call    _del16blk 
                jc      @writebad 
            
                lea     si, tmp_buf         ; Get buffer ptr to DS:SI 
                mov     cx, ax              ; Keep start block 
                mov     bx, 0               ; block counter 
                mov     bp, ax              ; save start block 
 
 
; Can only write one block each time 
@w16b0:     
                add     cx, bx              ; CX=Start block, BX=block index 
            
                XOUTB2  PORT_STATUS, STAT_WRITE    ; Notify begin 
            
                XOUTB2  PORT_CMD, CMD_READ0 ; Region select 
                XOUTB1  CMD_SEQ_DI          ; Notify write 
            
                XOUTB2  PORT_ADDR, 0        ; Send addresses(from 0) 
                XOUTB1  cl 
                and     ch, 01fh 
                XOUTB1  ch 
            
                mov     dx, PORT_WRITE 
                mov     cx, FLASH_BLK_SIZE 
@w16b1: 
                lodsb                       ; Read from buffer 
                out     dx, al              ; Write to port 
                nop                         ; nop=3 clock in 186 
                nop                         ; while jmp short=13 
                loop    @w16b1 
 
ifdef _BUGGY 
                cmp     al, BUG_BYTE 
                jne     @w16b2 
DEB_PUTC CHR_BUG 
; To fix the chip's bug, it should out one more byte, 
                XOUTB1  0 
endif           ; _BUGGY 
 
@w16b2: 
                XOUTB2  PORT_CMD, CMD_PAGE_PRG  ; Write one blk finished 
                mov     cx, [loop_count1]   ; delay 
@loopwrite:              
                DELAY 
                loop    @loopwrite 
 
                XOUTB2  PORT_STATUS, STAT_DOWN  ; Shut down chip 
                         
                call    _readstat 
                and     al, WRITE_MASK 
                cmp     al, GOOD_STAT 
                mov     cx, bp              ; restore CX 
                je      @nextblk 
 
@writebad: 
                mov     ax, [loop_count1]   ; Inc delay loop 
                cmp     ax, MAX_DELAY_LOOP 
                jae     @chkretry 
                add     ax, DELAY_INC 
                mov     word ptr [loop_count1], ax 
@chkretry: 
DEB_PUTC CHR_RETRY 
                inc     byte ptr [wr_retry] ; Failed, retry ? 
                cmp     byte ptr [wr_retry], MAX_RETRY 
                ja      @writeabort 
                jmp     @delblk 
@writeabort: 
                stc 
                ret 
@nextblk: 
                inc     bl 
                cmp     bl, 16              ; loop 16 times 
                jae     @donewrite 
                jmp     @w16b0 
@donewrite: 
                clc 
                ret 
 
_write16blk     endp 
 
 
 
ifndef          _COMFILE 
;---------------------------------------------------------------------------- 
; Read sector(s) 
; IN: 
;   AX=start sector number 
;   CX=sector count 
; OUT: 
;   CX=sector actually read 
;   AX=error code, DER_NOERR if no error 
; REG: ALL 
;---------------------------------------------------------------------------- 
_readsec        proc    near 
; In KM29xxx, you need to 
; 1. calc position 
; 2. send diff cmd for 3 part(0..255, 256..511, 512..527) 
; 3. read to buffer 
                mov     bp, ax 
                call    _chksecrange        ; Assume sector range 
                mov     ax, bp 
                cmp     cx, 0               ; Nothing to write ? 
                jne     @calc_ofs 
                mov     ax, DER_MISSEC 
                ret 
 
@calc_ofs: 
BREAK_HERE 
                mov     di, [tbuf_ofs]      ; Get transfer ptr 
                mov     si, [tbuf_seg] 
                mov     es, si 
                xor     si, si              ; SI = sector counter 
 
                mov     bx, cx              ; Save to BX=sector count 
                SEC_TO_BLK 
                mov     bp, ax              ; AX=block number, save it 
                mov     cx, dx 
 
                XOUTB2  PORT_STATUS, STAT_CE 
 
                cmp     cx, 256             ; determine which region 
                jae     @rdreg_1 
                mov     al, CMD_READ0       ; 1st part 
                jmp     @rdreg_2 
@rdreg_1: 
                mov     al, CMD_READ1       ; 2nd part 
                sub     cx, 256 
                cmp     cx, 256 
                jb      @rdreg_2 
 
                sub     cx, 255             ; Calc bytes to be skipped 
                mov     dx, PORT_CMD        ; Always 1 here 
                out     dx, al 
                XOUTB2  PORT_ADDR, 255      ; start from 511 
                mov     ax, bp 
                out     dx, al 
                and     ah, 01fh 
                XOUTB1  ah 
                mov     dx, PORT_READ 
@pread: 
                in      al, dx              ; Skip bytes 
                loop    @pread              ; always 1 if secsize=512 
                jmp     @rd_sec 
 
@rdreg_2: 
                mov     dx, PORT_CMD        ; Region select 
                out     dx, al 
 
                XOUTB2  PORT_ADDR, cl       ; Send addresses 
                mov     ax, bp              ; Get blk num 
                out     dx, al 
                and     ah, 01fh 
                XOUTB1  ah 
 
                mov     dx, PORT_READ 
@rd_sec: 
                cmp     bx, 0               ; BX = sector remain 
                je      @done_rd1           ; No sector to read ? 
                dec     bx 
                mov     cx, DEF_SEC_SIZE    ; Size to read 
@rd_port_1: 
                in      al, dx              ; Read to buffer (ES:DI) 
                stosb 
                loop    @rd_port_1 
 
                inc     si                  ; SI = Sector counter 
                jmp     @rd_sec             ; LOOP until all sectors read 
 
@done_rd1: 
                XOUTB2  PORT_STATUS, STAT_DOWN 
                call    _readstat 
                and     al, READ_MASK 
                cmp     al, GOOD_STAT 
                jne     @rderr 
                mov     ax, DER_NOERR       ; Return code 
                mov     cx, si              ; Sector count 
                ret 
@rderr: 
                mov     ax, DER_RDFAULT 
                ret 
_readsec        endp 
 
 
;---------------------------------------------------------------------------- 
; Copy write buffer to tmp_buf 
; IN:                        
;   ES:DI=destination        
;   SI=source offset         
;   CX=count in byte         
; OUT:                       
; REG: AX,CX,SI              
;---------------------------------------------------------------------------- 
_copytotmp      proc    near 
                mov     ax, [tbuf_seg] 
                mov     ds, ax 
;                test    cl, 1               ; restore this piece of code 
;                jz      @copyit             ; if sector/block size is odd 
;                movsb                       ; odd fix 
@copyit: 
                shr     cx, 1               ; faster use movsw 
                rep     movsw 
                mov     ax, cs              ; restore DS 
                mov     ds, ax 
                ret          
_copytotmp      endp         
 
 
;---------------------------------------------------------------------------- 
; Write sector(s) 
; IN: 
;   AX=start sector number 
;   CX=sector count 
; OUT:                         
;   CX=sector actually write 
;   AX=error code, DER_NOERR=no error 
; REG: ALL 
;---------------------------------------------------------------------------- 
_writesec       proc    near 
; In KM29xxx, you need to 
; 1. calc position 
; 2. save 16 blocks 
; 3. write data to saved buffer 
; 4. del 16 blocks 
; 5. write 16 blocks 
; 6. loop 1 if needed 
 
                mov     bp, ax 
                call    _chksecrange        ; Check sector range 
                mov     ax, bp 
                cmp     cx, 0               ; Nothing ? 
                jne     @write1 
                mov     ax, DER_MISSEC 
                ret 
 
@write1: 
                mov     dx, cs 
;                mov     ds, dx 
                mov     es, dx 
                mov     si, [tbuf_ofs]      ; Get transfer addr 
 
                xor     di, di              ; Offset=0 
                mov     [sec_count], cx 
                SEC_TO_BLK                  ; Sector to blk:ofs(AX:DX) 
                add     di, dx              ; DX=block offset, add offset 
                mov     cx, ax              ; AX=block number 
                and     ax, 0fff0h          ; Get the first of 16 blk 
                sub     cx, ax 
                mov     [first_blk_no], ax  ; Save first block number 
                mov     ax, cx 
                mov     cx, FLASH_BLK_SIZE 
                mul     cx 
                add     di, ax              ; Add blocks size, DI=BO 
                mov     ax, [sec_count]     ; Calc write size 
                mov     cx, DEF_SEC_SIZE 
                mul     cx 
                mov     word ptr [write_size], ax    ; Save it 
;                mov     word ptr [write_size+2], dx 
ifdef           _DEBUG 
                cmp     dx, 0               ; Check dx(rq > 64k?) 
                je      @write2 
DEB_PUTC CHR_OVERFLOW 
endif           ;_DEBUG 
 
; 1. Save blocks 
@write2:                       
                mov     bp, di              ; DI will be destroied 
                call    _read16blk 
                mov     di, bp 
                jc      @wrerr 
; 2. Write sector to saved buffer 
;      16 blk 
;    |=============| 
; 1.    |----|            ; write size in range 
; 2.    |---------------| ; write size out of range 
; 
                mov     ax, word ptr [write_size]       ; L > 16blk-BO ? 
;                mov     dx, word ptr [write_size+2]     ; DX:AX=L 
                mov     cx, TMP_BUF1_SIZE               ; 16*FLASH_BLK_SIZE 
                sub     cx, di                  ; CX=16blk-BO 
                add     di, offset tmp_buf      ; Make DI the actual index 
;                cmp     dx, 0                   ; DX>0, surely DX:AX>CX 
;                ja      @write3 
                cmp     ax, cx                  ; L > 16blk-BO ? 
                ja      @write3                 ; Yes, jump 
                mov     cx, ax                  ; No, WL=L 
                jmp     @write4 
                               
@write3:        ;  Not enough to hold the whole write buffer 
BREAK_HERE 
                sub     ax, cx                  ; Now CX=WL=16blk-BO 
;                sbb     dx, 0                   ; L -= WL 
                mov     word ptr [write_size], ax 
;                mov     word ptr [write_size+2], dx      ; Save write size 
 
                call    _copytotmp              ; SI is advanced 
 
                push    si                      ; Write a portion of data 
                call    _write16blk 
                pop     si 
                jc      @wrerr 
 
                mov     ax, [first_blk_no]      ; Adv. 16 blks 
                xor     di, di                  ; Offset=0 
                add     ax, 16 
                mov     [first_blk_no], ax       
                jmp     @write2 
 
@write4:        ; Can hold the remain buffer 
                call    _copytotmp 
@write5:                       
                call    _write16blk 
                jc      @wrerr 
                mov     cx, [sec_count]         ; Return sector count 
                mov     ax, DER_NOERR           ; and error code 
                ret 
@wrerr: 
                xor     cx, cx 
                mov     ax, DER_WRFAULT 
                ret 
_writesec       endp 
 
 
;---------------------------------------------------------------------------- 
; Check and assume sector range 
; IN: 
;   AX=start sector 
;   CX=sector count 
; OUT: 
;   CX=sector count 
; REG: AX, CX, DX 
;---------------------------------------------------------------------------- 
_chksecrange    proc    near 
 
                mov     dx, word ptr [flash_sec] 
                dec     dx 
                cmp     dx, ax              ; Total sector-1 < start sector ? 
                jb      @sec_err1           ; Yes, error! 
                inc     dx 
                sub     dx, ax              ; Max remain sector 
                cmp     cx, dx              ; Remain secotr < sector count ? 
                ja      @sec_err2           ; <, valid range 
                ret 
@sec_err1: 
                xor     cx, cx              ; Invalid start sector 
DEB_PUTC CHR_INVSEC 
                ret 
@sec_err2: 
                mov     cx, dx              ; no, alter it 
DEB_PUTC CHR_RANGE 
                ret 
 
_chksecrange    endp 
 
endif           ;!_COMFILE 
 
 
;---------------------------------------------------------------------------- 
; Read status (operation result) 
; IN: 
; OUT: 
;   AL=status 
; REG: AX,DX                   
;---------------------------------------------------------------------------- 
_readstat       proc    near 
                XOUTB2  PORT_STATUS, STAT_CE    ; Select 
                XOUTB2  PORT_CMD, CMD_READ_STA  ; Read status command 
                XIN     PORT_READ               ; Read status 
                mov     ah, al 
                XOUTB2  PORT_STATUS, STAT_DOWN  ; Shut down 
                mov     al, ah 
                ret 
_readstat       endp 
 
 
;---------------------------------------------------------------------------- 
; Erase 16 blocks 
; IN: 
;   AX=Block number 
; OUT: 
;   Carry set if error 
; REG: NONE 
;---------------------------------------------------------------------------- 
_del16blk       proc    near 
                push    ax                      ; Save registers 
                push    dx 
                push    cx 
 
                mov     cx, ax 
                XOUTB2  PORT_STATUS, STAT_WRITE      
                XOUTB2  PORT_CMD, CMD_BLK_ERA1  ; First step 
                XOUTB2  PORT_ADDR, cl 
                and     ch, 01fh 
                XOUTB1  ch 
                XOUTB2  PORT_CMD, CMD_BLK_ERA2  ; Second step 
 
                mov     cx, [loop_count1]       ; Delay 
@loopdel: 
                DELAY 
                loop    @loopdel 
 
                XOUTB2  PORT_STATUS, STAT_DOWN  ; Shut down chip 
 
                call    _readstat               ; Get operation result 
                and     al, ERASE_MASK 
                cmp     al, GOOD_STAT 
                jne     @delbad 
                clc 
                jmp     @donedel 
@delbad: 
                stc 
@donedel: 
                pop     cx                      ; Restore registers 
                pop     dx 
                pop     ax 
 
                ret 
_del16blk       endp 
 
 
@driver_end     =       $                   ; +tmp_buf=last part of driver code 
 
; Initialize tmp_buf only contains 1 byte. 
; After initialization, the code following will be 
; overwrited for use of buffer(16 block), save 16 blocks-1 
; BE SURE NOT TO CALL _read16blk/_write16blk BEFORE 
; FINISHING INITIALIZATION !!! 
 
; For non-device, the buffer locate at the end to save size 
ifndef          _COMFILE 
ifndef          _NEEDBUF 
tmp_buf         db      0 
else            ;!_NEEDBUF 
tmp_buf         db      (TMP_BUF1_SIZE) dup(?) 
endif           ;!_NEEDBUF 
endif           ;!_COMFILE 
                             
; Code below is not resident 
;============================================================================ 
 
root_sec        dw      0                   ; how many sec in root dir 
vol_0           dw      0                   ; to save volumn sector num 
vol_1           dw      0 
 
fat16_start     db      DRV_MEDIA, 0ffh, 0ffh, 0ffh 
FAT16_START_LEN equ     4 
 
disk_vol        db      VOL_NAME            ; 11-byte volume name 
                db      000001000b          ; volume label attribute 
                db      10 dup (0)          ; reserved space 
                dw      953 
                        ;FEDCBA9876543210b 
                        ;YYYYYYYMMMMDDDDD   YYYY=year-1980 
                dw      00010010010100100b  ; DATE 
                db      6 dup (0)           ; more reserved space 
DISK_VOL_LEN    equ     32 
 
 
;---------------------------------------------------------------------------- 
; Reset flash memory 
; IN:                    
; OUT: 
; REG: AX,DX 
;---------------------------------------------------------------------------- 
_resetflash     proc    near 
                XOUTB2  PORT_STATUS, STAT_CE    ; Select 
                XOUTB2  PORT_CMD, CMD_RESET     ; Reset command 
                XOUTB2  PORT_STATUS, STAT_DOWN  ; Shut down 
                ret 
_resetflash     endp     
 
 
;---------------------------------------------------------------------------- 
; Read one block to tmp_buf2 
; IN: 
;    AX=block num 
; OUT: 
;    AL=FALSE: faild 
; REG: 
;    AX, CX, DX, DI 
;---------------------------------------------------------------------------- 
_readblk        proc    near 
;                cld 
                mov     cx, ax 
                XOUTB2  PORT_STATUS, STAT_CE 
 
                XOUTB2  PORT_CMD, CMD_READ0 
                XOUTB2  PORT_ADDR, 0        ; Send addresses(from 0) 
                XOUTB1  cl 
                and     ch, 01fh 
                XOUTB1  ch 
 
                mov     cx, cs 
                mov     es, cx 
                mov     di, offset tmp_buf2 
                mov     word ptr es:[di], 55h 
                mov     cx, TMP_BUF2_SIZE   ; Size to read 
                mov     dx, PORT_READ 
@rb1: 
                in      al, dx              ; Read to buffer (ES:DI) 
                stosb 
                loop    @rb1 
 
                call    _readstat           ; Get operation result 
                and     al, READ_MASK 
                cmp     al, GOOD_STAT 
                mov     al, TRUE 
                je      @donerb 
                mov     al, FALSE 
@donerb: 
                ret 
_readblk        endp 
 
 
;---------------------------------------------------------------------------- 
; Write one block from tmp_buf2 
; IN: 
;    AX=block num 
; OUT: 
;    AL=FALSE: faild 
; REG: 
;    AX, CX, DX, DI 
;---------------------------------------------------------------------------- 
_writeblk       proc    near 
                mov     cx, ax              ; Save to CX 
 
                XOUTB2  PORT_STATUS, STAT_WRITE 
                XOUTB2  PORT_CMD, CMD_READ0 ; Region select 
                XOUTB1  CMD_SEQ_DI          ; Notify write 
            
                XOUTB2  PORT_ADDR, 0        ; Send addresses(from 0) 
                XOUTB1  cl 
                and     ch, 01fh 
                XOUTB1  ch 
 
                mov     dx, PORT_WRITE 
                mov     cx, TMP_BUF2_SIZE 
                lea     si, tmp_buf2 
@wblk1: 
                lodsb 
                out     dx, al              ; Write to port 
                DELAY 
                loop    @wblk1              ; Loop to fill 
 
                XOUTB2  PORT_CMD, CMD_PAGE_PRG  ; Write one blk finished 
                         
                mov     cx, [loop_count1]   ; delay 
@loopwblk: 
                DELAY 
                loop    @loopwblk 
 
                XOUTB2  PORT_STATUS, STAT_DOWN  ; Shut down chip 
                         
                call    _readstat           ; Check state/result 
                and     al, WRITE_MASK 
                cmp     al, GOOD_STAT 
                mov     al, TRUE 
                je      @donewblk 
                mov     al, FALSE 
@donewblk: 
                ret 
_writeblk       endp 
 
 
;---------------------------------------------------------------------------- 
; memset tmp_buf2 to AL 
; IN: 
;   ES=CS 
;   AL=char to fill 
; OUT: 
; REG: DI, CX 
;---------------------------------------------------------------------------- 
_memsetbuf      proc    near 
                lea     di, tmp_buf2 
                mov     cx, TMP_BUF2_SIZE 
                rep     stosb 
                ret 
_memsetbuf      endp 
 
 
;---------------------------------------------------------------------------- 
; Write boot info 
; IN: 
; OUT: 
;   AL=FALSE: failed 
; REG: ALL 
;---------------------------------------------------------------------------- 
_writeboot      proc    near 
; 1. delete needed blocks (already done in _do_format) 
; 2. write boot sector(ourboot) 0 sector 
; 3. write signature to first FAT 
; 4. write root dir's volumn label(not implement) 
 
                mov     ax, cs 
                mov     es, ax 
 
                mov     al, 0 
                call    _memsetbuf          ; Copy boot rec to buffer 
                lea     si, ourboot 
                mov     cx, OURBOOTLEN 
                lea     di, tmp_buf2 
                rep     movsb 
 
; !!! here I know that FAT header is at the end of block 0 !!! 
; it must be altered if not 
                lea     si, fat16_start     ; Copy FAT head to buffer 
                mov     cx, FAT16_START_LEN 
                lea     di, tmp_buf2 
                add     di, DEF_SEC_SIZE 
                rep     movsb 
 
                mov     ax, 0               ; Write block 0 
                call    _writeblk 
                cmp     al, FALSE 
                je      @wberr 
 
                mov     al, 0 
                call    _memsetbuf          ; Zero buffer 
                mov     ax, [sector_fat] 
                inc     ax                  ; Root start sector 
                SEC_TO_BLK 
                mov     [vol_0], ax 
                mov     [vol_1], ax 
                lea     di, tmp_buf2 
                add     di, dx              ; Calc offset in buffer 
                lea     si, disk_vol        ; Write volumn label to root dir 
                mov     cx, TMP_BUF2_SIZE 
                sub     cx, dx 
                cmp     cx, DISK_VOL_LEN 
                jb      @vol2blk 
                mov     cx, DISK_VOL_LEN 
                rep     movsb 
                call    _writeblk 
                cmp     al, FALSE 
                je      @wberr 
                jmp     @fillzero 
@wberr: 
                mov     al, FALSE           ; Error 
                ret 
@vol2blk:                                   ; Volumn is filled in two blocks 
                inc     word ptr [vol_1] 
                push    ax 
                push    cx 
                rep     movsb               ; Write first block 
                push    si 
                call    _writeblk 
                pop     si 
                mov     al, 0 
                call    _memsetbuf 
                pop     ax 
                mov     cx, DISK_VOL_LEN    ; Fill another block 
                sub     cx, ax 
                pop     ax 
                inc     ax 
                lea     di, tmp_buf2 
                rep     movsb 
                call    _writeblk 
                cmp     al, FALSE 
                je      @wberr 
 
ifdef           _DEBUG 
                mov     ax, [vol_0] 
                call    _readblk 
                mov     ax, [vol_1] 
                call    _readblk 
endif 
 
@fillzero: 
                mov     al, 0 
                call    _memsetbuf 
                mov     ax, [sector_fat] 
                add     ax, [root_sec] 
                inc     ax                  ; AX=sys area sector count 
                SEC_TO_BLK                  ; Calc blk count to hold sys area 
                cmp     dx, 0 
                je      @wb1 
                inc     ax 
 
@wb1: 
                mov     cx, ax 
                mov     ax, 1               ; Start from 1(2nd) 
@wb2: 
                cmp     ax, [vol_0] 
                je      @wb3 
                cmp     ax, [vol_1] 
                je      @wb3 
                push    ax 
                push    cx 
                call    _writeblk           ; Fill them to zero 
                cmp     al, FALSE 
                pop     cx 
                pop     ax 
                je      @wberr 
@wb3: 
                inc     ax 
                cmp     ax, cx 
                jb      @wb2 
 
ifdef           _DEBUG                      ; Check it 
                mov     ax, 0 
@wb4: 
                push    cx 
                push    ax 
                call    _memsetbuf          ; Fill buffer with AL 
                call    _readblk            ; Read from flash 
                cmp     al, FALSE 
                pop     ax 
                pop     cx 
                je      @wberr 
                inc     ax 
                cmp     ax, cx 
                jb      @wb4 
 
endif           ;_DEBUG 
 
                mov     al, TRUE            ; Success 
                ret 
_writeboot      endp 
 
 
 
;---------------------------------------------------------------------------- 
; Show progress via block num; 
; IN: 
;   AX=block number 
; OUT: 
; REG: 
;---------------------------------------------------------------------------- 
_showprg        proc    near 
                push    ax 
                push    dx 
                test    al, 00110000b 
                jnz     @noprint            ; 1/4 chance 
                mov     dl, '.' 
                CALLDOS DOS_PRNCHAR         ; Show progress 
@noprint: 
                pop     dx 
                pop     ax 
                ret 
_showprg        endp 
 
 
;---------------------------------------------------------------------------- 
; Format/initialize flash memory and make it a disk 
; IN: 
; OUT: 
;   AL=APE_NOERR: success 
;   otherwise AL=error code 
; REG: ALL 
;---------------------------------------------------------------------------- 
_do_format      proc    near 
; 1. Zero all data, check for bad? 
; 2. Calc sys params 
; 3. Call _writeboot to init sys area 
                CALLDOS     DOS_FLUSH 
                PRINTMSG    msg_format 
 
                mov     ax, [root_dirs] 
                mov     cx, 32 
                mul     cx                  ; Root dir size 
                add     ax, DEF_SEC_SIZE-1  ; Round it 
                mov     cx, DEF_SEC_SIZE 
                div     cx 
                mov     [root_sec], ax      ; Root dir sector count 
 
                mov     ax, DEF_SEC_SIZE    ; Update BPB 
                mov     [bytes_sector], ax 
 
                mov     ax, [flash_sec]     ; Total sector 
                mov     [num_sec], ax       ;  (2(TS-y-1-RS)+FSL+DSS-1)/DSS=y 
                                            ; => y = (2*(TS-RS)+DSS+FSL-3)/(DSS+2) 
                sub     ax, [root_sec]      ; Sub root sectors 
                shl     ax, 1               ; Every sector use 2 bytes in FAT 
                add     ax, DEF_SEC_SIZE    ; TS=total sec; RS=root sec 
                add     ax, FAT16_START_LEN ; FSL=FAT16_START_LEN 
                sub     ax, 3               ; DSS=DEF_SEC_SIZE 
                xor     dx, dx              ; 
                mov     cx, DEF_SEC_SIZE    ; 
                add     cx, 2               ; 
                div     cx                  ; 
                mov     [sector_fat], ax 
 
                mov     di, [flash_blk]     ; Include reserved blocks 
                mov     ax, 0               ; Begin with first block 
 
@erablk: 
                call    _del16blk           ; Erase 16 block 
                mov     dx, offset em_erase 
                jc      @makeerr 
 
@showfmtprg: 
                call    _showprg 
                add     ax, 16 
                cmp     ax, di 
                jb      @erablk             ; Finish ? 
 
                mov     ax, 0               ; Check boot sector 
 
                call    _readblk            ; Erased ? 
                cmp     al, FALSE 
                je      @makeerr 
                lea     di, tmp_buf2 
                mov     cx, TMP_BUF2_SIZE 
 
                mov     al, 0ffh 
                repe    scasb 
                jcxz    @dowriteboot 
                jmp     @makeerr 
 
;@chkFF: 
;                lodsb                       ; Loop to check 
;                cmp     al, 0ffh            ; if they are all 0xFF 
;                jne     @makeerr 
;                loop    @chkFF 
@dowriteboot: 
                call    _writeboot          ; Write boot info 
                cmp     al, FALSE 
                mov     dx, offset em_wrboot 
                je      @makeerr 
 
                PRINTMSG    msg_endformat   ; Success 
                mov     al, APE_NOERR 
                ret 
 
@makeerr: 
                CALLDOS DOS_PRNMSG 
                PRINTMSG    em_format       ; Error 
                mov     al, APE_FORMAT 
                ret 
_do_format      endp 
 
 
;----- Table of flash ID and their size 
LEN_SIZETBL     equ     6 
sizetbl1        dw      0eca4h, 0ec6eh, 0eceah, 0ece3h, 0ece6h, 0ec73h 
sizetbl2        dw      512,    1024,   2048,   4096,   8192,   16384 
 
;---------------------------------------------------------------------------- 
; Get flash memory information 
; IN: 
; OUT:                 
;   AL=0: no flash 
;   AL=1: success 
;   AL=2: need to be formatted 
; REG: ALL 
;---------------------------------------------------------------------------- 
_getflashinfo   proc    near 
; 1. get size of flash 
; 2. if flash exists, get 0th sector(1st block), else exit error 
; 3. if sector 0 is valid, get info and exit 
; 4. return need to be formatted 
 
                XOUTB2  PORT_STATUS, STAT_CE 
 
                XOUTB2  PORT_CMD, CMD_READ_ID 
                XOUTB2  PORT_ADDR, 0        ; Send addresses(from 0) 
                XIN     PORT_READ 
                mov     ah, al 
                XIN     PORT_READ 
                mov     cx, ax 
                XOUTB2  PORT_STATUS, STAT_DOWN 
                mov     ax, cx 
 
                mov     cx, LEN_SIZETBL     ; Search in size table 
                mov     dx, 0 
                lea     di, sizetbl1 
                repne   scasw 
                jne     @donegetsize 
                mov     bx, LEN_SIZETBL 
                sub     bx, cx 
                add     bx, offset sizetbl2 + 2 
                mov     dx, [bx] 
 
@donegetsize: 
                mov     ax, dx 
                cmp     ax, 0               ; No flash ? 
                je      @errflash 
 
                mov     [flash_kb], ax 
                mov     cx, 1024            ; shl 1? Get block count 
                mul     cx 
                mov     cx, FLASH_SEC_SIZE 
                div     cx 
                mov     [flash_blk], ax 
                sub     ax, DEF_RESV_BLK    ; Sub reserved blocks 
                mov     cx, FLASH_BLK_SIZE 
                mul     cx                  ; Get total bytes 
                mov     cx, DEF_SEC_SIZE 
                div     cx                  ; Get sector count 
                mov     [flash_sec], ax 
 
                mov     ax, DEF_RESV_BLK    ; Fill reserved block count 
                mov     [freserved], ax 
 
                mov     ax, 0               ; Read boot sector 
                call    _readblk 
                cmp     al, FALSE 
                je      @errflash 
 
;                cld 
                lea     si, ourboot 
                lea     di, tmp_buf2 
                mov     cx, (8+3+8)         ; Jump+Name+BYTE_SEC..ROOT_DIRS 
                repe    cmpsb 
                je      @copyboot 
                mov     al, 2               ; To be formatted 
                ret 
@copyboot: 
                lea     di, ourboot         ; Duplicat boot record & BPB 
                lea     si, tmp_buf2 
                mov     cx, OURBOOTLEN 
                rep     movsb 
                mov     ax, [num_sec] 
                mov     [flash_sec], ax 
                mov     al, 1               ; Success 
                ret 
@errflash: 
                mov     al, 0               ; Error 
                ret 
_getflashinfo   endp     
 
 
ifdef           _COMFILE 
scan_ret        db      APE_NOERR 
 
;---------------------------------------------------------------------------- 
; Show scan error 
; IN: 
;   AL=error code 
; OUT: 
; REG: 
;---------------------------------------------------------------------------- 
_scan_err       proc    near 
                mov     [scan_ret], al 
                call    _show_err 
                ret 
_scan_err       endp 
 
 
;---------------------------------------------------------------------------- 
; Scan flash memory method 1 
; IN: 
; OUT: 
;   AL=APE_NOERR: success 
;   otherwise AL=error code 
; REG: ALL 
;---------------------------------------------------------------------------- 
_do_scan1       proc    near 
; 1. Proceed level 1: read 16 blk to tmp_buf, copy to tmp_buf+TMP_BUF1_SIZE 
;    read 16 blk to tmp_buf again, cmp them, loop until end 
                mov     cx, [flash_blk] 
                mov     ax, 0 
                mov     byte ptr [scan_ret], APE_NOERR 
@scan1next: 
                cmp     byte ptr [ctrl_break], 1 
                je      @break 
                cmp     ax, cx 
                jae     @done_scan1 
                call    _showprg 
 
                push    cx 
                mov     [first_blk_no], ax 
                call    _read16blk              ; read 16 blk 
                jc      @scan1err 
                mov     cx, TMP_BUF1_SIZE 
                lea     si, tmp_buf 
                mov     di, si 
                add     di, cx 
                rep     movsb                   ; copy to another buffer 
                call    _read16blk              ; read again 
                jc      @scan1err 
                mov     cx, TMP_BUF1_SIZE 
                lea     si, tmp_buf 
                mov     di, si 
                add     di, cx 
                repe    cmpsb 
                jc      @scan1err 
@scan1_3: 
                mov     ax, [first_blk_no] 
                pop     cx 
                add     ax, 16 
                jmp     @scan1next 
@scan1err: 
                mov     al, APE_READ 
                call    _scan_err 
                jmp     @scan1_3 
 
@done_scan1: 
                mov     al, [scan_ret]          ; Get return code 
                ret 
 
_do_scan1       endp 
 
 
@break: 
                mov     al, APE_ABORT 
                mov     byte ptr [scan_ret], al 
                retn 
 
 
;---------------------------------------------------------------------------- 
; Scan flash memory method 2 
; IN: 
; OUT: 
;   AL=APE_NOERR: success 
;   otherwise AL=error code 
; REG: ALL 
;---------------------------------------------------------------------------- 
_do_scan2       proc    near 
; 2. Ask to procced level 2: read 16 blk, del 16 blk, chk del, write 16 blk, 
;    read 16 blk, cmp 
                mov     cx, [flash_blk] 
                mov     ax, 0 
                mov     byte ptr [scan_ret], APE_NOERR 
@scan2next: 
                cmp     byte ptr [ctrl_break], 1 
                je      @break 
                cmp     ax, cx 
                jae     @done_scan2 
                call    _showprg 
 
                push    cx 
                mov     [first_blk_no], ax 
                mov     byte ptr [wr_retry], 0 
                call    _read16blk              ; read 16 blk 
                jnc     @scan2_1 
                mov     al, APE_READ 
                jmp     @scan2err 
@scan2_1: 
                mov     cx, TMP_BUF1_SIZE 
                lea     si, tmp_buf 
                mov     di, si 
                add     di, cx 
                rep     movsb                   ; copy to another buffer 
 
@scan2del: 
                mov     ax, [first_blk_no] 
                call    _del16blk 
                jc      @scan2retry 
                call    _read16blk              ; test delete 
                jnc     @scan2_2 
                mov     al, APE_READ 
                jmp     @scan2err 
@scan2_2: 
                mov     al, 0ffh 
                lea     di, tmp_buf 
                mov     cx, TMP_BUF1_SIZE 
                repe    scasb                   ; all deleted? 
                je      @scan2copy 
@scan2retry: 
                inc     byte ptr [wr_retry] 
                cmp     byte ptr [wr_retry], MAX_RETRY 
                jbe     @scan2del 
                mov     al, APE_ERASE 
                jmp     @scan2err 
@scan2copy: 
                mov     cx, TMP_BUF1_SIZE       ; copy back 
                lea     di, tmp_buf 
                mov     si, di 
                add     si, cx 
                rep     movsb 
@scan2write: 
                call    _write16blk             ; write back 
                jnc     @scan2_3 
                inc     byte ptr [wr_retry] 
                cmp     byte ptr [wr_retry], MAX_RETRY 
                jbe     @scan2write 
                mov     al, APE_WRITE 
                jmp     @scan2err 
@scan2_3: 
                call    _read16blk              ; read again 
                jnc     @scan2_4 
                mov     al, APE_READ 
                jmp     @scan2err 
@scan2_4: 
                mov     cx, TMP_BUF1_SIZE 
                lea     si, tmp_buf 
                mov     di, si 
                add     di, cx 
                repe    cmpsb                   ; compare 
                je      @scan2_5 
                mov     al, APE_WRITE 
                jmp     @scan2err 
@scan2_5: 
                mov     ax, [first_blk_no] 
                pop     cx 
                add     ax, 16 
                jmp     @scan2next 
 
@done_scan2: 
                mov     al, [scan_ret] 
                ret 
@scan2err: 
                call    _show_err 
                jmp     @scan2_5 
                ret 
 
_do_scan2       endp 
 
 
endif           ;_COMFILE 
 
 
;**************************************************************************** 
; End of device dependent code 
 
                         
;----- Normal message    
msg_init        db      'Initilizing...', EOM 
msg_endinit     db      'Finished.', EOM 
msg_error       db      'Error found!', BELL, EOM 
msg_errcode     db      'Error message: ', EOS 
msg_format      db      'Formatting...', EOM 
msg_endformat   db      'Format finished.', EOM 
msg_disk1       db      'Driver installed as drive ' 
msg_disknum     db      '?' 
                db      ':', EOM 
msg_size1       db      ' KB total flash memory.', EOM 
msg_size2       db      ' blocks reserved.', EOM 
msg_readonly    db      'Device is readonly', EOM 
msg_writable    db      'Device is writable', EOM 
;----- Error message                         
em_noflash      db      'No flash installed', EOM 
em_format       db      'Error formatting flash disk', EOM 
em_erase        db      'Error erasing flash memory', EOM 
em_wrboot       db      'Error writing system area', EOM 
                         
;---------------------------------------------------------------------------- 
; Driver initialization 
;---------------------------------------------------------------------------- 
_initialize     proc    near 
DEB_PUTC CHR_INIT1 
                PRINTMSG    appname         ; Show copyright 
                PRINTMSG    copyright 
                PRINTMSG    msg_init 
                mov     ax, cs 
                mov     es, ax 
                call    _resetflash 
                call    _getflashinfo       ; Get flash info 
ifdef           _DEBUG 
                jmp     @formatdsk          ; Always format 
else            ;_DEBUG 
                cmp     al, 0 
                je      @noflash 
                cmp     al, 1 
                jne     @formatdsk 
                PRINTMSG    msg_endinit 
endif           ;_DEBUG 
 
@initgood: 
 
ifndef          _COMFILE 
;                GETRQADDR                               ; ????? 
;                mov     di, DEV_RQ.init_bpb_ofs         ; Get command line 
;                mov     ax, DEV_RQ.init_bpb_seg 
;                mov     es, ax 
;                mov     cx, 80 
;                mov     al, ' ' 
;                repe    scasb 
;                je      @initfill 
;                mov     al, es:[di-1] 
;                mov     dx, offset msg_writable 
;                cmp     al, 'R'                         ; Readonly ? 
;                jne     @initfill 
;                mov     byte ptr [devstatus], DEV_READONLY 
;                mov     dx, offset msg_readonly 
;@initfill: 
;                CALLDOS DOS_PRNMSG 
                ; Fill header 
                GETRQADDR 
                mov     byte ptr DEV_RQ.init_num_unit, 1 
                mov     ax, offset @driver_end 
                add     ax, TMP_BUF1_SIZE+1 
                mov     word ptr DEV_RQ.init_free1_ofs, ax  ; Last byte resident 
                mov     ax, offset bpbary 
                mov     word ptr DEV_RQ.init_bpb_ofs, ax 
                mov     ax, cs 
                mov     word ptr DEV_RQ.init_free1_seg, ax 
                mov     word ptr DEV_RQ.init_bpb_seg, ax 
                mov     al, DEV_RQ.init_drv_num             ; Get drive num 
                add     al, 'A' 
                mov     [msg_disknum], al 
                mov     [disknum], al 
 
endif           ;!_COMFILE 
 
@initshowsize: 
                mov     ax, [flash_kb]      ; Show size 
                call    _dos_outnum 
                PRINTMSG    msg_size1 
                mov     ax, [freserved] 
                call    _dos_outnum 
                PRINTMSG    msg_size2 
 
ifndef          _COMFILE 
                PRINTMSG    msg_disk1       ; Show drive num 
endif           ;!_COMFILE 
 
                mov     ax, DER_NOERR 
                jmp     @doneinit 
@formatdsk: 
                call    _do_format          ; Format disk 
                cmp     al, APE_NOERR 
                je      @initgood 
                mov     ax, DER_NOTREADY 
                jmp     @doneinit 
@noflash: 
                PRINTERRMSG     em_noflash  ; No flash 
                mov     ax, DER_NOTREADY 
@doneinit: 
DEB_PUTC CHR_INIT2 
                ret 
_initialize     endp 
 
 
;---------------------------------------------------------------------------- 
; Converts the number in AX to ASCII and displays it. 
; IN: AX 
; OUT: 
; REG: AX,BX,CX,DX 
;---------------------------------------------------------------------------- 
_dos_outnum     proc    near 
                mov     bx, 10              ; Initialize BX with divisor 
                sub     cx, cx              ; Initialize digit counter 
@divide:        inc     cx                  ; Increment counter 
                sub     dx, dx              ; Zero high word of DX:AX 
                div     bx                  ; Divide AX by 10 
                push    dx                  ; Save remainder on stack 
                or      ax, ax              ; Loop if AX != 0 
                jnz     @divide 
@output: 
                pop     dx                  ; Retrieve digit from stack 
                add     dl, '0'             ; Convert to ASCII 
                CALLDOS DOS_PRNCHAR         ; Output it 
                loop    @output             ; Loop until done 
                ret 
_dos_outnum     endp 
 
 
ifdef           _COMFILE 
;============================================================================ 
 
usage           db      RL, 'Usage(case sensitive):', RL, RL 
                db      '  flashdsk F [blks] : format disk with reserved blks(128)', RL 
                db      '  flashdsk R        : make driver readonly', RL 
                db      '  flashdsk W        : make driver writable', RL 
                db      '  flashdsk S[1|2]   : scan[r|w] flash disk for error', RL, EOM 
 
 
msg_RL          db      EOM 
msg_askformat   db      'Really want to format flash disk ? [y/N] ' 
LEN_ASKFMT      equ     this byte - msg_askformat 
msg_getinfo     db      'Getting information of device', RL, EOM 
msg_bytepersec  db      ' bytes per sector', EOM 
msg_rootdir     db      ' dir entries in root', EOM 
msg_totalsec    db      ' total sectors', EOM 
msg_scan1       db      'Performing read test, pls wait...', EOM 
msg_scan2       db      'Performing write test, pls wait...', EOM 
msg_askscan2    db      'Write test may cause data lose, pls BACKUP !!!', RL 
                db      'Proceed write test now ? [y/N] ' 
LEN_ASKSCAN2    equ     this byte - msg_askscan2 
msg_scanok      db      RL, 'Flash memory test passed', EOM 
 
em_nodev        db      'Device not found', EOM 
em_abort        db      'User abort', EOM 
em_read         db      'Error reading flash memory at ' 
em_write        db      'Error writing flash memory at ' 
em_del          db      'Error deleting flash memory at ' 
 
keylist_yn      db      'YN' 
KEY_YN_LEN      equ     this byte - keylist_yn 
KEY_Y           equ     'Y' 
KEY_N           equ     'N' 
 
SCAN_0          equ     '0'             ; scan method 
SCAN_1          equ     '1' 
SCAN_2          equ     '2' 
scan_mode       db      SCAN_0 
 
ctrl_break      db      0               ; Ctrl-Break/Ctrl-C pressed flag 
custreserved    dw      0               ; Customer reserved block 
 
;---------------------------------------------------------------------------- 
; Simple int 23h handler 
;---------------------------------------------------------------------------- 
@new_23: 
                mov     byte ptr cs:[ctrl_break], 1 
                iret 
 
;---------------------------------------------------------------------------- 
; Output(use video call) string to current position 
; IN: 
;   ES:DX: string to be showed 
;   CX: length 
; OUT: 
; REG: NONE 
;---------------------------------------------------------------------------- 
_vd_outstr      proc    near 
                pusha 
                mov     ah, VD_GETPAGE 
                int     VIDEO               ; get the current page to BH 
                mov     bp, dx 
                mov     ah, VD_GETCURSOR    ; get cursor position 
                push    cx 
                int     VIDEO 
                pop     cx 
                mov     ah, VD_GETATTR      ; get text attr 
                int     VIDEO 
                mov     bl, ah 
                mov     ax, VD_PRNSTR*256+01b 
                                            ; bit1: use attr; bit0: mov cursor 
                int     VIDEO               ; AX/BX/DX/ES:BP 
                popa 
                ret 
_vd_outstr      endp 
 
 
;---------------------------------------------------------------------------- 
; Print error msg of AL 
; IN: 
;   AL: error code 
; OUT: 
; REG: NONE 
;---------------------------------------------------------------------------- 
_show_err       proc    near 
                cmp     al, APE_NOERR 
                jne     @se_0 
                ret 
@se_0: 
                push    ax 
                push    dx 
 
                cmp     al, APE_READ 
                jne     @se_1 
                mov     dx, offset em_read 
                jmp     @done_se1 
@se_1: 
                cmp     al, APE_WRITE 
                jne     @se_2 
                mov     dx, offset em_write 
                jmp     @done_se1 
@se_2: 
                cmp     al, APE_ERASE 
                jne     @se_3 
                mov     dx, offset em_del 
                jmp     @done_se1 
@se_3: 
                cmp     al, APE_NODEV 
                jne     @se_4 
                mov     dx, offset em_nodev 
                jmp     @done_se 
@se_4: 
                cmp     al, APE_FORMAT 
                jne     @se_5 
                mov     dx, offset em_format 
                jmp     @done_se 
@se_5: 
                cmp     al, APE_ABORT 
                jne     @se_6 
                mov     dx, offset em_abort 
                jmp     @done_se 
@se_6: 
                cmp     al, APE_NOFLASH 
                jne     @se_7 
                mov     dx, offset em_noflash 
                jmp     @done_se 
@se_7: 
                pop     dx 
                pop     ax 
                ret 
@done_se: 
                CALLDOS DOS_PRNMSG 
                pop     dx 
                pop     ax 
                ret 
@done_se1: 
                CALLDOS DOS_PRNMSG 
                mov     ax, [first_blk_no] 
                call    _dos_outnum 
                pop     dx 
                pop     ax 
                ret 
 
_show_err       endp 
 
 
;---------------------------------------------------------------------------- 
; Show a msg and accept a list of keys then return 
; IN: 
;   DX: msg to show 
;   CX: msg len 
;   SI: key list(upper case) 
;   BX: key list len 
;   AH: default key 
; OUT: 
;   AL: key 
; REG: 
;---------------------------------------------------------------------------- 
_ask            proc    near 
                call    _vd_outstr 
                mov     dl, ah 
@getakey: 
                mov     ah, KB_READKEY 
                int     KEYB 
                cmp     al, RETURN 
                je      @defkey 
                cmp     al, 'a'                 ; Upcase 
                jb      @chkkeylist 
                cmp     al, 'z' 
                ja      @chkkeylist 
                sub     al, 'a'-'A' 
@chkkeylist: 
                mov     cx, bx 
                mov     di, si 
                repne   scasb 
                jne     @getakey 
@done_ask: 
                push    ax 
                mov     ah, VD_GETPAGE 
                int     VIDEO                   ; get the current page 
                pop     ax 
                push    ax 
                mov     ah, VD_PRNCHAR          ; print the character 
                int     VIDEO 
                mov     ah, VD_PRNCHAR          ; print the character 
                mov     al, RETURN 
                int     VIDEO 
                mov     ah, VD_PRNCHAR          ; print RL 
                mov     al, LINEFEED 
                int     VIDEO 
                pop     ax 
                ret 
@defkey: 
                mov     al, dl 
                jmp     @done_ask 
_ask            endp 
 
 
;---------------------------------------------------------------------------- 
; Get number from ES:DI 
; IN: 
;   DS:SI: chars 
;   CX: count 
;   BX: default 
; OUT: 
;   AX: result 
; REG: AX, CX, DX 
;---------------------------------------------------------------------------- 
_getnum         proc    near 
                jcxz    @getnumdef              ; none ? 
 
                mov     dx, 0                   ; DX=N=0 
                mov     ah, 0 
@numloop: 
                lodsb 
                cmp     al, '0'                 ; Digit ? 
                jb      @getnumdef 
                cmp     al, '9' 
                ja      @getnumdef 
                push    ax                      ; Save current digit 
                mov     ax, 10                  ; N*10->N 
                mul     dx 
                pop     dx                      ; Restore current digit 
                sub     dx, '0' 
                add     dx, ax                  ; N+N1->N 
                loop    @numloop 
                mov     ax, dx 
                ret 
@getnumdef: 
                mov     ax, bx 
                ret 
_getnum         endp 
 
 
;---------------------------------------------------------------------------- 
; Get device ptr 
; IN: 
; OUT: 
;   AL=FALSE if no device 
;   ES:BX=device head location if AL=TRUE 
; REG: ALL 
;---------------------------------------------------------------------------- 
_getdevptr      proc    near 
                CALLDOS DOS_GETVER 
                cld 
                cmp     al, 2           ; 2.x + ? 
                jb      @no_dev 
                mov     dx, 22h         ; first device head addr ofs 
                cmp     al, 4           ; 4.x + ? 
                jae     @rfind_dev 
                cmp     al, 3           ; 3.x ? 
                mov     dx, 17h 
                jne     @rfind_dev 
                mov     dx, 22h 
                cmp     ah, 0           ; 3.0 ? 
                jne     @rfind_dev 
                mov     dx, 28h 
@rfind_dev: 
                CALLDOS DOS_GETLOL 
                add     bx, dx 
@rfind_next: 
                les     bx, es:[bx] 
                mov     ax, es 
                cmp     ax, -1          ; NULL ? 
                jne     @isdevice 
                cmp     bx, -1 
                je      @no_dev 
@isdevice: 
                mov     cx, APP_NAME_LEN        ; Check me 
                mov     di, bx 
                lea     si, appname 
                add     di, offset appname 
                sub     di, offset header 
                repe    cmpsb 
                jne     @rfind_next 
@isme: 
                mov     al, TRUE 
                ret 
@no_dev: 
                mov     al, FALSE 
                ret 
_getdevptr      endp 
 
 
;---------------------------------------------------------------------------- 
; Toggle driver readonly/writable 
; IN: 
;   AL=DEV_READONLY -> readonly 
;   otherwise writable 
; OUT: 
;   AL=APE_NOERR: success 
;   otherwise AL=error code 
; REG: ALL 
;---------------------------------------------------------------------------- 
_do_toggle_wr   proc    near 
                push    ax 
                call    _getdevptr 
                cmp     al, FALSE 
                pop     ax 
                je      @dtwr_err 
                lea     di, header 
                lea     si, devstatus 
                sub     si, di 
                add     bx, si 
                mov     byte ptr es:[bx], al        ; Set status 
                mov     dx, offset msg_readonly 
                cmp     al, DEV_READONLY 
                je      @dtwr_show_st 
                mov     dx, offset msg_writable 
@dtwr_show_st: 
                CALLDOS DOS_PRNMSG 
                mov     al, APE_NOERR 
                ret 
@dtwr_err: 
                PRINTMSG    em_nodev 
                mov     al, APE_NODEV 
                ret 
_do_toggle_wr   endp 
 
 
;---------------------------------------------------------------------------- 
; Get/show device information 
; IN: 
; OUT: 
;   AL=APE_NOERR: success 
;   otherwise AL=error code 
; REG: ALL 
;---------------------------------------------------------------------------- 
_getdeviceinfo  proc    near 
                call    _getdevptr 
                cmp     al, FALSE 
                je      @gdi_err 
                PRINTMSG    msg_getinfo 
                lea     di, header          ; ES:BX->device head 
                mov     si, bx 
                mov     ax, es 
                mov     ds, ax              ; Exchange ES & DS 
                mov     ax, cs 
                mov     es, ax 
                mov     cx, DEV_INFO_LEN 
                rep     movsb               ; Copy information data 
                mov     ds, ax              ; Restore DS 
                mov     al, [disknum] 
                mov     [msg_disknum], al 
                mov     ax, [flash_kb]      ; Show size 
                call    _dos_outnum 
                PRINTMSG    msg_size1 
                mov     ax, [freserved] 
                call    _dos_outnum 
                PRINTMSG    msg_size2 
                PRINTMSG    msg_disk1       ; Show drive num 
 
                mov     ax, [bytes_sector]  ; Print usable message 
                call    _dos_outnum 
                PRINTMSG    msg_bytepersec 
                mov     ax, [root_dirs] 
                call    _dos_outnum 
                PRINTMSG    msg_rootdir 
                mov     ax, [num_sec] 
                call    _dos_outnum 
                PRINTMSG    msg_totalsec 
 
                mov     dx, offset msg_readonly 
                cmp     byte ptr [devstatus], DEV_READONLY 
                je      @gdi_show_st 
                mov     dx, offset msg_writable 
@gdi_show_st: 
                CALLDOS DOS_PRNMSG 
                PRINTMSG    msg_RL 
                mov     al, APE_NOERR 
                ret 
@gdi_err: 
                PRINTMSG    em_nodev 
                mov     al, APE_NODEV 
                ret 
_getdeviceinfo  endp 
 
 
;---------------------------------------------------------------------------- 
; Perform scan action 
; IN: 
; OUT: 
;   AL=APE_NOERR: success 
;   otherwise AL=error code 
; REG: ALL 
;---------------------------------------------------------------------------- 
_do_scan        proc    near 
                cmp     byte ptr [scan_mode], SCAN_2    ; Scan mode 2 already ? 
                je      @doscan2 
 
                PRINTMSG    msg_scan1 
                call    _do_scan1                       ; Call scan 1 
                cmp     al, APE_NOERR 
                jne     @scan_err 
                cmp     byte ptr [scan_mode], SCAN_1    ; Only scan mode 1 ? 
                je      @done_scan 
                PRINTMSG    msg_RL 
                mov     dx, offset msg_askscan2 
                mov     cx, LEN_ASKSCAN2  
                mov     si, offset keylist_yn           ; Ask to confirm 
                mov     bx, KEY_YN_LEN 
                mov     ah, KEY_N 
                call    _ask 
                cmp     al, KEY_Y 
                jne     @done_scan 
@doscan2: 
                PRINTMSG    msg_scan2                   ; Call scan 2 
                call    _do_scan2 
                cmp     al, APE_NOERR 
                jne     @scan_err 
@done_scan: 
                PRINTMSG    msg_scanok 
                mov     al, APE_NOERR 
                ret 
@scan_err: 
;                call    _show_err       ; already show 
                ret 
_do_scan        endp 
 
 
;---------------------------------------------------------------------------- 
; Show error if no flash installed 
; IN: 
; OUT: 
;   AL=APE_NOFLASH: no flash 
;   AL=APE_NOERR: flash detected 
; REG: ALL 
;---------------------------------------------------------------------------- 
_flash_exist    proc    near 
                call    _getflashinfo       ; Get flash info 
                cmp     al, 0 
                je      @no_flash 
                mov     al, APE_NOERR 
                ret 
@no_flash: 
                PRINTERRMSG     em_noflash  ; No flash 
                mov     al, APE_NOFLASH 
                ret 
_flash_exist    endp 
 
 
;---------------------------------------------------------------------------- 
; .com file jumps here to start execution 
;---------------------------------------------------------------------------- 
@com_start: 
                DOSSETVEC   23h, @new_23 
 
                xor     cx, cx 
                mov     cl, cs:[80h]            ; Any params ? 
                cmp     cl, 0 
                je      @showusage 
 
                mov     di, 81h 
                mov     al, ' ' 
                repe    scasb                   ; Get switch 
                mov     al, es:[di-1] 
                cmp     al, 'F'                 ; Format switch 
                je      @do_format 
                cmp     al, 'S'                 ; Scan switch 
                je      @do_scan 
                cmp     al, 'R'                 ; Readonly 
                je      @do_toggle_wr 
                cmp     al, 'W'                 ; Writable 
                je      @do_toggle_wr 
                cmp     al, '?'                 ; Information 
                je      @showinfo 
                jmp     @showusage 
 
@do_format: 
 
                mov     al, ' ' 
                repe    scasb 
                je      @do_format1 
                dec     di 
                mov     si, di 
                inc     cx 
                mov     bx, DEF_RESV_BLK        ; Get reserved block count 
                call    _getnum 
                mov     [custreserved], ax 
@do_format1: 
                mov     ax, 0 
                call    _del16blk 
                call    _flash_exist 
                cmp     al, APE_NOFLASH 
                je      @com_exit 
 
                mov     ax, [custreserved]      ; Reserved blocks 
                mov     [freserved], ax 
 
                mov     dx, offset msg_askformat 
                mov     cx, LEN_ASKFMT 
                mov     si, offset keylist_yn   ; Ask to confirm 
                mov     bx, KEY_YN_LEN 
                mov     ah, KEY_N 
                call    _ask 
                cmp     al, KEY_Y 
                mov     al, APE_ABORT 
                jne     @com_exit 
                call    _do_format 
                jmp     @com_exit 
 
@do_scan: 
                cmp     cl, 0                   ; S1/S2 ? 
                je      @do_scan2 
                mov     al, [di] 
                cmp     al, SCAN_1 
                je      @do_scan1 
                cmp     al, SCAN_2 
                jne     @do_scan2 
@do_scan1: 
                mov     [scan_mode], al         ; Set scan mode 
@do_scan2: 
                call    _flash_exist 
                cmp     al, APE_NOFLASH 
                je      @com_exit 
                call    _getdeviceinfo          ; Show device status 
                call    _do_scan                ; Scan it 
                jmp     @com_exit 
 
@do_toggle_wr: 
                cmp     al, 'R' 
                mov     al, DEV_WRITE 
                jne     @do_toggle_wr1 
                mov     al, DEV_READONLY 
@do_toggle_wr1: 
                call    _do_toggle_wr           ; Toggle readonly switch 
                jmp     @com_exit 
 
@showinfo: 
                PRINTMSG    information 
                mov     al, 0 
                jmp     @com_exit 
 
@showusage: 
                PRINTMSG    appname             ; Show copyright 
                PRINTMSG    copyright 
                PRINTMSG    usage               ; Show usage 
                call    _flash_exist 
                cmp     al, APE_NOFLASH 
                je      @com_exit 
                call    _getdeviceinfo 
 
@com_exit: 
                CALLDOS     DOS_EXIT            ; Keep retrun code and exit 
 
else            ;_COMFILE 
 
 
;============================================================================ 
ifdef           _DEVTEST 
;---------------------------------------------------------------------------- 
; device test .com file jumps here to start execution 
;---------------------------------------------------------------------------- 
@test_start: 
                mov     ax, cs                  ; Send requst addr 
                mov     es, ax 
                lea     bx, myreq 
                call    _strategy 
 
                xor     cx, cx 
                mov     cl, cs:[80h]            ; Any params ? 
                cmp     cl, 0 
                je      @test 
 
                mov     di, 81h 
                mov     al, ' ' 
                repe    scasb 
                cmp     byte ptr es:[di-1], 'I' 
                jne     @test 
 
                call    _do_format 
@test: 
                mov     es:[bx].init_drv_num, 4 ; 'E' 
                mov     es:[bx].command, DR0_INIT 
                call    _interrupt              ; Test 0: initialization 
 
                mov     es:[bx].command, DR1_MEDIACHK 
                call    _interrupt              ; Test 1: media check 
 
                mov     es:[bx].command, DR2_BUILDBPB 
                call    _interrupt              ; Test 2: build bpb 
 
                mov     es:[bx].command, DR4_INPUT 
                lea     di, tmp_buf2            ; Simulate read sys area 
                mov     ax, cs 
                mov     es:[bx].io_trans_ofs, di 
                mov     es:[bx].io_trans_seg, ax 
                mov     es:[bx].io_sec_cnt, 1 
                mov     ax, 0 
                mov     bp, [sector_fat] 
                add     bp, [root_sec] 
                inc     bp 
 
@again: 
                call    _memsetbuf 
                mov     es:[bx].io_start_sec, ax 
                call    _interrupt 
                inc     ax 
                cmp     ax, bp 
                jb      @again 
 
                mov     al, 1 
                call    _memsetbuf 
                mov     es:[bx].command, DR4_INPUT  ; Simulate read root 
                mov     ax, [sector_fat] 
                inc     ax 
                mov     es:[bx].io_start_sec, ax 
                call    _interrupt 
 
                lea     di, tmp_buf2 
                inc     byte ptr es:[di+10]         ; Next name 
 
                mov     es:[bx].command, DR8_OUTPUT 
                mov     ax, [sector_fat] 
                inc     ax 
                mov     es:[bx].io_start_sec, ax 
                mov     es:[bx].io_sec_cnt, 1 
                call    _interrupt 
 
                mov     es:[bx].command, DR4_INPUT ; Simulate read root again 
                mov     ax, [sector_fat] 
                inc     ax 
                mov     es:[bx].io_start_sec, ax 
                mov     es:[bx].io_sec_cnt, 1 
                call    _interrupt 
 
                mov     ax, es:[bx].status 
                CALLDOS     DOS_EXIT            ; Keep retrun code and exit 
 
endif           ;_DEVTEST 
endif           ;_COMFILE 
 
 
 
; Note that tmp_buf2 only contain 1 byte(to save app size), 
; but here I use to hold 1 block data when initialize 
ifndef          _NEEDBUF 
tmp_buf2        db      0 
else            ;!_NEEDBUF 
tmp_buf2        db      TMP_BUF2_SIZE dup (?) 
endif           ;!_NEEDBUF 
 
ifdef           _COMFILE 
tmp_buf         db      0 
 
information     db      'Just a short note: ' 
                db      'This is a FLASH memory disk driver for DOS,', RL 
                db      'it is original written to support our PDA', RL 
                db      'Q: Who are we ?', RL 
                db      'A: Shenzhen G&S Co. Ltd., ', RL 
                db      'Q: Who wrote this ?', RL 
                db      'A: Edward Guo(edguo@163.net), May 1998' 
                db      EOM 
 
endif           ;_COMFILE 
 
@end_of_code    =       $ 
 
 
; Code end 
;============================================================================ 
code            ends 
                end start 
;****************************************************************************;