        .text
***********************************************************************
*
*       sage computer - cpm bootstrap
*
*       file:   sage.cboot.text
*       date:   16-oct-82
*       issue:  1
*
*
*       copyright (c) 1982 sage computer technology
*
*
***********************************************************************
*
*       history:
*
*       date      modification
*     ---------   ----------------------------------------------------
*     16-Oct-82   Initial release
*     20-Jan-83   Corrected logon message
*
***********************************************************************
*
*       this bootstrap is to be located in logical blocks 0 and 1 on a
*       floppy diskette.  the floppy diskette bootstrap command in the
*       prom debugger will read logical blocks 0 and 1 into memory at
*       location 400h.
*
*       the bootstrap routine first reads in the cpm directory
*       from the floppy diskette.  then the bootstrap searches the
*       directory for the file system.bios which contains the sage ii
*       computer basic input/output system routines.  once found the
*       bios code is positioned into the highest memory available.
*
*       the cpm bios interface routines cpmbios.bys are read in under
*       the sagebios.bys routines.
*
*       the cpm bdos/ccp file cpm.bys is read in at the address specified 
*       by the first long word in the file less 4 bytes.
*
*       the cpm rtl file rtl.bys is read in at teh address specified by
*       the first long word in the file less 4 bytes.
*
*       the bios initialization routine is called with the address of
*       the ccp/bdos load address and the sagebios initialization 
*       address. both bios' are initialized and the ccp is entered.
*
***********************************************************************
        
*       permanent jump vector assignments in prom

termtext: .equ  $00fe0018       ;printout text string
termcrlf: .equ  $00fe001c       ;printout carriage return & line feed
fdread: .equ    $00fe0028       ;read from floppy diskette
debug:  .equ    $00fe0030       ;debugger entry point
        
*       ram variables for debugger
memtop: .equ    $100            ;top of memory

*       ram variables for bios
biosbase: .equ  $200

        
*       constants for boot positioning
trap1:   .equ    $84            ;rtl trap
progadr: .equ    $800           ;boot addr for second half
directry: .equ   16             ;block address of directory
dentry:  .equ    32             ;directory entry size
sectrk:  .equ    8              ;sectors per track
blksiz:  .equ    2048           ;bytes per cpm block
cnvtfact: .equ   blksiz/512     ;number of sage blocks per cpm block

diradr: .equ    2               ;disk addr for dir (offset from dskstat)

        .dc.b   "BOOT"          ;this text is checked by the debugger
*                               ; bootstrap process to insure that the
*                               ; boot area has been initialized with
*                               ; a bootstrap routine.

        bra     start


* bios interface area

initbios: .dc.l 0       ;Bios initialization address
cpmadr:   .dc.l 0       ;CPM load address
drive:    .dc.w 0       ;Drive in use
sbiosadr: .dc.l 0       ;Load address of bios

* Pre load text

initmsg: .dc.b  "Booting CPM..."
        .dc.b   0


loadmsg: .dc.b   "  ...cannot load 2/2 bootstrap "
        .dc.b   0

        .even
*       file load parameter table

filename:

sagebios:
        .dc.b   "SAGEBIOSSYS"
maxexta: .dc.b   0               ;max extent loaded
loadada: .dc.w   0               ;memory address of load
         .dc.w   0
blkptra: .ds.b   160             ;pointers for load
cpmbios:
        .dc.b   "CPM     SYS"
        .dc.b   0               ;max extent loaded
        .dc.w   0               ;memory address of load
        .dc.w   0
        .ds.b   160             ;pointers for load

flen:   .equ    cpmbios-sagebios ;file entry length
maxext: .equ    maxexta-sagebios ;max extent found in directory
loadad: .equ    loadada-sagebios ;load address of bios
blkptr: .equ    blkptra-sagebios ;block vectors


*                               ;the debugger transfers control to
*                               ; offset 4 in this file.
start:
        
*       output initial boot message
        
        jsr     termcrlf
        lea     initmsg,a0
        jsr     termtext
        jsr     termcrlf
        
*       load second half of boot routine

        move.w  #2,-(a7)        ;information in block 2
        movea.w #progadr,a0     ;disk status information area
        move.l  a0,-(a7)
        move.l  #1024,-(a7)     ;input 4 bytes
        move.w  14(a7),-(a7)    ;drive select
        jsr     fdread          ;read disk
        beq     loadstat
        lea     loadmsg,a0      ;printout "cannot read disk status"
        jsr     termtext
        jsr     debug

