    title   IMAGE BUILDER
*
* BUILD: Build an executable or downloadable file from ASM output
*
* ?COPY.TXT 1983-2005 Dave Dunfield
* **See COPY.TXT**.
*
* DIRECTORY STRUCTURE
    ORG 0
DPREFIX RMB 8
DNAME   RMB 8
DTYPE   RMB 3
DDADR   RMB 2
DRADR   RMB 2
DATTR   RMB 1
*
    ORG OSRAM       DOS APPLICATION PROGRAM SPACE
* PROGRAM ENTRY
BUILD   CMPA    #'?'        QUERY OPERAND?
    BNE QUAL        NO, LOOK FOR QUALIFIER
    SSR 25
    FCCZ    'Use: BUILD[/KEEP/MHX/QUIET] <object file>'
    SSR 0
*
* PARSE FOR QUALIFIERS
*
QUAL    LDA ,Y      GET CHAR FROM COMMAND LINE
    CMPA    #'/'        IS IT A QUALIFIER?
    BNE MAIN        NO, GET PARAMETERS
    LDX #QTABLE     POINT TO QUALIFIER TABLE
    SSR 18      LOOK IT UP
    CMPB    #QMAX       IS IT IN RANGE
    BHS QERR        IF SO, IT'S INVALID
    LDX #QFLAGS     POINT TO QUALIFIER FLAGS
    CLR B,X     SET THE FLAG
    BRA QUAL        LOOK FOR ANOTHER QUALIFIER
QERR    SSR 24      DISPLAY MESSAGE
    FCCZ    /Invalid qualifier: '/
    LDA ,Y+     GET CHARACTER
DSQU1   SSR 33      DISPLAY
    LDA ,Y+     GET NEXT CHAR
    BEQ GOABO       NULL IS DELIMITER
    CMPA    #'/'        START OF ANOTHER QUALIFIER?
    BEQ GOABO       IF SO, QUIT
    CMPA    #' '        SPACE?
    BEQ GOABO       IF SO, QUIT
    CMPA    #$0D        END OF LINE?
    BNE DSQU1       NO, KEEP DUMPING
GOABO   SSR 25      DISPLAY MESSAGE
    FCB $27,0       CHARACTERS TO DISPLAY
    LDA #1      INVALID OPERAND RETURN CODE
ABORT   SSR 0       RETURN TO OS
* MAIN BUILD PROGRAM
MAIN    SSR 11      GET FILENAME
    BNE ABORT       ERROR, EXIT
    STX FILPTR      SAVE FILE POINTER
    LDD #'OB'       FIRST TWO CHARS OF TYPE
    STD ,X      SAVE
    LDA #'J'        NEXT CHAR OF TYPE
    STA 2,X     SAVE IN FILENAME
    LDA MHX     ARE WE DOING A 'MHX' FILE?
    LBEQ    DOMHX       IF SO, BUILD IT
    SSR 70      LOOKUP FILE
    BNE ABORT       GET UPSET IF ERROR
    LDD DDADR,X     GET DISK ADDRESS
    STD >SECTOR     SAVE FOR LATER
    LDX #WRKSPC     POINT TO WORKSPACE
    STX >OFFSET     SAVE OFFSET
    SSR 92      READ A SECTOR
    BNE ABORT       ERROR
* READ OBJECT FILE, AND BUILD EXECUTABLE IMAGE IN RAM
    LDX #0      ASSUME OFFSET ZERO
NXCHR   LBSR    RDCHR       READ CHARACTER FROM FILE
    CMPA    #$CF        IS IT A SPECIAL CASE
    BNE NORCHR      NO, IT'S NORMAL
    LBSR    RDCHR       GET NEXT CHARACTER
    CMPA    #$CF        IS IT XPARENT '$CF'?
    BEQ NORCHR      IF SO, IT'S OK
    TSTA            IS THIS THE END?
    BEQ SAVMOD      IF SO, SAVE IT
    DECA            TEST FOR SET ADDRESS
    BNE INVCMD      NO, IT'S INVALID
