;               NEW BIOS BOOT MAINTENENCE PROGRAM
;
;PURPOSE
;               THIS PROGRAM PROVIDES THE MEANS TO SYSGEN A NEW
;       BIOS CP/M SYSTEM.  IT WRITES THE BOOT, BDOS AND BIOS TO
;       THE SYSTEM TRACKS OF DISK B.  IT CAN BE USED FOR MULTIPLE
;       GENERATIONS WITH AN OPTIONAL SYSTEM OBTAINED FROM DRIVE
;       A.
;DATE WRITTEN
;       MAY 23, 1980 WITH THE HELP OF S.J. SINGER'S PGEN PROGRAM
;OUTLINE
;       TO GENERATE A NEW SYSTEM, DO THE FOLLOWING:
;               DDT CPMXX.COM<RETURN>   (XX = MEMORY SIZE)
;               -M1180,1F80,980
;               -I21BIOS.HEX
;               -H1780,NNNN             (NNNN = START OF BIOS,
;                                               SEE ASSEMBLY LISTING.)
;                 XXXX YYYY
;               -RYYYY                  (LOADS BIOS)
;               -^C
;               NPGEN<RETURN>
;               GET SYSTEM (Y/N)? N
;               PUT SYSTEM (Y/N)? Y
;            ETC.
;       TOGENERATE FROM AN OLD SYSTEM, JUST RUN NPGEN TELLING
;       IT TO GET THE SYSTEM FROM DRIVE A.
;
;
       MACLIB  MACRO                   ;INCLUDE MACROS
FALSE   EQU     00H
TRUE    EQU     NOT FALSE
;
SPOOL   EQU     FALSE                   ;TRUE FOR KLH SPOOLER
;
;
MSIZE   EQU     61                      ;MEMORY SIZE IN KBYTES
CBASE   EQU     (MSIZE-20)*1024
CPMB    EQU     CBASE+3400H             ;START OF CP/M
BDOS    EQU     CPMB+0800H              ;START OF BDOS (ROUNDED TO
;                                        EVEN SECTOR BOUNDARY)
BIOS    EQU     CPMB+1600H              ;START OF BIOS
       IF      SPOOL
JMPDSP  EQU     033H+9  ;DISPLACEMENT TO SPECIAL BIOS JUMPS
       ENDIF
       IF      NOT SPOOL
JMPDSP  EQU     033H    ;DISPLACEMENT TO SPECIAL BIOS JUMPS
       ENDIF
TPA     EQU     100H
RDCON   EQU     1
WRBUF   EQU     9
START   EQU     900H
FBYTE   EQU     97FH                    ;DISK FORMAT BYTE LOCATION
;
;
;
       ORG     TPA
;
;
       LXI     H,0                     ;ZERO HL
       DAD     SP                      ;GET OLD STACK POINTER
       SHLD    OLDSTK                  ;AND SAVE IT
       LHLD    1
       SHLD    SAVADR
       LXI     SP,STACK
MAIN:   PRINT   <CR,LF,LF,'      NEW BIOS SYSGEN PROGRAM VERS 2.0'>
       PRINT   <CR,LF,LF>
       CALL    GET$BOOTER
       CALL    PUT$BOOTER
       JMP     REBOOT
;
;
SELDSK:                         ;SELECT DISK
       PUSH    H
       LHLD    1
       MVI     L,00H+JMPDSP
       XTHL
       RET
;
;
HOME:                           ;HOME DISK
       PUSH    H
       LHLD    1
       MVI     L,03H+JMPDSP
       XTHL
       RET
;
;
SEEK:                           ;SEEK TRACK
       PUSH    H
       LHLD    1
       MVI     L,06H+JMPDSP
       XTHL
       RET
;
;
READ:                           ;READ A SECTOR
       PUSH    H
       LHLD    1
       MVI     L,09H+JMPDSP
       XTHL
       RET
;
WRITE:                          ;WRITE A SECTOR
       PUSH    H
       LHLD    1
       MVI     L,0CH+JMPDSP
       XTHL
       RET
;
;
GET$BOOTER:                     ;MAYBE A BOOTER COMES IN
       PRINT   <CR,LF,LF,'GET SYSTEM (Y/N) '>
       CHARIN
       CPI     'Y'
       RNZ                     ;RETURN IF NOT YES
       PRINT   <CR,LF,'READING SYSTEM FROM DRIVE A, TYPE RETURN '>
       CHARIN
       CPI     3               ;IS IT CONTROL C
       JZ      MAIN
       MVI     A,0FFH
       STA     RDFLG           ;SET FLAG
       CALL    DO$READ
       RET                     ;DONE