*       read disk & directory status information

loadstat:
        move.w  #4,-(a7)        ;information in block 2
        lea     dskstat,a0      ;disk status information area
        move.l  a0,-(a7)
        move.l  #4,-(a7)        ;input 1k bytes
        move.w  14(a7),-(a7)    ;drive select
        jsr     fdread          ;read disk
        bne     badstat

*       read in cpm directory
        
        lea     dskstat,a1      ;disk status information
        move.w  diradr(a1),d0   ;first track for directory
        mulu    #sectrk,d0      ;compute block address
        move.w  d0,diradr(a1)   ;save block address
        move.w  d0,-(a7)        ;set block to read
        lea     dirbuf,a0       ;set directory buffer
        move.l  a0,-(a7)
        move.w  (a1),d0         ;trasnfer length
        mulu    #dentry,d0      ;bytes / entry
        move.l  d0,-(a7)
        move.w  14(a7),-(a7)    ;drive select
        jsr     fdread
        bne     baddir

*       search directory for bios.bys, cpmbios.bys, cpm.bys, rtl.bys
*       and set up block load vectors

        lea     dirbuf,a0       ;buffer address
        lea     dskstat,a3      ;set number of directory entries - 1
        move.w  (a3),d6
        subq.w  #1,d6
b5:
        lea     filename,a1     ;set address of files to be loaded
        tst.b   (a0)            ;entry in use?
        bne     b4              ;no - move to next entry
        moveq   #1,d5           ;number of files - 1
b1:
        moveq   #10,d4          ;number of char in file name - 1
b2:
        move.b  1(a0,d4.w),d0   ;file name from directory
        cmp.b   0(a1,d4.w),d0   ; with file being requested
        dbne    d4,b2           ;repeat for all chars or mismatch found
        bne     b6              ;file does not match move to next file
        clr.l   d0
        move.b  12(a0),d0       ;max extent from dir > last saved
        cmp.b   maxext(a1),d0
        ble     b8              ;no
        move.b  d0,maxext(a1)   ;set new max extent
b8:
        moveq   #16,d4          ;number of bytes of pointers per extent
        mulu    d0,d4           ;compute offset for extents pointers
        moveq   #16,d3          ;offset to pointers in extent
b7:
        move.b  1(a0,d3.w),d0   ;high byte of block address
        asl.w   #8,d0           ;move to high byte
        move.b  0(a0,d3.w),d0   ;low byte of block address
        tst.w   d0              ;entry in use?
        beq     b9              ;no
        mulu    #cnvtfact,d0    ;compute block address
        add.w   diradr(a3),d0
        move.w  d0,blkptr(a1,d4.w)  ;save block address
b9:
        addq.w  #2,d4           ;move to next entry in load table
        addq.w  #2,d3           ;move to next entry in directory
        cmpi.w  #32,d3          ;process last extent
        bne     b7              ;no
        bra.b   b4              ;dont process next file as found match
b6:
        adda.w  #flen,a1        ;move to next file
        dbf     d5,b1           ;process next file
b4:
        adda.w  #dentry,a0      ;move to next directory entry
        dbf     d6,b5           ;repeat untill all entries processed
        
*       load requested files into memory
        
        movea.l memtop,a2       ;set last memory address
        move.w  4(a7),drive     ;drive
        
        lea     sagebios,a0     ;load first file sagebios.bys
        clr.l   d5              ;clear word for extent
        move.b  maxext(a0),d5   ;find last extent to load
        move.l  d5,d6           ;calculate file size of previous block
        mulu    #(blksiz*8),d6
        addq.l  #1,d5
        mulu    #16,d5          ;address offset of last extent
c1:
        tst.w   blkptr-2(a0,d5.w) ;extent in use
        beq     c2              ;no
        addi.l  #blksiz,d6      ;up block size
c2:
        subq.w  #2,d5           ;move to previous entry
        bgt     c1              ;repeat untill each entry tested
        suba.l  d6,a2           ;load address of file
        move.l  a2,sbiosadr     ;Bios load address
        move.l  a2,loadad(a0)   ;save load address
        clr.l   d5              ;load from beginning
        bsr     setload
        bne     badsageb
        move.l  a2,a3           ;compute init addr
        adda.w  8(a2),a3
        move.l  a3,initbios     ;save in bios interface
        suba.w  6(a2),a2        ;compute TOM including buffers
        
        lea     cpmbios,a0      ;load second file cpmbios.bys
        bsr     relload
        bne     badcpmb