* ADDRESS CHANGE RECORD, SET UP NEW ADDRESS
    LBSR    RDCHR       GET HIGH BYTE
    TFR A,B     SAVE IN B
    LBSR    RDCHR       GET NEXT BYTE
    EXG A,B     SWAP HIGH AND LOW
* IF CODE HAS ALREADY BEEN OUTPUT, CALCULATE 'X'=
* THE ADDRESS WITHIN THE OUTPUT MODULE. OTHERWISE
    TST SETADR      HAS RUN ADDRESS BEEN FIXED YET?
    BEQ NXCH1       YES, DO NOT CHANGE
    SUBD    RUNADR      CONVERT TO OFFSET
    ADDD    #MODBUF     OFFSET TO MUDULT
NXCH1   TFR D,X     'X' = POINTER TO BUFFER
    BRA NXCHR       AND GET NEXT CHAR
* NORMAL CHARACTER, SAVE IT IN THE MODULE. IF THIS IS
* THE FIRST CHARACTER OUTPUT, ESTABLISH FILE RUN ADDRESS
* AND SET UP POINTER INTO MODULE
NORCHR  LDB SETADR      HAS FLAG BEEN SET?
    BNE NORCH1      YES, ITS HAS
    STX RUNADR      SET UP RUN ADDRESS
    LDX #MODBUF     BEGIN AT START OF MODULE
    DEC SETADR      INDICATE RUN ADDRESS
NORCH1  STA ,X+     SAVE IN GENERATING MODULE
    CMPX    HIADR       ARE WE ABOVE OUR HIGHEST?
    BLS NXCHR       NO, GET NEXT CHARACTER
    STX HIADR       SAVE THIS AS OUR NEW HIGH ADDRESS
    BRA NXCHR       READ NEXT
* INVALID COMMAND
INVCMD  SSR 25      MESSAGE
    FCCZ    'Invalid OBJ file format'
    LDA #99     RETURN CODE
    SSR 0       EXIT
* MODULE IS BUILT, SAVE IT OUT
SAVMOD  LDA KEEP        DO WE KEEP FILE?
    BEQ SAVM1       YES, DON'T DELETE
    SSR 73      DELETE THE FILE
SAVM1   LDX FILPTR      GET FILE POINTER
    LDD #'EX'       FIRST TWO CHARS
    STD ,X      WRITE IT OUT
    STA 2,X     SET LAST CHAR
* DISPLAY STATS ON THE OUTPUT FILE
    LDA QUIET       BEING QUIET?
    BEQ SAVM2       YES, NO DISPLAY
    SSR 24      DISPLAY MESSAGE
    FCCZ    'Load addr= $'  MESSAGE
    LDD RUNADR      GET RUN ADDRESS
    SSR 27      DISPLAY
    SSR 22      NEW LINE
    SSR 24      OUTPUT MESSAGE
    FCCZ    'File Size= '
    LDD HIADR       GET HIGHEST ADDRESS
    SUBD    #MODBUF     CONVERT TO OFFSET
    SSR 26      DISPLAY IN DECIMAL
    SSR 25      OUTPUT MESSAGE
    FCCZ    ' Bytes.'   
SAVM2   LDD HIADR       GET HIGHEST ADDRESS
    SUBD    #MODBUF     CONVERT TO OFFSET
    LSRA            CONVERT TO SECTOR ADDR
    INCA            ADD ONE FOR PARTIAL SECTORS
    TFR A,B     COPY TO LOW BYTE
    CLRA            CLEAR HIGH BYTE
    LDX #MODBUF     POINT TO MODULE BUFFER
    SSR 54      WRITE FILE
    BNE ABORT1      COULDN'T WRITE
    SSR 68      FIND DIRECTORY ENTRY
    LDD RUNADR      GET RUN ADDRESS
    STD DRADR,X     SAVE NEW RUN ADDRESS
    SSR 85      INDICATE WE CHANGED
*
* INDICATE WE CREATED A FILE
*
CREFIL  LDA QUIET       KEEPING QUIET?
    BEQ CREF1       IF SO, SAY NOTHING
    SSR 24      DISPLAY MESSAGE
    FCCZ    'Output file is: '
    SSR 30      DISPLAY
    SSR 22      NEW LINE
