| // Inferno utils/8a/l.s |
| // http://code.google.com/p/inferno-os/source/browse/utils/8a/l.s |
| // |
| // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. |
| // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) |
| // Portions Copyright © 1997-1999 Vita Nuova Limited |
| // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) |
| // Portions Copyright © 2004,2006 Bruce Ellis |
| // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) |
| // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others |
| // Portions Copyright © 2009 The Go Authors. All rights reserved. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a copy |
| // of this software and associated documentation files (the "Software"), to deal |
| // in the Software without restriction, including without limitation the rights |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| // copies of the Software, and to permit persons to whom the Software is |
| // furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| // THE SOFTWARE. |
| |
| /* |
| * Memory and machine-specific definitions. Used in C and assembler. |
| */ |
| |
| /* |
| * Sizes |
| */ |
| #define BI2BY 8 /* bits per byte */ |
| #define BI2WD 32 /* bits per word */ |
| #define BY2WD 4 /* bytes per word */ |
| #define BY2PG 4096 /* bytes per page */ |
| #define WD2PG (BY2PG/BY2WD) /* words per page */ |
| #define PGSHIFT 12 /* log(BY2PG) */ |
| #define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) |
| |
| #define MAXMACH 1 /* max # cpus system can run */ |
| |
| /* |
| * Time |
| */ |
| #define HZ (20) /* clock frequency */ |
| #define MS2HZ (1000/HZ) /* millisec per clock tick */ |
| #define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ |
| #define TK2MS(t) ((((ulong)(t))*1000)/HZ) /* ticks to milliseconds */ |
| #define MS2TK(t) ((((ulong)(t))*HZ)/1000) /* milliseconds to ticks */ |
| |
| /* |
| * Fundamental addresses |
| */ |
| |
| /* |
| * Address spaces |
| * |
| * User is at 0-2GB |
| * Kernel is at 2GB-4GB |
| * |
| * To avoid an extra page map, both the user stack (USTKTOP) and |
| * the temporary user stack (TSTKTOP) should be in the the same |
| * 4 meg. |
| */ |
| #define UZERO 0 /* base of user address space */ |
| #define UTZERO (UZERO+BY2PG) /* first address in user text */ |
| #define KZERO 0x80000000 /* base of kernel address space */ |
| #define KTZERO KZERO /* first address in kernel text */ |
| #define USERADDR 0xC0000000 /* struct User */ |
| #define UREGADDR (USERADDR+BY2PG-4*19) |
| #define TSTKTOP USERADDR /* end of new stack in sysexec */ |
| #define TSTKSIZ 10 |
| #define USTKTOP (TSTKTOP-TSTKSIZ*BY2PG) /* byte just beyond user stack */ |
| #define USTKSIZE (16*1024*1024 - TSTKSIZ*BY2PG) /* size of user stack */ |
| #define ROMBIOS (KZERO|0xF0000) |
| |
| #define MACHSIZE 4096 |
| |
| #define isphys(x) (((ulong)x)&KZERO) |
| |
| /* |
| * known 80386 segments (in GDT) and their selectors |
| */ |
| #define NULLSEG 0 /* null segment */ |
| #define KDSEG 1 /* kernel data/stack */ |
| #define KESEG 2 /* kernel executable */ |
| #define UDSEG 3 /* user data/stack */ |
| #define UESEG 4 /* user executable */ |
| #define TSSSEG 5 /* task segment */ |
| |
| #define SELGDT (0<<3) /* selector is in gdt */ |
| #define SELLDT (1<<3) /* selector is in ldt */ |
| |
| #define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) |
| |
| #define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) |
| #define KESEL SELECTOR(KESEG, SELGDT, 0) |
| #define KDSEL SELECTOR(KDSEG, SELGDT, 0) |
| #define UESEL SELECTOR(UESEG, SELGDT, 3) |
| #define UDSEL SELECTOR(UDSEG, SELGDT, 3) |
| #define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) |
| |
| /* |
| * fields in segment descriptors |
| */ |
| #define SEGDATA (0x10<<8) /* data/stack segment */ |
| #define SEGEXEC (0x18<<8) /* executable segment */ |
| #define SEGTSS (0x9<<8) /* TSS segment */ |
| #define SEGCG (0x0C<<8) /* call gate */ |
| #define SEGIG (0x0E<<8) /* interrupt gate */ |
| #define SEGTG (0x0F<<8) /* task gate */ |
| #define SEGTYPE (0x1F<<8) |
| |
| #define SEGP (1<<15) /* segment present */ |
| #define SEGPL(x) ((x)<<13) /* priority level */ |
| #define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ |
| #define SEGG (1<<23) /* granularity 1==4k (for other) */ |
| #define SEGE (1<<10) /* expand down */ |
| #define SEGW (1<<9) /* writable (for data/stack) */ |
| #define SEGR (1<<9) /* readable (for code) */ |
| #define SEGD (1<<22) /* default 1==32bit (for code) */ |
| |
| /* |
| * virtual MMU |
| */ |
| #define PTEMAPMEM (1024*1024) /* ??? */ |
| #define SEGMAPSIZE 16 /* ??? */ |
| #define PTEPERTAB (PTEMAPMEM/BY2PG) /* ??? */ |
| #define PPN(x) ((x)&~(BY2PG-1)) |
| |
| /* |
| * physical MMU |
| */ |
| #define PTEVALID (1<<0) |
| #define PTEUNCACHED 0 /* everything is uncached */ |
| #define PTEWRITE (1<<1) |
| #define PTERONLY (0<<1) |
| #define PTEKERNEL (0<<2) |
| #define PTEUSER (1<<2) |
| |
| /* |
| * flag register bits that we care about |
| */ |
| #define IFLAG 0x200 |
| |
| #define OP16 BYTE $0x66 |
| |
| /* |
| * about to walk all over ms/dos - turn off interrupts |
| */ |
| TEXT origin(SB),$0 |
| |
| CLI |
| |
| #ifdef BOOT |
| /* |
| * This part of l.s is used only in the boot kernel. |
| * It assumes that we are in real address mode, i.e., |
| * that we look like an 8086. |
| */ |
| /* |
| * relocate everything to a half meg and jump there |
| * - looks wierd because it is being assembled by a 32 bit |
| * assembler for a 16 bit world |
| */ |
| MOVL $0,BX |
| INCL BX |
| SHLL $15,BX |
| MOVL BX,CX |
| MOVW BX,ES |
| MOVL $0,SI |
| MOVL SI,DI |
| CLD; REP; MOVSL |
| /* JMPFAR 0X8000:$lowcore(SB) /**/ |
| BYTE $0xEA |
| WORD $lowcore(SB) |
| WORD $0X8000 |
| |
| TEXT lowcore(SB),$0 |
| |
| /* |
| * now that we're in low core, update the DS |
| */ |
| |
| MOVW BX,DS |
| |
| /* |
| * goto protected mode |
| */ |
| /* MOVL tgdtptr(SB),GDTR /**/ |
| BYTE $0x0f |
| BYTE $0x01 |
| BYTE $0x16 |
| WORD $tgdtptr(SB) |
| MOVL CR0,AX |
| ORL $1,AX |
| MOVL AX,CR0 |
| |
| /* |
| * clear prefetch queue (wierd code to avoid optimizations) |
| */ |
| CLC |
| JCC flush |
| MOVL AX,AX |
| flush: |
| |
| /* |
| * set all segs |
| */ |
| /* MOVW $SELECTOR(1, SELGDT, 0),AX /**/ |
| BYTE $0xc7 |
| BYTE $0xc0 |
| WORD $SELECTOR(1, SELGDT, 0) |
| MOVW AX,DS |
| MOVW AX,SS |
| MOVW AX,ES |
| MOVW AX,FS |
| MOVW AX,GS |
| |
| /* JMPFAR SELECTOR(2, SELGDT, 0):$mode32bit(SB) /**/ |
| BYTE $0x66 |
| BYTE $0xEA |
| LONG $mode32bit-KZERO(SB) |
| WORD $SELECTOR(2, SELGDT, 0) |
| |
| TEXT mode32bit(SB),$0 |
| |
| #endif BOOT |
| |
| /* |
| * Clear BSS |
| */ |
| LEAL edata-KZERO(SB),SI |
| MOVL SI,DI |
| ADDL $4,DI |
| MOVL $0,AX |
| MOVL AX,(SI) |
| LEAL end-KZERO(SB),CX |
| SUBL DI,CX |
| SHRL $2,CX |
| CLD; REP; MOVSL |
| |
| /* |
| * make a bottom level page table page that maps the first |
| * 16 meg of physical memory |
| */ |
| LEAL tpt-KZERO(SB),AX /* get phys addr of temporary page table */ |
| ADDL $(BY2PG-1),AX /* must be page aligned */ |
| ANDL $(~(BY2PG-1)),AX /* ... */ |
| MOVL $(4*1024),CX /* pte's per page */ |
| MOVL $((((4*1024)-1)<<PGSHIFT)|PTEVALID|PTEKERNEL|PTEWRITE),BX |
| setpte: |
| MOVL BX,-4(AX)(CX*4) |
| SUBL $(1<<PGSHIFT),BX |
| LOOP setpte |
| |
| /* |
| * make a top level page table page that maps the first |
| * 16 meg of memory to 0 thru 16meg and to KZERO thru KZERO+16meg |
| */ |
| MOVL AX,BX |
| ADDL $(4*BY2PG),AX |
| ADDL $(PTEVALID|PTEKERNEL|PTEWRITE),BX |
| MOVL BX,0(AX) |
| MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+0)(AX) |
| ADDL $BY2PG,BX |
| MOVL BX,4(AX) |
| MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+4)(AX) |
| ADDL $BY2PG,BX |
| MOVL BX,8(AX) |
| MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+8)(AX) |
| ADDL $BY2PG,BX |
| MOVL BX,12(AX) |
| MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+12)(AX) |
| |
| /* |
| * point processor to top level page & turn on paging |
| */ |
| MOVL AX,CR3 |
| MOVL CR0,AX |
| ORL $0X80000000,AX |
| ANDL $~(0x8|0x2),AX /* TS=0, MP=0 */ |
| MOVL AX,CR0 |
| |
| /* |
| * use a jump to an absolute location to get the PC into |
| * KZERO. |
| */ |
| LEAL tokzero(SB),AX |
| JMP* AX |
| |
| TEXT tokzero(SB),$0 |
| |
| /* |
| * stack and mach |
| */ |
| MOVL $mach0(SB),SP |
| MOVL SP,m(SB) |
| MOVL $0,0(SP) |
| ADDL $(MACHSIZE-4),SP /* start stack under machine struct */ |
| MOVL $0, u(SB) |
| |
| /* |
| * clear flags |
| */ |
| MOVL $0,AX |
| PUSHL AX |
| POPFL |
| |
| CALL main(SB) |
| |
| loop: |
| JMP loop |
| |
| GLOBL mach0+0(SB), $MACHSIZE |
| GLOBL u(SB), $4 |
| GLOBL m(SB), $4 |
| GLOBL tpt(SB), $(BY2PG*6) |
| |
| /* |
| * gdt to get us to 32-bit/segmented/unpaged mode |
| */ |
| TEXT tgdt(SB),$0 |
| |
| /* null descriptor */ |
| LONG $0 |
| LONG $0 |
| |
| /* data segment descriptor for 4 gigabytes (PL 0) */ |
| LONG $(0xFFFF) |
| LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) |
| |
| /* exec segment descriptor for 4 gigabytes (PL 0) */ |
| LONG $(0xFFFF) |
| LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) |
| |
| /* |
| * pointer to initial gdt |
| */ |
| TEXT tgdtptr(SB),$0 |
| |
| WORD $(3*8) |
| LONG $tgdt-KZERO(SB) |
| |
| /* |
| * input a byte |
| */ |
| TEXT inb(SB),$0 |
| |
| MOVL p+0(FP),DX |
| XORL AX,AX |
| INB |
| RET |
| |
| /* |
| * output a byte |
| */ |
| TEXT outb(SB),$0 |
| |
| MOVL p+0(FP),DX |
| MOVL b+4(FP),AX |
| OUTB |
| RET |
| |
| /* |
| * input a string of shorts from a port |
| */ |
| TEXT inss(SB),$0 |
| MOVL p+0(FP),DX |
| MOVL a+4(FP),DI |
| MOVL c+8(FP),CX |
| CLD; REP; OP16; INSL |
| RET |
| |
| /* |
| * output a string of shorts to a port |
| */ |
| TEXT outss(SB),$0 |
| MOVL p+0(FP),DX |
| MOVL a+4(FP),SI |
| MOVL c+8(FP),CX |
| CLD; REP; OP16; OUTSL |
| RET |
| |
| /* |
| * test and set |
| */ |
| TEXT tas(SB),$0 |
| MOVL $0xdeadead,AX |
| MOVL l+0(FP),BX |
| XCHGL AX,(BX) |
| RET |
| |
| /* |
| * routines to load/read various system registers |
| */ |
| GLOBL idtptr(SB),$6 |
| TEXT putidt(SB),$0 /* interrupt descriptor table */ |
| MOVL t+0(FP),AX |
| MOVL AX,idtptr+2(SB) |
| MOVL l+4(FP),AX |
| MOVW AX,idtptr(SB) |
| MOVL idtptr(SB),IDTR |
| RET |
| |
| GLOBL gdtptr(SB),$6 |
| TEXT putgdt(SB),$0 /* global descriptor table */ |
| MOVL t+0(FP),AX |
| MOVL AX,gdtptr+2(SB) |
| MOVL l+4(FP),AX |
| MOVW AX,gdtptr(SB) |
| MOVL gdtptr(SB),GDTR |
| RET |
| |
| TEXT putcr3(SB),$0 /* top level page table pointer */ |
| MOVL t+0(FP),AX |
| MOVL AX,CR3 |
| RET |
| |
| TEXT puttr(SB),$0 /* task register */ |
| MOVL t+0(FP),AX |
| MOVW AX,TASK |
| RET |
| |
| TEXT getcr0(SB),$0 /* coprocessor bits */ |
| MOVL CR0,AX |
| RET |
| |
| TEXT getcr2(SB),$0 /* fault address */ |
| MOVL CR2,AX |
| RET |
| |
| #define FPOFF\ |
| WAIT;\ |
| MOVL CR0,AX;\ |
| ORL $0x4,AX /* EM=1 */;\ |
| MOVL AX,CR0 |
| |
| #define FPON\ |
| MOVL CR0,AX;\ |
| ANDL $~0x4,AX /* EM=0 */;\ |
| MOVL AX,CR0 |
| |
| TEXT fpoff(SB),$0 /* turn off floating point */ |
| FPOFF |
| RET |
| |
| TEXT fpinit(SB),$0 /* turn on & init the floating point */ |
| FPON |
| FINIT |
| WAIT |
| PUSHW $0x0330 |
| FLDCW 0(SP) /* ignore underflow/precision, signal others */ |
| POPW AX |
| WAIT |
| RET |
| |
| TEXT fpsave(SB),$0 /* save floating point state and turn off */ |
| MOVL p+0(FP),AX |
| WAIT |
| FSAVE 0(AX) |
| FPOFF |
| RET |
| |
| TEXT fprestore(SB),$0 /* turn on floating point and restore regs */ |
| FPON |
| MOVL p+0(FP),AX |
| FRSTOR 0(AX) |
| WAIT |
| RET |
| |
| TEXT fpstatus(SB),$0 /* get floating point status */ |
| FSTSW AX |
| RET |
| |
| /* |
| * special traps |
| */ |
| TEXT intr0(SB),$0 |
| PUSHL $0 |
| PUSHL $0 |
| JMP intrcommon |
| TEXT intr1(SB),$0 |
| PUSHL $0 |
| PUSHL $1 |
| JMP intrcommon |
| TEXT intr2(SB),$0 |
| PUSHL $0 |
| PUSHL $2 |
| JMP intrcommon |
| TEXT intr3(SB),$0 |
| PUSHL $0 |
| PUSHL $3 |
| JMP intrcommon |
| TEXT intr4(SB),$0 |
| PUSHL $0 |
| PUSHL $4 |
| JMP intrcommon |
| TEXT intr5(SB),$0 |
| PUSHL $0 |
| PUSHL $5 |
| JMP intrcommon |
| TEXT intr6(SB),$0 |
| PUSHL $0 |
| PUSHL $6 |
| JMP intrcommon |
| TEXT intr7(SB),$0 |
| PUSHL $0 |
| PUSHL $7 |
| JMP intrcommon |
| TEXT intr8(SB),$0 |
| PUSHL $8 |
| JMP intrscommon |
| TEXT intr9(SB),$0 |
| PUSHL $0 |
| PUSHL $9 |
| JMP intrcommon |
| TEXT intr10(SB),$0 |
| PUSHL $10 |
| JMP intrscommon |
| TEXT intr11(SB),$0 |
| PUSHL $11 |
| JMP intrscommon |
| TEXT intr12(SB),$0 |
| PUSHL $12 |
| JMP intrscommon |
| TEXT intr13(SB),$0 |
| PUSHL $13 |
| JMP intrscommon |
| TEXT intr14(SB),$0 |
| PUSHL $14 |
| JMP intrscommon |
| TEXT intr15(SB),$0 |
| PUSHL $0 |
| PUSHL $15 |
| JMP intrcommon |
| TEXT intr16(SB),$0 |
| PUSHL $0 |
| PUSHL $16 |
| JMP intrcommon |
| TEXT intr24(SB),$0 |
| PUSHL $0 |
| PUSHL $24 |
| JMP intrcommon |
| TEXT intr25(SB),$0 |
| PUSHL $0 |
| PUSHL $25 |
| JMP intrcommon |
| TEXT intr26(SB),$0 |
| PUSHL $0 |
| PUSHL $26 |
| JMP intrcommon |
| TEXT intr27(SB),$0 |
| PUSHL $0 |
| PUSHL $27 |
| JMP intrcommon |
| TEXT intr28(SB),$0 |
| PUSHL $0 |
| PUSHL $28 |
| JMP intrcommon |
| TEXT intr29(SB),$0 |
| PUSHL $0 |
| PUSHL $29 |
| JMP intrcommon |
| TEXT intr30(SB),$0 |
| PUSHL $0 |
| PUSHL $30 |
| JMP intrcommon |
| TEXT intr31(SB),$0 |
| PUSHL $0 |
| PUSHL $31 |
| JMP intrcommon |
| TEXT intr32(SB),$0 |
| PUSHL $0 |
| PUSHL $16 |
| JMP intrcommon |
| TEXT intr33(SB),$0 |
| PUSHL $0 |
| PUSHL $33 |
| JMP intrcommon |
| TEXT intr34(SB),$0 |
| PUSHL $0 |
| PUSHL $34 |
| JMP intrcommon |
| TEXT intr35(SB),$0 |
| PUSHL $0 |
| PUSHL $35 |
| JMP intrcommon |
| TEXT intr36(SB),$0 |
| PUSHL $0 |
| PUSHL $36 |
| JMP intrcommon |
| TEXT intr37(SB),$0 |
| PUSHL $0 |
| PUSHL $37 |
| JMP intrcommon |
| TEXT intr38(SB),$0 |
| PUSHL $0 |
| PUSHL $38 |
| JMP intrcommon |
| TEXT intr39(SB),$0 |
| PUSHL $0 |
| PUSHL $39 |
| JMP intrcommon |
| TEXT intr64(SB),$0 |
| PUSHL $0 |
| PUSHL $64 |
| JMP intrcommon |
| TEXT intrbad(SB),$0 |
| PUSHL $0 |
| PUSHL $0x1ff |
| JMP intrcommon |
| |
| intrcommon: |
| PUSHL DS |
| PUSHL ES |
| PUSHL FS |
| PUSHL GS |
| PUSHAL |
| MOVL $(KDSEL),AX |
| MOVW AX,DS |
| MOVW AX,ES |
| LEAL 0(SP),AX |
| PUSHL AX |
| CALL trap(SB) |
| POPL AX |
| POPAL |
| POPL GS |
| POPL FS |
| POPL ES |
| POPL DS |
| ADDL $8,SP /* error code and trap type */ |
| IRETL |
| |
| intrscommon: |
| PUSHL DS |
| PUSHL ES |
| PUSHL FS |
| PUSHL GS |
| PUSHAL |
| MOVL $(KDSEL),AX |
| MOVW AX,DS |
| MOVW AX,ES |
| LEAL 0(SP),AX |
| PUSHL AX |
| CALL trap(SB) |
| POPL AX |
| POPAL |
| POPL GS |
| POPL FS |
| POPL ES |
| POPL DS |
| ADDL $8,SP /* error code and trap type */ |
| IRETL |
| |
| /* |
| * interrupt level is interrupts on or off |
| */ |
| TEXT spllo(SB),$0 |
| PUSHFL |
| POPL AX |
| STI |
| RET |
| |
| TEXT splhi(SB),$0 |
| PUSHFL |
| POPL AX |
| CLI |
| RET |
| |
| TEXT splx(SB),$0 |
| MOVL s+0(FP),AX |
| PUSHL AX |
| POPFL |
| RET |
| |
| /* |
| * do nothing whatsoever till interrupt happens |
| */ |
| TEXT idle(SB),$0 |
| HLT |
| RET |
| |
| /* |
| * label consists of a stack pointer and a PC |
| */ |
| TEXT gotolabel(SB),$0 |
| MOVL l+0(FP),AX |
| MOVL 0(AX),SP /* restore sp */ |
| MOVL 4(AX),AX /* put return pc on the stack */ |
| MOVL AX,0(SP) |
| MOVL $1,AX /* return 1 */ |
| RET |
| |
| TEXT setlabel(SB),$0 |
| MOVL l+0(FP),AX |
| MOVL SP,0(AX) /* store sp */ |
| MOVL 0(SP),BX /* store return pc */ |
| MOVL BX,4(AX) |
| MOVL $0,AX /* return 0 */ |
| RET |
| |
| /* |
| * Used to get to the first process. |
| * Set up an interrupt return frame and IRET to user level. |
| */ |
| TEXT touser(SB),$0 |
| PUSHL $(UDSEL) /* old ss */ |
| PUSHL $(USTKTOP) /* old sp */ |
| PUSHFL /* old flags */ |
| PUSHL $(UESEL) /* old cs */ |
| PUSHL $(UTZERO+32) /* old pc */ |
| MOVL $(UDSEL),AX |
| MOVW AX,DS |
| MOVW AX,ES |
| MOVW AX,GS |
| MOVW AX,FS |
| IRETL |
| |
| /* |
| * set configuration register |
| */ |
| TEXT config(SB),$0 |
| MOVL l+0(FP),AX |
| MOVL $0x3F3,DX |
| OUTB |
| OUTB |
| RET |