;
;
PUT$BOOTER:                     ;DOES THE BOOTER GO OUT?
       PRINT   <CR,LF,'PUT SYSTEM (Y/N) '>
       CHARIN
       CPI     3               ;CONTROL C
       JZ      MAIN
       CPI     'Y'
       RNZ                     ;EXIT IF NO
PUT$AGAIN:                      ;ELSE START TO PUT
       PRINT   <CR,LF,'WRITING SYSTEM TO DRIVE B, TYPE RETURN '>
       CHARIN
       CPI     3               ;CHECK FOR CONTROL C
       JZ      MAIN
       CALL    READ$FMT                ;READ T 0 S 1 FROM DESTINATION DISK
       CALL    MOVE$BOOT
       PRINT   <CR,LF,'THE DESTINATION DISK FORMAT IS - '>
       CALL    WRITE$FMT
       LDA     FMFLG           ;CHECK IF KNOWN FORMAT
       ORA     A
       CNZ     GET$FORMAT
;               *** TEMPORARY ***
       LDA     FBYTE           ;MUST BE SINGLE DENSITY
       CPI     020H            ;128-BYTE SECTORS.
       JNZ     FMT$ERR
;               *** END OF TEMPORARY ***
       CALL    DO$WRITE        ;NOW PUT IT OUT
       PRINT   <CR,LF,LF,'AGAIN (Y/N) '>
       CHARIN
       CPI     'Y'
       JZ      PUT$AGAIN       ;WRITE IT AGAIN
       RET                     ;ALL DONE
;               *** TEMPORARY ***
FMT$ERR:
       PRINT   <CR,LF,'*** FORMAT MUST BE SD 128... ***'>
       RET
;               *** TEMPORARY ***
;
;
REBOOT:                         ;PULL BACK CP/M
       PRINT   <CR,LF,LF,'REBOOTING CP/M, TYPE RETURN '>
       CHARIN
       MVI     C,0
       CALL    SELDSK          ;SELECT DRIVE 0
       LHLD    SAVADR
       SHLD    1
       LHLD    OLDSTK          ;RECOVER ORIGINAL STACK POINTER
       SPHL                    ;RESTORE IT
       JMP     0



;************************************************
;*      READ IN ALL SYSTEM TRACKS ASSUMING      *
;*      SD 128-BYTE SECTORS FOR ALL.            *
;************************************************

DO$READ:                        ;READ IN THE BOOTER
       MVI     C,0             ;SELECT DISK
       CALL    SELDSK
       CALL    TIME
       CALL    HOME            ;MAKE SURE IT'S INITIALIZED
       LXI     B,1             ;B=TRK C=SECTOR
       LXI     H,START         ;START OF AREA.
DR$LOOP:                                ;THEN PUT IT OUT
       PUSH    B
       PUSH    H
       PUSH    B
       MOV     C,B             ;SEEK TRACK.
       CALL    SEEK
       POP     B
       POP     H               ;RESTORE BUFFER PTR.
       PUSH    H
       CALL    READ            ;READ A SECTOR.
       POP     H
       POP     B
       LXI     D,128           ;BUMP BUFFER PTR.
       DAD     D
       INR     C               ;BUMP SECTOR PTR.
       MOV     A,C             ;END OF TRACK?
       CPI     26+1
       JC      DR$LOOP         ;...NO.
       MVI     C,1             ;RESET SECTOR.
       INR     B               ;BUMP TRACK PTR.
       MOV     A,B             ;END OF SYSTEM TRACKS?
       CPI     1+1
       JC      DR$LOOP         ;...NO.
       RET



;************************************************
;*      WRITE OUT ALL SYSTEM TRACKS ASSUMING    *
;*      SD 128-BYTE SECTORS FOR ALL.            *
;************************************************

DO$WRITE:                       ;READ IN THE BOOTER
       MVI     C,1             ;SELECT DISK
       CALL    SELDSK
       CALL    TIME
       CALL    HOME            ;MAKE SURE IT'S INITIALIZED
       LXI     B,1             ;B=TRK C=SECTOR
       LXI     H,START         ;START OF AREA.
