;===============================================================;
;                                                               ;
;       MBOOT86.A86 Ver 1.00                                    ;
;       (preliminary release 11/08/83)                          ;
;                                                               ;
;       This program accepts a file that comes in over          ;
;       an IBM ASYNC adapter board, port-0.                     ;
;                                                               ;
;                       **** BEWARE ****                        ;
;       This codes is a 2 hour attempt at converting MBOOT      ;
;       from CP/M-80,8080 code TO CP/M-86,8086 code.  It is     ;
;       not an exact translation!!  Like MBOOT, is does have    ;
;       a terminal mode and will receive a file used the        ;
;       MODEM/XMODEM protocol.                                  ;
;                                                               ;
;       CREDITS                                                 ;
;         MODEM BOOT(MBOOT)   - Keith Petersen W8SDZ            ;
;         Terminal routine    - John Taylor                     ;
;         Based on MODEM.ASM  - Ward Christensen                ;
;         MBOOT86 for CP/M-86 - Daniel Deward                   ;
;                                                               ;
;       FOR THE CP/M-86 OPERATING SYSTEM                        ;
;               To generate a .CMD file,                        ;
;       A>asm86 mboot86                                         ;
;       A>gencmd mboot86                                        ;
;                                                               ;
;===============================================================;
;
;       BDOS FUNCTION NUMBERS
;
CONINP  EQU     1               ;CONSOLE INPUT
CONOUT  EQU     2               ;CONSOLE OUTPUT
CSTAT   EQU     11              ;GET CONSOLE STATUS
;
;       ASCII CONTROL CHARACTER EQUATES
;
SOH     EQU     01H             ;START OF HEADER
EOT     EQU     04H             ;END OF TRANSMISSION
CR      EQU     0DH             ;CARRIAGE RETURN
LF      EQU     0AH             ;LINE FEED
ESC     EQU     1BH             ;ESCAPE
ACK     EQU     06H             ;ACKNOWLEDGE
NAK     EQU     15H             ;NEGATIVE ACK
CAN     EQU     18H             ;CANCEL TRANSMISSION
;
ERRLIM  EQU     10
EXITKEY EQU     03H             ;CONTROL-C
;
;       I/O PORT ASSIGNMENTS
;
MODDATP EQU     03F8H           ;MODEM DATA PORT
MODCTLP EQU     03FDH           ;MODEM CONTROL PORT
;
;
       CSEG
       CLI                             ;DISABLE INTERRUPTS
       MOV     DX,OFFSET MSG1          ;LOGON
       CALL    PSTRING
;
       MOV     AL,FCB+1                ;CHECK FOR FILE ON COMMAND LINE
       CMP     AL,' '
       JNZ     TERMI
;
       MOV     DX,OFFSET MSG2          ;NO FILE NAME SPECIFIED
       CALL    PSTRING
       JMP     EXIT
;
TERMI:
       MOV     DX,MODDATP
       IN      AL,DX                   ;GOBBLE UP GARBAGE
       IN      AL,DX
;
       MOV     DX,OFFSET MSG3          ;TERMINAL MODE
       CALL    PSTRING
;
TMODE:
       CALL    STAT            ;GET KEYBOARD STATUS
       JZ      TERML           ;NO CHAR READY
       CALL    KEYIN           ;ESLE GET CHAR
       CMP     AL,EXITKEY      ;TEST FOR EXIT
       JNZ     KEYOK
       JMP     EXIT
;
KEYOK:
       CMP     AL,ESC          ;START FILE XFER ?
       JZ      RCVFIL
;
       MOV     DX,MODDATP
       OUT     DX,AL
;
TERML:
       MOV     DX,MODCTLP      ;GET MODEM STATUS
       IN      AL,DX
       TEST    AL,01H
       JZ      TMODE           ;NO CHAR YET
       MOV     DX,MODDATP      ;GET MODEM DATA
       IN      AL,DX
       CALL    COUT
       JMP     TMODE
;
RCVFIL:
       CALL    MAKEFIL
       MOV     DX,OFFSET MSG4  ;FILE OPEN READY TO RECEIVE
       CALL    PSTRING
;
RCVLP:
       CALL    RCVSECT                 ;GET SECTOR
       JC      RCVEOT                  ;IF END OF TRANSFER
       CALL    WRSECT                  ;WRITE SECTOR
       CALL    INCRSNO                 ;ADVANCE SECTOR #
       CALL    SENDACK                 ;ACK RECORD
       JMP     RCVLP                   ;CONTINUE TILL EOT
;
RCVEOT:
       CALL    WRBLOCK                 ;WRITE OUT LAST BUFFER
       CALL    SENDACK                 ;SEND ACK
       CALL    CLOSEFIL                ;CLOSE THE FILE
;
       MOV     DX,OFFSET MSG5          ;TRANSFER COMPLETE
       CALL    PSTRING
       JMP     EXIT