*       setup jump vectors and transfer to cpm bios

        movea.l loadad(a0),a1
        move.l  a1,cpmadr       ;save CPM load address 
        jmp     (a1)            ;initialization

*       binary load routine

setload:
        movem.l d4/d6/a1/a2,-(a7)  ;save registers
        tst.w   blkptr(a0)      ;file found
        beq     c100            ;file not found in directory
c3:
        move.w  d5,d4           ;Set working index
        move.l  #blksiz,d7      ;set max block size to read
n1:
        move.w  blkptr(a0,d4.w),d0 ;Get starting block
        addq.w  #2,d4           ;Move to next block
        tst.w   blkptr(a0,d4.w) ;Next block in use?
        beq     n2              ;No- process blocks accum
        sub.w   blkptr(a0,d4.w),d0 ;Are blocks contiguous?
        addq.w  #cnvtfact,d0
        bne     n2              ;no read current data requested
        add.l   #blksiz,d7      ;up read count one block
        cmp.l   d7,d6           ;Reading too many blocks?
        bgt     n1              ;More to go
n3:
        move.l  d6,d7           ;Forse read of entire request
n2:
        cmp.l   d7,d6           ;Requesting too much data
        blt     n3              ;Yes- convert to max size
c5:
        bsr     readbloc        ;read block
        bne     c6                ;return it
        sub.w   d7,d6           ;reduce remaing bytes to read
        beq     c6              ;done
        adda.l  d7,a2           ;move to next load address
        move.w  d4,d5           ;move to next load pointer
        tst.w   blkptr(a0,d5.w) ;next entry in use
        bne     c3              ;yes load it
c100:
        moveq   #-1,d0          ;set error
c6:
        move.w  d4,d5                 ;Move to next drive
        tst.w   d0              ;set error status
        movem.l (a7)+,d4/d6/a1/a2  ;restore registers
        rts

*       reads cpm block

readbloc:
        movem.l d1-d7/a0-a2,-(a7) ;save file size, drive,
*                                  ;packet, load address
        move.w  blkptr(a0,d5.w),-(a7) ;block address
        move.l  a2,-(a7)              ;load address
        move.l  d7,-(a7)              ;transfer size
        move.w  drive,-(a7)           ;drive
        jsr     fdread                ;read disk
        movem.l (a7)+,d1-d7/a0-a2 ;restore file size, drive
*                                  ;packet, load address
        rts
        
* Relocatable loader

relload:
        move.l  a2,-(a7)        ;save load address

* Load & verify file header

        lea     dirbuf,a2       ;load address
        move.w  #headln,d7      ;bytes to load
        clr.l   d5              ;start at beginning of file
        bsr     readblock
        move.l  (a7)+,a2        ;restore load address
        lea     dirbuf,a1       ;Set address of header
        lea     rlerr1,a6       ;Illegal file type
        cmpi.w  #$601a,(a1)     ;contiguous files only
        bne     reterr          ;Illegal file type
        
* Load CPM and bios interface

        move.l  2(a1),d6        ;Test size
        add.l   6(a1),d6        ;Data size
        move.l  d6,transiz      ;save transfer size
        suba.l  d6,a2           ;Load address
        suba.l  10(a1),a2       ;BSS size
        move.l  a2,loadad(a0)   ;Save load address
        moveq   #headln,d0      ;Compute load parameters
        add.l   d0,d6           ;  number of bytes to load
        suba.w  d0,a2           ;must include header
        clr.l   d5              ;Load from beginning of file
        bsr     setload         ;Load cpm & interface
        lea     rlerr2,a6       ;Cannot load cpm
        bne     reterr

* Load header information

        lea     relbit,a2       ;load cpmload address
        lea     dirbuf,a1       ;Set to header
        move.l  d6,d5           ;Calculate disk block
        add.l   $e(a1),d5       ;skip symbol table
        divu    #blksiz,d5
        add.w   d5,d5           ;Address offset by 2
        move.l  d5,d0           ;move to begining of sector
        clr.w   d0
        swap    d0
        add.l   d0,d6           ;Total bytes to read
        bsr     setload         ;Load relocation info
        lea     rlerr2,a6       ;Cannot load cpm
        bne     reterr
        
