home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / simtel / sigm / vols200 / vol224 / iolib.c < prev    next >
Text File  |  1994-07-13  |  15KB  |  797 lines

  1. /*    name...
  2.         iolib
  3.  
  4.     purpose...
  5.         to provide a "standard" interface between c
  6.         programs and the CPM I/O system.
  7.  
  8.     notes...
  9.         Compile using -M option.
  10.  
  11.     history...
  12.         31 Jul 84  fbuf is now a pointer rather than
  13.             an array, and the disk buffer is
  14.             allocated from the heap.
  15.         28 Jul 84  Prepended '_' to all global
  16.             variables that shouldn't be visible
  17.             elsewhere.
  18.         27 Jul 84  Permitting I/O redirection. err()
  19.             temporarily resets STDOUT to 1 so
  20.             error messages go to console.
  21.         26 Jul 84  ccgo is initializing fmode[] at run
  22.             time, so compiled code need not be
  23.             changed before assembly.
  24.         14 Jul 84  gets() emitting LF after reading input.
  25.         1 Jul 84   Declaring _dfltdsk in c rather than
  26.             assembly, so references from c don't 
  27.             have to be corrected.
  28.         27 Jun 84  Changed ENDDATA to _END in alloc().
  29.         17 Jun 84  including iolib.h, so output can be
  30.             separately assembled.
  31.         14 Jun 84  Several bugs fixed, 'A' file mode
  32.             supported, putb() and getb() installed,
  33.             getc(1) comes from keyboard & putc(c,2)
  34.             goes to console.
  35.         29 Oct 83  Saving return addr in cpm().
  36.         13 Oct 83  QERR added from clibv.asm,
  37.             reformatted for ZMAC.
  38.         18 Sep 83  written (jrvz).
  39. */
  40. #include iolib.h
  41. #asm
  42. BDOS    =    5
  43. CR    =    13
  44. ;    cpm(bc,de) int bc,de;   BDOS call */
  45. QCPM:    POP    HL
  46.     POP    DE
  47.     POP    BC
  48.     PUSH    BC
  49.     PUSH    DE
  50.     PUSH    HL
  51.     CALL    BDOS
  52.     JP    CCSXT    ;move A to HL & sign extend
  53. ;
  54. ;    /* return address of a block of memory */
  55. ;    alloc(b)
  56. ;    int b;        /* # bytes desired */
  57. ;
  58. QALLOC:    POP    HL    ;return addr
  59.     POP    DE    ;block size
  60.     PUSH    DE
  61.     PUSH    HL
  62.     LD    HL,(HEAPTOP) ;current top of heap
  63.     EX    DE,HL
  64.     ADD    HL,DE    ;hl=new top of heap
  65.     LD    (HEAPTOP),HL
  66.     EX    DE,HL    ;hl=old top of heap
  67.     RET
  68. HEAPTOP: DEFW    _END
  69. ;
  70. ;    /* reset the top of heap pointer to addr* */
  71. ;
  72. ;    free(addr)
  73. ;    int addr;
  74. ;
  75. QFREE:    POP    DE
  76.     POP    HL    ;addr
  77.     PUSH    HL
  78.     PUSH    DE
  79.     LD    (HEAPTOP),HL
  80.     RET
  81. ;
  82. ;    /* return number of bytes between top of heap
  83. ;    and end of TPA.  Remember that this includes
  84. ;    the stack! */
  85. ;
  86. ;    avail()
  87. ;
  88. QAVAIL:    LD    HL,(6)    ;end of TPA
  89.     PUSH    HL
  90.     LD    HL,(HEAPTOP)    ;top of heap
  91.     JP    CCSUB    ;find (6)-HEAPTOP
  92. ;
  93. ;    error...print message & walkback trace (if available)
  94. ;
  95. ;            err(s) char *s;
  96. ;            {    int str;
  97. ;                puts("\nERROR ");
  98. QERR:    LD    HL,(QSTDOUT)
  99.     LD    (ERR6),HL
  100.     LD    HL,1
  101.     LD    (QSTDOUT),HL
  102.     LD    HL,MSGE
  103.     PUSH    HL
  104.     CALL    QPUTS
  105.     POP    HL
  106. ;                puts(s);
  107.     POP    DE
  108.     POP    HL
  109.     PUSH    HL
  110.     PUSH    DE
  111.     PUSH    HL
  112.     CALL    QPUTS
  113.     POP    HL
  114. ;                str=current;
  115.     LD    HL,(CURRENT)
  116. ;                while(str)
  117. ERR4:    LD    (ERR2),HL
  118.     LD    A,H
  119.     OR    L
  120.     JR    Z,ERR5
  121. ;                    {puts("\ncalled by ");
  122.     LD    HL,MSGE2
  123.     PUSH    HL
  124.     CALL    QPUTS
  125.     POP    HL
  126. ;                    puts(*(str+1));
  127.     LD    HL,(ERR2)
  128.     INC    HL
  129.     INC    HL
  130.     CALL    CCGINT
  131.     PUSH    HL
  132.     CALL    QPUTS
  133.     POP    HL
  134. ;                    str=*str;
  135.     LD    HL,(ERR2)
  136.     CALL    CCGINT
  137. ;                    }
  138.     JR    ERR4
  139. ;            }
  140. ERR5:    LD    HL,(ERR6)
  141.     LD    (QSTDOUT),HL
  142.     RET
  143. ;
  144. MSGE:    DB    CR,'ERROR: ',0
  145. MSGE2:    DB    CR,'CALLED BY ',0
  146. ERR2:    DW    0
  147. ERR6:    DW    0    ;temporary storage for STDOUT
  148. CURRENT: DW    0
  149. #endasm
  150. #define LF 10
  151.  
  152. int _dfltdsk,    /* "current disk" at beginning of execution */
  153. stdin,        /* 0 initially, or unit number for input file
  154.         if input has been redirected by args() */
  155. stdout;        /* 1 initially, or unit number for output file
  156.         if output has been redirected by args()    */
  157.  
  158. getchar()
  159. {    return getc(stdin);
  160. }
  161.  
  162. putchar(c) char c;
  163. {    putc(c,stdout);
  164.     return c;
  165. }
  166.  
  167. gets(buf) char *buf;   /* input a string (editing permitted) */
  168. {    char s1,s2;
  169.     int i;
  170.     if(stdin)    /* input has been redirected */
  171.         {i=80;
  172.         while(i--)
  173.             {s1=getc(stdin);
  174.             if((s1==-1)|(s1=='\n')) break;
  175.             *buf++=s1;
  176.             }
  177.         *buf=0;
  178.         }
  179.     else
  180.         {s2=buf[-2]; s1=buf[-1];    /* save 2 bytes */
  181.         buf[-2]=80;    /* assumed string length */
  182.         cpm(10,buf-2);
  183.         buf[buf[-1]]=0;    /* mark end using count left by cpm */
  184.         buf[-1]=s1; buf[-2]=s2;    /* restore the bytes */
  185.         putchar('\l');    /* LF */
  186.         }
  187. }
  188.  
  189. puts(buf) char *buf;    /* print a null-terminated string */
  190. {    char c;
  191.     while(c=*buf++) putchar(c);
  192. }
  193.  
  194. #define NBUFS 3
  195.     /*    = number of files which can be open at once */
  196.     /*    NOTE: ccgo() must initialize this many    */
  197.     /*    elements of fmode[].            */
  198. #define LGH 1024
  199.     /*    length of each file buffer        */
  200.     /*    = some multiple of 128: 128, 256, 384, 512... */
  201. #define BUFLGH 3171
  202.     /*    =NBUFS*(LGH+33)                */
  203.     /*  used in ccgo() to allocate the disk buffer    */
  204. #define MFREE 11387
  205. #define MREAD 22489
  206. #define MWRITE 17325
  207. #define MEOF -8734
  208. char *_fbuf;        /* for fcb's and disk buffers */
  209. int    _ffcb[NBUFS],    /* pointers to the fcb's */
  210.     _fnext[NBUFS],    /* pointers to the next char to be fed
  211.             to the program (for an input file) or
  212.             the next free byte in the buffer (for
  213.             an output file)            */
  214.     _ffirst[NBUFS],    /* ptrs to the starts of the buffers */
  215.     _flast[NBUFS],    /* ptrs to the ends of the buffers */
  216.     _fmode[NBUFS],    /* MFREE => buffer is free
  217.                MREAD => open for reading
  218.                MWRITE => open for writing
  219.                MEOF => was open for reading, but
  220.                 EOF encountered
  221.             = MFREE initially */
  222.     _ex,_cr;        /* extent & current record at beginning
  223.         of this buffer full (used for "A" access) */
  224.  
  225. fopen(name,mode)   /* open file in fmode
  226.         "r", "w", or "a" (upper or lower case)    */
  227. char *name,*mode;
  228. {    char c,*fcb;
  229.     int index,i,unit;
  230.     index=NBUFS;
  231.     while(index--)        /* search for free buffer */
  232.         {if(_fmode[index]==MFREE)break;
  233.         }
  234.     if(index==-1)
  235.         {err("OUT OF DISK BUFFERS"); exit();
  236.         }
  237.     unit=index+5;
  238.     _ffcb[index]=_fbuf+index*(33+LGH);
  239.     _ffirst[index]=_ffcb[index]+33;
  240.     _flast[index]=_ffirst[index]+LGH;
  241.     fcb=_ffcb[index];
  242.     i=11; while(i) fcb[i--]=' ';  /* clear file name */
  243.     fcb[12]=fcb[32]=0;        /* clear ex & cr */
  244.     if (name[1]==':')        /* transfer disk */
  245.         {fcb[0]= (*name&15) +1; /* either case */
  246.         name=name+2;
  247.         }
  248.     else    fcb[0]=_dfltdsk;
  249.     while(c=upper(*name++))        /* transfer name */
  250.         {if(c=='.')break;
  251.         fcb[++i]=c;
  252.         }
  253.     if(c=='.')        /* transfer extension */
  254.         {i=8;
  255.         while(c=upper(*name++)) {fcb[++i]=c;}
  256.         }
  257.     c=upper(*mode);
  258. /*    puts("OPENING FILE "); puts(fcb+1); putchar('\n');
  259.     puts("fcb at "); hex(fcb);
  260.     puts("\nbuffer "); hex(_ffirst[index]);
  261.     puts("through "); hex(_flast[index]);    */
  262.     if((c=='R')|(c=='A'))
  263.         {if(cpm(15,fcb)<0) 
  264.             {/* err("INPUT FILE DOESN\'T EXIST");*/
  265.             return 0; /* file not found */
  266.             }
  267.         _fmode[index]=MREAD;    /* open for reading */
  268.         _fnext[index]=_flast[index];
  269.                 /* forces immed. read */
  270.         if(c=='A')    /* append mode requested? */
  271.             {while(getc(unit)!=-1)
  272.                 {} /* read to EOF */
  273.             fcb[12]=_ex; /* reset to values at...*/
  274.             cpm(15,fcb); /* ...beginning of buffer */
  275.             fcb[32]=_cr;
  276.             _fmode[index]=MWRITE;
  277.             }
  278. /*        puts("\nnext char at "); hex(_fnext[index]); */
  279.         return unit;
  280.         }
  281.     else if(c=='W')
  282.         {cpm(19,fcb);        /* delete file */
  283.         i=cpm(22,fcb);        /* create file */
  284.         if(i<0) return 0;    /* creation failure */
  285.         _fmode[index]=MWRITE;    /* open for writing */
  286.         _fnext[index]=_ffirst[index];
  287.                 /* buffer is empty */
  288. /*        puts("\nnext char at "); hex(_fnext[index]);*/
  289.         return unit;
  290.         }
  291.     else return 0;
  292. }
  293.  
  294. fclose(unit) int unit;    /* close a file */
  295. {    int index,i,werror;
  296. /*    puts("\nfclose: closing unit "); hexb(unit); */
  297.     index=unit-5;
  298.     i=fchk(index);
  299.     if((i==MREAD)|(i==MEOF)) /* don't close read files */
  300.         {_fmode[index]=MFREE;
  301.         return 1;    /* success */
  302.         }
  303.     putb(26,unit);    /* append ^Z (CP/M EOF) */
  304.     werror=fflush(unit);
  305.     _fmode[index]=MFREE;
  306.     if((cpm(16,_ffcb[index])<0)|werror)
  307.         return 0; /* failure */
  308.     return 1;    /* success */
  309. }
  310.  
  311. fchk(index) int index;    /* check for legal index */
  312. {    int i;
  313.     if((index>=0)&(index<=NBUFS))
  314.         {i=_fmode[index];
  315.         if((i==MREAD)|(i==MWRITE)|(i==MEOF))
  316.             return i;
  317.         }
  318.     err("INVALID UNIT NUMBER");
  319.     exit();
  320. }
  321.  
  322. getc(unit) int unit;    /* get character from file 
  323.                 (return -1 at EOF)  */
  324. {    int c;
  325.     while((c=getb(unit))==LF){}    /* discard LF */
  326.     if(c==26)    /* CP/M EOF? */
  327.         {if(unit>=5)    /* leave _fnext[index] pointing
  328.                     at the ^Z */
  329.             {_fmode[unit-5]=MEOF;
  330.             --_fnext[unit-5];
  331.             }
  332.         return -1;
  333.         }
  334.     return c;
  335. }
  336.  
  337. getb(unit) int unit;    /* get byte from file 
  338.                 (return -1 at EOF)  */
  339. {    int index,mode,i;
  340.     char *next,*last,c,*fcb;
  341.  
  342.     if(unit==0)            /* STDIN */
  343.         {c=cpm(1,0);
  344.         if(c=='\n')cpm(2,LF);    /* add LF after CR */
  345.         return c;
  346.         }
  347.     index=unit-5;
  348.     mode=fchk(index);
  349.     if(mode==MEOF) return -1;    /* already found eof */
  350.     if(mode!=MREAD) {err("CAN\'T READ OUTFILE"); exit();}
  351.     next=_fnext[index];
  352.     if(next==_flast[index])    /* empty buffer? */
  353.         {fcb=_ffcb[index];
  354.         _ex=fcb[12]; _cr=fcb[32]; /* save for fopen() */
  355.         next=_ffirst[index];
  356.         last=next+LGH;
  357.         while(next<last)
  358.             {cpm(26,next);    /* set DMA */
  359.             if(cpm(20,fcb))break;
  360.             next=next+128;
  361.             }
  362.         cpm(26,128);        /* reset DMA */
  363.         if(next==_ffirst[index])    /* no records read? */
  364.             {/* _fmode[index]=MEOF; not needed */
  365.             return -1;
  366.             }
  367.         _flast[index]=next;
  368.         next=_ffirst[index];
  369.         }
  370.     c=*next++;
  371.     _fnext[index]=next;
  372.     return c;
  373. }
  374.  
  375. putc(c,unit) char c; int unit;  /* write a character to a file */
  376. {    putb(c,unit);
  377.     if(c=='\n')putb(LF,unit);    /* add LF after CR */
  378.     return c;
  379. }
  380.  
  381. putb(c,unit) char c; int unit;  /* write a byte to a file */
  382. {    int index,werror;
  383.     char *next;
  384.     if(unit==1)        /* STDOUT */
  385.         {cpm(2,c);
  386.         return c;
  387.         }
  388.     index=unit-5;
  389.     if(fchk(index)!=MWRITE)
  390.         {err("CAN\'T WRITE TO INFILE");
  391.         exit();
  392.         }
  393.     if(_fnext[index]==_flast[index]) werror=fflush(unit);
  394.     else werror=0;
  395.     next=_fnext[index];
  396.     *next++=c;
  397.     _fnext[index]=next;
  398.     if(werror) return werror;
  399.     return c;
  400. }
  401.  
  402. fflush(unit) int unit;  /* flush buffer to disk 
  403.             (on error returns nonzero)*/
  404. {    int index,i;
  405.     char *next,*going;
  406.     index=unit-5;
  407.     if(fchk(index)!=MWRITE)
  408.         {err("CAN\'T FLUSH INFILE");
  409.         exit();
  410.         }
  411. /*    puts("\nfflush: must write out "); hex(_ffirst[index]);
  412.     puts("through "); hex(_fnext[index]); */
  413.     next=_fnext[index];
  414.     going=_fnext[index]=_ffirst[index];
  415.     while(going<next)
  416.         {cpm(26,going); /* set DMA */
  417. /*        puts("\n writing from "); hex(going); */
  418.         if(cpm(21,_ffcb[index])) return -1; /* error? */
  419.         going=going+128;
  420.         }
  421.     cpm(26,128); /* reset DMA */
  422.     return 0;    /* no error */
  423. }
  424.  
  425. upper(c) int c;        /* converts to upper case */
  426. {    if(c>='a')return c-32;
  427.     return c;
  428. }
  429.  
  430. exit()
  431. {    if(stdout>=5)fclose(stdout);
  432. #asm
  433.     JP    0
  434. #endasm
  435. }
  436. #asm
  437. ;
  438. ; Runtime library initialization.
  439. ; Set up default drive for CP/M.
  440. CCGO:    LD    C,25     ;get current disk
  441.     CALL    BDOS
  442.     INC    A    ;now in range 1...16
  443.     LD    (Q_DFLTDSK),A
  444.     LD    HL,11387    ;initialize the elements of
  445.     LD    (Q_FMODE),HL    ;_fmode[] (# entries = NBUFS)
  446.     LD    (Q_FMODE+2),HL    ;to reflect free buffers
  447.     LD    (Q_FMODE+4),HL
  448.     LD    HL,(HEAPTOP)    ;current top of heap
  449.     LD    (Q_FBUF),HL    ;initialize buffer pointer
  450.     LD    DE,3171        ;= BUFLGH
  451.     ADD    HL,DE        ;hl=new top of heap
  452.     LD    (HEAPTOP),HL
  453.     LD    HL,0
  454.     LD    (QSTDIN),HL    ;initialize input unit
  455.     INC    HL
  456.     LD    (QSTDOUT),HL    ;initialize output unit
  457.     RET    
  458. ;Fetch a single byte from the address in HL and
  459. ; sign extend into HL
  460. CCGCHAR: LD    A,(HL)
  461. QARGC:
  462. CCSXT:    LD    L,A
  463.     RLCA    
  464.     SBC    A,A
  465.     LD    H,A
  466.     RET    
  467. ;Fetch integer from (HL+2)
  468. CCCDR:    INC    HL
  469.     INC    HL
  470. ;Fetch a full 16-bit integer from the address in HL
  471. CCCAR:
  472. CCGINT:    LD    A,(HL)
  473.     INC    HL
  474.     LD    H,(HL)
  475.     LD    L,A
  476.     RET    
  477. ;Store a 16-bit integer in HL at the address in TOS
  478. CCPINT:    POP    BC
  479.     POP    DE
  480.     PUSH    BC
  481.     LD    A,L
  482.     LD    (DE),A
  483.     INC    DE
  484.     LD    A,H
  485.     LD    (DE),A
  486.     RET    
  487. ;Inclusive "or" HL and TOS into HL
  488. CCOR:    POP    BC
  489.     POP    DE
  490.     PUSH    BC
  491.     LD    A,L
  492.     OR    E
  493.     LD    L,A
  494.     LD    A,H
  495.     OR    D
  496.     LD    H,A
  497.     RET    
  498. ;Exclusive "or" HL and TOS into HL
  499. CCXOR:    POP    BC
  500.     POP    DE
  501.     PUSH    BC
  502.     LD    A,L
  503.     XOR    E
  504.     LD    L,A
  505.     LD    A,H
  506.     XOR    D
  507.     LD    H,A
  508.     RET    
  509. ;"And" HL and TOS into HL
  510. CCAND:    POP    BC
  511.     POP    DE
  512.     PUSH    BC
  513.     LD    A,L
  514.     AND    E
  515.     LD    L,A
  516.     LD    A,H
  517.     AND    D
  518.     LD    H,A
  519.     RET    
  520. ;Test if HL = TOS and set HL = 1 if true else 0
  521. CCEQ:    POP    BC
  522.     POP    DE
  523.     PUSH    BC
  524.     CALL    CCCMP
  525.     RET    Z
  526.     DEC    HL
  527.     RET    
  528. ;Test if TOS ~= HL
  529. CCNE:    POP    BC
  530.     POP    DE
  531.     PUSH    BC
  532.     CALL    CCCMP
  533.     RET    NZ
  534.     DEC    HL
  535.     RET    
  536. ;Test if TOS > HL (signed)
  537. CCGT:    POP    BC
  538.     POP    DE
  539.     PUSH    BC
  540.     EX    DE,HL
  541.     CALL    CCCMP
  542.     RET    C
  543.     DEC    HL
  544.     RET    
  545. ;Test if TOS <= HL (signed)
  546. CCLE:    POP    BC
  547.     POP    DE
  548.     PUSH    BC
  549.     CALL    CCCMP
  550.     RET    Z
  551.     RET    C
  552.     DEC    HL
  553.     RET    
  554. ;Test if TOS >= HL (signed)
  555. CCGE:    POP    BC
  556.     POP    DE
  557.     PUSH    BC
  558.     CALL    CCCMP
  559.     RET    NC
  560.     DEC    HL
  561.     RET    
  562. ;Test if TOS < HL (signed)
  563. CCLT:    POP    BC
  564.     POP    DE
  565.     PUSH    BC
  566.     CALL    CCCMP
  567.     RET    C
  568.     DEC    HL
  569.     RET    
  570. ;Common routine to perform a signed compare
  571. ; of DE and HL
  572. ;This routine performs DE - HL and sets the conditions:
  573. ;    Carry reflects sign of difference (set means DE < HL)
  574. ;    Zero/non-zero set according to equality.
  575. CCCMP:    LD    A,E
  576.     SUB    L
  577.     LD    E,A
  578.     LD    A,D
  579.     SBC    A,H
  580.     LD    HL,1         ;preset true condition
  581.     JP    M,CCCMP1
  582.     OR    E         ;"OR" resets carry
  583.     RET    
  584. CCCMP1:    OR    E
  585.     SCF             ;set carry to signal minus
  586.     RET    
  587. ;
  588. ;Test if TOS >= HL (unsigned)
  589. CCUGE:    POP    BC
  590.     POP    DE
  591.     PUSH    BC
  592.     CALL    CCUCMP
  593.     RET    NC
  594.     DEC    HL
  595.     RET    
  596. ;
  597. ;Test if TOS < HL (unsigned)
  598. CCULT:    POP    BC
  599.     POP    DE
  600.     PUSH    BC
  601.     CALL    CCUCMP
  602.     RET    C
  603.     DEC    HL
  604.     RET    
  605. ;
  606. ;Test if TOS > HL (unsigned)
  607. CCUGT:    POP    BC
  608.     POP    DE
  609.     PUSH    BC
  610.     EX    DE,HL
  611.     CALL    CCUCMP
  612.     RET    C
  613.     DEC    HL
  614.     RET    
  615. ;
  616. ;Test if TOS <= HL (unsigned)
  617. CCULE:    POP    BC
  618.     POP    DE
  619.     PUSH    BC
  620.     CALL    CCUCMP
  621.     RET    Z
  622.     RET    C
  623.     DEC    HL
  624.     RET    
  625. ;
  626. ;Common routine to perform unsigned compare
  627. ;carry set if DE < HL
  628. ;zero/nonzero set accordingly
  629. CCUCMP:    LD    A,D
  630.     CP    H
  631.     JP    NZ,CUCMP1
  632.     LD    A,E
  633.     CP    L
  634. CUCMP1:    LD    HL,1
  635.     RET    
  636. ;
  637. ;Shift DE arithmetically right by HL and return in HL
  638. CCASR:    EX    DE,HL
  639.     DEC    E
  640.     RET    M         ;            7/2/82  jrvz
  641.     LD    A,H
  642.     RLA    
  643.     LD    A,H
  644.     RRA    
  645.     LD    H,A
  646.     LD    A,L
  647.     RRA    
  648.     LD    L,A
  649.     JP    CCASR+1
  650. ;Shift TOS arithmetically left by HL and return in HL
  651. CCASL:    POP    BC
  652.     POP    DE
  653.     PUSH    BC
  654.     EX    DE,HL
  655. CCASL4:    DEC    E
  656.     RET    M         ;        jrvz 7/2/82
  657.     ADD    HL,HL
  658.     JP    CCASL4
  659. ;Subtract HL from TOS and return in HL
  660. CCSUB:    POP    BC
  661.     POP    DE
  662.     PUSH    BC
  663.     LD    A,E
  664.     SUB    L
  665.     LD    L,A
  666.     LD    A,D
  667.     SBC    A,H
  668.     LD    H,A
  669.     RET    
  670. ;Form the two's complement of HL
  671. CCNEG:    CALL    CCCOM
  672.     INC    HL
  673.     RET    
  674. ;Form the one's complement of HL
  675. CCCOM:    LD    A,H
  676.     CPL    
  677.     LD    H,A
  678.     LD    A,L
  679.     CPL    
  680.     LD    L,A
  681.     RET    
  682. ;Multiply TOS by HL and return in HL
  683. CCMULT:    POP    BC
  684.     POP    DE
  685.     PUSH    BC
  686.     LD    B,H
  687.     LD    C,L
  688.     LD    HL,0
  689. CCMLT1:    LD    A,C
  690.     RRCA    
  691.     JP    NC,CMLT2
  692.     ADD    HL,DE
  693. CMLT2:    XOR    A
  694.     LD    A,B
  695.     RRA    
  696.     LD    B,A
  697.     LD    A,C
  698.     RRA    
  699.     LD    C,A
  700.     OR    B
  701.     RET    Z
  702.     XOR    A
  703.     LD    A,E
  704.     RLA    
  705.     LD    E,A
  706.     LD    A,D
  707.     RLA    
  708.     LD    D,A
  709.     OR    E
  710.     RET    Z
  711.     JP    CCMLT1
  712. ;Divide DE by HL and return quotient in HL, remainder in DE
  713. CCDIV:    LD    B,H
  714.     LD    C,L
  715.     LD    A,D
  716.     XOR    B
  717.     PUSH    AF
  718.     LD    A,D
  719.     OR    A
  720.     CALL    M,CCDENEG
  721.     LD    A,B
  722.     OR    A
  723.     CALL    M,CCBCNEG
  724.     LD    A,16
  725.     PUSH    AF
  726.     EX    DE,HL
  727.     LD    DE,0
  728. CCDIV1:    ADD    HL,HL
  729.     CALL    CCRDEL
  730.     JP    Z,CCDIV2
  731.     CALL    CCPBCDE
  732.     JP    M,CCDIV2
  733.     LD    A,L
  734.     OR    1
  735.     LD    L,A
  736.     LD    A,E
  737.     SUB    C
  738.     LD    E,A
  739.     LD    A,D
  740.     SBC    A,B
  741.     LD    D,A
  742. CCDIV2:    POP    AF
  743.     DEC    A
  744.     JP    Z,CCDIV3
  745.     PUSH    AF
  746.     JP    CCDIV1
  747. CCDIV3:    POP    AF
  748.     RET    P
  749.     CALL    CCDENEG
  750.     EX    DE,HL
  751.     CALL    CCDENEG
  752.     EX    DE,HL
  753.     RET    
  754. CCDENEG: LD    A,D
  755.     CPL    
  756.     LD    D,A
  757.     LD    A,E
  758.     CPL    
  759.     LD    E,A
  760.     INC    DE
  761.     RET    
  762. CCBCNEG: LD    A,B
  763.     CPL    
  764.     LD    B,A
  765.     LD    A,C
  766.     CPL    
  767.     LD    C,A
  768.     INC    BC
  769.     RET    
  770. CCRDEL:    LD    A,E
  771.     RLA    
  772.     LD    E,A
  773.     LD    A,D
  774.     RLA    
  775.     LD    D,A
  776.     OR    E
  777.     RET    
  778. CCPBCDE: LD    A,E
  779.     SUB    C
  780.     LD    A,D
  781.     SBC    A,B
  782.     RET    
  783. #endasm
  784. /*
  785. hex(x) int x;
  786. {    hexb(x>>8); hexb(x); putchar(' ');
  787. }
  788. hexb(x) int x;
  789. {    hexn(x>>4); hexn(x);
  790. }
  791. hexn(x) int x;
  792. {    x=x&15;
  793.     if(x<10) putchar(x+'0');
  794.     else putchar(x-10+'a');
  795. }
  796. */
  797.