DW$LOOP:                                ;THEN PUT IT OUT
       PUSH    B
       PUSH    H
       PUSH    B
       MOV     C,B             ;SEEK TRACK.
       CALL    SEEK
       POP     B
       POP     H               ;RESTORE BUFFER PTR.
       PUSH    H
       CALL    WRITE           ;READ A SECTOR.
       POP     H
       POP     B
       LXI     D,128           ;BUMP BUFFER PTR.
       DAD     D
       INR     C               ;BUMP SECTOR PTR.
       MOV     A,C             ;END OF TRACK?
       CPI     26+1
       JC      DW$LOOP         ;...NO.
       MVI     C,1             ;RESET SECTOR.
       INR     B               ;BUMP TRACK PTR.
       MOV     A,B             ;END OF SYSTEM TRACKS?
       CPI     1+1
       JC      DW$LOOP         ;...NO.
       RET
;
;
MOVE$BOOT:                      ;MOVE BOOT CODE TO 900H
       LXI     H,900H          ;POINT TO MEMORY
       LXI     D,BOOT          ;POINT TO BOOT CODE
       MVI     C,ENDBOOT-BOOT  ;LENGTH OF CODE
MLOOP:  LDAX    D               ;GET A BYTE
       MOV     M,A             ;STORE IT
       INX     H
       INX     D               ;INCR POINTERS
       DCR     C               ;BYTE COUNT
       JNZ     MLOOP
       MVI     A,0C7H          ;RESTART ZERO INSTRUCTION
       STA     97DH            ;PUT IT IN BUFFER TOO
       RET
;
GET$FORMAT:                     ;READ DISK FORMAT FROM CONSOLE AND SET FBYTE
       PRINT   <CR,LF,'DO YOU WISH TO WRITE A FMT CODE ON THE DISK? (Y/N) '>
       CHARIN
       CPI     'Y'
       RNZ
GF1:    PRINT   <CR,LF,'IS DISK SINGLE DENSITY? (Y/N) '>
       CHARIN
       CPI     'Y'
       MVI     A,20H           ;SINGLE DENSITY CODE
       JZ      GF2
       MVI     A,10H           ;DOUBLE DENSITY CODE
GF2:    STA     FBYTE           ;SAVE IN FBYTE
       PRINT   <CR,LF,'DOES DISK HAVE 128 BYTE SECTORS? (Y/N) '>
       CHARIN
       CPI     'Y'
       MVI     B,0             ;128 BYTE CODE
       JZ      GFE
       PRINT   <CR,LF,'DOES DISK HAVE 256 BYTE SECTORS? (Y/N) '>
       CHARIN
       CPI     'Y'
       MVI     B,1             ;256 BYTE CODE
       JZ      GFE
       PRINT   <CR,LF,'DOES DISK HAVE 512 BYTE SECTORS? (Y/N) '>
       CHARIN
       CPI     'Y'
       MVI     B,2             ;512 BYTE CODE
       JZ      GFE
       PRINT   <CR,LF,'DOES DISK HAVE 1024 BYTE SECTORS? (Y/N) '>
       CHARIN
       CPI     'Y'
       MVI     B,3             ;1024 BYTE CODE
       JZ      GFE
       PRINT   <CR,LF,'*** DEFAULTING TO 128 BYTE SECTORS.'>
       MVI     B,0
GFE:    LDA     FBYTE
       ORA     B               ;OR IN THE BYTE CODE
       STA     FBYTE
       RET
;
READ$FMT:                       ;READ FORMAT CODE FROM DESTINATION DISK
       MVI     C,1             ;SELECT THE DISK
       CALL    SELDSK
       CALL    TIME
       CALL    HOME
       CALL    TIME
       LXI     H,START
       MVI     C,1
       CALL    READ            ;READ DRIVE B TRACK 0 SECTOR 1
       RET
;
WRITE$FMT:                      ;DECODE FORMAT BYTE AND DISPLAY
       MVI     A,0FFH
       STA     FMFLG           ;SET FLAG TO KNOWN FORMAT
       LDA     FBYTE
       CPI     20H
       JNZ     WF2
       PRINT   <'SINGLE DENSITY 128 BYTE SECTORS',CR,LF>
       RET
WF2:    CPI     22H
       JNZ     WF3
       PRINT   <'SINGLE DENSITY 512 BYTE SECTORS',CR,LF>
       RET
WF3:    CPI     23H
       JNZ     WF4
       PRINT   <'SINGLE DENSITY 1024 BYTE SECTORS',CR,LF>
       RET