* Relocate CPM and bios

        move.l  loadad(a0),a2   ;get cpm memory address
        move.l  a2,d4           ;Text start address
        sub.l   $12(a1),d4
        move.l  d4,d3           ;Data base address
        add.l   2(a1),d3
        move.l  d3,d2           ;BSS base address
        add.l   6(a1),d2
        clr.l   d1              ;Set to word load
        lea     locfun,a3       ;Beginning of relocation routines
        lea     jumtab,a4       ;Jump table to reloc routines
        lea     relbit,a1       ;Start of relocation info
        swap    d5
        adda.w  d5,a1
        move.l  transiz,d6      ;Set number of bytes to relocate
        lsr.w   #1,d6           ;convert # bytes to # words
        subq.w  #1,d6           ;Number of words to relocate-1
i1:
        move.w  (a1)+,d0        ;relocation bits
        andi.w  #7,d0           ;save only relocation bits
        move.b  0(a4,d0.w),d0   ;get offset
        jmp     0(a3,d0.w)      ;process routine
jumtab:
        .dc.b   next-locfun     ;00 - No relocation
        .dc.b   data-locfun     ;01 - Data segment based
        .dc.b   text-locfun     ;02 - Text segment based
        .dc.b   bss-locfun      ;03 - BSS segment based
        .dc.b   undef-locfun    ;04 - Undefined
        .dc.b   long-locfun     ;05 - Upper part of longword
        .dc.b   next-locfun     ;06 - 16 bit PC relative
        .dc.b   next-locfun     ;07 - Instruction
locfun:
undef:
        lea     rlerr3,a6       ;Undefined reference
        bra     reterr
long:
        moveq   #1,d1           ;Set long reference
        bra     next
text:
bss:
data:
reloc:
        tst.b   d1              ;Long reference
        bne     relocl          ; Yes process it
        clr.l   d0              ;force to zero
        move.w  (a2),d0         ;get word
        add.l   d4,d0           ;compute address
        swap    d0              ;overflow 16 bits
        lea     rlerr4,a6       ;Overflow word boundry
        tst.w   d0
        bne     reterr
        swap    d0              ;Store upgraded word
        move.w  d0,(a2)
        bra     next2
relocl:
        add.l   d4,-2(a2)       ;Bias long word
next2:
        clr.l   d1              ;Clear word flag
next:
        addq.l  #2,a2           ;Move 1/2 longword
next1:
        dbf     d6,i1          ;repeat for entire file
        lea     dirbuf,a1      ;Get size of bss area
        move.l  10(a1),d5
        asr.l   #2,d5          ;Convert to long ref
clrbss:
        clr.l   (a2)+          ;Clear bss area
        dbf     d5,clrbss
        clr.l   d0             ;report all ok
rlerr:
        rts
reterr:
       moveq    #-1,d0         ;Set error
       rts
        

*       could not read disk status area

badstat:
        lea     statmsg,a0      ;printout "cannot read disk status"
        bra.b   prterr
        
*       could not read directory 

baddir:
        lea     dirmsg,a0       ;printout "cannot read directory "
        bra.b   prterr
        
*       could not load sage bios

badsageb:
        lea     sagebmsg,a0     ;printout "cannot load sagebios.sys "
        bra.b   prterr
        
*       could not load cpm bios

badcpmb:
        movea.l a6,a0           ;printout "cannot load cpmbios.sys
*       printout error message

prterr: jsr     termcrlf
        jsr     termtext
        jmp     debug


*       messages

dirmsg: .dc.b   "Cannot read directory ",0
statmsg: .dc.b   "Cannot read disk status ",0
sagebmsg: .dc.b  "Cannot load CPM.SYS ",0
rlerr1:  .dc.b   "Illegal file type: CPM.SYS",0
rlerr2:  .dc.b   "Cannot load: CPM.SYS",0
rlerr3:  .dc.b   "Undefined variable in: CPM.SYS",0
rlerr4:  .dc.b   "Word overflow in: CPM.SYS",0
        
        .even

*       directory buffer (entire buffer loaded here

dskstat: .ds.b   4              ;disk status info directory len,addr
transiz: .dc.l   0              ;Relocation transfer size

*                               ;directory buffer
headln: .equ     $1c            ;Header length
dirbuf: .ds.b    headln         ;Directory buffer
relbit: .dc.w    0              ;Start of reloctation
        .data
junk:   .dc.l    0,0,0,0        ;dummy data area
        .bss
        .ds.b    2048           ;directory area
        .end
