home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d1xx / d165 / parsnag.lha / Parsnag / parsnag.c < prev    next >
C/C++ Source or Header  |  1988-11-22  |  6KB  |  179 lines

  1. /* parsnag.c - (c) 1987 Dallas John Hodgson
  2.  
  3.     The purpose of this program is to intercept all write commands
  4. to the parallel.device, and route the data to a disk file. Besides being
  5. an interesting tutorial, this program works wonders for color print dumps!
  6.  
  7.     Entire dithered print grabs can be stored on disk for later use, and
  8. fast-printed by copying to PAR. Great way to benchmark printer (as opposed to
  9. printer driver) graphic throughput. Color separations could be even be
  10. performed on the resulting output; desktop color publishing is here!
  11.  
  12.     HOW IT WORKS
  13.     ============
  14.  
  15.     It helps to think of devices as libraries with only a few standardized
  16. functions; these include Open, Close, Expunge, BeginIO, AbortIO, &
  17. user-defined. As with any other library, these functions are negative-indexed
  18. into a jump table off its base address. What makes devices unique is that :
  19.  
  20.     a) OpenDevice returns more than just an OpenLibrary() base pointer
  21.     b) Most commands are processed through one subroutine (BeginIO)
  22.        acting as command-handler instead of through separate entries.
  23.     c) Devices commonly spawn tasks for asynchronous dirty work, in
  24.        which case they are expected to reply when done.
  25.  
  26.     This program replaces the BeginIO vector of the device in question and
  27. substitutes a 68K handler that does one of two things; if the command requested
  28. of BeginIO is other than a CMD_WRITE, control is transfered to the original
  29. vector. If it is a CMD_WRITE, control is transfered to our handler which writes
  30. the buffer to a disk file & replies to the caller.
  31.  
  32.     There is a lot of information-hiding going inside Exec; DoIO() may act
  33. synchronous, but internally it operates like so :
  34.  
  35.     1) Set the IOF_QUICK flag (politely request synchronous IO)
  36.     2) Call BeginIO()
  37.     3) Check to see if IOF_QUICK still set; if so, the routine really
  38.        performed synchronously, and we can return.
  39.     4) Otherwise, Wait() for a reply message then return.
  40.  
  41.     SendIO() resets the _QUICK bit and calls BeginIO().
  42.  
  43.     OpenDevice(), CloseDevice(), & AbortIO() all call the appropriate
  44. vectors off a user supplied base pointer as opposed to a statically-defined
  45. pointer like IntuitionBase. This way, all I/O routines can work with all
  46. devices.
  47.  
  48.     By the way; the Exec manual was NO help figuring out this stuff.
  49. Metascoping through KickStart was, however. Hmm, maybe this is how those
  50. 'floppy cache' programs are implemented! Hmm...
  51. */
  52.  
  53. extern void ParDispatch(),ParExpunge();
  54.  
  55. long OldExpunge,OldBeginIO; /* referenced by par.asm */
  56.  
  57. #define BUFNAME "ram:parfile"
  58.  
  59. /* most common device functions; first 4 should ALWAYS exist */
  60.  
  61. #define IO_OPEN    (-6)
  62. #define IO_CLOSE   (-12)
  63. #define IO_EXPUNGE (-18)
  64. #define IO_EXTFUNC (-24)
  65. #define IO_BEGIN   (-30)
  66. #define IO_ABORT   (-36)
  67.  
  68. void *IntuitionBase;
  69. struct MsgPort *txport;
  70. struct NewWindow nw;
  71.  
  72. main()
  73. {
  74.   struct IOExtPar *ioblock=0;
  75.   struct Device *deventry;
  76.   struct MsgPort *rport=0;
  77.   struct Window *window=0;
  78.  
  79.   nw.Width=320; nw.Height=10;
  80.   nw.DetailPen=nw.BlockPen=-1;
  81.   nw.IDCMPFlags=CLOSEWINDOW;
  82.   nw.Flags=WINDOWDEPTH|WINDOWDRAG|WINDOWCLOSE;
  83.   nw.Title=(UBYTE *)"ParSnag (c) 1987 John Hodgson";
  84.   nw.Type=WBENCHSCREEN;
  85.  
  86.   IntuitionBase=OpenLibrary("intuition.library",0);
  87.  
  88.   if (!(window=OpenWindow(&nw))) goto cleanup;
  89.   if (!(rport=CreatePort(0,0))) goto cleanup;
  90.   if (!(txport=CreatePort(0,0))) goto cleanup;
  91.  
  92.   if (!(ioblock=(struct IOExtPar *)CreateExtIO(rport,sizeof(*ioblock))))
  93.     goto cleanup;
  94.  
  95.   if (OpenDevice("parallel.device",0,ioblock,0)) goto cleanup;
  96.  
  97.   deventry=ioblock->IOPar.io_Device;
  98.  
  99.   /* misc note : Some Programs (DPaint II) may completely expunge its devices
  100.      in order to reclaim memory. We don't want this to happen, since an
  101.      expunged parallel.device will load in the second time around at a
  102.      different location and WITHOUT our sneaky little patches! */
  103.  
  104.   OldBeginIO=SetFunction(deventry,IO_BEGIN,ParDispatch);
  105.   OldExpunge=SetFunction(deventry,IO_EXPUNGE,ParExpunge); /* dummy expunge */
  106.  
  107.   CloseDevice(ioblock); /* free so others can use */
  108.  
  109.   ParFlush(window); /* blocks until CLOSEWINDOW */
  110.  
  111.   SetFunction(deventry,IO_BEGIN,OldBeginIO); /* restore the damage */
  112.   SetFunction(deventry,IO_EXPUNGE,OldExpunge);
  113.  
  114. cleanup:
  115.   if (window) CloseWindow(window);
  116.   if (rport) DeletePort(rport);
  117.   if (txport) DeletePort(txport);
  118.   if (ioblock) DeleteExtIO(ioblock,sizeof(*ioblock));
  119.  
  120.   CloseLibrary(IntuitionBase);
  121. }
  122.  
  123. ParFlush(window) /* par: to disk handler */
  124. struct Window *window;
  125. {
  126.   struct FileHandle *fp;
  127.   struct IOExtPar *iomsg;
  128.   long result;
  129.  
  130.   for (;;) {
  131.     result=Wait(1<<txport->mp_SigBit | 1<<window->UserPort->mp_SigBit);
  132.  
  133.     if (result & 1<<window->UserPort->mp_SigBit) return; /* back if close */
  134.  
  135.     iomsg=(struct IOExtPar *)GetMsg(txport);
  136.  
  137.     /* File Exists? Append It. File Non-Existent? Create It. */
  138.  
  139.     if (!(fp=Open(BUFNAME,MODE_OLDFILE))) fp=Open(BUFNAME,MODE_NEWFILE);
  140.  
  141.     /* tech note : There's no way of actually knowing when to close the disk
  142.        file, since we don't know when the user actually closes the parallel
  143.        device. We could patch the close vector to find out, but this works
  144.        just as well. */
  145.  
  146.     Seek(fp,0,OFFSET_END); /* extend the file */
  147.  
  148.     Write(fp,iomsg->IOPar.io_Data,iomsg->IOPar.io_Length); /* write buffer */
  149.  
  150.     Close(fp); /* assume each write is the last; can always reopen later */
  151.  
  152.     iomsg->IOPar.io_Flags=0; /* turn off QUICK_IO */
  153.     iomsg->IOPar.io_Error=0; /* no errors */
  154.     iomsg->IOPar.io_Actual=iomsg->IOPar.io_Length;
  155.  
  156.     printf("%d bytes written from $%x.\n",
  157.         iomsg->IOPar.io_Length,iomsg->IOPar.io_Data);
  158.  
  159.     ReplyMsg(iomsg); /* async I/O MUST reply */
  160.   }
  161.  
  162. /* our new BeginIO vector; intercepted CMD_WRITE cmds only! */
  163.  
  164. NewBeginSub(ioblock)
  165. struct IOExtPar *ioblock;
  166. {
  167.   /* the reason we're doing all this interprocess communication is because
  168.      there is NO guarantee that the task writing to the parallel.device is
  169.      a process! So, we send packets out to our process to get the job done.
  170.  
  171.      Note : This code is re-entrant.
  172.    */
  173.  
  174.   PutMsg(txport,ioblock); /* signal our DOS process to write! */
  175.  
  176.   return(0); /* return, no error in D0, ROM waiting for process to reply */
  177. }
  178.