;-------------------------------------------------------------- ; File Name: lb186.asm ; Format: asm source ; Base Address: FC00h Range: FC000h - 100000h Loaded length: 4000h ; Assembler: borland tasm ; Copyright 2016 joseph c. lang NO RIGHTS RESERVED ;-------------------------------------------------------------- ; This disassembly doesn't produce an exact binary image of ; the Ampro ROM. TASM does argument size optimization that ; the original assembler (what ever that was) didn't do. ; For example "and bx,5". The original 5 was a word. With ; tasm it's a byte. [sx+0] becomes [sx] The instructions ; are shorter so address alignment was not preserved. ; nops have been added to preserve address alignment. ; (marked "tasm alignment") ; It will still serve as a usefull guide and documentation ; for the Ampro ROM and produce a functional ROM image. ;-------------------------------------------------------------- .model medium P186 ;-------------------------------------------------------------- biosdata segment byte public 'DATA' ;BIOS data segment offsets: (at 40:0) bd_0h dw ? ; serial port 0 address dw 3 dup (?) ; unused bd_8h dw ? ; lpt port 0 address dw 3 dup (?) ; unused ; bd_10h dw ? ; equipment list db ? ; unused bd_13h dw ? ; memory size bd_15h db ? ; busy wait flag db ? ; unused bd_17h db ? ; keyboard shift db 2 dup (?) ; unused bd_1Ah dw ? ; get pointer bd_1Ch dw ? ; put pointer bd_1Eh db 32 dup (?) ; keyboard queue 32 bytes ; bd_3Eh db ? ; drive recal status bd_3Fh db ? ; drive motor status db ? ; unused bd_41h db ? ; floppy BIOS status bd_42h db 6 dup (?) ; CDB 6 bytes (also data for sense) db 36 dup (?) ; unused ; bd_6Ch dw ? ; tick counter lsw bd_6Eh dw ? ; tick counter msw bd_70h dw ? ; 24 hour rollover ; db 2 dup (?) ; unused bd_74h db ? ; HD BIOS error status bd_75h db ? ; last HD error (SCSI bits) bd_76h db ? ; HD error count db 13 dup (?) ; unused bd_84h db 4 dup (?) ; floppy step rate table bd_88h db 4 dup (?) ; settle time table bd_8Ch db ? ; status in buffer bd_8Dh db ? ; message out buffer bd_8Eh db ? ; message in buffer ; db ? ; unused bd_90h db 4 dup (?) ; floppy current track table db 3 dup (?) ; unused bd_97h db ? ; last used FD ; ;drive parameter table bd_98h dw ? ; HD 0 heads bd_9Ah dw ? ; HD 0 sectors bd_9Ch dw ? ; HD 0 cylinders dw ? ; HD 1 heads dw ? ; HD 1 sectors dw ? ; HD 1 cylinders ; dw 2 dup (?) ; unused ; bd_0A8h dw ? ; pointer to drive parameter table bd_0AAh dw ? ; segment ; bd_0ACh dw ? ; pointer to HD select bits table bd_0AEh dw ? ; segment ; bd_0B0h dw 16 dup (?) ; serial irq table 16 words ; bd_0D0h dw ? ; pointer to IO table bd_0D2h dw ? ; seg ; ;IO table bd_0D4h db ? ; my SCSI ID bd_0D5h db ? ; target ID bd_0D6h dw ? ; unused? bd_0D8h dw ? ; CDB pointer (scsi command block) bd_0DAh dw ? ; seg bd_0DCh dw ? ; data buffer pointer bd_0DEh dw ? ; seg bd_0E0h dw ? ; status buffer pointer bd_0E2h dw ? ; seg bd_0E4h dw ? ; message in buffer ptr bd_0E6h dw ? ; seg bd_0E8h dw ? ; message out buffer ptr bd_0EAh dw ? ; seg biosdata ends ; ;-------------------------------------------------------------- seg000 segment byte public 'CODE' assume cs:seg000, ds:biosdata assume es:nothing, ss:nothing ; start: jmp bios_init ;-------------------------------------------------------------- db '* AMPRO Little Board/186' db 20h db 'MS-DOS/Concurrent PC DOS' db 20h db 'Rom-Bios *' db 0Dh db 0Ah db 'Version 3.35 -' db 20h db '28 January 1987' db 0 db 24h ; $ db ' 08 mhz Version ** ' db 20h db 'CCooppyyrriigghhtt ((CC))' db 20h db ' 11998855,,11998866 ' db 20h db 'AAMMPPRROO CCoommppuutteerrss IInncc..' db 20h db 20h db 'AAllll RRiigghhttss RReesseerrvveedd **' db 90h ;-------------------------------------------------------------- ; d_seg_40 dw 40h ;segment for abs 400h d_seg_0 dw 0 ;segment for abs 0h ; ;-------------------------------------------------------------- ;initialize hardware bios_init: cli ;stop interrupts ; ;set chip selects mov dx, 0FFA0h ;UMCS chip sel control mov ax, 0FC38h ;16k@FC000 (ROM) out dx, ax mov dx, 0FFA8h ;MPCS chip sel control mov ax, 0C0B9h out dx, ax mov dx, 0FFA4h ;PACS chip sel control mov ax, 139h out dx, ax ; ;start timer 2 ;timer 2 generates refresh request to DMA channel 1 mov dx, 0FF62h ;timer 2 max count mov ax, 20h out dx, ax mov dx, 0FF66h ;timer 2 control mov ax, 0C001h out dx, ax ; ;DMA 1 is refresh 'counter' ;it cycles through all of RAM mov dx, 0FFD8h ;DMA 1 transfer count mov ax, 0FFFFh ; out dx, ax ; sub dx, 2 ;destination hi 4 bits xor ax, ax ;DMA 1 address (zero) out dx, ax sub dx, 2 ;destination out dx, ax sub dx, 2 ;source hi 4 bits out dx, ax sub dx, 2 ;source out dx, ax ; ;disable DMA IRQ and start (refresh) DMA cycles mov dx, 0FF34h ;DMA 0 int control mov ax, 0Fh ;mask int out dx, ax mov dx, 0FFDAh ;DMA 1 control word mov ax, 7477h out dx, ax ; ;refresh is running so clear all RAM xor ax, ax mov bl, 10h ;block count mov es, ax ;clear segment zero_loop: xor ax, ax ;clear AX mov di, ax ;clear pointer mov cx, 8000h ;word count cld rep stosw ;clear words mov ax, es add ax, 1000h ;inc segment mov es, ax dec bl ;dec count jnz zero_loop ; ;determine the size of memory mov ds, cs:d_seg_0 ;start of RAM assume ds:nothing mov bx, ds ;zero BX mov ax, 0FFFFh ;init pattern ; ;put known pattern in ram at seg:400h ;increment seg by 1000h and loop set_RAM: inc ax ;inc pattern mov [bx], ax ;write pattern to RAM add bx, 400h ;inc offset jnb set_RAM mov dx, ds add dx, 1000h ;inc segment mov ds, dx jnb set_RAM ;loop till end of RAM ; ;check RAM at seg:400h for pattern ;increment seg by 1000h and loop mov ds, cs:d_seg_0 mov bx, ds mov ax, 0FFFFh check_RAM: inc ax ;inc pattern (and size) cmp [bx], ax ;pattern match? jnz got_end ;no match=end of RAM add bx, 400h ;next block jnb check_RAM ;end of segment mov dx, ds add dx, 1000h ;inc segment mov ds, dx jnb check_RAM ;jmp not end of RAM ; ;end of RAM found so validate and save RAM size got_end: mov bx, ax ;save size mov dx, 101Ah in al, dx ;input port test al, 40h jnz set_size cmp bx, 280h ;less than 640k jle set_size mov bx, 280h ;more than 640k set_size: mov ax, bx ;RAM size to AX mov ds, cs:d_seg_40 assume ds:biosdata mov ds:[bd_13h], ax ;save memory size ; ;initialize DUART mov dx, 1004h ;command A mov al, 0Ah ;disable TX RX out dx, al ; mov dx, 1000h ;mode A mov al, 13h ;no parity 8 bits out dx, al ; mov dx, 1000h ;mode A mov al, 7 ;1 stop out dx, al ; mov dx, 1002h ;clk sel A mov al, 0BBh ;9600 baud out dx, al ; mov dx, 1008h ;aux control mov al, 60h ;hi baud,xtal osc out dx, al ; mov dx, 1004h ;command A mov al, 15h ;en TX RX rst mode reg ptr out dx, al ; mov dx, 101Ah ;output port ctl mov al, 0 ;all outputs out dx, al ; ;the output port is inverted ;setting a bit drives the pin low mov dx, 101Ch ;set output bits (low) mov al, 83h ;set RTS !LPR init out dx, al ; mov dx, 101Eh ;clr output bits (HI) mov al, 4 out dx, al ; mov ds, cs:d_seg_40 ;ds=biosdata mov ax, 0 mov es, ax ;es=zero assume es:nothing mov al, 40h ; mov ds:[bd_17h], al ;fake keyboard shift status mov ax, offset bd_1Eh ;init queue pointers mov ds:[bd_1Ah], ax ;'get' pointer mov ds:[bd_1Ch], ax ;'put' pointer ; mov word ptr ds:[bd_8h], 1280h ;set LPT1 address mov word ptr ds:[bd_0h], 1016h ;set COM1 address mov word ptr ds:[bd_10h], 423Dh ;set equipment list ; ;test for 1770 FDC presence ;try to write/readback the sector register mov dx, 1104h ;FDC sector reg mov al, 0AAh ;test pattern out dx, al ;put in register mov bl, al ;save pattern ; mov cx, 1Eh ;delay count loop001: loop loop001 ;delay ; in al, dx ;read register cmp al, bl ;match test pattern? jz flop_pres ; ;FDC not present mov ax, 0FF3Eh and ds:[bd_10h], ax ;remove floppy from equip list jmp short set_vector ; nop ; ;floppy controller is present ;get drive count from jumpers flop_pres: mov dx, 1200h in al, dx ;read jumpers and al, 0C0h ;mask to drive bits xor ah, ah ;clear hi byte or ds:[bd_10h], ax ;set in equip list mov cx, 1414h ;settle loop count (14h) mov word ptr ds:[bd_88h], cx ;set settle time mov word ptr ds:[bd_88h+2], cx ;for 4 drives ; ;copy interrupt vectors to RAM set_vector: mov di, 0 ;destination (zero) push cs ;copy CS to DS pop ds assume ds:seg000 mov si, offset int_init ;vector init data mov cx, 80h ; ;word count rep movsw ;move vectors ; ;copy serial irq table to RAM mov es, cs:d_seg_40 ;ES=biosdata assume es:biosdata mov di, offset bd_0B0h ;dest offset mov si, offset com_irq_tab ;source offset mov cx, 10h ;count rep movsw ; ;initialize stack cli ;stop interrupts mov ax, 30h ;stack segment mov ss, ax assume ss:nothing mov sp, 100h ;set stack pointer ; ;enable serial interrupt mov dx, 0FF38h ;int 0 control (serial) mov ax, 11h ;set priority out dx, ax mov dx, 100Ah mov al, 2 out dx, al sti ;allow interrupts ; ;setup timer 0 ;18 MS BIOS tick timer mov dx, 0FF52h ;timer 0 max count mov ax, 0D6Ah out dx, ax mov dx, 0FF56h ;timer 0 mode control mov ax, 0E009h out dx, ax mov ax, 0 ; ;enable timer,floppy, printer interrupts mov dx, 0FF32h ;timer 0 ctl out dx, ax mov dx, 0FF3Ch ;int 2 ctl (floppy) mov ax, 3 ;set priority out dx, ax mov dx, 0FF3Eh ;int 3 ctl (printer) mov ax, 0Fh ;set priority out dx, ax mov dx, 0FF28h ;int mask mov ax, 0ACh ; out dx, ax ; ;call BIOS to setup AUX serial port 1 mov ah, 0 ;function mov al, 0A3h ;baud/par/stop mov dx, 1 ;port 1 int 14h ;init com port ; ;detect SCSI presence xor al, al mov dx, 1084h ;reset 5380 out dx, al ;clear mode ; mov cx, 20h ;loop count loop002: loop loop002 ;delay ; in al, dx ;read port or al, al ; jnz boot_DOS ;try floppy boot ; ;install hd driver ;move floppy irq to int_40 ;put HD vector at int_13 xor ax, ax mov ds, ax ;zero DS assume ds:nothing mov ax, ds:[4Ch] ;get floppy int mov ds:[100h], ax ;move to int 40h mov ax, ds:[4Eh] ;copy segment ptr mov ds:[102h], ax ; mov word ptr ds:[4Ch], offset hd_int13 mov ds:[4Eh], cs ; ;set pointer to (BIOS) HD param table mov word ptr ds:[104h], offset hd_param mov ds:[106h], cs ; ;get initiator SCSI ID (my ID) push ds mov ax, cs mov ds, ax ;copy CS to DS assume ds:seg000 mov es, cs:d_seg_40 ;bios data to ES assume es:biosdata mov si, offset SEL_bits ;point to select bits mov dx, 1200h in al, dx ;read jumpers and al, 7 ;mask to ID bits cmp al, 7 ;my id=7? jz id_eq_7 ;jmp if ID=7 mov cx, 8 ;offset to 2nd tabl add si, cx ; ;set pointer to drive select table id_eq_7: mov es:[bd_0ACh], si ;save offset mov es:[bd_0AEh], cs ;save segment ; ;copy default drive parameters to RAM mov si, offset DRV_params ;source offset mov di, offset bd_98h ;dest offset mov es:[bd_0A8h], di ;set pointer mov word ptr es:[bd_0AAh], 40h ;set segment mov cx, 0Ch ;count nop rep movsb ;copy table ; ;copy default SCSI IO table to RAM mov si, offset IO_table ;source offset mov di, offset bd_0D4h ;dest offset mov cx, 18h ;count nop rep movsb ;copy 24 bytes ; ;set my SCSI id in IO table mov cl, al ;convert id to mov al, 1 ;select bit shl al, cl ;shift CL times mov es:[bd_0D4h], al ;set my ID ; ;call a ROM expansion stub pop ds ;DS=biosdata assume ds:biosdata ;I cant get this far call to assemble so I'll fake it. ; call far 0FC00h:3FE0h ;call bios extension db 09ah,0E0h,03Fh,0h,0FCh ; ;-------------------------------------------------------------- ;boot DOS boot_DOS: int 19h ; DISK BOOT ; int 19 doesn't return ; ;can't get here int 18h ; TRANSFER TO ROM BASIC ; int 18 doesn't return nop ;-------------------------------------------------------------- ;DUART HW interrupt service int_0c: push ax push si push dx push ds cld mov ds, cs:d_seg_40 assume ds:biosdata mov dx, 100Ah in al, dx ;get irq status bits ; ;decode status bits ;AL=status bits ;SI=index to handler test al, 2 ;rx A full mov si, 4 jnz srv_comm test al, 20h ;rx B full mov si, 14h jnz srv_comm test al, 1 ;tx A rdy mov si, 0 jnz srv_comm test al, 4 ;break A mov si, 8 jnz srv_comm test al, 8 ;counter mov si, 0Ch jnz srv_comm test al, 10h ;tx B rdy mov si, 10h jnz srv_comm test al, 40h ;break B mov si, 18h jnz srv_comm test al, 80h ;port change mov si, 1Ch ; ;-------------------------------------------------------------- ;vector to comm port service routines srv_comm: call dword ptr [si+0B0h] mov dx, 0FF22h mov ax, 0Ch out dx, ax ;reset irq pop ds pop dx pop si pop ax iret ; ;-------------------------------------------------------------- ;nothing to do just return irq_ret: retf ; nop ; ;-------------------------------------------------------------- ;rx A receive full irq (console) rx_A_full: push di mov dx, 1006h in al, dx ;get rx data cmp al, 3 ;ctrl C? jz got_ctrlC mov di, ds:[bd_1Ch] ;put pointer mov si, di inc di cmp di, offset bd_3Eh ;end of buffer jb rx_A_ful2 mov di, offset bd_1Eh ;then wrap pointer rx_A_ful2: cmp di, ds:[bd_1Ah] ;eq get pointer? jz key_ovrn mov [si], al ;save in buffer mov ds:[bd_1Ch], di pop di retf ; nop ; ; handle ctrl C got_ctrlC: mov di, offset bd_1Eh ;reset get pointer mov ds:[bd_1Ah], di mov si, di mov [si], al ;put ^C at head of queue inc di mov ds:[bd_1Ch], di ;reset put pointer pop di retf ; ;key buffer overrun key_ovrn: mov dx, 1002h in al, dx ;get status test al, 4 ;tx empty? jz key_ovrn1 ;jmp if not mov dx, 1006h mov al, 7 ;bell out dx, al ;send it key_ovrn1: pop di retf ;-------------------------------------------------------------- ; dw 0 dw 40h ; boot_point dw 7C00h ;pointer to boot location dw 0 ; ;-------------------------------------------------------------- ; DISK BOOT ; boot disk system int_19: sti ;allow interrupts mov ds, cs:d_seg_40 assume ds:biosdata xor al, al ;clear 5380 mov dx, 1084h out dx, al mov dx, 1082h out dx, al mov dx, 1086h out dx, al mov dx, 1088h out dx, al mov dx, 108Eh in al, dx mov dx, 1084h xor al, al out dx, al ; mov cx, 20h loop003: loop loop003 ; in al, dx or al, al mov ds:[bd_74h], al ;set error status jnz int_19c mov cx, 0FFFFh int_19a: mov dx, 1080h in al, dx cmp al, 1 jnz int_19b loopz int_19a mov ds:[bd_74h], al ;set error status jmp dsk_reset ; int_19b: mov dx, 1200h in al, dx ;read jumpers and al, 7 cmp al, 7 jnz int_19c ;long wait mov al, 8 ;8 loops xor cx, cx ;of 65535 cycles loop004: loop loop004 dec al jnz loop004 ; mov dx, 1082h mov al, 80h ;assert SCSI reset out dx, al ; mov cx, 32h ;kill time 32h loops loop005: loop loop005 ; xor al, al ;negate reset out dx, al ; ;wait for SCSI to come out of reset int_19c: mov al, 8 xor cx, cx loop006: loop loop006 dec al jnz loop006 ; test byte ptr ds:[bd_74h], 0FFh ;any bits set jnz dsk_reset ; ; ;wait for HD drive ready mov cx, 78h ;loop count wait_rdy: push cx mov ah, 10h ;test for ready mov dx, 80h ;hd drive 0 int 13h ;call BIOS pop cx jnb read_MBR ;jmp if ready loop wait_rdy ;wait for ready jmp short dsk_reset ; nop ; ;drive is ready so read MBR read_MBR: mov ax, 201h ;read 1 sector mov dx, 80h ;drive 0 head 0 mov cx, 1 ;track 0 sector 1 les bx, dword ptr cs:boot_point assume es:nothing int 13h ;call BIOS read jb dsk_reset ; ;validate MBR sector (magic number) cmp word ptr es:[7DB4h], 0ABCDh jnz dsk_reset cmp word ptr es:[7DB6h], 4 jb dsk_reset ; ;copy hard drive params to table mov ax, es:[7D9Fh] mov ds:[bd_9Ah], ax ;set sectors mov al, es:[7DA3h] xor ah, ah mov ds:[bd_98h], ax ;set heads mov ax, es:[7DA1h] mov ds:[bd_9Ch], ax ;set cylinders ; dsk_reset: xor ax, ax ;AH=reset disk mov dx, ax ;drive zero int 13h ;call BIOS ; ;-------------------------------------------------------------- ;attempt boot from floppy ;clear buffer for boot sector les di, dword ptr cs:boot_point mov cx, 100h ;word count xor ax, ax ;data cld rep stosw ;zero out buffer ; mov ds, cs:d_seg_40 test word ptr ds:[bd_10h], 1 ;no floppy disks? jz no_floppy ; ;read sector 1 from floppy disk mov al, 5 ;retry count fboot_rtry: push ax mov ax, 201h ;AH=read AL=sector les bx, dword ptr cs:boot_point mov dx, 0 ;head 0 FD drive 0 mov cx, 1 ;track 0, 1 sector int 13h ;call BIOS pop ax jnb exec_boot ;jmp if good read dec al ;dec retry count jnz fboot_rtry ;loop for more tries no_floppy: test byte ptr ds:[bd_74h], 0FFh ;any errors set? jnz boot_err ; ;-------------------------------------------------------------- ;attempt boot from HD ;clear boot sector data area les di, dword ptr cs:boot_point mov cx, 100h xor ax, ax cld rep stosw ; ;read sector 1 from hd mov ax, 201h ;read sector 1 les bx, dword ptr cs:boot_point mov dx, 80h ;1st hd drive mov cx, 1 ;sector count int 13h ;call BIOS jnb exec_boot boot_err: jmp int_19 ;loop forever ; nop ; ;-------------------------------------------------------------- ;execute boot loader exec_boot: lds si, dword ptr cs:boot_point assume ds:nothing cmp word ptr [si+1FEh], 0AA55h ;valid boot? jnz boot_err ;this doesn't produce correct results.... ; jmp far ptr 0:7C00h ;execute boot db 0EAh,0h,7ch,0,0 ; ;-------------------------------------------------------------- ;table of baud rate constants baud_tbl db 11h ;110 db 33h ;150 db 44h ;300 db 55h ;600 db 66h ;1200 db 88h ;2400 db 99h ;4800 db 0BBh ;9600 ; ;-------------------------------------------------------------- com_table dw offset com_speed ;AH=0 set speed dw offset com_tx ;AH=1 tx character dw offset com_rx ;AH=2 rx character dw offset com_status ;AH=3 get status ; ;-------------------------------------------------------------- ; SERIAL I/O - USART PORT B ; AL = initializing parameters, DX = port number (0) ; Return: AH = RS-232 status bits, AL = modem status bits int_14: cmp ah, 0CFh ;special function? jnz int_14a cmp bx, 5555h ;valid? jnz int_14a cmp dx, 0AAAAh ;really really valid? jnz int_14a ; ;reset interrupt table (undocumented function) cli ;stop interrupts push ds push es push di push si call rst_vectors ;reset int table pop si pop di pop es pop ds mov dx, 0CFCFh retf 2 ; int_14a: push cx push dx push si push ds enter 2, 0 mov [bp-2], al ;save params or char cmp ah, 3 ja int_14b ;out of range? mov ds, cs:d_seg_40 ;bios data to DS assume ds:biosdata xchg si, dx mov dx, [si+0] nop ;*** tasm alignmnet nop ;*** or dx, dx jz int_14b xchg ah, al ;funct to AL xor ah, ah ;zero to AH shl ax, 1 ;make word offset xchg ax, si jmp cs:com_table[si] ;jump to function ; int_14b: mov ah, 1 int_14c: mov al, [bp-2] ;get RX char leave pop ds pop si pop dx pop cx iret ; nop ;-------------------------------------------------------------- ;AH=0 serial port set baud rate, parity, stop bits com_speed: push bx mov dx, 1014h ;command register mov al, 1Ah out dx, al ;disable TX RX mov dx, 1010h ;mode register mov al, [bp-2] ;get params and al, 3 ;mask to size test byte ptr [bp-2], 8 jnz com_spe01 ;jmp if parity enabled or al, 10h ;set no parity com_spe01: test byte ptr [bp-2], 10h jnz com_spe02 or al, 4 ;set parity odd com_spe02: out dx, al ;set register ; mov al, 7 ;one stop bit test byte ptr [bp-2], 4 jz com_spe03 or al, 8 ;two stop bit com_spe03: out dx, al ;set register mov bx, [bp-2] ;get params shr bl, 5 and bx, 7 ;mask to baud nop ;*** tasm alignment mov al, cs:[bx+baud_tbl] ;get from table mov dx, 1012h ;clock select out dx, al ;set baud mov dx, 1014h ;command reg mov al, 15h ;start TX RX out dx, al pop bx call get_Bstat ;get status jmp short int_14c ;return ;-------------------------------------------------------------- ;AH=1 serial port transmit com_tx: xor cx, cx ;time out count mov dx, 1012h ;B status register com_tx1: in al, dx test al, 8 ;tx buffer empty jnz com_tx2 loop com_tx1 ;loop till empty call get_Bstat ;get status or ah, 80h ;set timeout bit jmp short int_14c ;return ; com_tx2: mov dx, 1016h mov al, [bp-2] ;get char out dx, al ;send it call get_Bstat ;get status jmp short int_14c ;return ;-------------------------------------------------------------- ;AH=2 serial port receive com_rx: xor cx, cx mov dx, 1012h ;get B status reg com_rx1: in al, dx test al, 1 ;data ready? jnz com_rx2 loop com_rx1 ;loop till data avail ; ;input timeout call get_Bstat ;get B status or ah, 80h ;set timeout jmp int_14c ;return ; nop com_rx2: mov dx, 1016h ;B rx holding reg in al, dx mov [bp-2], al ;return value call get_Bstat ;get status jmp int_14c ;return ; nop ;-------------------------------------------------------------- ;AH=3 serial port status com_status: mov dx, 101Ah ;input port in al, dx test al, 2 jz com_stat1 mov al, 0 jmp short com_stat2 ; nop nop com_stat1: mov al, 30h ; com_stat2: mov [bp-2], al call get_Bstat jmp int_14c ; nop ; ;-------------------------------------------------------------- ;get SIO-b status get_Bstat proc near mov dx, 1012h ;B status register in al, dx shr al, 3 and al, 1Eh mov ah, al in al, dx test al, 1 jz get_Bsta2 or ah, 1 get_Bsta2: and al, 0Ch shl al, 3 or ah, al retn get_Bstat endp ; nop ; ;-------------------------------------------------------------- key_table dw offset key_read ;AH=0 read input dw offset key_stat ;AH=1 get status dw offset key_shift ;AH=2 query shift key ; ;-------------------------------------------------------------- ;keyboard services ; AH=function int_16: sti ;allow interrupts push ds push si mov ds, cs:d_seg_40 assume ds:biosdata cmp ah, 2 ;out of range? ja int_16a mov al, ah ;func to AL xor ah, ah ;zero to AH shl ax, 1 ;offset to word xchg ax, si jmp cs:key_table[si] ;execute routine ; nop ; int_16a: pop si pop ds iret ; nop ;-------------------------------------------------------------- ;AH=0 read serial console (wait for char) key_read: cli ;stop interrupts mov si, ds:[bd_1Ah] cmp si, ds:[bd_1Ch] sti ;allow interrupts jz key_read ;loop till char available mov al, [si] ;get char inc si ;advance pointer cmp si, offset bd_3Eh ;end of queue? jb key_read1 mov si, offset bd_1Eh ;reset to start of queue key_read1: mov ds:[bd_1Ah], si ;save get pointer pop si pop ds iret ;exit ; nop ;-------------------------------------------------------------- ;AH=1 get serial console status key_stat: cli ;stop interrupts mov si, ds:[bd_1Ah] cmp si, ds:[bd_1Ch] mov al, [si] ;get char sti ;allow interrupts ; ;discard pointer (leave char on queue) pop si pop ds retf 2 ; nop ;-------------------------------------------------------------- ;AH=2 get serial console shift status key_shift: mov al, ds:[bd_17h] ;get keyboard shift pop si pop ds iret ; ;-------------------------------------------------------------- ;floppy disk function table flp_table dw offset flp_reset ;AH=0 reset dw offset flp_status ;AH=1 get status dw offset flp_read ;AH=2 read sectors dw offset flp_write ;AH=3 write sectors dw offset flp_verify ;AH=4 verify sectors dw offset flp_format ;AH=5 format track ; ;-------------------------------------------------------------- ;floppy disk IO ;AH=function DL=drive DH=head CH=track CL=sector ;AL=sector count ES:BX=buffer ;moved to int 40h if hdisk installed int_13: cmp dl, 7Fh ;special function? jnz int_13a cmp ah, 1 ;special function? jz int_13c int_13a: enter 0, 0 ; push ds ;save registers push ax push bx push cx push dx push di push si push es sti ;allow interrupts mov ds, cs:d_seg_40 assume ds:biosdata cmp ah, 5 ;out of range? jbe int_13b ; mov byte ptr ds:[bd_41h], 1 ;set illegal function jmp short flp_status ;exit through status ; nop nop int_13b: xchg ah, al ;function to AL xor ah, ah ;zero to AH shl ax, 1 ;word offset xchg ax, si jmp cs:flp_table[si] ;exec function ; ;floppy done get status flop_done: mov al, [bp-4] and ax, 0FFh sub ax, di mov [bp-4], al ; ;-------------------------------------------------------------- ;floppy function=1 get status flp_status: pop es ;clean up stack pop si pop di pop dx pop cx pop bx pop ax mov ah, ds:[bd_41h] ;get floppy status cmp ah, 1 ;set flags cmc pop ds leave retf 2 ; ; nop ;-------------------------------------------------------------- ;special function: get floppy busy flag ;DL=7fh AH=1 int_13c: push ds ;save segment mov ds, cs:d_seg_40 ;get DS assume ds:biosdata mov al, ds:[bd_15h] ;get flag cbw ;convert to words pop ds ;recover DS iret ;exit ; ;-------------------------------------------------------------- ;floppy disk HW interrupt service int_0e: sti ;allow interrupts push ds ;save regs push dx push ax mov ds, cs:d_seg_40 ;point to data seg assume ds:biosdata or byte ptr ds:[bd_15h], 80h ;set done flag mov ax, 9101h ;signal op complete int 15h ;call busy wait mov dx, 0FF22h ;EOI register mov ax, 0Eh ;mask off interrupt out dx, ax pop ax ;clean up pop dx pop ds iret ;exit ;-------------------------------------------------------------- ;delay subroutine delay proc near push cx mov cx, 22h loop007: loop loop007 pop cx retn delay endp ; ;-------------------------------------------------------------- ;select floppy drive, spin up and test ready flop_select proc near push cx push bx ; ;convert head/drive in DX to side ; and select bits in AL and dx, 103h ;mask to head and drive mov cl, dl ;get drive mov al, 1 shl al, cl ;shift sel bit mov cl, 4 ;shift side 4 places shl dh, cl ; or al, dh ;combine sel and side ; ;select drive and side mov dx, 1200h ;drive sel latch out dx, al ;select drive+side and al, 0Fh ;strip side bit push ax test ds:[bd_3Eh], al ;test if recal done jnz old_drive ;track should be valid ; ;uncalibrated, drive needs seek zero or ds:[bd_3Eh], al ;mark drive calibrated mov bx, [bp-0Ah] ;get drive and bx, 3 ;mask to 4 drives nop ;*** tasm alignment mov byte ptr [bx+bd_90h], 0 ;set track to zero mov al, 0 ;recal command or al, [bx+bd_84h] ;or in step rate call flop_GO ;seek trk zero jz old_drive pop ax pop bx pop cx retn ; ;drive already calibrated, seek zero not needed old_drive: mov dx, 1100h in al, dx ;get 1770 status test al, 80h ;motor on? jnz old_drv2 ; ;motor not running so spin up drive mov byte ptr ds:[bd_3Fh], 0 ;clear motor status mov dx, 1100h mov al, 0D0h ;force interrupt out dx, al mov cx, 1 ;time out mov ah, 4 ;time out count old_drv1: mov dx, 101Ah ;read input port in al, dx test al, 8 ;drive ready bit jnz old_drv2 ;ready so dont loop loop old_drv1 ;loop till ready dec ah jnz old_drv1 ;or timeout ; ;motor is running old_drv2: pop ax test ds:[bd_3Fh], al ;same drive? jnz old_drv4 ;then exit mov ds:[bd_3Fh], al ;set motor status ; mov al, 32h ;outer loop count old_drv3: mov cx, 215h ;inner loop count loop008: loop loop008 ;delay loop dec al jnz old_drv3 ; nop old_drv4: pop bx ;clean up stack pop cx xor ax, ax retn ;exit flop_select endp ; nop ;-------------------------------------------------------------- ;get floppy controllers attention ;stop FDC operation then reset FDC force_int proc near push ax push dx mov dx, 1200h xor al, al out dx, al ;clear FDC state mov ds:[bd_3Fh], al ;clear motor status mov ds:[bd_3Eh], al ;clear recal done mov ds:[bd_97h], al ;clear last used FD mov dx, 1100h ;FDC command reg mov al, 0D0h ;force interrupt out dx, al ;stop FDC call delay mov dx, 101Ch ;set output bits mov al, 20h ; out dx, al ;FDC reset bit (low) call delay mov dx, 101Eh ;clr output bits out dx, al ;release FDC reset call delay pop dx pop ax retn force_int endp ; nop ; ;-------------------------------------------------------------- ;start floppy operation flop_GO proc near pusha cli ;stop interrupts mov cx, ax mov dx, 0FF3Ch ;enable FDC interrupt mov ax, 3 out dx, ax mov dx, 0FF22h mov ax, 0Eh out dx, ax ; mov ax, cx ; and byte ptr ds:[bd_15h], 7Fh ;turn off done flag mov byte ptr ds:[bd_41h], 0 ;clear status mov dx, 1100h ;start floppy out dx, al mov ax, 9001h ;signal wait loop int 15h sti ;allow interrupts test byte ptr ds:[bd_41h], 80h ;done? jnz flop_GO2 ; ;wait for done bit ;done bit is set by interrupt routine xor cx, cx ;time out mov bl, 4 ;long time out flop_GO1: test byte ptr ds:[bd_15h], 80h ;done? jnz flop_GO3 loop flop_GO1 dec bl jnz flop_GO1 ; ;timed out or error out flop_GO2: call force_int ;stop FDC or byte ptr ds:[bd_41h], 80h ;set drive not ready ; ;good exit flop_GO3: or byte ptr ds:[bd_41h], 0 ; mov dx, 0FF3Ch ;turn off IRQ mov ax, 0Bh out dx, ax popa retn flop_GO endp ; ;-------------------------------------------------------------- ;seek to track flop_seek proc near pusha mov bx, [bp-0Ah] ;get drive and bx, 3 ;mask to 4 drives nop ;*** tasm alignment cmp [bx+bd_90h], ch ;same track? jnz flp_seek1 ;do seek if not mov al, ds:[bd_3Fh] ;get motor status cmp ds:[bd_97h], al ;same as last used drive? jz flp_seek3 ;then skip the rest mov ds:[bd_97h], al ;save last used FD ; ;update track reg in FDC ;and seek to desired track flp_seek1: mov al, [bx+bd_90h] ;get old track mov dx, 1102h ;track out dx, al ;set controller track mov [bx+bd_90h], ch ;save target mov al, ch mov dx, 1106h ;set target track out dx, al mov al, 18h ;floppy seek command or al, [bx+bd_84h] ;or in step rate call flop_GO ;execute seek ; ;settle time delay loop mov al, [bx+bd_88h] ;get settle time flp_seek2: mov cx, 215h settlet: loop settlet ;settle time on track dec al ;loop till done jnz flp_seek2 flp_seek3: popa retn flop_seek endp ; ;-------------------------------------------------------------- flp_rd01: mov byte ptr [bp-4], 0 jmp flp_status ; nop ;-------------------------------------------------------------- ;floppy function=2 read sector(s) flp_read: mov byte ptr ds:[bd_41h], 0 ;clear status call flop_select ;select drive jnz flp_rd01 ;error? call flop_seek ;seek to track mov al, [bp-4] and ax, 0FFh ;mask to 256 words mov di, ax mov ds, cs:d_seg_0 assume ds:nothing lds si, dword ptr ds:[78h] ;get floppy table mov cl, [si+3] ;sector size or cl, cl mov dx, 80h ;128 words jz flp_rd2 shl dx, cl flp_rd2: mul dx ;times sector size mov ds, cs:d_seg_40 assume ds:biosdata ; ;set up DMA in push ax mov dx, 0FFCAh ;control word mov ax, 4 ;stop DMA out dx, ax mov dx, 0FFC8h ;transfer count pop ax out dx, ax ; ;convert seg:offset to linear 20bit address mov ax, es mov cx, 10h mul cx add ax, bx adc dl, 0 xchg bx, dx ; ;setup DMA controller mov dx, 0FFC4h ;dest pointer out dx, ax xchg ax, bx mov dx, 0FFC6h ;dest pointer hi 4 bits out dx, ax ; mov dx, 0FFC0h mov ax, 1106h ;source pointer out dx, ax add dx, 2 ;transfer count xor ax, ax out dx, ax ; mov dx, 0FFCAh ;control word mov ax, 0AEA6h out dx, ax ; ;-------------------------------------------------------------- ;read sector mov bl, [bp-8] ;get sector num flp_rd3: mov al, bl mov dx, 1104h ;set sector out dx, al mov al, 88h ;read sector command call flop_GO ;start floppy IO jnz flp_rd4 ;error? mov dx, 1100h ;status reg in al, dx test al, 1Ch ;mask error bits jnz rd_s_err inc bl ;next sector dec di ;dec sector count jnz flp_rd3 ;loop if not zero flp_rd4: jmp flop_done ;exit ; nop ; ;read sector error rd_s_err: call force_int ;stop FDC jmp f_err_com ; ;-------------------------------------------------------------- ;floppy function=0 reset floppy subsystem flp_reset: mov dx, 101Ch ;set output mov al, 20h ;set reset pin low out dx, al call delay mov dx, 101Eh ;clear output out dx, al ;release reset call delay xor ax, ax mov ds:[bd_3Eh], al ;clear recal done mov ds:[bd_41h], al ;clear status mov ds:[bd_97h], al ;clear last used FD cld mov di, offset bd_90h ;point to track table mov es, cs:d_seg_40 stosw ;zero the table stosw jmp flp_status ; nop ;-------------------------------------------------------------- flp_wr01: mov byte ptr [bp-4], 0 jmp flp_status ; nop ; ;-------------------------------------------------------------- ;floppy function=3 write sector(s) flp_write: mov byte ptr ds:[bd_41h], 0 ;clear status call flop_select ;select floppy drive jnz flp_wr01 call flop_seek ;seek track mov al, [bp-4] and ax, 0FFh mov di, ax mov ds, cs:d_seg_0 assume ds:nothing lds si, dword ptr ds:[78h] ;get floppy table mov cl, [si+3] or cl, cl mov dx, 80h ; jz flp_wr02 shl dx, cl ; ;set up DMA out flp_wr02: mul dx mov ds, cs:d_seg_40 assume ds:biosdata push ax mov dx, 0FFCAh ;control word mov ax, 4 out dx, ax mov dx, 0FFC8h ;transfer count pop ax out dx, ax ; ;convert seg:offset to 20 bit linear address mov ax, es mov cx, 10h mul cx add ax, bx adc dl, 0 xchg bx, dx ; mov dx, 0FFC0h ;source pointer out dx, ax xchg ax, bx mov dx, 0FFC2h ;source pointer hi 4 bits out dx, ax ; mov dx, 0FFC4h ;dest pointer mov ax, 1106h ;data reg out dx, ax add dx, 2 ;dest pointer hi 4 bits xor ax, ax out dx, ax ; mov dx, 0FFCAh ;control word mov ax, 76A6h out dx, ax ; ;-------------------------------------------------------------- ;write sector mov bl, [bp-8] flp_wr03: mov al, bl mov dx, 1104h ;set sector out dx, al mov al, 0A8h ;write call flop_GO ;execute command jnz flp_wr04 ;error? mov dx, 1100h ;get status in al, dx and al, 5Ch ;error bits jnz wr_s_err ;error? inc bl ;inc sector num dec di ;dec sector count jnz flp_wr03 ;loop for next sector flp_wr04: jmp flop_done ;done ; nop ; ;write sector error wr_s_err: call force_int ;stop FDC test al, 40h jz f_err_com or byte ptr ds:[bd_41h], 3 ;write protect error jmp flop_done ;done ; nop ; ;-------------------------------------------------------------- ;floppy read/write/format error all come here ;convert 1770 error bits to BIOS error bits f_err_com: test al, 4 jz f_err_1 or byte ptr ds:[bd_41h], 8 ;DMA overrun jmp flop_done ;done ; f_err_1: test al, 8 jz f_err_2 or byte ptr ds:[bd_41h], 10h ;CRC error jmp flop_done ;done ; f_err_2: mov ds, cs:d_seg_0 assume ds:nothing lds si, dword ptr ds:[78h] ;get floppy table mov ah, [si+4] mov ds, cs:d_seg_40 assume ds:biosdata cmp ah, bl jge f_err_3 or byte ptr ds:[bd_41h], 2 ;AM not found jmp flop_done ; nop f_err_3: or byte ptr ds:[bd_41h], 4 ;sector not found jmp flop_done ; ;-------------------------------------------------------------- flp_ver01: mov byte ptr [bp-4], 0 jmp flp_status ; nop ; ;-------------------------------------------------------------- ;floppy function=4 verify sector flp_verify: mov byte ptr ds:[bd_41h], 0 ;clear status call flop_select ;select drive jnz flp_ver01 call flop_seek ;seek track mov al, [bp-4] and ax, 0FFh mov di, ax mov ds, cs:d_seg_0 ;point to seg 0 lds si, dword ptr ds:[78h] ;get floppy table mov cl, [si+3] ;get sector size or cl, cl mov dx, 80h ;128 words jz flp_vfy01 shl dx, cl ; ;set up DMA flp_vfy01: mul dx mov ds, cs:d_seg_40 assume ds:biosdata push ax mov dx, 0FFCAh ;control word mov ax, 4 ;stop DMA out dx, ax ; mov dx, 0FFC8h ;transfer count pop ax out dx, ax ; ;clear destination address xor ax, ax mov dx, 0FFC6h ;dest pointer hi 4 bits out dx, ax mov dx, 0FFC4h ;dest pointer out dx, ax ; mov dx, 0FFC0h ;source pointer mov ax, 1106h out dx, ax add dx, 2 ;source pointer hi 4 bits xor ax, ax out dx, ax ; mov dx, 0FFCAh ;control word mov ax, 6E66h out dx, ax ; ;verify sector CRC mov bl, [bp-8] ;get sector flp_vfy02: mov al, bl mov dx, 1104h ;set sector in FDC out dx, al mov al, 88h ;read sector command call flop_GO ;start IO jnz flp_vfy03 mov dx, 1100h in al, dx ;get status test al, 1Ch ;error bits set? jnz v_s_err inc bl ;inc sector number dec di ;dec sector count jnz flp_vfy02 ;loop flp_vfy03: jmp flop_done ; ;verify sector error v_s_err: call force_int ;stop FDC jmp f_err_com ;common error exit ; ;-------------------------------------------------------------- ;data patterns for write track ;index gap trk_data db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; ; ;-------------------------------------------------------------- ;index address mark db 0 ;PLL sync db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0F6h ;write C2 db 0F6h ;write C2 db 0F6h ;write C2 db 0FCh ;write FC index address mark ; ;-------------------------------------------------------------- ;first sector gap db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; ; ;-------------------------------------------------------------- ;sector header data trk_data2 db 0 ;PLL sync db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0F5h ;write A1 preset CRC db 0F5h ;write A1 preset CRC db 0F5h ;write A1 preset CRC db 0FEh ;write FE id address mark ;sector ID inserted here (4 bytes) ;track,side,sector,size db 0F7h ;write CRC ; ;-------------------------------------------------------------- ;sector data gap db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; db 4Eh ; ; ;-------------------------------------------------------------- ;sector data block db 0 ;PLL sync db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0 ; db 0F5h ;write A1 preset CRC db 0F5h ;write A1 preset CRC db 0F5h ;write A1 preset CRC db 0FBh ;write FB data address mark ;data inserted here (512 bytes of E5) ;write CRC then loop for next sector ; ;-------------------------------------------------------------- nop ; ; ;-------------------------------------------------------------- flp_for1: mov byte ptr [bp-4], 0 jmp flp_status ; nop ; ;-------------------------------------------------------------- ;floppy function=5 format track flp_format: mov byte ptr ds:[bd_41h], 0 ;clear status call flop_select ;select drive jnz flp_for1 call flop_seek ;seek track mov dx, 1102h in al, dx cmp ch, al jz flp_fmt02 or byte ptr ds:[bd_41h], 40h ;seek error jmp flp_status ; nop flp_fmt01: mov dx, 1100h mov al, 0D0h ;stop operation out dx, al call delay ;wait to complete mov ds, cs:d_seg_40 assume ds:biosdata or byte ptr ds:[bd_41h], 3 ;write protect sti ;allow interrupts leave jmp flp_status ; flp_fmt02: mov al, [bp-4] enter 2, 1 ;reserve space mov [bp-2], al cld ;direction=up mov dx, 0FFCAh ;control word mov ax, 4 ;turn off DMA out dx, ax mov di, bx mov bx, 1106h ;fdc data reg mov ax, cs mov ds, ax assume ds:seg000 cli ;no interrupts ; ;start the FDC write mov al, 0F8h ;write track mov dx, 1100h ;FDC CSR out dx, al flp_fmt03: in al, dx test al, 1 jz flp_fmt03 ;loop till busy sets test al, 40h ;write protect? jnz flp_fmt01 ; ;write index address mark mov cx, 94h ;byte count mov si, offset trk_data FDRQ01: in al, dx test al, 2 ;DRQ? jz FDRQ01 xchg bx, dx outsb ;send byte xchg bx, dx loop FDRQ01 ;loop ; ;write sector header fmt_hdr: mov si, offset trk_data2 mov cx, 10h ;byte count FDRQ02: in al, dx test al, 2 ;DRQ? jz FDRQ02 ;wait for DRQ xchg bx, dx outsb ;send to FDC xchg bx, dx loop FDRQ02 ;loop ; ;write sector id bytes mov cx, 4 ;byte count xchg si, di FDRQ03: in al, dx test al, 2 ;DRQ? jz FDRQ03 xchg bx, dx outs dx, byte ptr es:[si] xchg bx, dx loop FDRQ03 ;loop ; ;write gap and data mark xchg si, di mov cx, 27h ;byte count FDRQ04: in al, dx test al, 2 ;DRQ? jz FDRQ04 xchg bx, dx outsb ;send to FDC xchg bx, dx loop FDRQ04 ;loop ; ;write sector data ;512 bytes of 0E5h mov cx, 200h ;byte count FDRQ05: in al, dx test al, 2 ;DRQ? jz FDRQ05 mov al, 0E5h ;data byte xchg bx, dx out dx, al ;send to FDC xchg bx, dx loop FDRQ05 ;loop ; ;write data CRC FDRQ06: in al, dx test al, 2 ;DRQ? jz FDRQ06 mov al, 0F7h ;write CRC xchg bx, dx out dx, al ;send to FDC xchg bx, dx dec byte ptr [bp-2] ;last sector? jz fmt_fill ;then fill rest of track ; ;write inter sector gap ;40 bytes of 4Eh mov cx, 28h ;byte count FDRQ07: in al, dx test al, 2 ;DRQ? jz FDRQ07 mov al, 4Eh ;gap character xchg bx, dx out dx, al ;send to FDC xchg bx, dx loop FDRQ07 ;loop ; jmp short fmt_hdr ;next sector ; nop ; ;write 04Eh for rest of track fmt_fill: in al, dx test al, 1 ;BSY? jz fmt_fil2 test al, 2 ;DRQ? jz fmt_fill mov al, 4Eh ;data byte xchg bx, dx out dx, al ;write it xchg bx, dx jmp short fmt_fill ;loop ; ;delay for tunnel erase fmt_fil2: mov cx, 14D5h ;loop count loop00a: loop loop00a ;delay in al, dx ;get ending status sti ;allow interrupts leave ;clean up stack mov ax, 40h ;set data segment mov ds, ax assume ds:biosdata jmp flp_status ; ;-------------------------------------------------------------- lpr_table dw offset lpr_outch ;AH=0 print char dw offset lpr_setup ;AH=1 setup port dw offset lpr_gstat ;AH=2 get status ; ;-------------------------------------------------------------- ;error bits conversion tables lpt_tbl1 db 88h ;!paper !sel !busy db 88h ;!paper !sel busy db 88h ;!paper sel !busy db 29h ;!paper sel busy db 10h ; paper !sel !busy db 10h ; paper !sel busy db 88h ; paper sel !busy db 1 ; paper sel busy ; lpt_tbl2 db 88h ;!paper !sel !busy db 88h ;!paper !sel busy db 88h ;!paper sel !busy db 28h ;!paper sel busy db 90h ; paper !sel !busy db 10h ; paper !sel busy db 88h ; paper sel !busy db 0 ; paper sel busy ; ;-------------------------------------------------------------- ;line printer request ;AH=function DX=printer (0) int_17: push ds push dx push si enter 4, 0 mov [bp-2], ax cmp ah, 2 ;out of range? ja int_17a ;set error and exit or dx, dx jnz int_17a ;lpt!=0? xchg ah, al ;func to AL xor ah, ah ;zero to AH shl ax, 1 ;word offset xchg ax, si jmp cs:lpr_table[si] ;exec function ; nop int_17a: mov byte ptr [bp-1], 1 int_17b: mov ax, [bp-2] leave pop si pop dx pop ds iret ; ;-------------------------------------------------------------- ;send char to lpr lpr_outch: push cx push bx xor cx, cx mov byte ptr [bp-4], 14h call lpr_stat01 lea bx, lpt_tbl1 xlat byte ptr cs:[bx] mov [bp-1], al cmp byte ptr [bp-1], 88h ; jz lpr_ch03 lpr_ch01: mov dx, 101Ah ;read input port in al, dx test al, 4 ;busy? jz lpr_ch02 loop lpr_ch01 ;loop till not busy dec byte ptr [bp-4] jnz lpr_ch01 or byte ptr [bp-1], 1 jmp short lpr_ch03 ; nop lpr_ch02: mov al, [bp-2] ;get character mov dx, 1280h out dx, al ;send to LPT port mov al, 4 mov dx, 101Ch ;set output bit out dx, al ;assert (low) strobe mov dx, 101Eh ;clear output bit out dx, al lpr_ch03: pop bx pop cx jmp short int_17b ; nop ;-------------------------------------------------------------- ;setup lpr port lpr_setup: push cx mov dx, 1280h mov al, 0 out dx, al mov cx, 3E8h ;loop count mov al, 80h ; mov dx, 101Ch ;set output bit out dx, al ;assert (low) init mov dx, 101Eh ;clear output bit out dx, al ;release init loop00b: loop loop00b mov dx, 101Ch out dx, al call lpr_stat01 mov byte ptr [bp-1], 0 cmp al, 1 jnz lpr_se01 mov byte ptr [bp-1], 88h ; lpr_se01: pop cx jmp short int_17b ; ;-------------------------------------------------------------- ;get lpr status lpr_gstat: call lpr_stat01 ;get status push bx lea bx, cs:lpt_tbl2 ;conversion table xlat byte ptr cs:[bx] ;convert to BIOS mov [bp-1], al pop bx jmp int_17b ; nop ; ;get printer status and pack bits in AL ;bit0=busy bit1=sel bit2=paper lpr_stat01 proc near mov dx, 101Ah ;read port in al, dx mov ah, al ;save copy and ah, 30h ;sel+paper shr ah, 3 and al, 4 ;busy shr al, 2 or al, ah ;combine xor ah, ah ;clear hi byte retn lpr_stat01 endp ; ;-------------------------------------------------------------- ;table of video routines video_tbl dw offset vid_exit ;AH=0 dw offset vid_exit ;AH=1 dw offset vid_exit ;AH=2 dw offset vid_exit ;AH=3 dw offset vid_exit ;AH=4 dw offset vid_exit ;AH=5 dw offset scroll_up ;AH=6 scroll up dw offset vid_exit ;AH=7 dw offset vid_exit ;AH=8 dw offset vid_exit ;AH=9 dw offset vid_exit ;AH=a dw offset vid_exit ;AH=b dw offset vid_exit ;AH=c dw offset vid_exit ;AH=d dw offset write_tty ;AH=e write tty dw offset get_vmode ;AH=f read video mode ; ;-------------------------------------------------------------- ; Video out (serial console out) ; AL = character, AH = function int_10: sti ;allow interrupts cmp ah, 0Fh ;out of range? ja vid_noop enter 0, 0 push ds ;save registers push ax push bx push cx push dx push es push si push di mov bl, ah ;function to BL xor bh, bh ;zero to BH shl bx, 1 ;make word offset jmp cs:video_tbl[bx] ;jmp to function ; nop vid_noop: iret ; nop ;-------------------------------------------------------------- ;function=0Eh write tty write_tty: mov dx, 1002h ;get serial status in al, dx test al, 4 ;tx empty? jz write_tty ;loop till empty mov dx, 1006h mov al, [bp-4] ;get character out dx, al ;send ;exit vid_exit: pop di ;restore registers pop si pop es pop dx pop cx pop bx pop ax pop ds leave iret ; nop ;-------------------------------------------------------------- ;function=0Fh get video mode get_vmode: mov byte ptr [bp-4], 7 ;mode to AL mov byte ptr [bp-3], 50h ;width to AH mov word ptr [bp-6], 0 ;active page to BX jmp short vid_exit ; nop ;-------------------------------------------------------------- ;function=06h scroll up scroll_up: cmp ah, 6 ;redundant? jnz vid_exit ; ;validate arguments cmp al, cl ;AL=0? (scroll) jnz vid_exit cmp ch, cl ;upper left (0,0) jnz vid_exit cmp dh, 18h ;line 24 jnz vid_exit cmp dl, 4Fh ;column 80 jnz vid_exit ; ;do CR and 24 LFs mov ah, 0Eh ;write char mov al, 0Dh ;CR return to col 1 int 10h ; mov cx, 18h ;line count scroll_loop: mov al, 0Ah ;LF move down 1 line mov ah, 0Eh ;write char int 10h ; loop scroll_loop ;loop jmp short vid_exit ; ;-------------------------------------------------------------- ;get memory size ;AX=number of 1k blocks int_12: push ds mov ds, cs:d_seg_40 assume ds:biosdata mov ax, ds:[bd_13h] ;get mem size pop ds iret ; nop ;-------------------------------------------------------------- ;get equipment list ;exit AX=equipment list int_11: push ds mov ds, cs:d_seg_40 assume ds:biosdata mov ax, ds:[bd_10h] ;get equip list pop ds iret ; nop ;-------------------------------------------------------------- ;system services/cassette ;hook for operating systems to intercept busy wait loop int_15: cmp ah, 90h ;device busy loop jz int_15a cmp ah, 91h ;device complete jz int_15c mov ah, 86h ;wait jmp short int_15b ; ;-------------------------------------------------------------- ;AH=90 int_15a: clc ;flag int_15b: sti ;allow intrrupts retf 2 ; nop ;-------------------------------------------------------------- ;AH=91 int_15c: iret ; nop ;-------------------------------------------------------------- ;time of day functions int_1a: sti ;allow interrupts push ds mov ds, cs:d_seg_40 assume ds:biosdata cmp ah, 0 jz int_1a1 ;read clock cmp ah, 1 jz int_1a2 ;set the clock ; sti ;allow interrupts pop ds iret ;-------------------------------------------------------------- ;read clock AH=0 int_1a1: cli ;stop interrupts mov dx, ds:[bd_6Ch] mov cx, ds:[bd_6Eh] mov al, byte ptr ds:[bd_70h] mov byte ptr ds:[bd_70h], 0 sti ;allow interrupts pop ds iret ;-------------------------------------------------------------- ;set clock AH=1 int_1a2: cli ;stop interrupts mov ds:[bd_6Ch], dx mov ds:[bd_6Eh], cx mov byte ptr ds:[bd_70h], 0 sti ;allow interrupts pop ds iret ; nop ;-------------------------------------------------------------- ;timer interrupt handler (HW) ;increment the tick counters and exit int_08: sti ;allow interrupts push ds push ax push dx mov ax, 40h ;set segment reg mov ds, ax assume ds:biosdata inc word ptr ds:[bd_6Ch] ;inc tick counter jz int_08b cmp word ptr ds:[bd_6Eh],24 ;hours rollover jz int_08c int_08a: int 1Ch ; CLOCK TICK mov dx, 0FF22h mov ax, 8 out dx, ax ;reenable tick timer pop dx pop ax pop ds iret ; nop int_08b: inc word ptr ds:[bd_6Eh] jmp short int_08a ; int_08c: cmp word ptr ds:[bd_6Ch], 18 jb int_08a mov byte ptr ds:[bd_70h], 1 mov word ptr ds:[bd_6Ch], 0 mov word ptr ds:[bd_6Eh], 0 jmp short int_08a ; ;-------------------------------------------------------------- ; TRANSFER TO ROM BASIC ; No rom-basic available so reboots int_18: jmp far ptr start ; nop ;-------------------------------------------------------------- ;dummy interrupt just returns int_dummy: sti ;allow interrupts iret ; ;-------------------------------------------------------------- ;pointer to io table IOT_ptr dw offset bd_0D4h dw 40h ; ;pointer to drive parameter tables (unreferenced) dw 0A8h dw 40h ;-------------------------------------------------------------- ;alternate HD functions (undocumented) AH=0C0h to 0CFh ;this appears to be SCSI device access that's ;not BIOS supported disks hd_alt_tbl dw offset SCSI_unkn ;AH=C0 dw offset SCSI_exec ;AH=C1 dw offset SCSI_read ;AH=C2 dw offset SCSI_write ;AH=C3 dw offset hd_rtn_bad ;AH=C4 dw offset hd_rtn_bad ;AH=C5 dw offset hd_rtn_bad ;AH=C6 dw offset hd_rtn_bad ;AH=C7 dw offset hd_rtn_bad ;AH=C8 dw offset hd_rtn_bad ;AH=C9 dw offset hd_rtn_bad ;AH=CA dw offset hd_rtn_bad ;AH=CB dw offset hd_rtn_bad ;AH=CC dw offset hd_rtn_bad ;AH=CD dw offset SCSI_get_PTR ;AH=CE dw offset SCSI_rst ;AH=CF ; ;-------------------------------------------------------------- ;table of hard disk functions AH=0 to 15h hd_table dw offset hd_reset ;AH-0 disk reset dw offset hd_status ;AH=1 get status dw offset hd_read ;AH=2 read sectors dw offset hd_write ;AH=3 write sectors dw offset hd_verify ;AH=4 verify sector dw offset hd_rtn_good ;AH=5 format track dw offset hd_rtn_bad ;AH=6 flag bad track dw offset hd_rtn_good ;AH=7 format drive at track dw offset hd_gparm ;AH=8 get drive param dw offset hd_rtn_good ;AH=9 set drive param dw offset hd_rtn_bad ;AH=a read with ecc dw offset hd_rtn_bad ;AH=b write with ecc dw offset hd_seek ;AH=c seek dw offset hd_rtn_good ;AH=d reset disk controller dw offset hd_rtn_bad ;AH=e read buffer dw offset hd_rtn_bad ;AH=f write buffer dw offset hd_tst_rdy ;AH=10 test ready dw offset hd_recal ;AH=11 recalibrate dw offset hd_rtn_good ;AH=12 ram diag dw offset hd_rtn_good ;AH=13 drive diag dw offset hd_rtn_good ;AH=14 controller diag dw offset hd_rtn_bad ;AH=15 read dasd? ; ;-------------------------------------------------------------- ;hard disk service (int_13) ; AH=function DL=drive CH=track CL=sector AL=sector count ; ES:BX=buffer hd_int13: sti ;allow interrrupts cmp ah, 0C0h ; jnb hd_int_1 cmp dl, 80h ; jb hd2flop ;floppy? hd_int_1: cmp dl, 0FFh jnz hd_int_2 cmp ah, 1 jnz hd_int_2 push dx call SCSI_poll pop dx iret ; nop ; hd_int_2: enter 0, 0 push ds ;save registers push ax push bx push cx push dx push es push si push di mov ds, cs:d_seg_40 assume ds:biosdata cmp ah, 14h ;out of range? ja hd_int_3 mov ds:[bd_0DCh], bx ;set buffer pointer mov ds:[bd_0DEh], es ;in IO table mov bl, ah ;function to BL xor bh, bh ;zero BH shl bx, 1 ;word offset jmp cs:hd_table[bx] ;execute function ; nop hd_int_3: cmp ah, 0C0h ;lowest function jb hd_rtn_bad cmp ah, 0D0h ;highest function jb hd_alt_1 ;do special function ;-------------------------------------------------------------- ;return bad status hd_rtn_bad: mov byte ptr [bp-4], 0 ;AL=0 mov byte ptr ds:[bd_74h], 1 ;invalid function jmp short hd_status ; nop ;-------------------------------------------------------------- ;return good status hd_rtn_good: mov byte ptr ds:[bd_74h], 0 ;no error jmp short hd_status ; nop ;-------------------------------------------------------------- ;goto floppy handler hd2flop: int 40h ; Floppy Handler (original INT 13h) retf 2 ; ; nop ;-------------------------------------------------------------- ;vector to alternate functions (undocumented) hd_alt_1: mov al, ah ;func to AL and ax, 0Fh ;mask hi bits shl ax, 1 ;make word offset mov si, ax jmp cs:hd_alt_tbl[si] ;execute ; ;-------------------------------------------------------------- ;set error and exit hd_set_err: mov byte ptr ds:[bd_74h], 80h ;timeout error jmp short hd_status ; ;-------------------------------------------------------------- ;read sense data and exit hd_ck_sense: call hd_sense jmp short hd_status ; nop ;-------------------------------------------------------------- ;check error and read sense or just exit hd_chk_err: or al, al jnz hd_set_err test byte ptr ds:[bd_8Ch], 2 jnz hd_ck_sense mov ds:[bd_74h], al ;set error flag ;-------------------------------------------------------------- ;return HD status (exit) hd_status: pop di ;recover registers pop si pop es pop dx pop cx pop bx pop ax mov ah, ds:[bd_74h] ;get error status cmp ah, 1 cmc pop ds leave ;clean up stack retf 2 ; nop ;-------------------------------------------------------------- ;reset disks skip HD just reset floppy hd_reset: mov byte ptr ds:[bd_74h], 0 ;no error xor ax, ax int 40h ; Floppy Handler jmp short hd_status ; nop ;-------------------------------------------------------------- ;issue SCSI read hd_read: mov cl, 8 ;read command call make_CDB ;fill in cmd block les bx, dword ptr cs:IOT_ptr call SCSI_go ;execute cmd jmp short hd_chk_err ; nop ;-------------------------------------------------------------- ;issue SCSI write hd_write: mov cl, 0Ah ;write command call make_CDB ;fill in cmd block les bx, dword ptr cs:IOT_ptr call SCSI_go ;execute cmd jmp short hd_chk_err ;check for errors ; nop ;-------------------------------------------------------------- ;hd verify (stub) hd_verify: mov byte ptr ds:[bd_74h], 0 ;no error jmp short hd_status ;exit ; nop ;-------------------------------------------------------------- ;get hd parameters hd_gparm: mov byte ptr ds:[bd_74h], 0 ;no error mov ax, [bp-0Ah] ;get drive DX and ax, 7Fh ;mask mov cx, 6 ;size of entry mul cx ;make offset les si, dword ptr ds:[bd_0A8h] ;point to table add si, ax ;offset to drive mov ax, es:[si] ;get heads dec al ;zero base mov [bp-9], al ;return in DH mov byte ptr [bp-0Ah], 1 ;DL=1 drive mov ax, es:[si+4] ;get cyl xchg ah, al shl al, 6 ;make room for sectors or ax, es:[si+2] ;or in sectors mov [bp-8], ax ;return in CX jmp short hd_status ; ;-------------------------------------------------------------- ;issue SCSI seek hd_seek: mov cl, 0Bh ;seek command call make_CDB ;fill in cmd block les bx, dword ptr cs:IOT_ptr call SCSI_go ;exec command jmp hd_chk_err ;check for error ; ;-------------------------------------------------------------- ;issue SCSI test ready hd_tst_rdy: xor ax, ax mov dx, ax mov [bp-4], al ;clear status mov cl, 0 ;test drive ready cmd call make_CDB1 ;make cmd block les bx, dword ptr cs:IOT_ptr call SCSI_go ;execute cmd jmp hd_chk_err ; nop ;-------------------------------------------------------------- ;issue SCSI recalibrate hd_recal: xor ax, ax mov dx, ax mov [bp-4], al ;clear status mov cl, 1 ;recal command call make_CDB1 ;fill in cmd block les bx, dword ptr cs:IOT_ptr call SCSI_go ;do scsi IO jmp hd_chk_err ;check for error ; nop ; ;-------------------------------------------------------------- ;alternate: ;I cant figure out what this is for with out some documentation ;it sets some bits/regs and returns the initiator ID in DL SCSI_unkn: mov byte ptr ds:[bd_74h], 0FFh ;sense failed mov byte ptr [bp-4], 0FFh ;AL=0FFh mov byte ptr [bp-8], 1 ;CL=1 mov byte ptr [bp-7], 2 ;CH=2 les bx, dword ptr cs:IOT_ptr mov al, es:[bx] ;get initiator ID mov [bp-0Ah], al ;DL=ID jmp hd_status ;return ; nop ; ;-------------------------------------------------------------- ;alternate: SCSI read primitive SCSI_read: mov ds:[bd_0DCh], bx ;set data pointer mov ds:[bd_0DEh], es mov cl, 8 ;SCSI read command call SCSI_mkCDB ;fill in cmd block les bx, dword ptr cs:IOT_ptr call SCSI_go ;do SCSI IO jmp hd_chk_err ; ;-------------------------------------------------------------- ;alternate: SCSI write primitive SCSI_write: mov ds:[bd_0DCh], bx ;set data pointer mov ds:[bd_0DEh], es mov cl, 0Ah ;SCSI write command call SCSI_mkCDB ;fill in cmd block les bx, dword ptr cs:IOT_ptr call SCSI_go ;do SCSI IO jmp hd_chk_err ; ;-------------------------------------------------------------- ;alternate: get pointers to drive parameter table and Select bits SCSI_get_PTR: lea di, ds:[bd_0A8h] ;get DRV tbl pointer mov [bp-10h], di ;DI lea si, ds:[bd_0ACh] ;get pointer to SEL_bits mov [bp-0Eh], si ;SI mov [bp-2], ds ;DS mov byte ptr ds:[bd_74h], 0 ;no error jmp hd_status ; nop ; ;-------------------------------------------------------------- ;alternate: reset SCSI bus SCSI_rst: mov dx, 1082h ;initiator cmd mov al, 80h ;assert reset out dx, al ; mov cx, 32h ;short delay loop00c: loop loop00c ; xor al, al ;clear reset out dx, al mov ds:[bd_74h], al ;no error jmp hd_status ; ;-------------------------------------------------------------- ;alternate: exec user provided SCSI IO table ;ES:BX points to IO Table SCSI_exec: call SCSI_go ;do SCSI IO mov ds:[bd_74h], al ;set error mov byte ptr [bp-4], 0 ;AL=0 jmp hd_status ; nop ; ;-------------------------------------------------------------- ;alternate: make user SCSI CDB ; SI=cylinder (65535 max) SCSI_mkCDB proc near push cx ;save command mov ax, [bp-0Ah] ;get drive DX and ax, 7Fh ;strip bit 7 add ax, ax ;times 2 mov cx, ax ; add ax, ax ;times 4 add ax, cx ;plus 2= times 6 les si, dword ptr ds:[bd_0A8h] ;drive table base add si, ax ;make ptr to drive param ; ;convert CHS to LBN mov ax, [bp-0Eh] ;get cylinder SI mul word ptr es:[si] ;mult x heads mov cl, [bp-9] ;get head DH xor ch, ch ;zero hi byte add ax, cx adc dx, 0 mov cx, dx mul word ptr es:[si+2] ;mult x sectors mov bx, ax mov ax, cx mov cx, dx mul word ptr es:[si+2] ;mult x sectors add ax, cx mov dx, ax mov ax, [bp-8] ;get sector CX and ax, 3Fh ;mask to 63 dec al ;zero base add ax, bx ;add to LBA adc dx, 0 ;carry to LBA+2 pop cx ;recover command jmp short make_CDB1 SCSI_mkCDB endp ; nop ; ;-------------------------------------------------------------- ;make SCSI command descriptor block make_CDB: mov ax, [bp-0Ah] ;DX and ax, 7Fh ;drive num add ax, ax ;times 2 mov bx, ax ; add ax, ax ;times 4 add ax, bx ;plus2= times 6 les si, dword ptr ds:[bd_0A8h] ;drive table base add si, ax ;point to drv param ; ;convert CHS to LBN mov ax, [bp-8] ;get cyl§or mov ch, al ;save sector xchg ah, al shr ah, 6 ;shift cylhi down mul word ptr es:[si] ;mult x heads mov bl, [bp-9] ;get head xor bh, bh ;zero add ax, bx mul word ptr es:[si+2] ;mult x sectors mov bl, ch ;sector in BL and bx, 3Fh ;mask value nop ;*** tasm alignment dec bl ;zero base add ax, bx ;sector to LBN adc dl, bh ;carry? ; ;-------------------------------------------------------------- ;fill in CDB command/ID's/LUN make_CDB1 proc near mov bx, offset bd_42h ;point to CDB xchg ah, al mov [bx+2], ax ;set LBN (lo&mid) mov al, [bp-4] ;sector count AL xor ah, ah ;clear option bits mov [bx+4], ax ;set count&option les si, dword ptr ds:[bd_0ACh] ;point to SEL_bits mov ax, [bp-0Ah] ;get drive DX and ax, 7Fh ;mask bits shl ax, 1 ;make word offset add si, ax mov al, es:[si] ;get target ID mov ds:[bd_0D5h], al ;set target ID mov ah, es:[si+1] ;get target LUN and dl, 1Fh ;mask to LBN or ah, dl ;set LUN+LBA mov al, cl ;get command mov [bx], ax ;set cmd,lun,lba retn make_CDB1 endp ; ;-------------------------------------------------------------- ;error translate table ;convert sense data status to BIOS error tran_err db 0 ;no error db 4 ;sector not found db 40h ;seek failed db 0CCh ;write fault db 0AAh ;drive not ready db 1 ;invalid function db 40h ;seek failed db 20h ;general error db 80h ;timeout db 40h ;seek failed db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0Ah ;bad sector flag db 10h ;uncorrectable db 2 ;address mark not found db 2 ;address mark not found db 4 ;sector not found db 40h ;seek failed db 0 ;no error db 0 ;no error db 11h ;ecc corrected db 10h ;uncorrectable db 1 ;invalid function db 10h ;uncorrectable db 7 ;set parameter failed db 0Ah ;bad sector flag db 0 ;no error db 0BBh ;undefined error db 1 ;invalid function db 4 ;sector not found db 1 ;invalid function db 0BBh ;undefined error db 1 ;invalid function db 0AAh ;drive not ready db 1 ;invalid function db 3 ;invalid func&AM not found db 6 ;media changed db 0 ;no error db 20h ;general error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 4 ;sector not found db 4 ;sector not found db 4 ;sector not found db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 0BBh ;undefined error db 20h ;general error db 20h ;general error db 20h ;general error db 1 ;invalid function db 20h ;general error db 80h ;timeout db 20h ;general error db 20h ;general error db 20h ;general error db 1 ;invalid function ;-------------------------------------------------------------- ;read sense and set error code hd_sense proc near inc word ptr ds:[bd_76h] ;inc error counter jnz hd_sens1 dec word ptr ds:[bd_76h] ;dec if already max hd_sens1: les bx, dword ptr ds:[bd_0D8h] ;get buffer address mov ds:[bd_0DCh], bx ;set buffer pointer mov ds:[bd_0DEh], es xor ax, ax mov dx, ax mov [bp-4], al mov cl, 3 ;request sense call make_CDB1 ;make command block les bx, dword ptr cs:IOT_ptr call SCSI_go ;execute command or al, al ;error? jnz hd_sens3 mov al, ds:[bd_42h] ;get sense data mov ds:[bd_75h], al ;save error and al, 7Fh ;strip address valid cmp al, 49h ;in range? jbe hd_sens2 mov byte ptr ds:[bd_74h], 20h ;generic error code retn ; hd_sens2: lea bx, tran_err ;point to translate xlat byte ptr cs:[bx] ;convert to bios error hd_sens3: mov ds:[bd_74h], al ;save error code retn hd_sense endp ; ;-------------------------------------------------------------- ;initiate SCSI I/O SCSI_go proc near mov ds:[bd_0D2h], es ;save IOT pointer mov ds:[bd_0D0h], bx mov dx, 1200h mov al, ds:[bd_3Fh] ;get motor status or al, 40h ;set DRQ source (SCSI) out dx, al xor ax, ax ;stop 5380 mov dx, 1082h ;initiator command out dx, al mov dx, 1086h ;target command out dx, al ; ;SCSI arbitration SCSI_arb: xor ax, ax mov dx, 1084h ;mode register out dx, al ;clear mode mov dx, 108Eh ;reset irq in al, dx mov al, es:[bx] ;get my ID mov ah, al ;save it mov dx, 1080h ;output data out dx, al mov dx, 1084h ;get mode in al, dx or al, 1 ;drive bus out dx, al xor cx, cx wait4arb: mov dx, 1082h ;get initiator cmd in al, dx test al, 40h ;arb in progress jnz arb_in_prog mov dx, 108Ah ;get bus&status in al, dx test al, 10h ;irq active? jnz SCSI_arb ;then retry loop wait4arb ;loop till arb or irq ; mov ah, 0FEh ; jmp SCSI_err ; ;arb in progress arb_in_prog: mov cx, 2 ;short delay loop00d: loop loop00d ; mov dx, 1080h ;get current scsi data in al, dx sub al, ah sub al, ah jns SCSI_arb ;retry mov dx, 1082h ;get initiator cmd in al, dx test al, 20h ;lost arb? jnz SCSI_arb ;loop try again ; ;arbitration won mov al, 8 out dx, al ;assert bsy mov dx, 1084h ;get mode reg in al, dx and al, 0FEh out dx, al ;end arbitration ; mov dx, 1082h ;get initiator cmd in al, dx or al, 4 ;assert sel out dx, al mov dx, 1080h ; mov al, ah ;get my ID or al, es:[bx+1] ;set target ID out dx, al mov dx, 1082h ;get initiator cmd in al, dx or al, 1 ;assert data (ID's) out dx, al ; mov cx, 2 ;short delay loop00e: loop loop00e ; mov dx, 1082h ;initiator cmd mov al, 5 out dx, al ;assert sel+data ; mov cx, 9368h ;time out value loop00f: mov dx, 1088h ;SCSI bus status in al, dx test al, 40h ;BSY set? jnz targ_sel loop loop00f ;loop till timeout or BSY ; mov ah, 0FFh jmp SCSI_err ; ;BSY is set target is selected targ_sel: mov dx, 1082h ;get initiator cmd mov al, 1 out dx, al ;negate sel mov al, 0 out dx, al ;negate data mov dx, 1084h ;set mode register mov al, 6 out dx, al ;monitor BSY+DMA ; ;wait for request to set wait_req: mov dx, 108Ah ;bus & status in al, dx test al, 10h ;irq active? jnz irq_bit_set mov dx, 1088h ;get SCSI bus status in al, dx test al, 20h ;req? jnz req_activ jmp short wait_req ;loop till req ; db 90h ; ;-------------------------------------------------------------- ;SCSI bus phase table ;offsets of SCSI phase handlers SCSI_phase dw offset data_out ;!io !cd !msg data out dw offset data_in ; io !cs !msg data in dw offset cmd_out ;!io cd !msg cmd out dw offset status_in ; io cd !msg status in dw offset unspec ;!io !cd msg unspec dw offset unspec ; io !cd msg unspec dw offset mess_out ;!io cd msg message out dw offset mess_in ; io cd msg message in ; ;-------------------------------------------------------------- ;5380 irq is active ;stop current IO ;decode status irq_bit_set: xor al, al mov dx, 1082h ;initiator cmd out dx, al ;stop command mov dx, 108Ah ;bus&status reg in al, dx and ax, 0Ch ;phase match or busy loss jnz SCSI_err ;jmp if phase or busy ; ;req is active and phase mismatch ;decode new phase ;set 5380 phase and jump to handler req_activ: xor al, al mov dx, 1084h ;clear mode reg out dx, al mov dx, 108Eh ;reset parity/irq in al, dx mov al, 6 ;mon BSY+DMA mov dx, 1084h ;set mode reg out dx, al mov dx, 1088h ;get SCSI bus status in al, dx and ax, 1Ch ;mask to phase shr ax, 1 mov si, ax ;offset to phase handler shr ax, 1 mov dx, 1086h ;target command reg out dx, al ;set phase bits les bx, dword ptr ds:bd_0D0h ;get IOT pointer jmp cs:SCSI_phase[si] ;execute new phase ; nop ;-------------------------------------------------------------- ;data out phase data_out: les bx, es:[bx+8] ;get buffer pointer jmp short DMA_send ;start DMA ; nop nop ;-------------------------------------------------------------- ;data in phase data_in: les bx, es:[bx+8] ;get buffer pointer jmp DMA_receive ;start DMA ; nop ;-------------------------------------------------------------- ;command out phase cmd_out: les bx, es:[bx+4] ;get CDB pointer jmp short DMA_send ;start DMA ; nop nop ;-------------------------------------------------------------- ;status in phase status_in: les bx, es:[bx+0Ch] ;get status pointer jmp DMA_receive ;start DMA ; nop ;-------------------------------------------------------------- ;message out phase mess_out: les bx, es:[bx+14h] ;get message out pointer jmp short DMA_send ;start DMA ; nop nop ;-------------------------------------------------------------- ;message in phase mess_in: les bx, es:[bx+10h] ;get message in pointer jmp DMA_receive ;start DMA ; nop ;-------------------------------------------------------------- ;undefined phase (error) unspec: mov ah, 0FDh ;error code ; ;(irq active and phase match) or busy loss ;is an error so get off the bus SCSI_err: xor al, al mov dx, 1084h ;mode reg out dx, al mov dx, 1082h ;initiator cmd out dx, al mov dx, 108Eh ;reset parity/irq in al, dx mov dx, 1200h ;reset SCSI drq mov al, ds:3Fh ;get motor status and al, 0BFh out dx, al mov al, ah retn SCSI_go endp ; ;-------------------------------------------------------------- ;set up and start DMA send DMA_send: mov dx, 0FFC8h ;set transfer count mov ax, 0FFFFh out dx, ax ; ;convert segment:offset ;to linear 20 bit address mov ax, es ;get segment mov cx, 10h mul cx ;times 16 add ax, bx ;add offset adc dl, ch ;add carry mov bx, dx ; ;setup DMA controller mov dx, 0FFC0h ;source pointer out dx, ax mov ax, bx ;addr 0-15 mov dx, 0FFC2h ;src pointer hi 4 bits out dx, ax ;addr 16-19 mov dx, 0FFC4h ;dest pointer mov ax, 1180h out dx, ax ;SCSI data reg add dx, 2 xor ax, ax ;addr 16-19=0 out dx, ax mov dx, 0FFCAh ;DMA ctrl word (go) mov ax, 74A6h out dx, ax ; ;start SCSI send mov dx, 1082h ;initiator cmd mov al, 1 ;assert data out dx, al mov dx, 108Ah ;start DMA send out dx, al call SCSI_wait ;wait for SCSI done mov dx, 0FFCAh ;control word mov ax, 4 ;turn off dma out dx, ax jmp irq_bit_set ;next phase ; nop ; ;-------------------------------------------------------------- ;wait for SCSI done SCSI_wait proc near call SCSI_poll ;poll 5380 jnz wait_exit mov ax, 9000h ;signal wait loop int 15h wait_done: call SCSI_poll ;poll 5380 jz wait_done wait_exit: retn SCSI_wait endp ; ;-------------------------------------------------------------- ;check 5380 irq status SCSI_poll proc near mov dx, 108Ah ;bus & status reg in al, dx and al, 10h ;irq active? mov ah, al jz poll_exit or ah, 0FFh poll_exit: retn SCSI_poll endp ; ;-------------------------------------------------------------- ;SCSI set up and start DMA in DMA_receive: mov dx, 0FFC8h mov ax, 0FFFFh ;byte count out dx, ax ; ;convert segment:offset ;to linear 20 bit address mov ax, es ;get segment mov cx, 10h ;times 16 mul cx add ax, bx ;add to offset adc dl, ch ;add carry mov bx, dx ; ;setup DMA controller mov dx, 0FFC4h ;destination out dx, ax ;set addr 0-15 mov ax, bx mov dx, 0FFC6h out dx, ax ;set addr 16-19 mov dx, 0FFC0h mov ax, 118Ch ;source port (SCSI data) out dx, ax add dx, 2 xor ax, ax ;addr 16-19=0 out dx, ax mov dx, 0FFCAh mov ax, 0AC66h ;DMA control (go) out dx, ax ; ;start SCSI DMA in mov dx, 108Eh out dx, al ;start DMA call SCSI_wait ;wait for done ; mov dx, 0FFCAh mov ax, 4 ;turn off DMA out dx, ax jmp irq_bit_set ;next phase ; nop ; ;-------------------------------------------------------------- ;hard disk param table hd_param dw 400h ;max cyl db 0Fh ;max heads dw 0 ;RWC cyl dw 0FFFFh ;precomp cyl db 0Bh ;ecc length db 0 ;opt flags db 0 ;timeout db 0 ;timeout format db 0 ;timeout check dw 03FFh ;last cyl dw 17 ;heads ; ;-------------------------------------------------------------- ;floppy disk param table ;the table doesn't match bios documentation ; fd_param db 0DFh ;step rate, unload time db 2 ;dma,head load time db 19h ;motor off time db 2 ;sector size (512) db 9 ;sectors per track db 40h ;gap db 0FFh ;data length (ignored) ;gap length when formatting is missing from table.... (50h) db 0F6h ;format filler db 0Fh ;settle time db 8 ;motor start time ; ;-------------------------------------------------------------- ;serial IO irq service addresses ;only rx A full and break are serviced ;the rest just iret ;16 words copied to 40:B0h com_irq_tab dw offset irq_ret ;tx A dw 0FC00h ;ROM segment dw offset rx_A_full ;rx A full dw 0FC00h dw offset bios_init ;break A dw 0FC00h dw offset irq_ret ;counter dw 0FC00h dw offset irq_ret ;tx B dw 0FC00h dw offset irq_ret ;rx B dw 0FC00h dw offset irq_ret ;break B dw 0FC00h dw offset irq_ret ;port change dw 0FC00h ; ;-------------------------------------------------------------- ;irq vector initialize table ;128 words copied to 0:0 int_init dw offset int_dummy ;0 dw 0FC00h dw offset int_dummy ;4 dw 0FC00h dw offset int_dummy ;8 dw 0FC00h dw offset int_dummy ;c dw 0FC00h dw offset int_dummy ;10 dw 0FC00h dw offset int_dummy ;14 dw 0FC00h dw offset int_dummy ;18 dw 0FC00h dw offset int_dummy ;1c dw 0FC00h dw offset int_08 ;20 dw 0FC00h dw offset int_dummy ;24 dw 0FC00h dw offset int_dummy ;28 dw 0FC00h dw offset int_dummy ;2c dw 0FC00h dw offset int_0c ;30 dw 0FC00h dw offset int_dummy ;34 dw 0FC00h dw offset int_0e ;38 dw 0FC00h dw offset int_dummy ;3c dw 0FC00h dw offset int_10 ;40 dw 0FC00h dw offset int_11 ;44 dw 0FC00h dw offset int_12 ;48 dw 0FC00h dw offset int_13 ;4c dw 0FC00h dw offset int_14 ;50 dw 0FC00h dw offset int_15 ;54 dw 0FC00h dw offset int_16 ;58 dw 0FC00h dw offset int_17 ;5c dw 0FC00h dw offset int_18 ;60 dw 0FC00h dw offset int_19 ;64 dw 0FC00h dw offset int_1a ;68 dw 0FC00h dw offset int_dummy ;6c dw 0FC00h dw offset int_dummy ;70 dw 0FC00h dw offset int_dummy ;74 dw 0FC00h dw offset fd_param ;78 dw 0FC00h dw offset int_dummy ;7c dw 0FC00h dw 0 ;80 dw 0 dw 0 ;84 dw 0 dw 0 ;88 dw 0 dw 0 ;8c dw 0 dw 0 ;90 dw 0 dw 0 ;94 dw 0 dw 0 ;98 dw 0 dw 0 ;9c dw 0 dw 0 ;a0 dw 0 dw 0 ;a4 dw 0 dw 0 ;a8 dw 0 dw 0 ;ac dw 0 dw 0 ;b0 dw 0 dw 0 ;b4 dw 0 dw 0 ;b8 dw 0 dw 0 ;bc dw 0 dw 0 ;c0 dw 0 dw 0 ;c4 dw 0 dw 0 ;c8 dw 0 dw 0 ;cc dw 0 dw 0 ;d0 dw 0 dw 0 ;d4 dw 0 dw 0 ;d8 dw 0 dw 0 ;dc dw 0 dw 0 ;e0 dw 0 dw 0 ;e4 dw 0 dw 0 ;e8 dw 0 dw 0 ;ec dw 0 dw 0 ;f0 dw 0 dw 0 ;f4 dw 0 dw 0 ;f8 dw 0 dw 0 ;fc dw 0 ; ;-------------------------------------------------------------- ;target select bit patterns ;first table is used for initiator id=7 SEL_bits db 1 ;drive 0 target db 0 ;drive 0 LUN db 2 ;drive 1 target db 0 ;drive 1 LUN db 0 db 0 db 0 db 0 ;2nd. table is used for initiator id not=7 db 80h ;drive 0 target db 0 ;drive 0 LUN db 0 db 0 db 0 db 0 db 0 db 0 ; ;-------------------------------------------------------------- ;per drive parameters (for 2 drives) ;12 bytes copied to 40:98h DRV_params dw 4 ;drive 0 heads dw 17 ;drive 0 sectors dw 03FFh ;drive 0 tracks ; dw 0 ;drive 1 (stub) dw 0 ; dw 0 ; ; ;-------------------------------------------------------------- ;IO pointer table ;dword pointers to SCSI IO buffers ;12 words copied to 40:0D4h IO_table db 0 ;my ID db 0 ;target ID dw 0 ; dw bd_42h ;CDB pointer dw 40h ; dw bd_42h ;data pointer dw 40h ; dw bd_8Ch ;status pointer dw 40h ; dw bd_8Eh ;message in pointer dw 40h ; dw bd_8Dh ;message out pointer dw 40h ; ;-------------------------------------------------------------- ;(RE)install interrupt vectors rst_vectors proc near xor di, di mov es, di assume es:nothing mov di, 0 mov ax, cs mov ds, ax assume ds:seg000 mov si, offset int_init mov cx, 0Ch rep movsb mov cx, 70h mov ax, 4 add si, ax add di, ax rep movsb xor al, al ; ;look for 5380 mov dx, 1084h out dx, al mov cx, 20h loop010: loop loop010 in al, dx or al, al jnz rst_vect01 ;skip HD install ; ;move floppy to int 40 mov ax, es:[4Ch] mov es:[100h], ax mov ax, es:[4Eh] mov es:[102h], ax ; ;install HD at int 13 mov word ptr es:[4Ch], offset hd_int13 mov es:[4Eh], cs mov word ptr es:[104h], offset hd_param mov es:[106h], cs rst_vect01: retn rst_vectors endp ; ; dummy routine for option ROM? org 03FE0h far_stub proc far retf far_stub endp ; nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop ; ;-------------------------------------------------------------- org 03FF0h power_on: mov dx, 0FFA0h mov ax, 0FC3Ch ;set chip select out dx, ax jmp far ptr start ; ;checksum? db 4Fh db 1 db 0FFh db 0FFh seg000 ends end