;
; SUBROUTINE RCVSECT
;
RCVSECT:
       XOR     AL,AL
       MOV     ERRCT,AL        ;RESET ERROR COUNT
;
RCVRPT:
       MOV     CH,10           ;SET RETRY COUNT TO 10
       CALL    RECV
       JC      RCVSERR
       CMP     AL,SOH          ;START OF HEADER
       JZ      RCVSOH          ;GET IT
       OR      AL,AL
       JZ      RCVRPT
       CMP     AL,EOT          ;END OF TRANSMISSION
       STC                     ;INDICATE EOT
       JNZ     RCVSERR
       JMP     DORET           ;YES, END OF TRANSMISSION
;
RCVSERR:
       MOV     CH,1
       CALL    RECV
       JNC     RCVSERR
;
       MOV     AL,NAK          ;NAK THE BAD FRAME
       CALL    SEND
       MOV     AL,ERRCT
       INC     AL
       MOV     ERRCT,AL        ;SAVE IT BACK
;
       CMP     AL,ERRLIM               ;ERROR LIMIT REACHED ?
       JC      RCVRPT
;
; ABORT THE RECEIVE
;
RCVSABT:
       CALL    CLOSEFIL                ;SAVE WHATEVER WE GOT
       MOV     DX,OFFSET MSG6          ;UNABLE TO RECEIVE BLOCK
       CALL    PSTRING
       JMP     EXIT
;
RCVSOH:
       MOV     CH,1                    ;TIME OUT = 1 MSEC
       CALL    RECV                    ;GET A CHAR
       JC      RCVSERR
;
       MOV     DL,AL
       MOV     CH,1
       CALL    RECV
       JC      RCVSERR
;
       NOT     AL
       CMP     AL,DL
       JZ      RCVDATA
       JMP     RCVSERR
;
RCVDATA:
       MOV     AL,DL
       MOV     RCVSNO,AL
       MOV     CL,0
       MOV     DI,80H
;
RCVCHR:
       MOV     CH,1                    ;TIME OUT OF 1 MSEC
       CALL    RECV
       JC      RCVSERR
;
       MOV     BYTE PTR[DI],AL         ;SAVE THE CHARACTER
       INC     DI                      ;ADVANCE POINTER
       CMP     DI,0100H                ;END OF BUFFER ?
       JNZ     RCVCHR                  ;CONTINUE UNTIL BUFFER FULL
;
       MOV     DL,CL           ;SAVE CHECK SUM
       MOV     CH,1            ;SET TIME OUT TO 1 MSEC
       CALL    RECV
       JC      RCVSERR
       CMP     AL,DL
       JNZ     RCVSERR
;
       MOV     AL,RCVSNO
       MOV     BL,AL
       MOV     AL,SECTNO
       CMP     BL,AL
       JZ      RECVACK
       INC     AL
       CMP     BL,AL
       JZ      RCV0
       JMP     EXIT
;
RCV0:   RET
;
;
RECVACK:
       CALL    SENDACK
       JMP     RCVSECT
;
SENDACK:
       MOV     AL,ACK
SEND:   PUSH    AX              ;SAVE OUTPUT CHAR
       ADD     AL,CL
       MOV     CL,AL
;
SENDW:  MOV     DX,MODCTLP
       IN      AL,DX
       TEST    AL,20H
       JZ      SENDW
       POP     AX              ;RESTORE OUTPUT CHAR
       MOV     DX,MODDATP
       OUT     DX,AL
       RET
;
INCRSNO:
       MOV     AL,SECTNO
       INC     AL
       MOV     SECTNO,AL
       RET
;
MAKEFIL:
       MOV     DX,OFFSET FCB
       MOV     CL,22
       CALL    BDOS
       INC     AL
       JZ      DIRFULL
       JMP     DORET
;
DIRFULL:
       MOV     DX,OFFSET MSG8
       CALL    PSTRING         ;DIRCETORY FULL
       JMP     EXIT
;
CLOSEFIL:
       MOV     DX,OFFSET FCB
       MOV     CL,16
       CALL    BDOS
       INC     AL
       JZ      NOCLOSE
       JMP     DORET
;
NOCLOSE:
       MOV     DX,OFFSET MSG9          ;CAN'T CLOSE FILE
       CALL    PSTRING
       JMP     EXIT
;
WRSECT:
       MOV     DI,SECPTR
       MOV     SI,80H
       CALL    MOVE128                 ;COPY TO DISK BUFFER
       MOV     SECPTR,DI               ;SAVE NEW POINTER
;
       MOV     AL,SECINBF
       INC     AL
       MOV     SECINBF,AL
       CMP     AL,16
       JZ      WRBLOCK
       JMP     DORET
;
;       OUTPUT BUFFER TO DISK
;
WRBLOCK:
       MOV     AL,SECINBF              ;GET SECTOR COUNTER
       OR      AL,AL
       JNZ     WR0
       JMP     DORET
