xref: /original-bsd/sys/i386/i386/locore.s (revision 68d9582f)
1/*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * William Jolitz.
7 *
8 * %sccs.include.redist.c%
9 *
10 *	@(#)locore.s	7.8 (Berkeley) 05/20/92
11 */
12
13#include "assym.s"
14#include "machine/psl.h"
15#include "machine/pte.h"
16
17#include "errno.h"
18
19#include "machine/trap.h"
20
21#include "npx.h"
22
23/*
24 * Note: This version greatly munged to avoid various assembler errors
25 * that may be fixed in newer versions of gas. Perhaps newer versions
26 * will have more pleasant appearance.
27 */
28
29	.set	IDXSHIFT,10
30	.set	SYSTEM,0xFE000000	# virtual address of system start
31	/*note: gas copys sign bit (e.g. arithmetic >>), can't do SYSTEM>>22! */
32	.set	SYSPDROFF,0x3F8		# Page dir index of System Base
33
34/* IBM "compatible" nop - sensitive macro on "fast" 386 machines */
35#define	NOP	;
36
37/*
38 * PTmap is recursive pagemap at top of virtual address space.
39 * Within PTmap, the page directory can be found (third indirection).
40 */
41	.set	PDRPDROFF,0x3F7		# Page dir index of Page dir
42	.globl	_PTmap, _PTD, _PTDpde
43	.set	_PTmap,0xFDC00000
44	.set	_PTD,0xFDFF7000
45	.set	_PTDpde,0xFDFF7000+4*PDRPDROFF
46
47/*
48 * APTmap, APTD is the alternate recursive pagemap.
49 * It's used when modifying another process's page tables.
50 */
51	.set	APDRPDROFF,0x3FE		# Page dir index of Page dir
52	.globl	_APTmap, _APTD, _APTDpde
53	.set	_APTmap,0xFF800000
54	.set	_APTD,0xFFBFE000
55	.set	_APTDpde,0xFDFF7000+4*APDRPDROFF
56
57/*
58 * Access to each processes kernel stack is via a region of
59 * per-process address space (at the beginning), immediatly above
60 * the user process stack.
61 */
62	.set	_kstack, USRSTACK
63	.globl	_kstack
64	.set	PPDROFF,0x3F6
65	.set	PPTEOFF,0x400-UPAGES	# 0x3FE
66
67#define	ENTRY(name) \
68	.globl _/**/name; _/**/name:
69#define	ALTENTRY(name) \
70	.globl _/**/name; _/**/name:
71
72/*
73 * Initialization
74 */
75	.data
76	.globl	_cpu,_cold,_boothowto,_bootdev,_cyloffset,_atdevbase,_atdevphys
77_cpu:	.long	0		# are we 386, 386sx, or 486
78_cold:	.long	1		# cold till we are not
79_atdevbase:	.long	0	# location of start of iomem in virtual
80_atdevphys:	.long	0	# location of device mapping ptes (phys)
81
82	.globl	_IdlePTD, _KPTphys
83_IdlePTD:	.long	0
84_KPTphys:	.long	0
85
86pcb:
87	.space 8192
88tmpstk:
89pcb2:
90	.space 8192
91tmpstk2:
92	.text
93	.globl	start
94 #start:
95	.set start,0
96	movw	$0x1234,%ax
97	movw	%ax,0x472	# warm boot
98	jmp	1f
99	.space	0x500		# skip over warm boot shit
100
101	/* enable a20! yecchh!! - move this to bootstrap? */
1021:	inb	$0x64,%al
103	andb	$2,%al
104	jnz	1b
105	movb	$0xd1,%al
106	NOP
107	outb	%al,$0x64
108	NOP
1091:	inb	$0x64,%al
110	andb	$2,%al
111	jnz	1b
112	movb	$0xdf,%al
113	NOP
114	outb	%al,$0x60
115
116	/*
117	 * pass parameters on stack (howto, bootdev, unit, cyloffset)
118	 * note: 0(%esp) is return address of boot
119	 * ( if we want to hold onto /boot, it's physical %esp up to _end)
120	 */
121
122 1:	movl	4(%esp),%eax
123	movl	%eax,_boothowto-SYSTEM
124	movl	8(%esp),%eax
125	movl	%eax,_bootdev-SYSTEM
126	movl	12(%esp),%eax
127	movl	%eax, _cyloffset-SYSTEM
128
129	/* count up memory */
130
131	xorl	%edx,%edx		# start with base memory at 0x0
132	#movl	$ 0xA0000/NBPG,%ecx	# look every 4K up to 640K
133	movl	$ 0xA0,%ecx		# look every 4K up to 640K
1341:	movl	0(%edx),%ebx		# save location to check
135	movl	$0xa55a5aa5,0(%edx)	# write test pattern
136
137	inb	$0x84,%al		# flush write buffer
138	/* flush stupid cache here! (with bcopy (0,0,512*1024) ) */
139
140	cmpl	$0xa55a5aa5,0(%edx)	# does not check yet for rollover
141	jne	2f
142	movl	%ebx,0(%edx)		# restore memory
143	addl	$ NBPG,%edx
144	loop	1b
145
146	movl	$0x100000,%edx		# next, talley remaining memory
147	#movl	$((0xFFF000-0x100000)/NBPG),%ecx
148	movl	$(0xFFF-0x100),%ecx
1491:	movl	0(%edx),%ebx		# save location to check
150	movl	$0xa55a5aa5,0(%edx)	# write test pattern
151	cmpl	$0xa55a5aa5,0(%edx)	# does not check yet for rollover
152	jne	2f
153	movl	%ebx,0(%edx)		# restore memory
154	addl	$ NBPG,%edx
155	loop	1b
1562:	shrl	$12,%edx
157	movl	%edx,_Maxmem-SYSTEM
158
159/* find end of kernel image */
160	movl	$_end-SYSTEM,%ecx
161	addl	$ NBPG-1,%ecx
162	andl	$~(NBPG-1),%ecx
163	movl	%ecx,%esi
164
165/* clear bss and memory for bootstrap pagetables. */
166	movl	$_edata-SYSTEM,%edi
167	subl	%edi,%ecx
168	addl	$(UPAGES+5)*NBPG,%ecx
169/*
170 * Virtual address space of kernel:
171 *
172 *	text | data | bss | page dir | proc0 kernel stack | usr stk map | Sysmap
173 *			     0               1       2       3             4
174 */
175	xorl	%eax,%eax	# pattern
176	cld
177	rep
178	stosb
179
180	movl	%esi,_IdlePTD-SYSTEM /*physical address of Idle Address space */
181	movl	$ tmpstk-SYSTEM,%esp	# bootstrap stack end location
182
183#define	fillkpt		\
1841:	movl	%eax,0(%ebx)	; \
185	addl	$ NBPG,%eax	; /* increment physical address */ \
186	addl	$4,%ebx		; /* next pte */ \
187	loop	1b		;
188
189/*
190 * Map Kernel
191 * N.B. don't bother with making kernel text RO, as 386
192 * ignores R/W AND U/S bits on kernel access (only v works) !
193 *
194 * First step - build page tables
195 */
196	movl	%esi,%ecx		# this much memory,
197	shrl	$ PGSHIFT,%ecx		# for this many pte s
198	addl	$ UPAGES+4,%ecx		# including our early context
199	movl	$ PG_V,%eax		#  having these bits set,
200	lea	(4*NBPG)(%esi),%ebx	#   physical address of KPT in proc 0,
201	movl	%ebx,_KPTphys-SYSTEM	#    in the kernel page table,
202	fillkpt
203
204/* map I/O memory map */
205
206	movl	$0x100-0xa0,%ecx	# for this many pte s,
207	movl	$(0xa0000|PG_V),%eax	#  having these bits set, (perhaps URW?)
208	movl	%ebx,_atdevphys-SYSTEM	#   remember phys addr of ptes
209	fillkpt
210
211 /* map proc 0's kernel stack into user page table page */
212
213	movl	$ UPAGES,%ecx		# for this many pte s,
214	lea	(1*NBPG)(%esi),%eax	# physical address in proc 0
215	lea	(SYSTEM)(%eax),%edx
216	movl	%edx,_proc0paddr-SYSTEM  # remember VA for 0th process init
217	orl	$ PG_V|PG_URKW,%eax	#  having these bits set,
218	lea	(3*NBPG)(%esi),%ebx	# physical address of stack pt in proc 0
219	addl	$(PPTEOFF*4),%ebx
220	fillkpt
221
222/*
223 * Construct a page table directory
224 * (of page directory elements - pde's)
225 */
226	/* install a pde for temporary double map of bottom of VA */
227	lea	(4*NBPG)(%esi),%eax	# physical address of kernel page table
228	orl	$ PG_V,%eax		# pde entry is valid
229	movl	%eax,(%esi)		# which is where temp maps!
230
231	/* kernel pde's */
232	movl	$ 3,%ecx		# for this many pde s,
233	lea	(SYSPDROFF*4)(%esi), %ebx	# offset of pde for kernel
234	fillkpt
235
236	/* install a pde recursively mapping page directory as a page table! */
237	movl	%esi,%eax		# phys address of ptd in proc 0
238	orl	$ PG_V,%eax		# pde entry is valid
239	movl	%eax, PDRPDROFF*4(%esi)	# which is where PTmap maps!
240
241	/* install a pde to map kernel stack for proc 0 */
242	lea	(3*NBPG)(%esi),%eax	# physical address of pt in proc 0
243	orl	$ PG_V,%eax		# pde entry is valid
244	movl	%eax,PPDROFF*4(%esi)	# which is where kernel stack maps!
245
246	/* load base of page directory, and enable mapping */
247	movl	%esi,%eax		# phys address of ptd in proc 0
248 	orl	$ I386_CR3PAT,%eax
249	movl	%eax,%cr3		# load ptd addr into mmu
250	movl	%cr0,%eax		# get control word
251	orl	$0x80000001,%eax	# and let s page!
252	movl	%eax,%cr0		# NOW!
253
254	pushl	$begin				# jump to high mem!
255	ret
256
257begin: /* now running relocated at SYSTEM where the system is linked to run */
258
259	.globl _Crtat
260	movl	_Crtat,%eax
261	subl	$0xfe0a0000,%eax
262	movl	_atdevphys,%edx	# get pte PA
263	subl	_KPTphys,%edx	# remove base of ptes, now have phys offset
264	shll	$ PGSHIFT-2,%edx  # corresponding to virt offset
265	addl	$ SYSTEM,%edx	# add virtual base
266	movl	%edx, _atdevbase
267	addl	%eax,%edx
268	movl	%edx,_Crtat
269
270	/* set up bootstrap stack */
271	movl	$ _kstack+UPAGES*NBPG-4*12,%esp	# bootstrap stack end location
272	xorl	%eax,%eax		# mark end of frames
273	movl	%eax,%ebp
274	movl	_proc0paddr, %eax
275	movl	%esi, PCB_CR3(%eax)
276
277	lea	7*NBPG(%esi),%esi	# skip past stack.
278	pushl	%esi
279
280	call	_init386		# wire 386 chip for unix operation
281	popl	%esi
282
283	movl	$0,_PTD
284	call 	_main
285
286	.globl	__ucodesel,__udatasel
287	movzwl	__ucodesel,%eax
288	movzwl	__udatasel,%ecx
289	# build outer stack frame
290	pushl	%ecx		# user ss
291	pushl	$ USRSTACK	# user esp
292	pushl	%eax		# user cs
293	pushl	$0		# user ip
294	movw	%cx,%ds
295	movw	%cx,%es
296	# movw	%ax,%fs		# double map cs to fs
297	# movw	%cx,%gs		# and ds to gs
298	lret	# goto user!
299
300	pushl	$lretmsg1	/* "should never get here!" */
301	call	_panic
302lretmsg1:
303	.asciz	"lret: toinit\n"
304
305	.globl	__exit
306__exit:
307	call _reset_cpu
308	/* NOTREACHED */
309
310	.set	exec,59
311	.set	exit,1
312	.globl	_icode
313	.globl	_szicode
314
315#define	LCALL(x,y)	.byte 0x9a ; .long y; .word x
316/*
317 * Icode is copied out to process 1 to exec /etc/init.
318 * If the exec fails, process 1 exits.
319 */
320_icode:
321	# pushl	$argv-_icode	# gas fucks up again
322	movl	$argv,%eax
323	subl	$_icode,%eax
324	pushl	%eax
325
326	# pushl	$init-_icode
327	movl	$init,%eax
328	subl	$_icode,%eax
329	pushl	%eax
330	pushl	%eax	# dummy out rta
331
332	movl	%esp,%ebp
333	movl	$exec,%eax
334	LCALL(0x7,0x0)
335	pushl	%eax
336	movl	$exit,%eax
337	pushl	%eax	# dummy out rta
338	LCALL(0x7,0x0)
339
340init:
341	.asciz	"/sbin/init"
342	.align	2
343argv:
344	.long	init+6-_icode		# argv[0] = "init" ("/sbin/init" + 6)
345	.long	eicode-_icode		# argv[1] follows icode after copyout
346	.long	0
347eicode:
348
349_szicode:
350	.long	_szicode-_icode
351
352	.globl	_sigcode,_szsigcode
353_sigcode:
354	movl	12(%esp),%eax	# unsure if call will dec stack 1st
355	call	%eax
356	xorl	%eax,%eax	# smaller movl $103,%eax
357	movb	$103,%al	# sigreturn()
358	LCALL(0x7,0)		# enter kernel with args on stack
359	hlt			# never gets here
360
361_szsigcode:
362	.long	_szsigcode-_sigcode
363
364	.globl ___udivsi3
365___udivsi3:
366	movl 4(%esp),%eax
367	xorl %edx,%edx
368	divl 8(%esp)
369	ret
370
371	.globl ___divsi3
372___divsi3:
373	movl 4(%esp),%eax
374	xorl %edx,%edx
375	cltd
376	idivl 8(%esp)
377	ret
378
379	.globl	_inb
380_inb:	movl	4(%esp),%edx
381	# inb	$0x84,%al	# Compaq SystemPro
382	subl	%eax,%eax	# clr eax
383	NOP
384	inb	%dx,%al
385	NOP
386	ret
387
388
389	.globl	_rtcin
390_rtcin:	movl	4(%esp),%eax
391	outb	%al,$0x70
392	subl	%eax,%eax	# clr eax
393	inb	$0x71,%al	# Compaq SystemPro
394	ret
395
396	.globl	_outb
397_outb:	movl	4(%esp),%edx
398	movl	8(%esp),%eax
399	NOP
400	outb	%al,%dx
401	# inb	$0x84,%al
402	NOP
403	ret
404
405	#
406	# bzero (base,cnt)
407	#
408
409	.globl _bzero
410	.globl _blkclr
411_bzero:
412_blkclr:
413	pushl	%edi
414	movl	8(%esp),%edi
415	movl	12(%esp),%ecx
416	xorl	%eax,%eax
417	shrl	$2,%ecx
418	cld
419	rep
420	stosl
421	movl	12(%esp),%ecx
422	andl	$3,%ecx
423	rep
424	stosb
425	popl	%edi
426	ret
427
428	#
429	# fillw (pat,base,cnt)
430	#
431
432	.globl _fillw
433_fillw:
434	pushl	%edi
435	movl	8(%esp),%eax
436	movl	12(%esp),%edi
437	movl	16(%esp),%ecx
438	cld
439	rep
440	stosw
441	popl	%edi
442	ret
443
444	#
445	# bcopy (src,dst,cnt)
446	# NOTE: does not (yet) handle overlapped copies
447	#
448
449	.globl	_bcopy
450_bcopy:
451	pushl	%esi
452	pushl	%edi
453	movl	12(%esp),%esi
454	movl	16(%esp),%edi
455	movl	20(%esp),%ecx
456	shrl	$2,%ecx
457	cld
458	rep
459	movsl
460	movl	20(%esp),%ecx
461	andl	$3,%ecx
462	rep
463	movsb
464	popl	%edi
465	popl	%esi
466	xorl	%eax,%eax
467	ret
468
469	#
470	# ovbcopy (src,dst,cnt)
471	# NOTE: does not (yet) work doing words at a time
472	#
473
474	.globl	_ovbcopy
475_ovbcopy:
476	pushl	%esi
477	pushl	%edi
478	movl	12(%esp),%esi
479	movl	16(%esp),%edi
480	movl	20(%esp),%ecx
481	addl	%ecx,%esi	/* copy from end to beginning */
482	addl	%ecx,%edi
483	decl	%esi
484	decl	%edi
485	std			/* decrementing as we go */
486	rep
487	movsb
488	popl	%edi
489	popl	%esi
490	xorl	%eax,%eax
491	cld
492	ret
493
494	.globl	_copyout
495_copyout:
496	movl	_curpcb,%eax
497	movl	$cpyflt,PCB_ONFAULT(%eax) # in case we page/protection violate
498	pushl	%esi
499	pushl	%edi
500	movl	12(%esp),%esi
501	movl	16(%esp),%edi
502	movl	20(%esp),%ecx
503	shrl	$2,%ecx
504	cld
505	rep
506	movsl
507	movl	20(%esp),%ecx
508	andl	$3,%ecx
509	rep
510	movsb
511	popl	%edi
512	popl	%esi
513	xorl	%eax,%eax
514	movl	_curpcb,%edx
515	movl	%eax,PCB_ONFAULT(%edx)
516	ret
517
518	.globl	_copyin
519_copyin:
520	movl	_curpcb,%eax
521	movl	$cpyflt,PCB_ONFAULT(%eax) # in case we page/protection violate
522	pushl	%esi
523	pushl	%edi
524	movl	12(%esp),%esi
525	movl	16(%esp),%edi
526	movl	20(%esp),%ecx
527	shrl	$2,%ecx
528	cld
529	rep
530	movsl
531	movl	20(%esp),%ecx
532	andl	$3,%ecx
533	rep
534	movsb
535	popl	%edi
536	popl	%esi
537	xorl	%eax,%eax
538	movl	_curpcb,%edx
539	movl	%eax,PCB_ONFAULT(%edx)
540	ret
541
542cpyflt: popl	%edi
543	popl	%esi
544	movl	_curpcb,%edx
545	movl	$0,PCB_ONFAULT(%edx)
546	movl	$ EFAULT,%eax
547	ret
548
549	# insb(port,addr,cnt)
550	.globl	_insb
551_insb:
552	pushl	%edi
553	movw	8(%esp),%dx
554	movl	12(%esp),%edi
555	movl	16(%esp),%ecx
556	cld
557	NOP
558	rep
559	insb
560	NOP
561	movl	%edi,%eax
562	popl	%edi
563	ret
564
565	# insw(port,addr,cnt)
566	.globl	_insw
567_insw:
568	pushl	%edi
569	movw	8(%esp),%dx
570	movl	12(%esp),%edi
571	movl	16(%esp),%ecx
572	cld
573	NOP
574	.byte 0x66,0xf2,0x6d	# rep insw
575	NOP
576	movl	%edi,%eax
577	popl	%edi
578	ret
579
580	# outsw(port,addr,cnt)
581	.globl	_outsw
582_outsw:
583	pushl	%esi
584	movw	8(%esp),%dx
585	movl	12(%esp),%esi
586	movl	16(%esp),%ecx
587	cld
588	NOP
589	.byte 0x66,0xf2,0x6f	# rep outsw
590	NOP
591	movl	%esi,%eax
592	popl	%esi
593	ret
594
595	# lgdt(*gdt, ngdt)
596	.globl	_lgdt
597	# .globl	_gdt
598xxx:	.word 31
599	.long 0
600_lgdt:
601	movl	4(%esp),%eax
602	movl	%eax,xxx+2
603	movl	8(%esp),%eax
604	movw	%ax,xxx
605	lgdt	xxx
606	jmp	1f
607	NOP
6081:	movw	$0x10,%ax
609	movw	%ax,%ds
610	movw	%ax,%es
611	movw	%ax,%ss
612	movl	0(%esp),%eax
613	pushl	%eax
614	movl	$8,4(%esp)
615	lret
616
617	# lidt(*idt, nidt)
618	.globl	_lidt
619yyy:	.word	255
620	.long	0
621_lidt:
622	movl	4(%esp),%eax
623	movl	%eax,yyy+2
624	movl	8(%esp),%eax
625	movw	%ax,yyy
626	lidt	yyy
627	ret
628
629	# lldt(sel)
630	.globl	_lldt
631_lldt:
632	movl	4(%esp),%eax
633	lldt	%eax
634	ret
635
636	# ltr(sel)
637	.globl	_ltr
638_ltr:
639	movl	4(%esp),%eax
640	ltr	%eax
641	ret
642
643	# lcr3(cr3)
644	.globl	_lcr3
645	.globl	_load_cr3
646_load_cr3:
647_lcr3:
648	inb	$0x84,%al	# check wristwatch
649	movl	4(%esp),%eax
650 	orl	$ I386_CR3PAT,%eax
651
652	movl	$tmpstk2,%edx
653	movl	(%edx),%ecx	# touch stack, fault if not there
654	movl	%ecx,(%edx)
655	movl	%esp,%ecx
656	movl	%edx,%esp
657
658	movl	%eax,%cr3
659	inb	$0x84,%al	# check wristwatch
660
661	movl	(%ecx),%edx	# touch stack, fault if not there
662	movl	%edx,(%ecx)
663	movl	%ecx,%esp
664	ret
665
666	# tlbflush()
667	.globl	_tlbflush
668_tlbflush:
669	inb	$0x84,%al	# check wristwatch
670	movl	%cr3,%eax
671 	orl	$ I386_CR3PAT,%eax
672
673	movl	$tmpstk2,%edx
674	movl	(%edx),%ecx	# touch stack, fault if not there
675	movl	%ecx,(%edx)
676	movl	%esp,%ecx
677	movl	%edx,%esp
678
679	movl	%eax,%cr3
680	inb	$0x84,%al	# check wristwatch
681
682	movl	(%ecx),%edx	# touch stack, fault if not there
683	movl	%edx,(%ecx)
684	movl	%ecx,%esp
685	ret
686
687	# lcr0(cr0)
688	.globl	_lcr0,_load_cr0
689_lcr0:
690_load_cr0:
691	movl	4(%esp),%eax
692	movl	%eax,%cr0
693	ret
694
695	# rcr0()
696	.globl	_rcr0
697_rcr0:
698	movl	%cr0,%eax
699	ret
700
701	# rcr2()
702	.globl	_rcr2
703_rcr2:
704	movl	%cr2,%eax
705	ret
706
707	# rcr3()
708	.globl	_rcr3
709	.globl	__cr3
710__cr3:
711_rcr3:
712	movl	%cr3,%eax
713	ret
714
715	# ssdtosd(*ssdp,*sdp)
716	.globl	_ssdtosd
717_ssdtosd:
718	pushl	%ebx
719	movl	8(%esp),%ecx
720	movl	8(%ecx),%ebx
721	shll	$16,%ebx
722	movl	(%ecx),%edx
723	roll	$16,%edx
724	movb	%dh,%bl
725	movb	%dl,%bh
726	rorl	$8,%ebx
727	movl	4(%ecx),%eax
728	movw	%ax,%dx
729	andl	$0xf0000,%eax
730	orl	%eax,%ebx
731	movl	12(%esp),%ecx
732	movl	%edx,(%ecx)
733	movl	%ebx,4(%ecx)
734	popl	%ebx
735	ret
736
737/*
738 * {fu,su},{byte,word}
739 */
740ALTENTRY(fuiword)
741ENTRY(fuword)
742	movl	_curpcb,%ecx
743	movl	$fusufault,PCB_ONFAULT(%ecx)
744	movl	4(%esp),%edx
745	# .byte	0x65		# use gs
746	movl	0(%edx),%eax
747	movl	$0,PCB_ONFAULT(%ecx)
748	ret
749
750ENTRY(fusword)
751	movl	_curpcb,%ecx
752	movl	$fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate
753	movl	4(%esp),%edx
754	# .byte	0x65		# use gs
755	movzwl	0(%edx),%eax
756	movl	$0,PCB_ONFAULT(%ecx)
757	ret
758
759ALTENTRY(fuibyte)
760ENTRY(fubyte)
761	movl	_curpcb,%ecx
762	movl	$fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate
763	movl	4(%esp),%edx
764	# .byte	0x65		# use gs
765	movzbl	0(%edx),%eax
766	movl	$0,PCB_ONFAULT(%ecx)
767	ret
768
769fusufault:
770	movl	_curpcb,%ecx
771	xorl	%eax,%eax
772	movl	%eax,PCB_ONFAULT(%ecx) #in case we page/protection violate
773	decl	%eax
774	ret
775
776ALTENTRY(suiword)
777ENTRY(suword)
778	movl	_curpcb,%ecx
779	movl	$fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate
780	movl	4(%esp),%edx
781	movl	8(%esp),%eax
782	# .byte	0x65		# use gs
783	movl	%eax,0(%edx)
784	xorl	%eax,%eax
785	movl	%eax,PCB_ONFAULT(%ecx) #in case we page/protection violate
786	ret
787
788ENTRY(susword)
789	movl	_curpcb,%ecx
790	movl	$fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate
791	movl	4(%esp),%edx
792	movl	8(%esp),%eax
793	# .byte	0x65		# use gs
794	movw	%ax,0(%edx)
795	xorl	%eax,%eax
796	movl	%eax,PCB_ONFAULT(%ecx) #in case we page/protection violate
797	ret
798
799ALTENTRY(suibyte)
800ENTRY(subyte)
801	movl	_curpcb,%ecx
802	movl	$fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate
803	movl	4(%esp),%edx
804	movl	8(%esp),%eax
805	# .byte	0x65		# use gs
806	movb	%eax,0(%edx)
807	xorl	%eax,%eax
808	movl	%eax,PCB_ONFAULT(%ecx) #in case we page/protection violate
809	ret
810
811	ENTRY(setjmp)
812	movl	4(%esp),%eax
813	movl	%ebx, 0(%eax)		# save ebx
814	movl	%esp, 4(%eax)		# save esp
815	movl	%ebp, 8(%eax)		# save ebp
816	movl	%esi,12(%eax)		# save esi
817	movl	%edi,16(%eax)		# save edi
818	movl	(%esp),%edx		# get rta
819	movl	%edx,20(%eax)		# save eip
820	xorl	%eax,%eax		# return (0);
821	ret
822
823#ifdef notdef
824	ENTRY(longjmp)
825	movl	4(%esp),%eax
826	movl	 0(%eax),%ebx		# restore ebx
827	movl	 4(%eax),%esp		# restore esp
828	movl	 8(%eax),%ebp		# restore ebp
829	movl	12(%eax),%esi		# restore esi
830	movl	16(%eax),%edi		# restore edi
831	movl	20(%eax),%edx		# get rta
832	movl	%edx,(%esp)		# put in return frame
833	xorl	%eax,%eax		# return (1);
834	incl	%eax
835	ret
836#endif
837/*
838 * The following primitives manipulate the run queues.
839 * _whichqs tells which of the 32 queues _qs
840 * have processes in them.  Setrq puts processes into queues, Remrq
841 * removes them from queues.  The running process is on no queue,
842 * other processes are on a queue related to p->p_pri, divided by 4
843 * actually to shrink the 0-127 range of priorities into the 32 available
844 * queues.
845 */
846
847	.globl	_whichqs,_qs,_cnt,_panic
848	.comm	_noproc,4
849	.comm	_runrun,4
850
851/*
852 * Setrq(p)
853 *
854 * Call should be made at spl6(), and p->p_stat should be SRUN
855 */
856ENTRY(setrq)
857	movl	4(%esp),%eax
858	cmpl	$0,P_RLINK(%eax)	# should not be on q already
859	je	set1
860	pushl	$set2
861	call	_panic
862set1:
863	movzbl	P_PRI(%eax),%edx
864	shrl	$2,%edx
865	btsl	%edx,_whichqs		# set q full bit
866	shll	$3,%edx
867	addl	$_qs,%edx		# locate q hdr
868	movl	%edx,P_LINK(%eax)	# link process on tail of q
869	movl	P_RLINK(%edx),%ecx
870	movl	%ecx,P_RLINK(%eax)
871	movl	%eax,P_RLINK(%edx)
872	movl	%eax,P_LINK(%ecx)
873	ret
874
875set2:	.asciz	"setrq"
876
877/*
878 * Remrq(p)
879 *
880 * Call should be made at spl6().
881 */
882ENTRY(remrq)
883	movl	4(%esp),%eax
884	movzbl	P_PRI(%eax),%edx
885	shrl	$2,%edx
886	btrl	%edx,_whichqs		# clear full bit, panic if clear already
887	jb	rem1
888	pushl	$rem3
889	call	_panic
890rem1:
891	pushl	%edx
892	movl	P_LINK(%eax),%ecx	# unlink process
893	movl	P_RLINK(%eax),%edx
894	movl	%edx,P_RLINK(%ecx)
895	movl	P_RLINK(%eax),%ecx
896	movl	P_LINK(%eax),%edx
897	movl	%edx,P_LINK(%ecx)
898	popl	%edx
899	movl	$_qs,%ecx
900	shll	$3,%edx
901	addl	%edx,%ecx
902	cmpl	P_LINK(%ecx),%ecx	# q still has something?
903	je	rem2
904	shrl	$3,%edx			# yes, set bit as still full
905	btsl	%edx,_whichqs
906rem2:
907	movl	$0,P_RLINK(%eax)	# zap reverse link to indicate off list
908	ret
909
910rem3:	.asciz	"remrq"
911sw0:	.asciz	"swtch"
912
913/*
914 * When no processes are on the runq, Swtch branches to idle
915 * to wait for something to come ready.
916 */
917	.globl	Idle
918Idle:
919idle:
920	call	_spl0
921	cmpl	$0,_whichqs
922	jne	sw1
923	hlt		# wait for interrupt
924	jmp	idle
925
926badsw:
927	pushl	$sw0
928	call	_panic
929	/*NOTREACHED*/
930
931/*
932 * Swtch()
933 */
934ENTRY(swtch)
935
936	incl	_cnt+V_SWTCH
937
938	/* switch to new process. first, save context as needed */
939
940	movl	_curproc,%ecx
941	movl	P_ADDR(%ecx),%ecx
942
943
944	movl	(%esp),%eax		# Hardware registers
945	movl	%eax, PCB_EIP(%ecx)
946	movl	%ebx, PCB_EBX(%ecx)
947	movl	%esp, PCB_ESP(%ecx)
948	movl	%ebp, PCB_EBP(%ecx)
949	movl	%esi, PCB_ESI(%ecx)
950	movl	%edi, PCB_EDI(%ecx)
951
952#if 0 && NNPX > 0
953	movb	PCB_FLAGS(%ecx),%al
954	/* have we used fp, and need a save? */
955	andb	$ FP_WASUSED|FP_NEEDSSAVE,%al
956	cmpb	$ FP_WASUSED|FP_NEEDSSAVE,%al
957	jne	1f
958	movl	%cr0,%eax		/* insure fp is enabled */
959	andb 	$0xfb,%al
960	movl	%eax,%cr0
961	fnsave	PCB_SAVEFPU(%ecx)
962	orb 	$4,%al			/* disable it */
963	movl	%eax,%cr0
964	movb	PCB_FLAGS(%ecx),%al
965	xorb	$ FP_NEEDSSAVE,%al	/* save processed */
966	movb	%al,PCB_FLAGS(%ecx)
9671:
968#endif
969
970	movl	_CMAP2,%eax		# save temporary map PTE
971	movl	%eax,PCB_CMAP2(%ecx)	# in our context
972
973	movw	_cpl, %ax
974	movw	%ax, PCB_IML(%ecx)	# save ipl
975
976	movl	$tmpstk2,%edx
977	movl	(%edx),%eax	# touch stack, fault if not there
978	movl	%eax,(%edx)
979	movl	%edx,%esp
980	movl	$pcb2,_curpcb
981
982	/* save is done, now choose a new process or idle */
983sw1:
984	cli				# XXX?
985	movl	_whichqs,%edi
9862:
987	bsfl	%edi,%eax		# find a full q
988	jz	idle			# if none, idle
989	# XX update whichqs?
990swfnd:
991	btrl	%eax,%edi		# clear q full status
992	jnb	2b		# if it was clear, look for another
993	movl	%eax,%ebx		# save which one we are using
994
995	shll	$3,%eax
996	addl	$_qs,%eax		# select q
997	movl	%eax,%esi
998
999#ifdef	DIAGNOSTIC
1000	cmpl	P_LINK(%eax),%eax # linked to self? (e.g. not on list)
1001	je	badsw			# not possible
1002#endif
1003
1004	movl	P_LINK(%eax),%ecx	# unlink from front of process q
1005	movl	P_LINK(%ecx),%edx
1006	movl	%edx,P_LINK(%eax)
1007	movl	P_RLINK(%ecx),%eax
1008	movl	%eax,P_RLINK(%edx)
1009
1010	cmpl	P_LINK(%ecx),%esi	# q empty
1011	je	3f
1012	btsl	%ebx,%edi		# nope, set to indicate full
10133:
1014	movl	%edi,_whichqs		# update q status
1015
1016	movl	$0,%eax
1017	movl	%ecx,_curproc
1018	movl	%eax,_want_resched
1019
1020#ifdef	DIAGNOSTIC
1021	cmpl	%eax,P_WCHAN(%ecx)
1022	jne	badsw
1023	cmpb	$ SRUN,P_STAT(%ecx)
1024	jne	badsw
1025#endif
1026
1027	movl	%eax,P_RLINK(%ecx) /* isolate process to run */
1028	movl	P_ADDR(%ecx),%edx
1029	movl	%edx,_curpcb
1030	inb	$0x84,%al	# flush write buffers
1031	movl	PCB_CR3(%edx),%ebx
1032
1033	/* switch address space */
1034	cli
1035 	orl	$ I386_CR3PAT,%ebx
1036	movl	%ebx,%cr3	# context switch address space
1037
1038	jmp	7f
1039	nop
1040 7:	inb	$0x84,%al	# flush write buffers
1041	movl	PCB_ESP(%edx), %ecx
1042	movl	(%ecx),%eax	# touch stack, fault if not there
1043	movl	%eax,(%ecx)
1044	movl	%ecx,%esp
1045
1046	/* restore context */
1047	movl	PCB_EBX(%edx), %ebx
1048	movl	PCB_ESP(%edx), %esp
1049	movl	PCB_EBP(%edx), %ebp
1050	movl	PCB_ESI(%edx), %esi
1051	movl	PCB_EDI(%edx), %edi
1052	movl	PCB_EIP(%edx), %eax
1053	movl	%eax, (%esp)
1054
1055#if NNPX > 0
1056#ifdef notdef
1057	movb	PCB_FLAGS(%edx),%al
1058	/* if fp could be used, a dna trap will do a restore */
1059	testb	$ FP_WASUSED,%al
1060	je	1f
1061	orb	$ FP_NEEDSRESTORE,PCB_FLAGS(%ecx)
10621:
1063#endif
1064	movl	%cr0,%eax
1065	orb 	$4,%al			/* disable it */
1066	movl	%eax,%cr0
1067#endif
1068
1069	movl	PCB_CMAP2(%edx),%eax	# get temporary map
1070	movl	%eax,_CMAP2		# reload temporary map PTE
1071
1072	pushl	PCB_IML(%edx)
1073	call	_splx
1074	popl	%eax
1075
1076	movl	%edx,%eax		# return (1);
1077	ret
1078
1079/*
1080 * struct proc *swtch_to_inactive(p) ; struct proc *p;
1081 *
1082 * At exit of a process, move off the address space of the
1083 * process and onto a "safe" one. Then, on a temporary stack
1084 * return and run code that disposes of the old state.
1085 * Since this code requires a parameter from the "old" stack,
1086 * pass it back as a return value.
1087 */
1088ENTRY(swtch_to_inactive)
1089
1090	movl	$tmpstk2-4,%ecx		# temporary stack, compensated for call
1091	movl	(%ecx),%eax		# touch stack, fault if not there
1092	movl	%eax,(%ecx)
1093
1094	popl	%edx			# old pc
1095	popl	%eax			# arg, our return value
1096	inb	$0x84,%al	# flush write buffers
1097
1098	movl	%ecx,%esp
1099
1100	movl	_IdlePTD,%ecx
1101
1102	movl	%ecx,%cr3		# good bye address space
1103	inb	$0x84,%al	# flush write buffers
1104
1105 #write buffer?
1106	movl	$pcb2,_curpcb
1107	jmp	%edx			# return, execute remainder of cleanup
1108
1109/*
1110 * savectx(pcb, altreturn)
1111 * Update pcb, saving current processor state and arranging
1112 * for alternate return ala longjmp in swtch if altreturn is true.
1113 */
1114ENTRY(savectx)
1115	movl	4(%esp), %ecx
1116	movw	_cpl, %ax
1117	movw	%ax,  PCB_IML(%ecx)
1118	movl	(%esp), %eax
1119	movl	%eax, PCB_EIP(%ecx)
1120	movl	%ebx, PCB_EBX(%ecx)
1121	movl	%esp, PCB_ESP(%ecx)
1122	movl	%ebp, PCB_EBP(%ecx)
1123	movl	%esi, PCB_ESI(%ecx)
1124	movl	%edi, PCB_EDI(%ecx)
1125#if 0 && NNPX > 0
1126	/* have we ever used fp, and need to save? */
1127	testb	$ FP_WASUSED, PCB_FLAGS(%ecx)
1128	je	1f
1129	movl	%cr0, %edx
1130	andb 	$0xfb, %dl
1131	movl	%edx, %cr0
1132	fnsave	PCB_SAVEFPU(%ecx)
1133	orb 	$4, %edx
1134	movl	%edx, %cr0
11351:
1136#endif
1137	movl	_CMAP2, %edx		# save temporary map PTE
1138	movl	%edx, PCB_CMAP2(%ecx)	# in our context
1139
1140	cmpl	$0, 8(%esp)
1141	je	1f
1142	movl	%esp, %edx		# relocate current sp relative to pcb
1143	subl	$_kstack, %edx		#   (sp is relative to kstack):
1144	addl	%edx, %ecx		#   pcb += sp - kstack;
1145	movl	%eax, (%ecx)		# write return pc at (relocated) sp@
1146	# this mess deals with replicating register state gcc hides
1147	movl	12(%esp),%eax
1148	movl	%eax,12(%ecx)
1149	movl	16(%esp),%eax
1150	movl	%eax,16(%ecx)
1151	movl	20(%esp),%eax
1152	movl	%eax,20(%ecx)
1153	movl	24(%esp),%eax
1154	movl	%eax,24(%ecx)
11551:
1156	xorl	%eax, %eax		# return 0
1157	ret
1158
1159	.globl	_mvesp
1160_mvesp:	movl	%esp,%eax
1161	ret
1162
1163/*
1164 * update profiling information for the user
1165 * addupc(pc, up, ticks) struct uprof *up;
1166 */
1167
1168ENTRY(addupc)
1169	movl	4(%esp),%eax		/* pc */
1170	movl	8(%esp),%ecx		/* up */
1171
1172	/* does sampled pc fall within bottom of profiling window? */
1173	subl	PR_OFF(%ecx),%eax 	/* pc -= up->pr_off; */
1174	jl	1f 			/* if (pc < 0) return; */
1175
1176	/* construct scaled index */
1177	shrl	$1,%eax			/* reduce pc to a short index */
1178	mull	PR_SCALE(%ecx)		/* pc*up->pr_scale */
1179	shrdl	$15,%edx,%eax 		/* praddr >> 15 */
1180	cmpl	$0,%edx			/* if overflow, ignore */
1181	jne	1f
1182	andb	$0xfe,%al		/* praddr &= ~1 */
1183
1184	/* within profiling buffer? if so, compute address */
1185	cmpl	%eax,PR_SIZE(%ecx)	/* if (praddr > up->pr_size) return; */
1186	jg	1f
1187	addl	PR_BASE(%ecx),%eax	/* praddr += up->pr_base; */
1188
1189	/* tally ticks to selected counter */
1190	movl	_curpcb,%ecx
1191	movl	$proffault,PCB_ONFAULT(%ecx) #in case we page/protection violate
1192	movl	12(%esp),%edx		/* ticks */
1193	addw	%dx,(%eax)
1194	movl	$0,PCB_ONFAULT(%ecx)
11951:	ret
1196
1197proffault:
1198	/* disable profiling if we get a fault */
1199	movl	$0,PR_SCALE(%ecx) /*	up->pr_scale = 0; */
1200	movl	_curpcb,%ecx
1201	movl	$0,PCB_ONFAULT(%ecx)
1202	ret
1203
1204.data
1205	.globl	_cyloffset, _curpcb
1206_cyloffset:	.long	0
1207	.globl	_proc0paddr
1208_proc0paddr:	.long	0
1209LF:	.asciz "swtch %x"
1210
1211.text
1212	.globl _astoff
1213_astoff:
1214	movl	$0,_astpending
1215	ret
1216
1217#define	IDTVEC(name)	.align 4; .globl _X/**/name; _X/**/name:
1218#define	PANIC(msg)	xorl %eax,%eax; movl %eax,_waittime; pushl 1f; \
1219			call _panic; 1: .asciz msg
1220#define	PRINTF(n,msg)	pushal ; pushl 1f; call _printf; MSG(msg) ; \
1221			 popl %eax ; popal
1222#define	MSG(msg)	.data; 1: .asciz msg; .text
1223
1224	.text
1225
1226/*
1227 * Trap and fault vector routines
1228 */
1229#define	TRAP(a)		pushl $a ; jmp alltraps
1230#ifdef KGDB
1231#define	BPTTRAP(a)	pushl $a ; jmp bpttraps
1232#else
1233#define	BPTTRAP(a)	TRAP(a)
1234#endif
1235
1236IDTVEC(div)
1237	pushl $0; TRAP(T_DIVIDE)
1238IDTVEC(dbg)
1239	pushl $0; BPTTRAP(T_TRCTRAP)
1240IDTVEC(nmi)
1241	pushl $0; TRAP(T_NMI)
1242IDTVEC(bpt)
1243	pushl $0; BPTTRAP(T_BPTFLT)
1244IDTVEC(ofl)
1245	pushl $0; TRAP(T_OFLOW)
1246IDTVEC(bnd)
1247	pushl $0; TRAP(T_BOUND)
1248IDTVEC(ill)
1249	pushl $0; TRAP(T_PRIVINFLT)
1250IDTVEC(dna)
1251	pushl $0; TRAP(T_DNA)
1252IDTVEC(dble)
1253	TRAP(T_DOUBLEFLT)
1254	/*PANIC("Double Fault");*/
1255IDTVEC(fpusegm)
1256	pushl $0; TRAP(T_FPOPFLT)
1257IDTVEC(tss)
1258	TRAP(T_TSSFLT)
1259	/*PANIC("TSS not valid");*/
1260IDTVEC(missing)
1261	TRAP(T_SEGNPFLT)
1262IDTVEC(stk)
1263	TRAP(T_STKFLT)
1264IDTVEC(prot)
1265	TRAP(T_PROTFLT)
1266IDTVEC(page)
1267	TRAP(T_PAGEFLT)
1268IDTVEC(rsvd)
1269	pushl $0; TRAP(T_RESERVED)
1270IDTVEC(fpu)
1271	pushl $0; TRAP(T_ARITHTRAP)
1272	/* 17 - 31 reserved for future exp */
1273IDTVEC(rsvd0)
1274	pushl $0; TRAP(17)
1275IDTVEC(rsvd1)
1276	pushl $0; TRAP(18)
1277IDTVEC(rsvd2)
1278	pushl $0; TRAP(19)
1279IDTVEC(rsvd3)
1280	pushl $0; TRAP(20)
1281IDTVEC(rsvd4)
1282	pushl $0; TRAP(21)
1283IDTVEC(rsvd5)
1284	pushl $0; TRAP(22)
1285IDTVEC(rsvd6)
1286	pushl $0; TRAP(23)
1287IDTVEC(rsvd7)
1288	pushl $0; TRAP(24)
1289IDTVEC(rsvd8)
1290	pushl $0; TRAP(25)
1291IDTVEC(rsvd9)
1292	pushl $0; TRAP(26)
1293IDTVEC(rsvd10)
1294	pushl $0; TRAP(27)
1295IDTVEC(rsvd11)
1296	pushl $0; TRAP(28)
1297IDTVEC(rsvd12)
1298	pushl $0; TRAP(29)
1299IDTVEC(rsvd13)
1300	pushl $0; TRAP(30)
1301IDTVEC(rsvd14)
1302	pushl $0; TRAP(31)
1303
1304alltraps:
1305	pushal
1306	push %ds
1307	push %es
1308	movw	$0x10,%ax
1309	movw	%ax,%ds
1310	movw	%ax,%es
1311calltrap:
1312	incl	_cnt+V_TRAP
1313	call	_trap
1314
1315	cli
1316
1317	/* this value may also be used in return_to_user_mode */
1318	movl	0x34(%esp),%esi /* previous cs */
1319	andl	$3,%esi
1320	jz	trap_return
1321
1322	cmpl	$0,_astpending
1323	jnz	do_astflt
1324
1325	cmpw	$0,_cpl
1326	jnz	return_to_user_mode /* in icu.s */
1327
1328trap_return:
1329	pop %es
1330	pop %ds
1331	popal
1332	nop
1333	addl	$8,%esp			# pop type, code
1334	iret
1335
1336do_astflt:
1337	/* pop off the old trap frame, then create a new one that
1338	 * will give trap() another chance
1339	 */
1340	pop	%es
1341	pop	%ds
1342	popa
1343	addl	$8,%esp
1344
1345	pushl	$0
1346	TRAP (T_ASTFLT)
1347	/* NORETURN */
1348
1349
1350
1351#ifdef KGDB
1352/*
1353 * This code checks for a kgdb trap, then falls through
1354 * to the regular trap code.
1355 */
1356bpttraps:
1357	pushal
1358	push	%es
1359	push	%ds
1360	movw	$0x10,%ax
1361	movw	%ax,%ds
1362	movw	%ax,%es
1363	movzwl	52(%esp),%eax
1364	test	$3,%eax
1365	jne	calltrap
1366	call	_kgdb_trap_glue
1367	jmp	calltrap
1368#endif
1369
1370/*
1371 * Call gate entry for syscall
1372 */
1373
1374IDTVEC(syscall)
1375	pushfl	# only for stupid carry bit and more stupid wait3 cc kludge
1376	pushal	# only need eax,ecx,edx - trap resaves others
1377	movw	$0x10,%ax	# switch to kernel segments
1378	movw	%ax,%ds
1379	movw	%ax,%es
1380	call	_syscall
1381
1382	cli
1383
1384	cmpl	$0,_astpending
1385	jnz	syscall_ast
1386
1387	cmpw	$0,_cpl
1388	jnz	syscall_fix_cpl
1389
1390	movw	__udatasel,%ax	# switch back to user segments
1391	movw	%ax,%ds
1392	movw	%ax,%es
1393	popal
1394	nop
1395	popfl
1396	lret
1397
1398syscall_ast:
1399	movw	__udatasel,%ax
1400	movw	%ax,%ds
1401	movw	%ax,%es
1402	popal
1403
1404	/* convert to trap frame
1405	 * stack is now ss, sp, cs,    ip, flags
1406	 * we want      ss, sp, flags, cs, ip
1407 	 * offsets      16  12  8      4   0
1408	 */
1409	xchgl	%eax,8(%esp) /* now eax has cs */
1410	xchgl	%eax,4(%esp) /* now eax has ip */
1411	xchgl	%eax,0(%esp) /* now eax has flags */
1412	xchgl	%eax,8(%esp) /* now eax has its original value */
1413	pushl $0
1414	TRAP (T_ASTFLT)
1415	/* NORETURN */
1416
1417syscall_fix_cpl:
1418	movw	__udatasel,%ax
1419	movw	%ax,%ds
1420	movw	%ax,%es
1421	popal
1422
1423	/* convert to trap frame
1424	 * stack is now ss, sp, cs,    ip, flags
1425	 * we want      ss, sp, flags, cs, ip
1426 	 * offsets      16  12  8      4   0
1427	 */
1428	xchgl	%eax,8(%esp) /* now eax has cs */
1429	xchgl	%eax,4(%esp) /* now eax has ip */
1430	xchgl	%eax,0(%esp) /* now eax has flags */
1431	xchgl	%eax,8(%esp) /* now eax has its original value */
1432
1433	pushl $0
1434	pushl $ T_ASTFLT
1435
1436	pushal
1437	nop
1438	push %ds
1439	push %es
1440	movw	$0x10,%ax
1441	movw	%ax,%ds
1442	movw	%ax,%es
1443
1444	movl	$1, %esi /* non-zero to indicate return to user mode */
1445	jmp	return_to_user_mode /* in icu.s */
1446
1447
1448ENTRY(htonl)
1449ENTRY(ntohl)
1450	movl	4(%esp),%eax
1451	xchgb	%al,%ah
1452	roll	$16,%eax
1453	xchgb	%al,%ah
1454	ret
1455
1456ENTRY(htons)
1457ENTRY(ntohs)
1458	movzwl	4(%esp),%eax
1459	xchgb	%al,%ah
1460	ret
1461
1462/* DELAY(n)  delay about n microseconds */
1463ENTRY(DELAY)
1464	movl 4(%esp), %ecx
1465	incl %ecx /* make DELAY(0) go through the loop just once */
1466
1467	/*
1468	 * 0x80 is the manufacturing test port, which should be safe to
1469	 * write to on any motherboard.  The output instruction will
1470	 * be executed at bus speed, rather than processor speed, so
1471	 * it will be about 750ns on any ISA or EISA machine.
1472	 */
14731:
1474	outb %al, $0x80
1475	loop 1b
1476	ret
1477
1478#include "vector.s"
1479#include "i386/isa/icu.s"
1480