$title('EMIT module for GENDEF')
emit:
do;
/* D I S K D E F   l i b   g e n e r a t o r   e m i t t e r */

mon1: procedure(f,a) external;
    declare f byte, a address;
    end mon1;

mon2: procedure(f,a) byte external;
    declare f byte, a address;
    end mon2;

boot: procedure public;
    call mon1(0,0);
    end boot;

abort: procedure(msg) external;
    declare msg address;
    end abort;

/* global variables */
declare
    parm (26) byte external;  /* initial parameters */
    /* a b c d e f g h i j k l m n o p q r s t u v w x y z
       0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 */

declare
    acclen   byte external,    /* accumulator length */
    accum(32)byte external;    /* accumulator */

declare
    lbuff(75) byte external,   /* physical line */
    lblen     byte external,   /* lbuff length */
    lineready byte external,   /* line ready flag */
    lineout   byte external,   /* true if current line out */
    cbuff(8)  byte external;   /* line prefix */

declare
    lit literally 'literally',
    true    lit '1',
    false   lit '0',
    forever lit 'while true',
    eofile  lit '1ah',         /* end of file */
    tab     lit '09h',         /* horizontal tab */
    cr      lit '0dh',
    lf      lit '0ah';

declare dcnt byte external;

close: procedure(fcb) external;
    declare fcb address;
    end close;

delete: procedure(fcb) external;
    declare fcb address;
    end delete;

diskwrite: procedure(fcb) byte external;
    declare fcb address;
    end diskwrite;

make: procedure(fcb) external;
    declare fcb address;
    end make;

rename: procedure(fcb) external;
    declare fcb address;
    end rename;

setdma: procedure(dma) external;
    declare dma address;
    end setdma;

/* local variables */
declare
    fcb (33) byte external,
    ifcb (33) byte at (.fcb),  /* initial input file control block */
    efcb (33) byte,           /* .asm file control block */
    eufb  byte at (.efcb(13)),/* unfilled bytes field */
    efcbr byte at (.efcb(32));/* next record field */

/* buffers */
declare
    comcol lit '32',          /* beginning column for comments */
    ecolumn     byte,         /* output column 0,1,... */
    ebuff (512) byte,         /* emit file buffer */
    ebp address;              /* emit buffer pointer */

/* sector size definitions */
declare
    sectsize lit '128',       /* bytes per sector */
    sectmsk  lit '7fh',       /* sectsize - 1 */
    sectshf  lit '7';         /* shl(1,sectshf) = sectsize */

setemit: procedure public;
    /* set up code emitter file */
    call move(16,.ifcb(0),.efcb(0)); /* copy name */
    call move(4,.('LIB',0),.efcb(9));/* copy type */
    if ifcb(16) <> 0 then /* select destination drive */
        efcb(0) = ifcb(16);
    call delete(.efcb);
    call make(.efcb);
    if dcnt = 255 then /* no space */
        call abort(.('no ".LIB" directory space$'));
    efcbr,ebp,ecolumn = 0;
    lineout = true; /* prevent initial line write */
    end setemit;

clearebuff: procedure;
    /* clear the ebuff buffer up to (not including) ebp */
    declare (i,n) byte;
    /* skip the case of no buffers to write */
    if low(n:=shr(ebp,7)-1) = 255 then return;
    /* n is last buffer to write */
    ebp = 0;
        do i = 0 to n;
        call setdma(.ebuff(ebp));
        if diskwrite(.efcb) <> 0 then
            call abort(.('".LIB" disk full$'));
        ebp = ebp + sectsize;
        end;
    ebp = 0;
    end clearebuff;

emitbyte: procedure(b) public;
    declare b byte;
    /* write b to emit file */
    if b = cr then
        ecolumn = 0;
    if b = tab then
        ecolumn = (ecolumn + 8) and 11111000b;
    if b >= ' ' then
        ecolumn = ecolumn + 1;
    if ebp >= length(ebuff) then call clearebuff;
    ebuff(ebp) = b;
    ebp = ebp + 1;
    end emitbyte;

efinis: procedure public;
    /* emit file finis */
    /* eufb = 0; /* clear the unfilled bytes field  */
    /*  do while (low(ebp) and sectmsk) <> 0;       */
    /*    eufb = eufb + 1; /* counts unfilled bytes */
    /*    call emitbyte(eofile);                    */
    /*    end;    Illegal under BDOS 3.0            */
    call clearebuff; /* write all buffers */
    call close(.efcb);
    if dcnt = 255 then
        call abort(.('cannot close ".LIB"$'));
    end efinis;