WF4:    CPI     10H
       JNZ     WF5
       PRINT   <'DOUBLE DENSITY 128 BYTE SECTORS',CR,LF>
       RET
WF5:    CPI     11H
       JNZ     WF6
       PRINT   <'DOUBLE DENSITY 256 BYTE SECTORS',CR,LF>
       RET
WF6:    CPI     12H
       JNZ     WF7
       PRINT   <'DOUBLE DENSITY 512 BYTE SECTORS',CR,LF>
       RET
WF7:    CPI     13H
       JNZ     WF8
       PRINT   <'DOUBLE DENSITY 1024 BYTE SECTORS',CR,LF>
       RET
WF8:    PRINT   <'NO FORMAT CODE ON DISK',CR,LF>
       XRA     A
       STA     FMFLG           ;SET FLAG FOR NO FORMAT
       RET
;
TIME:   LXI     B,3000H         ;TIMING DELAY
DELAY:  DCX     B
       MOV     A,B
       ORA     C
       JNZ     DELAY
       RET
;
OLDSTK: DW      0               ;STORAGE FOR OLD STACK POINTER
SAVADR: DW      0
RDFLG   DB      0                       ;INDICATES BOOTER READ FROM DISK
FMFLG   DB      0                       ;INDICATES FORMAT FLAG ON DEST DISK
;
;
;
;               BDOS/BIOS BOOT LOADER
;
;THIS IS A MODIFICATION OF TARBELL'S BOOT LOADER DESIGNED TO RUN AT 0H
;THE CODE IS MOVED TO 900H BY PGEN WITH THE PROPER FORMAT CODE IN THE
;LAST BYTE FOR WRITING ON THE SYSTEM TRACK OF A PASCAL DISK.
;THE PROGRAM LOADS 51 SECTORS FROM DISK WHEN EXECUTED STARTING WITH
;TRACK 0 SECTOR 2.
;
;
DISK    EQU     0F8H            ;BASE ADDR FOR DISK I/O PORTS
DCOM    EQU     DISK
DSTAT   EQU     DISK
TRACK   EQU     DISK+1
SECT    EQU     DISK+2
DDATA   EQU     DISK+3
WAIT    EQU     DISK+4
NS      EQU     51              ;NUMBER OF SECTORS TO LOAD
;
OFFSET  EQU     00H             ;BOOT OFFSET
;
;
;
BOOT:
       MVI     E,10
BLOOP:
       LXI     SP,100H
       MVI     D,NS            ;D CONTAINS SECTOR COUNT
       LXI     H,BDOS          ;STARTING LOAD ADDR FOR PROGRAM
       MVI     C,2             ;SECTOR NUMBER.
RNTRK:  MVI     B,4             ;FOR HEAD LOAD.
RNSEC:  CALL    BREAD-BOOT+OFFSET ;READ A SECTOR.
       DCR     D               ;IF DONE.
       JZ      BIOS            ;GO TO CP/M.
       MVI     B,0             ;FOR NO HEAD LOAD.
       INR     C               ;INCR TRACK COUNT.
       MOV     A,C             ;DONE WITH
       CPI     26+1            ;THIS TRACK?
       JC      RNSEC-BOOT+OFFSET ;...NO.
       MVI     A,053H          ;ISSUE STEP COMMAND.
       OUT     DCOM
       IN      WAIT            ;WAIT UNTIL DONE.
       MVI     C,1             ;RESET SECTOR NUMBER.
       JMP     RNTRK-BOOT+OFFSET
;
BREAD:
       MOV     A,C             ;SET SECTOR.
       OUT     SECT
       MVI     A,088H          ;GET READ CMD.
       ORA     B               ;GET HEAD LOAD BIT.
       OUT     DCOM            ;ISSUE IT.
RLOOP:
       IN      WAIT            ;WAIT FOR DRQ
       ORA     A
       JP      CHECK-BOOT+OFFSET ;JUMP IF DONE
       IN      DDATA
       MOV     M,A
       INX     H
       JMP     RLOOP-BOOT+OFFSET
;
CHECK:
       IN      DSTAT           ;READ STATUS.
       ANI     09DH
       RZ
       DCR     E
       JNZ     BLOOP-BOOT+OFFSET
       STA     EC-BOOT+OFFSET
HERE:
       JMP     HERE-BOOT+OFFSET
;
EC:     DS      1
ENDBOOT:
       DS      128             ;LOTS OF SPACE FOR STACK
STACK:  EQU     $
       END