CREF1   CLRA            ZERO RC
ABORT1  SSR 0       BACK TO OS
*
* CREATE A MOTOROLA HEX FORMAT OUTPUT FILE
*
DOMHX   LDX #MODBUF     POINT TO MODULE BUFFER
    TFR X,Y     SET UP OUTPUT PTR
    SSR 53      LOAD THE OBJECT FILE
    STX HIADR       SET HIGH ADDRESS
    LDA KEEP        DO WE KEEP THE FILE?
    BEQ DOMHX1      YES, DON'T DELETE
    SSR 73      DELETE THE FILE
DOMHX1  LDX FILPTR      GET FILE POINTER
    LDD #'MH'       GET FIRST TWO CHARS
    STD ,X      SET TYPE
    LDA #'X'        GET LAST CHAR
    STA 2,X     SET LAST CHAR
    LDU #OUTFIL     GET OUTPUT PTR
    SSR 56      OPEN FILE FOR WRITE
    BNE ABORT1      ERROR, DON'T WRITE
* WRITE THE FILE FROM THE MEMORY IMAGE
GENREC  CLRB            START HERE
    LDX #MODBUF     POINT TO BUFFER
GENR1   CMPY    HIADR       ARE WE OVER?
    LBHS    INVCMD      INVALID
    LDA ,Y+     GET CHAR
    CMPA    #$CF        SPECIAL CHARACTER?
    BNE CHRNOR      NO, IT'S OK
    LDA ,Y+     READ NEXT CHARACTER
    CMPA    #$CF        SPECIAL CHARACTER?
    BEQ CHRNOR      NORMAL CHARACTER
    TSTA            IS IT END OF FILE?
    BEQ CLOSE       IF SO, CLOSE FILE AND RETIRE
    DECA            TEST FOR ADDRESS CHANGE
    LBNE    INVCMD      INVALID ENTRY
    BSR WRREC       WRITE OUT RECORD
    LDA ,Y+     GET HIGH BYTE OF ADDRESS
    STA RUNADR      SET RUN ADDRESS
    LDA ,Y+     GET LOW BYTE OF ADDRESS
    STA RUNADR+1    SET LOW BYTE OF RUN ADDRESS
    BRA GENREC      GENERATE A NEW RECORD
CHRNOR  STA ,X+     SAVE IN RECORD
    INCB            ADVANCE B REGISTER
    CMPB    #32     ARE WE OVER ONE RECORD?
    BLO GENR1       IF NOT, KEEP GOING
    BSR WRREC       WRITE A RECORD
    BRA GENREC      AND START A NEW ONE
*
* CLOSE FILE
*
CLOSE   BSR WRREC       OUTPUT LAST RECORD IF ANY
    LDX #S9REC      LAST MESSAGE
CLOS1   LDA ,X      GET CHARACTER FROM MESSAGE
    BSR WRCHAR      OUTPUT CHARACTER
    LDA ,X+     GET CHAR BACK
    CMPA    #$0D        CARRIAGE RETURN?
    BNE CLOS1       NO, KEEP GOING
CLSFIL  SSR 57      CLOSE FILE
    LBRA    CREFIL      OUTPUT MESSAGE
*
* WRITES A RECORD TO THE OUTPUT FILE
*
WRREC   TSTB            WHAT IS LENGTH?
    BEQ NOREC       DON'T WRITE RECORD
    LDU #OUTFIL     PT TO OUTPUT
    LDX #MODBUF     POINT TO BUFFER
    LDA #'S'        GET RECORD
    BSR WRCHAR      OUTPUT CHAR
    LDA #'1'        TYPE ONE
    BSR WRCHAR      OUTPUT CHAR
    PSHS    B       SAVE LENGTH
    STB OFFSET      SAVE COUNT
    ADDB    #3      ADVANCE FOR LENGTH BYTES
    TFR B,A     SAVE FOR LATER
    ADDA    RUNADR      ADD IN HIGH ADDRESS
    ADDA    RUNADR+1    AND LOW ADDRESS
    STA SETADR      START CHECKSUM
    TFR B,A     RESTORE LENGTH
    BSR WRBYTE      OUTPUT
    LDD RUNADR      GET RUN ADDRESS
    BSR WRBYTE      OUTPUT
    TFR B,A     COPY TO ACCA
    BSR WRBYTE      OUTPUT LOW ADDRESS