emitcrlf: procedure public;
    /* emit end of physical line */
    call emitbyte(cr);
    call emitbyte(lf);
    end emitcrlf;

emittab: procedure public;
    call emitbyte(tab);
    end emittab;

emitcomma: procedure public;
    call emitbyte(',');
    end emitcomma;

emitdigit: procedure(d);
    declare d byte;
    /* emit the decimal digit given by d */
    call emitbyte('0'+d);
    end emitdigit;

emitnulcom: procedure public;
    call emitbyte(';');
    call emitcrlf;
    end emitnulcom;

declare
    /* literals for decimal output control */
    zsup$on   lit 'true',   /* zero suppression on */
    zsup$off  lit 'false',  /* zero suppression off */
    bsup$on   lit 'true',   /* blank suppress */
    bsup$off  lit 'false',  /* blank suppress off */
    byte$base lit '100',    /* byte value */
    word$base lit '10000';  /* word value */

emitdecz: procedure(v,zsup,bsup,base) public;
    declare
        v address, /* value to emit */
        zsup byte, /* zero suppress if true */
        bsup byte,    /* blank suppression */
        base address, /* 100 for byte, 10000 for word */
        d byte;
        do while base <> 0;
        d = v/base mod 10;
        if (zsup := zsup and (d = 0) and (base > 1)) then
            do;
            if not bsup then call emitbyte(' ');
            end; else
            call emitdigit(d);
        base = base/10;
        end;
    end emitdecz;

emitnib: procedure(n);
    declare n byte;
    if n > 9 then
        n = n - 10 + 'A'; else
        n = n + '0';
    call emitbyte(n);
    end emitnib;

emithex8: procedure(h) public;
    declare h byte;
    call emitnib(shr(h,4));
    call emitnib(h and 0fh);
    end emithex8;

emithex16: procedure(a) public;
    declare a address;
    call emithex8(high(a));
    call emithex8(low(a));
    end emithex16;

emitaddr: procedure(a) public;
    declare a address;
    call emithex16(a);
    call emitbyte('h');
    end emitaddr;

emitdec8: procedure(b) public;
    declare b byte;
    call emitdecz(b,zsup$on,bsup$on,byte$base);
    end emitdec8;

emitdec16: procedure(w) public;
    declare w address;
    call emitdecz(w,zsup$on,bsup$on,word$base);
    end emitdec16;

emitdecb: procedure(w) public;
    declare w address;
    call emitdecz(w,zsup$on,bsup$off,word$base);
    end emitdecb;

emitline: procedure public;
    /* write current line to emit file */
    declare i byte;
    if lineready then /* line is ready to write */
        do;
        if lineout then return; /* already written */
        lineout = true;
        if lblen = 0 then return;
        /* non - zero line length */
        call emitbyte(';');
        call emittab;
        i = 0;
            do while (i:=i+1) < lblen;
            call emitbyte(lbuff(i-1));
            end;
        call emitcrlf;
        end;
    end emitline;

emitchar: procedure(a) public;
    /* emit literal string */
    declare a address;
    declare s based a byte;
        do while s <> '$';
        call emitbyte(s);
        a = a + 1;
        end;
    end emitchar;

emitcharn: procedure(a,n) public;
    declare
        a address,
        n byte;
    /* emitchar(a), followed by number */
    call emitchar(a);
    call emitdecz(n,zsup$on,bsup$on,byte$base);
    end emitcharn;

emitop: procedure(a) public;
    /* tab, operator */
    declare a address;
    call emittab;
    call emitchar(a);
    end emitop;

emitoptab: procedure(a) public;
    /* emit tab, operator, tab */
    declare a address;
    call emitop(a);
    call emittab;
    end emitoptab;

emitcomment: procedure(a) public;
    declare a address;
        do while ecolumn < comcol;
        call emittab;
        end;
    call emitbyte(';');
    call emitchar(a);
    call emitcrlf;
    end emitcomment;

emitdw: procedure public;
    call emitoptab(.('dw$'));
    end emitdw;

emitdb: procedure public;
    call emitoptab(.('db$'));
    end emitdb;

emitdwn: procedure(n) public;
    declare n address;
    call emitdw;
    call emitdec16(n);
    end emitdwn;

emitdbn: procedure(n) public;
    declare n byte;
    call emitdb;
    call emitdec8(n);
    end emitdbn;

emitdwnc: procedure(n,c) public;
    declare (n,c) address;
    call emitdwn(n);
    call emitcomment(c);
    end emitdwnc;

emitdbnc: procedure(n,c) public;
    declare n byte, c address;
    call emitdbn(n);
    call emitcomment(c);
    end emitdbnc;

end;
