loadbsd.c revision 1.9 1 1.7 chopps /*
2 1.9 chopps * $Id: loadbsd.c,v 1.9 1994/03/28 06:16:54 chopps Exp $
3 1.7 chopps */
4 1.7 chopps
5 1.1 mw #include <sys/types.h>
6 1.1 mw #include <a.out.h>
7 1.1 mw #include <stdio.h>
8 1.8 chopps #include <unistd.h>
9 1.1 mw
10 1.1 mw #include <exec/types.h>
11 1.1 mw #include <exec/execbase.h>
12 1.1 mw #include <exec/memory.h>
13 1.1 mw #include <libraries/configregs.h>
14 1.1 mw #include <libraries/expansionbase.h>
15 1.5 mw #include <graphics/gfxbase.h>
16 1.1 mw
17 1.1 mw #include <inline/exec.h>
18 1.1 mw #include <inline/expansion.h>
19 1.5 mw #include <inline/graphics.h>
20 1.1 mw
21 1.3 mw /* Get definitions for boothowto */
22 1.3 mw #include "reboot.h"
23 1.3 mw
24 1.6 chopps static char usage[] =
25 1.6 chopps "
26 1.6 chopps NAME
27 1.6 chopps \t%s - loads NetBSD from amiga dos.
28 1.6 chopps SYNOPSIS
29 1.6 chopps \t%s some-vmunix [-a] [-b] [-k] [-m memory] [-p] [-t] [-V]
30 1.6 chopps OPTIONS
31 1.6 chopps \t-a Boot up to multiuser mode.
32 1.6 chopps \t-b Ask for which root device.
33 1.6 chopps \t Its possible to have multiple roots and choose between them.
34 1.6 chopps \t-k Reserve the first 4M of fast mem [Some one else
35 1.6 chopps \t is going to have to answer what that it is used for].
36 1.6 chopps \t-m Tweak amount of available memory, for finding minimum amount
37 1.6 chopps \t of memory required to run. Sets fastmem size to specified
38 1.6 chopps \t size in Kbytes.
39 1.6 chopps \t-p Use highest priority fastmem segement instead of the largest
40 1.6 chopps \t segment. The higher priority segment is usually faster
41 1.6 chopps \t (i.e. 32 bit memory), but some people have smaller amounts
42 1.6 chopps \t of 32 bit memory.
43 1.6 chopps \t-t This is a *test* option. It prints out the memory
44 1.6 chopps \t list information being passed to the kernel and also
45 1.6 chopps \t exits without actually starting NetBSD.
46 1.8 chopps \t-S Include kernel symbol table.
47 1.6 chopps \t-V Version of loadbsd program.
48 1.6 chopps HISTORY
49 1.6 chopps This version supports Kernel version 720 +
50 1.6 chopps ";
51 1.6 chopps
52 1.1 mw struct ExpansionBase *ExpansionBase;
53 1.5 mw struct GfxBase *GfxBase;
54 1.1 mw
55 1.1 mw #undef __LDPGSZ
56 1.1 mw #define __LDPGSZ 8192
57 1.1 mw
58 1.5 mw #define MAX_MEM_SEG 16
59 1.5 mw
60 1.6 chopps /*
61 1.6 chopps * Kernel parameter passing version
62 1.8 chopps * 1: first version of loadbsd
63 1.8 chopps * 2: needs esym location passed in a4
64 1.6 chopps */
65 1.8 chopps #define KERNEL_PARAMETER_VERSION 2
66 1.6 chopps
67 1.9 chopps /*
68 1.9 chopps * Version history:
69 1.9 chopps * 1.x Kernel parameter passing version check
70 1.9 chopps * 2.0 Added symbol table end address and symbol table support
71 1.9 chopps * 2.1 03/23/94 - round up end of fastram segment
72 1.9 chopps * check fastram segment size for minimum of 2M
73 1.9 chopps * use largest segment of highest priority if -p option
74 1.9 chopps * print out fastram size in KB if not a multiple of MB
75 1.9 chopps * 2.2 03/24/94 - zero out all unused registers
76 1.9 chopps * started version history comment
77 1.9 chopps */
78 1.9 chopps
79 1.5 mw struct MEM_LIST {
80 1.5 mw u_long num_mem;
81 1.5 mw struct MEM_SEG {
82 1.5 mw u_long mem_start;
83 1.5 mw u_long mem_size;
84 1.5 mw u_short mem_attrib;
85 1.5 mw short mem_prio;
86 1.5 mw } mem_seg[MAX_MEM_SEG];
87 1.5 mw } mem_list, *kmem_list;
88 1.5 mw
89 1.5 mw int k_opt;
90 1.5 mw int a_opt;
91 1.5 mw int b_opt;
92 1.5 mw int p_opt;
93 1.5 mw int t_opt;
94 1.6 chopps int m_opt;
95 1.8 chopps int S_opt;
96 1.5 mw
97 1.5 mw extern char *optarg;
98 1.5 mw extern int optind;
99 1.5 mw
100 1.1 mw void get_mem_config (void **fastmem_start, u_long *fastmem_size, u_long *chipmem_size);
101 1.6 chopps void Usage (char *program_name);
102 1.6 chopps void Version (void);
103 1.6 chopps
104 1.9 chopps static const char _version[] = "$VER: LoadBSD 2.2 (24.3.94)";
105 1.1 mw
106 1.1 mw int
107 1.1 mw main (int argc, char *argv[])
108 1.1 mw {
109 1.1 mw struct exec e;
110 1.1 mw int fd;
111 1.3 mw int boothowto = RB_SINGLE;
112 1.1 mw
113 1.1 mw if (argc >= 2)
114 1.1 mw {
115 1.1 mw if ((fd = open (argv[1], 0)) >= 0)
116 1.1 mw {
117 1.1 mw if (read (fd, &e, sizeof (e)) == sizeof (e))
118 1.1 mw {
119 1.1 mw if (e.a_magic == NMAGIC)
120 1.1 mw {
121 1.1 mw u_char *kernel;
122 1.8 chopps int kernel_size;
123 1.1 mw int text_size;
124 1.1 mw struct ConfigDev *cd;
125 1.1 mw int num_cd;
126 1.5 mw void *fastmem_start;
127 1.5 mw u_long fastmem_size, chipmem_size;
128 1.5 mw int i;
129 1.6 chopps u_short *kern_vers;
130 1.8 chopps char *esym;
131 1.8 chopps int string_size;
132 1.1 mw
133 1.5 mw GfxBase = (struct GfxBase *) OpenLibrary ("graphics.library", 0);
134 1.5 mw if (! GfxBase) /* not supposed to fail... */
135 1.5 mw abort();
136 1.1 mw ExpansionBase= (struct ExpansionBase *) OpenLibrary ("expansion.library", 0);
137 1.1 mw if (! ExpansionBase) /* not supposed to fail... */
138 1.1 mw abort();
139 1.5 mw optind = 2;
140 1.8 chopps while ((i = getopt (argc, argv, "kabptVm:S")) != EOF)
141 1.5 mw switch (i) {
142 1.5 mw case 'k':
143 1.5 mw k_opt = 1;
144 1.5 mw break;
145 1.5 mw case 'a':
146 1.5 mw a_opt = 1;
147 1.5 mw break;
148 1.5 mw case 'b':
149 1.5 mw b_opt = 1;
150 1.5 mw break;
151 1.5 mw case 'p':
152 1.5 mw p_opt = 1;
153 1.5 mw break;
154 1.5 mw case 't':
155 1.5 mw t_opt = 1;
156 1.5 mw break;
157 1.6 chopps case 'm':
158 1.6 chopps m_opt = atoi (optarg) * 1024;
159 1.6 chopps break;
160 1.6 chopps case 'V':
161 1.6 chopps Version();
162 1.6 chopps break;
163 1.8 chopps case 'S':
164 1.8 chopps S_opt = 1;
165 1.8 chopps break;
166 1.6 chopps default:
167 1.6 chopps Usage(argv[0]);
168 1.6 chopps fprintf(stderr,"Unrecognized option \n");
169 1.6 chopps exit(-1);
170 1.5 mw }
171 1.5 mw
172 1.1 mw for (cd = 0, num_cd = 0; cd = FindConfigDev (cd, -1, -1); num_cd++) ;
173 1.5 mw get_mem_config (&fastmem_start, &fastmem_size, &chipmem_size);
174 1.1 mw
175 1.1 mw text_size = (e.a_text + __LDPGSZ - 1) & (-__LDPGSZ);
176 1.8 chopps esym = NULL;
177 1.8 chopps kernel_size = text_size + e.a_data + e.a_bss
178 1.8 chopps + num_cd*sizeof(*cd) + 4
179 1.8 chopps + mem_list.num_mem*sizeof(struct MEM_SEG) + 4;
180 1.8 chopps /*
181 1.8 chopps * get symbol table size & string size
182 1.8 chopps * (should check kernel version to see if it will handle it)
183 1.8 chopps */
184 1.8 chopps if (S_opt && e.a_syms) {
185 1.8 chopps S_opt = 0; /* prepare for failure */
186 1.8 chopps if (lseek(fd, e.a_text + e.a_data + e.a_syms, SEEK_CUR) > 0) {
187 1.8 chopps if (read (fd, &string_size, 4) == 4) {
188 1.8 chopps if (lseek(fd, sizeof(e), SEEK_SET) < 0) {
189 1.8 chopps printf ("Error repositioning to text\n");
190 1.8 chopps exit(0); /* Give up! */
191 1.8 chopps }
192 1.8 chopps kernel_size += e.a_syms + 4 + string_size;
193 1.8 chopps S_opt = 1; /* sucess! Keep -S option */
194 1.8 chopps }
195 1.8 chopps }
196 1.8 chopps }
197 1.8 chopps
198 1.8 chopps kernel = (u_char *) malloc (kernel_size);
199 1.5 mw
200 1.5 mw if (t_opt)
201 1.5 mw for (i = 0; i < mem_list.num_mem; ++i) {
202 1.5 mw printf ("mem segment %d: start=%08lx size=%08lx attribute=%04lx pri=%d\n",
203 1.5 mw i + 1, mem_list.mem_seg[i].mem_start,
204 1.5 mw mem_list.mem_seg[i].mem_size,
205 1.5 mw mem_list.mem_seg[i].mem_attrib,
206 1.5 mw mem_list.mem_seg[i].mem_prio);
207 1.5 mw }
208 1.1 mw
209 1.1 mw if (kernel)
210 1.1 mw {
211 1.1 mw if (read (fd, kernel, e.a_text) == e.a_text
212 1.1 mw && read (fd, kernel + text_size, e.a_data) == e.a_data)
213 1.1 mw {
214 1.1 mw int *knum_cd;
215 1.1 mw struct ConfigDev *kcd;
216 1.5 mw int mem_ix;
217 1.8 chopps
218 1.5 mw if (k_opt)
219 1.1 mw {
220 1.1 mw fastmem_start += 4*1024*1024;
221 1.1 mw fastmem_size -= 4*1024*1024;
222 1.1 mw }
223 1.6 chopps
224 1.6 chopps if (m_opt && m_opt <= fastmem_size)
225 1.6 chopps {
226 1.6 chopps fastmem_size = m_opt;
227 1.6 chopps }
228 1.1 mw
229 1.5 mw if (a_opt)
230 1.3 mw {
231 1.3 mw printf("Autobooting...");
232 1.3 mw boothowto = RB_AUTOBOOT;
233 1.3 mw }
234 1.5 mw
235 1.5 mw if (b_opt)
236 1.5 mw {
237 1.5 mw printf("Askboot...");
238 1.5 mw boothowto |= RB_ASKNAME;
239 1.5 mw }
240 1.3 mw
241 1.9 chopps printf ("Using %d%c FASTMEM at 0x%x, %dM CHIPMEM\n",
242 1.9 chopps (fastmem_size & 0xfffff) ? fastmem_size>>10 :
243 1.9 chopps fastmem_size>>20,
244 1.9 chopps (fastmem_size & 0xfffff) ? 'K' : 'M',
245 1.9 chopps fastmem_start, chipmem_size>>20);
246 1.6 chopps kern_vers = (u_short *) (kernel + e.a_entry - 2);
247 1.6 chopps if (*kern_vers > KERNEL_PARAMETER_VERSION &&
248 1.6 chopps *kern_vers != 0x4e73)
249 1.6 chopps {
250 1.6 chopps printf ("This kernel requires a newer version of loadbsd: %d\n", *kern_vers);
251 1.6 chopps exit (0);
252 1.6 chopps }
253 1.1 mw /* give them a chance to read the information... */
254 1.1 mw sleep(2);
255 1.1 mw
256 1.1 mw bzero (kernel + text_size + e.a_data, e.a_bss);
257 1.8 chopps /*
258 1.8 chopps * If symbols wanted (and kernel can handle them),
259 1.8 chopps * load symbol table & strings and set esym to end.
260 1.8 chopps */
261 1.1 mw knum_cd = (int *) (kernel + text_size + e.a_data + e.a_bss);
262 1.8 chopps if (*kern_vers != 0x4e73 && *kern_vers > 1 && S_opt && e.a_syms) {
263 1.8 chopps *knum_cd++ = e.a_syms;
264 1.8 chopps read(fd, (char *)knum_cd, e.a_syms);
265 1.8 chopps knum_cd = (int *)((char *)knum_cd + e.a_syms);
266 1.8 chopps read(fd, (char *)knum_cd, string_size);
267 1.8 chopps knum_cd = (int*)((char *)knum_cd + string_size);
268 1.8 chopps esym = (char *) (text_size + e.a_data + e.a_bss
269 1.8 chopps + e.a_syms + 4 + string_size);
270 1.8 chopps }
271 1.1 mw *knum_cd = num_cd;
272 1.5 mw for (kcd = (struct ConfigDev *) (knum_cd+1);
273 1.5 mw cd = FindConfigDev (cd, -1, -1);
274 1.6 chopps *kcd++ = *cd)
275 1.6 chopps ;
276 1.5 mw kmem_list = (struct MEM_LIST *)kcd;
277 1.5 mw kmem_list->num_mem = mem_list.num_mem;
278 1.5 mw for (mem_ix = 0; mem_ix < mem_list.num_mem; mem_ix++)
279 1.5 mw kmem_list->mem_seg[mem_ix] = mem_list.mem_seg[mem_ix];
280 1.8 chopps if (t_opt) /* if test option */
281 1.8 chopps exit (0); /* don't start kernel */
282 1.8 chopps /* AGA startup - may need more */
283 1.5 mw LoadView (NULL);
284 1.8 chopps startit (kernel, kernel_size,
285 1.1 mw e.a_entry, fastmem_start,
286 1.3 mw fastmem_size, chipmem_size,
287 1.8 chopps boothowto, esym );
288 1.1 mw }
289 1.1 mw else
290 1.1 mw fprintf (stderr, "Executable corrupt!\n");
291 1.1 mw }
292 1.1 mw else
293 1.1 mw fprintf (stderr, "Out of memory! (%d)\n", text_size + e.a_data + e.a_bss
294 1.5 mw + num_cd*sizeof(*cd) + 4
295 1.5 mw + mem_list.num_mem*sizeof(struct MEM_SEG) + 4);
296 1.1 mw }
297 1.1 mw else
298 1.1 mw fprintf (stderr, "Unsupported executable: %o\n", e.a_magic);
299 1.1 mw }
300 1.1 mw else
301 1.1 mw fprintf (stderr, "Can't read header of %s\n", argv[1]);
302 1.1 mw
303 1.1 mw close (fd);
304 1.1 mw }
305 1.1 mw else
306 1.1 mw perror ("open");
307 1.1 mw }
308 1.1 mw else
309 1.6 chopps Usage(argv[0]);
310 1.6 chopps Version();
311 1.6 chopps }/* main() */
312 1.1 mw
313 1.1 mw void
314 1.1 mw get_mem_config (void **fastmem_start, u_long *fastmem_size, u_long *chipmem_size)
315 1.1 mw {
316 1.1 mw extern struct ExecBase *SysBase;
317 1.1 mw struct MemHeader *mh, *nmh;
318 1.5 mw int num_mem = 0;
319 1.5 mw u_int seg_size;
320 1.5 mw u_int seg_start;
321 1.5 mw u_int seg_end;
322 1.9 chopps char mem_pri = -128;
323 1.1 mw
324 1.1 mw *fastmem_size = 0;
325 1.1 mw *chipmem_size = 0;
326 1.1 mw
327 1.1 mw /* walk thru the exec memory list */
328 1.1 mw Forbid ();
329 1.1 mw for (mh = (struct MemHeader *) SysBase->MemList.lh_Head;
330 1.1 mw nmh = (struct MemHeader *) mh->mh_Node.ln_Succ;
331 1.5 mw mh = nmh, num_mem++)
332 1.1 mw {
333 1.5 mw mem_list.mem_seg[num_mem].mem_attrib = mh->mh_Attributes;
334 1.5 mw mem_list.mem_seg[num_mem].mem_prio = mh->mh_Node.ln_Pri;
335 1.5 mw seg_start = (u_int)mh->mh_Lower;
336 1.5 mw seg_end = (u_int)mh->mh_Upper;
337 1.5 mw seg_size = seg_end - seg_start;
338 1.5 mw mem_list.mem_seg[num_mem].mem_size = seg_size;
339 1.5 mw mem_list.mem_seg[num_mem].mem_start = seg_start;
340 1.5 mw
341 1.1 mw if (mh->mh_Attributes & MEMF_CHIP)
342 1.1 mw {
343 1.1 mw /* there should hardly be more than one entry for chip mem, but
344 1.1 mw handle it the same nevertheless */
345 1.5 mw /* chipmem always starts at 0, so include vector area */
346 1.6 chopps mem_list.mem_seg[num_mem].mem_start = seg_start = 0;
347 1.5 mw /* round to multiple of 512K */
348 1.5 mw seg_size = (seg_size + 512*1024 - 1) & -(512*1024);
349 1.5 mw mem_list.mem_seg[num_mem].mem_size = seg_size;
350 1.5 mw if (seg_size > *chipmem_size)
351 1.1 mw {
352 1.5 mw *chipmem_size = seg_size;
353 1.1 mw }
354 1.1 mw }
355 1.1 mw else
356 1.1 mw {
357 1.5 mw /* some heuristics.. */
358 1.5 mw seg_start &= -__LDPGSZ;
359 1.9 chopps seg_end = (seg_end + __LDPGSZ - 1) & -__LDPGSZ;
360 1.5 mw /* get the mem back stolen by incore kickstart on A3000 with
361 1.5 mw V36 bootrom. */
362 1.5 mw if (seg_end == 0x07f80000)
363 1.5 mw seg_end = 0x08000000;
364 1.5 mw
365 1.5 mw /* or by zkick on a A2000. */
366 1.5 mw if (seg_start == 0x280000
367 1.5 mw && strcmp (mh->mh_Node.ln_Name, "zkick memory") == 0)
368 1.5 mw seg_start = 0x200000;
369 1.5 mw
370 1.5 mw seg_size = seg_end - seg_start;
371 1.5 mw mem_list.mem_seg[num_mem].mem_start = seg_start;
372 1.5 mw mem_list.mem_seg[num_mem].mem_size = seg_size;
373 1.9 chopps /*
374 1.9 chopps * If this segment is smaller than 2M,
375 1.9 chopps * don't use it to load the kernel
376 1.9 chopps */
377 1.9 chopps if (seg_size < 2 * 1024 * 1024)
378 1.9 chopps continue;
379 1.5 mw /* if p_opt is set, select memory by priority instead of size */
380 1.6 chopps if ((!p_opt && seg_size > *fastmem_size) ||
381 1.9 chopps (p_opt && mem_pri <= mh->mh_Node.ln_Pri && seg_size > *fastmem_size))
382 1.1 mw {
383 1.5 mw *fastmem_size = seg_size;
384 1.5 mw *fastmem_start = (void *)seg_start;
385 1.9 chopps mem_pri = mh->mh_Node.ln_Pri;
386 1.1 mw }
387 1.1 mw }
388 1.1 mw }
389 1.5 mw mem_list.num_mem = num_mem;
390 1.1 mw Permit();
391 1.1 mw }
392 1.1 mw
393 1.1 mw
394 1.1 mw
395 1.1 mw
396 1.1 mw asm ("
397 1.1 mw .set ABSEXECBASE,4
398 1.1 mw
399 1.1 mw .text
400 1.1 mw .globl _startit
401 1.1 mw
402 1.1 mw _startit:
403 1.1 mw movel sp,a3
404 1.1 mw movel 4:w,a6
405 1.1 mw lea pc@(start_super-.+2),a5
406 1.1 mw jmp a6@(-0x1e) | supervisor-call
407 1.1 mw
408 1.1 mw start_super:
409 1.1 mw movew #0x2700,sr
410 1.1 mw
411 1.1 mw | the BSD kernel wants values into the following registers:
412 1.1 mw | a0: fastmem-start
413 1.1 mw | d0: fastmem-size
414 1.1 mw | d1: chipmem-size
415 1.5 mw | d5: AttnFlags (cpuid)
416 1.3 mw | d7: boothowto
417 1.8 chopps | a4: esym location
418 1.1 mw
419 1.1 mw movel a3@(4),a1 | loaded kernel
420 1.1 mw movel a3@(8),d2 | length of loaded kernel
421 1.9 chopps movel a3@(12),sp@- | entry point [save on stack for rts]
422 1.1 mw movel a3@(16),a0 | fastmem-start
423 1.1 mw movel a3@(20),d0 | fastmem-size
424 1.1 mw movel a3@(24),d1 | chipmem-size
425 1.5 mw movel #0,d5
426 1.5 mw movew (ABSEXECBASE)@(0x128),d5 | SysBase->AttnFlags
427 1.3 mw movel a3@(28),d7 | boothowto
428 1.8 chopps movel a3@(32),a4 | esym
429 1.8 chopps subl a5,a5 | target, load to 0
430 1.1 mw
431 1.5 mw btst #3,(ABSEXECBASE)@(0x129) | AFB_68040,SysBase->AttnFlags
432 1.5 mw beq not040
433 1.5 mw
434 1.5 mw | Turn off 68040 MMU
435 1.5 mw
436 1.8 chopps .word 0x4e7b,0xd003 | movec a5,tc
437 1.8 chopps .word 0x4e7b,0xd806 | movec a5,urp
438 1.8 chopps .word 0x4e7b,0xd807 | movec a5,srp
439 1.8 chopps .word 0x4e7b,0xd004 | movec a5,itt0
440 1.8 chopps .word 0x4e7b,0xd005 | movec a5,itt1
441 1.8 chopps .word 0x4e7b,0xd006 | movec a5,dtt0
442 1.8 chopps .word 0x4e7b,0xd007 | movec a5,dtt1
443 1.5 mw bra nott
444 1.5 mw
445 1.5 mw not040:
446 1.1 mw lea pc@(zero-.+2),a3
447 1.1 mw pmove a3@,tc | Turn off MMU
448 1.1 mw lea pc@(nullrp-.+2),a3
449 1.1 mw pmove a3@,crp | Turn off MMU some more
450 1.1 mw pmove a3@,srp | Really, really, turn off MMU
451 1.1 mw
452 1.1 mw | Turn off 68030 TT registers
453 1.1 mw
454 1.1 mw btst #2,(ABSEXECBASE)@(0x129) | AFB_68030,SysBase->AttnFlags
455 1.1 mw beq nott | Skip TT registers if not 68030
456 1.1 mw lea pc@(zero-.+2),a3
457 1.1 mw .word 0xf013,0x0800 | pmove a3@,tt0 (gas only knows about 68851 ops..)
458 1.1 mw .word 0xf013,0x0c00 | pmove a3@,tt1 (gas only knows about 68851 ops..)
459 1.1 mw
460 1.1 mw nott:
461 1.1 mw
462 1.1 mw movew #(1<<9),0xdff096 | disable DMA
463 1.1 mw
464 1.1 mw L0:
465 1.8 chopps moveb a1@+,a5@+
466 1.1 mw subl #1,d2
467 1.1 mw bcc L0
468 1.1 mw
469 1.1 mw
470 1.9 chopps moveq #0,d2 | zero out unused registers
471 1.9 chopps moveq #0,d3 | (might make future compatibility
472 1.9 chopps moveq #0,d4 | a little easier, since all registers
473 1.9 chopps moveq #0,d6 | would have known contents)
474 1.9 chopps movel d6,a1
475 1.9 chopps movel d6,a2
476 1.9 chopps movel d6,a3
477 1.9 chopps movel d6,a5
478 1.9 chopps movel d6,a6
479 1.9 chopps rts | return to kernel entry point
480 1.1 mw
481 1.1 mw
482 1.1 mw | A do-nothing MMU root pointer (includes the following long as well)
483 1.1 mw
484 1.1 mw nullrp: .long 0x7fff0001
485 1.1 mw zero: .long 0
486 1.1 mw
487 1.1 mw
488 1.1 mw ");
489 1.1 mw
490 1.6 chopps void Usage(char *program_name)
491 1.6 chopps {
492 1.6 chopps fprintf(stderr,usage,program_name,program_name);
493 1.6 chopps }
494 1.6 chopps
495 1.6 chopps void Version()
496 1.6 chopps {
497 1.6 chopps fprintf(stderr,"%s\n",_version + 6);
498 1.6 chopps }
499