WRE1    LDA ,X      GET BYTE OF DATA
    BSR WRBYTE      OUTPUT
    LDA ,X+     GET BYTE BACK
    ADDA    SETADR      ADD TO CHECKSUM
    STA SETADR      RESAVE CHECKSUM
    DEC OFFSET      IS THIS IT?
    BNE WRE1        NO, KEEP GOING
WRE2    LDA #$FF        GET -1
    SUBA    SETADR      CALCULATE CHECKSUM VALUE
    BSR WRBYTE      OUTPUT
    LDA #$0D        NEW LINE
    BSR WRCHAR      OUTPUT CHAR
    PULS    B       RESTORE LENGTH
    CLRA            ZERO HIGH BYTE
    ADDD    RUNADR      OFSET INTO MEMORY ADDRESS
    STD RUNADR      RESAVE
NOREC   RTS
*
* WRITES A BYTE IN HEX TO THE SERIAL FILE
*
WRBYTE  PSHS    A       SAVE A REGISTER
    ASRA
    ASRA            ROTATE HIGH NIBBLE
    ASRA            INTO LOW NIBBLE
    ASRA
    BSR WRNIB       OUTPUT A NIBBLE
    PULS    A       RESTORE LOW NIBBLE
WRNIB   ANDA    #$F     REMOVE HIGH GARBAGE
    ADDA    #$30        CONVERT TO ASCII
    CMPA    #$3A        IS IT '0' TO '9'
    BLO WRCHAR      IF SO, IT'S OK
    ADDA    #7      CONVERT TO ALPHA
* WRITE A CHARACTER TO THE OUTPUT FILE
WRCHAR  SSR 61      OUTPUT CHARACTER
    BEQ NOREC       SUCCESS, CONTINUE
    SSR 0       ABORT WRITE
*
* READ A CHARACTER FROM THE INPUT FILE
*
RDCHR   PSHS    B,X     SAVE REGS
    LDX >OFFSET     GET OFFSET INFO FILE
    CMPX    #WRKSPC+512 OVER LIMIT?
    BLO RDC1        NO, WE ARE OK
    LDD >SECTOR     GET SECTOR
    SSR 77      LOOKUP LINK
    LBEQ    INVCMD      INVALID FILE
    STD >SECTOR     RESAVE SECTOR
    LDX #WRKSPC     POINT TO WORK AREA
    SSR 92      READ SECTOR
RDC1    LDA ,X+     GET CHAR
    STX >OFFSET     RESAVE OFFSET
    PULS    B,X,PC
*
* END OF FILE RECORD FOR MHX FORMAT
*
S9REC   FCC 'S9030000FC'
    FCB $0D
*
* QUALIFIER  TABLE
*
QTABLE  FCB $82
    FCC '/KEEP'
    FCB $82
    FCC '/MHX'
    FCB $82
    FCC '/QUIET'
    FCB $80
QMAX    EQU 3       TWO OUTPUT FORMATS APPLICABLE
* QUALIFIER FLAG TABLE
QFLAGS  EQU *
KEEP    FCB $FF     KEEP '.OBJ' FILE
MHX FCB $FF     WRITE MHX FILE
QUIET   FCB $FF     KEEP QUIET
*
FILPTR  FDB 0       POINTER TO FILE SPACE
RUNADR  FDB 0       FILE RUN ADDRESS
HIADR   FDB 0       HIGEST ADDRESS IN GENERATION
SETADR  FCB 0       FLAG THAT ADDRESS HAS BEEN SET
SECTOR  RMB 2       INPUT FILE SECTOR
OFFSET  RMB 2       INPUT FILE DRIVE
WRKSPC  RMB 512     INPUT FILE BUFFER
OUTFIL  RMB 522     OUTPUT FILE BUFFER
MODBUF  EQU *
