echo x - Conv_script
sed '/^X/s///' > Conv_script << '/'
Xs/^\([^|]*\)!/\1~/g
X/^[ 	]*!/{
Xp
Xd
X}
X/^[_a-zA-Z][_a-zA-Z0-9]*:/!s/^/:/
Xh
Xs/^:[ 	]*//
Xs/^[_a-zA-Z][_a-zA-Z0-9]*:[ 	]*//
Xs/^\.byte\([ 	]\)/.data1\1/
Xs/^\.word\([ 	]\)/.data2\1/
Xs/^\.long\([ 	]\)/.data4\1/
Xs/;\([ 	]*\.\)byte\([ 	]\)/;\1data1\2/g
Xs/;\([ 	]*\.\)word\([ 	]\)/;\1data2\2/g
Xs/;\([ 	]*\.\)long\([ 	]\)/;\1data4\2/g
Xs/^\.globl\([ 	]\)/.extern\1/
Xs/(b\([xp]\)_\([sd]\)i)/(b\1),(\2i)/g
Xs/^seg[ 	][ 	]*\([ecsd]\)s$/\1seg/
Xs/^seg[ 	][ 	]*\([ecsd]\)s\([ 	]\)/\1seg\2/
Xs/^in\([ 	]\)/inb\1/
Xs/^in$/inb/
Xs/^out\([ 	]\)/outb\1/
Xs/^out$/outb/
Xs/^stob\([ 	]\)/stosb\1/
Xs/^stob$/stosb/
Xs/^stow\([ 	]\)/stos\1/
Xs/^stow$/stos/
Xs/^lodb\([ 	]\)/lodsb\1/
Xs/^lodb$/lodsb/
Xs/^lodw\([ 	]\)/lods\1/
Xs/^lodw$/lods/
Xs/^scab\([ 	]\)/scasb\1/
Xs/^scab$/scasb/
Xs/^scaw\([ 	]\)/scas\1/
Xs/^scaw$/scas/
Xs/^jc\([ 	]\)/jb\1/
Xs/^jnc\([ 	]\)/jnb\1/
Xs/^calli\([ 	]\)/callf\1/
Xs/^jmpi\([ 	]\)/jmpf\1/
Xs/^reti\([ 	]\)/retf\1/
Xs/^reti$/retf/
Xs/^j\([ 	]\)/jmp\1/
Xs/^br\([ 	]\)/jmp\1/
Xs/^beq\([ 	]\)/je\1/
Xs/^bge\([ 	]\)/jge\1/
Xs/^bgt\([ 	]\)/jg\1/
Xs/^bhi\([ 	]\)/jnbe\1/
Xs/^bhis\([ 	]\)/jnb\1/
Xs/^ble\([ 	]\)/jle\1/
Xs/^blo\([ 	]\)/jb\1/
Xs/^blos\([ 	]\)/jbe\1/
Xs/^blt\([ 	]\)/jnge\1/
Xs/^bne\([ 	]\)/jne\1/
Xs/|/!/
XH
Xg
X/^[_a-zA-Z][_a-zA-Z0-9]*:/{
Xs/:\([ 	]*\)[^ 	]*.*\n/:\1/
X}
X/^:/{
Xs/:\([ 	]*\)[^ 	]*.*\n/\1/
X}
/
echo x - Conv_to_asld
sed '/^X/s///' > Conv_to_asld << '/'
X# Conv_to_asld is a filter that converts xenix assembly files to asld format
X
Xsed -e '
X:start
X/^#/{
Xn
Xbstart
X}
Xs/^[ 	][ 	]*//
Xs/!/|/
Xs/[ 	]*$//
Xh
Xs/^[_a-zA-Z][_a-zA-Z0-9]*:[ 	]*//
Xs/\.data1/.byte /g
Xs/\.data2/.word /g
Xs/\.data4/.long /g
X#
X# Old .globl and old .extern are mapped to new .extern. Thus it is
X# not possible to simply map .extern to .globl.
X#
X#s/^\.extern[ 	][ 	]*/.globl /g
Xs/(b\([xp]\)),(\([sd]\)i)/(b\1_\2i)/g
Xs/^\([ecsd]\)seg$/seg \1s/g
Xs/^\([ecsd]\)seg	/seg \1s/g
Xs/^inb\([ 	]*\)/in\1/g
Xs/^outb\([ 	]*\)/out\1/g
Xs/^stosb$/stob/g
Xs/^stos$/stow/g
Xs/^lodsb$/lodb/g
Xs/^lods$/lodw/g
Xs/^scasb$/scab/g
Xs/^scas$/scaw/g
Xs/^stosb	/stob/g
Xs/^stos	/stow/g
Xs/^lodsb	/lodb/g
Xs/^lods	/lodw/g
Xs/^scasb	/scab/g
Xs/^scas	/scaw/g
Xs/^callf[ 	][ 	]*/calli /g
Xs/^jmpf[ 	][ 	]*/jmpi /g
Xs/^retf$/reti/
Xs/^retf	/reti/
X#
X# Old bcc uses j for short jumps and does not do jump sizing on jmp.
X#
Xs/^jmp[ 	][ 	]*\([_a-zA-Z]\)/j \1/g
Xs/^br[ 	][ 	]*/jmp /g
XH
Xg
X/^[_a-zA-Z][_a-zA-Z0-9]*:/{
Xs/:.*\n/: /
Xs/^/\
X/
X}
Xs/^.*\n//'  >tmp.2
X
X
Xsed -e 'y/$/#/' <tmp.2 | grep "."
Xrm -f $$.? tmp.?
Xexit 0
/
echo x - Conv_to_xenix
sed '/^X/s///' > Conv_to_xenix << '/'
X# Conv_to_xenix is a filter that converts asld assembly files to xenix format
X
Xsed -f Conv_script
/
echo x - Libc.a.order
sed '/^X/s///' > Libc.a.order << '/'
Xsyslib.s      
Xfsversion.s   
Xmtab.s        
Xfprintk.s     
Xprintk.s      
Xstrerror.s    
Xcurses.s      
Xgetpw.s       
Xnlist.s       
Xtermcap.s     
Xpopen.s       
Xttyname.s     
Xclosedir.s    
Xfclose.s      
Xopendir.s     
Xsetbuf.s      
Xfdopen.s      
Xfopen.s       
Xputenv.s      
Xregexp.s      
Xgetopt.s      
Xputs.s        
Xctime.s       
X_sigsetjmp.s  
Xassert.s      
Xlock.s        
Xsetjmp.s      
Xfprintf.s     
Xsprintf.s     
Xdoprintf.s    
Xfputs.s       
Xfwrite.s      
Xputw.s        
Xvsprintf.s    
Xfputc.s       
Xcleanup.s     
Xfreopen.s     
Xfseek.s       
Xfflush.s      
Xfputk.s       
Xmalloc.s      
Xpeekpoke.s    
Xperror.s      
Xportio.s      
Xprints.s      
Xputk.s        
Xstderr.s      
Xgetpass.s     
X_longjerr.s   
Xwrite.s       
Xwaitpid.s     
Xsystem.s      
Xwait.s        
Xunlink.s      
Xumount.s      
Xumask.s       
Xtimes.s       
Xuniqport.s    
Xutime.s       
X_utime.s      
Xtime.s        
Xsync.s        
Xstime.s       
Xgetcwd.s      
X_getcwd.s     
Xstat.s        
Xsleep.s       
X_sleep.s      
Xsigsuspend.s  
Xsigaction.s   
Xsignal.s      
X_sigaction.s  
X__sigreturn.s 
Xsigreturn.s   
X_sigreturn.s  
Xsigprocmask.s 
Xsigpending.s  
Xsetuid.s      
Xsetgid.s      
Xrmdir.s       
Xrename.s      
Xcuserid.s     
Xgetlogin.s    
Xseekdir.s     
Xreaddir.s     
Xfgets.s       
Xfread.s       
Xgets.s        
Xgetw.s        
Xscanf.s       
Xuname.s       
X_uname.s      
Xfgetc.s       
Xgetdents.s    
Xgetpwent.s    
Xgetgrent.s    
Xread.s        
Xptrace.s      
Xpipe.s        
Xpause.s       
Xpathconf.s    
X_pathconf.s   
Xopen.s        
Xmount.s       
Xmknod.s       
Xmkfifo.s      
X_mkfifo.s     
X_mknod.s      
Xmknod4.s      
Xmkdir.s       
Xftell.s       
Xrewinddir.s   
Xtelldir.s     
Xlseek.s       
Xlink.s        
Xabort.s       
Xkill.s        
Xisatty.s      
Xstty.s        
Xgtty.s        
X_gtty.s       
X_stty.s       
Xioctl.s       
Xgetuid.s      
Xgetppid.s     
Xtmpnam.s      
Xmktemp.s      
X_mktemp.s     
Xgetpid.s      
Xgetgid.s      
Xgeteuid.s     
Xgetegid.s     
Xfpathconf.s   
X_fpathconf.s  
Xfstat.s       
Xfork.s        
Xdup2.s        
Xdup.s         
X_dup.s        
X_dup2.s       
Xfcntl.s       
Xcreat.s       
Xclose.s       
Xchroot.s      
Xchown.s       
Xchmod.s       
Xchdir.s       
Xexeclp.s      
Xexecv.s       
Xexecl.s       
Xexecle.s      
Xexecve.s      
Xbrk.s         
X_exec.s       
Xsbrk.s        
Xalarm.s       
Xaccess.s      
X_access.s     
X_alarm.s      
X_brk.s        
X_chdir.s      
X_chmod.s      
X_chown.s      
X_chroot.s     
X_close.s      
X_creat.s      
X_execn.s      
X_execnl.s     
X_exit.s       
X_fcntl.s      
X_fork.s       
X_fstat.s      
X_getegid.s    
X_geteuid.s    
X_getgid.s     
X_getpid.s     
X_getppid.s    
X_getuid.s     
X_ioctl.s      
X_isatty.s     
X_kill.s       
X_link.s       
X_lseek.s      
X_mkdir.s      
X_mknod4.s     
X_mount.s      
X_open.s       
X_pause.s      
X_pipe.s       
X_ptrace.s     
X_read.s       
X_rename.s     
X_rmdir.s      
X_setgid.s     
X_setuid.s     
X_sigpending.s 
X_sigprocmask.s
X_sigsuspend.s 
X_stat.s       
X_stime.s      
X_sync.s       
X_time.s       
X_times.s      
X_umask.s      
X_umount.s     
X_unlink.s     
X_wait.s       
X_waitpid.s    
X_write.s      
Xiaar.s        
Xilar.s        
Xisar.s        
Xhypot.s       
Xsincos.s      
Xfslib.s       
Xsysconf.s     
Xcii.s         
Xcsa2.s        
Xcsb2.s        
Xcuu.s         
Xfakfp.s       
Xstrhp.s       
Xunknown.s     
Xadi.s         
Xsbi.s         
Xfat.s         
Xmon.s         
Xstop.s        
Xtrp.s         
Xexit.s        
Xamoeba.s      
Xcall.s        
Xreceive.s     
Xsend.s        
Xsendrec.s     
Xsyscall.s     
Xtaskcall.s    
Xstrtol.s      
Xsigfillset.s  
Xsigemptyset.s 
Xsigaddset.s   
Xsigdelset.s   
Xsigismember.s 
X_sigset.s     
Xstrtoul.s     
Xatoi.s        
Xatol.s        
Xlrand.s       
Xgetenv.s      
Xbcmp.s        
Xbzero.s       
Xcatchsig.s    
Xrand.s        
Xlfr6.s        
Xlfr8.s        
Xret6.s        
Xret8.s        
Xlsearch.s     
Xbcopy.s       
Xmemmove.s     
Xgetgroups.s   
X_getgroups.s  
Xindex.s       
Xrindex.s      
Xbsearch.s     
Xcrypt.s       
Xffs.s         
Xitoa.s        
Xloadname.s    
Xmemccpy.s     
Xmemcpy.s      
Xqsort.s       
Xregsub.s      
Xstb.s         
Xswab.s        
Xungetc.s      
Xabs.s         
Xctermid.s     
Xstrcoll.s     
Xstrxfrm.s     
Xxor.s         
Xvectab.s      
Xvars.s        
Xstrtok.s      
Xstrstr.s      
Xstrspn.s      
Xstrrchr.s     
Xstrpbrk.s     
Xstrncpy.s     
Xstrncmp.s     
Xstrncat.s     
Xstrlen.s      
Xstrcspn.s     
Xstrcpy.s      
Xstrcmp.s      
Xstrchr.s      
Xstrcat.s      
Xsti.s         
Xset.s         
Xsar2.s        
Xrmu4.s        
Xrmi4.s        
Xreturn.s      
Xretarea.s     
Xrck.s         
Xprintdat.s    
Xnop.s         
Xmli4.s        
Xmessage.s     
Xmemset.s      
Xmemcmp.s      
Xmemchr.s      
Xloi.s         
Xlar2.s        
Xior.s         
Xinn.s         
Xgto.s         
Xgetutil.s     
Xexg.s         
Xerrno.s       
Xenviron.s     
Xdvu4.s        
Xdvi4.s        
Xctype.s       
Xcom.s         
Xcmu4.s        
Xcms.s         
Xcmi4.s        
Xbrksize.s     
Xblm.s         
Xand.s         
X_sendrec.s    
Xcrtso.s       
Xend.s         
Xhead.s        
Xcsa4.s        
Xcsb4.s        
Xerror.s       
Xfp8087.s      
/
echo x - Makefile.ansi
sed '/^X/s///' > Makefile.ansi << '/'
X# Makefile for the boot monitor package.
X
XCC=exec cc
Xp=../kernel/kernel ../mm/mm ../fs/fs
Xl=/usr/lib
XLIB=$l/libd.A $l/libc.A $l/libe.A
Xs=/usr/src  # directory containing kernel, fs, mm
X
X# ACK ANSI C compiler
XCFLAGS=	-m -D_MINIX -D_POSIX_SOURCE -I$s
XLD=	$(CC) -s -r
X
XLIBS=
XO=	o
XS=	s
X
Xall:	construct bootblk monitor init
X
X# Assembly code boot block
Xbootblk:	bootblk.$S
X	$(LD) bootblk.$S -o bootblk
X
X# Put kernel, mm, fs into image file (old build program)
Xconstruct:	construct.$O rawfs.$O
X	$(CC) -s -i -o construct construct.$O rawfs.$O
X	@chmem =12288 construct >/dev/null
X
X# Display menu just after being booted (old menu program)
Xmonitor:	monhead.$S mon1.$O mon2.$O rawfs.$O
X	$(LD) monhead.$S -i mon1.$O mon2.$O rawfs.$O $(LIBS) -o monitor
X	@chmem =12000 monitor >/dev/null
X
X# System initialization; fork off login processes
Xinit:	$l/nhead.$O init.$O
X	$l/ld -o tmp $l/nhead.$O init.$O $(LIB) $l/end.A
X	$l/cv tmp >init
X	@rm -f tmp
X	@echo init done.
X
Ximage:	all
X	construct -i image $p init
X	@getlf "Insert a blank diskette in drive 0 and hit return"
X	construct -b /dev/fd0 bootblk monitor image
X
X# Object files
X#mon1.$O:	mon1.c
X#	$(CC) $(CFLAGS) -c mon1.c
X#
X#mon2.$O:	mon2.c
X#	$(CC) $(CFLAGS) -c mon2.c
X#
X#rawfs.$O:	rawfs.c
X#	$(CC) $(CFLAGS) -c rawfs.c
X
X
Xclean:
X	@rm -f *.bak bootblk.o bootblk construct construct.[so] mon?.[so] \
X	monitor rawfs rawfs.[so] init.[so] init *.out log
/
echo x - Makefile.kr
sed '/^X/s///' > Makefile.kr << '/'
X# Makefile for the boot monitor package.
X
XCC=exec cc
Xp=../kernel/kernel ../mm/mm ../fs/fs
Xl=/usr/lib
XLIB=$l/libc.a
Xs=/usr/src  # directory containing kernel, fs, mm
X
X# ACK K&R compiler
XCFLAGS=-I/usr/include -D_MINIX -D_POSIX_SOURCE -I$s
XLD=	exec asld
XLIBS=	$l/libc.a $l/end.s
XO=s
XS=s
Xall:	construct bootblk monitor init
X
X# Assembly code boot block
Xbootblk:	bootblk.$S
X	$(LD) bootblk.$S -o bootblk
X
X# Put kernel, mm, fs into image file (old build program)
Xconstruct:	construct.$O rawfs.$O
X	$(CC) -s -i -o construct construct.$O rawfs.$O
X	@chmem =12288 construct >/dev/null
X
X# Display menu just after being booted (old menu program)
Xmonitor:	monhead.$S mon1.$O mon2.$O rawfs.$O
X	$(LD) monhead.$S -i mon1.$O mon2.$O rawfs.$O $(LIBS) -o monitor
X	@chmem =12000 monitor >/dev/null
X
X# System initialization; fork off login processes
Xinit:	$l/head.$O init.$O
X	asld -o init $l/head.$O init.$O $(LIB) $l/end.s
X	@echo init done.
X
Ximage:	all
X	construct -i image $p init
X	@getlf "Insert a blank diskette in drive 0 and hit return"
X	construct -b /dev/fd0 bootblk monitor image
X
X# Object files
X#mon1.$O:	mon1.c
X#	$(CC) $(CFLAGS) -c mon1.c
X#
X#mon2.$O:	mon2.c
X#	$(CC) $(CFLAGS) -c mon2.c
X#
X#rawfs.$O:	rawfs.c
X#	$(CC) $(CFLAGS) -c rawfs.c
X
X
Xclean:
X	@rm -f *.bak bootblk.o bootblk construct construct.[so] mon?.[so] \
X	monitor rawfs rawfs.[so] init.[so] init *.out log
/
echo x - bootblk.s.ansi
sed '/^X/s///' > bootblk.s.ansi << '/'
X!	Bootblock 1.2 - Minix boot block.		Author: Kees J. Bot
X!					   Floppy sensing code: Guy Helmer
X!
X! When the PC is powered on, it will try to read the first sector of floppy
X! disk 0 into address 0x7C00.  If this fails due to the absence of flexible
X! magnetic media, it will read the master boot record from the first sector
X! of the hard disk.  This sector not only contains executable code, but also
X! the partition table of the hard disk.  When executed, it will select the
X! active partition and load the first sector of that into address 0x7C00.
X! This file contains the code that is eventually read from either the floppy
X! disk, or the hard disk partition.  It is just smart enough to load the
X! secondary boot code from the boot device into memory at address 0x10000 and
X! execute that.  The disk addresses for this secondary boot code are patched
X! into this code by installboot as 24-bit sector numbers and 8-bit sector
X! counts above enddata upwards.  The secondary boot code is in turn smart
X! enough to load the different parts of the Minix kernel into memory and
X! execute them to finally get Minix started.
X!
X! Kees J. Bot - 91/12/21:
X! Adapted Guy Helmers code and added hard disk support for my boot monitor
X! package.
X!
X
X	LOADOFF	   =	0x7C00	! 0x0000:LOADOFF is where this code is loaded
X	BOOTSEG    =	0x1000	! Secondary boot code segment.
X	BOOTOFF	   =	0x0030	! Offset into secondary boot above header
X	BUFFER	   =	0x0600	! First free memory
X	DSKBASE    =	  0x1E	! Floppy disk parameter vector
X	DSKPARSIZE =	    11	! 11 bytes of floppy parameters
X	SECTORS	   =	     4	! Offset into parameters to sectors per track
X	LOWSEC     =	     8	! Offset of logical first sector in partition
X				! table
X
X	! Variables addressed using bp register
X	device	   =	     0	! The boot device
X	lowsec	   =	     2	! Offset of boot partition within drive
X	secpcyl	   =	     6	! Sectors per cylinder = heads * sectors
X
X.define begtext, begdata, begbss, endtext, enddata, endbss, _main
X.data
Xbegdata:
X.bss
Xbegbss:
X.text
Xbegtext:
X_main:
X
X! Start boot procedure.
X
Xboot:
X	xor	ax, ax		! ax = 0x0000, the vector segment
X	mov	ds, ax
X	cli			! Ignore interrupts while setting stack
X	mov	ss, ax		! ss = ds = vector segment
X	mov	sp, #LOADOFF	! Usual place for a bootstrap stack
X	sti
X
X	push	ax
X	push	ax		! Push a zero lowsec(bp)
X
X	push	dx		! Boot device in dl will be device(bp)
X	mov	bp, sp		! Using var(bp) is one byte cheaper then var.
X
X	push	es
X	push	si		! es:si = partition table entry if hard disk
X
X	mov	di, #LOADOFF+parameters	! char (*di)[DSKPARSIZE] = parameters;
X
X	testb	dl, dl		! Winchester disks if dl >= 0x80
X	jge	floppy
X
Xwinchester:
X
X! Get the offset of the first sector of the boot partition from the partition
X! table.  The table is found at es:si, the lowsec parameter at offset LOWSEC.
X
X	eseg
X	les	ax, LOWSEC(si)	  ! es:ax = LOWSEC+2(si):LOWSEC(si)
X	mov	lowsec+0(bp), ax  ! Low 16 bits of partition's first sector
X	mov	lowsec+2(bp), es  ! High 16 bits of partition's first sector
X
X! Get the drive parameters, the number of sectors is bluntly written into the
X! floppy disk parameters.
X
X	movb	ah, #0x08	! Code for drive parameters
X	int	0x13		! dl still contains drive
X	andb	cl, #0x3F	! cl = max sector number (1-origin)
X	movb	SECTORS(di), cl	! Number of sectors per track
X	incb	dh		! dh = 1 + max head number (0-origin)
X	jmp	loadboot
X
X! Floppy:
X! Execute three read tests to determine the drive type.  Test for each floppy
X! type by reading the last sector on the first track.  If it fails, try a type
X! that has less sectors.  Therefore we start with 1.44M (18 sectors) then 1.2M
X! (15 sectors) ending with 720K/360K (both 9 sectors).  (The floppy parameters
X! of the last two are equal, apart from the motor start time.  This saves us
X! the rather painful "try to read track 41" test.)
X
Xnext:	add	di, #DSKPARSIZE	! Next set of parameters
X
Xfloppy:	mov	DSKBASE*4+0, di	! Load offset of disk parameters
X	mov	DSKBASE*4+2, ds	! Load segment of disk parameters
X
X	xorb	ah, ah		! Reset drive
X	int	0x13
X
X	movb	cl, SECTORS(di)	! cl = number of last sector on track
X
X	cmp	di, #LOADOFF+dsdd3  ! No need to do the last 720K/360K test
X	jz	success
X
X! Try to read the last sector on track 0
X
X	mov	es, lowsec(bp)	! es = vector segment (lowsec = 0)
X	mov	bx, #BUFFER	! es:bx buffer = 0x0000:0x0600
X	mov	ax, #0x0201	! Read sector, #sectors = 1
X	xorb	ch, ch		! Track 0, last sector
X	xorb	dh, dh		! Drive dl, head 0
X	int	0x13
X	jb	next		! Error, try the next floppy type
X
Xsuccess:movb	dh, #2		! Load number of heads for multiply
X				! Number of sectors is still in cl
X
Xloadboot:
X! Load the secondary boot code from the boot device
X
X	movb	al, cl		! al = cl = sectors per track
X	mulb	dh		! dh = heads, ax = heads * sectors
X	mov	secpcyl(bp), ax	! Sectors per cylinder = heads * sectors
X
X	mov	ax, #BOOTSEG	! Segment to load secondary boot code into
X	mov	es, ax
X	xor	bx, bx		! Load first sector at es:bx = BOOTSEG:0x0000
X	mov	si, #LOADOFF+addresses	! Start of the boot code addresses
Xload:
X	mov	ax, 1(si)	! Get next sector number: low 16 bits
X	movb	dl, 3(si)	! Bits 16-23 for your 8GB disk
X	xorb	dh, dh		! dx:ax = sector within partition
X	add	ax, lowsec+0(bp)
X	adc	dx, lowsec+2(bp)! dx:ax = sector within drive
X	div	secpcyl(bp)	! ax = cylinder, dx = sector within cylinder
X	xchg	ax, dx		! ax = sector within cylinder, dx = cylinder
X	movb	ch, dl		! ch = low 8 bits of cylinder
X	divb	SECTORS(di)	! al = head, ah = sector (0-origin)
X	xorb	dl, dl		! About to shift bits 8-9 of cylinder into dl
X	shr	dx, #1
X	shr	dx, #1		! dl[6..7] = high cylinder
X	orb	dl, ah		! dl[0..5] = sector (0-origin)
X	movb	cl, dl		! cl[0..5] = sector, cl[6..7] = high cyl
X	incb	cl		! cl[0..5] = sector (1-origin)
X	movb	dh, al		! dh = al = head
X	movb	dl, device(bp)	! dl = device to read
X	movb	al, SECTORS(di)	! Sectors per track - Sector number (0-origin)
X	subb	al, ah		! = Sectors left on this track
X	cmpb	al, (si)	! Compare with # sectors to read
X	jbe	read		! Can't read past the end of a cylinder?
X	movb	al, (si)	! (si) < sectors left on this track
Xread:	push	ax		! Save al = sectors to read
X	movb	ah, #2		! Code for disk read (all registers in use now!)
X	int	0x13		! Call the BIOS for a read
X	pop	cx		! Restore al in cl
X	jb	error		! Jump on disk read error
X	movb	al, cl		! Restore al = sectors read
X	addb	bh, al		! bx += 2 * al * 256 (add bytes read)
X	addb	bh, al		! es:bx = where next sector must be read
X	add	1(si), ax	! Update address by sectors read
X	adcb	3(si), ah	! Don't forget bits 16-23 (add ah = 0)
X	subb	(si), al	! Decrement sector count by sectors read
X	jnz	load		! Not all sectors have been read
X	add	si, #4		! Next (address, count) pair
X	cmpb	ah, (si)	! Done when no sectors to read
X	jnz	load		! Read next chunk of secondary boot code
X
Xdone:
X
X! Call secondary boot, assuming a long a.out header (48 bytes).  The a.out
X! header is usually short (32 bytes), but secondary boot has two entry points:
X! One at offset 0 for the long, and one at offset 16 for the short header.
X! Parameters passed in registers are:
X!
X!	dl	= Boot-device.
X!	es:si	= Partition table entry if hard disk.
X!
X	pop	si		! Restore es:si = partition table entry
X	pop	es		! dl is still loaded
X	jmpf	BOOTOFF, BOOTSEG  ! jmp to sec. boot (skipping header).
X
X! Read error: print message, hang forever
Xerror:
X!	mov	si, #LOADOFF+errno+1	! Uncomment this at disaster time
X!prnum:	movb	al, ah		! Error number in ah
X!	andb	al, #0x0F	! Low 4 bits
X!	cmpb	al, #10		! A-F?
X!	jb	digit		! 0-9!
X!	addb	al, #7		! 'A' - ':'
X!digit:	addb	(si), al	! Modify '0' in string
X!	dec	si
X!	movb	cl, #4		! Next 4 bits
X!	shrb	ah, cl
X!	jnz	prnum		! Again if digit > 0
X
X	mov	si, #LOADOFF+rderr  ! String to print
Xprint:	lodsb			! al = *si++ is char to be printed
X	movb	ah, #14		! 14 = print char
X	mov	bx, #0x0001	! Page 0, foreground color
X	int	0x10		! Call BIOS VIDEO_IO
X	cmp	si, #LOADOFF+errend  ! End of string reached?
X	jb	print
X
X! Hang forever waiting for CTRL-ALT-DEL
Xhang:	jmp	hang
X
X.data
Xrderr:	.ascii	"Read error "
X!errno:	.ascii	"00 "
Xerrend:
X
Xparameters:
X! Floppy disk parameters sorted down by sectors per track.  (The format gap
X! length params of the 3.5" disks might be wrong, but that won't matter.)
X
X! 1.44M 3.5"
Xdshd3:	.data1	0xAF, 0x02, 25, 2, 18, 0x1B, 0xFF, 0x54, 0xF6, 15, 8
X
X! 1.2M 5.25"
Xdshd5:	.data1	0xDF, 0x02, 25, 2, 15, 0x1B, 0xFF, 0x54, 0xF6, 15, 8
X
X! 720K 3.5", also used for 360K 5.25"
Xdsdd3:	.data1	0xDF, 0x02, 25, 2,  9, 0x2A, 0xFF, 0x50, 0xF6, 15, 8
X
X! Just for completeness, here are the real 360K params.
X!dsdd5:	.data1	0xDF, 0x02, 25, 2,  9, 0x2A, 0xFF, 0x54, 0xF6, 15, 3
X
X.text
Xendtext:
X.data
Xenddata:
Xaddresses:
X! The space below this is for disk addresses for a 62K secondary boot
X! program (worst case, i.e. file is fragmented).  It should be enough.
X.bss
Xendbss:
/
echo x - bootblk.s.kr
sed '/^X/s///' > bootblk.s.kr << '/'
XLOADOFF	   =	0x7C00
XBOOTSEG    =	0x1000
XBOOTOFF	   =	0x0030
XBUFFER	   =	0x0600
XDSKBASE    =	  0x1E
XDSKPARSIZE =	    11
XSECTORS	   =	     4
XLOWSEC     =	     8
Xdevice	   =	     0
Xlowsec	   =	     2
Xsecpcyl	   =	     6
X.define begtext, begdata, begbss, endtext, enddata, endbss
X.data
Xbegdata: 
X.bss
Xbegbss: 
X.text
Xbegtext: 
Xboot: 
Xxor	ax, ax
Xmov	ds, ax
Xcli
Xmov	ss, ax
Xmov	sp, #LOADOFF
Xsti
Xpush	ax
Xpush	ax
Xpush	dx
Xmov	bp, sp
Xpush	es
Xpush	si
Xmov	di, #LOADOFF+parameters
Xtestb	dl, dl
Xjge	floppy
Xwinchester: 
Xseg es
Xles	ax, LOWSEC(si)
Xmov	lowsec+0(bp), ax
Xmov	lowsec+2(bp), es
Xmovb	ah, #0x08
Xint	0x13
Xandb	cl, #0x3F
Xmovb	SECTORS(di), cl
Xincb	dh
Xj loadboot
Xnext: add	di, #DSKPARSIZE
Xfloppy: mov	DSKBASE*4+0, di
Xmov	DSKBASE*4+2, ds
Xxorb	ah, ah
Xint	0x13
Xmovb	cl, SECTORS(di)
Xcmp	di, #LOADOFF+dsdd3
Xjz	success
Xmov	es, lowsec(bp)
Xmov	bx, #BUFFER
Xmov	ax, #0x0201
Xxorb	ch, ch
Xxorb	dh, dh
Xint	0x13
Xjb	next
Xsuccess: movb	dh, #2
Xloadboot: 
Xmovb	al, cl
Xmulb	dh
Xmov	secpcyl(bp), ax
Xmov	ax, #BOOTSEG
Xmov	es, ax
Xxor	bx, bx
Xmov	si, #LOADOFF+addresses
Xload: 
Xmov	ax, 1(si)
Xmovb	dl, 3(si)
Xxorb	dh, dh
Xadd	ax, lowsec+0(bp)
Xadc	dx, lowsec+2(bp)
Xdiv	secpcyl(bp)
Xxchg	ax, dx
Xmovb	ch, dl
Xdivb	SECTORS(di)
Xxorb	dl, dl
Xshr	dx, #1
Xshr	dx, #1
Xorb	dl, ah
Xmovb	cl, dl
Xincb	cl
Xmovb	dh, al
Xmovb	dl, device(bp)
Xmovb	al, SECTORS(di)
Xsubb	al, ah
Xcmpb	al, (si)
Xjbe	read
Xmovb	al, (si)
Xread: push	ax
Xmovb	ah, #2
Xint	0x13
Xpop	cx
Xjb	error
Xmovb	al, cl
Xaddb	bh, al
Xaddb	bh, al
Xadd	1(si), ax
Xadcb	3(si), ah
Xsubb	(si), al
Xjnz	load
Xadd	si, #4
Xcmpb	ah, (si)
Xjnz	load
Xdone: 
Xpop	si
Xpop	es
Xjmpi BOOTOFF, BOOTSEG
Xerror: 
Xmov	si, #LOADOFF+rderr
Xprint: lodb
Xmovb	ah, #14
Xmov	bx, #0x0001
Xint	0x10
Xcmp	si, #LOADOFF+errend
Xjb	print
Xhang: j hang
X.data
Xrderr: .ascii	"Read error "
Xerrend: 
Xparameters: 
Xdshd3: .byte 	0xAF, 0x02, 25, 2, 18, 0x1B, 0xFF, 0x54, 0xF6, 15, 8
Xdshd5: .byte 	0xDF, 0x02, 25, 2, 15, 0x1B, 0xFF, 0x54, 0xF6, 15, 8
Xdsdd3: .byte 	0xDF, 0x02, 25, 2,  9, 0x2A, 0xFF, 0x50, 0xF6, 15, 8
X.text
Xendtext: 
X.data
Xenddata: 
Xaddresses: 
X.bss
Xendbss: 
/
echo x - construct.c
sed '/^X/s///' > construct.c << '/'
X/* construct 1.7 - Make a device bootable	Author: Kees J. Bot */
X
X/* Construct is a program that enables MINIX to be booted in various ways.
X * It is the successor to 'build'.  It deals with four components:
X *	- boot sector		# a 512-byte program written in assembler
X *	- param sector		# sector following boot sector, holding params
X *	- monitor		# a C program that offers menus at startup
X *	- image			# a file containing kernel(s), mm, fs, init
X *
X * Booting can occur from any of these kinds of devices:
X *	- floppy containing boot sector, param sector, monitor, image
X *	- floppy containing boot sector, param sector, file sys, monitor, image
X *	- HD partition: boot sector, param sector, files /monitor and /minix
X *
X * Construct can be called in several ways, most commonly:
X *     construct -i image kernel mm fs init		   # make image on file
X *     construct -b /dev/fdx bootblk monitor [label:]image # make boot floppy
X *     construct -d /dev/fdx bootblk monitor [label:]image # make demo floppy
X *     construct -h /dev/hdx bootblk monitor		   # install HD monitor
X *
X * The -i flag combines the pieces to make an image file (like the old 'build'
X * program did).  It is also possible to put >= 2 kernels on a boot disk, e.g.:
X *     construct -i image at:at_ker xt:xt_ker mm fs init   # 2 kernels on disk
X *     construct -b /dev/fd0 bootblk monitor at,xt:image
X *
X * The -b flag takes such an image file, a bootblk, and a monitor file, and 
X * puts them on a floppy disk, along with a parameter sector so the book 
X * sector can locate the monitor to start.  
X *
X * The -d flag is like -b, except that expects the device already contains a
X * valid file system.  The monitor will be placed after the file system instead
X * of in sector 2 (directly after the param sector), as -b does.
X *
X * The -h flag installs the boot block on a hard disk partition.  It also 
X * looks up the block addresses on the disk where the monitor is located, and
X * patches them into the boot block. This means that when /monitor is changed, 
X * construct -h must be run again.  However, changing /minix does not require
X * running construct -h.
X *
X * The most common combinations are thus (to build a boot disk on floppy):
X *	construct -i image kernel mm fs init		# build the image file
X *	construct -b /dev/fdx bootblk monitor image	# create boot floppy
X *
X * or, to build a demo disk on a floppy already containing a file system:
X *	construct -i image kernel mm fs init		# build the image file
X *	construct -d /dev/fdx bootblk monitor image	# create boot floppy
X *
X * or, to install a new /minix on a HD partition, compiled monitor and copy
X * it to /monitor on /dev/hdx, then type:
X *
X *	construct -i /minix kernel mm fs init		# install new binary
X *      construct -h /dev/hdx bootblk monitor          # install HD monitor
X *
X * The construct -h call need only be repeated when /monitor is changed.
X * Booting from the hard disk requires the monitor partition to be active.
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stddef.h>
X#include <stdlib.h>
X#include <unistd.h>
X#include <fcntl.h>
X#include <string.h>
X#include <errno.h>
X#include <a.out.h>
X#include <minix/config.h>
X#include <minix/const.h>	/* Gives us BLOCK_SIZE. */
X#include <stdio.h>
X#include "tools.h"
X
X#define BOOTBLOCK	0	/* Of course */
X#define SIGNATURE	0xAA55	/* Boot block signature. */
X#define BOOT_MAX	64	/* Absolute maximum size of secondary boot */
X#define SIGPOS		510	/* Where to put signature word. */
X#define PARTPOS		446	/* Offset to the partition table in a master
X				 * boot block. */
Xenum howto {FS, BOOT, DEMO};
X
X#define align(n)	(((n) + ((SECTOR_SIZE) - 1)) & ~((SECTOR_SIZE) - 1))
X#define intel()(memcmp((void*)little_endian,(void*)&some_endian,(size_t)4)==0)
X
Xoff_t total_text = 0;
Xoff_t total_data = 0;
Xoff_t total_bss = 0;
X
Xint making_image = 0;
Xlong some_endian = 0x12345678;
Xchar little_endian[] = {0x78, 0x56, 0x34, 0x12};
Xint load_syms;		/* Flag that tells if the symbol table must be loaded*/
X
X_PROTOTYPE(int main, (int argc, char **argv ));
X_PROTOTYPE(void report, (char *label ));
X_PROTOTYPE(void fatal, (char *label ));
X_PROTOTYPE(char *basename, (char *name ));
X_PROTOTYPE(void hdr2hdr, (struct exec *hdr, struct exec *rawhdr ));
X_PROTOTYPE(void read_header, (char *proc, FILE *procf, struct image_header *ih,
X					struct exec *hdr ));
X_PROTOTYPE(void padimage, (char *image, FILE *imagef, int n ));
X_PROTOTYPE(void copyexec, (char *proc, FILE *procf, char *image, FILE *imagef,
X					long n ));
X_PROTOTYPE(void make_image, (char *image, char **procv ));
X_PROTOTYPE(void readblock, (off_t blk, char *buf ));
X_PROTOTYPE(void writeblock, (off_t blk, char *buf ));
X_PROTOTYPE(void make_bootable, (enum howto how, char *device, char *bootblock,
X			char *bootcode, off_t *bootoff, off_t *bootlen ));
X_PROTOTYPE(void raw_install, (char *file, off_t start, off_t *len ));
X_PROTOTYPE(void boot_disk, (enum howto how, char *device, char *bootblock, 
X					char *bootcode, char **imagev ));
X_PROTOTYPE(void install_master, (char *device, char *masterboot ));
X_PROTOTYPE(void usage, (void));
X
X#define between(a, c, z)	((unsigned) ((c) - (a)) <= ((z) - (a)))
X
X
Xint main(argc, argv)
Xint argc;
Xchar **argv;
X{
X  int n;
X
X  if (argc < 2) usage();
X  if ((n = strlen(argv[1])) < 2) usage();
X
X  if (argc >= 4 && n <= 6 && strncmp(argv[1], "-image", n) == 0)
X	make_image(argv[2], argv + 3);
X  else if (!intel())
X	usage();
X  else if (argc == 5 && n <= 5 && strncmp(argv[1], "-hard", n) == 0)
X	make_bootable(FS, argv[2], argv[3], argv[4], NULL, NULL);
X  else if (argc >= 6 && n <= 5 && strncmp(argv[1], "-boot", n) == 0)
X	boot_disk(BOOT, argv[2], argv[3], argv[4], argv + 5);
X  else if (argc >= 6 && n <= 5 && strncmp(argv[1], "-demo", n) == 0)
X	boot_disk(DEMO, argv[2], argv[3], argv[4], argv + 5);
X  else if (argc == 4 && n <= 7 && strncmp(argv[1], "-master", n) == 0)
X	install_master(argv[2], argv[3]);
X  else
X	usage();
X  exit(0);
X}
X
Xvoid report(label)
Xchar *label;
X/* Label: No such file or directory */
X{
X  int e = errno;
X  fprintf(stderr, "construct: ");
X  fflush(stderr);
X  errno = e;
X  perror(label);
X}
X
Xvoid fatal(label)
Xchar *label;
X{
X  report(label);
X  exit(1);
X}
X
Xchar *basename(name)
Xchar *name;
X/* Return the last component of name, stripping trailing slashes from name.
X * Precondition: name != "/".  If name is prefixed by a label, then the
X * label is copied to the basename too.
X */
X{
X  static char base[IM_NAME_MAX];
X  char *p, *bp = base;
X
X  if ((p = strchr(name, ':')) != NULL) {
X	while (name <= p && bp < base + IM_NAME_MAX - 1) *bp++ = *name++;
X  }
X  for (;;) {
X	if ((p = strrchr(name, '/')) == NULL) {
X		p = name;
X		break;
X	}
X	if (*++p != 0) break;
X	*--p = 0;
X  }
X  while (*p != 0 && bp < base + IM_NAME_MAX - 1) *bp++ = *p++;
X  *bp = 0;
X  return(base);
X}
X
X
Xvoid hdr2hdr(hdr, rawhdr)
Xstruct exec *hdr, *rawhdr;
X/* Transform an Intel little-endian header to a header for internal use. */
X{
X  int n = sizeof(*hdr) / sizeof(long) - 2;
X  long *h = (long *) hdr + 2;
X  unsigned char *r;
X
X  /* Copy the character fields. */
X  *hdr = *rawhdr;
X
X  /* A_version is a short. */
X  r = (unsigned char *) &rawhdr->a_version;
X  hdr->a_version = (short) *r++ << 0;
X  hdr->a_version |= (short) *r++ << 8;
X
X  /* The rest are longs. */
X  do {
X	*h = (long) *r++ << 0;
X	*h |= (long) *r++ << 8;
X	*h |= (long) *r++ << 16;
X	*h++ |= (long) *r++ << 24;
X  } while (--n > 0);
X}
X
Xvoid read_header(proc, procf, image_hdr, hdr)
Xchar *proc;
XFILE *procf;
Xstruct image_header *image_hdr;
Xstruct exec *hdr;
X/* Read the a.out header of a program and check it.  If procf happens to be
X * NULL then the header is already in *image_hdr and need only be checked.
X */
X{
X  int n, big = 0;
X  static int banner = 0;
X  long a_text, a_bss;
X
X  /* Put the basename of proc in the header. */
X  strncpy(image_hdr->name, basename(proc), IM_NAME_MAX);
X  image_hdr->name[IM_NAME_MAX] = 0;
X  if (procf == NULL) {
X	n = image_hdr->process.a_hdrlen;	/* header already present. */
X  } else {
X	/* Read the raw header. */
X	n = fread((char *) &image_hdr->process, (int) sizeof(char),
X		  A_MINHDR, procf);
X
X	if (ferror(procf)) fatal(proc);
X  }
X
X  if (n < A_MINHDR || BADMAG(image_hdr->process)) {
X	fprintf(stderr, "construct: %s is not an executable\n", proc);
X	exit(1);
X  }
X
X  /* Get the rest of the exec header (usually nothing). */
X  if (procf != NULL) {
X	(void) fread((char *) &image_hdr->process + A_MINHDR,
X		     (int) sizeof(char),
X		     image_hdr->process.a_hdrlen - A_MINHDR, procf);
X
X	if (ferror(procf)) fatal(proc);
X  }
X
X  /* No symbol table loaded by default. */
X  if (!load_syms) image_hdr->process.a_syms = 0;
X
X  /* Cook the header from Intel to internal format. */
X  hdr2hdr(hdr, &image_hdr->process);
X
X  if (!banner) {
X	printf("\n    text    data     bss     size\n");
X	banner = 1;
X  }
X  a_text = (making_image && hdr->a_flags & A_SEP) ? align(hdr->a_text)
X	: hdr->a_text;
X  a_bss = making_image ? align(hdr->a_bss) : hdr->a_bss;
X
X  printf("%8ld%8ld%8ld%9ld  %s\n", a_text, hdr->a_data, a_bss,
X         a_text + hdr->a_data + a_bss, proc);
X  total_text += a_text;
X  total_data += hdr->a_data;
X  total_bss += a_bss;
X
X  if (hdr->a_cpu == A_I8086) {
X	long data = hdr->a_data + hdr->a_bss;
X
X	if (!(hdr->a_flags & A_SEP)) data += hdr->a_text;
X	if (hdr->a_text >= 65536) big |= 1;
X	if (data >= 65536) big |= 2;
X  }
X  if (big) {
X	fprintf(stderr,
X		"%s will crash, %s%s%s segment%s larger then 64K\n",
X		proc,
X		big & 1 ? "text" : "",
X		big == 2 ? " and " : "",
X		big & 2 ? "data" : "",
X		big == 2 ? "s are" : " is"
X		);
X  }
X}
X
Xvoid padimage(image, imagef, n)
Xchar *image;
XFILE *imagef;
Xint n;
X/* Add n zeros to image to pad it to a sector boundary. */
X{
X  while (n > 0) {
X	if (putc(0, imagef) == EOF) fatal(image);
X	n--;
X  }
X}
X
Xvoid copyexec(proc, procf, image, imagef, n)
Xchar *proc;
XFILE *procf;
Xchar *image;
XFILE *imagef;
Xlong n;
X/* Copy n bytes from proc to image padded to fill a sector. */
X{
X  int pad, c;
X
X  /* Compute number of padding bytes. */
X  pad = align(n) - n;
X
X  while (n > 0) {
X	if ((c = getc(procf)) == EOF) {
X		if (ferror(procf)) fatal(proc);
X		fprintf(stderr, "construct: premature EOF on %s\n",
X			proc);
X		exit(1);
X	}
X	if (putc(c, imagef) == EOF) fatal(image);
X	n--;
X  }
X  padimage(image, imagef, pad);
X}
X
Xvoid make_image(image, procv)
Xchar *image, **procv;
X/* Collect a set of files in an image, each "segment" is nicely padded out
X * to SECTOR_SIZE, so it may be read from disk into memory without trickery.
X */
X{
X  FILE *imagef, *procf;
X  char *proc, *file;
X  int procn;
X  struct image_header proc_hdr;
X  struct exec hdr;
X  struct stat st;
X  int k_flags;
X
X  making_image = 1;
X  if ((imagef = fopen(image, "w")) == NULL) fatal(image);
X  for (procn = 0; (proc = *procv++) != NULL; procn++) {
X	/* Check for the nostrip flag. */
X	if (proc[0] == '-' && proc[1] == 's') {
X		if (*(proc += 2) == 0 && (proc = *procv++) == NULL) break;
X		load_syms = 1;
X	} else {
X		load_syms = 0;
X	}
X
X	/* Remove the label from the file name. */
X	if ((file = strchr(proc, ':')) != NULL)
X		file++;
X	else
X		file = proc;
X
X	/* Real files please, may need to seek. */
X	if (stat(file, &st) < 0 || (errno = EISDIR, !S_ISREG(st.st_mode))
X				    || (procf = fopen(file, "r")) == NULL)
X		fatal(proc);
X
X	/* Read a.out header. */
X	read_header(proc, procf, &proc_hdr, &hdr);
X
X	/* Write header padded to fill a sector */
X	(void) fwrite((char *) &proc_hdr, (int) sizeof(proc_hdr), 1, imagef);
X	if (ferror(imagef)) fatal(image);
X	padimage(image, imagef, (int) (SECTOR_SIZE - sizeof(proc_hdr)));
X
X	/* Copy text and data of proc to image. */
X	if (hdr.a_flags & A_SEP) {
X		/* Separate I&D: pad text & data separately. */
X
X		copyexec(proc, procf, image, imagef, hdr.a_text);
X		copyexec(proc, procf, image, imagef, hdr.a_data);
X	} else {
X		/* Common I&D: keep text and data together. */
X
X		copyexec(proc, procf, image, imagef,
X			 hdr.a_text + hdr.a_data);
X	}
X
X	/* Load the symbol table. */
X	copyexec(proc, procf, image, imagef, hdr.a_syms);
X
X	/* Done with proc. */
X	(void) fclose(procf);
X  }
X
X  /* Done with image. */
X  if (fclose(imagef) == EOF) fatal(image);
X  printf("  ------  ------  ------  -------\n");
X  printf("%8ld%8ld%8ld%9ld  total\n\n\n", total_text, total_data, total_bss,
X			         total_text + total_data + total_bss, proc);
X}
X
Xint rawfd;			/* File descriptor to open device. */
Xchar *rawdev;			/* Name of device. */
X
Xvoid readblock(blk, buf)
Xoff_t blk;
Xchar *buf;
X/* For rawfs, so that it can read blocks. */
X{
X  int n;
X
X  if (lseek(rawfd, blk * BLOCK_SIZE, SEEK_SET) < 0
X				|| (n = read(rawfd, buf, BLOCK_SIZE)) < 0)
X	fatal(rawdev);
X
X  if (n < BLOCK_SIZE) {
X	fprintf(stderr, "construct: unexpected EOF on %s\n", rawdev);
X	exit(1);
X  }
X}
X
Xvoid writeblock(blk, buf)
Xoff_t blk;
Xchar *buf;
X/* Add a function to write blocks for local use. */
X{
X  if (lseek(rawfd, blk * BLOCK_SIZE, SEEK_SET) < 0
X				      || write(rawfd, buf, BLOCK_SIZE) < 0)
X	fatal(rawdev);
X}
X
X
Xvoid make_bootable(how, device, bootblock, bootcode, bootoff, bootlen)
Xenum howto how;
Xchar *device, *bootblock, *bootcode;
Xoff_t *bootoff, *bootlen;
X/* Install bootblock on the bootsector of device with the disk addresses to
X * bootcode patched into the data segment of bootblock.  "How" tells if the
X * boot code is present inside a file system, simply behind the boot block,
X * or behind the file system for a demo disk.
X */
X{
X  char buf[BLOCK_SIZE], *adrp;
X  struct fileaddr {
X	off_t address;
X	int count;
X  } bootaddr[BOOT_MAX + 1], *bap = bootaddr;
X  struct exec boothdr;
X  struct image_header dummy;
X  struct stat st;
X  ino_t ino;
X  off_t sector, max_sector;
X  FILE *bootf;
X  off_t addr, fssize;
X
X  /* Open device and set variables for readblock. */
X  if ((rawfd = open(rawdev = device, O_RDWR)) < 0) fatal(device);
X
X  /* Read and check the superblock. */
X  fssize = r_super();
X
X  switch (how) {
X      case FS:
X      case DEMO:
X	if (fssize == 0) {
X		fprintf(stderr,
X		     "construct: %s is not a Minix file system\n", device);
X		exit(1);
X	}
X	break;
X      case BOOT:
X	if (fssize != 0) {
X		int s;
X		printf("%s contains a file system!\n", device);
X		printf("Scribbling in 10 seconds");
X		for (s = 0; s < 10; s++) {
X			fputc('.', stdout);
X			fflush(stdout);
X			sleep(1);
X		}
X		fputc('\n', stdout);
X	}
X	fssize = 1;		/* Just a boot block. */
X  }
X
X  if (how != FS) {
X	/* For a raw installation, we need to copy the boot code onto
X	 * the device, so we need to look at the file to be copied. 
X	 */
X	if (stat(bootcode, &st) < 0) fatal(bootcode);
X
X	if ((bootf = fopen(bootcode, "r")) == NULL) fatal(bootcode);
X  } else {
X	/* For a normal installation, the boot code is already
X	 * present on the device, so we find and check that. */
X	if ((ino = r_lookup(ROOT_INO, bootcode)) == 0) fatal(bootcode);
X
X	r_stat(ino, &st);
X
X	/* Get the header from the first block. */
X	if ((addr = r_vir2abs((off_t) 0)) == 0) {
X		dummy.process.a_magic[0] = (unsigned char) (~A_MAGIC0 & BYTE);
X	} else {
X		readblock(addr, buf);
X		memcpy((void*) &dummy.process, (char*)buf,sizeof(struct exec));
X	}
X	bootf = NULL;
X  }
X
X  /* See if it is an executable (read_header does the check). */
X  read_header(bootcode, bootf, &dummy, &boothdr);
X  if (bootf != NULL) fclose(bootf);
X
X  /* Get all the sector addresses of the secondary boot code. */
X  max_sector = (boothdr.a_hdrlen + boothdr.a_text
X	      + boothdr.a_data + SECTOR_SIZE - 1) / SECTOR_SIZE;
X
X  if (max_sector > BOOT_MAX * RATIO) {
X	fprintf(stderr, "construct: %s is way too big\n", bootcode);
X	exit(0);
X  }
X  if (how != FS) {
X	/* Pass offset and lenght of boot code to caller. */
X	*bootoff = fssize * RATIO;
X	*bootlen = max_sector;
X  }
X  bap->count = 0;		/* Trick to get the address recording going. */
X
X  for (sector = 0; sector < max_sector; sector++) {
X	if (how != FS)
X		addr = fssize + (sector / RATIO);
X	else if ((addr = r_vir2abs(sector / RATIO)) == 0) {
X		fprintf(stderr, "construct: %s has holes!\n");
X		exit(1);
X	}
X	addr = (addr * RATIO) + (sector % RATIO);
X
X	/* First address of the addresses array? */
X	if (bap->count == 0) bap->address = addr;
X
X	/* Paste sectors together in a multisector read. */
X	if (bap->address + bap->count == addr)
X		bap->count++;
X	else {
X		/* New address. */
X		bap++;
X		bap->address = addr;
X		bap->count = 1;
X	}
X  }
X  (++bap)->count = 0;		/* No more. */
X
X  /* Get the boot block and patch the pieces in. */
X  readblock((off_t) BOOTBLOCK, buf);
X  if ((bootf = fopen(bootblock, "r")) == NULL) fatal(bootblock);
X  read_header(bootblock, bootf, &dummy, &boothdr);
X
X  if (boothdr.a_text + boothdr.a_data + 4 * (bap - bootaddr) + 1 > SIGPOS) {
X	fprintf(stderr,
X		"construct: %s + addresses to %s don't fit in boot sector\n",
X		bootblock, bootcode);
X	fprintf(stderr,
X	   "You can try copying/reinstalling %s to defragment it\n", bootcode);
X	exit(1);
X  }
X
X  /* All checks out right.  Read bootblock into the boot block! */
X  (void) fread(buf, 1, (int) (boothdr.a_text + boothdr.a_data), bootf);
X  if (ferror(bootf)) fatal(bootblock);
X  (void) fclose(bootf);
X
X  /* Patch the addresses in. */
X  adrp = buf + (int) (boothdr.a_text + boothdr.a_data);
X  for (bap = bootaddr; bap->count != 0; bap++) {
X	*adrp++ = bap->count;
X	*adrp++ = (bap->address >> 0) & 0xFF;
X	*adrp++ = (bap->address >> 8) & 0xFF;
X	*adrp++ = (bap->address >> 16) & 0xFF;
X  }
X
X  /* Zero count stops bootblock's reading loop. */
X  *adrp++ = 0;
X
X/* In case somebody is some day interested.
X  printf("%d address%s (%d bytes) patched into the boot block\n",
X         bap - bootaddr, bap != bootaddr + 1 ? "es" : "",
X         4 * (bap - bootaddr) + 1);
X*/
X
X  /* Boot block signature. */
X  adrp = buf + SIGPOS;
X  *adrp++ = (SIGNATURE >> 0) & 0xFF;
X  *adrp++ = (SIGNATURE >> 8) & 0xFF;
X
X  /* Sector 2 of the boot block is used for boot parameters, initially
X   * filled with null commands (newlines).  Initialize it only if there
X   * is a control character there other than newline.
X   */
X  for (adrp = buf + SECTOR_SIZE; adrp < buf + 2 * SECTOR_SIZE; adrp++) {
X	if (*adrp != '\n' && (unsigned) *adrp < ' ') {
X		/* Assume param sector is junk, initialize it. */
X		memset((void *) (buf + SECTOR_SIZE), '\n',
X		       (size_t) SECTOR_SIZE);
X		break;
X	}
X  }
X
X  /* Install boot block. */
X  writeblock((off_t) BOOTBLOCK, buf);
X}
X
Xvoid raw_install(file, start, len)
Xchar *file;
Xoff_t start, *len;
X/* Copy bootcode or an image to the boot device at the given absolute disk
X * block number.  This "raw" installation is used to place bootcode and
X * image on a disk without a filesystem to make a simple boot disk.  Useful
X * in automated scripts for J. Random User.
X * Note: *len == 0 when an image is read.  It is set right afterwards.
X */
X{
X  static char buf[BLOCK_SIZE];	/* Nonvolatile block buffer. */
X  FILE *f;
X  off_t sec = start;
X  static int banner = 0;
X
X  if ((f = fopen(file, "r")) == NULL) fatal(file);
X
X  /* Copy sectors from file onto the boot device. */
X  do {
X	int off = sec % RATIO;
X
X	if (fread(buf + off * SECTOR_SIZE, 1, SECTOR_SIZE, f) == 0) break;
X	if (off == RATIO - 1) writeblock(sec / RATIO, buf);
X  } while (++sec != start + *len);
X
X  if (ferror(f)) fatal(file);
X  (void) fclose(f);
X
X  /* Write a partial block, this may be the last image. */
X  if (sec % RATIO != 0) writeblock(sec / RATIO, buf);
X
X  if (!banner) {
X	printf("\n                    start sectors\n");
X	banner = 1;
X  }
X  *len = sec - start;
X  printf("%25ld%8ld  %s\n", start, *len, file);
X}
X
Xvoid boot_disk(how, device, bootblock, bootcode, imagev)
Xenum howto how;
Xchar *device, *bootblock, *bootcode, **imagev;
X/* Make a boot or demo disk placing code in the params sector to select
X * among several kernels, each containing a different winchester driver.
X */
X{
X  off_t pos;			/* Sector to write next file. */
X  off_t len;			/* Lenght of file in sectors. */
X  char buf[BLOCK_SIZE + 256];	/* Boot and params sector. */
X  char *parmp = buf + SECTOR_SIZE;
X  char *labels, *label, *image;
X  int nolabel = 0, choice;
X
X  /* Fill the boot block with code & addresses.  Ask for position and
X   * length of boot code. 
X   */
X  make_bootable(how, device, bootblock, bootcode, &pos, &len);
X  readblock((off_t) BOOTBLOCK, buf);
X
X  /* Place the boot code onto the boot device. */
X  raw_install(bootcode, pos, &len);
X
X  if (how == BOOT) {
X	/* A boot only disk needs a floppy swap. */
X	strcpy(parmp, "delay=swap\n");
X	parmp += strlen(parmp);
X  }
X  while ((labels = *imagev++) != NULL) {
X	/* Place each kernel image on the boot device. */
X
X	if ((image = strchr(labels, ':')) != NULL) {
X		*image++ = 0;
X	} else {
X		if (nolabel) {
X			fprintf(stderr,
X			     "construct: Only one image can be the default\n");
X			exit(1);
X		}
X		nolabel = 1;
X		image = labels;
X		labels = NULL;
X	}
X	pos += len;
X	len = 0;
X	raw_install(image, pos, &len);
X
X	if (labels == NULL) {
X		/* Let this image be the default. */
X		sprintf(parmp, "image=%ld:%ld\n", pos, len);
X		parmp += strlen(parmp);
X	}
X	while (labels != NULL) {
X		/* Image is prefixed by a comma separated list of
X		 * labels.  Define functions to select label and
X		 * image. 
X		 */
X		label = labels;
X		if ((labels = strchr(labels, ',')) != NULL) *labels++ = 0;
X
X		choice = label[0];
X		if (between('A', choice, 'Z')) choice = choice - 'A' + 'a';
X
X		sprintf(parmp,
X		"%s(%c){label=%s;image=%ld:%ld;echo %s kernel selected;menu}\n",
X			label, choice, label, pos, len, label);
X		parmp += strlen(parmp);
X	}
X
X	if (parmp > buf + BLOCK_SIZE) {
X		fprintf(stderr, "construct: Out of param space\n");
X		exit(1);
X	}
X  }
X  memset((void *) parmp, '\n', buf + BLOCK_SIZE - parmp);
X  writeblock((off_t) BOOTBLOCK, buf);
X
X  /* Tell the total size of the data on the device, nice for dd(1). */
X  pos += len;
X  printf("\nNumber of disk blocks used = %ld\n", (pos + RATIO - 1) / RATIO);
X}
X
Xvoid install_master(device, masterboot)
Xchar *device, *masterboot;
X/* Booting a hard disk is a two stage process:  The master bootstrap in sector
X * 0 loads the bootstrap from sector 0 of the active partition which in turn
X * starts the operating system.  This code installs such a master bootstrap
X * on a hard disk.
X */
X{
X  FILE *masf;
X  unsigned long size;
X  struct stat st;
X  char buf[BLOCK_SIZE];
X
X  /* Open device. */
X  if ((rawfd = open(rawdev = device, O_RDWR)) < 0) fatal(device);
X
X  /* Open the master boot code. */
X  if ((masf = fopen(masterboot, "r")) == NULL) fatal(masterboot);
X
X  /* See if the user is cloning a device. */
X  if (fstat(fileno(masf), &st) >= 0 && S_ISBLK(st.st_mode))
X	size = PARTPOS;
X  else {
X	/* Read and check header otherwise. */
X	struct image_header dummy;
X	struct exec hdr;
X
X	read_header(masterboot, masf, &dummy, &hdr);
X	size = hdr.a_text + hdr.a_data;
X  }
X  if (size > PARTPOS) {
X	fprintf(stderr, "construct: %s is too big\n", masterboot);
X	exit(1);
X  }
X
X  /* Read the master boot block, patch it, write. */
X  readblock((off_t) BOOTBLOCK, buf);
X
X  (void) fread(buf, 1, (int) size, masf);
X  if (ferror(masf)) fatal(masterboot);
X
X  /* Install signature. */
X  buf[SIGPOS + 0] = (SIGNATURE >> 0) & 0xFF;
X  buf[SIGPOS + 1] = (SIGNATURE >> 8) & 0xFF;
X
X  writeblock((off_t) BOOTBLOCK, buf);
X}
X
Xvoid usage()
X{
X  fprintf(stderr, "Usage:\n\tconstruct -i image kernel mm fs init		(To make image file)\n");
X  if (!intel()) {
X	fprintf(stderr,	"\nThese don't work on this non-Intel machine:\n");
X  }
X  fprintf(stderr, "\tconstruct -b device bootblk monitor image	(To make a boot disk)\n");
X  fprintf(stderr, "\tconstruct -d device bootblk monitor image	(To make a demo disk)\n");
X  fprintf(stderr, "\tconstruct -h device bootblk monitor		(To install HD monitor)\n");
X  exit(1);
X}
/
echo x - mon.doc.ms
sed '/^X/s///' > mon.doc.ms << '/'
X.SH
XDoing more with the Minix Boot Monitor.
X.PP
XThis text describes the menu interface of the Minix Boot Monitor, and
Xthe commands that may be used to customize it.
X.LP
XFirst of all, the monitor mode as distributed normally hides some of the
Xfunctionality, but shows you an explanation of the environment
Xvariables instead.  If you add -DEXTENDED_LIST to CFLAGS in the Makefile
Xand recompile, then you will no longer see the long explanation (you
Xshould know it by now), but you will see all the commands the monitor
Xknows about.
X.SH
XThe commands.
X.PP
XThe boot command has two functions, one is to load and start Minix, the
Xother is to boot a different operating system.  If the first partition
Xon your hard disk contains MS-DOS, then
X.DS
X.B
Xboot hd1
X.R
X.DE
Xwill boot MS-DOS.  (Not all operating systems like to be called this
Xway, some insist on being on the active partition.)
X.LP
XThe delay, ls, and other simple commands are not too difficult to
Xunderstand, just try them out.  The trap command may be used to execute
Xa function after a delay.  You can show a menu first and boot Minix
Xafter 5 seconds of inactivity like this:
X.DS
X.B
Xtrap 5000 boot; menu
X.R
X.DE
X(This must be typed on one line, traps are cancelled when the prompt is
Xprinted.)
X.SH
XFunctions.
X.PP
XFunctions are used to bundle commands, or to build menu items.  The best
Xexample of a simple function is 'main', the function executed by the
Xmonitor on startup.  Main is by default defined as:
X.DS
X.B
Xmain() { menu }
X.R
X.DE
XSo that's why you see a menu at the start.  The example with 'trap'
Xabove could be executed by main if you type:
X.DS
X.B
Xmain() { trap 5000 boot; menu }
Xsave
X.R
X.DE
XThe save command will save the changed environment of the monitor to the
Xsecond half of the boot block, the "boot parameters sector".
X.LP
XFunctions may have one or two arguments, the first is a key to be
Xpressed from the menu to execute the function, the optional second
Xargument is the text that is to be displayed on the menu.  The single
Xargument functions should only be produced by construct, like this
Xone:
X.DS
X.B
XAT(a) {label=AT;image=42:626;echo AT kernel selected;menu}
X.R
X.DE
XIt invites you to choose one of many kernels on a special boot floppy.
X.LP
XThe two argument functions are used to customize the menu, once you
Xdefine one the default option disappears, so your first function will
Xprobably be one to start Minix.  Example:
X.DS
X.B
Xminix(=,Start Minix) { boot }
Xdos(d,Boot MS-DOS) { boot hd1 }
Xsave
Xmenu
X.R
X.DE
XNow you can type '=' or 'd' to choose between Minix and DOS.
/
echo x - mon1.c
sed '/^X/s///' > mon1.c << '/'
X/* mon1.c 1.4.9 - Load and start Minix.		Author: Kees J. Bot */
X
X/* This program has more features than are officially supported. If
X * EXTENDED_LIST is defined, the help menu lists additional options.
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stddef.h>
X#include <stdlib.h>
X#include <limits.h>
X#include <string.h>
X#include <errno.h>
X#include <a.out.h>
X
X#include "minix/config.h"
X#include "minix/const.h"
X#include "minix/partition.h"
X#include "minix/boot.h"
X#include "minix/type.h"
X#include "minix/syslib.h"
X#include "kernel/const.h"
X#include "kernel/type.h"
X
X#undef EXTERN
X#define EXTERN			/* Empty */
X
X#define _MONHEAD
X#include <tools.h>
X
X#ifndef _ANSI
X#undef NULL
X#define NULL 0
X#endif
X
X#define B_NODEV		-1
X#define B_NOSIG		-2
X
X#define arraysize(a)	(sizeof(a) / sizeof((a)[0]))
X#define arraylimit(a)	((a) + arraysize(a))
X#define b_getenv(name)	(*searchenv(name))
X#define between(a, c, z)	((unsigned) ((c) - (a)) <= ((z) - (a)))
X#define	printf	printk
X
X#define DEV_HD1a (DEV_HD0 + 128)/* First subpartition /dev/hd1a. */
X
Xtypedef struct token {
X  struct token *next;		/* Next in a command chain. */
X  char *token;
X} token;
X
X#define CACHE_SIZE	32	/* More than enough. */
X
Xint cache_live = 0;
X
Xstruct cache_entry {
X  u32_t block;
X  u16_t seg;
X} cache[CACHE_SIZE];
X
Xenum whatfun { NOFUN, SELECT, DEFFUN, USERFUN };
X
X_PROTOTYPE(char *bios_err, (int err ));
X_PROTOTYPE(char *unix_err, (int err ));
X_PROTOTYPE(void rwerr, (char *rw, off_t sec, int err ));
X_PROTOTYPE(void writerr, (off_t sec, int err ));
X_PROTOTYPE(void init_cache, (void));
X_PROTOTYPE(void invalidate_cache, (void));
X_PROTOTYPE(void readblock, (off_t blk, char *buf ));
X_PROTOTYPE(char *readline, (void));
X_PROTOTYPE(int sugar, (char *tok ));
X_PROTOTYPE(char *onetoken, (char **aline, int arg ));
X_PROTOTYPE(token **tokenize, (token **acmds, char *line, int *fundef ));
X_PROTOTYPE(char *poptoken, (void));
X_PROTOTYPE(void voidtoken, (void));
X_PROTOTYPE(void void_cmds, (void));
X_PROTOTYPE(void interrupt, (void));
X_PROTOTYPE(void migrate, (void));
X_PROTOTYPE(void partsort, (struct part_entry *table ));
X_PROTOTYPE(int get_master, (char *master, struct part_entry **tbl, u32_t pos));
X_PROTOTYPE(void initialize, (void));
X_PROTOTYPE(void sfree, (char *s ));
X_PROTOTYPE(char *copystr, (char *s ));
X_PROTOTYPE(int is_default, (environment *e ));
X_PROTOTYPE(environment **searchenv, (char *name ));
X_PROTOTYPE(char *b_body, (char *name ));
X_PROTOTYPE(int b_setenv, (int flags, char *name, char *arg, char *value ));
X_PROTOTYPE(int b_setvar, (int flags, char *name, char *value ));
X_PROTOTYPE(void b_unset, (char *name ));
X_PROTOTYPE(void get_parameters, (void));
X_PROTOTYPE(void addparm, (char **ap, char *n ));
X_PROTOTYPE(void save_parameters, (void));
X_PROTOTYPE(void show_env, (void));
X_PROTOTYPE(int exec_bootstrap, (void));
X_PROTOTYPE(void boot_device, (char *devname ));
X_PROTOTYPE(void ls, (char *dir ));
X_PROTOTYPE(void unschedule, (void));
X_PROTOTYPE(void schedule, (long msec, char *cmd ));
X_PROTOTYPE(int expired, (void));
X_PROTOTYPE(int delay, (char *msec ));
X_PROTOTYPE(enum whatfun menufun, (environment *e ));
X_PROTOTYPE(void menu, (void));
X_PROTOTYPE(void execute, (void));
X_PROTOTYPE(void monitor, (void));
X_PROTOTYPE(void boot, (void));
X_PROTOTYPE(void disp_cmds, (void));
X
Xchar *bios_err(err)
Xint err;
X/* Translate BIOS error code to a readable string.  (This is a rare trait
X * known as error checking and reporting.  Take a good look at it, you won't
X * see it often.)
X */
X{
X  static struct errlist {
X	unsigned char err;
X	char *what;
X  } errlist[] = {
X	{ 0x00, "No error" },
X	{ 0x01, "Invalid command" },
X	{ 0x02, "Address mark not found" },
X	{ 0x03, "Disk write-protected" },
X	{ 0x04, "Sector not found" },
X	{ 0x05, "Reset failed" },
X	{ 0x06, "Floppy disk removed" },
X	{ 0x07, "Bad parameter table" },
X	{ 0x08, "DMA overrun" },
X	{ 0x09, "DMA crossed 64 KB boundary" },
X	{ 0x0A, "Bad sector flag" },
X	{ 0x0B, "Bad track flag" },
X	{ 0x0C, "Media type not found" },
X	{ 0x0D, "Invalid number of sectors on format" },
X	{ 0x0E, "Control data address mark detected" },
X	{ 0x0F, "DMA arbitration level out of range" },
X	{ 0x10, "Uncorrectable CRC or ECC data error" },
X	{ 0x11, "ECC corrected data error" },
X	{ 0x20, "Controller failed" },
X	{ 0x40, "Seek failed" },
X	{ 0x80, "Disk timed-out" },
X	{ 0xAA, "Drive not ready" },
X	{ 0xBB, "Undefined error" },
X	{ 0xCC, "Write fault" },
X	{ 0xE0, "Status register error" },
X	{ 0xFF, "Sense operation failed" }
X  };
X  struct errlist *errp;
X
X  for (errp = errlist; errp < arraylimit(errlist); errp++)
X	if (errp->err == err) return(errp->what);
X  return("Unknown error");
X}
X
Xchar *unix_err(err)
Xint err;
X{
X/* Translate the few errors rawfs can give. */
X  switch (err) {
X      case ENOENT:
X	return("No such file or directory");
X      case ENOTDIR:
X	return("Not a directory");
X      default:	return("Unknown error");
X  }
X}
X
Xvoid rwerr(rw, sec, err)
Xchar *rw;
Xoff_t sec;
Xint err;
X{
X  printf("\n%s error 0x%02x (%s) at sector %ld absolute\n",
X         rw, err, bios_err(err), sec);
X}
X
Xvoid readerr(sec, err)
Xoff_t sec;
Xint err;
X{
X  rwerr("Read", sec, err);
X}
X
Xvoid writerr(sec, err)
Xoff_t sec;
Xint err;
X{
X  rwerr("Write", sec, err);
X}
X
X/* Readblock support for rawfs.c */
Xvoid init_cache()
X{
X/* Initialize the block cache. */
X  struct cache_entry *pc;
X  u16_t seg = FREESEG;
X
X  for (pc = cache; pc < arraylimit(cache); pc++) {
X	pc->block = -1;
X	pc->seg = seg;
X	seg += BLOCK_SIZE / HCLICK_SIZE;
X  }
X
X  cache_live = 1;		/* Turn it on. */
X}
X
Xvoid invalidate_cache()
X/* The cache can't be used when Minix is loaded. */
X{
X  cache_live = 0;
X}
X
Xvoid readblock(blk, buf)
Xoff_t blk;
Xchar *buf;
X{
X/* Read blocks for the rawfs package with caching.  Wins 2 seconds. */
X  int r = 0;
X  u32_t sec = lowsec + blk * RATIO;
X
X  if (!cache_live) {
X	/* Cache invalidated, load block directly in place. */
X	r = readsectors((u16_t) buf, dseg, sec, 1 * RATIO);
X  } else {
X	/* Search through the cache from 0 up.  Move the one found to
X	 * the front of the cache, then optionally read a block. */
X	struct cache_entry *pc, lifo, tmp;
X
X	for (pc = cache; pc < arraylimit(cache); pc++) {
X		tmp = *pc;
X		*pc = lifo;
X		lifo = tmp;
X		if (lifo.block == blk) break;
X	}
X	cache[0] = lifo;
X
X	if (cache[0].block != blk) {
X		r = readsectors(0, cache[0].seg, sec, 1 * RATIO);
X		cache[0].block = blk;
X	}
X	raw_copy((u16_t) buf, dseg, 0, cache[0].seg, BLOCK_SIZE);
X  }
X  if (r != 0) {
X	readerr(sec, r);
X	reboot();
X  }
X}
X
Xchar *readline()
X{
X/* Read a line including a newline with echoing. */
X  char *line;
X  size_t i = 0, z;
X  int c;
X
X  line = (char *) malloc((z = 20) * sizeof(char));
X
X  do {
X	c = getchar();
X
X	if (strchr("\b\177\25\30", c) != NULL) {
X		/* Backspace, DEL, ctrl-U, or ctrl-X. */
X		do {
X			if (i == 0) break;
X			printf("\b \b");
X			i--;
X		} while (c == '\25' || c == '\30');
X	} else if (c < ' ' && c != '\n')
X		putchar('\7');
X	else {
X		putchar(c);
X		line[i++] = c;
X		if (i == z) line = (char *) realloc((void *) line,
X					   (z *= 2) * sizeof(char));
X	}
X  } while (c != '\n');
X  line[i] = 0;
X  return(line);
X}
X
Xint sugar(tok)
Xchar *tok;
X{
X/* Recognize special tokens. */
X  return(strchr("=(){};\n", tok[0]) != NULL);
X}
X
Xchar *onetoken(aline, arg)
Xchar **aline;
Xint arg;
X{
X/* Returns a string with one token for tokenize.  Arg is true when reading
X * between ( and ).
X */
X  char *line = *aline;
X  size_t n;
X  char *tok;
X
X  /* Skip spaces. */
X  while (*line == ' ') line++;
X
X  *aline = line;
X
X  /* Don't do odd junk (nor the terminating 0!). */
X  if ((unsigned) *line < ' ' && *line != '\n') return(NULL);
X
X  if (arg) {
X	/* Function argument, anything goes except ). */
X	int depth = 0;
X
X	while ((unsigned) *line >= ' ') {
X		if (*line == '(') depth++;
X		if (*line == ')' && --depth < 0) break;
X		line++;
X	}
X	while (line > *aline && line[-1] == ' ') line--;
X  } else if (sugar(line)) {
X	/* Single character token. */
X	line++;
X  } else {
X	/* Multicharacter token. */
X	do
X		line++;
X	while ((unsigned) *line > ' ' && !sugar(line));
X  }
X  n = line - *aline;
X  tok = (char *) malloc((n + 1) * sizeof(char));
X  memcpy((void *) tok, (void *) *aline, n);
X  tok[n] = 0;
X  if (tok[0] == '\n') tok[0] = ';';	/* ';' same as '\n' */
X
X  *aline = line;
X  return(tok);
X}
X
X/* Typed commands form strings of tokens. */
Xtoken **tokenize(acmds, line, fundef)
Xtoken **acmds;			/* Splice tokenized line into this list. */
Xchar *line;			/* Characters to be tokenized. */
Xint *fundef;			/* Keeps state when forming a function def. */
X{
X/* Takes a line apart to form tokens.  The tokens are inserted into a command
X * chain at *acmds.  Tokenize returns a reference to where another line could
X * be added.  The fundef variable holds the state tokenize is in when decoding
X * a multiline function definition.  It is nonzero when more must be read.
X * Tokenize looks at spaces as token separators, and recognizes only
X * ';', '=', '(', ')' '{', '}', and '\n' as single character tokens.
X */
X  int fd = *fundef;
X  char *tok;
X  token *newcmd;
X  static char funsugar[] = {'(', 0, ')', '{', '}'};
X
X  while ((tok = onetoken(&line, fd == 1)) != NULL) {
X	if (fd == 1)
X		fd++;		/* Function argument. */
X	else if (funsugar[fd] == tok[0]) {
X		/* Recognize next token as part of a function def. */
X		fd = tok[0] == '}' ? 0 : fd + 1;
X	} else if (fd != 0) {
X		if (tok[0] == ';' && fd == 3) {
X			/* Kill separator between ')' and '{'. */
X			free((void *) tok);
X			continue;
X		}
X
X		/* Syntax error unless between '{' and '}'. */
X		if (fd != 4) fd = 0;
X	}
X	newcmd = (token *) malloc(sizeof(*newcmd));
X	newcmd->token = tok;
X	newcmd->next = *acmds;
X	*acmds = newcmd;
X	acmds = &newcmd->next;
X  }
X  *fundef = fd;
X  return(acmds);
X}
X
Xtoken *cmds;			/* String of commands to execute. */
X
Xchar *poptoken()
X{
X/* Pop one token off the command chain. */
X  token *cmd = cmds;
X  char *tok = cmd->token;
X
X  cmds = cmd->next;
X  free((void *) cmd);
X
X  return(tok);
X}
X
Xvoid voidtoken()
X/* Remove one token from the command chain. */
X{
X  free((void *) poptoken());
X}
X
Xvoid void_cmds()
X{
X/* Void the whole list. */
X  while (cmds != NULL) voidtoken();
X}
X
Xvoid interrupt()
X{
X/* Clean up after an ESC has been typed. */
X  disp_cmds();		/* display the command list */
X  while (peekchar() == ESC) (void) getchar();
X
X  /* Delete leftover commands. */
X  void_cmds();
X}
X
Xstruct biosdev {
X  int device, primary, secondary;
X} bootdev, tmpdev;
X
Xstruct part_entry boot_part;
Xchar dskpars[DSKPARSIZE] =	/* 360K floppy disk parameters (for now). */
X{0xDF, 0x02, 25, 2, 9, 0x2A, 0xFF, 0x50, 0xF6, 15, 8};
X
Xvoid migrate()
X{
X/* Copy boot program to the far end of memory, this must be done asap to
X * put the data area cleanly inside a 64K chunk (no DMA problems).
X */
X  u16_t oldseg = cseg;
X  u16_t size = (runsize + HCLICK_SIZE - 1) >> HCLICK_SHIFT;
X  u16_t memsize = get_low_memsize() * (1024 / HCLICK_SIZE);
X  u16_t dma64k = (memsize - 1) & 0xF000;
X  u16_t newseg = memsize - size;
X  u16_t dst_off, src_off, src_seg;
X  vector dskbase;
X
X  /* Check if data segment crosses a 64k boundary. */
X  if (newseg + (dseg - cseg) < dma64k) newseg = dma64k - size;
X
X  /* Get some variables into my address space before they get mashed. */
X  if (device < 0x80) {
X	/* Floppy disk parameters. */
X	dst_off = (u16_t) &dskbase;
X	src_off = (u16_t) (DSKBASE * sizeof(vector));
X	raw_copy(dst_off, dseg, src_off, 0, sizeof(vector));
X
X	dst_off = (u16_t) dskpars;
X	src_off = (u16_t) dskbase.offset;
X	src_seg = (u16_t) dskbase.segment;
X	raw_copy(dst_off, dseg, src_off, src_seg, DSKPARSIZE);
X  } else {
X	/* Hard disk partition table entry into boot_part. */
X	dst_off = (u16_t) &boot_part;
X	src_off = (u16_t) rem_part.offset;
X	src_seg = rem_part.segment;
X	raw_copy(dst_off, dseg, src_off, src_seg, sizeof(boot_part));
X  }
X
X  /* Set the new cseg for relocate. */
X  cseg = newseg;
X
X  /* Copy code and data in large chunks. */
X  do {
X	u16_t chunk = size < 0x0FFF ? size : 0x0FFF;
X
X	raw_copy(0, newseg, 0, oldseg, chunk << HCLICK_SHIFT);
X	oldseg += chunk;
X	newseg += chunk;
X	size -= chunk;
X  } while (size > 0);
X
X  relocate();			/* Make the copy running. */
X
X  /* Set the parameters for the boot device using global variables
X   * device and dskpars.  (This particular call should not fail.) 
X   */
X  (void) dev_geometry();
X}
X
Xint get_master(master, table, pos)
Xchar *master;
Xstruct part_entry **table;
Xu32_t pos;
X{
X/* Read a master boot sector and its partition table. */
X  int r, n;
X  struct part_entry *pe, **pt;
X
X  if ((r = readsectors((u16_t) master, dseg, pos, 1)) != 0) return(r);
X
X  pe = (struct part_entry *) (master + PART_TABLE_OFF);
X  for (pt = table; pt < table + NR_PARTITIONS; pt++) *pt = pe++;
X
X  /* DOS has the misguided idea that partition tables must be sorted. */
X  if (pos != 0) return(0);	/* But only the primary. */
X
X  n = NR_PARTITIONS;
X  do {
X	for (pt = table; pt < table + NR_PARTITIONS - 1; pt++) {
X		if (pt[0]->sysind == NO_PART
X		    || (pt[0]->lowsec > pt[1]->lowsec
X			&& pt[1]->sysind != NO_PART)) {
X			pe = pt[0];
X			pt[0] = pt[1];
X			pt[1] = pe;
X		}
X	}
X  } while (--n > 0);
X  return(0);
X}
X
Xvoid initialize()
X{
X  char master[SECTOR_SIZE];
X  struct part_entry *table[NR_PARTITIONS];
X  int r, p;
X  u32_t masterpos;
X
X  /* Find out what the boot device[A and partition was. */
X  bootdev.device = device;
X  bootdev.primary = -1;
X  bootdev.secondary = -1;
X
X  if (device < 0x80) return;
X
X  /* Get the partition table from the very first sector, and determine
X   * the partition we booted from.  Migrate() was so nice to put the
X   * partition table entry of the booted partition in boot_part. */
X
X  /* The only thing really needed from the booted partition: */
X  lowsec = boot_part.lowsec;
X
X  masterpos = 0;		/* Master bootsector position. */
X
X  for (;;) {
X	/* Extract the partition table from the master boot sector. */
X	if ((r = get_master(master, table, masterpos)) != 0) {
X		readerr(masterpos, r);
X		reboot();
X	}
X
X	/* See if you can find "lowsec" back. */
X	for (p = 0; p < NR_PARTITIONS; p++)
X		if (lowsec - table[p]->lowsec < table[p]->size) break;
X
X	if (lowsec == table[p]->lowsec) {	/* Found! */
X		if (bootdev.primary < 0)
X			bootdev.primary = p;
X		else
X			bootdev.secondary = p;
X		break;
X	}
X
X	/* This happens when lightning strikes while booting: */
X	if (p == NR_PARTITIONS || bootdev.primary >= 0) {
X		printf("Can't find the partition starting at %lu???\n",
X		       lowsec);
X		reboot();
X	}
X
X	/* See if the primary partition is subpartitioned. */
X	bootdev.primary = p;
X	masterpos = table[p]->lowsec;
X  }
X}
X
Xchar null[] = "";
X
Xvoid sfree(s)
Xchar *s;
X{
X/* Free a non-null string. */
X  if (s != NULL && s != null) free((void *) s);
X}
X
Xchar *copystr(s)
Xchar *s;
X{
X/* Copy a non-null string using malloc. */
X  char *c;
X
X  if (*s == 0) return(null);
X  c = (char *) malloc((strlen(s) + 1) * sizeof(char));
X  strcpy(c, s);
X  return(c);
X}
X
Xint is_default(e)
Xenvironment *e;
X{
X  return(e->flags & E_SPECIAL) && e->defval == NULL;
X}
X
Xenvironment **searchenv(name)
Xchar *name;
X{
X  environment **aenv = &env;
X
X  while (*aenv != NULL && strcmp((*aenv)->name, name) != 0)
X	aenv = &(*aenv)->next;
X
X  return(aenv);
X}
X
Xchar *b_value(name)
Xchar *name;
X{
X/* The value of a variable. */
X  environment *e = b_getenv(name);
X  return(e == NULL || !(e->flags & E_VAR) ? NULL : e->value);
X}
X
Xchar *b_body(name)
Xchar *name;
X/* The value of a function. */
X{
X  environment *e = b_getenv(name);
X  return(e == NULL || !(e->flags & E_FUNCTION) ? NULL : e->value);
X}
X
Xint b_setenv(flags, name, arg, value)
Xint flags;
Xchar *name, *arg, *value;
X{
X/* Change the value of an environment variable.  Returns the flags of the
X * variable if you are not allowed to change it, 0 otherwise.
X */
X  environment **aenv, *e;
X
X  if (*(aenv = searchenv(name)) == NULL) {
X	e = (environment *) malloc(sizeof(*e));
X	e->name = copystr(name);
X	e->flags = flags;
X	e->defval = NULL;
X	e->next = NULL;
X	*aenv = e;
X  } else {
X	e = *aenv;
X
X	/* Don't touch reserved names and don't change special
X	 * variables to functions or vv. */
X	if (e->flags & E_RESERVED || (e->flags & E_SPECIAL
X		  && (e->flags & E_FUNCTION) != (flags & E_FUNCTION)
X				      ))
X		return(e->flags);
X
X	e->flags = (e->flags & E_STICKY) | flags;
X	if (is_default(e))
X		e->defval = e->value;
X	else
X		sfree(e->value);
X	sfree(e->arg);
X  }
X  e->arg = copystr(arg);
X  e->value = copystr(value);
X  return(0);
X}
X
Xint b_setvar(flags, name, value)
Xint flags;
Xchar *name, *value;
X/* Set variable or simple function. */
X{
X  return(b_setenv(flags, name, null, value));
X}
X
Xvoid b_unset(name)
Xchar *name;
X{
X/* Remove a variable from the environment.  A special variable is reset to
X * its default value.
X */
X  environment **aenv, *e;
X
X  if ((e = *(aenv = searchenv(name))) == NULL) return;
X
X  if (e->flags & E_SPECIAL) {
X	if (e->defval != NULL) {
X		sfree(e->arg);
X		e->arg = null;
X		sfree(e->value);
X		e->value = e->defval;
X		e->defval = NULL;
X	}
X  } else {
X	sfree(e->name);
X	sfree(e->arg);
X	sfree(e->value);
X	*aenv = e->next;
X	free((void *) e);
X  }
X}
X
X
Xlong a2l(a)
Xchar *a;
X{
X/* Cheap atol(). */
X  int sign = 1;
X  long l = 0;
X
X  if (*a == '-') {
X	sign = -1;
X	a++;
X  }
X  while (between('0', *a, '9')) l = l * 10 + (*a++ - '0');
X
X  return(sign * l);
X}
X
Xchar *ul2a(n)
Xu32_t n;
X{
X/* Transform a long number to ascii digits. */
X  static char num[3 * sizeof(n)];
X  char *pn = arraylimit(num) - 1;
X
X  do {
X	*--pn = (n % 10) + '0';
X  } while ((n /= 10) > 0);
X  return(pn);
X}
X
Xchar *u2a(n1)
Xint n1;
X{
X/* Transform a short number to ascii digits. */
X  u16_t n;
X
X  n = (u16_t) n1;
X  return(ul2a((u32_t) n));
X}
X
Xvoid get_parameters()
X{
X  char params[SECTOR_SIZE + 1];
X  token **acmds;
X  int vid, r, fundef = 0;
X  static char *vid_type[] = {"mda", "cga", "ega", "ega", "vga", "vga"};
X
X  /* Variables that Minix needs: */
X  b_setvar(E_SPECIAL | E_VAR | E_DEV, "rootdev", "ram");
X  b_setvar(E_SPECIAL | E_VAR | E_DEV, "ramimagedev", "bootdev");
X  b_setvar(E_SPECIAL | E_VAR, "ramsize", "0");
X  b_setvar(E_SPECIAL | E_VAR, "keyboard", "standard");
X  b_setvar(E_SPECIAL | E_VAR, "processor", u2a(get_processor()));
X  b_setvar(E_SPECIAL | E_VAR, "memsize", u2a(get_low_memsize()));
X  b_setvar(E_SPECIAL | E_VAR, "emssize", u2a(get_ext_memsize()));
X  vid = get_video();
X  b_setvar(E_SPECIAL | E_VAR, "chrome", (vid & 1) ? "color" : "mono");
X  b_setvar(E_SPECIAL | E_VAR, "video", vid_type[vid]);
X
X  /* Variables boot needs: */
X  b_setvar(E_SPECIAL | E_VAR, "image", "minix");
X  b_setvar(E_SPECIAL | E_FUNCTION, "main", "menu");
X
X  /* Default menu function: */
X  b_setenv(E_RESERVED | E_FUNCTION, "\1", "=,Start Minix", "boot");
X
X  /* Reserved names: */
X  b_setvar(E_RESERVED, "scancode", null);
X  b_setvar(E_RESERVED, "boot", null);
X  b_setvar(E_RESERVED, "menu", null);
X  b_setvar(E_RESERVED, "set", null);
X  b_setvar(E_RESERVED, "unset", null);
X  b_setvar(E_RESERVED, "save", null);
X  b_setvar(E_RESERVED, "ls", null);
X  b_setvar(E_RESERVED, "echo", null);
X  b_setvar(E_RESERVED, "trap", null);
X
X  /* Tokenize bootparams sector. */
X  if ((r = readsectors((u16_t) params, dseg, lowsec + PARAMSEC, 1)) != 0)
X	readerr(lowsec + PARAMSEC, r);
X  else {
X	params[SECTOR_SIZE] = 0;
X	acmds = tokenize(&cmds, params, &fundef);
X
X	/* Reboot code may have new parameters. */
X	if (device >= 0x80 && boot_part.sysind == NO_PART) {
X		raw_copy((u16_t) params, dseg, (u16_t) (boot_part.size & 0x0F),
X				 (u16_t) (boot_part.size >> 4), SECTOR_SIZE);
X		acmds = tokenize(&cmds, params, &fundef);
X	}
X
X	/* Stuff the default action into the command chain. */
X	(void) tokenize(acmds, ":;main", &fundef);
X  }
X}
X
Xvoid addparm(ap, n)
Xchar **ap, *n;
X{
X  char *p = *ap;
X  while (*n != 0 && *p != 0) *p++ = *n++;
X  *ap = p;
X}
X
Xvoid save_parameters()
X{
X/* Save nondefault environment variables to the bootparams sector. */
X  environment *e;
X  char params[SECTOR_SIZE + 1];
X  char *p = params;
X  int r;
X
X  /* Default filling: */
X  memset((void *) params, '\n', (size_t) SECTOR_SIZE);
X
X  /* Don't touch the 0! */
X  params[SECTOR_SIZE] = 0;
X
X  for (e = env; e != NULL; e = e->next) {
X	if (e->flags & E_RESERVED || is_default(e)) continue;
X
X	addparm(&p, e->name);
X	if (e->flags & E_FUNCTION) {
X		addparm(&p, "(");
X		addparm(&p, e->arg);
X		addparm(&p, "){");
X	} else {
X		addparm(&p, (e->flags & (E_DEV | E_SPECIAL)) != E_DEV
X			? "=" : "=d ");
X	}
X	addparm(&p, e->value);
X	if (e->flags & E_FUNCTION) addparm(&p, "}");
X	if (*p == 0) {
X		printf("The environment is too big\n");
X		return;
X	}
X	*p++ = '\n';
X  }
X
X  /* Save the parameters on disk. */
X  if ((r = writesectors((u16_t) params, dseg, lowsec + PARAMSEC, 1)) != 0) {
X	writerr(lowsec + PARAMSEC, r);
X	printf("Can't save environment\n");
X  }
X}
X
Xvoid show_env()
X{
X/* Show the environment settings. */
X  environment *e;
X
X  for (e = env; e != NULL; e = e->next) {
X	if (e->flags & E_RESERVED) continue;
X
X#ifndef EXTENDED_LIST
X/* Suppress main and delay unless EXTENDED_LIST is defined. */
X	if (strcmp(e->name, "main") == 0) continue;
X	if (strcmp(e->name, "delay") == 0) continue;
X#endif
X
X	if (e->flags & E_FUNCTION) {
X		printf("%s(%s) {%s}", e->name, e->arg, e->value);
X	} else {
X		printf("%s = ", e->name);
X		if (is_default(e)) putchar('(');
X		printf("%s", e->value);
X		if (is_default(e)) putchar(')');
X	}
X	putchar('\n');
X  }
X}
X
Xint numprefix(s, ps)
Xchar *s, **ps;
X{
X/* True iff s is a string of digits.  *ps will be set to the first nondigit
X * if non-NULL, otherwise the string should end.
X */
X  if (!between('0', *s, '9')) return(0);
X
X  do
X	s++;
X  while (between('0', *s, '9'));
X
X  if (ps == NULL) return(*s == 0);
X
X  *ps = s;
X  return(1);
X}
X
Xint numeric(s)
Xchar *s;
X{
X  return(numprefix(s, (char **) NULL));
X}
X
Xdev_t name2dev(name)
Xchar *name;
X{
X/* Translate, say, /dev/hd3 to a device number.  If the name can't be
X * found on the boot device, then do some guesswork.  The global structure
X * "tmpdev" will be filled in based on the name, so that "boot hd6" knows
X * what device to boot without interpreting device numbers.
X */
X  dev_t dev = -1;
X  ino_t ino;
X  struct stat st;
X  char *n, *s;
X  static char fdN[] = "fdN";
X  static char hdNX[] = "hdNNX";
X  static char sub[] = "a";
X
X  tmpdev.primary = tmpdev.secondary = -1;
X
X  /* The special name "bootdev" must be translated to the boot device. */
X  if (strcmp(name, "bootdev") == 0) {
X	if (device < 0x80) {
X		/* Floppy disk. */
X		strcpy(fdN + 2, u2a(device));
X		name = fdN;
X	} else {
X		/* Hard disk */
X		strcpy(hdNX + 2, u2a((device - 0x80) * (1 + NR_PARTITIONS)
X				     + 1 + bootdev.primary));
X		if (bootdev.secondary >= 0) {
X			sub[0] = 'a' + bootdev.secondary;
X			strcat(hdNX, sub);
X		}
X		name = hdNX;
X	}
X  }
X
X  /* Now translate fd0, hd6, etc. the BIOS way. */
X  n = name;
X  if (strncmp(n, "/dev/", 5) == 0) n += 5;
X
X  if (n[0] == 'f' && n[1] == 'd' && numeric(n + 2))
X	tmpdev.device = a2l(n + 2);
X  else if (n[0] == 'h' && n[1] == 'd' && numprefix(n + 2, &s)) {
X	tmpdev.primary = a2l(n + 2);
X	tmpdev.device = 0x80 + tmpdev.primary / (1 + NR_PARTITIONS);
X	tmpdev.primary = (tmpdev.primary % (1 + NR_PARTITIONS)) - 1;
X	if (*s == 0)
X		 /* Not a secondary */ ;
X	else if (tmpdev.primary >= 0 && between('a', *s, 'd') && s[1] == 0)
X		tmpdev.secondary = *s - 'a';
X	else
X		tmpdev.device = -1;
X  } else
X	tmpdev.device = -1;
X
X  /* Look the name up on the boot device for the UNIX device number. */
X  if (fsok) {
X	/* The current working directory is "/dev". */
X	ino = name[0] == '/' ? ROOT_INO : r_lookup(ROOT_INO, "dev");
X
X	if (ino != 0) ino = r_lookup(ino, name);
X
X	if (ino != 0) {
X		/* Name has been found, extract the device number. */
X		r_stat(ino, &st);
X		if (!S_ISBLK(st.st_mode)) {
X			printf("%s is not a block device\n");
X			errno = 0;
X			return((dev_t) -1);
X		}
X		dev = st.st_rdev;
X	}
X  }
X  if (dev == -1 && tmpdev.device >= 0) {
X	/* The name can't be found on the boot device, do guesswork. */
X	if (tmpdev.device < 0x80) 
X		dev = DEV_FD0 + tmpdev.device;
X	else if (tmpdev.secondary < 0)
X		dev = DEV_HD0 + (tmpdev.device - 0x80) * (1 + NR_PARTITIONS)
X			      + (1 + tmpdev.primary);
X	else
X		dev = DEV_HD1a + ((tmpdev.device - 0x80) * NR_PARTITIONS
X			  + tmpdev.primary) * NR_PARTITIONS + tmpdev.secondary;
X  }
X
X  /* Don't forget the RAM device. */
X  if (dev == -1 && strcmp(n, "ram") == 0) dev = DEV_RAM;
X
X  if (dev == -1) {
X	printf("Can't recognize %s as a device\n", name);
X	errno = 0;
X  }
X  return(dev);
X}
X
Xint exec_bootstrap()
X{
X/* Load boot sector from the disk or floppy described by tmpdev and execute it.
X * The floppy parameters may not be right for the floppy we want to read, but
X * reading sector 0 seems to be no problem.
X */
X  int r;
X  char master[SECTOR_SIZE];
X  struct part_entry *table[NR_PARTITIONS], dummy, *active = &dummy;
X  u32_t masterpos;
X
X  device = tmpdev.device;
X  if (!dev_geometry()) return(B_NODEV);
X
X  active->lowsec = 0;
X
X  /* Select a partition table entry. */
X  while (tmpdev.primary >= 0) {
X	masterpos = active->lowsec;
X
X	if ((r = get_master(master, table, masterpos)) != 0) return(r);
X
X	active = table[tmpdev.primary];
X
X	/* How does one check a partition table entry? */
X	if (active->sysind == NO_PART) return(B_NOSIG);
X
X	tmpdev.primary = tmpdev.secondary;
X	tmpdev.secondary = -1;
X  }
X
X  /* Read the boot sector. */
X  if ((r = readsectors(0, BOOTSEG, active->lowsec, 1)) != 0) return(r);
X
X  /* Check signature word. */
X  if (get_word(SIGNATPOS, BOOTSEG) != SIGNATURE) return(B_NOSIG);
X
X  bootstrap(device, (u16_t) active, dseg);
X}
X
Xvoid boot_device(devname)
Xchar *devname;
X{
X  dev_t dev = name2dev(devname);
X  int r;
X
X  if (tmpdev.device < 0) {
X	if (dev != -1) printf("Can't boot from %s\n", devname);
X	return;
X  }
X  switch (r = exec_bootstrap()) {
X      case B_NODEV:
X	printf("%s: device not present\n", devname);
X	break;
X      case B_NOSIG:
X	printf("%s is not bootable\n", devname);
X	break;
X      default:
X	printf("Can't boot %s: %s\n", devname, bios_err(r));
X  }
X  device = bootdev.device;
X  (void) dev_geometry();	/* Restore boot device setting. */
X}
X
Xvoid ls(dir)
Xchar *dir;
X{
X  ino_t ino;
X  struct stat st;
X  char name[NAME_MAX + 1];
X
X  if (!fsok) return;
X
X  if ((ino = r_lookup(ROOT_INO, dir)) == 0
X      || (r_stat(ino, &st), r_readdir(name)) == -1
X	) {
X	printf("ls: %s: %s\n", dir, unix_err(errno));
X	return;
X  }
X  (void) r_readdir(name);	/* Skip ".." too. */
X
X  while ((ino = r_readdir(name)) != 0) printf("%s/%s\n", dir, name);
X}
X
Xchar *Thandler;
Xu32_t Tbase, Tcount;
X
Xvoid unschedule()
X{
X/* Invalidate a waiting command. */
X  if (Thandler != NULL) {
X	free((void *) Thandler);
X	Thandler = NULL;
X  }
X}
X
Xvoid schedule(msec, cmd)
Xlong msec;
Xchar *cmd;
X{
X/* Schedule command at a certain time from now. */
X  unschedule();
X  Thandler = cmd;
X  Tbase = get_tick();
X  Tcount = (msec + MSEC_PER_TICK - 1) / MSEC_PER_TICK;
X}
X
Xint expired()
X{
X/* Check if the timer expired.  If so prepend the scheduled command to
X * the command chain and return 1.
X */
X  int fundef = 0;
X
X  if (Thandler == NULL || (get_tick() - Tbase) < Tcount) return(0);
X  (void) tokenize(tokenize(&cmds, Thandler, &fundef), ";", &fundef);
X  unschedule();
X  return(1);
X}
X
Xint delay(msec)
Xchar *msec;
X{
X/* Delay for a given time.  Returns true iff delay was not interrupted.
X * Make sure get_tick is not called for nonpositive msec, because get_tick
X * may do funny things on the original IBM PC (not the XT!).
X * If msec happens to be the string "swap" then wait till the user hits
X * return after changing diskettes.
X */
X  int swap = 0;
X  u32_t base, count;
X
X  if (strcmp(msec, "swap") == 0) {
X	swap = 1;
X	count = 0;
X	printf("\nInsert the root diskette then hit RETURN\n");
X  } else if ((count = a2l(msec)) > 0) {
X	count /= MSEC_PER_TICK;
X	base = get_tick();
X  }
X  do {
X	switch (peekchar()) {
X	    case 0:
X		break;
X	    case ESC:
X		interrupt();
X		return(0);
X	    case '\n':
X		swap = 0;
X	    default:	(void) getchar();
X	}
X  } while (!expired()
X	 && (swap || (count > 0 && (get_tick() - base) < count))
X	);
X  return(1);
X}
X
Xenum whatfun menufun(e)
Xenvironment *e;
X{
X  if (!(e->flags & E_FUNCTION) || e->arg[0] == 0) return(NOFUN);
X  if (e->arg[1] != ',') return(SELECT);
X  if (e->flags & E_RESERVED) return(DEFFUN);
X  return(USERFUN);
X}
X
Xvoid menu()
X{
X/* By default:  Show a simple menu.
X * Multiple kernels/images:  Show extra selection options.
X * User defined function:  Kill the defaults and show these.
X * Wait for a keypress and execute the given function.
X */
X  int fundef = 0, c, def = 1;
X  char *choice = NULL;
X  environment *e;
X
X  /* Just a default menu? */
X  for (e = env; e != NULL; e = e->next)
X	if (menufun(e) == USERFUN) def = 0;
X
X  printf("\nHit a key as follows:\n\n");
X
X  /* Show the choices. */
X  for (e = env; e != NULL; e = e->next) {
X	switch (menufun(e)) {
X	    case DEFFUN:
X		if (!def) break;
X	    case USERFUN:
X		printf("    %c  %s\n", e->arg[0], e->arg + 2);
X		break;
X	    case SELECT:
X		printf("    %c  Select %s kernel\n", e->arg[0], e->name);
X	}
X  }
X
X  /* Wait for a keypress. */
X  do {
X	while (peekchar() == 0)
X		if (expired()) return;
X
X	if ((c = getchar()) == ESC) {
X		interrupt();
X		return;
X	}
X	for (e = env; e != NULL; e = e->next) {
X		switch (menufun(e)) {
X		    case DEFFUN:
X			if (!def) break;
X		    case USERFUN:
X		    case SELECT:
X			if (c == e->arg[0]) choice = e->value;
X		}
X	}
X  } while (choice == NULL);
X
X  /* Execute the chosen function. */
X  printf("%c\n", c);
X  (void) tokenize(&cmds, choice, &fundef);
X}
X
Xvoid execute()
X{
X/* Get one command from the command chain and execute it.  Legal commands are
X * listed at the end.
X */
X  token *second, *third, *fourth, *fifth, *sep;
X  char *name = cmds->token;
X  size_t n = 0;
X
X  /* There must be a separator lurking somewhere. */
X  for (sep = cmds; sep != NULL && sep->token[0] != ';'; sep = sep->next) n++;
X
X  if ((second = cmds->next) != NULL
X      && (third = second->next) != NULL
X      && (fourth = third->next) != NULL)
X	fifth = fourth->next;
X
X  /* Null command? */
X  if (n == 0) {
X	voidtoken();
X	return;
X  } else
X	/* Name = [device] value? */
X	if ((n == 3 || n == 4) && !sugar(name)
X	    && second->token[0] == '='
X	    && !sugar(third->token)
X	    && (n == 3 || (n == 4 && third->token[0] == 'd'
X			   && !sugar(fourth->token)
X			   ))) {
X	char *value = third->token;
X	int flags = E_VAR;
X
X	if (n == 4) {
X		value = fourth->token;
X		flags |= E_DEV;
X	}
X	if ((flags = b_setvar(flags, name, value)) != 0) {
X		printf("%s is a %s\n", name,
X		       flags & E_RESERVED ? "reserved word" :
X		       "special function");
X		void_cmds();
X	} else
X		while (cmds != sep) voidtoken();
X	return;
X  } else
X	/* Name '(' arg ')' '{' ... '}'? */
X	if (n >= 5
X	    && !sugar(name)
X	    && second->token[0] == '('
X	    && fourth->token[0] == ')'
X	    && fifth->token[0] == '{'
X	) {
X	token *fun = fifth->next;
X	int ok = 1, flags;
X	char *body;
X	size_t len = 1;
X
X	sep = fun;
X	while (sep != NULL && sep->token[0] != '}') {
X		len += strlen(sep->token) + 1;
X		sep = sep->next;
X	}
X	if (sep == NULL || (sep = sep->next) == NULL
X	    || sep->token[0] != ';'
X		)
X		ok = 0;
X
X	if (ok) {
X		body = (char *) malloc(len * sizeof(char));
X		*body = 0;
X
X		while (fun->token[0] != '}') {
X			strcat(body, fun->token);
X			if (!sugar(fun->token) && !sugar(fun->next->token)
X				)
X				strcat(body, " ");
X			fun = fun->next;
X		}
X
X		if ((flags = b_setenv(E_FUNCTION, name,
X				      third->token, body)) != 0) {
X			printf("%s is a %s\n", name,
X			       flags & E_RESERVED ? "reserved word" :
X			       "special variable");
X			void_cmds();
X		} else
X			while (cmds != sep) voidtoken();
X		free((void *) body);
X		return;
X	}
X  } else
X	/* Command coming up, check if ESC typed. */
X  if (peekchar() == ESC) {
X	interrupt();
X	return;
X  } else
X	/* Unset name ..., echo word ...? */
X	if (n >= 1 && (
X		       strcmp(name, "unset") == 0
X		       || strcmp(name, "echo") == 0
X		       )) {
X	int cmd = name[0];
X	char *arg = poptoken();
X
X	for (;;) {
X		free((void *) arg);
X		if (cmds == sep) break;
X		arg = poptoken();
X		if (cmd == 'u')
X			b_unset(arg);
X		else {
X			printf("%s", arg);
X			if (cmds != sep) putchar(' ');
X		}
X	}
X	if (cmd == 'e') putchar('\n');
X	return;
X  } else
X	/* Boot device, ls dir, delay msec? */
X	if (n == 2 && (strcmp(name, "boot") == 0 || strcmp(name, "delay") == 0
X					       || strcmp(name, "ls") == 0 )) {
X	int cmd = name[0];
X	char *arg;
X
X	voidtoken();
X	arg = poptoken();
X	switch (cmd) {
X	    case 'b':	boot_device(arg);	break;
X	    case 'd':	(void) delay(arg);	break;
X	    case 'l':	ls(arg);
X	}
X	free((void *) arg);
X	return;
X  } else
X	/* Trap msec command? */
X  if (n == 3 && strcmp(name, "trap") == 0 && numeric(second->token)) {
X	long msec = a2l(second->token);
X
X	voidtoken();
X	voidtoken();
X	schedule(msec, poptoken());
X	return;
X  } else
X	/* Simple command. */
X  if (n == 1) {
X	char *cmd = poptoken();
X	char *body;
X	int fundef = 0;
X	int ok = 0;
X
X	if (strcmp(cmd, "boot") == 0) {
X		minix();
X		ok = 1;
X	}
X	if (strcmp(cmd, "delay") == 0) {
X		(void) delay("500");
X		ok = 1;
X	}
X	if (strcmp(cmd, "ls") == 0) {
X		ls("");
X		ok = 1;
X	}
X	if (strcmp(cmd, "menu") == 0) {
X		menu();
X		ok = 1;
X	}
X	if (strcmp(cmd, "save") == 0) {
X		save_parameters();
X		ok = 1;
X	}
X	if (strcmp(cmd, "set") == 0) {
X		show_env();
X		ok = 1;
X	}
X
X	/* Command to check bootparams: */
X	if (strcmp(cmd, ":") == 0) ok = 1;
X
X	/* User defined function. */
X	if (!ok && (body = b_body(cmd)) != NULL) {
X		(void) tokenize(&cmds, body, &fundef);
X		ok = 1;
X	}
X	if (!ok) printf("%s: unknown function", cmd);
X	free((void *) cmd);
X	if (ok) return;
X  } else {
X	/* Syntax error. */
X	printf("Can't parse:");
X	while (cmds != sep) {
X		printf(" %s", cmds->token);
X		voidtoken();
X	}
X  }
X  disp_cmds();
X  void_cmds();
X}
X
Xvoid monitor()
X/* Read one or more lines and tokenize them. */
X{
X  char *line;
X  int fundef = 0;
X  token **acmds = &cmds;
X
X  unschedule();			/* Kill a trap. */
X
X  do {
X	putchar(fundef == 0 ? '>' : '+');
X	line = readline();
X	acmds = tokenize(acmds, line, &fundef);
X	free((void *) line);
X  } while (fundef != 0);
X}
X
Xvoid boot()
X{
X/* Load Minix and start it, among other things. */
X  int color;
X
X  /* Print greeting message.  The copyright message is not yet
X   * displayed, because this boot program need not necessarily start
X   * Minix. */
X  color = get_video() & 1;		/* true if color */
X  reset_video(color);			/* clear the screen */
X  printf("\nMINIX boot monitor\n");
X  printf("\nPress ESC to enter the monitor to change boot parameters\n");
X
X  /* Relocate program to end of memory. */
X  migrate();
X
X  /* Initialize tables. */
X  initialize();
X
X  /* Block cache. */
X  init_cache();
X
X  /* Get environment variables from the parameter sector. */
X  get_parameters();
X
X  /* Read and check the superblock. */
X  fsok = r_super() != 0;
X
X  while (1) {
X	/* While there are commands, execute them! */
X	while (cmds != NULL) {
X		execute();
X		(void) expired();
X	}
X
X	/* The "monitor" is just a "read one command" thing. */
X	monitor();
X  }
X}
X
Xvoid disp_cmds()
X{
X  /* Display commands. */
X  printf("\n\n");
X  printf("\nCommand summary:\n");
X  printf("    name = value       - Set environment variable\n");
X  printf("    boot [device]      - Boot Minix [e.g. boot  or  boot hd3]\n");
X  printf("    menu               - Return to previous menu\n");
X  printf("    save               - Save environment on disk\n");
X  printf("    set                - Show environment variables\n");
X  printf("    help               - Redisplay this command summary\n");
X
X/* Only show all the rest of this stuff if there is genuine interest. */
X#ifdef EXTENDED_LIST
X  printf("    name(arg) { ... }  - Define function\n");
X  printf("    name               - Call function\n");
X  printf("    ls [directory]     - List contents of directory\n");
X  printf("    trap msec command  - Schedule command\n");
X  printf("    unset name ...     - Unset environment variables\n");
X  printf("    delay [msec]       - Delay (500 msec default)\n");
X  printf("    echo word ...      - Print these words\n");
X#endif
X
X#ifndef EXTENDED_LIST
X/* Advanced users know this stuff, and besides, there is no room for it. */
X  printf("\n\nValid environmental variables that can be set. Defaults in ().\n");
X  printf("    rootdev [e.g. hd3] - Root device or (ram)\n");
X  printf("    ramimagedev        - Where to get RAM image (boot device)\n");
X  printf("    ramsize            - Size of RAM disk when not used for root (0)\n");
X  printf("    keyboard           - Keyboard type (olivetti, us, dutch) (standard)\n");
X  printf("    processor          - CPU: 86, 186, 286, 386, 486 (automatic)\n");
X  printf("    memsize            - Kilobytes of conventional memory (automatic)\n");
X  printf("    emsize             - Kilobytes of extended memory (automatic)\n");
X  printf("    chrome             - Monitor type: mono or color (automatic)\n");
X  printf("    video              - Display: mda, cga, ega, vga (automatic)\n");
X  printf("    image              - FD: start:length;  HD: image file (minix)\n");
X  printf("\n");
X#endif
X}
/
echo x - mon2.c
sed '/^X/s///' > mon2.c << '/'
X/* mon2.c - Load an image and start it.		Author: Kees J. Bot */
X
X#include "sys/types.h"
X#include "sys/stat.h"
X#include "stdlib.h"
X#include "stddef.h"
X#include "limits.h"
X#include "string.h"
X#include "errno.h"
X#include "minix/config.h"
X#include "minix/const.h"
X#include "minix/partition.h"
X#include "minix/boot.h"
X#include "minix/syslib.h"
X#include "minix/type.h"
X#include "kernel/const.h"
X#include "kernel/type.h"
X#include "a.out.h"
X
X#define _MONHEAD
X#include "tools.h"
X
X#ifndef _ANSI
X#undef NULL
X#define NULL 0
X#endif
X
X#define between(a, c, z)	((unsigned) ((c) - (a)) <= ((z) - (a)))
X#define	printf	printk
X#define click_shift	clck_shft	/* 7 char clash with click_size. */
X
X/* 386 Kernels may require something extra: */
X#define K_I386	0x0001	      /* Make the 386 transition before you call me */
X#define K_CLAIM	0x0002	      /* I will acquire my own bss pages, thank you */
X#define K_CHMEM 0x0004	      /* This kernel listens to chmem for stack size */
X#define K_HDR	0x0010	      /* No need to patch sizes, kernel uses the hdrs*/
X
X/* To force all unread sectors in: */
X#define get_force()	get_sector((int) 0, (u32_t) 0)
X#define align(i, n)	(((i) + ((n) - 1)) & ~((n) - 1))
X
Xchar *minix_version = "1.6.25";
Xchar *copyright = "Copyright 1993 Prentice-Hall, Inc.";
X
Xu16_t click_shift;
Xu16_t click_size;		/* click_size = Smallest kernel memory object*/
Xu16_t segclick;			/* Size of a click in paragraphs. */
Xu16_t segalign;			/* For align of a seg to max(sector, click) */
Xu16_t click2click;		/* To go from hardwr clicks to clicks and vv*/
Xu16_t k_flags;			/* Not all kernels are created equal. */
Xoff_t image_off, image_size;
Xu16_t eqscancode = STANDARD_SCAN;	/* IBM scancode by default. */
X
X/* Data about the different processes. */
X#define PROCESS_MAX	16	/* Must match the space in kernel/startx.x */
X#define KERNEL		0	/* The first process is the kernel. */
X#define FS		2	/* The third must be fs. */
X
Xstruct process {
X  char name[IM_NAME_MAX + 1];	/* Nice to have a name for the thing. */
X  u16_t cs;			/* Code segment. */
X  u16_t ds;			/* Data segment. */
X  u16_t bss;			/* Missing bss clicks. */
X  u16_t stack;			/* Missing stack clicks. */
X  u16_t dseg;			/* To access data segment. */
X  u16_t doff;			/* dseg:doff = start of data. */
X} process[PROCESS_MAX + 1];
X
X/* Magic numbers in process' data space. */
X#define MAGIC_OFF	0	/* Offset of magic # in data seg. */
X#define CLICK_OFF	2	/* Offset in kernel text to click_shift. */
X#define FLAGS_OFF	4	/* Offset in kernel text to flags. */
X#define KERNEL_D_MAGIC	0x526F	/* In kernel data. */
X#define OTHER_D_MAGIC	0xDADA	/* The other processes. */
X
X/* Offsets of sizes to be patched into kernel and fs. */
X#define P_SIZ_OFF	0	/* Process' sizes into kernel data. */
X#define P_KDS_OFF	4	/* Kernel ds into kernel text. */
X#define P_INIT_OFF	4	/* Init cs & sizes into fs data. */
X
X_PROTOTYPE(void pretty_image, (char *image ));
X_PROTOTYPE(int get_sector, (int seg, u32_t sec ));
X_PROTOTYPE(int check_magic, (void));
X_PROTOTYPE(void patch_sizes, (void));
X_PROTOTYPE(int read_header, (struct image_header *hdr, u32_t sec ));
X_PROTOTYPE(int selected, (char *name ));
X_PROTOTYPE(u32_t proc_size, (struct image_header *hdrp ));
X_PROTOTYPE(u32_t file_vir2sec, (u32_t vsec ));
X_PROTOTYPE(u32_t flat_vir2sec, (u32_t vsec ));
X_PROTOTYPE(int get_click, (u32_t sec, struct exec *hdrp ));
X_PROTOTYPE(int read_code_segment, (u32_t *vsec, long *size, u16_t *seg ));
X_PROTOTYPE(void exec_image, (char *image, char *params, size_t paramsize ));
X_PROTOTYPE(char *params2params, (size_t *size ));
X_PROTOTYPE(ino_t latest_version, (char *version, struct stat *stp ));
X_PROTOTYPE(char *select_image, (char *image ));
X
X_PROTOTYPE(u32_t (*vir2sec), (u32_t vsec )); /* Where is a sector on disk? */
X
Xvoid pretty_image(image)
Xchar *image;
X/* Pretty print the name of the image to load.  Translate '/' and '_' to
X * space, first letter goes uppercase.  An 'r' before a digit prints as
X * 'revision'.  E.g. 'minix/1.6.16r10' -> 'Minix 1.6.16 revision 10'.
X * The idea is that the part before the 'r' is the official Minix release
X * and after the 'r' you can put version numbers for your own changes.
X * The default version number 'minix_version' is printed if no digit was
X * seen at all.  Not everyone is a kernel hacker.
X */
X{
X  int up = 0, digit = 0, c;
X
X  while ((c = *image++) != 0) {
X	if (c == '/' || c == '_') {
X		putchar(' ');
X		continue;
X	}
X	if (c == 'r' && between('0', *image, '9')) {
X		printf(" revision ");
X		continue;
X	}
X	if (!up && between('a', c, 'z')) c = c - 'a' + 'A';
X
X	if (between('A', c, 'Z')) up = 1;
X
X	putchar(c);
X	if (between('0', c, '9')) digit = 1;
X  }
X  if (!digit) printf(" %s", minix_version);
X}
X
Xint get_sector(s, sec)
Xint s;
Xu32_t sec;
X/* Read sector "sec" at segment "seg" (offset 0).  Seg must be at a 512-byte
X * boundary in memory!  It is awfully smart at reading many sectors at once.
X * It even knows that address == 0 means a hole in a file.
X * Use get_sector(0, 0) to read the last few sectors.  This is the first
X * function to feel the "can't read past a 64K boundary" limitation of the
X * DMA chip.  Return value is 1 for success, 0 if out of memory.
X */
X{
X  static u32_t address;		/* Sector to read. */
X  static u16_t count;		/* How many to read. */
X  static u16_t segment;		/* Where to put them. */
X  static u16_t dma64k;		/* Can't read past this segment. */
X  u16_t highseg = segment + count * HRATIO;
X  int r;
X  u16_t seg;
X
X  seg = (u16_t) s;
X  if (highseg == seg && address + count == sec && seg < dma64k)
X	count++;
X  else {
X	if (segment != 0) {
X		if (highseg > cseg) {
X			/* Sector can't be read where this code sits. */
X			errno = ENOMEM;
X			return(0);
X		}
X		if (address == 0)	/* A hole, count must be 1. */
X			raw_clear(0, segment, SECTOR_SIZE);
X		else if ((r = readsectors(0, segment, address, count)) != 0) {
X			readerr(address, r);
X			errno = 0;
X			return(0);
X		}
X	}
X	address = sec;
X	count = 1;
X	segment = seg;
X	dma64k = (seg + 0x0FFF) & 0xF000;
X  }
X  return(1);
X}
X
X
Xint check_magic()
X/* Check the magic numbers and clickshifts of the different processes. */
X{
X  struct process *procp;
X  int ok = 1;
X
X  for (procp = process; procp->ds != 0; procp++) {
X	if ((procp == process + KERNEL ? KERNEL_D_MAGIC : OTHER_D_MAGIC)
X			   != get_word(procp->doff + MAGIC_OFF, procp->dseg)) {
X		printf("%s magic number is incorrect\n", procp->name);
X		ok = 0;
X	}
X	if (get_word(procp->doff + CLICK_OFF, procp->dseg) != click_shift) {
X		printf("%s click size doesn't agree with %s, ",
X		       procp->name, process[KERNEL].name);
X		printf("it may need to be recompiled\n");
X		ok = 0;
X	}
X  }
X  errno = 0;
X  return(ok);
X}
X
Xvoid patch_sizes()
X/* Patch sizes of each process into kernel data space, kernel ds into kernel
X * text space, and sizes of init into data space of fs.  All the patched
X * numbers are based on the kernel click_shift, not hardware segments.
X */
X{
X  u16_t text_size, data_size;
X  struct process *procp, *initp;
X  u16_t doff;
X
X  /* Patch kernel ds into the code segment. */
X  put_word(P_KDS_OFF, process[KERNEL].cs, process[KERNEL].ds);
X
X  /* Patch text and data sizes of the processes into kernel data space.
X   * Kernels that want to claim their own bss pages and/or stack will
X   * get that information too. 
X   */
X  doff = process[KERNEL].doff + P_SIZ_OFF;
X
X  for (procp = process; procp->ds != 0; procp++) {
X	text_size = (procp->ds - procp->cs) >> click2click;
X	data_size = ((procp + 1)->cs - procp->ds) >> click2click;
X
X	/* Standard two words, text and data size: */
X	put_word(doff, process[KERNEL].dseg, text_size);
X	doff += 2;
X	put_word(doff, process[KERNEL].dseg, data_size);
X	doff += 2;
X
X	if (k_flags & K_CLAIM) {
X		/* Size of missing bss: */
X		put_word(doff, process[KERNEL].dseg, procp->bss);
X		doff += 2;
X	}
X	if ((k_flags & (K_CLAIM | K_CHMEM)) == (K_CLAIM | K_CHMEM)) {
X		/* Size of missing stack: */
X		put_word(doff, process[KERNEL].dseg, procp->stack);
X		doff += 2;
X	}
X	initp = procp;		/* The last process must be init. */
X  }
X
X  if (k_flags & K_CLAIM) return;	/* This kernel tells fs about init. */
X
X  /* Patch cs and sizes of init into fs data. */
X  put_word(process[FS].doff + P_INIT_OFF + 0, process[FS].dseg,
X	 initp->cs >> click2click);
X  put_word(process[FS].doff + P_INIT_OFF + 2, process[FS].dseg, text_size);
X  put_word(process[FS].doff + P_INIT_OFF + 4, process[FS].dseg, data_size);
X}
X
Xint read_header(hdr, sec)
Xstruct image_header *hdr;
Xu32_t sec;
X/* Read and check the a.out header at sector sec. */
X{
X  int r;
X  char buf[SECTOR_SIZE];
X
X  if (sec == 0) return(0);
X
X  if ((r = readsectors((u16_t) buf, dseg, sec, 1)) != 0) {
X	readerr(sec, r);
X	errno = 0;
X	return(0);
X  }
X  memcpy((void *) hdr, (void *) buf, sizeof(*hdr));
X
X  if (BADMAG(hdr->process)) {
X	errno = ENOEXEC;
X	return(0);
X  }
X  return(1);
X}
X
Xint selected(name)
Xchar *name;
X/* True iff name has no label or the proper label. */
X{
X  char *colon, *label;
X  int cmp;
X
X  if ((colon = strchr(name, ':')) == NULL) return(1);
X  if ((label = b_value("label")) == NULL) return(1);
X  *colon = 0;
X  cmp = strcmp(label, name);
X  *colon = ':';
X  return(cmp == 0);
X}
X
Xu32_t proc_size(hdrp)
Xstruct image_header *hdrp;
X/* Return the size of a process in sectors as found in an image. */
X{
X  u32_t len = hdrp->process.a_text;
X
X  if (hdrp->process.a_flags & A_SEP) len = align(len, SECTOR_SIZE);
X  len = align(len + hdrp->process.a_data, SECTOR_SIZE);
X  len = align(len + hdrp->process.a_syms, SECTOR_SIZE);
X
X  return(len >> SECTOR_SHIFT);
X}
X
Xu32_t file_vir2sec(vsec)
Xu32_t vsec;
X/* Translate a virtual sector number to an absolute disk sector. */
X{
X  off_t blk;
X
X  if ((blk = r_vir2abs((off_t) (vsec / RATIO))) == 0) {
X	errno = EIO;
X	return(0);
X  }
X  return(lowsec + blk * RATIO + vsec % RATIO);
X}
X
Xu32_t flat_vir2sec(vsec)
Xu32_t vsec;
X/* Simply add an absolute sector offset to vsec. */
X{
X  if (vsec >= image_size) {
X	errno = EIO;
X	return(0);
X  }
X  return(lowsec + image_off + vsec);
X}
X
Xint get_click(sec, hdrp)
Xu32_t sec;
Xstruct exec *hdrp;
X/* Get the click shift and special flags from the start of kernel text. */
X{
X  int r;
X  char buf[SECTOR_SIZE];
X  char *textp = buf;
X
X  if (sec == 0) return(0);
X
X  if ((r = readsectors((u16_t) buf, dseg, sec, 1)) != 0) {
X	readerr(sec, r);
X	errno = 0;
X	return(0);
X  }
X
X  click_shift = *(u16_t *) (textp + CLICK_OFF);
X  if (click_shift < HCLICK_SHIFT || click_shift > 16)
X	click_shift = HCLICK_SHIFT;
X  click_size = 1 << click_shift;
X  click2click = click_shift - HCLICK_SHIFT;
X  segclick = 1 << click2click;
X  segalign = click_size > SECTOR_SIZE ? segclick : HRATIO;
X
X  k_flags = *(u16_t *) (textp + FLAGS_OFF);
X
X  return(1);
X}
X
Xint read_code_segment(vsec, size, seg)
Xu32_t *vsec;			/* Virtual sector number to read. */
Xlong *size;			/* Bytes to read. */
Xu16_t *seg;			/* Segment to put it in. */
X{
X  while (*size > 0) {
X	if (!get_sector(*seg, (*vir2sec) (*vsec))) return(0);
X	*vsec += 1;
X	*seg += HRATIO;
X	*size -= SECTOR_SIZE;
X  }
X  return(1);
X}
X
Xvoid exec_image(image, params, paramsize)
Xchar *image, *params;
Xsize_t paramsize;
X/* Get a named Minix image into core, patch it up and execute. */
X{
X  struct image_header hdr;
X  u32_t vsec = 0;		/* Load this sector from image next. */
X  u16_t seg = MINIXSEG;		/* Put it at this segment. */
X  u16_t n, p_cs, p_ds;
X  struct process *procp;	/* Process under construction. */
X  int procn;			/* Counts them. */
X  long a_text, a_data, a_bss, a_stack, a_syms;
X  char *msec;
X  int banner = 0;
X  long processor = a2l(b_value("processor"));
X
X  /* Clear the area where the headers will be placed. */
X  raw_clear(0, HEADERSEG, PROCESS_MAX * A_MINHDR);
X
X  /* Read the many different processes: */
X  for (procn = 0, procp = process; vsec < image_size; procn++, procp++) {
X	if (procn == PROCESS_MAX) {
X		printf("There are more then %d programs in %s\n",
X		       PROCESS_MAX, image);
X		errno = 0;
X		return;
X	}
X
X	/* Read header. */
X	for (;;) {
X		if (!read_header(&hdr, (*vir2sec) (vsec++))) return;
X
X		/* Check the optional label on the process. */
X		if (selected(hdr.name)) break;
X
X		/* Bad label, skip this process. */
X		vsec += proc_size(&hdr);
X	}
X
X	/* Place a copy of the header where the kernel can get it. */
X	raw_copy(procn * A_MINHDR, HEADERSEG,
X		 (u16_t) & hdr.process, dseg, A_MINHDR);
X
X	/* Sanity check: an 8086 can't run a 386 kernel. */
X	if (hdr.process.a_cpu == A_I80386 && processor < 386) {
X		printf("You can't run a 386 kernel on this 80%ld\n",
X		       processor);
X		errno = 0;
X		return;
X	}
X
X	/* Get the click shift from the first kernel sector. */
X	if (procn == KERNEL) {
X		if (!get_click((*vir2sec) (vsec), &hdr.process)) return;
X	}
X	if (!banner) {
X		printf("   cs     ds    text    data     bss");
X		if (k_flags & K_CHMEM) printf("   stack");
X		putchar('\n');
X		banner = 1;
X	}
X
X	/* Segment sizes. */
X	a_text = hdr.process.a_text;
X	a_data = hdr.process.a_data;
X	a_bss = hdr.process.a_bss;
X	if (k_flags & K_CHMEM) {
X		a_stack = hdr.process.a_total - a_data - a_bss;
X		if (!(hdr.process.a_flags & A_SEP)) a_stack -= a_text;
X	} else
X		a_stack = 0;
X	a_syms = hdr.process.a_syms;
X
X	/* Collect info about the process to be. */
X	strcpy(procp->name, hdr.name);
X	procp->cs = seg = align(seg, segalign);
X
X	/* Read the text segment. */
X	if (!read_code_segment(&vsec, &a_text, &seg)) return;
X
X	/* Note that some of the data of a common I&D program has
X	 * been read at this point to fill a sector.  This can be
X	 * corrected by adding the now negative a_text to a_data.
X	 */
X
X	if (hdr.process.a_flags & A_SEP) {
X		/* Align the data segment to a click. */
X		seg = align(seg, segclick);
X		procp->ds = seg;
X		procp->dseg = seg;
X		procp->doff = 0;
X	} else {
X		/* Compute precise start of data and correct a_data. */
X		procp->ds = procp->cs;
X		procp->dseg = seg - HRATIO;
X		procp->doff = SECTOR_SIZE + a_text;
X		a_data += a_text;
X	}
X
X	printf("%04x0  %04x0%8ld%8ld%8ld", procp->cs, procp->ds,
X	            hdr.process.a_text, hdr.process.a_data, hdr.process.a_bss);
X	if (k_flags & K_CHMEM) printf("%8ld", a_stack);
X	printf("  %s\n", hdr.name);
X
X	/* Read the data segment. */
X	if (!read_code_segment(&vsec, &a_data, &seg)) return;
X
X	/* Force the last sectors into memory. */
X	if (!get_force()) return;
X
X	/* Zero extend data to a click. */
X	n = align(seg, segclick) - seg;
X	if (seg + n > cseg) {
X		errno = ENOMEM;
X		return;
X	}
X	raw_clear(0, seg, n << HCLICK_SHIFT);
X	a_data -= n << HCLICK_SHIFT;
X	seg += n;
X
X	/* Compute the number of bss clicks left. */
X	a_bss += a_data;
X	procp->bss = (a_bss + click_size - 1) >> click_shift;
X	a_bss -= (long) procp->bss << click_shift;
X
X	/* And the number of stack clicks. */
X	a_stack += a_bss;
X	procp->stack = (a_stack + click_size - 1) >> click_shift;
X
X	/* Make space for bss and stack unless... */
X	if (procn == KERNEL || !(k_flags & K_CLAIM)) {
X		/* Zero out bss a click at a time. */
X		while (procp->bss > 0) {
X			if (seg + segclick > cseg) {
X				errno = ENOMEM;
X				return;
X			}
X			raw_clear(0, seg, click_size);
X			seg += segclick;
X			procp->bss--;
X		}
X
X		/* Add space for the stack. */
X		seg += procp->stack << click2click;
X		procp->stack = 0;
X	}
X
X	/* Skip symbol table. */
X	vsec += (a_syms + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
X  }
X  if (procn == 0) {
X	printf("There are no programs in %s\n", image);
X	errno = 0;
X	return;
X  }
X  procp->cs = seg;		/* Record first free segment. */
X  procp->ds = 0;		/* Mark end. */
X
X  /* Check magic numbers and clickshifts in each process. */
X  if (!check_magic()) return;
X
X  /* Patch sizes, etc. into kernel data unless it uses the headers. */
X  if (!(k_flags & K_HDR)) patch_sizes();
X
X  /* Wait a while if delay is set, bail out if ESC typed. */
X  msec = b_value("delay");
X  if (!delay(msec != NULL ? msec : "0")) {
X	errno = 0;
X	return;
X  }
X
X  /* Minix. */
X  p_cs = process[KERNEL].cs;
X  p_ds = process[KERNEL].ds;
X
X  if (k_flags & K_I386)
X	minix386(p_cs, p_ds, params, paramsize);
X  else
X	minix86(p_cs, p_ds, params, paramsize);
X}
X
Xchar *params2params(size)
Xsize_t *size;
X/* Package the environment settings for the kernel. */
X{
X  char *parms;
X  size_t i, z;
X  environment *e;
X  int sc;
X
X  parms = (char *) malloc((z = 64) * sizeof(char *));
X  i = 0;
X
X  for (e = env; e != NULL; e = e->next) {
X	char *name = e->name, *value = e->value;
X	size_t n;
X	dev_t dev;
X
X	if (!(e->flags & E_VAR)) continue;
X
X	if (strcmp(name, "keyboard") == 0) {
X		name = "scancode";
X
X		if (!numeric(value)) {
X			sc = eqscancode;	/* default */
X			if (strcmp(value, "olivetti") == 0) sc = OLIVETTI_SCAN;
X			if (strcmp(value, "dutch") == 0) sc = DUTCH_EXT_SCAN;
X			if (strcmp(value, "us") == 0) sc = US_EXT_SCAN;
X			value = u2a(sc);
X		}
X	} else if (e->flags & E_DEV) {
X		if ((dev = name2dev(value)) == -1) {
X			free(parms);
X			errno = 0;
X			return(NULL);
X		}
X		value = u2a((u16_t) dev);
X	}
X	n = i + strlen(name) + 1 + strlen(value) + 1;
X	if (n > z) {
X		parms = (char *) realloc((void *) parms,
X					 (z += n) * sizeof(char));
X	}
X	strcpy(parms + i, name);
X	strcat(parms + i, "=");
X	strcat(parms + i, value);
X	i = n;
X  }
X  parms[i++] = 0;		/* end marked with empty string. */
X  *size = i;
X  return(parms);
X}
X
Xino_t latest_version(version, stp)
Xchar *version;
Xstruct stat *stp;
X/* Recursively read the current directory, selecting the newest image on
X * the way up.  (One can't use r_stat while reading a directory.)
X */
X{
X  char name[NAME_MAX + 1];
X  ino_t ino, newest;
X  time_t mtime;
X
X  if ((ino = r_readdir(name)) == 0) {
X	stp->st_mtime = 0;
X	return(0);
X  }
X  newest = latest_version(version, stp);
X  mtime = stp->st_mtime;
X  r_stat(ino, stp);
X
X  if (S_ISREG(stp->st_mode) && stp->st_mtime > mtime) {
X	newest = ino;
X	strcpy(version, name);
X  } else
X	stp->st_mtime = mtime;
X  return(newest);
X}
X
Xchar *select_image(image)
Xchar *image;
X/* Look image up on the filesystem, if it is a file then we're done, but
X * if its a directory then we want the newest file in that directory.  If
X * it doesn't exist at all, then see if it is 'number:number' and get the
X * image from that absolute offset off the disk.
X */
X{
X  ino_t image_ino;
X  struct stat st;
X
X  image = strcpy((char *) malloc((strlen(image) + 1 + NAME_MAX + 1)
X			       * sizeof(char)), image);
X
X  if (!fsok || (image_ino = r_lookup(ROOT_INO, image)) == 0) {
X	char *size;
X
X	if (numprefix(image, &size) && *size++ == ':'
X	    && numeric(size)) {
X		vir2sec = flat_vir2sec;
X		image_off = a2l(image);
X		image_size = a2l(size);
X		strcpy(image, "Minix");
X		return(image);
X	}
X	if (!fsok)
X		printf("No image selected\n");
X	else
X		printf("Can't load %s: %s\n", image, unix_err(errno));
X	goto bail_out;
X  }
X  r_stat(image_ino, &st);
X  if (!S_ISREG(st.st_mode)) {
X	char *version = image + strlen(image);
X	char dots[NAME_MAX + 1];
X
X	if (!S_ISDIR(st.st_mode)) {
X		printf("%s: %s\n", *image, unix_err(ENOTDIR));
X		goto bail_out;
X	}
X	(void) r_readdir(dots);
X	(void) r_readdir(dots);	/* "." & ".." */
X	*version++ = '/';
X	*version = 0;
X	if ((image_ino = latest_version(version, &st)) == 0) {
X		printf("There are no images in %s\n", image);
X		goto bail_out;
X	}
X	r_stat(image_ino, &st);
X  }
X  vir2sec = file_vir2sec;
X  image_size = (st.st_size + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
X  return(image);
X
Xbail_out:
X  free(image);
X  return(NULL);
X}
X
Xvoid minix()
X/* Load Minix and run it.  (Given the size of this program it is surprising
X * that it ever gets to that.)
X */
X{
X  char *image;
X  char *minixparams;
X  size_t paramsize;
X  char *chrome;
X  int color;
X
X  /* Translate the bootparameters to what Minix likes best. */
X  if ((minixparams = params2params(&paramsize)) == NULL) return;
X
X  if ((image = select_image(b_value("image"))) == NULL) return;
X
X  /* Things are getting serious, kill the cache! */
X  invalidate_cache();
X
X  /* Reset and clear the screen setting the proper video mode.  This is more
X   * important than it seems, Minix depends on the mode set right.
X   */
X  chrome = b_value("chrome");		/* display type: "mono" or "color" */
X  color = strcmp(chrome, "color") == 0;	/* true if color */
X  reset_video(color);			/* reset to the proper video mode */
X
X  /* Display copyright message and load the image. */
X  printf("Loading ");
X  pretty_image(image);
X  printf(".  %s\n\n", copyright);
X
X  exec_image(image, minixparams, paramsize);
X  /* Not supposed to return, if it does however, errno tells why. */
X
X  switch (errno) {
X     case ENOEXEC:
X	printf("%s contains a bad program header\n", image);
X	break;
X
X     case ENOMEM:
X	printf("%s does not fit in %d KB memory\n",
X	       image, (cseg - MINIXSEG) / (1024 / HCLICK_SIZE));
X	break;
X
X     case EIO:
X	printf("Unsuspected EOF on %s\n", image);
X
X     case 0:		/* Error already reported. */ ;
X}
X
X  /* Put all that free memory to use again. */
X  init_cache();
X  free((void *) minixparams);
X  free((void *) image);
X}
/
echo x - monhead.s.ansi
sed '/^X/s///' > monhead.s.ansi << '/'
X!	Monhead.s - BIOS support for boot.c		Author: Kees J. Bot
X!
X!
X! This file contains the startup and low level support for the secondary
X! boot program.  It contains functions for disk, tty and keyboard I/O,
X! copying memory to arbitrary locations, etc.
X!
X! The primary bootstrap code supplies the following parameters in registers:
X!	dl	= Boot-device.
X!	es:si	= Partition table entry if hard disk.
X!
X
X.define begtext, begdata, begbss
X.data
Xbegdata:
X	.asciz	"(null)"
X.bss
Xbegbss:
X
X	BOOTOFF	   =	0x7C00	! 0x0000:BOOTOFF load a bootstrap here
X	LOADSEG    =	0x1000	! Where this code is loaded.
X	BUFFER	   =	0x0600	! First free memory
X	PENTRYSIZE =	    16	! Partition table entry size.
X	DSKBASE    =	   120	! 120 = 4 * 0x1E = ptr to disk parameters
X	DSKPARSIZE =	    11	! 11 bytes of floppy parameters
X	SECTORS	   =	     4	! Offset into parameters to sectors per track
X	a_flags	   =	     2	! From a.out.h, struct exec
X	a_text	   =	     8
X	a_data	   =	    12
X	a_bss	   =	    16
X	a_total	   =	    24
X	A_SEP	   =	  0x20	! Separate I&D flag.
X
X! Imported variables and functions:
X.extern _cseg, _dseg, _runsize, _edata, _end	! Runtime environment
X.extern _device, _dskpars, _heads, _sectors	! Boot disk parameters
X.extern _eqscancode				! Ugly scancode trick
X.extern _rem_part				! To pass partition info
X
X.text
Xbegtext:
X.extern _boot, _printk				! Boot Minix, kernel printf
X
X! Set segment registers and stack pointer using the programs own header!
X! The header is either 32 bytes (short form) or 48 bytes (long form).  The
X! bootblock will jump to address 0x10030 in both cases, calling one of the
X! two jmpf instructions below.
X
X	jmpf	boot, LOADSEG+3	! Set cs right (skipping long a.out header)
X	.space	11		! jmpf + 11 = 16 bytes
X	jmpf	boot, LOADSEG+2	! Set cs right (skipping short a.out header)
Xboot:
X	mov	ax, #LOADSEG
X	mov	ds, ax		! ds = header
X
X	movb	al, a_flags
X	testb	al, #A_SEP	! Separate I&D?
X	jnz	sepID
XcomID:	xor	ax, ax
X	xchg	ax, a_text	! No text
X	add	a_data, ax	! Treat all text as data
XsepID:
X	mov	ax, a_total	! Total nontext memory usage
X	and	ax, #0xFFFE	! Round down to even
X	mov	a_total, ax	! total - text = data + bss + heap + stack
X	cli			! Ignore interrupts while stack in limbo
X	mov	sp, ax		! Set sp at the top of all that
X
X	mov	ax, a_text	! Determine offset of ds above cs
X	movb	cl, #4
X	shr	ax, cl
X	mov	cx, cs
X	add	ax, cx
X	mov	ds, ax		! ds = cs + text / 16
X	mov	ss, ax
X	sti			! Stack ok now
X	push	es		! Save es, we need it for the partition table
X	mov	es, ax
X	cld			! C compiler wants UP
X
X! Clear bss
X	xor	ax, ax		! Zero
X	mov	di, #_edata	! Start of bss is at end of data
X	mov	cx, #_end	! End of bss (begin of heap)
X	sub	cx, di		! Number of bss bytes
X	shr	cx, #1		! Number of words
X	rep
X	stos			! Clear bss
X
X! Copy primary boot parameters to variables.  (Can do this now that bss is
X! cleared and may be written into).
X	xorb	dh, dh
X	mov	_device, dx	! Boot device (probably 0x00 or 0x80)
X	mov	_rem_part+0, si	! Remote partition table offset
X	pop	_rem_part+2	! and segment (saved es)
X
X! Give C code access to the code segment, data segment and the size of this
X! process.
X	mov	_cseg, cs
X	mov	_dseg, ds
X	push	ds
X	mov	ax, #LOADSEG
X	mov	ds, ax		! Back to the header once more
X	mov	ax, a_total+0
X	mov	dx, a_total+2	! dx:ax = data + bss + heap + stack
X	add	ax, a_text+0
X	adc	dx, a_text+2	! dx:ax = text + data + bss + heap + stack
X	pop	ds
X	mov	_runsize+0, ax
X	mov	_runsize+2, dx	! 32 bit size of this process
X
X! Time to switch to a higher level language (not much higher)
X	call	_boot
X
X.define _exit, __exit, _main	! Make various compilers happy
X.define _reboot			! Normal reboot entry point
X_exit:
X__exit:
X_main:
X_reboot:
X	mov	ax, #any_key
X	push	ax
X	call	_printk
X	call	_getchar
X	int	0x19
X.data
Xany_key:
X	.asciz	"\nHit any key to reboot\n"
X.text
X
X! Alas we need some low level support
X!
X! void raw_copy(dstoff, dstseg, srcoff, srcseg, count)
X!	Copy count bytes from srcseg:srcoff to dstseg:dstoff
X!	Don't do overlaps.  Count is 16 bits.
X.define _raw_copy
X_raw_copy:
X	mov	bx, sp
X	push	si
X	push	di		! Save C variable registers
X	mov	cx, 10(bx)	! Count (get it before ds is trashed)
X	les	di, 2(bx)	! es:di = dstseg:dstoff
X	lds	si, 6(bx)	! ds:si = srcseg:srcoff
X	shr	cx, #1		! Words to move
X	jb	bytecopy	! No, no, bytes!
X	rep
X	movw			! Do the word copy
X	jmp	copydone
Xbytecopy:
X	rcl	cx, #1		! Get that lost bit back
X	rep
X	movsb			! Do the byte copy (use movb in asld version)
Xcopydone:
X	mov	ax, ss		! Restore ds and es from the remaining ss
X	mov	ds, ax
X	mov	es, ax
X	pop	di
X	pop	si		! Restore C variable registers
X	ret
X
X! void raw_clear(off, seg, count)
X!	Set count bytes at seg:off to zero.
X.define _raw_clear
X_raw_clear:
X	mov	bx, sp
X	push	di
X	les	di, 2(bx)	! es:di = seg:off
X	xor	ax, ax		! Zero
X	mov	cx, 6(bx)	! Count
X	shr	cx, #1		! Words to clear
X	jb	byteclear	! No, no, bytes!
X	rep
X	stos			! Clear
X	jmp	cleardone
Xbyteclear:
X	rcl	cx, #1		! Get that lost bit back
X	rep
X	stosb			! Byte clear
Xcleardone:
X	mov	ax, ds		! Restore es from ds
X	mov	es, ax
X	pop	di
X	ret
X
X! u16_t get_word(off, seg);
X! void put_word(off, seg, word);
X!	Read or write a 16 bits word at an arbitrary location.
X.define _get_word, _put_word
X_get_word:
X	mov	bx, sp
X	push	ds		! Save ds
X	lds	bx, 2(bx)	! ds:bx = seg:off
X	mov	ax, (bx)	! Get the word in ax
X	pop	ds		! Restore ds
X	ret
X_put_word:
X	mov	bx, sp
X	push	ds
X	mov	ax, 6(bx)	! Word to store at seg:off
X	lds	bx, 2(bx)
X	mov	(bx), ax	! Store the word
X	pop	ds
X	ret
X
X! void relocate(void);
X!	After the program has copied itself to a safer place, it needs to change
X!	the segment registers.  Cseg has already been set to the new location.
X.define _relocate
X_relocate:
X	pop	bx		! Return address
X	mov	ax, cs		! Old code segment
X	sub	ax, _cseg	! ax = -(new - old) = -Moving offset
X	mov	dx, ds
X	sub	dx, ax
X	mov	ds, dx		! ds += (new - old)
X	mov	es, dx
X	mov	ss, dx
X	mov	_dseg, dx
X	push	_cseg		! New text segment
X	push	bx		! Return offset of this function
X	retf			! Relocate
X
X! void *brk(void *addr)
X! void *sbrk(size_t incr)
X!	Cannot fail implementations of brk(2) and sbrk(3), so we can use
X!	malloc(3).  They reboot on stack collision instead of returning -1.
X.data
X	.align	2
Xbreak:	.data2	_end		! A fake heap pointer
X.text
X.define _brk, __brk, _sbrk, __sbrk
X_brk:
X__brk:				! __brk is for the standard C compiler
X	xor	ax, ax
X	jmp	sbrk		! break= 0; return sbrk(addr);
X_sbrk:
X__sbrk:
X	mov	ax, break	! ax= current break
Xsbrk:	push	ax		! save it as future return value
X	mov	bx, sp		! Stack is now: (retval, retaddr, incr, ...)
X	add	ax, 4(bx)	! ax= break + increment
X	mov	break, ax	! Set new break
X	lea	dx, -1024(bx)	! sp minus a bit of breathing space
X	cmp	dx, ax		! Compare with the new break
X	jb	heaperr		! Suffocating noises
X	lea	dx, -4096(bx)	! A warning when heap+stack goes < 4K
X	cmp	dx, ax
X	jae	plenty		! No reason to complain
X	mov	ax, #memwarn
X	push	ax
X	call	_printk		! Warn about memory running low
X	pop	ax
X	movb	memwarn, #0	! No more warnings
Xplenty:	pop	ax		! Return old break (0 for brk)
X	ret
Xheaperr:mov	ax, #chmem
X	push	ax
X	mov	ax, #nomem
X	push	ax
X	call	_printk
X	call	_reboot
X.data
Xnomem:	.asciz	"\nOut of%s"
Xmemwarn:.ascii	"\nLow on"
Xchmem:	.asciz	" memory, use chmem to increase the heap\n"
X.text
X
X! int dev_geometry(void);
X!	Given the device "_device" and floppy disk parameters "_dskpars",
X!	set the number of heads and sectors.  It returns true iff the device
X!	exists.
X.define _dev_geometry
X_dev_geometry:
X	movb	dl, _device	! The default device
X	cmpb	dl, #0x80	! Floppy < 0x80, winchester >= 0x80
X	jae	winchester
Xfloppy:
X	int	0x11		! Get equipment configuration
X	testb	al, #0x01	! Bit 0 set if floppies available
X	jz	geoerr		! No floppy drives on this box
X	shl	ax, #1		! Highest floppy drive # in bits 6-7
X	shl	ax, #1		! Now in bits 0-1 of ah
X	andb	ah, #0x03	! Extract bits
X	cmpb	dl, ah		! Must be dl <= ah for drive to exist
X	ja	geoerr		! Alas no drive dl.
X	movb	dh, #2
X	movb	_heads, dh	! Number of heads for this device
X	mov	bx, #_dskpars	! bx = disk parameters
X	movb	cl, SECTORS(bx)
X	movb	_sectors, cl	! Sectors per track
X	xor	ax, ax
X	mov	es, ax		! es = 0 = vector segment
X	eseg
X	mov	DSKBASE+0, bx
X	eseg
X	mov	DSKBASE+2, ds	! DSKBASE+2:DSKBASE+0 = ds:bx = floppy parms
X	jmp	geoboth
Xwinchester:
X	movb	ah, #0x08	! Code for drive parameters
X	int	0x13		! dl still contains drive
X	jb	geoerr		! No such drive?
X	andb	cl, #0x3F	! cl = max sector number (1-origin)
X	movb	_sectors, cl	! Number of sectors per track
X	incb	dh		! dh = 1 + max head number (0-origin)
X	movb	_heads, dh
Xgeoboth:
X	mov	ax, #1		! Code for success
X	push	ax
X	movb	al, cl		! al = sectors per track
X	mulb	dh		! ax = heads * sectors
X	mov	secspcyl, ax	! Sectors per cylinder = heads * sectors
Xgeodone:push	ds
X	pop	es		! Restore es register
X	pop	ax		! Return code
X	ret
Xgeoerr:	xor	ax, ax
X	push	ax
X	jmp	geodone
X.bss
Xsecspcyl:	.zerow 1
X.text
X
X! int readsectors(bufoff, bufseg, address, count)
X! int writesectors(bufoff, bufseg, address, count)
X!	Read/write several sectors from/to disk or floppy.  The buffer must
X!	be between 64K boundaries!  Count must fit in a byte.  The external
X!	variables _device, _sectors and _heads describe the disk and its
X!	geometry.  Returns 0 for success, otherwise the BIOS error code.
X!
X.define _readsectors, _writesectors
X_writesectors:
X	push	bp
X	mov	bp, sp
X	movb	13(bp), #3	! Code for a disk write
X	jmp	rwsec
X_readsectors:
X	push	bp
X	mov	bp, sp
X	movb	13(bp), #2	! Code for a disk read
Xrwsec:	push	di
X	push	es
X	les	bx, 4(bp)	! es:bx = bufseg:bufoff
X	mov	di, #3		! Execute 3 resets on floppy error
X	cmpb	_device, #0x80
X	jb	nohd
X	mov	di, #1		! But only 1 reset on hard disk error
Xnohd:	cmpb	12(bp), #0	! count equals zero?
X	jz	done
Xmore:	mov	ax, 8(bp)
X	mov	dx, 10(bp)	! dx:ax = address.  Divide it by sectors/cyl
X	div	secspcyl	! ax = cylinder, dx = sector within cylinder
X	xchg	ax, dx		! ax = sector within cylinder, dx = cylinder
X	movb	ch, dl		! ch = low 8 bits of cylinder
X	divb	_sectors	! al = head, ah = sector (0-origin)
X	xorb	dl, dl		! About to shift bits 8-9 of cylinder into dl
X	shr	dx, #1
X	shr	dx, #1		! dl[6..7] = high cylinder
X	orb	dl, ah		! dl[0..5] = sector (0-origin)
X	movb	cl, dl		! cl[0..5] = sector, cl[6..7] = high cyl
X	incb	cl		! cl[0..5] = sector (1-origin)
X	movb	dh, al		! dh = head
X	movb	dl, _device	! dl = device to use
X	movb	al, _sectors	! Sectors per track - Sector number (0-origin)
X	subb	al, ah		! = Sectors left on this track
X	cmpb	al, 12(bp)	! Compare with # sectors to transfer
X	jbe	doit		! Can't go past the end of a cylinder?
X	movb	al, 12(bp)	! 12(bp) < sectors left on this track
Xdoit:	movb	ah, 13(bp)	! Code for disk read (2) or write (3)
X	push	ax		! Save al = sectors to read
X	int	0x13		! call the BIOS to do the transfer
X	pop	cx		! Restore al in cl
X	jb	ioerr		! I/O error
X	movb	al, cl		! Restore al = sectors read
X	addb	bh, al		! bx += 2 * al * 256 (add bytes transferred)
X	addb	bh, al		! es:bx = where next sector is located
X	add	8(bp), ax	! Update address by sectors transferred
X	adc	10(bp), #0	! Don't forget high word
X	subb	12(bp), al	! Decrement sector count by sectors transferred
X	jnz	more		! Not all sectors have been transferred
Xdone:	call	wheel		! Display tricks
X	xorb	ah, ah		! No error here!
X	jmp	finish
Xioerr:	dec	di		! Do we allow another reset?
X	jl	finish		! No, report the error
X	xorb	ah, ah		! Code for a reset (0)
X	int	0x13
X	jae	more		! Succesful reset, try request again
Xfinish:	movb	al, ah
X	xorb	ah, ah		! ax = error number
X	pop	es
X	pop	di
X	pop	bp
X	ret
X
X! int getchar(), peekchar();
X!	Read a character from the keyboard, or just look if there is one.
X!	A carriage return is changed into a linefeed for UNIX compatibility.
X!	The scancode of the character '=' is saved in eqscancode if it is
X!	typed, because it can be used to recognize an Olivetti keyboard.
X.define _getchar, _peekchar
X_peekchar:
X	movb	ah, #1		! Keyboard status
X	int	0x16
X	jnz	getc		! Keypress?
X	xor	ax, ax		! No key
X	ret
X_getchar:
X	movb	ah, #0		! Read character from keyboard
X	int	0x16
Xgetc:	cmpb	al, #0x3D	! '=' character
X	jnz	noeq
X	movb	_eqscancode, ah	! Save scancode of '=' key
Xnoeq:	cmpb	al, #0x0D	! Carriage return
X	jnz	nocr
X	movb	al, #0x0A	! Change to linefeed
Xnocr:	xorb	ah, ah		! ax = al
X	ret
X
X! int putchar(c);
X!	Write a character in teletype mode.  The putc and putk synonyms
X!	are for the kernel printk function that uses one of them.
X!	Newlines are automatically prepended by a carriage return.
X!
X.define _putchar, _putc, _putk
X_putchar:
X_putc:
X_putk:	mov	bx, sp
X	movb	al, 2(bx)	! al = character to be printed
X	testb	al, al		! 1.6.* printk prints null characters
X	jz	nulch		! that appear as blanks, so don't do it.
X	cmpb	al, #0x0A	! al = newline?
X	jnz	putc		! No
X	cmpb	wdirty, #0	! Yes, erase wheel and do a carriage return
X	jz	nodirt
X	movb	al, #0x20	! putc(' ');
X	call	putc
Xnodirt:	movb	al, #0x0D	! putc('\r')
X	call	putc
X	movb	al, #0x0A	! Restore the '\n' and print it
Xputc:	movb	ah, #14		! 14 = print character in teletype mode
X	mov	bx, #0x0001	! Page 0, foreground color
X	int	0x10		! Call BIOS VIDEO_IO
X	movb	wdirty, #0
Xnulch:	ret
X
X! |/-\|/-\|/-\|/-\|/-\	(playtime)
Xwheel:	push	si
X	mov	si, gp		! si = gp
X	lodsb
X	call	putc		! putc(*si++)
X	movb	al, #0x08
X	call	putc		! putc('\b')
X	cmp	si, #glyphs+4
X	jne	wmore
X	mov	si, #glyphs
Xwmore:	mov	gp, si		! gp= si == glyphs + 4 ? glyphs : si;
X	incb	wdirty
X	pop	si
X	ret
X.data
X	.align	2
Xgp:	.data2	glyphs
Xglyphs:	.ascii	"|/-\\"
Xwdirty:	.data1	0
X.text
X
X! void reset_video(int color);
X!	Reset and clear the screen.
X! void set_cursor(column, row);
X!	Position the cursor at a specified column & row.
X.define _reset_video, _set_cursor
X_reset_video:
X	mov	bx, sp
X	movb	al, #7		! Assume mode 7: 80x24 monochrome
X	cmp	2(bx), #0	! Or is color requested?
X	jz	clear
X	movb	al, #3		! Mode 3: 80x25 color
Xclear:	xorb	ah, ah		! Reset video
X	int	0x10
X	xor	dx, dx		! dl = column = 0, dh = row = 0
X	jmp	setcur
X_set_cursor:
X	mov	bx, sp
X	movb	dl, 2(bx)	! dl = column
X	movb	dh, 4(bx)	! dh = row
Xsetcur:	xorb	bh, bh		! Page 0
X	movb	ah, #0x02	! Set cursor position
X	int	0x10
X	ret
X
X! u32_t get_tick(void);
X!	Return the current value of the clock tick counter.  This counter
X!	increments 18.2 times per second.  Poll it to do delays.  Does not
X!	work on the original PC, but works on the PC/XT.
X.define _get_tick
X_get_tick:
X	xorb	ah, ah		! Code for get tick count
X	int	0x1A
X	mov	ax, dx
X	mov	dx, cx		! dx:ax = cx:dx = tick count
X	ret
X
X
X! Functions used to obtain info about the hardware, like the type of video
X! and amount of memory.  Boot uses this information itself, but will also
X! pass them on to a pure 386 kernel, because one can't make BIOS calls from
X! protected mode.  The video type could probably be determined by the kernel
X! too by looking at the hardware, but there is a small chance on errors that
X! the monitor allows you to correct by setting variables.
X
X.define 	_get_video	! returns type of display
X.define 	_get_ext_memsize  ! returns amount of extended memory in K
X.define 	_get_low_memsize  ! returns amount of low memory in K
X.define 	_get_processor	! returns processor type (86, 186, 286, 386)
X
X! u16_t get_video(void)
X!	Return type of display, in order: MDA, CGA, mono EGA, color EGA,
X!	mono VGA, color VGA.
X
X_get_video:
X	mov	ax, #0x1A00	! Function 1A returns display code
X	int	0x10		! al = 1A if supported
X	cmpb	al, #0x1A
X	jnz	no_dc		! No display code function supported
X
X	mov	ax, #2
X	cmpb	bl, #5		! Is it a monochrome EGA?
X	jz	got_video
X	inc	ax
X	cmpb	bl, #4		! Is it a color EGA?
X	jz	got_video
X	inc	ax
X	cmpb	bl, #7		! Is it a monochrome VGA?
X	jz	got_video
X	inc	ax
X	cmpb	bl, #8		! Is it a color VGA?
X	jz	got_video
X
Xno_dc:	movb	ah, #0x12	! Get information about the EGA
X	movb	bl, #0x10
X	int	0x10
X	cmpb	bl, #0x10	! Did it come back as 0x10? (No EGA)
X	jz	no_ega
X
X	mov	ax, #2
X	cmpb	bh, #1		! Is it monochrome?
X	jz	got_video
X	inc	ax
X	jmp	got_video
X
Xno_ega:	int	0x11		! Get bit pattern for equipment
X	and	ax, #0x30	! Isolate color/mono field
X	sub	ax, #0x30
X	jz	got_video	! Is it an MDA?
X	mov	ax, #1		! No it's CGA
X
Xgot_video:
X	ret
X
X! u16_t get_ext_memsize(void);
X!	Ask the BIOS how much extended memory there is.
X
X_get_ext_memsize:
X	call	_get_processor	! Is this an old crate?
X	cmp	ax, #186
X	jbe	no_ext		! Don't try the next function, it crashed an XT
X	movb	ah, #0x88
X	clc			! Carry will stay clear if call exists
X	int	0x15		! Returns size (in K) in ax for AT's
X	jnb	got_ext_memsize
Xno_ext:	sub	ax, ax		! Error, probably a PC
Xgot_ext_memsize:
X	ret
X
X! u16_t get_low_memsize(void);
X!	Ask the BIOS how much normal memory there is.
X
X_get_low_memsize:
X	int	0x12		! Returns the size (in K) in ax
X	ret
X
X! u16_t get_processor(void);
X!	Decide processor type among 8088=8086, 80188=80186, 80286, 80386, 80486.
X!	Return 86, 186, 286, 386 or 486.
X!	Preserves all registers except the flags and the return register ax.
X
X! Method:
X!	8088=8086 and 80188=80186 push sp as new sp, 80286 and 80386 as old sp.
X!	All but 8088=8086 do shifts mod 32 or 16.
X!	386 stores 0 for the upper 8 bits of the GDT pointer in 16 bit mode,
X!	while 286 stores 0xFF.
X!	486 has an AC flag the 386 doesn't have.
X
X	o32 = 0x66		! 32 bit operand size prefix
X
X_get_processor:
X	push	bp
X	mov	bp, sp
X	push	sp		! see if pushed sp == sp
X	pop	ax
X	cmp	ax, sp
X	jz	new_processor
X	mov	cx, #0x0120	! see if shifts are mod 32
X	shlb	ch, cl		! zero tells if 86
X	mov	ax, #86
X	jz	got_processor
X	mov	ax, #186
X	jmp	got_processor
X
Xnew_processor:			! see if high bits are set in saved GDT
X	sub	sp, #6		! space for GDT ptr
X	.data1	0x0F		! Prefix for 286 instruction: sgdt -6(bp)
X	add	-6(bp), ax	! save 3 word GDT ptr (This is NOT an add!)
X	cmpb	-1(bp), #0	! top byte of GDT ptr is zero only for 386
X	mov	ax, #286
X	jnz	got_processor
X
X! 386 or 486
X	and	sp, #0xFFFC	! Align stack to avoid AC fault (needed?)
X	.data1	o32		! About to operate on a 32 bit object
X	pushf			! Push eflags
X	pop	ax
X	pop	dx		! dx:ax = eflags
X	mov	bx, ax
X	mov	cx, dx		! Save original eflags
X	xor	dx, #0x0004	! Flip the AC bit
X	push	dx
X	push	ax		! Push modified eflags value
X	.data1	o32
X	popf			! Load modified eflags register
X	.data1	o32
X	pushf
X	pop	ax
X	pop	dx		! Get it again
X	push	cx
X	push	bx
X	.data1	o32
X	popf			! Restore original eflags register
X	xor	dx, cx		! See if AC bit changed
X	test	dx, #0x0004
X	mov	ax, #386	! 386 if it didn't react to "flipping"
X	jz	got_processor
X	mov	ax, #486	! 486 if you can modify the AC bit
X
Xgot_processor:
X	mov	sp, bp
X	pop	bp
X	ret
X
X! void _bootstrap(device, partoff, partseg)
X!	Call another bootstrap routine to boot MS-DOS for instance.  (No real
X!	need for that anymore, now that you can format floppies under Minix).
X!	The bootstrap must have been loaded at BOOTSEG from "device".
X.define _bootstrap
X_bootstrap:
X	mov	bx, sp
X	movb	dl, 2(bx)	! Device to boot from
X	lds	si, 4(bx)	! ds:si = partition table entry
X	xor	ax, ax
X	mov	es, ax		! Vector segment
X	mov	di, #BUFFER	! es:di = buffer in low core
X	mov	cx, #PENTRYSIZE	! cx = size of partition table entry
X	rep
X	movb			! Copy the entry to low core
X	mov	si, #BUFFER	! es:si = partition table entry
X	mov	ds, ax		! Some bootstraps need zero segment registers
X	cli
X	mov	ss, ax
X	mov	sp, #BOOTOFF	! This should do it
X	sti
X	jmpf	BOOTOFF, 0	! Back to where the BIOS loads the boot code
X
X! To my surprise this code is so fast that floppy drive 0 was still running
X! (tries to boot floppy first), when Minix was started after a hard disk boot.
Xstop_motor:
X	mov	dx, #0x03F2	! Motor drive control bits
X	movb	al, #0x0C	! Bits 4-7 for floppy 0-3 are off
X	outb			! Kill the motors
X	ret
X
X! void minix86(kcs, kds, bootparams, paramsize);
X!	Call 8086 Minix or 386 Minix with an 8086 startoff.
X.define _minix86
X_minix86:
X	call	stop_motor	! Turn off floppy drives
X	mov	bp, sp		! Pointer to arguments
X	mov	ax, #0x100	! Newer boot code
X	mov	si, 6(bp)
X	mov	di, ds		! di:si = boot parameters
X	mov	cx, 8(bp)	! # bytes of boot parameters
X	push	cx
X	push	si		! Kernel may find these on the stack too
X	mov	ds, 4(bp)	! Kernel data segment set
X	mov	es, 4(bp)	! Set es to kernel data too
X	cli			! Disable interrupts
X	mov	(bp), #0	! 2(bp):(bp) = kcs:0
X	jmpf	@(bp)		! Finally out of this mess!
X
X! Minix is called with ax, di and si as expected, but also with segment
X! registers and stack compatible with the 386 call.
X
X! void minix386(kcs, kds, bootparams, paramsize);
X!	Call 386 Minix with a 386 mode switch.  Code inspired by the Amoeba
X!	386 bootstrap by Leendert van Doorn.
X.define _minix386
X_minix386:
X	call	stop_motor	! Turn off floppy drives
X	mov	bp, sp		! Pointer to arguments
X
X	mov	di, ds		! Monitor ds
X	mov	si, #gdt	! di:si = Global descriptor table
X	mov	bx, #gdt_desc
X	call	set_base	! Set base of gdt descriptor
X
X	mov	di, 4(bp)	! Kernel ds
X	xor	si, si		! di:si = Kernel data segment
X	mov	bx, #ds_desc
X	call	set_base	! Set base of kernel ds
X
X	mov	di, ss		! Monitor ss
X	xor	si, si		! di:si = Monitor stack segment
X	mov	bx, #ss_desc
X	call	set_base	! Minix starts with the stack of the monitor
X
X	mov	di, 2(bp)	! Kernel cs
X	xor	si, si		! di:si = Kernel text segment
X	mov	bx, #cs_desc
X	call	set_base	! Set base of kernel cs
X
X	xor	ax, ax
X	push	ax
X	push	8(bp)		! 32 bit size of parameters on stack
X	push	ax
X	push	6(bp)		! 32 bit address of parameters (ss relative)
X
X! Use the BIOS to kick us into protected mode.  This is the most portable
X! way to enable the A20 address line.  A real programmer would use cr0.
X
X	mov	si, #gdt	! es:si = global descriptor table
X	xor	bx, bx		! 8259's must be initialized by the kernel
X	movb	ah, #0x89	! Protected mode function code
X! Fake an interrupt stack frame as if called from the kernel entry point
X	cli			! No more interruptions
X	pushf			! Flags
X	push	2(bp)		! Kernel cs
X	push	bx		! Kernel entry point (0)
X	mov	ds, bx		! ds = vector segment
X	jmpf	@4*0x15		! "int 0x15"
X
X! The "interrupt" will return directly to the Minix kernel in 386 mode.  The
X! split is clean:  No 386 code here, and no 8086 code in the kernel.  The
X! boot parameters address and size are on the stack.  They may be retrieved
X! using the ss descriptor.
X
Xset_base:
X	! Set the base of descriptor bx to the 8086 address di:si
X	mov	2(bx), di	! Base = segment
X	mov	cx, #4
Xseg2abs:
X	shl	2(bx), #1
X	rclb	4(bx), #1	! Base = segment << 4
X	loop	seg2abs
X	add	2(bx), si
X	adcb	4(bx), #0	! Base = (segment << 4) + offset
X	ret
X
X.data
X	.align	2
X	UNSET	= 0	! Must be computed
X
X! Global descriptor table.
Xgdt:
Xnull_desc:
X	! Null descriptor
X	.data2	0x0000, 0x0000
X	.data1	0x00, 0x00, 0x00, 0x00
Xgdt_desc:
X	! Descriptor for this descriptor table
X	.data2	8*8-1, UNSET
X	.data1	UNSET, 0x00, 0x00, 0x00
Xidt_desc:
X	! Interrupt descriptor table descriptor (no interrupts allowed)
X	.data2	0x0000, 0x0000
X	.data1	0x00, 0x00, 0x00, 0x00
Xds_desc:
X	! Kernel data segment descriptor (4Gb flat)
X	.data2	0xFFFF, UNSET
X	.data1	UNSET, 0x92, 0xCF, 0x00
Xes_desc:
X	! Physical memory descriptor (4Gb flat)
X	.data2	0xFFFF, 0x0000
X	.data1	0x00, 0x92, 0xCF, 0x00
Xss_desc:
X	! Monitor data segment descriptor (64Kb flat)
X	.data2	0xFFFF, UNSET
X	.data1	UNSET, 0x92, 0x40, 0x00
Xcs_desc:
X	! Kernel code segment descriptor (4Gb flat)
X	.data2	0xFFFF, UNSET
X	.data1	UNSET, 0x9A, 0xCF, 0x00
Xbios_desc:
X	! BIOS segment descriptor (scratch for int 0x15)
X	.data2	UNSET, UNSET
X	.data1	UNSET, UNSET, UNSET, UNSET
/
echo x - monhead.s.kr
sed '/^X/s///' > monhead.s.kr << '/'
X.define begtext, begdata, begbss
X.data
Xbegdata: 
X.asciz	"(null)"
X.bss
Xbegbss: 
XBOOTOFF	   =	0x7C00
XLOADSEG    =	0x1000
XBUFFER	   =	0x0600
XPENTRYSIZE =	    16
XDSKBASE    =	   120
XDSKPARSIZE =	    11
XSECTORS	   =	     4
Xa_flags	   =	     2
Xa_text	   =	     8
Xa_data	   =	    12
Xa_bss	   =	    16
Xa_total	   =	    24
XA_SEP	   =	  0x20
X.extern _cseg, _dseg, _runsize, _edata, _end
X.extern _device, _dskpars, _heads, _sectors
X.extern _eqscancode
X.extern _rem_part
X.text
Xbegtext: 
X.extern _boot, _printk
Xjmpi boot, LOADSEG+3
X.space	11
Xjmpi boot, LOADSEG+2
Xboot: 
Xmov	ax, #LOADSEG
Xmov	ds, ax
Xmovb	al, a_flags
Xtestb	al, #A_SEP
Xjnz	sepID
XcomID: xor	ax, ax
Xxchg	ax, a_text
Xadd	a_data, ax
XsepID: 
Xmov	ax, a_total
Xand	ax, #0xFFFE
Xmov	a_total, ax
Xcli
Xmov	sp, ax
Xmov	ax, a_text
Xmovb	cl, #4
Xshr	ax, cl
Xmov	cx, cs
Xadd	ax, cx
Xmov	ds, ax
Xmov	ss, ax
Xsti
Xpush	es
Xmov	es, ax
Xcld
Xxor	ax, ax
Xmov	di, #_edata
Xmov	cx, #_end
Xsub	cx, di
Xshr	cx, #1
Xrep
Xstow
Xxorb	dh, dh
Xmov	_device, dx
Xmov	_rem_part+0, si
Xpop	_rem_part+2
Xmov	_cseg, cs
Xmov	_dseg, ds
Xpush	ds
Xmov	ax, #LOADSEG
Xmov	ds, ax
Xmov	ax, a_total+0
Xmov	dx, a_total+2
Xadd	ax, a_text+0
Xadc	dx, a_text+2
Xpop	ds
Xmov	_runsize+0, ax
Xmov	_runsize+2, dx
Xcall	_boot
X.define _exit, __exit, _main
X.define _reboot
X_exit: 
X__exit: 
X_main: 
X_reboot: 
Xmov	ax, #any_key
Xpush	ax
Xcall	_printk
Xcall	_getchar
Xint	0x19
X.data
Xany_key: 
X.asciz	"\nHit any key to reboot\n"
X.text
X.define _raw_copy
X_raw_copy: 
Xmov	bx, sp
Xpush	si
Xpush	di
Xmov	cx, 10(bx)
Xles	di, 2(bx)
Xlds	si, 6(bx)
Xshr	cx, #1
Xjb	bytecopy
Xrep
Xmovw
Xj copydone
Xbytecopy: 
Xrcl	cx, #1
Xrep
Xmovb
Xcopydone: 
Xmov	ax, ss
Xmov	ds, ax
Xmov	es, ax
Xpop	di
Xpop	si
Xret
X.define _raw_clear
X_raw_clear: 
Xmov	bx, sp
Xpush	di
Xles	di, 2(bx)
Xxor	ax, ax
Xmov	cx, 6(bx)
Xshr	cx, #1
Xjb	byteclear
Xrep
Xstow
Xj cleardone
Xbyteclear: 
Xrcl	cx, #1
Xrep
Xstob
Xcleardone: 
Xmov	ax, ds
Xmov	es, ax
Xpop	di
Xret
X.define _get_word, _put_word
X_get_word: 
Xmov	bx, sp
Xpush	ds
Xlds	bx, 2(bx)
Xmov	ax, (bx)
Xpop	ds
Xret
X_put_word: 
Xmov	bx, sp
Xpush	ds
Xmov	ax, 6(bx)
Xlds	bx, 2(bx)
Xmov	(bx), ax
Xpop	ds
Xret
X.define _relocate
X_relocate: 
Xpop	bx
Xmov	ax, cs
Xsub	ax, _cseg
Xmov	dx, ds
Xsub	dx, ax
Xmov	ds, dx
Xmov	es, dx
Xmov	ss, dx
Xmov	_dseg, dx
Xpush	_cseg
Xpush	bx
Xreti
X.data
X.align	2
Xbreak: .word 	_end
X.text
X.define _brk, __brk, _sbrk, __sbrk
X_brk: 
X__brk: 
Xxor	ax, ax
Xj sbrk
X_sbrk: 
X__sbrk: 
Xmov	ax, break
Xsbrk: push	ax
Xmov	bx, sp
Xadd	ax, 4(bx)
Xmov	break, ax
Xlea	dx, -1024(bx)
Xcmp	dx, ax
Xjb	heaperr
Xlea	dx, -4096(bx)
Xcmp	dx, ax
Xjae	plenty
Xmov	ax, #memwarn
Xpush	ax
Xcall	_printk
Xpop	ax
Xmovb	memwarn, #0
Xplenty: pop	ax
Xret
Xheaperr: mov	ax, #chmem
Xpush	ax
Xmov	ax, #nomem
Xpush	ax
Xcall	_printk
Xcall	_reboot
X.data
Xnomem: .asciz	"\nOut of%s"
Xmemwarn: .ascii	"\nLow on"
Xchmem: .asciz	" memory, use chmem to increase the heap\n"
X.text
X.define _dev_geometry
X_dev_geometry: 
Xmovb	dl, _device
Xcmpb	dl, #0x80
Xjae	winchester
Xfloppy: 
Xint	0x11
Xtestb	al, #0x01
Xjz	geoerr
Xshl	ax, #1
Xshl	ax, #1
Xandb	ah, #0x03
Xcmpb	dl, ah
Xja	geoerr
Xmovb	dh, #2
Xmovb	_heads, dh
Xmov	bx, #_dskpars
Xmovb	cl, SECTORS(bx)
Xmovb	_sectors, cl
Xxor	ax, ax
Xmov	es, ax
Xseg es
Xmov	DSKBASE+0, bx
Xseg es
Xmov	DSKBASE+2, ds
Xj geoboth
Xwinchester: 
Xmovb	ah, #0x08
Xint	0x13
Xjb	geoerr
Xandb	cl, #0x3F
Xmovb	_sectors, cl
Xincb	dh
Xmovb	_heads, dh
Xgeoboth: 
Xmov	ax, #1
Xpush	ax
Xmovb	al, cl
Xmulb	dh
Xmov	secspcyl, ax
Xgeodone: push	ds
Xpop	es
Xpop	ax
Xret
Xgeoerr: xor	ax, ax
Xpush	ax
Xj geodone
X.bss
Xsecspcyl: .zerow 1
X.text
X.define _readsectors, _writesectors
X_writesectors: 
Xpush	bp
Xmov	bp, sp
Xmovb	13(bp), #3
Xj rwsec
X_readsectors: 
Xpush	bp
Xmov	bp, sp
Xmovb	13(bp), #2
Xrwsec: push	di
Xpush	es
Xles	bx, 4(bp)
Xmov	di, #3
Xcmpb	_device, #0x80
Xjb	nohd
Xmov	di, #1
Xnohd: cmpb	12(bp), #0
Xjz	done
Xmore: mov	ax, 8(bp)
Xmov	dx, 10(bp)
Xdiv	secspcyl
Xxchg	ax, dx
Xmovb	ch, dl
Xdivb	_sectors
Xxorb	dl, dl
Xshr	dx, #1
Xshr	dx, #1
Xorb	dl, ah
Xmovb	cl, dl
Xincb	cl
Xmovb	dh, al
Xmovb	dl, _device
Xmovb	al, _sectors
Xsubb	al, ah
Xcmpb	al, 12(bp)
Xjbe	doit
Xmovb	al, 12(bp)
Xdoit: movb	ah, 13(bp)
Xpush	ax
Xint	0x13
Xpop	cx
Xjb	ioerr
Xmovb	al, cl
Xaddb	bh, al
Xaddb	bh, al
Xadd	8(bp), ax
Xadc	10(bp), #0
Xsubb	12(bp), al
Xjnz	more
Xdone: call	wheel
Xxorb	ah, ah
Xj finish
Xioerr: dec	di
Xjl	finish
Xxorb	ah, ah
Xint	0x13
Xjae	more
Xfinish: movb	al, ah
Xxorb	ah, ah
Xpop	es
Xpop	di
Xpop	bp
Xret
X.define _getchar, _peekchar
X_peekchar: 
Xmovb	ah, #1
Xint	0x16
Xjnz	getc
Xxor	ax, ax
Xret
X_getchar: 
Xmovb	ah, #0
Xint	0x16
Xgetc: cmpb	al, #0x3D
Xjnz	noeq
Xmovb	_eqscancode, ah
Xnoeq: cmpb	al, #0x0D
Xjnz	nocr
Xmovb	al, #0x0A
Xnocr: xorb	ah, ah
Xret
X.define _putchar, _putc, _putk
X_putchar: 
X_putc: 
X_putk: mov	bx, sp
Xmovb	al, 2(bx)
Xtestb	al, al
Xjz	nulch
Xcmpb	al, #0x0A
Xjnz	putc
Xcmpb	wdirty, #0
Xjz	nodirt
Xmovb	al, #0x20
Xcall	putc
Xnodirt: movb	al, #0x0D
Xcall	putc
Xmovb	al, #0x0A
Xputc: movb	ah, #14
Xmov	bx, #0x0001
Xint	0x10
Xmovb	wdirty, #0
Xnulch: ret
Xwheel: push	si
Xmov	si, gp
Xlodb
Xcall	putc
Xmovb	al, #0x08
Xcall	putc
Xcmp	si, #glyphs+4
Xjne	wmore
Xmov	si, #glyphs
Xwmore: mov	gp, si
Xincb	wdirty
Xpop	si
Xret
X.data
X.align	2
Xgp: .word 	glyphs
Xglyphs: .ascii	"|/-\\"
Xwdirty: .byte 	0
X.text
X.define _reset_video, _set_cursor
X_reset_video: 
Xmov	bx, sp
Xmovb	al, #7
Xcmp	2(bx), #0
Xjz	clear
Xmovb	al, #3
Xclear: xorb	ah, ah
Xint	0x10
Xxor	dx, dx
Xj setcur
X_set_cursor: 
Xmov	bx, sp
Xmovb	dl, 2(bx)
Xmovb	dh, 4(bx)
Xsetcur: xorb	bh, bh
Xmovb	ah, #0x02
Xint	0x10
Xret
X.define _get_tick
X_get_tick: 
Xxorb	ah, ah
Xint	0x1A
Xmov	ax, dx
Xmov	dx, cx
Xret
X.define 	_get_video
X.define 	_get_ext_memsize
X.define 	_get_low_memsize
X.define 	_get_processor
X_get_video: 
Xmov	ax, #0x1A00
Xint	0x10
Xcmpb	al, #0x1A
Xjnz	no_dc
Xmov	ax, #2
Xcmpb	bl, #5
Xjz	got_video
Xinc	ax
Xcmpb	bl, #4
Xjz	got_video
Xinc	ax
Xcmpb	bl, #7
Xjz	got_video
Xinc	ax
Xcmpb	bl, #8
Xjz	got_video
Xno_dc: movb	ah, #0x12
Xmovb	bl, #0x10
Xint	0x10
Xcmpb	bl, #0x10
Xjz	no_ega
Xmov	ax, #2
Xcmpb	bh, #1
Xjz	got_video
Xinc	ax
Xj got_video
Xno_ega: int	0x11
Xand	ax, #0x30
Xsub	ax, #0x30
Xjz	got_video
Xmov	ax, #1
Xgot_video: 
Xret
X_get_ext_memsize: 
Xcall	_get_processor
Xcmp	ax, #186
Xjbe	no_ext
Xmovb	ah, #0x88
Xclc
Xint	0x15
Xjnb	got_ext_memsize
Xno_ext: sub	ax, ax
Xgot_ext_memsize: 
Xret
X_get_low_memsize: 
Xint	0x12
Xret
Xo32 = 0x66
X_get_processor: 
Xpush	bp
Xmov	bp, sp
Xpush	sp
Xpop	ax
Xcmp	ax, sp
Xjz	new_processor
Xmov	cx, #0x0120
Xshlb	ch, cl
Xmov	ax, #86
Xjz	got_processor
Xmov	ax, #186
Xj got_processor
Xnew_processor: 
Xsub	sp, #6
X.byte 	0x0F
Xadd	-6(bp), ax
Xcmpb	-1(bp), #0
Xmov	ax, #286
Xjnz	got_processor
Xand	sp, #0xFFFC
X.byte 	o32
Xpushf
Xpop	ax
Xpop	dx
Xmov	bx, ax
Xmov	cx, dx
Xxor	dx, #0x0004
Xpush	dx
Xpush	ax
X.byte 	o32
Xpopf
X.byte 	o32
Xpushf
Xpop	ax
Xpop	dx
Xpush	cx
Xpush	bx
X.byte 	o32
Xpopf
Xxor	dx, cx
Xtest	dx, #0x0004
Xmov	ax, #386
Xjz	got_processor
Xmov	ax, #486
Xgot_processor: 
Xmov	sp, bp
Xpop	bp
Xret
X.define _bootstrap
X_bootstrap: 
Xmov	bx, sp
Xmovb	dl, 2(bx)
Xlds	si, 4(bx)
Xxor	ax, ax
Xmov	es, ax
Xmov	di, #BUFFER
Xmov	cx, #PENTRYSIZE
Xrep
Xmovb
Xmov	si, #BUFFER
Xmov	ds, ax
Xcli
Xmov	ss, ax
Xmov	sp, #BOOTOFF
Xsti
Xjmpi BOOTOFF, 0
Xstop_motor: 
Xmov	dx, #0x03F2
Xmovb	al, #0x0C
Xout
Xret
X.define _minix86
X_minix86: 
Xcall	stop_motor
Xmov	bp, sp
Xmov	ax, #0x100
Xmov	si, 6(bp)
Xmov	di, ds
Xmov	cx, 8(bp)
Xpush	cx
Xpush	si
Xmov	ds, 4(bp)
Xmov	es, 4(bp)
Xcli
Xmov	(bp), #0
Xjmpi @(bp)
X.define _minix386
X_minix386: 
Xcall	stop_motor
Xmov	bp, sp
Xmov	di, ds
Xmov	si, #gdt
Xmov	bx, #gdt_desc
Xcall	set_base
Xmov	di, 4(bp)
Xxor	si, si
Xmov	bx, #ds_desc
Xcall	set_base
Xmov	di, ss
Xxor	si, si
Xmov	bx, #ss_desc
Xcall	set_base
Xmov	di, 2(bp)
Xxor	si, si
Xmov	bx, #cs_desc
Xcall	set_base
Xxor	ax, ax
Xpush	ax
Xpush	8(bp)
Xpush	ax
Xpush	6(bp)
Xmov	si, #gdt
Xxor	bx, bx
Xmovb	ah, #0x89
Xcli
Xpushf
Xpush	2(bp)
Xpush	bx
Xmov	ds, bx
Xjmpi @4*0x15
Xset_base: 
Xmov	2(bx), di
Xmov	cx, #4
Xseg2abs: 
Xshl	2(bx), #1
Xrclb	4(bx), #1
Xloop	seg2abs
Xadd	2(bx), si
Xadcb	4(bx), #0
Xret
X.data
X.align	2
XUNSET	= 0
Xgdt: 
Xnull_desc: 
X.word 	0x0000, 0x0000
X.byte 	0x00, 0x00, 0x00, 0x00
Xgdt_desc: 
X.word 	8*8-1, UNSET
X.byte 	UNSET, 0x00, 0x00, 0x00
Xidt_desc: 
X.word 	0x0000, 0x0000
X.byte 	0x00, 0x00, 0x00, 0x00
Xds_desc: 
X.word 	0xFFFF, UNSET
X.byte 	UNSET, 0x92, 0xCF, 0x00
Xes_desc: 
X.word 	0xFFFF, 0x0000
X.byte 	0x00, 0x92, 0xCF, 0x00
Xss_desc: 
X.word 	0xFFFF, UNSET
X.byte 	UNSET, 0x92, 0x40, 0x00
Xcs_desc: 
X.word 	0xFFFF, UNSET
X.byte 	UNSET, 0x9A, 0xCF, 0x00
Xbios_desc: 
X.word 	UNSET, UNSET
X.byte 	UNSET, UNSET, UNSET, UNSET
/
echo x - rawfs.c
sed '/^X/s///' > rawfs.c << '/'
X/* rawfs.c - Raw Minix file system support.	Author: Kees J. Bot */
X
X/* rawfs is based on readfs by Paul Polderman */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <stdlib.h>
X#include <limits.h>
X#include <string.h>
X#include <errno.h>
X
X#include "minix/config.h"
X#include "minix/const.h"
X#include "minix/type.h"
X#include "fs/const.h"
X#include "fs/type.h"
X#include "fs/buf.h"
X#include "fs/super.h"
X#include "fs/inode.h"
X#include <a.out.h>
X#include <tools.h>
X
X#define fsbuf(b)	(* (struct buf *) (b))
X#define	zone_shift	(super.s_log_zone_size)	/* zone to block ratio */
X#define scratch dirbuf
X
X/* The following code handles two file system types: 
X *    Version 1 with small inodes and 16-bit disk addresses
X *    Version 2 with big inodes and 32-bit disk addresses.
X */
X
X/* Version 1 or 2 file system constants. */
Xstatic unsigned nr_dzones;	/* Fill these in after reading superblock. */
Xstatic unsigned nr_tzones;
Xstatic unsigned nr_indirects;
Xstatic unsigned inodes_per_block;
X
Xstatic struct super_block super;/* Superblock of file system */
Xstatic struct inode curfil;	/* Inode of file under examination */
Xstatic char indir[BLOCK_SIZE];	/* Single indirect block. */
Xstatic char dindir[BLOCK_SIZE];	/* Double indirect block. */
Xstatic char dirbuf[BLOCK_SIZE];	/* Scratch/Directory block. */
Xstatic block_t a_indir, a_dindir;	/* Addresses of the indirects. */
Xstatic off_t dirpos;		/* Reading pos in a dir. */
X
X_PROTOTYPE(void readblock, (off_t blk, char *buf ));
X
Xoff_t r_super()
X{
X/* Initialize variables, return size of a file system in blocks,
X * (zero on error).
X */
X
X  readblock((off_t) SUPER_BLOCK, scratch);	  /* read superblock */
X  memcpy((void *) &super, (void *) scratch, sizeof(super));
X
X  /* Is it really a MINIX file system ? */
X  if (super.s_magic == SUPER_V2) {
X	nr_dzones = V2_NR_DZONES;
X	nr_tzones = V2_NR_TZONES;
X	nr_indirects = V2_INDIRECTS;
X	inodes_per_block = V2_INODES_PER_BLOCK;
X	return(off_t) super.s_zones << zone_shift;
X  } else
X  if (super.s_magic == SUPER_MAGIC) {
X	nr_dzones = V1_NR_DZONES;
X	nr_tzones = V1_NR_TZONES;
X	nr_indirects = V1_INDIRECTS;
X	inodes_per_block = V1_INODES_PER_BLOCK;
X	return(off_t) super.s_nzones << zone_shift;
X  } else {
X	/* Filesystem not recognized as Minix. */
X	return 0;
X  }
X}
X
Xvoid r_stat(inum, stp)
XIno_t inum;
Xstruct stat *stp;
X{
X/* Return information about a file like stat(2) and remember it. */
X
X  block_t block;
X  block_t ino_block;
X  Ino_t ino_offset;
X
X  /* Calculate start of i-list. */
X  block = SUPER_BLOCK + 1 + super.s_imap_blocks + super.s_zmap_blocks;
X
X  /* Calculate block with inode inum. */
X  ino_block = ((inum - 1) / inodes_per_block);
X  ino_offset = ((inum - 1) % inodes_per_block);
X  block += ino_block;
X
X  /* Fetch the block. */
X  readblock((off_t) block, scratch);
X
X  if (super.s_magic == SUPER_V2) {
X	d2_inode *dip;
X	int i;
X
X	dip = &fsbuf(scratch).b_v2_ino[ino_offset];
X
X	curfil.i_mode = dip->d2_mode;
X	curfil.i_nlinks = dip->d2_nlinks;
X	curfil.i_uid = dip->d2_uid;
X	curfil.i_gid = dip->d2_gid;
X	curfil.i_size = dip->d2_size;
X	curfil.i_atime = dip->d2_atime;
X	curfil.i_mtime = dip->d2_mtime;
X	curfil.i_ctime = dip->d2_ctime;
X	for (i = 0; i < V2_NR_TZONES; i++)
X		curfil.i_zone[i] = dip->d2_zone[i];
X  } else {
X	d1_inode *dip;
X	int i;
X
X	dip = &fsbuf(scratch).b_v1_ino[ino_offset];
X
X	curfil.i_mode = dip->d1_mode;
X	curfil.i_nlinks = dip->d1_nlinks;
X	curfil.i_uid = dip->d1_uid;
X	curfil.i_gid = dip->d1_gid;
X	curfil.i_size = dip->d1_size;
X	curfil.i_atime = dip->d1_mtime;
X	curfil.i_mtime = dip->d1_mtime;
X	curfil.i_ctime = dip->d1_mtime;
X	for (i = 0; i < V1_NR_TZONES; i++)
X		curfil.i_zone[i] = dip->d1_zone[i];
X  }
X  curfil.i_dev = -1;		/* Can't fill this in alas. */
X  curfil.i_num = inum;
X
X  stp->st_dev = curfil.i_dev;
X  stp->st_ino = curfil.i_num;
X  stp->st_mode = curfil.i_mode;
X  stp->st_nlink = curfil.i_nlinks;
X  stp->st_uid = curfil.i_uid;
X  stp->st_gid = curfil.i_gid;
X  stp->st_rdev = (dev_t) curfil.i_zone[0];
X  stp->st_size = curfil.i_size;
X  stp->st_atime = curfil.i_atime;
X  stp->st_mtime = curfil.i_mtime;
X  stp->st_ctime = curfil.i_ctime;
X
X  a_indir = a_dindir = 0;
X  dirpos = 0;
X}
X
Xino_t r_readdir(name)
Xchar *name;
X/* Read next directory entry at "dirpos" from file "curfil". */
X{
X  Ino_t inum = 0;
X  int blkpos;
X  struct direct *dp;
X
X  if (!S_ISDIR(curfil.i_mode)) {
X	errno = ENOTDIR;
X	return -1;
X  }
X  while (inum == 0 && dirpos < curfil.i_size) {
X	if ((blkpos = (int) (dirpos % BLOCK_SIZE)) == 0) {
X		/* Need to fetch a new directory block. */
X
X		readblock(r_vir2abs(dirpos / BLOCK_SIZE), dirbuf);
X	}
X
X	/* Let dp point to the next entry. */
X	dp = (struct direct *) (dirbuf + blkpos);
X
X	if ((inum = dp->d_ino) != 0) {
X		/* This entry is occupied, return name. */
X		strncpy(name, dp->d_name, sizeof(dp->d_name));
X		name[sizeof(dp->d_name)] = 0;
X	}
X	dirpos += DIR_ENTRY_SIZE;
X  }
X  return inum;
X}
X
Xoff_t r_vir2abs(virblk)
Xoff_t virblk;
X{
X/* Translate a block number in a file to an absolute disk block number.
X * Returns 0 for a hole or if block is past end of file.
X */
X
X  block_t b = virblk;
X  zone_t zone, ind_zone;
X  block_t z, zone_index;
X
X  /* Check if virblk within file. */
X  if (virblk * BLOCK_SIZE >= curfil.i_size) return 0;
X
X  /* Calculate zone in which the datablock number is contained */
X  zone = (zone_t) (b >> zone_shift);
X
X  /* Calculate index of the block number in the zone */
X  zone_index = b - ((block_t) zone << zone_shift);
X
X  /* Go get the zone */
X  if (zone < (zone_t) nr_dzones) {	/* direct block */
X	zone = curfil.i_zone[(int) zone];
X	z = ((block_t) zone << zone_shift) + zone_index;
X	return z;
X  }
X
X  /* The zone is not a direct one */
X  zone -= (zone_t) nr_dzones;
X
X  /* Is it single indirect ? */
X  if (zone < (zone_t) nr_indirects) {	/* single indirect block */
X	ind_zone = curfil.i_zone[nr_dzones];
X  } else {			/* double indirect block */
X	/* Fetch the double indirect block */
X	if ((ind_zone = curfil.i_zone[nr_dzones + 1]) == 0) return 0;
X
X	z = (block_t) ind_zone << zone_shift;
X	if (a_dindir != z) {
X		readblock((off_t) z, dindir);
X		a_dindir = z;
X	}
X
X	/* Extract the indirect zone number from it */
X	zone -= (zone_t) nr_indirects;
X
X	ind_zone = super.s_magic == SUPER_V2 ?
X		fsbuf(dindir).b_v2_ind[(int) (zone/(zone_t) nr_indirects)] :
X		fsbuf(dindir).b_v1_ind[(int) (zone/(zone_t) nr_indirects)];
X	zone %= (zone_t) nr_indirects;
X  }
X  if (ind_zone == 0) return 0;
X
X  /* Extract the datablock number from the indirect zone */
X  z = (block_t) ind_zone << zone_shift;
X  if (a_indir != z) {
X	readblock((off_t) z, indir);
X	a_indir = z;
X  }
X  zone = super.s_magic == SUPER_V2 ?
X	fsbuf(indir).b_v2_ind[(int) zone] :
X	fsbuf(indir).b_v1_ind[(int) zone];
X
X  /* Calculate absolute datablock number */
X  z = ((block_t) zone << zone_shift) + zone_index;
X  return z;
X}
X
Xino_t r_lookup(cwd, path)
XIno_t cwd;
Xchar *path;
X/* Translates a pathname to an inode number.  This is just a nice utility
X * function.  It needs r_stat and r_readdir. It starts from 'cwd' unless
X * the path starts with '/', in which case it starts from ROOT_INO.
X */
X{
X  char name[NAME_MAX + 1], r_name[NAME_MAX + 1];
X  char *n;
X  struct stat st;
X  Ino_t ino;
X
X  ino = path[0] == '/' ? ROOT_INO : cwd;
X
X  for (;;) {
X	if (ino == 0) {
X		errno = ENOENT;
X		return 0;
X	}
X	while (*path == '/') path++;
X
X	if (*path == 0) return ino;
X
X	r_stat(ino, &st);
X
X	if (!S_ISDIR(st.st_mode)) {
X		errno = ENOTDIR;
X		return 0;
X	}
X	n = name;
X	while (*path != 0 && *path != '/')
X		if (n < name + NAME_MAX) *n++ = *path++;
X	*n = 0;
X
X	while ((ino = r_readdir(r_name)) != 0
X	       && strcmp(name, r_name) != 0) {
X	}
X  }
X}
/
echo x - setcompiler
sed '/^X/s///' > setcompiler << '/'
Xu=
Xs=
Xl=/usr/src/lib
Xc=/usr/src/commands
X
Xif test -f /usr/lib/libc.a.15; then :; else echo libc.a not in place; exit; fi
Xif test `whoami` != "root"; then echo Must be superuser ; exit; fi
X
Xcase $1 in
Xkr)
X	suffix=a
X	archiver=ar
X
X	# Install header files
X	cp $u/include/ctype.h.kr  $u/include/ctype.h
X	cp $u/include/setjmp.h.kr $u/include/setjmp.h
X	cp $u/include/stdio.h.kr  $u/include/stdio.h
X
X	# Install Makefiles
X	cp $s/kernel/Makefile.kr  $s/kernel/Makefile
X	cp $s/fs/Makefile.kr      $s/fs/Makefile
X	cp $s/mm/Makefile.kr      $s/mm/Makefile
X	cp $s/tools/Makefile.kr   $s/tools/Makefile
X	cp $l/Makefile.kr	  $l/Makefile
X	cp $l/ansi/Makefile.kr    $l/ansi/Makefile
X	cp $l/other/Makefile.kr   $l/other/Makefile
X	cp $l/curses/Makefile.kr  $l/curses/Makefile
X	cp $l/posix/Makefile.kr   $l/posix/Makefile
X	cp $l/rts/Makefile.kr     $l/rts/Makefile
X	cp $c/simple/Makefile.kr  $c/simple/Makefile
X	cp $c/ibm/Makefile.kr     $c/ibm/Makefile
X
X	# Install asld kernel assembler files
X	cp $s/kernel/klib88.x.kr  $s/kernel/klib.x
X	cp $s/kernel/mpx88.x.kr   $s/kernel/mpx.x
X	cp $s/kernel/rs2.x.kr     $s/kernel/rs2.x
X	cp $s/kernel/sconst.h.kr  $s/kernel/sconst.h
X
X	# Install tools assembler files
X	cp $s/tools/bootblk.s.kr  $s/tools/bootblk.s
X	cp $s/tools/monhead.s.kr  $s/tools/monhead.s
X
X	# Install lib/syscall and lib/ibm assembler files
X	rm -rf $l/syscall $l/ibm
X	cpdir $l/syscall.kr       $l/syscall
X	cpdir $l/ibm.kr           $l/ibm
X	chown ast $l/syscall $l/syscall/* 
X	chown ast $l/ibm $l/ibm/*
X
X	# Fix up Commands Makefiles
X	cd $c
X	for i in advent/Makefile bawk/Makefile bc/Makefile de/Makefile \
X	    dis88/Makefile elle/Makefile ibm/Makefile ic/Makefile \
X	    indent/Makefile m4/Makefile make/Makefile mined/Makefile \
X	    nroff/Makefile patch/Makefile sh/Makefile zmodem/Makefile 
X	do sed <$i '/O=o/s//O=s/' >tmp
X	   cp tmp $i
X	done
X
X	# Install C compiler
X	rm -f /lib/*cpp /lib/*head.* /lib/*crtso*
X	cp /usr/bin/cc.kr	  /bin/cc
X	cp /usr/lib/cpp		  /lib/cpp
X	cp /usr/lib/cem		  /lib/cem
X	cp /usr/lib/opt		  /lib/opt
X	cp /usr/lib/cg		  /lib/cg
X	cp /usr/lib/*.s		  /lib
X	cp /usr/lib/libc.a.15	  /lib/libc.a
X	cp /usr/lib/libc.a.15	  /usr/lib/libc.a
X	cp /usr/lib/head.s	  /lib/head.s
X	cp /usr/lib/crtso.s	  /lib/crtso.s
X	;;
X
Xansi)
X	suffix=o
X	archiver=aal
X
X	# Install header files
X	cp $u/include/ctype.h.ansi  $u/include/ctype.h
X	cp $u/include/setjmp.h.ansi $u/include/setjmp.h
X	cp $u/include/stdio.h.ansi  $u/include/stdio.h
X
X	# Install Makefiles
X	cp $s/kernel/Makefile.ansi  $s/kernel/Makefile
X	cp $s/fs/Makefile.ansi      $s/fs/Makefile
X	cp $s/mm/Makefile.ansi      $s/mm/Makefile
X	cp $s/tools/Makefile.ansi   $s/tools/Makefile
X	cp $l/Makefile.ansi	    $l/Makefile
X	cp $l/ansi/Makefile.ansi    $l/ansi/Makefile
X	cp $l/other/Makefile.ansi   $l/other/Makefile
X	cp $l/curses/Makefile.ansi  $l/curses/Makefile
X	cp $l/posix/Makefile.ansi   $l/posix/Makefile
X	cp $l/rts/Makefile.ansi     $l/rts/Makefile
X	cp $c/simple/Makefile.ansi  $c/simple/Makefile
X	cp $c/ibm/Makefile.ansi     $c/ibm/Makefile
X
X	# Install as kernel assembler files
X	cp $s/kernel/klib88.x.ansi  $s/kernel/klib.x
X	cp $s/kernel/mpx88.x.ansi   $s/kernel/mpx.x
X	cp $s/kernel/rs2.x.ansi     $s/kernel/rs2.x
X	cp $s/kernel/sconst.h.ansi  $s/kernel/sconst.h
X
X	# Install tools assembler files
X	cp $s/tools/bootblk.s.ansi  $s/tools/bootblk.s
X	cp $s/tools/monhead.s.ansi  $s/tools/monhead.s
X
X	# Install lib/syscall and lib/ibm assembler files
X	rm -rf $l/syscall $l/ibm
X	cpdir $l/syscall.ansi       $l/syscall
X	cpdir $l/ibm.ansi           $l/ibm
X	chown ast $l/syscall $l/syscall/* 
X	chown ast $l/ibm $l/ibm/*
X
X	# Fix up Commands Makefiles
X	cd $c
X	for i in advent/Makefile bawk/Makefile bc/Makefile de/Makefile \
X	    dis88/Makefile elle/Makefile ibm/Makefile ic/Makefile \
X	    indent/Makefile m4/Makefile make/Makefile mined/Makefile \
X	    nroff/Makefile patch/Makefile sh/Makefile zmodem/Makefile 
X	do sed <$i '/O=s/s//O=o/' >tmp
X	   cp tmp $i
X	done
X
X	# Install C compiler
X	cp /usr/bin/cc.ansi	    /bin/cc
X	cp /usr/lib/ncpp	    /lib/ncpp
X	cd /lib
X	rm -f cpp *head* *crtso* cem opt cg libc.a *.s
X
X	ln ncpp			    cpp
X	cp /usr/lib/ncem	    ncem
X	cp /usr/lib/nopt	    nopt
X	cp /usr/lib/ncg		    ncg
X	cp /usr/lib/nhead.o	    nhead.o
X	cp /usr/lib/ncrtso.o	    ncrtso.o
X	;;
X
X*)
X	echo "Usage: $0 [kr | ansi]"
X	exit 
Xesac
X
Xrm -rf /usr/include
Xcpdir /include /usr/include
Xchown ast /usr/include /usr/include/*
/
echo x - tools.cd
sed '/^X/s///' > tools.cd << '/'
Xecho x - init.c.d
Xsed '/^X/s///' > init.c.d << '/'
XX*** /home/top/ast/minix/1.5/tools/init.c  crc=12821  12765	Sat Apr 21 22:26:26 1990
XX--- /home/top/ast/minix/1.6.25/tools/init.c  crc=34138  13872	Tue Nov  3 21:23:08 1992
XX***************
XX*** 33,39 ****
XX--- 33,41 ----
XX   * up new shell processes if necessary.  It will not, however, kill off
XX   * login processes for lines that have been turned off; do this manually.
XX   */
XX+ 
XX  #include <sys/types.h>
XX+ #include <sys/wait.h>
XX  #include <fcntl.h>
XX  #include <limits.h>
XX  #include <signal.h>
XX***************
XX*** 43,48 ****
XX--- 45,54 ----
XX  #include <unistd.h>
XX  #include <utmp.h>
XX  
XX+ #define FORWARD		static
XX+ #define PRIVATE		static
XX+ #define PUBLIC
XX+ 
XX  #define CONSNAME	"/dev/tty0"	/* system console device */
XX  
XX  #define SHELL1		"/bin/sh"
XX***************
XX*** 59,64 ****
XX--- 65,73 ----
XX  #define EXIT_EXECFAIL	254		/* child couldn't exec something */
XX  #define EXIT_OPENFAIL	255		/* child couldn't open something */
XX  
XX+ #define HANG            1		/* hang after error */
XX+ #define NOHANG		2		/* do not hang after error */
XX+ 
XX  struct uart {
XX    int baud;
XX    int flags;
XX***************
XX*** 90,96 ****
XX  struct slotent {
XX    int onflag;			/* should this ttyslot be on? */
XX    int pid;			/* pid of login process for this tty line */
XX!   int exit;			/* eit status of child */
XX    char name[8];			/* name of this tty */
XX    int flags;			/* sg_flags field for this tty */
XX    int speed;			/* sg_ispeed for this tty */
XX--- 99,105 ----
XX  struct slotent {
XX    int onflag;			/* should this ttyslot be on? */
XX    int pid;			/* pid of login process for this tty line */
XX!   int exit;			/* exit status of child */
XX    char name[8];			/* name of this tty */
XX    int flags;			/* sg_flags field for this tty */
XX    int speed;			/* sg_ispeed for this tty */
XX***************
XX*** 101,137 ****
XX  char stack[STACKSIZE];		/* init's stack */
XX  char *stackpt = &stack[STACKSIZE];
XX  char **environ;			/* declaration required by library routines */
XX- extern int errno;
XX  
XX  char *CONSOLE = CONSNAME;	/* name of system console */
XX  struct sgttyb args;		/* buffer for TIOCGETP */
XX! int gothup = 0;			/* flag, showing signal 1 was recieved */
XX  int pidct = 0;			/* count of running children */
XX  
XX  char *env[] = { (char *)0 };	/* tiny environment for execle */
XX  
XX! main()
XX  {
XX    int pid;			/* pid of child process */
XX    int fd;			/* fd of console for error messages */
XX    int i;			/* loop variable */
XX    int status;			/* return status from child process */
XX    struct slotent *slotp;	/* slots[] pointer */
XX-   void onhup();			/* SIGHUP interrupt catch routine */
XX  
XX    sync();			/* force buffers out onto disk */
XX  
XX    /* Execute the /etc/rc file. */
XX!   if(fork()) {
XX  	/* Parent just waits. */
XX! 	wait(&status);
XX    } else {
XX  	/* Child exec's the shell to do the work. */
XX! 	if(open("/etc/rc", 0) < 0) exit(EXIT_OPENFAIL);
XX! 	dup(open(CONSOLE, 1));	/* std output, error */
XX! 	execle(SHELL1, SHELL1, (char *)0, env);
XX! 	execle(SHELL2, SHELL2, (char *)0, env);
XX! 	exit(EXIT_EXECFAIL);	/* impossible, we hope */
XX    }
XX  
XX    /* Log system reboot. */
XX--- 110,158 ----
XX  char stack[STACKSIZE];		/* init's stack */
XX  char *stackpt = &stack[STACKSIZE];
XX  char **environ;			/* declaration required by library routines */
XX  
XX  char *CONSOLE = CONSNAME;	/* name of system console */
XX  struct sgttyb args;		/* buffer for TIOCGETP */
XX! int gothup = 0;			/* flag, showing signal 1 was received */
XX  int pidct = 0;			/* count of running children */
XX  
XX  char *env[] = { (char *)0 };	/* tiny environment for execle */
XX  
XX! _PROTOTYPE( int main, (void)						);
XX! _PROTOTYPE( char *_sbrk, (int incr)					);
XX! _PROTOTYPE( char *sbrk, (int incr)					);
XX! 
XX! FORWARD _PROTOTYPE( void error, (char *s, int n)			);
XX! FORWARD _PROTOTYPE( void wtmp, (char *user, char *id, char *line,
XX! 				int pid, int type, int lineno)		);
XX! FORWARD _PROTOTYPE( void readttys, (void)				);
XX! FORWARD _PROTOTYPE( void startup, (int linenr, int mode1, int mode2)	);
XX! FORWARD _PROTOTYPE( void onhup, (int s)					);
XX! 
XX! PUBLIC int main()
XX  {
XX    int pid;			/* pid of child process */
XX    int fd;			/* fd of console for error messages */
XX    int i;			/* loop variable */
XX    int status;			/* return status from child process */
XX    struct slotent *slotp;	/* slots[] pointer */
XX  
XX    sync();			/* force buffers out onto disk */
XX  
XX    /* Execute the /etc/rc file. */
XX!   if(pid = fork()) {
XX  	/* Parent just waits. */
XX! 	while (wait(&status) != pid)
XX! 		;
XX    } else {
XX  	/* Child exec's the shell to do the work. */
XX! 	open(CONSOLE, O_RDWR);		/* std input */
XX! 	dup(0);				/* std output */
XX! 	dup(0);				/* std error */
XX! 	execle(SHELL1, "sh", "/etc/rc", (char *)0, env);
XX! 	execle(SHELL2, "sh", "/etc/rc", (char *)0, env);
XX! 	error("unable to exec shell to run /etc/rc", NOHANG);
XX! 	_exit(EXIT_EXECFAIL);	/* impossible, we hope */
XX    }
XX  
XX    /* Log system reboot. */
XX***************
XX*** 162,168 ****
XX  			    slotp->exit = status;
XX  
XX  			    if(((status >> 8) & 0xFF) == EXIT_TTYFAIL) {
XX! 				fd = open(CONSOLE, 1);
XX  				write(fd, "init: tty problems, shutting down ", 39);
XX  				write(fd, slotp->name, sizeof slotp->name);
XX  				write(fd, "\n", 1);
XX--- 183,189 ----
XX  			    slotp->exit = status;
XX  
XX  			    if(((status >> 8) & 0xFF) == EXIT_TTYFAIL) {
XX! 				fd = open(CONSOLE, O_WRONLY);
XX  				write(fd, "init: tty problems, shutting down ", 39);
XX  				write(fd, slotp->name, sizeof slotp->name);
XX  				write(fd, "\n", 1);
XX***************
XX*** 184,201 ****
XX  	/* See which lines need a login process started up. */
XX  	for(slotp = slots; slotp < &slots[PIDSLOTS]; ++slotp) {
XX  		if(slotp->onflag && slotp->pid <= 0)
XX! 			startup(slotp - slots, DEAD_PROCESS, LOGIN_PROCESS);
XX  	}
XX    }
XX  }
XX  
XX! void onhup()
XX  {
XX    gothup = 1;
XX    signal(SIGHUP, onhup);
XX  }
XX  
XX! readttys()
XX  {
XX    /* (Re)read /etc/ttys. */
XX  
XX--- 205,223 ----
XX  	/* See which lines need a login process started up. */
XX  	for(slotp = slots; slotp < &slots[PIDSLOTS]; ++slotp) {
XX  		if(slotp->onflag && slotp->pid <= 0)
XX! 		      startup((int)(slotp-slots), DEAD_PROCESS, LOGIN_PROCESS);
XX  	}
XX    }
XX  }
XX  
XX! PRIVATE void onhup(s)
XX! int s;				/* ANSI C requires a parameter */
XX  {
XX    gothup = 1;
XX    signal(SIGHUP, onhup);
XX  }
XX  
XX! PRIVATE void readttys()
XX  {
XX    /* (Re)read /etc/ttys. */
XX  
XX***************
XX*** 206,215 ****
XX    struct slotent *slotp = slots;	/* entry in slots[] */
XX    char *q;				/* pointer for copying ttyname */
XX  
XX!   if((fd = open("/etc/ttys", 0)) < 0) {
XX! 	write(open(CONSOLE, 1), "init: can't open /etc/ttys\n", 27);
XX! 	while (1) ;		/* just hang -- system cannot be started */
XX!   }
XX  
XX    /* Read /etc/ttys file. */
XX    endp = (p = ttys) + read(fd, ttys, TTYSBUF);
XX--- 228,235 ----
XX    struct slotent *slotp = slots;	/* entry in slots[] */
XX    char *q;				/* pointer for copying ttyname */
XX  
XX!   if((fd = open("/etc/ttys", O_RDONLY)) < 0) 
XX! 	error("cannot open /etc/ttys", HANG);
XX  
XX    /* Read /etc/ttys file. */
XX    endp = (p = ttys) + read(fd, ttys, TTYSBUF);
XX***************
XX*** 268,274 ****
XX    close(fd);
XX  }
XX  
XX! startup(linenr, mode1, mode2)
XX  int linenr;
XX  int mode1;
XX  int mode2;
XX--- 288,294 ----
XX    close(fd);
XX  }
XX  
XX! PRIVATE void startup(linenr, mode1, mode2)
XX  int linenr;
XX  int mode1;
XX  int mode2;
XX***************
XX*** 294,309 ****
XX  	strcpy(line, "/dev/");			/* part of device name */
XX  	strncat(line, slotp->name, sizeof (slotp->name)); /* rest of name */
XX  
XX! 	if( open(line, 2) != 0 ) exit(EXIT_TTYFAIL);	/* standard input */
XX! 	if(	   dup(0) != 1 ) exit(EXIT_TTYFAIL);	/* standard output */
XX! 	if( 	   dup(1) != 2 ) exit(EXIT_TTYFAIL);	/* standard error */
XX  
XX  	/* Set line parameters. */
XX  
XX! 	if(ioctl(0, TIOCGETP, &args) < 0) exit(EXIT_TTYFAIL);
XX  	args.sg_ispeed = args.sg_ospeed = slotp->speed;
XX  	args.sg_flags = slotp->flags;
XX! 	if(ioctl(0, TIOCSETP, &args) < 0) exit(EXIT_TTYFAIL);
XX  
XX  	/* Try to exec GETTY first if needed.  Call it with "-k CONSOLE" if
XX  	 * the line is the console.  This causes GETTY to skip the speed 
XX--- 314,329 ----
XX  	strcpy(line, "/dev/");			/* part of device name */
XX  	strncat(line, slotp->name, sizeof (slotp->name)); /* rest of name */
XX  
XX! 	if( open(line, O_RDWR) != 0 ) _exit(EXIT_TTYFAIL);  /* standard input*/
XX! 	if(dup(0) != 1 ) _exit(EXIT_TTYFAIL);	/* standard output */
XX! 	if(dup(1) != 2 ) _exit(EXIT_TTYFAIL);	/* standard error */
XX  
XX  	/* Set line parameters. */
XX  
XX! 	if(ioctl(0, TIOCGETP, &args) < 0) _exit(EXIT_TTYFAIL);
XX  	args.sg_ispeed = args.sg_ospeed = slotp->speed;
XX  	args.sg_flags = slotp->flags;
XX! 	if(ioctl(0, TIOCSETP, &args) < 0) _exit(EXIT_TTYFAIL);
XX  
XX  	/* Try to exec GETTY first if needed.  Call it with "-k CONSOLE" if
XX  	 * the line is the console.  This causes GETTY to skip the speed 
XX***************
XX*** 312,319 ****
XX  	 */
XX  	if (slotp->onflag == 2) {
XX  		if (linenr == 0) {
XX! 			execle(GETTY, GETTY, line, "-k", "CONSOLE", (char *)0,
XX! 			       env);
XX  		 } else {
XX  			execle(GETTY, GETTY, line, (char *)0, env);
XX  		}
XX--- 332,338 ----
XX  	 */
XX  	if (slotp->onflag == 2) {
XX  		if (linenr == 0) {
XX! 			execle(GETTY, GETTY,line,"-k","CONSOLE",(char *)0,env);
XX  		 } else {
XX  			execle(GETTY, GETTY, line, (char *)0, env);
XX  		}
XX***************
XX*** 327,339 ****
XX  	execle(SHELL1, SHELL1, (char *)0, env);
XX  	execle(SHELL2, SHELL2, (char *)0, env);
XX  
XX! 	write(open(CONSOLE, 1), "init: couldn't exec login\n", 26);
XX! 	exit(EXIT_EXECFAIL);
XX    }
XX  }
XX  
XX  
XX! wtmp(user, id, line, pid, type, lineno)
XX  char *user;			/* name of user */
XX  char *id;			/* inittab ID */
XX  char *line;			/* TTY name */
XX--- 346,358 ----
XX  	execle(SHELL1, SHELL1, (char *)0, env);
XX  	execle(SHELL2, SHELL2, (char *)0, env);
XX  
XX! 	error("cannot exec login", NOHANG);
XX! 	_exit(EXIT_EXECFAIL);
XX    }
XX  }
XX  
XX  
XX! PRIVATE void wtmp(user, id, line, pid, type, lineno)
XX  char *user;			/* name of user */
XX  char *id;			/* inittab ID */
XX  char *line;			/* TTY name */
XX***************
XX*** 381,389 ****
XX    }
XX  }
XX  
XX! char *sbrk(incr)
XX  int incr;
XX  {
XX  /* One-off sbrk to allocate memory for execle.  The stack and heap are not set
XX   * up right for the library sbrk.
XX   */
XX--- 400,416 ----
XX    }
XX  }
XX  
XX! PUBLIC char *sbrk(incr)
XX  int incr;
XX  {
XX+ /* Version of _sbrk for old libraries. */
XX+ 
XX+   return _sbrk(incr);
XX+ }
XX+ 
XX+ PUBLIC char *_sbrk(incr)
XX+ int incr;
XX+ {
XX  /* One-off sbrk to allocate memory for execle.  The stack and heap are not set
XX   * up right for the library sbrk.
XX   */
XX***************
XX*** 398,406 ****
XX     */
XX    new_brk = old_brk + incr;
XX    if (new_brk > (char *) some_memory + sizeof (some_memory) ||
XX!       new_brk < (char *) some_memory)
XX  	return((char *) -1);
XX    result = old_brk;
XX    old_brk = new_brk;
XX    return(result);
XX  }
XX--- 425,451 ----
XX     */
XX    new_brk = old_brk + incr;
XX    if (new_brk > (char *) some_memory + sizeof (some_memory) ||
XX!       new_brk < (char *) some_memory) {
XX  	return((char *) -1);
XX+   }	
XX    result = old_brk;
XX    old_brk = new_brk;
XX    return(result);
XX+ }
XX+ 
XX+ PRIVATE void error(s, n)
XX+ char *s;			/* string to print */
XX+ int n;				/* HANG, NOHANG */
XX+ {
XX+ /* Something awful has happened. */
XX+ 
XX+   int fd;
XX+ 
XX+   fd = open(CONSOLE, O_WRONLY);
XX+   write(fd, "init: ", 6);
XX+   write(fd, s, strlen(s));
XX+   write(fd, "\n", 1);
XX+   close(fd);
XX+   if (n == NOHANG) return;
XX+   while(1);			/* hang forever */
XX  }
X/
/
