/*      MACHINE LANGUAGE PROGRAM LOADER

            Usage : ld file1 file2 .... filen

        This program will load the specified Intel .HEX
        files and write out object .COM files without
        worrying about the address fields in the .HEX
        files. All object is written to disk in the
        order the of the hex in the input file.

        Program was written to simplify program which
        have bits and pieces floating all over the
        place in RAM. All that is neccesary is to ORG
        the code to the correct address, and then have
        a routine to move it there in your program. What
        this effectively does is turns the ORG statement
        into a  command simmilar to the .PHASE of
        Microsofts MACRO-80. For example, take the
        following program :

          8080                             Z80

        ORG     0100H                   ORG     0100H
DEST:   EQU     08000H          DEST:   EQU     08000H
        LXI     H,ENDP+1                LD      HL,ENDP+1
        LXI     D,DEST                  LD      DE,DEST
        LXI     B,ENDR-START            LD      BC,ENDR-START
LOOP:   MOV     A,M             LOOP:   LD      A,(HL)
        XCHG                            EX      HL,DE
        MOV     M,A                     LD      (HL),A
        XCHG                            EX      HL,DE
        INX     H                       INC     HL
        INX     D                       INC     DE
        DCX     B                       DEC     BC
        MOV     A,B                     LD      A,B
        ORA     C                       OR      C
        JNZ     LOOP                    JMP     NZ,LOOP
        RET                             RET
ENDP:   DB      0               ENDP:   DB      0

          ;START OF MOVABLE PROGRAM
        ORG     DEST                    ORG     DEST
START:  .                       START:  .
         .                               .
          .                               .
           .                               .
            . . . . . . . . . . . . . . . . . . this can be anything
             .                               .
              .                               .
               .                               .
ENDR:   DB      0               ENR:    DB      0

        When assembled it will produce a .HEX file that
        looks like the following (I have expanded this
        to make it more meaningful) :

 N  ADDR    <------------CODE-------------->
:10 0100 00 2116011100800105007EEB77EB23130B 14
:06 0110 00 78B1C2090100 F4
:06 8000 00 010203040500 6B      <- program to execute
:00 0000 00 00

        When using the normal LOAD command, this would
        produce a .COM file such that when executed,
        each hex value above will load strting at the
        address in the ADDR field, padding the gap
        between 0116 -> 8000 with garbage. This is
        undesirable (for me anyway!!), as if I want
        to generate code to reside in high memory
        e.g. CP/M watch-dogs, I must make a
        relocator...a pest. This program will
        LD a file(s) ignoring the ADDR field, although
        it will warn you if the first ADDRess is not
        0100H. This allows the assembler to do all the
        hard work. Just remember that any labels in the
        part to move will have values reflecting the
        move recent ORG. This explains the couple of
        DB 0 statements above. Note that ENDR-START
        is the length of the code to be moved.


        Have fun with it, and tell me if it doesn't
        work. By the way, if you make some good
        patches or fixes to this, document it somewhere
        in this file (preferably above) and then add the
        new version to the VERSION CONTROL below. I have
        tried to make the program "quiet". Please try
        and keep it that way.

                        Thanks,  David Brown

VERSION CONTROL:  (most recent first)

Version     Description                      Author     Date
----------------------------------------------------------------
  1.0      Initial program written            Me !      06/11/82

----------------------------------------------------------------
*/
#include "bdscio.h"     /* Get all the good gear */
#define FLAG char
#define TPA (BASE+0x100)

main(argc, argv)        /* here we go... */
char **argv;
{
        if (argc==1)
                printf("Usage: ld file1 file2 ... filen");

        while (--argc)
                convert(*++argv);        /* process each file in
                                            order of command line */

}

convert(f)
char *f;
{
        char    infil[BUFSIZ], otfil[BUFSIZ];
        char    work[MAXLINE];
        int     iores, num, i;
        FLAG    assume, first, find();

        assume = !find('.',f);   /* find a '.' in f ?? */

        if ((iores=fopen(f,infil))==ERROR) {   /* try and open.. */
                printf("Cannot open : %s",f);
                if (assume) {                      /* if no type try it */
                        printf(", trying .HEX\n"); /*   with a .HEX added */
                        strcpy(work,f);
                        strcat(work,".HEX");
                        if ((iores=fopen(work,infil))==ERROR)
                             printf("Cannot open : %s, skipping..\n",work);
                } else printf(", skipping..\n");
        }
        if (iores != ERROR) {     /* if successfully opened go for it.. */
                if (!assume)      /* get rid of type */
                        f[find('.',f)-1] = '\0';
                strcpy(work,f);
                strcat(work,".COM");
                if (fcreat(work,otfil)==ERROR)
                        printf("Cannot create : %s, skipping..\n",work);
                else {
                        first = TRUE;
                        do {
                            skip(infil,1);
                            num = readhex(infil,2);
                            if (first) {
                              first = FALSE;
                              if (readhex(infil,4) != TPA)
                                  printf("Warning: %s, not at %^4x\n",f,TPA);
                              skip(infil,2);
                              }
                              else skip(infil,6);
                            for (i=1;i<=num;i++)
                                 putc(readhex(infil,2),otfil);
                            skip(infil,4);  /* skip check and CRLF */
                        } while (num != 0);
                        fflush(otfil);
                        fclose(otfil);
                }
                fclose(infil);
        }
}

FLAG find(c,s)
char c, *s;
{
        int pos;        /* position found c in s */

        pos = 1;
        while (*s != '\0')
                if (c == *s) return pos;
                else {
                        s++;
                        pos++;
                }
        return FALSE;
}

skip(f,n)       /* skip n characters from file f */
char *f;
{
        while (n--) getc(f);
}

readhex(f,n)    /* Reads n hex digits from file f. If a non-hex
                   digit is encounted '0' is assumed */
char *f;
{
        unsigned val;
        char c;

        val = 0;
        while(n--) {
                val *= 16;
                c=getc(f);
                if (isdigit(c))
                        val += (c - '0');
                else if (isupper(c) && c < 'G')
                        val += (c - 'A' + 10);
        }
        return val;
}

