xref: /plan9-contrib/sys/src/9/pc/l.s (revision 61d44851dbae9c6db4696bac4b180d884ecea735)
1#include "mem.h"
2#include "/sys/src/boot/pc/x16.h"
3#undef DELAY
4
5#define PADDR(a)	((a) & ~KZERO)
6#define KADDR(a)	(KZERO|(a))
7
8/*
9 * Some machine instructions not handled by 8[al].
10 */
11#define OP16		BYTE $0x66
12#define DELAY		BYTE $0xEB; BYTE $0x00	/* JMP .+2 */
13#define CPUID		BYTE $0x0F; BYTE $0xA2	/* CPUID, argument in AX */
14#define WRMSR		BYTE $0x0F; BYTE $0x30	/* WRMSR, argument in AX/DX (lo/hi) */
15#define RDTSC 		BYTE $0x0F; BYTE $0x31	/* RDTSC, result in AX/DX (lo/hi) */
16#define RDMSR		BYTE $0x0F; BYTE $0x32	/* RDMSR, result in AX/DX (lo/hi) */
17#define HLT		BYTE $0xF4
18#define INVLPG	BYTE $0x0F; BYTE $0x01; BYTE $0x39	/* INVLPG (%ecx) */
19#define WBINVD	BYTE $0x0F; BYTE $0x09
20#define FXSAVE		BYTE $0x0f; BYTE $0xae; BYTE $0x00  /* SSE FP save */
21#define FXRSTOR		BYTE $0x0f; BYTE $0xae; BYTE $0x08  /* SSE FP restore */
22
23/*
24 * Macros for calculating offsets within the page directory base
25 * and page tables. Note that these are assembler-specific hence
26 * the '<<2'.
27 */
28#define PDO(a)		(((((a))>>22) & 0x03FF)<<2)
29#define PTO(a)		(((((a))>>12) & 0x03FF)<<2)
30
31/*
32 * For backwards compatiblity with 9load - should go away when 9load is changed
33 * 9load currently sets up the mmu, however the first 16MB of memory is identity
34 * mapped, so behave as if the mmu was not setup
35 */
36TEXT _startKADDR(SB), $0
37	MOVL	$_startPADDR(SB), AX
38	ANDL	$~KZERO, AX
39	JMP*	AX
40
41/*
42 * Must be 4-byte aligned.
43 */
44TEXT _multibootheader(SB), $0
45	LONG	$0x1BADB002			/* magic */
46	LONG	$0x00010003			/* flags */
47	LONG	$-(0x1BADB002 + 0x00010003)	/* checksum */
48	LONG	$_multibootheader-KZERO(SB)	/* header_addr */
49	LONG	$_startKADDR-KZERO(SB)		/* load_addr */
50	LONG	$edata-KZERO(SB)		/* load_end_addr */
51	LONG	$end-KZERO(SB)			/* bss_end_addr */
52	LONG	$_startKADDR-KZERO(SB)		/* entry_addr */
53	LONG	$0				/* mode_type */
54	LONG	$0				/* width */
55	LONG	$0				/* height */
56	LONG	$0				/* depth */
57
58/*
59 * In protected mode with paging turned off and segment registers setup
60 * to linear map all memory. Entered via a jump to PADDR(entry),
61 * the physical address of the virtual kernel entry point of KADDR(entry).
62 * Make the basic page tables for processor 0. Six pages are needed for
63 * the basic set:
64 *	a page directory;
65 *	page tables for mapping the first 8MB of physical memory to KZERO;
66 *	a page for the GDT;
67 *	virtual and physical pages for mapping the Mach structure.
68 * The remaining PTEs will be allocated later when memory is sized.
69 * An identity mmu map is also needed for the switch to virtual mode.
70 * This identity mapping is removed once the MMU is going and the JMP has
71 * been made to virtual memory.
72 */
73TEXT _startPADDR(SB), $0
74	CLI					/* make sure interrupts are off */
75
76	/* set up the gdt so we have sane plan 9 style gdts. */
77	MOVL	$tgdtptr(SB), AX
78	ANDL	$~KZERO, AX
79	MOVL	(AX), GDTR
80	MOVW	$1, AX
81	MOVW	AX, MSW
82
83	/* clear prefetch queue (weird code to avoid optimizations) */
84	DELAY
85
86	/* set segs to something sane (avoid traps later) */
87	MOVW	$(1<<3), AX
88	MOVW	AX, DS
89	MOVW	AX, SS
90	MOVW	AX, ES
91	MOVW	AX, FS
92	MOVW	AX, GS
93
94/*	JMP	$(2<<3):$mode32bit(SB) /**/
95	 BYTE	$0xEA
96	 LONG	$mode32bit-KZERO(SB)
97	 WORD	$(2<<3)
98
99/*
100 *  gdt to get us to 32-bit/segmented/unpaged mode
101 */
102TEXT tgdt(SB), $0
103
104	/* null descriptor */
105	LONG	$0
106	LONG	$0
107
108	/* data segment descriptor for 4 gigabytes (PL 0) */
109	LONG	$(0xFFFF)
110	LONG	$(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
111
112	/* exec segment descriptor for 4 gigabytes (PL 0) */
113	LONG	$(0xFFFF)
114	LONG	$(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
115
116/*
117 *  pointer to initial gdt
118 *  Note the -KZERO which puts the physical address in the gdtptr.
119 *  that's needed as we start executing in physical addresses.
120 */
121TEXT tgdtptr(SB), $0
122	WORD	$(3*8)
123	LONG	$tgdt-KZERO(SB)
124
125TEXT m0rgdtptr(SB), $0
126	WORD	$(NGDT*8-1)
127	LONG	$(CPU0GDT-KZERO)
128
129TEXT m0gdtptr(SB), $0
130	WORD	$(NGDT*8-1)
131	LONG	$CPU0GDT
132
133TEXT m0idtptr(SB), $0
134	WORD $(256*8-1)
135	LONG $IDTADDR
136
137TEXT mode32bit(SB), $0
138	/* At this point, the GDT setup is done. */
139
140	MOVL	$PADDR(CPU0PDB), DI		/* clear 4 pages for the tables etc. */
141	XORL	AX, AX
142	MOVL	$(4*BY2PG), CX
143	SHRL	$2, CX
144
145	CLD
146	REP;	STOSL
147
148	MOVL	$PADDR(CPU0PDB), AX
149	ADDL	$PDO(KZERO), AX			/* page directory offset for KZERO */
150	MOVL	$PADDR(CPU0PTE), (AX)		/* PTE's for KZERO */
151	MOVL	$(PTEWRITE|PTEVALID), BX	/* page permissions */
152	ORL	BX, (AX)
153
154	ADDL	$4, AX
155	MOVL	$PADDR(CPU0PTE1), (AX)		/* PTE's for KZERO+4MB */
156	MOVL	$(PTEWRITE|PTEVALID), BX	/* page permissions */
157	ORL	BX, (AX)
158
159	MOVL	$PADDR(CPU0PTE), AX		/* first page of page table */
160	MOVL	$1024, CX			/* 1024 pages in 4MB */
161_setpte:
162	MOVL	BX, (AX)
163	ADDL	$(1<<PGSHIFT), BX
164	ADDL	$4, AX
165	LOOP	_setpte
166
167	MOVL	$PADDR(CPU0PTE1), AX		/* second page of page table */
168	MOVL	$1024, CX			/* 1024 pages in 4MB */
169_setpte1:
170	MOVL	BX, (AX)
171	ADDL	$(1<<PGSHIFT), BX
172	ADDL	$4, AX
173	LOOP	_setpte1
174
175	MOVL	$PADDR(CPU0PTE), AX
176	ADDL	$PTO(MACHADDR), AX		/* page table entry offset for MACHADDR */
177	MOVL	$PADDR(CPU0MACH), (AX)		/* PTE for Mach */
178	MOVL	$(PTEWRITE|PTEVALID), BX	/* page permissions */
179	ORL	BX, (AX)
180
181/*
182 * Now ready to use the new map. Make sure the processor options are what is wanted.
183 * It is necessary on some processors to immediately follow mode switching with a JMP instruction
184 * to clear the prefetch queues.
185 */
186	MOVL	$PADDR(CPU0PDB), CX		/* load address of page directory */
187	MOVL	(PDO(KZERO))(CX), DX		/* double-map KZERO at 0 */
188	MOVL	DX, (PDO(0))(CX)
189	MOVL	CX, CR3
190	DELAY					/* JMP .+2 */
191
192	MOVL	CR0, DX
193	ORL	$0x80010000, DX			/* PG|WP */
194	ANDL	$~0x6000000A, DX		/* ~(CD|NW|TS|MP) */
195
196	MOVL	$_startpg(SB), AX		/* this is a virtual address */
197	MOVL	DX, CR0				/* turn on paging */
198	JMP*	AX				/* jump to the virtual nirvana */
199
200/*
201 * Basic machine environment set, can clear BSS and create a stack.
202 * The stack starts at the top of the page containing the Mach structure.
203 * The x86 architecture forces the use of the same virtual address for
204 * each processor's Mach structure, so the global Mach pointer 'm' can
205 * be initialised here.
206 */
207TEXT _startpg(SB), $0
208	MOVL	$0, (PDO(0))(CX)		/* undo double-map of KZERO at 0 */
209	MOVL	CX, CR3				/* load and flush the mmu */
210
211_clearbss:
212	MOVL	$edata(SB), DI
213	XORL	AX, AX
214	MOVL	$end(SB), CX
215	SUBL	DI, CX				/* end-edata bytes */
216	SHRL	$2, CX				/* end-edata doublewords */
217
218	CLD
219	REP;	STOSL				/* clear BSS */
220
221	MOVL	$MACHADDR, SP
222	MOVL	SP, m(SB)			/* initialise global Mach pointer */
223	MOVL	$0, 0(SP)			/* initialise m->machno */
224
225
226	ADDL	$(MACHSIZE-4), SP		/* initialise stack */
227
228/*
229 * Need to do one final thing to ensure a clean machine environment,
230 * clear the EFLAGS register, which can only be done once there is a stack.
231 */
232	MOVL	$0, AX
233	PUSHL	AX
234	POPFL
235
236	CALL	main(SB)
237
238/*
239 * Park a processor. Should never fall through a return from main to here,
240 * should only be called by application processors when shutting down.
241 */
242TEXT idle(SB), $0
243_idle:
244	STI
245	HLT
246	JMP	_idle
247
248/*
249 * Save registers.
250 */
251TEXT saveregs(SB), $0
252	/* appease 8l */
253	SUBL $32, SP
254	POPL AX
255	POPL AX
256	POPL AX
257	POPL AX
258	POPL AX
259	POPL AX
260	POPL AX
261	POPL AX
262
263	PUSHL	AX
264	PUSHL	BX
265	PUSHL	CX
266	PUSHL	DX
267	PUSHL	BP
268	PUSHL	DI
269	PUSHL	SI
270	PUSHFL
271
272	XCHGL	32(SP), AX	/* swap return PC and saved flags */
273	XCHGL	0(SP), AX
274	XCHGL	32(SP), AX
275	RET
276
277TEXT restoreregs(SB), $0
278	/* appease 8l */
279	PUSHL	AX
280	PUSHL	AX
281	PUSHL	AX
282	PUSHL	AX
283	PUSHL	AX
284	PUSHL	AX
285	PUSHL	AX
286	PUSHL	AX
287	ADDL	$32, SP
288
289	XCHGL	32(SP), AX	/* swap return PC and saved flags */
290	XCHGL	0(SP), AX
291	XCHGL	32(SP), AX
292
293	POPFL
294	POPL	SI
295	POPL	DI
296	POPL	BP
297	POPL	DX
298	POPL	CX
299	POPL	BX
300	POPL	AX
301	RET
302
303/*
304 * Assumed to be in protected mode at time of call.
305 * Switch to real mode, execute an interrupt, and
306 * then switch back to protected mode.
307 *
308 * Assumes:
309 *
310 *	- no device interrupts are going to come in
311 *	- 0-16MB is identity mapped in page tables
312 *	- realmode() has copied us down from 0x100000 to 0x8000
313 *	- can use code segment 0x0800 in real mode
314 *		to get at l.s code
315 *	- l.s code is less than 1 page
316 */
317#define RELOC	(RMCODE-KTZERO)
318
319TEXT realmodeidtptr(SB), $0
320	WORD	$(4*256-1)
321	LONG	$0
322
323TEXT realmode0(SB), $0
324	CALL	saveregs(SB)
325
326	/* switch to low code address */
327	LEAL	physcode-KZERO(SB), AX
328	JMP *AX
329
330TEXT physcode(SB), $0
331
332	/* switch to low stack */
333	MOVL	SP, AX
334	MOVL	$0x7C00, SP
335	PUSHL	AX
336
337	/* change gdt to physical pointer */
338	MOVL	m0rgdtptr-KZERO(SB), GDTR
339
340	/* load IDT with real-mode version*/
341	MOVL	realmodeidtptr-KZERO(SB), IDTR
342
343	/* edit INT $0x00 instruction below */
344	MOVL	$(RMUADDR-KZERO+48), AX	/* &rmu.trap */
345	MOVL	(AX), AX
346	MOVB	AX, realmodeintrinst+(-KZERO+1+RELOC)(SB)
347
348	/* disable paging */
349	MOVL	CR0, AX
350	ANDL	$0x7FFFFFFF, AX
351	MOVL	AX, CR0
352	/* JMP .+2 to clear prefetch queue*/
353	BYTE $0xEB; BYTE $0x00
354
355	/* jump to 16-bit code segment */
356/*	JMPFAR	SELECTOR(KESEG16, SELGDT, 0):$again16bit(SB) /**/
357	 BYTE	$0xEA
358	 LONG	$again16bit-KZERO(SB)
359	 WORD	$SELECTOR(KESEG16, SELGDT, 0)
360
361TEXT again16bit(SB), $0
362	/*
363	 * Now in 16-bit compatibility mode.
364	 * These are 32-bit instructions being interpreted
365	 * as 16-bit instructions.  I'm being lazy and
366	 * not using the macros because I know when
367	 * the 16- and 32-bit instructions look the same
368	 * or close enough.
369	 */
370
371	/* disable protected mode and jump to real mode cs */
372	OPSIZE; MOVL CR0, AX
373	OPSIZE; XORL BX, BX
374	OPSIZE; INCL BX
375	OPSIZE; XORL BX, AX
376	OPSIZE; MOVL AX, CR0
377
378	/* JMPFAR 0x0800:now16real */
379	 BYTE $0xEA
380	 WORD	$now16real-KZERO(SB)
381	 WORD	$0x0800
382
383TEXT now16real(SB), $0
384	/* copy the registers for the bios call */
385	LWI(0x0000, rAX)
386	MOVW	AX,SS
387	LWI(RMUADDR, rBP)
388
389	/* offsets are in Ureg */
390	LXW(44, xBP, rAX)
391	MOVW	AX, DS
392	LXW(40, xBP, rAX)
393	MOVW	AX, ES
394
395	OPSIZE; LXW(0, xBP, rDI)
396	OPSIZE; LXW(4, xBP, rSI)
397	OPSIZE; LXW(16, xBP, rBX)
398	OPSIZE; LXW(20, xBP, rDX)
399	OPSIZE; LXW(24, xBP, rCX)
400	OPSIZE; LXW(28, xBP, rAX)
401
402	CLC
403
404TEXT realmodeintrinst(SB), $0
405	INT $0x00
406	CLI			/* who knows what evil the bios got up to */
407
408	/* save the registers after the call */
409
410	LWI(0x7bfc, rSP)
411	OPSIZE; PUSHFL
412	OPSIZE; PUSHL AX
413
414	LWI(0, rAX)
415	MOVW	AX,SS
416	LWI(RMUADDR, rBP)
417
418	OPSIZE; SXW(rDI, 0, xBP)
419	OPSIZE; SXW(rSI, 4, xBP)
420	OPSIZE; SXW(rBX, 16, xBP)
421	OPSIZE; SXW(rDX, 20, xBP)
422	OPSIZE; SXW(rCX, 24, xBP)
423	OPSIZE; POPL AX
424	OPSIZE; SXW(rAX, 28, xBP)
425
426	MOVW	DS, AX
427	OPSIZE; SXW(rAX, 44, xBP)
428	MOVW	ES, AX
429	OPSIZE; SXW(rAX, 40, xBP)
430
431	OPSIZE; POPL AX
432	OPSIZE; SXW(rAX, 64, xBP)	/* flags */
433
434	/* re-enter protected mode and jump to 32-bit code */
435	OPSIZE; MOVL $1, AX
436	OPSIZE; MOVL AX, CR0
437
438/*	JMPFAR	SELECTOR(KESEG, SELGDT, 0):$again32bit(SB) /**/
439	 OPSIZE
440	 BYTE $0xEA
441	 LONG	$again32bit-KZERO(SB)
442	 WORD	$SELECTOR(KESEG, SELGDT, 0)
443
444TEXT again32bit(SB), $0
445	MOVW	$SELECTOR(KDSEG, SELGDT, 0),AX
446	MOVW	AX,DS
447	MOVW	AX,SS
448	MOVW	AX,ES
449	MOVW	AX,FS
450	MOVW	AX,GS
451
452	/* enable paging and jump to kzero-address code */
453	MOVL	CR0, AX
454	ORL	$0x80010000, AX	/* PG|WP */
455	MOVL	AX, CR0
456	LEAL	again32kzero(SB), AX
457	JMP*	AX
458
459TEXT again32kzero(SB), $0
460	/* breathe a sigh of relief - back in 32-bit protected mode */
461
462	/* switch to old stack */
463	PUSHL	AX	/* match popl below for 8l */
464	MOVL	$0x7BFC, SP
465	POPL	SP
466
467	/* restore idt */
468	MOVL	m0idtptr(SB),IDTR
469
470	/* restore gdt */
471	MOVL	m0gdtptr(SB), GDTR
472
473	CALL	restoreregs(SB)
474	RET
475
476/*
477 * BIOS32.
478 */
479TEXT bios32call(SB), $0
480	MOVL	ci+0(FP), BP
481	MOVL	0(BP), AX
482	MOVL	4(BP), BX
483	MOVL	8(BP), CX
484	MOVL	12(BP), DX
485	MOVL	16(BP), SI
486	MOVL	20(BP), DI
487	PUSHL	BP
488
489	MOVL	12(SP), BP			/* ptr */
490	BYTE $0xFF; BYTE $0x5D; BYTE $0x00	/* CALL FAR 0(BP) */
491
492	POPL	BP
493	MOVL	DI, 20(BP)
494	MOVL	SI, 16(BP)
495	MOVL	DX, 12(BP)
496	MOVL	CX, 8(BP)
497	MOVL	BX, 4(BP)
498	MOVL	AX, 0(BP)
499
500	XORL	AX, AX
501	JCC	_bios32xxret
502	INCL	AX
503
504_bios32xxret:
505	RET
506
507/*
508 * Port I/O.
509 *	in[bsl]		input a byte|short|long
510 *	ins[bsl]	input a string of bytes|shorts|longs
511 *	out[bsl]	output a byte|short|long
512 *	outs[bsl]	output a string of bytes|shorts|longs
513 */
514TEXT inb(SB), $0
515	MOVL	port+0(FP), DX
516	XORL	AX, AX
517	INB
518	RET
519
520TEXT insb(SB), $0
521	MOVL	port+0(FP), DX
522	MOVL	address+4(FP), DI
523	MOVL	count+8(FP), CX
524	CLD
525	REP;	INSB
526	RET
527
528TEXT ins(SB), $0
529	MOVL	port+0(FP), DX
530	XORL	AX, AX
531	OP16;	INL
532	RET
533
534TEXT inss(SB), $0
535	MOVL	port+0(FP), DX
536	MOVL	address+4(FP), DI
537	MOVL	count+8(FP), CX
538	CLD
539	REP;	OP16; INSL
540	RET
541
542TEXT inl(SB), $0
543	MOVL	port+0(FP), DX
544	INL
545	RET
546
547TEXT insl(SB), $0
548	MOVL	port+0(FP), DX
549	MOVL	address+4(FP), DI
550	MOVL	count+8(FP), CX
551	CLD
552	REP;	INSL
553	RET
554
555TEXT outb(SB), $0
556	MOVL	port+0(FP), DX
557	MOVL	byte+4(FP), AX
558	OUTB
559	RET
560
561TEXT outsb(SB), $0
562	MOVL	port+0(FP), DX
563	MOVL	address+4(FP), SI
564	MOVL	count+8(FP), CX
565	CLD
566	REP;	OUTSB
567	RET
568
569TEXT outs(SB), $0
570	MOVL	port+0(FP), DX
571	MOVL	short+4(FP), AX
572	OP16;	OUTL
573	RET
574
575TEXT outss(SB), $0
576	MOVL	port+0(FP), DX
577	MOVL	address+4(FP), SI
578	MOVL	count+8(FP), CX
579	CLD
580	REP;	OP16; OUTSL
581	RET
582
583TEXT outl(SB), $0
584	MOVL	port+0(FP), DX
585	MOVL	long+4(FP), AX
586	OUTL
587	RET
588
589TEXT outsl(SB), $0
590	MOVL	port+0(FP), DX
591	MOVL	address+4(FP), SI
592	MOVL	count+8(FP), CX
593	CLD
594	REP;	OUTSL
595	RET
596
597/*
598 * Read/write various system registers.
599 * CR4 and the 'model specific registers' should only be read/written
600 * after it has been determined the processor supports them
601 */
602TEXT lgdt(SB), $0				/* GDTR - global descriptor table */
603	MOVL	gdtptr+0(FP), AX
604	MOVL	(AX), GDTR
605	RET
606
607TEXT lidt(SB), $0				/* IDTR - interrupt descriptor table */
608	MOVL	idtptr+0(FP), AX
609	MOVL	(AX), IDTR
610	RET
611
612TEXT ltr(SB), $0				/* TR - task register */
613	MOVL	tptr+0(FP), AX
614	MOVW	AX, TASK
615	RET
616
617TEXT getcr0(SB), $0				/* CR0 - processor control */
618	MOVL	CR0, AX
619	RET
620
621TEXT getcr2(SB), $0				/* CR2 - page fault linear address */
622	MOVL	CR2, AX
623	RET
624
625TEXT getcr3(SB), $0				/* CR3 - page directory base */
626	MOVL	CR3, AX
627	RET
628
629TEXT putcr0(SB), $0
630	MOVL	cr0+0(FP), AX
631	MOVL	AX, CR0
632	RET
633
634TEXT putcr3(SB), $0
635	MOVL	cr3+0(FP), AX
636	MOVL	AX, CR3
637	RET
638
639TEXT getcr4(SB), $0				/* CR4 - extensions */
640	MOVL	CR4, AX
641	RET
642
643TEXT putcr4(SB), $0
644	MOVL	cr4+0(FP), AX
645	MOVL	AX, CR4
646	RET
647
648TEXT invlpg(SB), $0
649	/* 486+ only */
650	MOVL	va+0(FP), CX
651	INVLPG
652	RET
653
654TEXT wbinvd(SB), $0
655	WBINVD
656	RET
657
658TEXT _cycles(SB), $0				/* time stamp counter */
659	RDTSC
660	MOVL	vlong+0(FP), CX			/* &vlong */
661	MOVL	AX, 0(CX)			/* lo */
662	MOVL	DX, 4(CX)			/* hi */
663	RET
664
665/*
666 * stub for:
667 * time stamp counter; low-order 32 bits of 64-bit cycle counter
668 * Runs at fasthz/4 cycles per second (m->clkin>>3)
669 */
670TEXT lcycles(SB),1,$0
671	RDTSC
672	RET
673
674TEXT rdmsr(SB), $0				/* model-specific register */
675	MOVL	index+0(FP), CX
676	RDMSR
677	MOVL	vlong+4(FP), CX			/* &vlong */
678	MOVL	AX, 0(CX)			/* lo */
679	MOVL	DX, 4(CX)			/* hi */
680	RET
681
682TEXT wrmsr(SB), $0
683	MOVL	index+0(FP), CX
684	MOVL	lo+4(FP), AX
685	MOVL	hi+8(FP), DX
686	WRMSR
687	RET
688
689/*
690 * Try to determine the CPU type which requires fiddling with EFLAGS.
691 * If the Id bit can be toggled then the CPUID instruction can be used
692 * to determine CPU identity and features. First have to check if it's
693 * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be
694 * toggled then it's an older 486 of some kind.
695 *
696 *	cpuid(fun, regs[4]);
697 */
698TEXT cpuid(SB), $0
699	MOVL	$0x240000, AX
700	PUSHL	AX
701	POPFL					/* set Id|Ac */
702	PUSHFL
703	POPL	BX				/* retrieve value */
704	MOVL	$0, AX
705	PUSHL	AX
706	POPFL					/* clear Id|Ac, EFLAGS initialised */
707	PUSHFL
708	POPL	AX				/* retrieve value */
709	XORL	BX, AX
710	TESTL	$0x040000, AX			/* Ac */
711	JZ	_cpu386				/* can't set this bit on 386 */
712	TESTL	$0x200000, AX			/* Id */
713	JZ	_cpu486				/* can't toggle this bit on some 486 */
714	/* load registers */
715	MOVL	regs+4(FP), BP
716	MOVL	fn+0(FP), AX			/* cpuid function */
717	MOVL	4(BP), BX
718	MOVL	8(BP), CX			/* typically an index */
719	MOVL	12(BP), DX
720	CPUID
721	JMP	_cpuid
722_cpu486:
723	MOVL	$0x400, AX
724	JMP	_maybezapax
725_cpu386:
726	MOVL	$0x300, AX
727_maybezapax:
728	CMPL	fn+0(FP), $1
729	JE	_zaprest
730	XORL	AX, AX
731_zaprest:
732	XORL	BX, BX
733	XORL	CX, CX
734	XORL	DX, DX
735_cpuid:
736	MOVL	regs+4(FP), BP
737	MOVL	AX, 0(BP)
738	MOVL	BX, 4(BP)
739	MOVL	CX, 8(BP)
740	MOVL	DX, 12(BP)
741	RET
742
743/*
744 * Basic timing loop to determine CPU frequency.
745 */
746TEXT aamloop(SB), $0
747	MOVL	count+0(FP), CX
748_aamloop:
749	AAM
750	LOOP	_aamloop
751	RET
752
753/*
754 * Floating point.
755 * Note: the encodings for the FCLEX, FINIT, FSAVE, FSTCW, FSENV and FSTSW
756 * instructions do NOT have the WAIT prefix byte (i.e. they act like their
757 * FNxxx variations) so WAIT instructions must be explicitly placed in the
758 * code as necessary.
759 */
760#define	FPOFF(l)						 ;\
761	MOVL	CR0, AX 					 ;\
762	ANDL	$0xC, AX			/* EM, TS */	 ;\
763	CMPL	AX, $0x8					 ;\
764	JEQ 	l						 ;\
765	WAIT							 ;\
766l:								 ;\
767	MOVL	CR0, AX						 ;\
768	ANDL	$~0x4, AX			/* EM=0 */	 ;\
769	ORL	$0x28, AX			/* NE=1, TS=1 */ ;\
770	MOVL	AX, CR0
771
772#define	FPON							 ;\
773	MOVL	CR0, AX						 ;\
774	ANDL	$~0xC, AX			/* EM=0, TS=0 */ ;\
775	MOVL	AX, CR0
776
777TEXT fpon(SB), $0				/* enable */
778	FPON
779	RET
780
781TEXT fpoff(SB), $0				/* disable */
782	FPOFF(l1)
783	RET
784
785TEXT fpinit(SB), $0				/* enable and init */
786	FPON
787	FINIT
788	WAIT
789	/* setfcr(FPPDBL|FPRNR|FPINVAL|FPZDIV|FPOVFL) */
790	/* note that low 6 bits are masks, not enables, on this chip */
791	PUSHW	$0x0232
792	FLDCW	0(SP)
793	POPW	AX
794	WAIT
795	RET
796
797TEXT fpx87save(SB), $0				/* save state and disable */
798	MOVL	p+0(FP), AX
799	FSAVE	0(AX)				/* no WAIT */
800	FPOFF(l2)
801	RET
802
803TEXT fpx87restore(SB), $0			/* enable and restore state */
804	FPON
805	MOVL	p+0(FP), AX
806	FRSTOR	0(AX)
807	WAIT
808	RET
809
810TEXT fpstatus(SB), $0				/* get floating point status */
811	FSTSW	AX
812	RET
813
814TEXT fpenv(SB), $0				/* save state without waiting */
815	MOVL	p+0(FP), AX
816	FSTENV	0(AX)				/* also masks FP exceptions */
817	RET
818
819TEXT fpclear(SB), $0				/* clear pending exceptions */
820	FPON
821	FCLEX					/* no WAIT */
822	FPOFF(l3)
823	RET
824
825TEXT fpssesave0(SB), $0				/* save state and disable */
826	MOVL	p+0(FP), AX
827	FXSAVE					/* no WAIT */
828	FPOFF(l4)
829	RET
830
831TEXT fpsserestore0(SB), $0			/* enable and restore state */
832	FPON
833	MOVL	p+0(FP), AX
834	FXRSTOR
835	WAIT
836	RET
837
838/*
839 */
840TEXT splhi(SB), $0
841shi:
842	PUSHFL
843	POPL	AX
844	TESTL	$0x200, AX
845	JZ	alreadyhi
846	MOVL	$(MACHADDR+0x04), CX 		/* save PC in m->splpc */
847	MOVL	(SP), BX
848	MOVL	BX, (CX)
849alreadyhi:
850	CLI
851	RET
852
853TEXT spllo(SB), $0
854slo:
855	PUSHFL
856	POPL	AX
857	TESTL	$0x200, AX
858	JNZ	alreadylo
859	MOVL	$(MACHADDR+0x04), CX		/* clear m->splpc */
860	MOVL	$0, (CX)
861alreadylo:
862	STI
863	RET
864
865TEXT splx(SB), $0
866	MOVL	s+0(FP), AX
867	TESTL	$0x200, AX
868	JNZ	slo
869	JMP	shi
870
871TEXT spldone(SB), $0
872	RET
873
874TEXT islo(SB), $0
875	PUSHFL
876	POPL	AX
877	ANDL	$0x200, AX			/* interrupt enable flag */
878	RET
879
880/*
881 * Miscellany
882 */
883TEXT bsr(SB), $0
884	BSRL	word+0(FP), AX		/* return bit index of leftmost 1 bit */
885	RET
886
887/*
888 * Test-And-Set
889 */
890TEXT tas(SB), $0
891	MOVL	$0xDEADDEAD, AX
892	MOVL	lock+0(FP), BX
893	XCHGL	AX, (BX)			/* lock->key */
894	RET
895
896TEXT ainc(SB), $0				/* void ainc(long*); */
897	MOVL	l+0(FP), AX
898	LOCK;	INCL 0(AX)
899	RET
900
901TEXT adec(SB), $0				/* long adec(long*); */
902	MOVL	l+0(FP), BX
903	XORL	AX, AX
904	LOCK;	DECL 0(BX)
905	JLT	adeclt
906	JGT	adecgt
907	RET
908adecgt:
909	INCL	AX
910	RET
911adeclt:
912	DECL	AX
913	RET
914
915TEXT mb386(SB), $0
916	POPL	AX				/* return PC */
917	PUSHFL
918	PUSHL	CS
919	PUSHL	AX
920	IRETL
921
922TEXT mb586(SB), $0
923	XORL	AX, AX
924	CPUID
925	RET
926
927TEXT sfence(SB), $0
928	BYTE $0x0f
929	BYTE $0xae
930	BYTE $0xf8
931	RET
932
933TEXT lfence(SB), $0
934	BYTE $0x0f
935	BYTE $0xae
936	BYTE $0xe8
937	RET
938
939TEXT mfence(SB), $0
940	BYTE $0x0f
941	BYTE $0xae
942	BYTE $0xf0
943	RET
944
945TEXT xchgw(SB), $0
946	MOVL	v+4(FP), AX
947	MOVL	p+0(FP), BX
948	XCHGW	AX, (BX)
949	RET
950
951TEXT cmpswap486(SB), $0
952	MOVL	addr+0(FP), BX
953	MOVL	old+4(FP), AX
954	MOVL	new+8(FP), CX
955	LOCK
956	BYTE $0x0F; BYTE $0xB1; BYTE $0x0B	/* CMPXCHGL CX, (BX) */
957	JNZ didnt
958	MOVL	$1, AX
959	RET
960didnt:
961	XORL	AX,AX
962	RET
963
964TEXT mul64fract(SB), $0
965/*
966 * Multiply two 64-bit number s and keep the middle 64 bits from the 128-bit result
967 * See ../port/tod.c for motivation.
968 */
969	MOVL	r+0(FP), CX
970	XORL	BX, BX				/* BX = 0 */
971
972	MOVL	a+8(FP), AX
973	MULL	b+16(FP)			/* a1*b1 */
974	MOVL	AX, 4(CX)			/* r2 = lo(a1*b1) */
975
976	MOVL	a+8(FP), AX
977	MULL	b+12(FP)			/* a1*b0 */
978	MOVL	AX, 0(CX)			/* r1 = lo(a1*b0) */
979	ADDL	DX, 4(CX)			/* r2 += hi(a1*b0) */
980
981	MOVL	a+4(FP), AX
982	MULL	b+16(FP)			/* a0*b1 */
983	ADDL	AX, 0(CX)			/* r1 += lo(a0*b1) */
984	ADCL	DX, 4(CX)			/* r2 += hi(a0*b1) + carry */
985
986	MOVL	a+4(FP), AX
987	MULL	b+12(FP)			/* a0*b0 */
988	ADDL	DX, 0(CX)			/* r1 += hi(a0*b0) */
989	ADCL	BX, 4(CX)			/* r2 += carry */
990	RET
991
992/*
993 *  label consists of a stack pointer and a PC
994 */
995TEXT gotolabel(SB), $0
996	MOVL	label+0(FP), AX
997	MOVL	0(AX), SP			/* restore sp */
998	MOVL	4(AX), AX			/* put return pc on the stack */
999	MOVL	AX, 0(SP)
1000	MOVL	$1, AX				/* return 1 */
1001	RET
1002
1003TEXT setlabel(SB), $0
1004	MOVL	label+0(FP), AX
1005	MOVL	SP, 0(AX)			/* store sp */
1006	MOVL	0(SP), BX			/* store return pc */
1007	MOVL	BX, 4(AX)
1008	MOVL	$0, AX				/* return 0 */
1009	RET
1010
1011/*
1012 * Attempt at power saving. -rsc
1013 */
1014TEXT halt(SB), $0
1015	CLI
1016	CMPL	nrdy(SB), $0
1017	JEQ	_nothingready
1018	STI
1019	RET
1020
1021_nothingready:
1022	STI
1023	HLT
1024	RET
1025
1026/*
1027 * Interrupt/exception handling.
1028 * Each entry in the vector table calls either _strayintr or _strayintrx depending
1029 * on whether an error code has been automatically pushed onto the stack
1030 * (_strayintrx) or not, in which case a dummy entry must be pushed before retrieving
1031 * the trap type from the vector table entry and placing it on the stack as part
1032 * of the Ureg structure.
1033 * The size of each entry in the vector table (6 bytes) is known in trapinit().
1034 */
1035TEXT _strayintr(SB), $0
1036	PUSHL	AX			/* save AX */
1037	MOVL	4(SP), AX		/* return PC from vectortable(SB) */
1038	JMP	intrcommon
1039
1040TEXT _strayintrx(SB), $0
1041	XCHGL	AX, (SP)		/* swap AX with vectortable CALL PC */
1042intrcommon:
1043	PUSHL	DS			/* save DS */
1044	PUSHL	$(KDSEL)
1045	POPL	DS			/* fix up DS */
1046	MOVBLZX	(AX), AX		/* trap type -> AX */
1047	XCHGL	AX, 4(SP)		/* exchange trap type with saved AX */
1048
1049	PUSHL	ES			/* save ES */
1050	PUSHL	$(KDSEL)
1051	POPL	ES			/* fix up ES */
1052
1053	PUSHL	FS			/* save the rest of the Ureg struct */
1054	PUSHL	GS
1055	PUSHAL
1056
1057	PUSHL	SP			/* Ureg* argument to trap */
1058	CALL	trap(SB)
1059
1060TEXT forkret(SB), $0
1061	POPL	AX
1062	POPAL
1063	POPL	GS
1064	POPL	FS
1065	POPL	ES
1066	POPL	DS
1067	ADDL	$8, SP			/* pop error code and trap type */
1068	IRETL
1069
1070TEXT vectortable(SB), $0
1071	CALL _strayintr(SB); BYTE $0x00		/* divide error */
1072	CALL _strayintr(SB); BYTE $0x01		/* debug exception */
1073	CALL _strayintr(SB); BYTE $0x02		/* NMI interrupt */
1074	CALL _strayintr(SB); BYTE $0x03		/* breakpoint */
1075	CALL _strayintr(SB); BYTE $0x04		/* overflow */
1076	CALL _strayintr(SB); BYTE $0x05		/* bound */
1077	CALL _strayintr(SB); BYTE $0x06		/* invalid opcode */
1078	CALL _strayintr(SB); BYTE $0x07		/* no coprocessor available */
1079	CALL _strayintrx(SB); BYTE $0x08	/* double fault */
1080	CALL _strayintr(SB); BYTE $0x09		/* coprocessor segment overflow */
1081	CALL _strayintrx(SB); BYTE $0x0A	/* invalid TSS */
1082	CALL _strayintrx(SB); BYTE $0x0B	/* segment not available */
1083	CALL _strayintrx(SB); BYTE $0x0C	/* stack exception */
1084	CALL _strayintrx(SB); BYTE $0x0D	/* general protection error */
1085	CALL _strayintrx(SB); BYTE $0x0E	/* page fault */
1086	CALL _strayintr(SB); BYTE $0x0F		/*  */
1087	CALL _strayintr(SB); BYTE $0x10		/* coprocessor error */
1088	CALL _strayintrx(SB); BYTE $0x11	/* alignment check */
1089	CALL _strayintr(SB); BYTE $0x12		/* machine check */
1090	CALL _strayintr(SB); BYTE $0x13
1091	CALL _strayintr(SB); BYTE $0x14
1092	CALL _strayintr(SB); BYTE $0x15
1093	CALL _strayintr(SB); BYTE $0x16
1094	CALL _strayintr(SB); BYTE $0x17
1095	CALL _strayintr(SB); BYTE $0x18
1096	CALL _strayintr(SB); BYTE $0x19
1097	CALL _strayintr(SB); BYTE $0x1A
1098	CALL _strayintr(SB); BYTE $0x1B
1099	CALL _strayintr(SB); BYTE $0x1C
1100	CALL _strayintr(SB); BYTE $0x1D
1101	CALL _strayintr(SB); BYTE $0x1E
1102	CALL _strayintr(SB); BYTE $0x1F
1103	CALL _strayintr(SB); BYTE $0x20		/* VectorLAPIC */
1104	CALL _strayintr(SB); BYTE $0x21
1105	CALL _strayintr(SB); BYTE $0x22
1106	CALL _strayintr(SB); BYTE $0x23
1107	CALL _strayintr(SB); BYTE $0x24
1108	CALL _strayintr(SB); BYTE $0x25
1109	CALL _strayintr(SB); BYTE $0x26
1110	CALL _strayintr(SB); BYTE $0x27
1111	CALL _strayintr(SB); BYTE $0x28
1112	CALL _strayintr(SB); BYTE $0x29
1113	CALL _strayintr(SB); BYTE $0x2A
1114	CALL _strayintr(SB); BYTE $0x2B
1115	CALL _strayintr(SB); BYTE $0x2C
1116	CALL _strayintr(SB); BYTE $0x2D
1117	CALL _strayintr(SB); BYTE $0x2E
1118	CALL _strayintr(SB); BYTE $0x2F
1119	CALL _strayintr(SB); BYTE $0x30
1120	CALL _strayintr(SB); BYTE $0x31
1121	CALL _strayintr(SB); BYTE $0x32
1122	CALL _strayintr(SB); BYTE $0x33
1123	CALL _strayintr(SB); BYTE $0x34
1124	CALL _strayintr(SB); BYTE $0x35
1125	CALL _strayintr(SB); BYTE $0x36
1126	CALL _strayintr(SB); BYTE $0x37
1127	CALL _strayintr(SB); BYTE $0x38
1128	CALL _strayintr(SB); BYTE $0x39
1129	CALL _strayintr(SB); BYTE $0x3A
1130	CALL _strayintr(SB); BYTE $0x3B
1131	CALL _strayintr(SB); BYTE $0x3C
1132	CALL _strayintr(SB); BYTE $0x3D
1133	CALL _strayintr(SB); BYTE $0x3E
1134	CALL _strayintr(SB); BYTE $0x3F
1135	CALL _syscallintr(SB); BYTE $0x40	/* VectorSYSCALL */
1136	CALL _strayintr(SB); BYTE $0x41
1137	CALL _strayintr(SB); BYTE $0x42
1138	CALL _strayintr(SB); BYTE $0x43
1139	CALL _strayintr(SB); BYTE $0x44
1140	CALL _strayintr(SB); BYTE $0x45
1141	CALL _strayintr(SB); BYTE $0x46
1142	CALL _strayintr(SB); BYTE $0x47
1143	CALL _strayintr(SB); BYTE $0x48
1144	CALL _strayintr(SB); BYTE $0x49
1145	CALL _strayintr(SB); BYTE $0x4A
1146	CALL _strayintr(SB); BYTE $0x4B
1147	CALL _strayintr(SB); BYTE $0x4C
1148	CALL _strayintr(SB); BYTE $0x4D
1149	CALL _strayintr(SB); BYTE $0x4E
1150	CALL _strayintr(SB); BYTE $0x4F
1151	CALL _strayintr(SB); BYTE $0x50
1152	CALL _strayintr(SB); BYTE $0x51
1153	CALL _strayintr(SB); BYTE $0x52
1154	CALL _strayintr(SB); BYTE $0x53
1155	CALL _strayintr(SB); BYTE $0x54
1156	CALL _strayintr(SB); BYTE $0x55
1157	CALL _strayintr(SB); BYTE $0x56
1158	CALL _strayintr(SB); BYTE $0x57
1159	CALL _strayintr(SB); BYTE $0x58
1160	CALL _strayintr(SB); BYTE $0x59
1161	CALL _strayintr(SB); BYTE $0x5A
1162	CALL _strayintr(SB); BYTE $0x5B
1163	CALL _strayintr(SB); BYTE $0x5C
1164	CALL _strayintr(SB); BYTE $0x5D
1165	CALL _strayintr(SB); BYTE $0x5E
1166	CALL _strayintr(SB); BYTE $0x5F
1167	CALL _strayintr(SB); BYTE $0x60
1168	CALL _strayintr(SB); BYTE $0x61
1169	CALL _strayintr(SB); BYTE $0x62
1170	CALL _strayintr(SB); BYTE $0x63
1171	CALL _strayintr(SB); BYTE $0x64
1172	CALL _strayintr(SB); BYTE $0x65
1173	CALL _strayintr(SB); BYTE $0x66
1174	CALL _strayintr(SB); BYTE $0x67
1175	CALL _strayintr(SB); BYTE $0x68
1176	CALL _strayintr(SB); BYTE $0x69
1177	CALL _strayintr(SB); BYTE $0x6A
1178	CALL _strayintr(SB); BYTE $0x6B
1179	CALL _strayintr(SB); BYTE $0x6C
1180	CALL _strayintr(SB); BYTE $0x6D
1181	CALL _strayintr(SB); BYTE $0x6E
1182	CALL _strayintr(SB); BYTE $0x6F
1183	CALL _strayintr(SB); BYTE $0x70
1184	CALL _strayintr(SB); BYTE $0x71
1185	CALL _strayintr(SB); BYTE $0x72
1186	CALL _strayintr(SB); BYTE $0x73
1187	CALL _strayintr(SB); BYTE $0x74
1188	CALL _strayintr(SB); BYTE $0x75
1189	CALL _strayintr(SB); BYTE $0x76
1190	CALL _strayintr(SB); BYTE $0x77
1191	CALL _strayintr(SB); BYTE $0x78
1192	CALL _strayintr(SB); BYTE $0x79
1193	CALL _strayintr(SB); BYTE $0x7A
1194	CALL _strayintr(SB); BYTE $0x7B
1195	CALL _strayintr(SB); BYTE $0x7C
1196	CALL _strayintr(SB); BYTE $0x7D
1197	CALL _strayintr(SB); BYTE $0x7E
1198	CALL _strayintr(SB); BYTE $0x7F
1199	CALL _strayintr(SB); BYTE $0x80		/* Vector[A]PIC */
1200	CALL _strayintr(SB); BYTE $0x81
1201	CALL _strayintr(SB); BYTE $0x82
1202	CALL _strayintr(SB); BYTE $0x83
1203	CALL _strayintr(SB); BYTE $0x84
1204	CALL _strayintr(SB); BYTE $0x85
1205	CALL _strayintr(SB); BYTE $0x86
1206	CALL _strayintr(SB); BYTE $0x87
1207	CALL _strayintr(SB); BYTE $0x88
1208	CALL _strayintr(SB); BYTE $0x89
1209	CALL _strayintr(SB); BYTE $0x8A
1210	CALL _strayintr(SB); BYTE $0x8B
1211	CALL _strayintr(SB); BYTE $0x8C
1212	CALL _strayintr(SB); BYTE $0x8D
1213	CALL _strayintr(SB); BYTE $0x8E
1214	CALL _strayintr(SB); BYTE $0x8F
1215	CALL _strayintr(SB); BYTE $0x90
1216	CALL _strayintr(SB); BYTE $0x91
1217	CALL _strayintr(SB); BYTE $0x92
1218	CALL _strayintr(SB); BYTE $0x93
1219	CALL _strayintr(SB); BYTE $0x94
1220	CALL _strayintr(SB); BYTE $0x95
1221	CALL _strayintr(SB); BYTE $0x96
1222	CALL _strayintr(SB); BYTE $0x97
1223	CALL _strayintr(SB); BYTE $0x98
1224	CALL _strayintr(SB); BYTE $0x99
1225	CALL _strayintr(SB); BYTE $0x9A
1226	CALL _strayintr(SB); BYTE $0x9B
1227	CALL _strayintr(SB); BYTE $0x9C
1228	CALL _strayintr(SB); BYTE $0x9D
1229	CALL _strayintr(SB); BYTE $0x9E
1230	CALL _strayintr(SB); BYTE $0x9F
1231	CALL _strayintr(SB); BYTE $0xA0
1232	CALL _strayintr(SB); BYTE $0xA1
1233	CALL _strayintr(SB); BYTE $0xA2
1234	CALL _strayintr(SB); BYTE $0xA3
1235	CALL _strayintr(SB); BYTE $0xA4
1236	CALL _strayintr(SB); BYTE $0xA5
1237	CALL _strayintr(SB); BYTE $0xA6
1238	CALL _strayintr(SB); BYTE $0xA7
1239	CALL _strayintr(SB); BYTE $0xA8
1240	CALL _strayintr(SB); BYTE $0xA9
1241	CALL _strayintr(SB); BYTE $0xAA
1242	CALL _strayintr(SB); BYTE $0xAB
1243	CALL _strayintr(SB); BYTE $0xAC
1244	CALL _strayintr(SB); BYTE $0xAD
1245	CALL _strayintr(SB); BYTE $0xAE
1246	CALL _strayintr(SB); BYTE $0xAF
1247	CALL _strayintr(SB); BYTE $0xB0
1248	CALL _strayintr(SB); BYTE $0xB1
1249	CALL _strayintr(SB); BYTE $0xB2
1250	CALL _strayintr(SB); BYTE $0xB3
1251	CALL _strayintr(SB); BYTE $0xB4
1252	CALL _strayintr(SB); BYTE $0xB5
1253	CALL _strayintr(SB); BYTE $0xB6
1254	CALL _strayintr(SB); BYTE $0xB7
1255	CALL _strayintr(SB); BYTE $0xB8
1256	CALL _strayintr(SB); BYTE $0xB9
1257	CALL _strayintr(SB); BYTE $0xBA
1258	CALL _strayintr(SB); BYTE $0xBB
1259	CALL _strayintr(SB); BYTE $0xBC
1260	CALL _strayintr(SB); BYTE $0xBD
1261	CALL _strayintr(SB); BYTE $0xBE
1262	CALL _strayintr(SB); BYTE $0xBF
1263	CALL _strayintr(SB); BYTE $0xC0
1264	CALL _strayintr(SB); BYTE $0xC1
1265	CALL _strayintr(SB); BYTE $0xC2
1266	CALL _strayintr(SB); BYTE $0xC3
1267	CALL _strayintr(SB); BYTE $0xC4
1268	CALL _strayintr(SB); BYTE $0xC5
1269	CALL _strayintr(SB); BYTE $0xC6
1270	CALL _strayintr(SB); BYTE $0xC7
1271	CALL _strayintr(SB); BYTE $0xC8
1272	CALL _strayintr(SB); BYTE $0xC9
1273	CALL _strayintr(SB); BYTE $0xCA
1274	CALL _strayintr(SB); BYTE $0xCB
1275	CALL _strayintr(SB); BYTE $0xCC
1276	CALL _strayintr(SB); BYTE $0xCD
1277	CALL _strayintr(SB); BYTE $0xCE
1278	CALL _strayintr(SB); BYTE $0xCF
1279	CALL _strayintr(SB); BYTE $0xD0
1280	CALL _strayintr(SB); BYTE $0xD1
1281	CALL _strayintr(SB); BYTE $0xD2
1282	CALL _strayintr(SB); BYTE $0xD3
1283	CALL _strayintr(SB); BYTE $0xD4
1284	CALL _strayintr(SB); BYTE $0xD5
1285	CALL _strayintr(SB); BYTE $0xD6
1286	CALL _strayintr(SB); BYTE $0xD7
1287	CALL _strayintr(SB); BYTE $0xD8
1288	CALL _strayintr(SB); BYTE $0xD9
1289	CALL _strayintr(SB); BYTE $0xDA
1290	CALL _strayintr(SB); BYTE $0xDB
1291	CALL _strayintr(SB); BYTE $0xDC
1292	CALL _strayintr(SB); BYTE $0xDD
1293	CALL _strayintr(SB); BYTE $0xDE
1294	CALL _strayintr(SB); BYTE $0xDF
1295	CALL _strayintr(SB); BYTE $0xE0
1296	CALL _strayintr(SB); BYTE $0xE1
1297	CALL _strayintr(SB); BYTE $0xE2
1298	CALL _strayintr(SB); BYTE $0xE3
1299	CALL _strayintr(SB); BYTE $0xE4
1300	CALL _strayintr(SB); BYTE $0xE5
1301	CALL _strayintr(SB); BYTE $0xE6
1302	CALL _strayintr(SB); BYTE $0xE7
1303	CALL _strayintr(SB); BYTE $0xE8
1304	CALL _strayintr(SB); BYTE $0xE9
1305	CALL _strayintr(SB); BYTE $0xEA
1306	CALL _strayintr(SB); BYTE $0xEB
1307	CALL _strayintr(SB); BYTE $0xEC
1308	CALL _strayintr(SB); BYTE $0xED
1309	CALL _strayintr(SB); BYTE $0xEE
1310	CALL _strayintr(SB); BYTE $0xEF
1311	CALL _strayintr(SB); BYTE $0xF0
1312	CALL _strayintr(SB); BYTE $0xF1
1313	CALL _strayintr(SB); BYTE $0xF2
1314	CALL _strayintr(SB); BYTE $0xF3
1315	CALL _strayintr(SB); BYTE $0xF4
1316	CALL _strayintr(SB); BYTE $0xF5
1317	CALL _strayintr(SB); BYTE $0xF6
1318	CALL _strayintr(SB); BYTE $0xF7
1319	CALL _strayintr(SB); BYTE $0xF8
1320	CALL _strayintr(SB); BYTE $0xF9
1321	CALL _strayintr(SB); BYTE $0xFA
1322	CALL _strayintr(SB); BYTE $0xFB
1323	CALL _strayintr(SB); BYTE $0xFC
1324	CALL _strayintr(SB); BYTE $0xFD
1325	CALL _strayintr(SB); BYTE $0xFE
1326	CALL _strayintr(SB); BYTE $0xFF
1327