#main(argc,argv)
17 messages in this thread
Two Major Questions (involving a bunch of minor puzzles).
Finishing up a little program, almost ready to release it to the world.
Just two relatively simple things to add: WorkBench startup capability and an
ASL file requester for those running from OS 2.0+. Both simple things are
proving formidable.
For the WorkBench part, I linked with Astart.obj, which I got from the kind
folks at Commodore. But the documentation says that, if the program was
started from CLI, the number of command line arguments will be in argc and the
arguments themselves in argv, passed as "main (argc,argv)," with "int argc;"
and "char **argv;." But no mention of what all that means, such as where does
one find this "argc" and "argv."
Wherever they may be, I found an address register that points approximately
to copies of the second (and later) CLI arguments, so I can run from the CLI.
As for running the program from the WorkBench, I can do that too, in a way.
Except that when I do an extended select I need a file name so I can Open() and
Read() the file clicked on, but all the address in the sm_ArgList offset in the
WBStartup structure points to is a bunch of strange numbers, e.g. (after
shift-double-clicking on the tool icon and one project icon so there were two
arguments) 000A17D5 002023FC 000A1803 … (middle one must be an address).
So Major Question number 1 is, How do I convert sm_ArgList into something
useful?
I didn't have to read the fine manual much to get a general idea of how to put
in an ASL file requester. But instead of popping up on my custom screen, it
came out on the WorkBench screen. Read the fine manual some more. OK, I need
a TagList to tell it what window. Spend a couple more hours to come up with a
fairly clear–but apparently wrong–idea of how to manage a TagList, and the
requester still comes out behind my screen on the WorkBench.
Major Question number 2: What am I doing wrong? Here are the snippets of my
code that seem relevant (most BEQs, BNEs, and LEAs deleted for brevity):
[ MORE ]
Major Question 1:
AS you have found out, the docs are biased towards C programmers. I don't
use Astart.obj myself, but being a C programmer, can explain what the main()
stuff means.
AStart.obj fronts your program, and does some magic that sets up the overall
environment (stack, etc), then formats the command line arguments, and calls
your code. I think it does a jsr _main?
When your code runs, it will have access to 2 variables, which will both be
on the stack (although you may coincidentally find them in registers also). On
the top of the stack will be a long integer, containing the number of CLI
parameters typed. Typing the name of the program counts as 1, additional params
are 2,3,.. etc. A program launched from WB will have 0 in here. The next long
is an address. It is the addr of the following structure:
addr –> Address1
Address2
Address3
0
The number of addresses pointed to varies, and there can be none (so addr
would point to the 0) So dereferencing the Addr will give another address
(Address1). Adding 4 and dereferencing will give Address2, etc until you find
all zeroes. Each of these addresses points to a string of characters
(terminated with a 0 byte). Each string is a single command line parameter, and
the number of them is the same as the first param on the stack.
Steve the G. [BEDFORDSHIRE, UK]
Thanks for the quick response. You UK people are really coming through for me
on this one.
With your information, I should have my CLI-started runs working the way they
were intended; I was uneasy about using the copies of argument strings I found
at A3 + 4, even though the results happened to be right.
I spent a couple of hours reading RKM Libraries today, and noticed the (argc,
**argv) rubric cropping up repeatedly. Do they always refer to the top
longwords on the stack, I wonder. (And what do all those *'s mean, and what is
the difference between UBYTE and (UBYTE) and BYTE, etc., etc. I guess I'm
going to have to pick up a rudimentary knowledge of C somewhere.)
Toiling over the RKM will probably pay off for my sm_ArgList problem too. I
finally found out where it is discussed before I pooped out. Tomorrow…
Thanks again.
– via Whap!
Bart,
I don't know enough to help you with the stack question, but I think I
can help you with the rest. The (argc, *argv[]) construct is quite common
in C. When a program is executed on most systems, the whole argument line
from the CLI is passed to the executing program. Using a fairly simple
routine, it is possible to parse the argument string and figure out what
the user typed so that arguments are accepted. K&R (Kernighan & Ritchie)
"The C Language," Second Edition, provides an adequate example of how to
parse CLI arguments in pages 114-118. As you stated, a knowledge of the C
language helps immensely with programming this beast.
I did wonder as to your use of (argc, **argv). The first value is the
number of arguments, including the first argument, i.e. the program name,
that is passed to the called program. The second argument, *argv[], is an
array of strings (characters) that can be indexed into to parse the
strings. While **argv is "technically" correct, the more familiar usage
is *argv[] (which is exactly the same thing as **argv). What does this
mean to you?
A bit of discussion is called for. In C, pointers are one of the more
powerful tools available to the programmer. I'm sure, at this point, that
you understand "what" a pointer is (i.e. an address of a data _value_),
but I don't think you understand the syntax involved. In C, when you
declare a pointer, you declare it in the following syntax:
long *x;
where x is the address of some long integer (i.e. 32 bit integer) you have
somewhere, and *x is the value stored there. (Confused? I was! * in the
declaration of a variable tells the compiler that you want to store an
address, * in the usage of a variable means "I want the contents referred
to by this address"). It is a given that you may already have the data
_value_ stored somewhere, all you are doing here is telling the compiler
to provide space to store the _pointer_ (i.e. address of) that _value_.
Okay, now that you have the pointer, what do you do with it? Well, you
can have it point to successive values stored in the system. If you have
20 long integers stored _succesively_, you can simply bump (increment) the
pointer by one to point to the next value in memory. If, for instance,
you had 3 long integers stored in memory, in successive memory locations,
you could do an assignment picking off each of the successive values.
Look at the following code fragment:
long a[]={32, 64, 128}; /* Array of three elements 32, 64, 128 */
long *x=a; /* Assign pointer x to a[0]'s location */
printf("%l",*x); /* print first element */
printf("%l, *x+1); /* print second element */
printf("%l, *x+2); /* print third element */
As you will notice, bumping the pointer automagically skips, no matter
what the element size, be it short, long, or even a complex record, to the
next element, as it should, since we _are_ incrementing the "address of"
some structural element's pointer.
One of the curious, but nice, features of C is that array names behave
like pointers, i.e., the first array element (element 0) is exactly the
same address as a pointer of the same name. They are one and the same.
So, when you see **argv, this is the same thing as *argv[0], which means,
I've passed you a pointer to the first element of an array. You can bump
this variable to point to the next element in the array. **argv+1 should
give you the next string in the array of strings. So, if you, the user,
had typed in a command line as follows:
Whatzit abc def
The programmer would see, passed to him, is an array of three strings with
the following values:
**argv = Whatzit
**argv+1 = abc
**argv+2 = def
What is passed to you, the programmer, is a sequence of strings in an
array. What you need to do is create a loop that, using argc (the
ARGument Count) as the loop counter, grabs each of the strings and parses
the meaning, or non-meaning, of each of the arguments. Each element will
end in a C standard null byte, i.e. \0 = 0x00.
The first argument *argv[0], or **argv, is, supposedly, the program
name of your program.
I hope this helps.
Brian
Brian,
I took a couple of days off to work on my bicycle, and came back to find a
terrific C lesson from you.
I'm hoping that by the time I digest what you explain about the use of "*"
that I might get a handle on my remaining problem, which is having an ASL file
requester open on the Workbench screen instead of my custom one. I've been
wondering if there is something about
request = AllocAslRequest( type, ptags )
struct TagItem *ptags (RKM: Includes & Autodocs, p16)
that I don't understand that is causing the problem. Now I'll reconsider that
in light of your explanation. What I've done so far is replace "ptags" with
the address of my TagItem (namely ASL_Window,myWindow), but that doesn't work,
so I'm obviously misunderstanding something.
Maybe I'll buy K&R. I started looking at C textbooks at Walden Books
yesterday.
My use of **argv, by the way, was a direct quote from "Using Amiga
Startups" in 2.0 Native Developer Update.
Thanks again. Bart
– via Whap!
Bart,
I think this code fragment will help you with getting the ASL requster
to open on your custom screen.
struct TagItem loadtags[] =
{
ASLFR_TitleText, (ULONG)"Select Script File to Load",
ASLFR_InitialHeight, fr_HEIGHT,
ASLFR_InitialWidth, fr_WIDTH,
ASLFR_InitialLeftEdge, fr_LEFTEDGE,
ASLFR_InitialTopEdge, fr_TOPEDGE,
ASLFR_InitialDrawer, (ULONG)"rexx:",
ASLFR_InitialPattern, (ULONG)"#?.adpro",
ASLFR_DoSaveMode, FALSE,
ASLFR_Window, NULL, /* NOTE THIS LINE */
TAG_DONE,
};
Then add this line in your program when calling the fr….
loadtags[8].ti_Data = (ULONG)MainWnd; /* Assign MainWnd to ASLFR_Window*/
Hope this helps….
MikeM:) "Amiga means never having to say you're sorry!"
Michael,
Thanks, that may help, but I'll either have to figure out how to translate
it to assembly language, or get a C compiler. Where you have /* NOTE THIS LINE
*/ is quite different from what I have done (though I use ALF_Window instead of
ASLFR_Window, which I haven't been able to find in the RKM yet). That
certainly suggests at least one more thing to try.
Bart
– via Whap!
Bart,
I forgot to mention that is for 2.1 release of the OS.
MikeM:) "Amiga means never having to say you're sorry!"
Bart,
I'm happy to see that others can help you on the rest of this. You
just went over my head <grin>. You really should check out the C Class
files in the libs here, since they can probably give you enough to
translate the C stuff to assembly.
Good luck on your projects!
Brian
Brian,
Yes, I got the help I needed to eventually force me down the right track.
But I very much appreciate your efforts to help, and I will take your advice
to check out the C Class files.
Bart
– via Whap!
In standard C, ALL arguments are always passed on the stack. Every C compiler
I have seen though, allows you to choose to pass params in registers, and some
allow both stack AND registers to be used. Since the startup would be expected
to be "standard" however, the stack is the place to look. Coincidentally, there
*may* happen to be some leftovers in registers from when the startup fromend
did its magic processing.
The * is the neophyte C programmers nightmare. Its use is *very*
context-dependent. When declaring a variable, it means address (such as would
be stored in A1). In an expression where it does not mean multiplication, it
dereferences an address (such as move.b (a3),d0. Double asterisks mean the
address of an address. This only makes sense in assembly terms if you realise
that an address is stored in memory like any other variable.
UBYTE and BYTE are non-standard definitions that refer to 8bit quantities.
The first is unsigned and the second signed. The only difference is subtleties
such as sign extending when you convert them into 16 bit quantities, ASR vs LSR
when shifting them right, etc. Brackets around a type (UBYTE) means that you
are converting a variable to another type. So if you have a 16bit quantity
(move.w d0,d1) and then use (UBYTE), the compiler will start generating move.b
instructions.
Steve the G. [BEDFORDSHIRE, UK]
Thanks for your note on C's *. I'm sure it will help. (But I'm also pretty
sure I'm going to have to buy a C manual, even if just for the index.)
With your help, and Shradhan's, I'm able to get my CLI and Workbench
arguments in the approved manner. Now if I can just get my ASL file requester
to open on the right screen…
– via Whap!
I can't actually remember what the problem with ASL was. Was it something to
do with putting reqs on the WB screen?
From another message you mention AllocAslRequest. As you have mentioned, the
taglist (2nd) parameter to this is a pointer to a list of tagitems. Remember
that the list must end with TAG_DONE, 0. The ASLFR_Window tag allows you to
specify which window the requester is attached to (and therefore which screen).
There is also a tag called ASLFR_Screen, which specifies which screen to use.
This overrides the window tag. The intuition function LockPubScreen will get
you a screen pointer to the WB screen.
Steve the G. [BEDFORDSHIRE, UK]
My other problem is getting a file requester to open on (the window in) my
CUSTOMSCREEN. It opens behind my screen, on WorkBench, instead. I will try
your suggestions, but I find no ASLFR_Window tag mentioned on a quick look in
RKM. Only the ASL_Window that I have been using. And so far I haven't been
able to find either an ASLFR_Screen or an ASL_Screen, but there is a
WA_CustomScreen that is certainly worth a try.
Meanwhile, just in case my problem is more fundamental, let me rehash what I
have as of now. I open a custom screen 640X400 with NS_EXTENDED ored with
CUSTOMSCREEN, and dc.l SA_Width,#-1 dc.l SA_Height,#-1 dc.l TAG_DONE
tagged on at the end. I use OpenScreen() and OpenWindow() for 1.3
compatibility, and add tags to fix certain things that don't come out right
otherwise in 2.04-2.1. I save the screen structure pointer in a memory
location my assembler lets me call NSptr, which coincides with Screen in my
NewWindow structure. Shortly thereafter I open the window, with WA_Borderless
tagged TRUE (and followed by a TAG_DONE at the end). (This is where I'm
thinking of trying to use WA_CustomScreen, though I'm not sure how.) I save
the pointer at a random location named NWptr.
In any event, my screen and window open just the way I want them to.
Meanwhile (providing I got ver 36 or better of dos.library) I have opened the
asl.library and save ASLbase.
Then
move.l ASL_FileRequest
movea.l aslreqtag,a1
SYS AllocAslRequest([ASLbase]) [= jsr LVO_…]
move.l d0,ASLfr
and far below in my data storage:
aslreqtag: dc.l ASL_Hail,frtit
dc.l ASL_Window,NWptr
dc.l TAG_DONE frtit: dc.b "JIStoJi File Requester",0
cnop 0,4
Apparently the "movea.l aslreqtag,a1" is not being understood, because when
the user calls up a requester it comes out with the standard title on the
WorkBench screen.
– via Whap!
Bart,
Looking at your codefragment I believe I know the solution to your
problem. First, the AllocAslRequest() function needs the address of
the taglist so you should use 'lea aslreqtag,a1' instead of
'movea.l aslreqtag,a1'. The move instruction will put the contents at
address 'aslreqtag' into a1 and you really need the address in a1 :-).
The second problem is in the taglist itself.
aslreqtag: dc.l ASL_Hail,frtit
dc.l ASL_Window,NWptr <—–
dc.l TAG_DONE
frtit: dc.b "…title…",0
The problem is in the marked line, NWptr is some storage where the
address of the OpenWindow() call is stored as I understand from your
explanation. The above code will store the address of 'NWptr' into the
taglist *not* the address of the windowstructure which should be put
there.
There are two solution: copy the contents of 'NWptr' to the correct
position in the taglist or more neatly, place the NWptr storage directly
into the taglist:
aslreqtag: dc.l ASL_Hail,frtit
dc.l ASL_Window
NWptr: dc.l 0
dc.l TAG_DONE
If you store the address of the windowstructure returned by OpenWindow()
in NWptr it will be in the correct place. I tried both solutions and
they work fine.
Hope this solves your problem,
Freddy. [On manual from Bilzen, Belgium]
Freddy,
It was immediately obvious that changing move.l to lea as you suggested would
help to solve my problem. I made that change and followed up with your
suggestion to save my window structure pointer directly in the taglist. Then I
compiled and ran, and the requester STILL came up on the WorkBench screen.
So instead of working inside the big program I wrote a little one that does
nothing but open a screen, and window, and an ASL requester, and started trying
all kinds of things. They mostly didn't work until a few minutes ago.
I don't think anyone would believe the number of errors of the kind you
spotted I found in addition–I really wouldn't want anyone to. Suffice it to
say that I found out I can read the manual over and over and over, and still
not see what it says!
I got it fixed. Thanks for speaking my language.
Bart
– via Whap!
Bart,
I've never used sm_ArgList. When your program gets started from the CLI,
the operating system passes the number of arguments (argv ?) in D0 and a
pointer to the CLI text in A0.
I then just parse this text string. I didn't know there might be an
easier way. (Is there, anybody?)
As for using the ASL requester, I've always avoided it. I much prefer
Khalid Aldoseri's requester; otherwise I'm using a WB1.3 machine and the
old but simple ARP requester.
Regards,
Shraddhan (via AP at last from Hertfordshire, England)