As of: 1990-08-23
This driver concept is intended to enable calling of memory-resident device drivers of various types from any desired programs. The communication between the programs and the drivers takes place via a data structure made available by the driver. A fixed kernel for the data structure is prescribed for this, which must be adhered to by all drivers that comply with this standard. In addition, some extensions are possible. From the version numbers the calling program then can establish if and which extensions of the data structure are present. All data structures are upwardly compatible.
Preliminary remarks
All values that require more than 1 byte are defined always in
the order MSB...LSB. The sample codes are formulated in 68000
Assembler. The description for ATARI ST follows.
Basis of the GDPS is the vector $41C which is unused by the operating system. Normally this vector contains 0L. In GDPS this vector points to the header of the first device driver in the chain. This header is built up as follows (rAdr=relative Address, S=Size):
rAdr S Meaning OH L Pointer to the next driver header, or 0 in last driver of the chain 4H L 'GDPS' = 0x47445053 magic string as identifier for GDPS
From this follows the method for linking a driver in and out of the chain.
Linking in of a driver
The driver copies the vector placed at 0x41C into its header.
Subsequently it enters the address of its header into 0x41C.
Assembler example (to be executed in supervisor-mode):
len header(pe),a0 * Address of the GDPS header clr.l (a0) * Initialize NEXT to 0 cmp.l 0x41C,a0 * = old value beq unten * Yes, no endless chaining! move.l 0x41C,(a0) * sonst NEXT eintragen move.l a0,0x41C * and link header into the chain
Unlinking of a driver
The driver searches the chain for the pointer to its header. It
then replaces it with a pointer found in its header.
Assembler example (to be executed in supervisor-mode):
move.l #0x41C,a0 lea header(pe),a1 search: cmp.l (a0),a1 beq found move.l (a0),a0 bra search found: move.l (a1),(a0)
Searching for a driver
Searches for a driver too should be performed in
supervisor-mode. As the operating system does not set the variable
0x41C to 0 at a warm start, one has to check for the magic string for
each vector found.
There follows a sample routine in GFA Basic notation that writes
the found and valid vectors into an integer array:
DIM vector%(31) ! Here addresses of the drivers lie index%=0 ! Initialize counter to 0 adr%=SLPEEK($41C) ! Address of the first driver WHILE adr%<>0 AND SLPEEK(adr%+4)=$47445053 ! Test: Address valid and magic OK? vector%(index%)=adr% ! If yes, remember address inc index% ! Increment counter by one WEND PRINT index%;" Driver found!"
rAdr S Meaning 0H L Pointer to the next driver header, or 0 in last driver of the chain 4H L 'GDPS' = 0x47445053 magic string as identifier for GDPS 8H W 100 = Version number (Data structure definition) * 100 AH W Driver type CH L Pointer to driver info, 0-terminated string, max. 32 chars. 10H L Pointer to programmer/Copyright, 0-terminated string, max. 32 characters 14H onwards follows the driver-specific data structure.
There may be any number of drivers in memory, even drivers of the same type!
The driver types are first subdivided into groups:
Group | Driver type | Meaning |
0 | 000-0FF | Graphical input devices |
1 | 100-1FF | Graphical output devices |
2 | 200-2FF | Various input interfaces (ports) |
3 | 300-3FF | Various output interfaces (ports) |
4 | 400-4FF | I/O interfaces |
5 | 500-5FF | Mass storage |
6-F | 600-FFF | Reserved |
10-FF | 1000-FFFF | 'Private' drivers, whose data structure is not |
contained in the DPS definitions |
Introduction: With bi-level data, a set bit corresponds to a pixel that is present. With multi-value data, a 0 corresponds to `nothing` and with increasing values the relevant colour intensity or brightness rises.
Warning: With commands in the 10xH group, greyscale images are inverted!
14H W Scanner description If this WORD contains the value 0 then the scanner/driver is not yet initialized, execute command 105H Bit 0: Bi-level possible 1: Dithering possible 2: Multi-value possible 3: Multi-value and dithering possible 4: Bi-level colour possible 5: Colour dithering possible 6: Multi-value in colour possible 7: Multi-value in colour and dithering possible 8: Compression of data (see below) possible 9: Block-wise return possible 10: Single sheet feed with separate command 11: Single sheet feed automatic 12: Prescan possible 13: Virtual memory management possible (GDPS-Version >= 1.10) 14: Reserved (0) 15: Reserved (0) Multi-value and dithering: Special mode, always 2 bits/pixel 16H W Number of colours 18H W Possible multi-value bit depths Makeup: Bit 0 set: Monochrome possible Bit 1 set: 2 grey-steps possible Bit 2 set: 4 grey-steps possible Bit 3 set: 8 grey-steps possible etc... 1AH W Reserve scanner 1CH W Command to scanner (0=Ready, 1-FFH reserved!!!) 1EH L Pointer to command structure
A scanner driver with its own user interface is assumed here. If a program wants to address the scanner driver, then this is done as follows:
a) Wait until WORD 1AH = 0 (scanner free) b) Reserve scanner be entering a WORD in 1AH c) Create a command structure d) Enter the pointer to the command structure in 1EH e) Enter the command in 1CH f) In an EVENT loop (!!) (timer- or multi-event) wait until the command WORD of the scanner driver is set to 0 again g) Repeat steps from c) onwards as often as required h) Release scanner (enter 0 in 1AH)
The command structure to be made available by the calling program has three tasks:
a) Passsing of the command structure b) Return of a success or error message c) Return of the parameters used
Makeup of the command structure:
00H W Return value, is initialized by the calling program to 0. After processing the command, the scanner driver enters 0xFFFF (=OK) or a positive error-message here. If the scanner is operating in block mode, then 0xFFFE is entered after a block and 0xFFFF after the last block. If working in block mode AND in colour, there are two options: a) The scanner scans the colour separations individually; then the passing will proceed as above, but several times corresponding to the number of colours. b) The scanner scans all colours simultaneously; in that case 0xFFFD is entered after the blocks. The returned block then consists of 'number of colours' equal-sized areas. 02H W Permitted scan modes. Makeup of this WORD is as for 14H; a set bit denotes that the scanner driver may use the corresponding mode. 04H W Permitted bit depths (makeup as above for 18H) 06H L Pointer to memory 0AH L Number of bytes in memory (Return: Number of bytes used) 0EH W Bytes per scanline, or 0 (always mod 2!) 10H W Number of scanlines, or 0 12H W Width of the image in 1/10 mm, or 0 14H W Height of the image in 1/10 mm, or 0 16H W Resolution in main scan direction (X) in dpi, or 0 18H W Resolution in sub scan direction (Y) in dpi, or 0 (For return: Resolution = Resolution * zoom factor!) 1AH W Modulo value for scanlines (in bytes, e.g. 2 = 2*n bytes per scanline) 1CH W X-position of the image in 1/10 mm 1EH W Y-position of the image in 1/10 mm Since GDPS Version >= 1.10: 20H L Serial number of the calling program 24H W (!I)add_bits:(!i) Additional memory required by program (in bits per pixel) 26H L Pointer to 'Dchange_pointer' (function of the vmm) 2AH L Pointer to 'Dupdate' (function of the vmm) 2EH W (!I)v_handle(!i) READ (vmm) 30H W (!I)v_handle(!i) WRITE (vmm) 32H W (!I)virt_flag(!i) (1 = vmm being used)
vmm = virtual memory management
After processing a command the scanner driver enters the actually used values into this data structure.
If the image size is to remain fixed, then this can be defined with the WORDS 0EH, 10H or 12H, 14H, 1CH, 1EH. If both value pairs are preset, the default in bytes will be used. If the scanner is not able to scan in this format, a change may be made to suitable values if necessary; therefore, check the return vales!!
Compression:
Bi-level and dither data are always packed in such a way that 8 pixels are present in a byte. For this the first pixel is entered in the most significant bit.
Multi-value data are present either uncompressed, in which case one pixel occupies exactly one byte. Here (with fewer than 256 grey steps) the data is always formatted to the MS bit, and the LS bits are filled if appropriate (depending on the scanner with 0 or 1). In compressed form the multi-value data are packed in such a way that byte borders are not exceeded; if necessary empty bits are filled with (depending on the scanner) with 0 or 1:
+--+--+--+--+--+--+--+--+ 2 bits/pixel |B1|B0|B1|B0|B1|B0|B1|B0| -> 4 pixels/byte +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ 3 bits/pixel |B2|B1|B0|--|B2|B1|B0|--| -> 2 pixels/byte +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ 4 bits/pixel |B3|B2|B1|B0|B3|B2|B1|B0| -> 2 pixels/byte +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ 5 bits/pixel |B4|B3|B2|B1|B0|--|--|--| -> 1 pixel/byte +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ 6 bits/pixel |B5|B4|B3|B2|B1|B0|--|--| -> 1 pixel/byte +--+--+--+--+--+--+--+--+ etc.
If the compression bit is not set in the command structure, then the data are decompressed by the scanner driver if necessary. If the bit is set, however, then after processing the command one has to test whether the data are really compressed.
Fixed commands:
100H: Scan The user dialog is called up, and if appropriate the scan process is performed 101H: Continue In block mode this command continues the scan 102H: Scan without dialog The scanning process is initiated without further user intervention; the same user-set parameters are used as for the previous scan 103H: Next sheet The next sheet is fed in from the single-sheet tray 104H: Prescan Executes a prescan with set parameters 105H: Initialize scanner/driver Gets device description from scanner
Commands 102H and 103H are meant specially for automatic optical character recognition (OCR).
Since GDPS Version 1.10 new commands exist (as above, but instead of 10xH, 20xH is used). If these commands are used, one can work in a virtual mode provided virt_flag=1. Furthermore greyscale images with these commands are delivered as follows:
0=black 255=white
Warning: With 20xH commands the calling program must pass the extended command structure according to GDPS 1.10 !!!
Error-messages in return value:
FFFEH: Block ready, further blocks follow FFFFH: Scan process (possibly for one colour) completed, last (or only) data block is present 0H: Wait and have a cup of tea (if appropriate wait with an EVENT loop) 1H: Unknown command 2H: Scanner error 3H: Abort by the user 4H: Out of paper (only with single-sheet feed) 5H: Out of memory: The user has set too large a paper format 6H: Scanner not yet initialized
The error-messages are displayed to the user of the driver. They only serve as information for the calling program.
#define VOR 1 #define ZURUECK -1 #define MITTE 0 /********************************************************************/ /* Scanner structures */ /********************************************************************/ typedef struct /* Passing structure for scanner */ { void *next; /* Pointer to the next driver */ char ident[4]; /* Magic GDPS as driver identifier */ int version; /* Version number, currently < 200 */ int type; /* Driver type, 0 for scanner */ char *info; /* Pointer to driver info and to */ char *copyright; /* the Copyright message */ int devdescr; /* Device description flags */ int colours; /* Number of colours, 0=B/W */ int deep; /* Possible bit-depths */ int free; /* Flag whether scanner is free */ int scommand; /* Command to scanner */ void *command; /* Pointer to the command structure*/ } SCANHEADER; typedef struct /* Command structure for GDPI scanners*/ { int result; /* Result that reports drivers */ int modes; /* Permitted scan modes */ int depth; /* Image depth in bits/pixel */ void *vmemory: /* Where image should be placed */ long vmaxlen; /* Available memory */ int bytewidth; /* Width of a line in bytes */ int height; /* Height of the image in lines */ int mmwidth; /* Width (!U)and(!u) */ int mmheight; /* Height in 1/10 millimeters */ int xdpi; /* Resolution in X (!U)and(!u) */ int ydpi; /* Y direction */ int modulo; /* 2=>image becomes word-aligned */ int start_x; /* Top left corner X in 1/10 mm */ int start_y; /* Top left corner Y in 1/10 mm */ long ser_no; /* Serial number */ /**** The following part is defined only for calls of the scanners with the commands of the 0x2XX series ******/ int add_bits; /* Specifies how many additional */ /* bits are required. For instance */ /* CRANACH Studio has 2 masks for */ /* each image. If one scans a bit- */ /* map, the program requires not */ /* just 1 bit per pixel, but three */ /* bits. In a similar way, with a */ /* greyscale picture one has to */ /* calculate 8 + 2 = 10 pixels. If */ /* this value is not allowed for, */ /* then it may happen that after */ /* scanning CRANACH Studio can not */ /* open its window, because though */ /* memory is available for the */ /* scanned image, there is none */ /* for the required masks. */ /* One needs, for exampple, two */ /* additional bits for the mask. */ void *Dchange_pointer; /* Pointer to this function */ void *Dupdate; /* Pointer to that function */ int read; /* Virtual read buffer index */ int write; /* Virtual write buffer index */ int virt_flag; /* Flag whether working with */ /* virtual memory management(1=yes)*/ } SCANCOM; SCANCOM scancom; void *Dchange_pointer( void *pointer, int v_handle, int richtung, long *max_vor, long *max_zurueck );
void Dupdate(int v_handle);
All tms products work with virtual memory management. This means that it does not have to access memory areas that are present in RAM. The virtual memory management implemented in tms products was optimized for the requirements of images.
General procedure: The driver passes in scancom.write a virtual handle. With this handle one can then access the virtual data. The data are obtained automatically from the hard drive when required, and stored.
One possible application would be:
UCHAR *real;
long max_forward, max_back;
scancom->vmemory points to the free virtual memory. This is to be treated just like normal memory. so, say, free memory from addr 16MB to addr 50MB with scancom->vmemory = addr 30MB. As this address does not really exist, the pointer must be mapped to the real memory and the data loaded from the hard drive. This is achieved with the function Dchange_pointer.
real = (UCHAR*)Dchange_pointer( scancom->vmemory, scancom->write, FORWARD, &max_forward, &max_back );
So real now points to an area of RAM.
scancom->write is the memory handle passed by the program.
FORWARD tells the memory management that we want to move as far as possible to the start of the memory. This optimizes disk accesses.
max_forward returns how many bytes from real one may move forward in the memory.
max_back returns how many bytes from real one may move backwards in the memory.
If these limits are reached then Dchange_pointer must be called anew. The minimum lengths for max_forward and max_back are:
FORWARD | BACK | MID | |
max_forward: | 32k | 0k | 16k |
max_back: | 0k | 32k | 16k |
Der Ram Speicher ist in 6 Blöcke unterteilt, von denen jeder einen he RAM memory is subdivided into 6 blocks, which each can represent a separate or also an overlapping portion of the virtual memory. To guarantee with overlapping blocks that after a memory alteration all blocks will reproduce the current memory contents, call the function Dupdate(scancom->write). This updates the other blocks. However, one only needs to use Dupdate before accessing a different block. For a scanner driver that only uses the block scancom->write, it suffices therefore to call Dupdate(scancom->write) at the end of the scanning process.
Sample code for the deletion of 10 MB as of address 32 MB:
v_pointer=32MB size=10MB while(size>0) { real=Dchange_pointer( v_pointer, scancom->write, FORWARD, &max_forward, &max_back); if(max_forward<=size) { memset(real,0,max_forward); size-=max_forward; v_pointer+=max_forward; } else { memset(real,0,size); size=0; v_pointer+=size; } } Dupdate(scancom->write); !!!!!!!!!!!!! *************************************************************************** Old programs call the scanner with the command 0x100 (and not 0x200). These programs (e.g. tms CRANACH) also do not yet use virtual memory management. Therefore the SCANCOM structure is not defined from the position marked onwards. The functions Dchange_pointer or Dupdate should then be replaced in the driver by dummy functions. /********************************************************************/ Possible application as ACC: /********************************************************************/ /* main() */ /* */ /* Heart of the program */ /********************************************************************/ int main( void ) { int work_in[12],work_out[58],dummy; int buffer[20]; appl_id = appl_init(); /* Open an own workstation */ handle=graf_handle(&dummy,&dummy,&dummy,&dummy); for ( dummy=0; dummy<10; work_in[dummy++]=1 ); work_in[10]=2; v_opnvwk(work_in,&handle,work_out); if(!rsrc_load("SCANNER.RSC")) { form_alert(0,NO_RSC_FILE); goto FOREVER; } if( appl_id != -1 ) { if( !_app ) { scanner_moeglichkeiten(); menu_id = menu_register( appl_id, " SCANNER" ); event_loop(); /* Here one waits for the call of the */ /* driver by the program; the dialog is */ /* handled, the image is scanned and */ /* the values placed in vmemory */ } } FOREVER: /* Initialization has not worked */ while(1) evnt_mesag((int*)buffer); } /********************************************************************/ /* Initialize scanner */ /********************************************************************/ void scanner_moeglichkeiten() { long **zeiger; long stack; /********** Divert pointer to GDPS *******************************/ stack=Super(0); zeiger = (long **)0x41c; if(*(*zeiger+1)== 0x47445053l) header.next = *zeiger; else header.next = NULL; *zeiger = (void *)&header; Super((void *)stack); /********************************************************************/ sprintf(header.ident,"GDPS"); header.version =100; header.type =0; /* Scanner */ header.info =info; header.copyright =copyright; header.devdescr =1 | 2 | 4; /* Device description flags */ header.colours =1; /* Number of colours, 0=B/W */ header.deep =1 | 16; /* Possible bit-depths */ header.free =0; header.scommand =0; /* Command to scanner */ header.command =&command; }