int13sniff: Move the .S file locally as Sourceforge has no longer any
authorJan Kratochvil <jan@jankratochvil.net>
Mon, 6 Sep 2021 14:40:22 +0000 (16:40 +0200)
committerJan Kratochvil <jan@jankratochvil.net>
Mon, 6 Sep 2021 14:40:22 +0000 (16:40 +0200)
  HTTP access to CVS repositories.

project/int13sniff/Index.pm
project/int13sniff/int13sniff.S [new file with mode: 0644]

index e5292c2..45651a9 100644 (file)
@@ -33,7 +33,7 @@ our @ListItem=(
                "priority"=>370,
                "icon"=>"int13sniff-icon.png",
                "summary"=>'Trace PC bootloader disk operations',
-               "download-source"=>'http://surprise.cvs.sourceforge.net/viewvc/surprise/surprise/misc/int13sniff.S?content-type=text/plain',
+               "download-source"=>'int13sniff.S',
                "download-gzip(1)ped floppy image head"=>'int13sniff.bin.gz',
                "link-parent Surprise project"=>"/project/surprise/",
                "license"=>"GPL",
diff --git a/project/int13sniff/int13sniff.S b/project/int13sniff/int13sniff.S
new file mode 100644 (file)
index 0000000..c82675e
--- /dev/null
@@ -0,0 +1,1019 @@
+/*
+ *     misc/int13sniff.S
+ *
+ *     Copyright (C) 2000
+ *     Partition Surprise Team <surprise-dev@lists.sourceforge.net>
+ *
+ *     $Id: int13sniff.S,v 1.4 2001/02/13 00:47:18 kratochvil Exp $
+ */
+
+
+/*
+ *
+ *     Sniffer for INT13 calls
+ *
+ *     Prepare your floppy and reboot from it with 0x80 disk for boot-sniffing:
+ *     ANY DATA ON YOUR FLOPPY (/dev/fd0) WILL BE ERASED BY THIS COMMAND!
+ *
+ *             make int13sniff.fd0
+ *
+ *
+ */
+
+
+/* !!!  All defines below must be COMMENTED-OUT !
+ * !!!  Define to value 0 has no effect.
+ */
+
+/* Enable of normal screen dump output.
+ * You can probably never need to undefine it, only if it severly
+ * harms some needed (fullscreen) application display output.
+ */
+#define SCREEN_PRINT 1
+
+/* Enable (if defined) dump output to all serials found
+ */
+#define SERIAL_BAUDRATE 9600
+
+/* Enable to refuse dumping output to port without DSR active
+ *  - pro: doesn't get slow by dumping to not-connected ports
+ *  - con: some cables may not have DSR (DTR<->DSR wiring would be sufficient)
+ */
+#define SERIAL_REQUIRE_DSR
+
+/* Enable to dump all messages back to floppy
+ *  - con: message output is slowed down a bit
+ * WARNING: some BIOSes fail to report geometry (INT 0x13/AH=0x08) for HDD!
+ */
+#define OUTPUT_STORE_DISK 0x00
+
+/* Linear sector offset where to start writing buffer sectors
+ * If undefined, it is calculated to be the last sector of "int13sniff.bin".
+#define OUTPUT_STORE_ADDRESS -1
+ */
+
+/* Stack size to reserve in the top-memory area
+ * WARNING: Due to SECTORS_LEN reading, this value MUST be always >=
+ *          than the sector size (0x200)!
+ */
+#define STACKSIZE_TOP 0x200
+
+/* Maintainers: Disable occupying of any (0x13 & 0x18) interrupt vectors
+#define DISABLE_SNIFF 1
+ */
+
+/* Maintainers: Disable .org macroinstructions: ONLY FOR compilation tuning!
+ *  - image produced doen't have any functionality!
+#define DISABLE_ORG 1
+ */
+
+
+/* Internal defines */
+#define SERIAL_SENDWAIT 16                             /* multiplied by 0x10000, Linux kernel has ~15 (looping 1E6 times) */
+#define SERIAL_LCR_INIT (UART_LCR_WLEN8)               /* 8N1, no DLAB */
+#define SERIAL_MCR_INIT (UART_MCR_DTR | UART_MCR_RTS)  /* we are RTSed! */
+#define MEM_KBYTES    0x413    /* BIOS variable */
+#define PORTADDR_BASE 0x400     /* BIOS variable */
+#define SECTOR2_MAGIC 0xEFBE
+#define SERIAL_DIVISOR (115200/SERIAL_BAUDRATE)
+#define OUTPUT_STORE_WRITE_RETRIES 5           /* how many times to try Write16 & ResetDisk sequence */
+#define OUTPUT_STORE_EOF 26
+
+#define DIVIDE_UP(what,by) (((what)+(by)-1)/(by))
+#define ROUND_UP(what,by) (DIVIDE_UP((what),(by))*(by))
+
+#define ALLOC_KB DIVIDE_UP((ALLOC_END-START)+STACKSIZE_TOP,0x400)
+#define SECTORS_LEN ((SECTORS_END-START)/0x200)
+
+
+#define NIBBLE_TO_HEX1(nibble) ('0'+(nibble)+('A'-('9'+1))*((nibble&8)&&((nibble&2)||(nibble&4))))
+#define NIBBLE_TO_HEX(nibble) NIBBLE_TO_HEX1((nibble)&0xF)
+#define BYTE_TO_HEX(byte) NIBBLE_TO_HEX((byte)>>4),NIBBLE_TO_HEX((byte))
+
+/* Extract from <linux/serial_reg.h>: */
+#define UART_TX                0       /* Out: Transmit buffer           (DLAB=0) */
+#define UART_DLL       0       /* Out: Divisor Latch Low         (DLAB=1) */
+#define UART_IER       1       /* Out: Interrupt Enable Register (DLAB=0) */
+#define UART_DLM       1       /* Out: Divisor Latch High        (DLAB=1) */
+#define UART_LCR       3       /* Out: Line Control Register */
+#define UART_MCR       4       /* Out: Modem Control Register */
+#define UART_LSR       5       /* In:  Line Status Register */
+#define UART_MSR       6       /* In:  Modem Status Register */
+
+#define UART_LCR_DLAB  0x80    /* Divisor latch access bit */
+#define UART_LCR_WLEN8 0x03    /* Wordlength: 8 bits */
+
+#define UART_MCR_RTS   0x02    /* RTS complement */
+#define UART_MCR_DTR   0x01    /* DTR complement */
+
+#define UART_MSR_DSR   0x20    /* Data Set Ready */
+
+#define UART_LSR_THRE  0x20    /* Transmit-hold-register empty */
+
+
+/* Macros: */
+
+       .macro  outchar char
+       movw    $0x0E00 | \char,%ax
+       movw    $7,%bx
+       int     $0x10
+       .endm
+
+       .macro  stosw_sniff
+#ifdef DISABLE_SNIFF
+       nop
+#else
+       stosw
+#endif
+       .endm
+
+#ifdef DISABLE_ORG
+#define MYORG(offset)
+#else
+#define MYORG(offset) .org (offset) + START            /* "+ START" MUST be on the end of line - WHY?!? */
+#endif
+
+       .macro  pushALL
+       pushw   %ax
+       pushw   %bx
+       pushw   %cx
+       pushw   %dx
+       pushw   %si
+       pushw   %di
+       pushw   %bp
+       .endm
+
+       .macro  popALL
+       popw    %bp
+       popw    %di
+       popw    %si
+       popw    %dx
+       popw    %cx
+       popw    %bx
+       popw    %ax
+       .endm
+
+#define PUSHALL_SIZE (7*2)
+#define PUSHALL_SI   ((1/*%bp*/+1/*%di*/)*2)
+
+       .macro  CallJumpVector0x13
+       pushf                   /* create 'lret' stack by this pushf... */
+       pushw   %cs             /* ...this segment... */
+       call    JumpVector0x13  /* ...and this return address */
+       .endm
+
+
+
+/* Code starts here */
+/********************/
+
+#ifdef HAVE_GAS_ARCH_I8086
+       .arch   i8086,nojumps
+#endif
+       .code16
+       .text
+       .globl  _start
+_start:
+
+/* init stack */
+START:
+       cli                     /* just a paranoia, shouldn't be needed */
+       jmp     InitRawStack    /* We don't want to have 0x90 on offset 2 (one of recognization rules by DOS) */
+       .org    START+0x03
+
+       .macro  places offset length string
+       .org    (\offset) + START
+       .ascii  "\string"
+       .org    (\offset) + (\length) + START           /* we can't check whether it isn't too short :-(  */
+       .endm
+       .macro  placex offset length bytes
+       .org    (\offset) + START
+       .byte   \bytes
+       .org    (\offset) + (\length) + START           /* we can't check whether it isn't too short :-(  */
+       .endm
+
+/* We rather supply empty (=zeroed) FAT filesystem table
+ */
+       places  0x03,8,"I13Sniff"               /* OEM ID */
+       placex  0x43,4,"0x5E,0x81,0xA2,0x99"    /* Volume Serial Number: 5E81A299 as "SERIAL99" */
+       places  0x47,11,"Int13Sniff\0"          /* Volume Label */
+       places  0x52,8,"BootOnly"               /* Filesystem ID */
+
+InitRawStack:
+       .org    0x5A + START
+
+       xorw    %ax,%ax
+       movw    %ax,%ss
+       movw    $0x7C00,%sp
+       sti
+       outchar '1'
+       ljmp    $0x7C0/*segment*/ , $InitContinue7C0-START/*offset*/
+
+InitContinue7C0:
+       outchar '3'
+
+/* move to top memory */
+       xorw    %dx,%dx         /* DX=null */
+       movw    %dx,%ds
+       movw    %ds:MEM_KBYTES,%ax
+       subw    $ALLOC_KB,%ax
+       movw    %ax,%ds:MEM_KBYTES
+       movb    $6,%cl
+       shlw    %cl,%ax
+       movw    %ax,%es
+       xorw    %bx,%bx         /* buffer=ES:BX= topram:0x0000 */
+       movw    $0x0200 | SECTORS_LEN,%ax       /* READ16 (SECTORS_LEN) sectors */
+       xorw    %dx,%dx         /* Head 0, Drive 0x00 */
+       call    ReadSectors
+       xorw    %di,%di
+       pushw   %cs
+       popw    %ds
+       xorw    %si,%si
+       movw    $0x200/2,%cx
+repe   cmpsw
+       pushw   %es
+       movw    $InitContinueTop-START,%ax
+       pushw   %ax
+       movw    $BadCmpMsg-START,%si
+       jnz     PrintFatal
+       cmpw    $SECTOR2_MAGIC,%es:(Sector2_MagicBuf-START)
+       je      LReturn1
+/* fallthru */
+PrintFatal:
+       call    PrintString
+PrintFatal_dead:
+       jmp     PrintFatal_dead
+LReturn1:
+       lret
+
+Return1:
+       ret
+
+/* Interface to retry-capable INT13, set everything except CX */
+/* function may destroy DS value! */
+ReadSectors:
+       movw    $0x0001,%cx     /* Cylinder 0, Sector 1 */
+       int     $0x13
+       jnc     Return1
+       pushw   %dx     /* save drive for resetting the controller */
+       pushw   %ax     /* error code in AH */
+       movw    $InitMsg-START,%si
+       call    PrintString
+       pushw   %bx     /* offset... */
+       pushw   %es     /* and segment of bufffer */
+       pushw   %ax     /* count */
+       pushw   %dx     /* head */
+       pushw   %cx     /* CX<sec/cyl> */
+       pushw   %dx     /* drive */
+       movw    $SF13_02_Msg-START,%si
+       call    PrintString
+       movw    $SnifferErrTail_MSG-START,%si
+       call    PrintString_noPref
+       movb    $0x00,%ah       /* reset controller - FDC or HDC */
+       popw    %dx             /* restore the drive */
+       int     $0x13
+       jmp     ReadSectors
+
+PrintTrailNL:
+       movw    $TrailNL_Msg-START,%si
+       jmp     PrintString_noPref
+
+/* String is given in SI, returns updated SI */
+PrintString:
+       movb    $'%',%al
+       call    PrintChar
+PrintString_noPref:
+       pushw   %cs
+       popw    %ds
+PrintString_loop:
+       cld
+       lodsb
+       testb   %al,%al
+       jz      Return1
+       movw    $PrintString_loop-START,%bx     /* from now do just 'ret' to close the loop */
+       pushw   %bx
+       testb   $0x80,%al       /* should be (x&E0==E0) */
+       jz      PrintChar
+       testb   $0x10,%al
+       jz      PrintString_no0x
+       pushw   %ax
+       movb    $'$',%al
+       call    PrintChar
+       popw    %ax
+PrintString_no0x:
+       popw    %bx     /* discard $PrintString_loop-START */
+       popw    %bx     /* return address */
+       popw    %dx     /* value to print */
+       pushw   %bx     /* return back the return address */
+       movw    $PrintString_loop-START,%bx
+       pushw   %bx
+       testb   $0x02,%al
+       jz      PrintString_noHhex
+       pushw   %ax
+       pushw   %dx
+       call    PrintString_Lhex
+       popw    %dx
+       popw    %ax
+PrintString_noHhex:
+       movb    %dl,%dh
+       testb   $0x01,%al
+Return1_jump1:
+       jz      Return1
+
+/* One byte to print is in DH */
+PrintString_Lhex:
+       pushw   %dx
+       movb    $4,%cl
+       rorb    %cl,%dh
+       call    PrintString_Nhex
+       popw    %dx
+
+/* One nibble to print is in the lower one of DH */
+PrintString_Nhex:
+       andb    $0x0F,%dh
+       addb    $'0',%dh
+       cmpb    $'9'+1,%dh
+       jc      PrintString_charDH
+       addb    $'A'-('9'+1),%dh
+PrintString_charDH:
+       movb    %dh,%al
+/* fallthru */
+
+/* Character is given in AL
+ * Preserves: DS, ES, SI, DI
+ * Destroys: AX (even AL!), BX, CX, DX
+ * DS is sometimes left preserved, sometimes set to DS=CS
+ */
+PrintChar:
+#ifdef SCREEN_PRINT
+       movb    $0x0E,%ah       /* print character */
+       movw    $7,%bx
+       pushw   %ax
+       int     $0x10
+       popw    %ax
+#else
+       xorb    %bh,%bh         /* assumed below */
+#endif
+#if defined(SERIAL_BAUDRATE) || defined(OUTPUT_STORE_DISK)
+       pushw   %cs
+       popw    %ds             /* set DS=CS */
+       cmpb    %bh,%ds:(SerialEnable-START)
+       jz      Return1_jump1
+#ifdef SERIAL_BAUDRATE
+       jmp     SerialPrintChar
+#else /* SERIAL_BAUDRATE */
+       jmp     OutputStorePrintChar
+#endif /* SERIAL_BAUDRATE */
+#else /* defined(SERIAL_BAUDRATE) || defined(OUTPUT_STORE_DISK) */
+       ret
+#endif /* defined(SERIAL_BAUDRATE) || defined(OUTPUT_STORE_DISK) */
+
+
+#if defined(SERIAL_BAUDRATE) || defined(OUTPUT_STORE_DISK)
+SerialEnable:
+       .byte   0       /* map of port bits which want to be enabled */
+
+#ifdef OUTPUT_STORE_DISK
+#define OUTPUT_STORE_SerialEnable (0x02)       /* FloppyPrintChar enabled */
+#else
+#define OUTPUT_STORE_SerialEnable (0x00)
+#endif
+
+#endif
+
+InitMsg:
+       .asciz  "INIT: "
+BadCmpMsg:
+       .asciz  "Disk 0x00 sector 0 doesn't match myself!"
+SF13_02_Msg:
+       .ascii  "Read"
+Common16disk_Msg:
+       .asciz  "16(drv=\xF1,CX<sec/cyl>=\xF3,head=\xF2,count=\xF1,buf=\xF3:\xF3)"
+SnifferErrTail_MSG:
+       .ascii  "=\xF2 !"
+TrailNL_Msg:
+       .byte   13,10,0
+
+/*******************************************************************/
+/* Storage area - code isn't needed to be primary-sector reachable */
+/*******************************************************************/
+
+/**************************************************************/
+/* Final primary-sector signature                             */
+/**************************************************************/
+
+       MYORG(0x1FC)
+       .byte   0xDE,0xAD       /* FAT32 defines somehow extended, 32-bit signature (=> 0xDEAD) */
+       .byte   0x55,0xAA
+
+/**************************************************************/
+/* Section run during boot but from the upper half of code    */
+/**************************************************************/
+
+
+/* Main initialization */
+/***********************/
+/* DS invalid, ES==CS */
+InitContinueTop:
+       outchar ':'
+
+       pushw   %cs
+       cli
+       popw    %ss
+       movw    $ALLOC_KB*1024,%sp
+       sti
+
+#ifdef OUTPUT_STORE_DISK
+       movw    $OutputStoreBufSpaceStart-START,%di
+       movw    $((OutputStoreBuf+0x200-OutputStoreBufSpaceStart)+1)/2,%cx      /* NOTE_1: Buffer may got +1 overfilled */
+       xorw    %ax,%ax
+       rep     stosw
+#endif
+
+#ifndef SERIAL_BAUDRATE
+#ifdef OUTPUT_STORE_DISK
+       movb    $OUTPUT_STORE_SerialEnable,%cs:(SerialEnable-START)
+#endif
+       movw    $HelloMsg-START,%si
+       call    PrintString
+#else /* SERIAL_BAUDRATE */
+       movb    $0x55|OUTPUT_STORE_SerialEnable,%es:(SerialEnable-START)
+       movw    $HelloMsg-START,%si
+       call    PrintString             /* this string will not have EOL when SERIAL_BAUDRATE is defined */
+       movb    %ds:(SerialEnable-START),%cl
+       movw    $PORTADDR_BASE,%si
+SerialHello_loop:
+       xorw    %ax,%ax
+       movw    %ax,%ds
+       lodsw
+       rorb    $1,%cl
+       jnc     SerialHello_skipOne
+       testw   %ax,%ax
+       jz      SerialHello_skipOne
+       pushw   %cx                             /* push SerialEnable mask, S=1 */
+       pushw   %si                             /* push PORTADDR_BASE+x pointer, S=2 */
+       pushw   %ax                             /* print port base */
+       movw    $SerialHelloPort_Msg-START,%si
+       call    PrintString_noPref
+       popw    %si                             /* restore PORTADDR_BASE+x pointer, S=1 */
+       popw    %cx                             /* restore SerialEnable mask, S=0 */
+SerialHello_skipOne:
+       rorb    $1,%cl                          /* skip unused even bits of SerialEnable mask */
+       cmpw    $PORTADDR_BASE+4*2,%si
+       jne     SerialHello_loop
+       call    PrintTrailNL
+#endif
+
+/* now install our SniffFunction0x13 sniffer */
+
+       xorw    %ax,%ax
+       movw    %ax,%ds
+       movw    $0x13*4,%si
+       pushw   %cs
+       popw    %es
+       movw    $OrigVector0x13-START,%di
+       pushw   %si
+       movw    $SniffFunction0x13-START,%ax
+       cld
+       cli
+       movsw
+       movsw
+       popw    %di     /* = $0x13*4 */
+       pushw   %ds
+       popw    %es     /* = null */
+       stosw_sniff     /* $SniffFunction0x13-START */
+       pushw   %cs
+       popw    %ax
+       stosw_sniff
+       movw    $0x18*4,%di     /* ROM basic - failed boot */
+       movw    $SniffFunction0x18-START,%ax
+       stosw_sniff
+       pushw   %cs
+       popw    %ax
+       stosw_sniff
+       sti
+#ifdef OUTPUT_STORE_DISK
+       incb    %cs:(OutputStoreFlushEnable-START)      /* we have now functional JumpVector0x13 */
+#endif
+       /* we WANT DS left with 0x0000 */
+       /* we WANT ES left with 0x0000 */
+
+/* and give the system control to disk=0x80/masterboot */
+       movw    $0x0201,%ax     /* READ16 1 sector */
+       movw    $0x7C00,%bx     /* buffer=ES:BX= 0x0000:0x7C00 */
+       pushw   %ds
+       pushw   %bx             /* BX/DS on stack for later 'lret', S=2, prepared for far ret */
+       movw    $0x0080,%dx     /* Head 0, Drive 0x80 */
+       call    ReadSectors             /* it returns only when successfully read */
+       cmpw    $0xAA55,%es:(0x7C00+0x1FE)              /* 0x55,0xAA */
+       movw    $BadSignatureMsg-START,%si
+       jne     PrintFatal_jump1
+       cli                     /* IMPORTANT: Boot sectors must be run with CLI! */
+       lret                    /* lret to 0x0000:0x7C00 */
+
+SniffFunction0x18:
+       movw    $Interrupt0x18Msg-START,%si     /* %ds gets fixed in PrintFatal */
+PrintFatal_jump1:
+       jmp     PrintFatal
+
+
+/* SerialPort handling code */
+/****************************/
+
+#ifdef SERIAL_BAUDRATE
+
+SerialPrintChar:
+
+/* Function assumes DS == CS, it preserves SI, DI, ES but uses ES==0 ! */
+/* Function expects character to print in AL, preserves it. */
+
+/* fallthru */
+SerialLoop:
+       movb    $0,%ah          /* offset from PORTADDR_BASE for port #1 */
+       xorw    %bx,%bx         /* any register */
+       pushw   %es             /* push ES, S=1 */
+       movw    %bx,%es
+
+SerialLoop_loop:
+       movw    $PORTADDR_BASE,%bx
+       addb    %ah,%bl
+       movw    %es:(%bx),%bx
+       testw   %bx,%bx
+       jz      SerialLoop_noport       /* now we have port base in BX */
+       movb    $1,%ch
+       movb    %ah,%cl
+       rolb    %cl,%ch
+       testb   %ch,%ds:(SerialEnable-START)
+       jz      SerialLoop_noport       /* disabled port */
+       pushw   %ax                     /* push CHARACTER and AH(port offset), S=2 */
+       cmpb    $'%',%al
+       jnz     SerialLoop_noInit
+
+/* Serial Init */
+       leaw    UART_IER(%bx),%dx       /* DX=+1, UAT_IER - interrupt enable */
+       xorb    %al,%al                 /* NO interrupts */
+       outb    %al,%dx
+       incw    %dx
+       incw    %dx             /* DX=+3, UART_LCR - used as divisor enable */
+       movb    $SERIAL_LCR_INIT | UART_LCR_DLAB,%al
+       outb    %al,%dx
+       movw    %bx,%dx         /* DX=+0, UART_DLL - divisor LOW */
+       movb    $(SERIAL_DIVISOR & 0xFF),%al
+       outb    %al,%dx
+       incw    %dx             /* DX=+1, UART_DLM - divisor HIGH */
+       movb    $(SERIAL_DIVISOR >> 8),%al
+       outb    %al,%dx
+       incw    %dx
+       incw    %dx             /* DX=+3, UART_LCR - line control */
+       movb    $SERIAL_LCR_INIT,%al
+       outb    %al,%dx
+       incw    %dx             /* DX=+4, UART_MCR - modem control */
+       movb    $SERIAL_MCR_INIT,%al
+       outb    %al,%dx
+       incw    %dx             /* DX=+5, UART_LSR - line status */
+       inb     %dx,%al
+       incb    %al
+       jz      PrintChar_serial_invalidPort            /* LSR port is 0xFF - no device on the bus */
+
+#ifdef SERIAL_REQUIRE_DSR
+       incw    %dx             /* DX=+6, UART_MSR - modem status */
+       xorw    %cx,%cx                 /* Try ~ 60msec for DSR transition delayd by pure wiring after our DTR */
+PrintChar_serialWaitDSR:
+       inb     %dx,%al
+       andb    $UART_MSR_DSR,%al       /* we MUST have AL==0 when passing to invalidPort ! */
+       jnz     PrintChar_serialSend
+       loop    PrintChar_serialWaitDSR
+       jmp     PrintChar_serial_invalidPort
+#endif /* SERIAL_REQUIRE_DSR */
+
+SerialLoop_loop_jump1:
+       jmp     SerialLoop_loop
+
+SerialLoop_noInit:             /* still CHARACTER and AH(port offset) on stack, S=2 */
+PrintChar_serialSend:
+       movb    $SERIAL_SENDWAIT,%ah
+PrintChar_serialSend_wait:
+       leaw    UART_LSR(%bx),%dx       /* UART_LSR - line status */
+       inb     %dx,%al
+       andb    $UART_LSR_THRE,%al      /* test transmitter-buffer-empty bit */
+       jnz     PrintChar_serialSend_free       /* we MUST have AL==0 when passing to invalidPort ! */
+       loop    PrintChar_serialSend_wait
+       decb    %ah                     /* out-loop count SERIAL_SENDWAIT */
+       jnz     PrintChar_serialSend_wait
+PrintChar_serial_invalidPort:
+       leaw    UART_MCR(%bx),%dx       /* UART_LSR - line status */
+       outb    %al,%dx                 /* assumed AL==0: turn off DTR & RTS of the failing port */
+       movb    %ds:(SerialEnable-START),%ah
+       pushw   %ax                     /* pushed AH(SerialEnable), S=3 */
+       movb    %al,%ds:(SerialEnable-START)    /* assumed AL==0; disable temporarily complete serial output */
+       pushw   %si             /* save SI, S=4 */
+       pushw   %bx                     /* port base to print out */
+       movw    $SerialPortError_Msg-START,%si
+       call    PrintString
+       popw    %si             /* recover SI, S=3 */
+       popw    %bx                     /* restored BH=AH(SerialEnable), S=2 */
+       popw    %ax                     /* restored CHARACTER and AH(port offset), S=1 */
+       movb    %ah,%cl
+       movb    $0xFE /* ==~1 */,%ch
+       rolb    %cl,%ch         /* masking-out pattern */
+       andb    %ch,%bh         /* mask-out the original SerialEnable left in BH */
+       movb    %bh,%ds:(SerialEnable-START)    /* disable the failed port for futher serial dumping */
+       jmp     SerialLoop_noport
+
+PrintChar_serialSend_free:
+       movw    %bx,%dx                 /* UART_TX - xmit buffer */
+       popw    %ax                     /* restored CHARACTER and AH(port offset), S=1 */
+       outb    %al,%dx                 /* final character transmit */
+
+SerialLoop_noport:
+       inc     %ah
+       inc     %ah                     /* skip by two bytes of PORTADDR_BASE entry offset */
+       cmp     $4*2,%ah
+       jne     SerialLoop_loop_jump1
+       popw    %es                     /* pop ES, S=0 */
+#ifndef OUTPUT_STORE_DISK
+       ret
+#else
+/* FALLTHRU */
+#endif
+#endif /* SERIAL_BAUDRATE */
+
+/* OutputStore handling code */
+/*****************************/
+
+#ifdef OUTPUT_STORE_DISK
+
+#if OUTPUT_STORE_DISK!=0x00
+#error "int13sniff WILL destroy the data on OUTPUT_STORE_DISK!!! Are you sure?"
+#endif
+
+OutputStorePrintChar:
+
+/* Function assumes DS == CS, it preserves SI, DI, ES. */
+/* Function expects character to print in AL. */
+
+       testb   $OUTPUT_STORE_SerialEnable,%ds:(SerialEnable-START)
+       jz      Return3
+OS_NotDisabled:
+       cmp     $13,%al                         /* we completely ignore and filter-out all '\r' */
+       je      Return3
+       pushw   %es                     /* SAVE %ES */
+       pushw   %di                     /* SAVE %DI */
+       pushw   %si                     /* SAVE %SI */
+       movw    %ds:(OutputStoreBufPtr-START),%di
+       pushw   %cs
+       popw    %es                     /* ES=CS */
+       stosb
+OutputStoreTerminate:          /* we will terminate the OutputStoreBuf at the given %DI position */
+       movw    %di,%ds:(OutputStoreBufPtr-START)
+       movb    $OUTPUT_STORE_EOF,%ds:(%di)
+       cmpb    $0,%ds:(OutputStoreFlushEnable-START)
+       jz      Return2pop_jump1                /* we cannot yet call CallJumpVector0x13 */
+       cmpb    $10,%al
+       je      OutputStoreFlush                /* always flush bufer on message newline */
+       cmpw    $(OutputStoreBuf-START)+0x200,%di
+       jnc     OutputStoreFlush
+Return2pop_jump1:
+       jmp     Return2pop
+
+Return3:
+       ret
+
+OutputStoreFlush:
+       movw    $OUTPUT_STORE_WRITE_RETRIES,%bp
+OS_RetryWrite:
+       mov     $0x08,%ah                       /* %AH=get drive geometry, WARNING: DESTROYS %DI! */
+       mov     $OUTPUT_STORE_DISK,%dl
+       CallJumpVector0x13              /* now %DH=maximum heads, %CX=maximum cylinder&sector */
+       jc      OS_MediaOver
+       movw    %cx,%bx
+       xchg    %bh,%bl
+       rol     $1,%bh
+       rol     $1,%bh
+       andb    $0x03,%bh               /* now %BX==# of cylinders */
+       andb    $0x3F,%cl               /* now %CL==# of sectors */
+       pushw   %cx             /* SAVE LOW==# of sectors */
+       inc     %dh                     /* now %DH==# of heads */
+       movb    %dh,%al
+       mulb    %cl                     /* now %AX==# of sectors in cylinder */
+       movw    %ax,%cx
+       /* now we have %CX==# of sectors in cylinder, %BX=# of cylinders */
+       movw    %cs:(OutputStoreAddress-START+0),%ax
+       movw    %cs:(OutputStoreAddress-START+2),%dx            /* %DX:%AX==OutputStoreAddress */
+       divw    %cx                                     /* now %AX==cylinder, %DX==head&sector */
+       cmp     %ax,%bx
+       popw    %bx                     /* RESTORE ->%BL==# of sectors */
+       jc      OS_MediaOver    /* cylinders exceeded */
+       xchg    %ax,%dx
+       divb    %bl                     /* now %AL==head, %AH=0-based sector (and %DX==cylinder) */
+       incb    %ah                                     /* now %AL==head, %AH=1-based sector (and %DX==cylinder) */
+       testb   $0xC0,%ah
+       jnz     OS_MediaOver
+       testb   $0xFC,%dh
+       jnz     OS_MediaOver
+       movb    %ah,%cl
+       movb    %dl,%ch
+       rorb    $1,%dh
+       rorb    $1,%dh
+       orb     %dh,%cl                 /* now %CX==cylinder&sector in BIOS format */
+       movb    %al,%dh                 /* now %DH==head */
+       pushw   %cs
+       popw    %ds                     /* DS=CS */
+       movb    $OUTPUT_STORE_DISK,%dl
+       movw    $0x0301,%ax                     /* %AH=write sectors, %AL=1 sector */
+       movw    $(OutputStoreBuf-START),%bx
+       pushw   %cs
+       popw    %es                     /* ES=CS */
+       CallJumpVector0x13
+       jnc     OS_WriteOK
+       movw    %ax,%es         /* %ES=Write16 error code */
+       xorb    %ah,%ah                         /* %AH=reset drive */
+       CallJumpVector0x13
+       decw    %bp
+       jnz     OS_RetryWrite
+       andb    $~OUTPUT_STORE_SerialEnable,%ds:(SerialEnable-START)    /* OutputStorePrintChar got DISABLED here */
+       pushw   %es     /* Write16 error code, for secondary "SnifferErrTail_MSG" */
+                       /* now parameters for "Common16disk_Msg": */
+       pushw   %bx     /* buf 2nd */
+       pushw   %cs     /* buf 1st (%ES has been destroyed by "Write16 error code" */
+       movb    $0x01,%al       /* "count" has been destroyed by "reset disk" call */
+       pushw   %ax     /* count */
+       pushw   %dx     /* head */
+       pushw   %cx     /* CX<sec/cyl> */
+       pushw   %dx     /* drive */
+
+OS_MediaOver:
+       movw    $OutputFailedWrite_Msg-START,%si
+       call    PrintString
+       movw    $Common16disk_Msg-START,%si
+       call    PrintString_noPref              /* pops up 6*2 bytes (6 words) */
+       movw    $SnifferErrTail_MSG-START,%si
+       call    PrintString_noPref              /* pops up 1*2 bytes (1 word ) */
+Return2pop:
+       popw    %si             /* RESTORE %SI */
+       popw    %di             /* RESTORE %DI */
+       popw    %es             /* RESTORE %ES */
+Return2:       ret
+
+OS_WriteOK:
+       cmpw    $(OutputStoreBuf-START)+0x200,%ds:(OutputStoreBufPtr-START)
+       jc      Return2pop                      /* buffer is not yet filled */
+       incw    %ds:(OutputStoreAddress-START+0)
+       jnz     OS_IncAddrNotWordCarry
+       incw    %ds:(OutputStoreAddress-START+2)
+OS_IncAddrNotWordCarry:
+       movw    $OutputStoreBuf-START,%di
+       pushw   %di
+       xorw    %ax,%ax
+       movw    $0x200/2,%cx
+       rep     stosw                   /* OutputStore got cleared */
+       popw    %di
+       movb    $10,%al                         /* we want to immediately Flush even the new sector */
+       jmp     OutputStoreTerminate
+
+#endif /* OUTPUT_STORE_DISK */
+
+/**************************************************************/
+/* Section for INT13 sniffing                                 */
+/* THIS point may start to be after 0x200 boundary            */
+/**************************************************************/
+
+SniffFunction0x13:
+       pushw   %ax     /* trash */
+       pushw   %ds
+       pushALL
+
+       cmpb    $0x00,%ah       /* reset */
+       jne     SF13_not00
+       movw    $JumpVector0x13popa-START,%ax
+       pushw   %ax     /* return address */
+       pushw   %dx     /* drive */
+       movw    $SF13_00_Msg-START,%si
+JumpVector0x13printStringPopa:
+       call    PrintString
+       jmp     PrintTrailNL
+
+SF13_not00:
+       cmpb    $0x02,%ah       /* read16 */
+       jne     SF13_not02
+       pushw   %bx     /* buf 2nd */
+       pushw   %es     /* buf 1st */
+       pushw   %ax     /* count */
+       pushw   %dx     /* head */
+       pushw   %cx     /* CX<sec/cyl> */
+       pushw   %dx     /* drive */
+       movw    $SF13_02_Msg-START,%si
+       call    PrintString
+Sniffer13bottomStdHalf:
+       call    CallVector0x13
+       movw    $SnifferOKTail_Msg-START,%si
+       jnc     SF13_not00_tail         /* ...if CY is SET */
+Sniffer13DumpErrorAH:
+       movw    $SnifferErrTail_MSG-START,%si
+       pushw   %ax
+SF13_not00_tail:
+Sniffer13iretPrintStringFinal:
+       call    PrintString_noPref
+       popALL
+       popw    %ds
+       incw    %sp
+       incw    %sp     /* trash discarded */
+       iret
+
+SF13_not02:
+       cmpb    $0x41,%ah       /* presence32 */
+       jne     SF13_not41
+       cmpw    $0x55AA,%bx
+       jne     SF13_not41
+       pushw   %dx     /* drive */
+       movw    $SF13_41_Msg-START,%si
+       call    PrintString
+       call    CallVector0x13
+       cmp     $0xAA55,%bx
+       jne     Sniffer13DumpErrorAH
+       pushw   %cx     /* supports */
+       pushw   %ax     /* version */
+       movw    $SF13_41_out_Msg-START,%si
+       jmp     Sniffer13iretPrintStringFinal
+
+CallVector0x13:
+       popw    %ax     /* return address */
+       pushw   %bp
+       movw    %sp,%bp
+       movw    %ax,%ss:2/*%bp*/+PUSHALL_SIZE+2/*%ds*/(%bp)     /* store %ax to 'trash' */
+       popw    %bp
+       popALL
+       popw    %ds
+       CallJumpVector0x13
+       pushw   %ds
+       pushALL                 /* stack is back to normal NOW */
+       pushw   %bp             /* trash - just to prepare return address16 */
+       pushw   %ax
+       pushw   %bp
+       movw    %sp,%bp
+       movw    %ss:2/*%bp*/+2/*%ax*/+2/*return16*/+PUSHALL_SIZE+2/*%ds*/(%bp),%ax
+       movw    %ax,%ss:2/*%bp*/+2/*ax*/(%bp)
+       pushf
+       pop     %ax
+       movw    %ax,%ss:2/*%bp*/+2/*%ax*/+2/*return16*/+PUSHALL_SIZE+2/*%ds*/+2/*trash*/+4/*ret-seg:offs*/(%bp)
+       popw    %bp
+       popw    %ax
+       ret                     /* to prepared return16 and now will be stack in normal again */
+
+SF13_not41:
+       cmpb    $0x42,%ah       /* read32 */
+       je      SF13_do42
+       jmp     JumpVector0x13popa      /* too far for conditional-jump */
+
+SF13_do42:
+       movb    %ds:2(%si),%al  /* count */
+       pushw   %ax
+       pushw   %ds:8(%si)      /* LBA32 sector LOWEST */
+       pushw   %ds:8+2(%si)    /* LBA32 sector LOWER */
+       pushw   %ds:8+4(%si)    /* LBA32 sector HIGHER */
+       pushw   %ds:8+6(%si)    /* LBA32 sector HIGHEST */
+       pushw   %dx             /* drive */
+       movw    $SF13_42_Msg-START,%si
+       call    PrintString     /* first half of the entry-message */
+
+       movw    %sp,%bp
+       movw    %ss:PUSHALL_SI(%bp),%si
+       movw    %ss:PUSHALL_SIZE(%bp),%ds       /* restore DS:SI packet */
+
+       movw    %ds:4(%si),%ax          /* buf16 offset */
+       pushw   %ax             /* buf16 offset */
+       andw    %ds:4+2(%si),%ax        /* buf16 segment */
+       pushw   %ds:4+2(%si)    /* buf16 segment */
+       incw    %ax
+       movw    $SF13_42_buf16_Msg-START,%si
+       jnz     SF13_42_buf16           /* buf16 was NOT 0xFFFF:0xFFFF => jump as OK */
+       addw    $4,%sp          /* drop buf16 address seg:offs */
+       pushw   %ds:0x10(%si)   /* 64-bit buffer LOWEST */
+       pushw   %ds:0x10+2(%si) /* 64-bit buffer LOWER */
+       pushw   %ds:0x10+4(%si) /* 64-bit buffer HIGHER */
+       pushw   %ds:0x10+6(%si) /* 64-bit buffer HIGHEST */
+       movw    $SF13_42_buf64_Msg-START,%si
+
+SF13_42_buf16: /* too far for conditional-jump */
+       call    PrintString_noPref
+       jmp     Sniffer13bottomStdHalf
+
+JumpVector0x13popa:
+       popALL
+       popw    %ds
+       incw    %sp
+       incw    %sp     /* trash discarded */
+JumpVector0x13:
+       .byte   0xEA    /* ljmp */
+OrigVector0x13:
+       .skip   4
+
+/* Serial support upper half initialization messages */
+/*****************************************************/
+
+#ifdef SERIAL_BAUDRATE
+
+SerialPortError_Msg:
+       .asciz  "SerialPort \xF3 error: stopping its communication\r\n"
+
+SerialHelloPort_Msg:
+       .asciz  " \xF3"
+
+#endif /* SERIAL_BAUDRATE */
+
+
+/* Main upper half initialization messages */
+/*******************************************/
+
+HelloMsg:
+       .ascii  "INT13-Sniff, version ",VERSION,", RCS revision ",REVISION
+#ifdef OUTPUT_STORE_DISK
+       .ascii  ", Output Store on disk 0x"
+       .byte   BYTE_TO_HEX(OUTPUT_STORE_DISK)
+#endif
+#ifdef SERIAL_BAUDRATE
+       .ascii  ", using serial ports:"
+#else
+       .byte   13,10
+#endif
+       .byte   0
+BadSignatureMsg:
+       .asciz  "Disk 0x80 has invalid signature!"
+
+
+/* Interrupt sniffing messages */
+/*******************************/
+
+Interrupt0x18Msg:
+       .asciz  "Interrupt 0x18 - Failed boot!"
+SF13_00_Msg:
+       .asciz  "Reset(drv=\xF1)"
+SF13_41_Msg:
+       .asciz  "Presence32(drv=\xF1)"
+SF13_41_out_Msg:
+       .ascii  "=(version=\xF2,supports=\xF3)"
+       /* fallthru */
+SnifferOKTail_Msg:
+       .ascii  " OK"
+       .byte   13,10,0
+SF13_42_Msg:
+       .asciz  "Read32(drv=\xF1,LBA32=\xF3\xE3\xE3\xE3,count=\xF1,buf="
+SF13_42_buf16_Msg:
+       .asciz  "\xF3:\xF3)"
+SF13_42_buf64_Msg:
+       .asciz  "\xF3\xE3\xE3\xE3)"
+
+
+/* Output Store messages / variables */
+/*************************************/
+#ifdef OUTPUT_STORE_DISK
+
+OutputFailedWrite_Msg:
+       .asciz  "Output Store error, stopping store: Write"
+
+OutputStoreBufPtr:
+       .word   OutputStoreBufSpaceStart-START
+OutputStoreAddress:
+#ifdef OUTPUT_STORE_ADDRESS
+       .long   OUTPUT_STORE_ADDRESS
+#else
+       .long   (OutputStoreBuf-START)/0x200
+#endif
+OutputStoreFlushEnable:
+       .byte   0               /* disable calling of JumpVector0x13 as it is not functional yet */
+
+#endif
+/**************************************************************/
+/* Sectors finalisation                                       */
+/**************************************************************/
+
+#ifndef OUTPUT_STORE_DISK
+ALLOC_END:
+#endif
+
+       MYORG(ROUND_UP(.+2-START,0x200)-2)
+Sector2_MagicBuf:
+       .word   SECTOR2_MAGIC
+
+#ifdef OUTPUT_STORE_DISK
+OutputStoreBuf:
+#endif
+       .ascii  "\n<OuTpUt_sToRe>\n"
+#ifdef OUTPUT_STORE_DISK
+OutputStoreBufSpaceStart:
+       .ascii  "Buffer empty, no data stored yet.\n"
+#else
+       .ascii  "OUTPUT_STORE_DISK not defined, Output Store disabled!\n"
+#endif
+       .byte   OUTPUT_STORE_EOF
+
+       MYORG(ROUND_UP(.-START,0x200))  /* WARNING, we may overfull the buffer by +1 bytes, see NOTE_1 */
+
+#ifdef OUTPUT_STORE_DISK
+       .equ    ALLOC_END,.+1           /* we need one 'trash' byte for exceeding OUTPUT_STORE_EOF */
+#endif
+
+SECTORS_END:
+
+/* vi:ts=8:sw=8
+ */