;
WR0:
       MOV     CL,AL                   ;CL = WRITE COUNTER
       MOV     DX,OFFSET DBUF
;
DKWRLP:
       PUSH    DX
       PUSH    BX
       PUSH    CX              ;SAVE REGISTERS
;
       MOV     CL,26
       CALL    BDOS
;
       STI                     ;ENABLE INTERRUPTS
       MOV     DX,OFFSET FCB
       MOV     CL,21
       CALL    BDOS
       CLI                     ;DISABLE INTERRUPTS
;
       POP     CX
       POP     BX
       POP     DX
       OR      AL,AL           ;TEST DISK WRITE STATUS
       JNZ     WRERR           ;DISK WRITE ERROR
;
       ADD     DX,80H          ;ADVANCE DMA POINTER
       DEC     CL
       JNZ     DKWRLP          ;CONTINUE WRITTING
;
       MOV     SECINBF,0
       MOV     DX,OFFSET DBUF
       MOV     SECPTR,DX
;
RSDMA:  MOV     DX,80H
       MOV     CL,26
       CALL    BDOS
       RET
;
WRERR:  CALL    RSDMA
       MOV     DX,OFFSET MSG10
       CALL    PSTRING         ;ERROR IN WRITTING FILE
       JMP     EXIT
;
;       RECV
;       UPON ENTRY
;               CH = # OF MSEC BEFORE TIME OUT
;
;       UPON EXIT
;               CL = UPDATED CHECKSUM
;
RECV:
       PUSH    DX
       PUSH    BX
MSEC:   MOV     BX,50000
MWTI:   MOV     DX,MODCTLP
       IN      AL,DX
       TEST    AL,01H
       JNZ     MCHAR
       DEC     BL
       JNZ     MWTI
       DEC     BH
       JNZ     MWTI
       DEC     CH
       JNZ     MSEC
       POP     BX
       POP     DX
       STC
       RET
;
MCHAR:  MOV     DX,MODDATP
       IN      AL,DX
       ADD     CL,AL           ;UPDATE CHECKSUM
       OR      AL,AL
       POP     BX
       POP     DX
       RET
;
MOVE128:
       PUSH    BX
       MOV     BL,128
MOVE:   MOV     AL,BYTE PTR [SI]
       MOV     BYTE PTR [DI],AL
       INC     SI
       INC     DI
       DEC     BL
       JNZ     MOVE
       POP     BX
       RET
;
EXIT:
       STI
       MOV     CL,0
       CALL    BDOS
;
PSTRING:
       MOV     CL,9
       CALL    BDOS
       RET
;
KEYIN:
       PUSH    BX
       PUSH    DX
       MOV     CL,CONINP
       CALL    BDOS
       POP     DX
       POP     BX
       RET
;
COUT:
       PUSH    BX
       PUSH    DX
       MOV     DL,AL
       MOV     CL,CONOUT
       CALL    BDOS
       POP     DX
       POP     BX
       RET
;
STAT:
       PUSH    BX
       PUSH    DX
       MOV     CL,CSTAT
       CALL    BDOS
       OR      AL,AL
       POP     DX
       POP     BX
       RET
;
DORET:  RET
;
BDOS:
       INT     224
       RET
;
;       DATA AREA
;
       DSEG
       ORG     5CH
FCB     RB      35              ;FILE CONTROL BLOCK
BUFF    RB      128             ;DISK WRITE BUFFER
;
       DW      0               ;ALIGN TO WORD BOUNDARY
;
RCVSNO  DB      0               ;RECEIVED SECTOR NUMBER
SECTNO  DB      0               ;CURRENT SECTOR NUMBER
ERRCT   DB      0               ;ERROR COUNT
SECPTR  DW      DBUF            ;DISK BUFFER POINTER
SECINBF DB      0               ;# OF SECTORS IN DISK BUFFER
;
DBUF    RS      4096            ;DISK BUFFER
       DW      0
;
;       MESSAGES
;
MSG1    DB      CR,LF,'MBOOT FOR THE IBM-PC',CR,LF
       DB      'Version 1.00',CR,LF,'$'

MSG2    DB      CR,LF,'No file name specified',CR,LF,'$'

MSG3    DB      CR,LF,'Terminal mode',CR,LF,LF,'$'

MSG4    DB      CR,LF,'File open, ready to receive',CR,LF,'$'

MSG5    DB      CR,LF,'Transfer complete',CR,LF,'$'

MSG6    DB      CR,LF,'Unable to receive block',CR,LF,'$'

MSG8    DB      CR,LF,'Directory full',CR,LF,'$'

MSG9    DB      CR,LF,'Cannot close file',CR,LF,'$'

MSG10   DB      CR,LF,'Error in writting file',CR,LF,'$'
;
       END