home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff236.lzh / XprZmodem / utils.c < prev    next >
C/C++ Source or Header  |  1989-08-09  |  18KB  |  613 lines

  1. /*  Utils.c: Miscellaneous support routines for xprzmodem.library;
  2.     Version 1.0, 29 July 1989, by Rick Huebner.
  3.     Released to the Public Domain; do as you like with this code.  */
  4.  
  5.  
  6. #include <exec/memory.h>
  7. #include "aztec.h"
  8. #include "xproto.h"
  9. #include "zmodem.h"
  10. #include "defs.h"
  11.  
  12. /* Label and version info for .library file */
  13. char XPRname[] = "xprzmodem.library";
  14. char XPRid[]   = "xprzmodem 1.0, 29 July 89\r\n";
  15. short XPRrevision = 0;  /* Version number is XPRVERSION in rtag.asm */
  16.  
  17. /* Transfer options to use if XProtocolSetup not called */
  18. char Default_Config[] = "T?,ON,B16,F0";
  19.  
  20. #ifdef DEBUG
  21. UBYTE DebugName[] = "Log:ZDebug.log";
  22. long DebugLog = NULL;
  23. #endif
  24.  
  25.  
  26. /* Called by terminal program to set transfer options */
  27. long XProtocolSetup(io)
  28. register struct XPR_IO *io;
  29. {
  30.   UBYTE buf[256], t, o;
  31.   register UBYTE *p;
  32.   register long len, b, f;
  33.  
  34.   /* Allocate memory for transfer options string */
  35.   if (!io->xpr_data) {
  36.     io->xpr_data = AllocMem((long)CONFIGLEN,0L);
  37.     if (!io->xpr_data) {
  38.       ioerr(io,"Not enough memory for ZModem config string");
  39.       return 0;
  40.     }
  41.     /* Start out with default options; merge user changes into defaults */
  42.     strcpy(io->xpr_data,Default_Config);
  43.   }
  44.  
  45.   /* Extract current settings from options string */
  46.   t = *(strchr(io->xpr_data,'T')+1);
  47.   o = *(strchr(io->xpr_data,'O')+1);
  48.   b = atol(strchr(io->xpr_data,'B')+1);
  49.   f = atol(strchr(io->xpr_data,'F')+1);
  50.  
  51.   /* If config string passed by term prog, use it; else prompt user */
  52.   if (io->xpr_filename) strcpy(buf,io->xpr_filename);
  53.   else {
  54.     /* Start buffer with current settings so user can see/edit them in place */
  55.     strcpy(buf,io->xpr_data);
  56.     if (io->xpr_gets) callaa(io->xpr_gets,"ZModem options:",buf);
  57.   }
  58.   /* Upshift config string for easier parsing */
  59.   for (p=buf; *p; ++p)
  60.     *p = toupper(*p);
  61.  
  62.   /* Merge new T(ext) option into current settings if given */
  63.   /* "TY" = Force Text mode on,
  64.      "TN" = Force Text mode off,
  65.      "T?" = Use other end's text mode suggestion (default to binary) */
  66.   if (p = strchr(buf,'T')) {
  67.     ++p;
  68.     if (*p == 'Y' || *p == 'N' || *p == '?') t = *p;
  69.     else ioerr(io,"Invalid T flag ignored; should be Y, N, or ?");
  70.   }
  71.  
  72.   /* Merge new O(verwrite) option into current settings if given */
  73.   /* "OY" = Yes, delete old file and replace with new one,
  74.      "ON" = No, prevent overwrite by appending ".dup" to avoid name collision,
  75.      "OR" = Resume transfer at end of existing file,
  76.      "OS" = Skip file if it already exists; go on to next */
  77.   if (p = strchr(buf,'O')) {
  78.     ++p;
  79.     if (*p == 'R' && !io->xpr_finfo) ioerr(io,"Can't Resume; xpr_finfo() not supported");
  80.     else if (*p == 'Y' || *p == 'N' || *p == 'R' || *p == 'S') o = *p;
  81.     else ioerr(io,"Invalid O flag ignored; should be Y, N, R, or S");
  82.   }
  83.  
  84.   /* Merge new B(uffer) setting into current settings if given */
  85.   /* Size of file I/O buffer in kilobytes */
  86.   if (p = strchr(buf,'B')) {
  87.     len = atol(++p);
  88.     if (len < 1) len = 1;
  89.     b = len;
  90.   }
  91.  
  92.   /* Merge new F(ramelength) setting into other settings if given */
  93.   /* Number of bytes we're willing to send or receive between ACKs.
  94.      0 = unlimited; nonstop streaming data */
  95.   if (p = strchr(buf,'F')) {
  96.     len = atol(++p);
  97.     if (len < 0) len = 0;
  98.     if (len > 0 && len < MINBLOCK) len = MINBLOCK;
  99.     f = len;
  100.   }
  101.  
  102.   /* Update config string with new settings */
  103.   sprintf(io->xpr_data,"T%c,O%c,B%ld,F%ld",t,o,b,f);
  104.  
  105.   return 1;
  106. }
  107.  
  108.  
  109. /* Called by terminal program to give us a chance to clean up before program ends */
  110. long XProtocolCleanup(io)
  111. register struct XPR_IO *io;
  112. {
  113.   /* Release config option memory, if any */
  114.   if (io->xpr_data) {
  115.     FreeMem(io->xpr_data,(long)CONFIGLEN);
  116.     io->xpr_data = NULL;
  117.   }
  118.  
  119.   return 1;
  120. }
  121.  
  122.  
  123. /* Perform setup and initializations common to both Send and Receive routines */
  124. struct Vars *setup(io)
  125. register struct XPR_IO *io;
  126. {
  127.   static long bauds[] = { 110,300,1200,2400,4800,9600,19200,38400,38400,57600,76800,115200 };
  128.   register struct Vars *v;
  129.   register long newstatus;
  130.  
  131.   /* Make sure terminal program supports the required call-back functions */
  132.   if (!io->xpr_update) return NULL;
  133.   if (!io->xpr_fopen || !io->xpr_fclose || !io->xpr_fread || !io->xpr_fwrite ||
  134.       !io->xpr_fseek || !io->xpr_sread || !io->xpr_swrite) {
  135.     ioerr(io,"Term prog missing required function(s); see docs");
  136.     return NULL;
  137.   }
  138.  
  139.   /* Hook in default transfer options if XProtocolSetup wasn't called */
  140.   if (!io->xpr_data) {
  141.     io->xpr_data = AllocMem((long)CONFIGLEN,0L);
  142.     if (!io->xpr_data) {
  143.       ioerr(io,"Not enough memory for ZModem config string");
  144.       return NULL;
  145.     }
  146.     strcpy(io->xpr_data,Default_Config);
  147.   }
  148.  
  149.   /* Allocate memory for our unshared variables, to provide reentrancy */  
  150.   if (!(v = AllocMem((long)sizeof(struct Vars),MEMF_CLEAR))) {
  151. nomem:
  152.     ioerr(io,"Not enough memory for xprzmodem");
  153.     return NULL;
  154.   }
  155.  
  156.   /* Allocate memory for our file I/O buffer; if we can't get as much as
  157.      requested, keep asking for less until we hit minimum before giving up */
  158.   v->Filebufmax = atol(strchr(io->xpr_data,'B')+1) * 1024;
  159.   while (!(v->Filebuf = AllocMem(v->Filebufmax,0L))) {
  160.     if (v->Filebufmax > 1024) v->Filebufmax -= 1024;
  161.     else {
  162.       FreeMem(v,(long)sizeof(struct Vars));
  163.       goto nomem;
  164.     }
  165.   }
  166.   
  167.   /* Copy caller's io struct into our Vars for easier passing */
  168.   v->io = *io;
  169.  
  170. #ifdef DEBUG
  171.   if (!DebugLog) DebugLog = callaa(v->io.xpr_fopen,DebugName,"w");
  172. #endif
  173.  
  174.   /* Initialize Vars as required */
  175.   switch(*(strchr(io->xpr_data,'T')+1)) {
  176.     case 'Y':
  177.       v->Rxascii = TRUE;
  178.       v->Rxbinary = FALSE;
  179.       v->Lzconv = ZCNL;
  180.       break;
  181.     case 'N':
  182.       v->Rxascii = FALSE;
  183.       v->Rxbinary = TRUE;
  184.       v->Lzconv = ZCBIN;
  185.       break;
  186.     case '?':
  187.       v->Rxascii = v->Rxbinary = FALSE;
  188.       v->Lzconv = 0;
  189.       break;
  190.   }
  191.   v->Tframlen = atol(strchr(io->xpr_data,'F')+1);
  192.  
  193.   /* Get baud rate; set serial port mode if necessary (and possible) */  
  194.   if (v->io.xpr_setserial) {
  195.     v->Oldstatus = calld(v->io.xpr_setserial,-1L);
  196.     /* ZModem requires 8 data bits, no parity (full transparency), 
  197.        leave other settings alone */
  198.     newstatus = v->Oldstatus & 0xFFFFE0BC;
  199.     /* newstatus |= on_flags; */
  200.     if (newstatus != v->Oldstatus) calld(v->io.xpr_setserial,newstatus);
  201.     v->Baud = bauds[(newstatus>>16) & 0xFF];
  202. #ifdef DEBUG
  203.     sprintf(v->Msgbuf,"Old serial status = %lx, new = %lx, baud = %ld\n",v->Oldstatus,newstatus,v->Baud);
  204.     dlog(v,v->Msgbuf);
  205. #endif
  206.   /* If no xpr_setserial(), muddle along with most likely guess */
  207.   } else v->Baud = 2400;
  208.  
  209.   return v;
  210. }
  211.  
  212.  
  213. /* send cancel string to get the other end to shut up */
  214. void canit(v)
  215. register struct Vars *v;
  216. {
  217.   static char canistr[] = { 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0 };
  218.  
  219.   zmputs(v,canistr);
  220. }
  221.  
  222.  
  223. /* Send a string to the modem, with processing for \336 (sleep 1 sec)
  224.    and \335 (break signal, ignored since XPR spec doesn't support it) */
  225. void zmputs(v,s)
  226. register struct Vars *v;
  227. register UBYTE *s;
  228. {
  229.   register short c;
  230.  
  231.   while (*s) {
  232.     switch (c = *s++) {
  233.       case '\336':
  234.         TimeOut(50L);
  235.       case '\335':
  236.         break;
  237.       default:
  238.         sendline(v,c);
  239.     }
  240.   }
  241. }
  242.  
  243.  
  244. /* Write one character to the modem */
  245. void xsendline(v,c)
  246. register struct Vars *v;
  247. UWORD c;
  248. {
  249.   UBYTE buf = c;
  250.  
  251.   callad(v->io.xpr_swrite,&buf,1L);
  252. }
  253.  
  254.  
  255. /* Get a byte from the modem;
  256.    return TIMEOUT if no read within timeout tenths of a second,
  257.    return RCDO if carrier lost (supposedly; XPR spec doesn't support carrier detect, though).
  258.    Added in some buffering so we wouldn't hammer the system with single-byte
  259.    serial port reads.  Also, the buffering makes char_avail() a lot easier to implement. */
  260. short readock(v,tenths)
  261. register struct Vars *v;
  262. short tenths;
  263. {
  264.   /* If there's data waiting in the buffer, return next byte */
  265.   if (v->Modemcount) {
  266. gotdata:
  267.     --v->Modemcount;
  268.     return *v->Modemchar++;
  269.   }
  270.  
  271.   /* Buffer is empty; try to read more data into it */
  272.   v->Modemcount = calladd(v->io.xpr_sread,v->Modembuf,(long)sizeof(v->Modembuf),tenths*100000L);
  273.   if (v->Modemcount < 1) {   /* Didn't get anything within time limit; timeout */
  274.     v->Modemcount = 0;
  275.     return TIMEOUT;
  276.   } else {                   /* Got something; return first byte of it */
  277.     v->Modemchar = v->Modembuf;  /* Reset buffer pointer to start of data */
  278.     goto gotdata;
  279.   }
  280. }
  281.  
  282.  
  283. /* Check if there's anything available to read from the modem */
  284. char char_avail(v)
  285. register struct Vars *v;
  286. {
  287.   if (v->Modemcount) return TRUE;
  288.  
  289.   /* No data in our buffer; check system's input buffer */  
  290.   v->Modemcount = calladd(v->io.xpr_sread,v->Modembuf,(long)sizeof(v->Modembuf),0L);
  291.   if (v->Modemcount < 1) {  /* Nothing in system buffer either */
  292.     v->Modemcount = 0;
  293.     return FALSE;
  294.   } else {                  /* System buffer had something waiting for us */
  295.     v->Modemchar = v->Modembuf;
  296.     return TRUE;
  297.   }
  298. }
  299.  
  300.  
  301. /* Update the elapsed time, expected total time, and effective data
  302.    transfer rate values for status display */
  303. void update_rate(v)
  304. register struct Vars *v;
  305. {
  306.   static char *timefmt = "%2d:%02d:%02d";
  307.   register long sent, elapsed, expect;
  308.   register short hr, min;
  309.  
  310.   /* Compute effective data rate so far in characters per second */
  311.   sent = v->xpru.xpru_bytes - v->Strtpos; /* Actual number of chars transferred */
  312.   elapsed = time(NULL) - v->Starttime;    /* Time it took to send them */
  313.   if (elapsed < 1) elapsed = 1;
  314.   /* If we haven't transferred anything yet (just starting), make reasonable
  315.      guess (95% throughput); otherwise, compute actual effective transfer rate */
  316.   v->xpru.xpru_datarate = (sent) ? sent / elapsed : v->Baud * 95L / 1000;
  317.  
  318.   /* Compute expected total transfer time based on data rate so far */
  319.   if (v->xpru.xpru_filesize < 0) expect = 0; /* Don't know filesize; display time=0 */
  320.   else expect = (v->xpru.xpru_filesize - v->Strtpos) / v->xpru.xpru_datarate;
  321.   hr = expect / (60*60);   /* How many whole hours */
  322.   expect -= hr * (60*60);  /* Remainder not counting hours */
  323.   min = expect / 60;       /* How many whole minutes */
  324.   expect -= min * 60;      /* Remaining seconds */
  325.   sprintf(v->Msgbuf,timefmt,hr,min,(short)expect);
  326.   v->xpru.xpru_expecttime = (char *)v->Msgbuf;
  327.  
  328.   /* Compute elapsed time for this transfer so far */
  329.   hr = elapsed / (60*60);
  330.   elapsed -= hr * (60*60);
  331.   min = elapsed / 60;
  332.   elapsed -= min * 60;
  333.   sprintf(v->Msgbuf+20,timefmt,hr,min,(short)elapsed);
  334.   v->xpru.xpru_elapsedtime = (char *)v->Msgbuf+20;
  335. }
  336.  
  337.  
  338. /* Buffered file I/O fopen() interface routine */
  339. long bfopen(v,mode)
  340. register struct Vars *v;
  341. UBYTE *mode;
  342. {
  343.   /* Initialize file-handling variables */
  344.   v->Filebufpos = v->Filebuflen = v->Filebufcnt = 0;
  345.   v->Fileflush = FALSE;
  346.   v->Filebufptr = v->Filebuf;
  347.   /* Open the file */
  348.   return callaa(v->io.xpr_fopen,v->Filename,mode);
  349. }
  350.  
  351.  
  352. /* Buffered file I/O fclose() interface routine */
  353. void bfclose(v)
  354. register struct Vars *v;
  355. {
  356.   /* If bfwrite() left data lingering in buffer, flush it out before closing */
  357.   if (v->Fileflush) calladda(v->io.xpr_fwrite,v->Filebuf,1L,v->Filebufcnt,v->File);
  358.   /* Close the file */
  359.   calla(v->io.xpr_fclose,v->File);
  360.   v->File = NULL;
  361. }
  362.  
  363.  
  364. /* Buffered file I/O fseek() interface routine */
  365. void bfseek(v,pos)
  366. register struct Vars *v;
  367. register long pos;
  368. {
  369.   register long offset;
  370.  
  371.   /* If new file position is within currently buffered section, reset pointers */
  372.   if (pos >= v->Filebufpos && pos <= v->Filebufpos + v->Filebuflen - 1) {
  373.     offset = pos - v->Filebufpos;
  374.     v->Filebufptr = v->Filebuf + offset;
  375.     v->Filebufcnt = v->Filebuflen - offset;
  376.   /* Otherwise, fseek() file and discard buffer contents to force new read */
  377.   } else {
  378.     calladd(v->io.xpr_fseek,v->File,pos,0L);
  379.     v->Filebuflen = v->Filebufcnt = 0;
  380.     v->Filebufpos = pos;
  381.   }
  382. }
  383.  
  384.  
  385. /* Buffered file I/O fread() interface routine */
  386. long bfread(v,buf,length)
  387. register struct Vars *v;
  388. UBYTE *buf;
  389. register long length;
  390. {
  391.   register long count, total = 0;
  392.  
  393.   /* If there's already data buffered up, try to get what we need from there */
  394.   if (v->Filebufcnt) {
  395. readmore:
  396.     count = (length <= v->Filebufcnt) ? length : v->Filebufcnt;
  397.     CopyMem(v->Filebufptr,buf,count);
  398. #ifdef DEBUG
  399.     sprintf(v->Msgbuf,"bfread got %ld bytes from buffer\n",count);
  400.     dlog(v,v->Msgbuf);
  401. #endif
  402.     total += count;
  403.     v->Filebufptr += count;
  404.     v->Filebufcnt -= count;
  405.   }
  406.  
  407.   /* If there wasn't enough in the buffer, read next buffer's worth and try again */
  408.   if (total < length) {
  409.     v->Filebufpos += v->Filebuflen;
  410.     v->Filebufptr = v->Filebuf;
  411.     v->Filebufcnt = v->Filebuflen = calladda(v->io.xpr_fread,v->Filebuf,1L,v->Filebufmax,v->File);
  412. #ifdef DEBUG
  413.     sprintf(v->Msgbuf,"bfread read %ld bytes\n",v->Filebuflen);
  414.     dlog(v,v->Msgbuf);
  415. #endif
  416.     if (v->Filebufcnt) goto readmore;
  417.     /* else we couldn't read as much as requested; return partial count */
  418.   }
  419.  
  420.   return total;
  421. }
  422.  
  423.  
  424. /* Buffered file I/O fwrite() interface routine */
  425. long bfwrite(v,buf,length)
  426. register struct Vars *v;
  427. register UBYTE *buf;
  428. register long length;
  429. {
  430.   register long count, total = 0;
  431.  
  432.   /* Keep going until entire request completed */
  433.   while (length > 0) {
  434.     /* Copy as much as will fit into the buffer */
  435.     count = v->Filebufmax - v->Filebufcnt;
  436.     if (length < count) count = length;
  437.     CopyMem(buf,v->Filebufptr,count);
  438. #ifdef DEBUG
  439.     sprintf(v->Msgbuf,"bfwrite buffered %ld bytes\n",count);
  440.     dlog(v,v->Msgbuf);
  441. #endif
  442.     buf += count;
  443.     total += count;
  444.     length -= count;
  445.     v->Filebufptr += count;
  446.     v->Filebufcnt += count;
  447.     v->Fileflush = TRUE;
  448.  
  449.     /* If we've filled the buffer, write it out */
  450.     if (v->Filebufcnt == v->Filebufmax) {
  451.       count = calladda(v->io.xpr_fwrite,v->Filebuf,1L,v->Filebufcnt,v->File);
  452. #ifdef DEBUG
  453.       sprintf(v->Msgbuf,"bfwrite wrote %ld bytes\n",count);
  454.       dlog(v,v->Msgbuf);
  455. #endif
  456.       if (count < v->Filebufcnt) return -1;
  457.       v->Filebufptr = v->Filebuf;
  458.       v->Filebufcnt = 0;
  459.       v->Fileflush = FALSE;
  460.     }
  461.   }
  462.  
  463.   return total;
  464. }
  465.     
  466.  
  467. /* Have the terminal program display an error message for us, using a
  468.    temporary XPR_UPDATE structure; used to display errors before Vars 
  469.    gets allocated */
  470. void ioerr(io,msg)
  471. register struct XPR_IO *io;
  472. char *msg;
  473. {
  474.   struct XPR_UPDATE xpru;
  475.  
  476.   if (io->xpr_update) {
  477.     xpru.xpru_updatemask = XPRU_ERRORMSG;
  478.     xpru.xpru_errormsg = msg;
  479.     calla(io->xpr_update,&xpru);
  480.   }
  481. }
  482.  
  483.  
  484. /* Have the terminal program display an error message for us, using the
  485.    normal XPR_IO structure allocated in Vars */
  486. void upderr(v,msg)
  487. register struct Vars *v;
  488. char *msg;
  489. {
  490.   v->xpru.xpru_updatemask = XPRU_ERRORMSG;
  491.   v->xpru.xpru_errormsg = msg;
  492.   calla(v->io.xpr_update,&v->xpru);
  493. #ifdef DEBUG
  494.   dlog(v,msg);
  495.   dlog(v,"\n");
  496. #endif
  497. }
  498.  
  499.  
  500. /* Have the terminal program display a normal message for us */
  501. void updmsg(v,msg)
  502. register struct Vars *v;
  503. char *msg;
  504. {
  505.   v->xpru.xpru_updatemask = XPRU_MSG;
  506.   v->xpru.xpru_msg = msg;
  507.   calla(v->io.xpr_update,&v->xpru);
  508. #ifdef DEBUG
  509.   dlog(v,msg);
  510.   dlog(v,"\n");
  511. #endif
  512. }
  513.  
  514.  
  515. /* Figure out how many bytes are free on the drive we're uploading to.
  516.    Stubbed out for now; not supported by XPR spec. */
  517. long getfree() {
  518.   return 0x7FFFFFFF;
  519. }
  520.  
  521.  
  522. /* Check whether file already exists; used to detect potential overwrites */
  523. char exist(v)
  524. register struct Vars *v;
  525. {
  526.   register long file;
  527.  
  528.   file = callaa(v->io.xpr_fopen,v->Filename,"r");
  529.   if (file) {
  530.     calla(v->io.xpr_fclose,file);
  531.     return TRUE;
  532.   } else return FALSE;
  533. }
  534.  
  535.  
  536. #ifdef DEBUG
  537. /* Write a message to the debug log */
  538. dlog(v,s)
  539. register struct Vars *v;
  540. register UBYTE *s;
  541. {
  542.   /* Open the debug log if it isn't already open */
  543.   if (!DebugLog) DebugLog = callaa(v->io.xpr_fopen,DebugName,"a");
  544.   calladda(v->io.xpr_fwrite,s,1L,(long)strlen(s),DebugLog);
  545.   /* Close file to flush output buffer; comment these two lines out if
  546.      you aren't crashing your system and don't mind waiting until the
  547.      transfer finishes to look at your log file. */
  548.   calla(v->io.xpr_fclose,DebugLog);
  549.   DebugLog = NULL;
  550. }
  551. #endif
  552.  
  553.  
  554. /**
  555. *
  556. *   The following functions setup the proper registers for the call-back 
  557. *   functions.
  558. *
  559. **/
  560. #asm
  561.         public  _calla
  562. _calla:
  563.         movea.l 8(sp),a0                ; Second argument goes in a0
  564.         ; Clever trick to allow indirect JSR without using register
  565.         move.l  4(sp),-(sp)             ; Push address of function to call
  566.         rts                             ; "Return" to new function; its rts will...
  567.                                         ; ...return to function who called us
  568.         public  _callaa
  569. _callaa:
  570.         movea.l 8(sp),a0                ; Second argument goes in a0
  571.         movea.l 12(sp),a1               ; Third  argument goes in a1
  572.         move.l  4(sp),-(sp)             ; First  argument is function
  573.         rts
  574.  
  575.         public _callad
  576. _callad:
  577.         movea.l 8(sp),a0                ; Second argument goes in a0
  578.         move.l  12(sp),d0               ; Third  argument goes in d0
  579.         move.l  4(sp),-(sp)             ; First  argument is function
  580.         rts
  581.  
  582.         public  _calladd
  583. _calladd:
  584.         movea.l 8(sp),a0                ; Second argument goes in a0
  585.         move.l  12(sp),d0               ; Third  argument goes in d0
  586.         move.l  16(sp),d1               ; Fourth argument goes in d1
  587.         move.l  4(sp),-(sp)             ; First  argument is function
  588.         rts
  589.  
  590.         public  _calladda
  591. _calladda:
  592.         movea.l 8(sp),a0                ; Second argument goes in a0
  593.         move.l  12(sp),d0               ; Third  argument goes in d0
  594.         move.l  16(sp),d1               ; Fourth argument goes in d1
  595.         movea.l 20(sp),a1               ; Fifth  argument goes in a1
  596.         move.l  4(sp),-(sp)             ; First  argument is function
  597.         rts
  598.  
  599.         public  _calld
  600. _calld:
  601.         move.l  8(sp),d0                ; Second argument goes in d0
  602.         move.l  4(sp),-(sp)             ; First  argument is function
  603.         rts
  604.  
  605.         public  _calldaa
  606. _calldaa:
  607.         move.l  8(sp),d0                ; Second argument goes in d0
  608.         movea.l 12(sp),a0               ; Third  argument goes in a0
  609.         movea.l 16(sp),a1               ; Fourth argument goes in a1
  610.         move.l  4(sp),-(sp)             ; First  argument is function
  611.         rts
  612. #endasm
  613.