Console Handler code review (if you've already write one or)

A forum for general AmigaOS 4.x support questions that are not platform-specific
Post Reply
User avatar
gdridi
Posts: 64
Joined: Sat Aug 11, 2012 10:17 am

Console Handler code review (if you've already write one or)

Post by gdridi »

Hello Amigans,

Here are our private virtual DevCon2016 !

I want to write an console handler after writing the ar.console.device , although it is hard because no official RKM example,
and wondering what (ACTION_) is misimplememted.

Because, it works with the command copy on itself, i.e : "copy arCon: to arCon:" shows each line twice (copy itself) but seems _to be slowed_ when I replace copy with a 'type' command : "type arCon: to arCon:".

Also, it can't read (input) correctly with program like ed (UNIX line editing) with redirection like "Ed <arCon: " . It did not work, input seems not to be in ! when I put 'a' append command, return and write text, ending with a line with a period '.' as usual. And '1' or 'p' for printing out !

Perhaps, I'm not implementing ACTION_WAIT_CHAR , the right way ; I though ! But, It is buffering too that I suspect..

Here is the code acom.c : gcc -no-startup-files -o arCon_handler acom.c

The entry in the mountlist is
ArCon:
Handler = arCon_handler
Stack = 10000
...

Code: Select all

/*
 *  DOSDEVICE.C 	V1.10	2 November 1987
 *  ARAB CONSOLE HANDLER  7    October 2017 minus one year !
 *
 *  EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C   PUBLIC DOMAIN.
 *
 *  By Matthew Dillon.
 *
 *  Modified for OS4 by Fredrik Wikstrom
 *
 *  Adaptated for a [ar]console handler by Gilles Dridi
 *
 */

// variables below belongs to startX() process
short NLo;
UBYTE b[1024], s[1024];
struct window *win;

int _startX();	// entry code for my other ConsoleArabOther to be launch on openning_action, which communicates (input/output) with the [ar]console.device


// Here is the main process _start (without an 'X') handler launched automatically when entering command : "mount arcon: from mountlist"

int _start(int argc, char *argv[]) {
	PACKET *packet;
	short   error;
	MSG     *msg;
	int   noquit;
	void    *tmp;

	/*
	 *	Initialize all global variables.  SysBase MUST be initialized before
	 *	we can make Exec calls.  AbsExecBase is a library symbol
	 *	referencing absolute memory location 4.  The DOS library is openned
	 *	for the debug process only.
	 */

	PROC	*DosProc, *OtherProc;   /*	Our Process, and "(a)com.c"				    */
	DEVNODE *DosNode;   /*	Our DOS node.. created by DOS for us	    */
   	struct FileHandle *fh;

	SysBase = *(struct Library **)4;
	IExec = (struct ExecIFace *)((struct ExecBase *)SysBase)->MainInterface;
	DOSBase = IExec->OpenLibrary("dos.library", 0L);
	IDOS = (struct DOSIFace *)IExec->GetInterface(DOSBase, "main", 1, NULL);	// As Exec library it is normally always open ...

	DosProc = (struct Process *)IExec->FindTask(NULL);

	IExec->WaitPort(&DosProc->pr_MsgPort); 	//  Get Startup Packet
	msg = IExec->GetMsg(&DosProc->pr_MsgPort);
	packet = (PACKET *)msg->mn_Node.ln_Name;
	packet->dp_Res1 = DOS_TRUE;
	packet->dp_Res2 = 0;

	/*
	 *  Loading DosNode->dn_Task causes DOS *NOT* to startup a new
	 *  instance of the device driver for every reference.	E.G. if
	 *  you were writing a CON device you would want this field to
	 *  be NULL.
	 */
	DosNode = BTOC(packet->dp_Arg3);

	/*
	 *	Set dn_Task field which tells DOS not to startup a new
	 *	process on every reference.
	 */
	DosNode->dn_Task = NULL;	//&DosProc->pr_MsgPort;

	packet->dp_Res1 = DOS_TRUE;
	packet->dp_Res2 = 0;

	returnpacket(packet, DosProc);


	NLo = 0;
top:
	finDiff = 0;
	noquit = 1;
	while(noquit) {
		IExec->WaitPort(&(DosProc->pr_MsgPort));
		while ( msg = IExec->GetMsg(&(DosProc->pr_MsgPort)) ) {
			ubyte *ptr;
			packet = (PACKET *)msg->mn_Node.ln_Name;
			packet->dp_Res1 = DOS_TRUE;
			packet->dp_Res2 = 0;
			error = 0;
			dbprintf("Packet: %3ld %08lx %08lx %08lx %10s ",
				packet->dp_Type,
				packet->dp_Arg1, packet->dp_Arg2,
				packet->dp_Arg3,
				typetostr(packet->dp_Type)
			);

			switch(packet->dp_Type) {
				case ACTION_DIE:	    /*	attempt to die? 		    */
					{
						noquit = 0;	    /*	try to die			    */
					}
					break;

				case ACTION_OPENRW:     /*	FileHandle,Lock,Name	    Bool    */
//					break;
				case ACTION_OPENOLD:    /*	FileHandle,Lock,Name	    Bool    */
//					break;
				case ACTION_OPENNEW:    /*	FileHandle,Lock,Name	    Bool    */
					{

					if ( IExec->FindTask("ConsoleArabOther") == NULL ) {
						OtherProc = IDOS->CreateNewProcTags( 
										NP_Entry, _startX,
										NP_Name, "ConsoleArabOther",
										TAG_DONE);

						IDOS->Delay(50);	// Hard Hack : to be sure the process is running
					//	Or ... try something with signals
					//	IDOS->NotifyDosListChange(NULL, SIGBREAKB_CTRL_E, 0);
					//	IExec->Wait(SIGBREAKF_CTRL_E);
					//	IDOS->NotifyProcListChange(NULL, NPLC_END, 0);    /* Turn it off */

					}
					fh=(struct FileHandle*)BADDR(packet->dp_Arg1);
					/* Nicht-Interaktives File */
					fh->fh_Port=DOS_TRUE;
				//	fh->fh_Args = (long)fh;	// fh_Arg1 !! or = CTOB(fh); ?
					}
					break;
				case ACTION_WAIT_CHAR:  /*	 Timeout, ticks 	    Bool       */
					if ( NLo ) goto label_ToBeREAD;
					//IDOS->Delay(10); 	// Wait : packet->dp_Arg1 ou 10 or nothing to debug
					if ( NLo )	{
label_ToBeREAD:			;//packet->dp_Res1 = DOS_TRUE; //	already set at the top
						packet->dp_Res2 = (long)1;	// line to read (1 for my simple (ar)console handler)
					} else {
						//packet->dp_Res1 = DOS_FALSE;	//	done at the end
						error = ERROR_ACTION_NOT_KNOWN;
					}
					break;
				case ACTION_READ:	    /*	 FHArg1,CPTRBuffer,Length   ActLength  */
					{
					ubyte  *ptr = (ubyte *)packet->dp_Arg2;
					long   left = packet->dp_Arg3;
					long j = 0;

					IExec->Forbid();
					if ( NLo ) {	// set by the ConsoleArabOther task(ProcessHandler)
						while( left--) {
							if ( b[j] == '\n' ) 	{ *ptr++ = b[j++]; break; } // j+ not to exit
							if ( &b[j] < &b[1024-1] ) *ptr++ = b[j++];	// 1024 size of buffer (it could be a #define MAXBUF 1024)
						}
						b[0] = 0;
						*ptr++ = 0;	// Really needed ?

						NLo = 0;
					} else {
						*ptr = 0; j = 1;		// We try not to escape ... from polling (see below) ! READ , READ, READ, etc
					}
					IExec->Permit();

					if ( finDiff )	{ finDiff = 0; packet->dp_Res1  = 0; }	// EOF (finDiff set by ConsoleArabOther when quitting this other process)
					else 	packet->dp_Res1 = j;
					} break;
				case ACTION_FLUSH:	    /*	 writeout bufs, disk motor off	       */
					if ( packet->dp_Arg2 && packet->dp_Arg2  != '\0' ) //packet->dp_Res1 = DOS_TRUE;
						; // fall-through WRITE
					else {
						// packet->dp_Res1 = DOS_TRUE;	// GURU book says must always be DOS_TRUE !!
						break;						// stop here and no error we have Res1 == DOS_TRUE
					}
				case ACTION_WRITE:	    /*	 FHArg1,CPTRBuffer,Length   ActLength  */
					{ UBYTE d[16];
					ubyte  *ptr  = (ubyte *)packet->dp_Arg2;
					long   left = packet->dp_Arg3;
					long	  left0 = left, j = 0;
					short nl = 0;

					if ( *ptr == '\0' ) {	// nothing to write ! we are (in) polling (see above)
						packet->dp_Res1 = left;
						break;
					}

					if ( io == NULL ) break;	// don't forget the is set by ConsoleArabOtherTask (ProcessHandler)

					while(left--) {
						if ( *ptr == '\n' ) { nl = 1; break; }
						decale1Tampon(s,k);	// shiftBuffer
						s[k++] = *ptr++;
					}
					//s[k] = 0; // note that if we could be inserting text
		
					if ( !(ConsoleMP2 = (struct MsgPort *)CreatePort("Arabe2.Console", 0))) {
 						break;
 					}
					if ( !(io2 = (struct IOStdReq *)CreateExtIO(ConsoleMP2, (LONG)sizeof(struct IOStdReq)))) {
 						break;
 					}
	
					// duplicate from "io" (simple, without io[2] ) used in/by ConsoleArabOtherTask launch when openned
					io2->io_Device = io->io_Device;
					io2->io_Unit     = io->io_Unit;

					if ( nl ) { k = 0; while(s[k] != 0) k++; s[k++] = '\n';		s[k] = 0; }

					io2->io_Data = (APTR)&s[0];
					io2->io_Length = -1;
					io2->io_Command = CMD_WRITE;
					IExec->DoIO((struct IORequest *)io2);

					if ( nl ) { s[0] = k = 0; }

					DeleteExtIO(io2);
					DeletePort(ConsoleMP2);

					packet->dp_Res1 = left0;
					} break;
				case ACTION_CLOSE:	    /*	 FHArg1 		    Bool:TRUE  */
					break;
				case ACTION_DISK_INFO:  /*	InfoData	  Bool:TRUE    */
					if ( win ) { ((struct InfoData *)packet->dp_Arg1)->id_VolumeNode = (BPTR)win;	// win var. from ConsoleArabOtherTask launch at the top
							 ((struct InfoData *)packet->dp_Arg1)->id_InUse = (BPTR)packet;
					} else error = ERROR_ACTION_NOT_KNOWN;
					break;
				case ACTION_INHIBIT:    /*	 Bool			    Bool       */
					/*  Return success for the hell of it	*/
					break;
				case ACTION_RAWMODE:    /*	 Bool(-1:RAW 0:CON)	    OldState   */
				case ACTION_SEEK:
				case ACTION_EXAMINE_NEXT:
				case ACTION_EXAMINE_OBJECT: /*   Lock,Fib			Bool	   */
				case ACTION_EXAMINE_FH: /* added by F.W */
				case ACTION_INFO:	    /*	Lock, InfoData	  Bool:TRUE    */
				case ACTION_PARENT:     /*	 Lock			    ParentLock */
				case ACTION_DELETE_OBJECT: /*Lock,Name		    Bool       */
				case ACTION_CREATE_DIR: /*	 Lock,Name		    Lock       */
				case ACTION_LOCATE_OBJECT:	/*   Lock,Name,Mode		Lock	   */
				case ACTION_COPY_DIR:   /*	 Lock,			    Lock       */
				case ACTION_FREE_LOCK:  /*	 Lock,			    Bool       */
				case ACTION_SET_PROTECT:/*	 -,Lock,Name,Mask	   Bool       */
				case ACTION_SET_COMMENT:/*	 -,Lock,Name,Comment	   Bool       */
				case ACTION_RENAME_DISK:
				case ACTION_RENAME_OBJECT:/* SLock,SName,DLock,DName    Bool       */
				/*
				 *	A few other packet types which we do not support
				 */
				case ACTION_MORECACHE:  /*	 #BufsToAdd		    Bool       */
				default:
					error = ERROR_ACTION_NOT_KNOWN;
					break;
			}
			if (packet) {
				if (error) {
					dbprintf("ERR=%ld\n", error);
					packet->dp_Res1 = DOS_FALSE;
					packet->dp_Res2 = error;
				} else {
					dbprintf("RES=%06lx\n", packet->dp_Res1);
				}
				returnpacket(packet, DosProc);
			}
		}
	} // while(noquit);

	dbprintf("Can we remove ourselves? ");
//	Delay(50);	    /*	I wanna even see the debug message! */
	IExec->Forbid();
	if ( packetsqueued(DosProc) )	// || GetHead((struct List *)&FHBase) || GetHead((struct List *)&LCBase)
								//|| GetHead((struct List *)&RFRoot.list))
	{
		IExec->Permit();
		dbprintf(" ..  not yet!\n");
		goto top;		//  sorry... can't exit 
	}

	/*
	 *	Causes a new process to be created on next reference
	 */

	DosNode->dn_Task = FALSE;

	/*
	 *	Remove debug process, closedown, fall of the end of the world
	 *	(which is how you kill yourself if a PROCESS.  A TASK would have
	 *	had to RemTask(NULL) itself).
	 */

	//IExec->DropInterface((struct Interface *)IDOS);	// normally not to comment
	//IExec->CloseLibrary(DOSBase);
}

// Twin process
int _startX() { win = NULL; NLo = 0; b[0] = 0;
	...
	initialize win-dow open [ar]console.device
	...
	communicates (input/output : "io" request) with the [ar]console.device
	and fills b[1024] and set NLo = NewLineo.
	...
}

/*
 *  PACKET ROUTINES.	Dos Packets are in a rather strange format as you
 *  can see by this and how the PACKET structure is extracted in the
 *  GetMsg() of the main routine.
 */

void returnpacket(struct DosPacket *packet, struct Process * DosProc) {
    struct Message *mess;
    struct MsgPort *replyport;

    replyport		     = packet->dp_Port;
    mess		     = packet->dp_Link;
    packet->dp_Port	     = &DosProc->pr_MsgPort;
    mess->mn_Node.ln_Name    = (char *)packet;
    mess->mn_Node.ln_Succ    = NULL;
    mess->mn_Node.ln_Pred    = NULL;
    IExec->PutMsg(replyport, mess);
}

/*
 *  Are there any packets queued to our device?
 */

int packetsqueued (struct Process * DosProc) {
    return ((void *)DosProc->pr_MsgPort.mp_MsgList.lh_Head !=
	    (void *)&DosProc->pr_MsgPort.mp_MsgList.lh_Tail);
}
Hi tonyw ! Help all !

I think an RKM - console: handler , excerpt or simplified code example on the Amiga wiki will help too.

BEST REGARDS -- DGILLES
User avatar
tonyw
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 1479
Joined: Wed Mar 09, 2011 1:36 pm
Location: Sydney, Australia

Re: Console Handler code review (if you've already write one

Post by tonyw »

Writing a handler (for a console or anything else) is not a trivial matter and requires good knowledge of DOS and the kernel and access to the OS source code for examples. It is not part of the SDK and AFAIK was never well described in the RKMs. Without that access you are fighting uphill.

Why do you want to write a new console handler? Can't you use existing system components?
cheers
tony
User avatar
salass00
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 530
Joined: Sat Jun 18, 2011 3:12 pm
Location: Finland
Contact:

Re: Console Handler code review (if you've already write one

Post by salass00 »

If you haven't found it yet, SDK:Documentation/AutoDocs/dos.dospackets.doc has very useful information on how to implement the DOS packets/vector calls for a handler or file system.
User avatar
gdridi
Posts: 64
Joined: Sat Aug 11, 2012 10:17 am

Re: Console Handler code review (if you've already write one

Post by gdridi »

@salass
My SDK is from 2009?
I should have to make an update ... because the DOS packets was not in the SDK.

Tony my code lacks a blocking READ

Code: Select all

label_READ:
if ( Nlo ) {
...
} else {
   *ptr = 0;
    j = 1; // no more needed polling because we wait
    // ***** blocked : better is to Wait a signal for instance *****
    while( NLo == 0 ) if ( finDiff ) goto fin; else ; // ";" blocked here !!
    j = 0;
    goto label_READ;
}
fin:
...
No it works with copy and type command because copy was doing read and write one to one interlaced but type was doing say 40 reads and 40 writes interleaved so with my read not blocking, I had ! (Add) "huge" delay between input and display of a line.

Now that I found the solution for basics AmigaOS commands I.e. :
Copy from arabCon: to arabCon:
And
Type arabCon: to arabCon:

Want my Arabic DOS handler to work with basic UNIX commands like the line by line editor ed :
Ed < arabCon: > arabCon:
Not working at all but :
Ed < console: >console:
Works pretty well

The console: since 2.0? Support Unix like "fstat" but I dont' know how to implement it properly and if setting fib_DirEntryType to ST_FILE or ST_PIPEFILE returning 0 for Res2 are enough !! In fact it bugs.

So the packets more are :
972 : not documented (seems to be cryptic... )
1028 : have to implement a correct? CHGMOD UNIX like
1034 : the UNIX FSTAT are setting fib_FileName to capital letters ending ´:´ ( "ARABCON:" ) and fib_DirEntryType to ST_FILE or PIPEFILE (but what the difference) are enough ...

It seems to be a valid reasons to document new DOS packets for UNIX transduction.. migration .. you said Multicore tasks balancing load -- i stopped here this allusion was a joke !

There's a lot? of core text binutils from GNU/FSF which seem to be working with AmigaOS thanks to 2.0? extended A-DOS packets with just a recompilation thanks to GCC port, some UNIX core commands and no need to ixemul.library : Great !!

For this reason the " Console: " DOS handler might be detailed / excerpted or packets to implement for UNIX commands support .. compatibility explained.

Tony or Thomas F. should have a look ???

Thanks guys (tony, Salas's..) DGILLES
User avatar
salass00
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 530
Joined: Sat Jun 18, 2011 3:12 pm
Location: Finland
Contact:

Re: Console Handler code review (if you've already write one

Post by salass00 »

gdridi wrote:@salass
My SDK is from 2009?
I should have to make an update ... because the DOS packets was not in the SDK.
Latest public SDK (53.30) is from September 2015:

http://hyperion-entertainment.biz/index ... &parent=30

Autodocs are also available on the AmigaOS wiki now BTW (including the DOS packets one):

http://wiki.amigaos.net/wiki/Autodocs:Main
The console: since 2.0? Support Unix like "fstat" but I dont' know how to implement it properly and if setting fib_DirEntryType to ST_FILE or ST_PIPEFILE returning 0 for Res2 are enough !! In fact it bugs.
If you want to be compatible with con-handler you should fail all examine packets with dp_Res1 set to DOSFALSE and dp_Res2 set to ERROR_ACTION_NOT_KNOWN.

There is no special entry type for consoles so the way consoles are identified is by the fh_Interactive field in the FileHandle structure. IDOS->IsInteractive() is used to check this field.
So the packets more are :
972 : not documented (seems to be cryptic... )
1028 : have to implement a correct? CHGMOD UNIX like
1034 : the UNIX FSTAT are setting fib_FileName to capital letters ending ´:´ ( "ARABCON:" ) and fib_DirEntryType to ST_FILE or PIPEFILE (but what the difference) are enough ...
I couldn't find anything on packet 972 but 1028 is ACTION_CHANGE_MODE and 1034 is ACTION_EXAMINE_FH.
User avatar
salass00
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 530
Joined: Sat Jun 18, 2011 3:12 pm
Location: Finland
Contact:

Re: Console Handler code review (if you've already write one

Post by salass00 »

The reason you want to fail ACTION_EXAMINE_FH is that both clib2 and newlib fstat() implementations only check IDOS->IsInteractive() if the examine fails and IoErr() returns ERROR_ACTION_NOT_KNOWN.

It is the only way to get your handler to be recognised as a console/terminal by fstat() and isatty() functions.
User avatar
gdridi
Posts: 64
Joined: Sat Aug 11, 2012 10:17 am

Re: Console Handler code review (if you've already write one

Post by gdridi »

Thank you salass

I just forgot to treat a char by char in the ACTION_READ too.

Code: Select all

If ( NLo ) {
   While( left-- ) copy some b[j++] in *ptr++
   ...
   *** Shift left by the ( left0-left ) the b[] array ***
} else
...
Super ! Isn't it ?

DGILLES
Post Reply